mirror of
https://github.com/Ylianst/MeshCentral.git
synced 2024-11-23 06:34:54 +03:00
Merge branch 'master' of https://github.com/Ylianst/MeshCentral
This commit is contained in:
commit
e3575046a1
@ -117,6 +117,7 @@
|
||||
<Compile Include="meshuser.js" />
|
||||
<Compile Include="mpsserver.js" />
|
||||
<Compile Include="mqttbroker.js" />
|
||||
<Compile Include="pluginHandler.js" />
|
||||
<Compile Include="public\scripts\agent-redir-rtc-0.1.0.js" />
|
||||
<Compile Include="swarmserver.js" />
|
||||
<Compile Include="multiserver.js" />
|
||||
|
@ -37,12 +37,10 @@ var MESHRIGHT_NOFILES = 1024;
|
||||
var MESHRIGHT_NOAMT = 2048;
|
||||
var MESHRIGHT_LIMITEDINPUT = 4096;
|
||||
|
||||
function createMeshCore(agent)
|
||||
{
|
||||
function createMeshCore(agent) {
|
||||
var obj = {};
|
||||
|
||||
if (process.platform == 'darwin' && !process.versions)
|
||||
{
|
||||
if (process.platform == 'darwin' && !process.versions) {
|
||||
// This is an older MacOS Agent, so we'll need to check the service definition so that Auto-Update will function correctly
|
||||
var child = require('child_process').execFile('/bin/sh', ['sh']);
|
||||
child.stdout.str = '';
|
||||
@ -51,21 +49,18 @@ function createMeshCore(agent)
|
||||
child.stdin.write(" if(c[1]==\"dict\"){ split(a[2], d, \"</dict>\"); if(split(d[1], truval, \"<true/>\")>1) { split(truval[1], kn1, \"<key>\"); split(kn1[2], kn2, \"</key>\"); print kn2[1]; } }");
|
||||
child.stdin.write(" else { split(c[1], ka, \"/\"); if(ka[1]==\"true\") {print \"ALWAYS\";} } }'\nexit\n");
|
||||
child.waitExit();
|
||||
if (child.stdout.str.trim() == 'Crashed')
|
||||
{
|
||||
if (child.stdout.str.trim() == 'Crashed') {
|
||||
child = require('child_process').execFile('/bin/sh', ['sh']);
|
||||
child.stdout.str = '';
|
||||
child.stdout.on('data', function (chunk) { this.str += chunk.toString(); });
|
||||
child.stdin.write("launchctl list | grep 'meshagent' | awk '{ if($3==\"meshagent\"){print $1;}}'\nexit\n");
|
||||
child.waitExit();
|
||||
|
||||
if (parseInt(child.stdout.str.trim()) == process.pid)
|
||||
{
|
||||
if (parseInt(child.stdout.str.trim()) == process.pid) {
|
||||
// The currently running MeshAgent is us, so we can continue with the update
|
||||
var plist = require('fs').readFileSync('/Library/LaunchDaemons/meshagent_osx64_LaunchDaemon.plist').toString();
|
||||
var tokens = plist.split('<key>KeepAlive</key>');
|
||||
if (tokens[1].split('>')[0].split('<')[1] == 'dict')
|
||||
{
|
||||
if (tokens[1].split('>')[0].split('<')[1] == 'dict') {
|
||||
var tmp = tokens[1].split('</dict>');
|
||||
tmp.shift();
|
||||
tokens[1] = '\n <true/>' + tmp.join('</dict>');
|
||||
@ -125,10 +120,8 @@ function createMeshCore(agent)
|
||||
if (process.platform != 'win32') { try { require('fs').unlinkSync(process.cwd() + '/DAIPC'); } catch (ee) { } }
|
||||
obj.DAIPC.IPCPATH = process.platform == 'win32' ? ('\\\\.\\pipe\\' + require('_agentNodeId')() + '-DAIPC') : (process.cwd() + '/DAIPC');
|
||||
try { obj.DAIPC.listen({ path: obj.DAIPC.IPCPATH }); } catch (e) { }
|
||||
obj.DAIPC.on('connection', function (c)
|
||||
{
|
||||
c._send = function (j)
|
||||
{
|
||||
obj.DAIPC.on('connection', function (c) {
|
||||
c._send = function (j) {
|
||||
var data = JSON.stringify(j);
|
||||
var packet = Buffer.alloc(data.length + 4);
|
||||
packet.writeUInt32LE(data.length + 4, 0);
|
||||
@ -138,32 +131,26 @@ function createMeshCore(agent)
|
||||
this._daipc = c;
|
||||
c.parent = this;
|
||||
c.on('end', function () { console.log('Connection Closed'); this.parent._daipc = null; });
|
||||
c.on('data', function (chunk)
|
||||
{
|
||||
c.on('data', function (chunk) {
|
||||
if (chunk.length < 4) { this.unshift(chunk); return; }
|
||||
var len = chunk.readUInt32LE(0);
|
||||
if (len > 8192) { this.parent._daipc = null; this.end(); return; }
|
||||
if (chunk.length < len) { this.unshift(chunk); return; }
|
||||
|
||||
|
||||
var data = chunk.slice(4, len);
|
||||
try
|
||||
{
|
||||
try {
|
||||
data = JSON.parse(data.toString());
|
||||
}
|
||||
catch(de)
|
||||
{
|
||||
catch (de) {
|
||||
this.parent._daipc = null; this.end(); return;
|
||||
}
|
||||
|
||||
|
||||
if (!data.cmd) { this.parent._daipc = null; this.end(); return; }
|
||||
|
||||
try
|
||||
{
|
||||
switch(data.cmd)
|
||||
{
|
||||
try {
|
||||
switch (data.cmd) {
|
||||
case 'query':
|
||||
switch(data.value)
|
||||
{
|
||||
switch (data.value) {
|
||||
case 'connection':
|
||||
data.result = require('MeshAgent').ConnectedServer;
|
||||
this._send(data);
|
||||
@ -176,32 +163,26 @@ function createMeshCore(agent)
|
||||
return;
|
||||
}
|
||||
}
|
||||
catch(xe)
|
||||
{
|
||||
catch (xe) {
|
||||
this.parent._daipc = null; this.end(); return;
|
||||
}
|
||||
});
|
||||
});
|
||||
function diagnosticAgent_uninstall()
|
||||
{
|
||||
function diagnosticAgent_uninstall() {
|
||||
require('service-manager').manager.uninstallService('meshagentDiagnostic');
|
||||
require('task-scheduler').delete('meshagentDiagnostic/periodicStart');
|
||||
};
|
||||
function diagnosticAgent_installCheck(install)
|
||||
{
|
||||
try
|
||||
{
|
||||
function diagnosticAgent_installCheck(install) {
|
||||
try {
|
||||
var diag = require('service-manager').manager.getService('meshagentDiagnostic');
|
||||
return (diag);
|
||||
}
|
||||
catch (e)
|
||||
{
|
||||
catch (e) {
|
||||
}
|
||||
if (!install) { return (null); }
|
||||
|
||||
var svc = null;
|
||||
try
|
||||
{
|
||||
try {
|
||||
require('service-manager').manager.installService(
|
||||
{
|
||||
name: 'meshagentDiagnostic',
|
||||
@ -213,8 +194,7 @@ function createMeshCore(agent)
|
||||
});
|
||||
svc = require('service-manager').manager.getService('meshagentDiagnostic');
|
||||
}
|
||||
catch (e)
|
||||
{
|
||||
catch (e) {
|
||||
return (null);
|
||||
}
|
||||
var proxyConfig = require('global-tunnel').proxyConfig;
|
||||
@ -227,12 +207,10 @@ function createMeshCore(agent)
|
||||
ddb.Put('MeshServer', require('MeshAgent').ServerInfo.ServerUri);
|
||||
if (cert.root.pfx) { ddb.Put('SelfNodeCert', cert.root.pfx); }
|
||||
if (cert.tls) { ddb.Put('SelfNodeTlsCert', cert.tls.pfx); }
|
||||
if (proxyConfig)
|
||||
{
|
||||
if (proxyConfig) {
|
||||
ddb.Put('WebProxy', proxyConfig.host + ':' + proxyConfig.port);
|
||||
}
|
||||
else
|
||||
{
|
||||
else {
|
||||
ddb.Put('ignoreProxyFile', '1');
|
||||
}
|
||||
|
||||
@ -242,7 +220,7 @@ function createMeshCore(agent)
|
||||
delete ddb;
|
||||
|
||||
// Set a recurrent task, to run the Diagnostic Agent every 2 days
|
||||
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'});
|
||||
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' });
|
||||
//require('task-scheduler').create({ name: 'meshagentDiagnostic/periodicStart', daily: '1', time: '17:16', service: 'meshagentDiagnostic' });
|
||||
|
||||
return (svc);
|
||||
@ -281,7 +259,7 @@ function createMeshCore(agent)
|
||||
}
|
||||
obj.borderManager = new borderController();
|
||||
*/
|
||||
|
||||
|
||||
// MeshAgent JavaScript Core Module. This code is sent to and running on the mesh agent.
|
||||
var meshCoreObj = { "action": "coreinfo", "value": "MeshCore v6", "caps": 14 }; // Capability bitmask: 1 = Desktop, 2 = Terminal, 4 = Files, 8 = Console, 16 = JavaScript, 32 = Temporary Agent, 64 = Recovery Agent
|
||||
|
||||
@ -397,7 +375,7 @@ function createMeshCore(agent)
|
||||
});
|
||||
}
|
||||
} catch (ex) { sendConsoleText("ex1: " + ex); }
|
||||
|
||||
|
||||
// Try to load up the WIFI scanner
|
||||
try {
|
||||
var wifiScannerLib = require('wifi-scanner');
|
||||
@ -436,7 +414,7 @@ function createMeshCore(agent)
|
||||
}
|
||||
catch (e) { return false; }
|
||||
}
|
||||
|
||||
|
||||
// Remove all Gateway MAC addresses for interface list. This is useful because the gateway MAC is not always populated reliably.
|
||||
function clearGatewayMac(str) {
|
||||
if (str == null) return null;
|
||||
@ -444,7 +422,7 @@ function createMeshCore(agent)
|
||||
for (var i in x.netif) { if (x.netif[i].gatewaymac) { delete x.netif[i].gatewaymac } }
|
||||
return JSON.stringify(x);
|
||||
}
|
||||
|
||||
|
||||
function getIpLocationData(func) {
|
||||
// Get the location information for the cache if possible
|
||||
var publicLocationInfo = db.Get('publicLocationInfo');
|
||||
@ -483,7 +461,7 @@ function createMeshCore(agent)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Polyfill String.endsWith
|
||||
if (!String.prototype.endsWith) {
|
||||
String.prototype.endsWith = function (searchString, position) {
|
||||
@ -494,7 +472,7 @@ function createMeshCore(agent)
|
||||
return lastIndex !== -1 && lastIndex === position;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
// Polyfill path.join
|
||||
obj.path = {
|
||||
join: function () {
|
||||
@ -513,19 +491,19 @@ function createMeshCore(agent)
|
||||
return x.join('/');
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// Replace a string with a number if the string is an exact number
|
||||
function toNumberIfNumber(x) { if ((typeof x == 'string') && (+parseInt(x) === x)) { x = parseInt(x); } return x; }
|
||||
|
||||
|
||||
// Convert decimal to hex
|
||||
function char2hex(i) { return (i + 0x100).toString(16).substr(-2).toUpperCase(); }
|
||||
|
||||
|
||||
// Convert a raw string to a hex string
|
||||
function rstr2hex(input) { var r = '', i; for (i = 0; i < input.length; i++) { r += char2hex(input.charCodeAt(i)); } return r; }
|
||||
|
||||
|
||||
// Convert a buffer into a string
|
||||
function buf2rstr(buf) { var r = ''; for (var i = 0; i < buf.length; i++) { r += String.fromCharCode(buf[i]); } return r; }
|
||||
|
||||
|
||||
// Convert a hex string to a raw string // TODO: Do this using Buffer(), will be MUCH faster
|
||||
function hex2rstr(d) {
|
||||
if (typeof d != "string" || d.length == 0) return '';
|
||||
@ -533,7 +511,7 @@ function createMeshCore(agent)
|
||||
while (t = m.shift()) r += String.fromCharCode('0x' + t);
|
||||
return r
|
||||
}
|
||||
|
||||
|
||||
// Convert an object to string with all functions
|
||||
function objToString(x, p, pad, ret) {
|
||||
if (ret == undefined) ret = '';
|
||||
@ -548,17 +526,17 @@ function createMeshCore(agent)
|
||||
for (var i in x) { if (i != '_ObjectID') { r += (addPad(p + 2, pad) + i + ': ' + objToString(x[i], p + 2, pad, ret) + (ret ? '\r\n' : ' ')); } }
|
||||
return r + addPad(p, pad) + '}';
|
||||
}
|
||||
|
||||
|
||||
// Return p number of spaces
|
||||
function addPad(p, ret) { var r = ''; for (var i = 0; i < p; i++) { r += ret; } return r; }
|
||||
|
||||
|
||||
// Split a string taking into account the quoats. Used for command line parsing
|
||||
function splitArgs(str) {
|
||||
var myArray = [], myRegexp = /[^\s"]+|"([^"]*)"/gi;
|
||||
do { var match = myRegexp.exec(str); if (match != null) { myArray.push(match[1] ? match[1] : match[0]); } } while (match != null);
|
||||
return myArray;
|
||||
}
|
||||
|
||||
|
||||
// Parse arguments string array into an object
|
||||
function parseArgs(argv) {
|
||||
var results = { '_': [] }, current = null;
|
||||
@ -574,7 +552,7 @@ function createMeshCore(agent)
|
||||
if (current != null) { results[current] = true; }
|
||||
return results;
|
||||
}
|
||||
|
||||
|
||||
// Get server target url with a custom path
|
||||
function getServerTargetUrl(path) {
|
||||
var x = mesh.ServerUrl;
|
||||
@ -585,13 +563,13 @@ function createMeshCore(agent)
|
||||
if (x == null) return null;
|
||||
return x.protocol + '//' + x.host + ':' + x.port + '/' + path;
|
||||
}
|
||||
|
||||
|
||||
// Get server url. If the url starts with "*/..." change it, it not use the url as is.
|
||||
function getServerTargetUrlEx(url) {
|
||||
if (url.substring(0, 2) == '*/') { return getServerTargetUrl(url.substring(2)); }
|
||||
return url;
|
||||
}
|
||||
|
||||
|
||||
// Send a wake-on-lan packet
|
||||
function sendWakeOnLan(hexMac) {
|
||||
var count = 0;
|
||||
@ -600,7 +578,7 @@ function createMeshCore(agent)
|
||||
var magic = 'FFFFFFFFFFFF';
|
||||
for (var x = 1; x <= 16; ++x) { magic += hexMac; }
|
||||
var magicbin = Buffer.from(magic, 'hex');
|
||||
|
||||
|
||||
for (var adapter in interfaces) {
|
||||
if (interfaces.hasOwnProperty(adapter)) {
|
||||
for (var i = 0; i < interfaces[adapter].length; ++i) {
|
||||
@ -618,7 +596,7 @@ function createMeshCore(agent)
|
||||
} catch (e) { }
|
||||
return count;
|
||||
}
|
||||
|
||||
|
||||
// Handle a mesh agent command
|
||||
function handleServerCommand(data) {
|
||||
if (typeof data == 'object') {
|
||||
@ -722,7 +700,7 @@ function createMeshCore(agent)
|
||||
// Open a local web browser and return success/fail
|
||||
MeshServerLog('Opening: ' + data.url, data);
|
||||
sendConsoleText('OpenURL: ' + data.url);
|
||||
if (data.url) { mesh.SendCommand({ "action": "msg", "type":"openUrl", "url": data.url, "sessionid": data.sessionid, "success": (openUserDesktopUrl(data.url) != null) }); }
|
||||
if (data.url) { mesh.SendCommand({ "action": "msg", "type": "openUrl", "url": data.url, "sessionid": data.sessionid, "success": (openUserDesktopUrl(data.url) != null) }); }
|
||||
break;
|
||||
}
|
||||
case 'getclip': {
|
||||
@ -798,6 +776,7 @@ function createMeshCore(agent)
|
||||
// Display a toast message
|
||||
if (data.title && data.msg) {
|
||||
MeshServerLog('Displaying toast message, title=' + data.title + ', message=' + data.msg, data);
|
||||
data.msg = data.msg.split('\r').join('\\r').split('\n').join('\\n');
|
||||
try { require('toaster').Toast(data.title, data.msg); } catch (ex) { }
|
||||
}
|
||||
break;
|
||||
@ -830,13 +809,25 @@ function createMeshCore(agent)
|
||||
}
|
||||
case 'ping': { mesh.SendCommand('{"action":"pong"}'); break; }
|
||||
case 'pong': { break; }
|
||||
case 'plugin': {
|
||||
if (typeof data.pluginaction == 'string') {
|
||||
try {
|
||||
MeshServerLog('Plugin called', data);
|
||||
// Not yet implemented
|
||||
// require(data.plugin.name).serveraction(data);
|
||||
} catch (e) {
|
||||
MeshServerLog('Error calling plugin', data);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
// Unknown action, ignore it.
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Called when a file changed in the file system
|
||||
/*
|
||||
function onFileWatcher(a, b) {
|
||||
@ -888,8 +879,8 @@ function createMeshCore(agent)
|
||||
pr.then(defragResult, defragResult);
|
||||
} else {
|
||||
*/
|
||||
results.hash = require('SHA384Stream').create().syncHash(JSON.stringify(results)).toString('hex');
|
||||
func(results);
|
||||
results.hash = require('SHA384Stream').create().syncHash(JSON.stringify(results)).toString('hex');
|
||||
func(results);
|
||||
//}
|
||||
} catch (ex) { func(null, ex); }
|
||||
}
|
||||
@ -933,7 +924,7 @@ function createMeshCore(agent)
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
|
||||
// Tunnel callback operations
|
||||
function onTunnelUpgrade(response, s, head) {
|
||||
this.s = s;
|
||||
@ -989,7 +980,7 @@ function createMeshCore(agent)
|
||||
peerTunnel.s.first = true;
|
||||
peerTunnel.s.resume();
|
||||
}
|
||||
|
||||
|
||||
// Called when we get data from the server for a TCP relay (We have to skip the first received 'c' and pipe the rest)
|
||||
function onTcpRelayServerTunnelData(data) {
|
||||
if (this.first == true) { this.first = false; this.pipe(this.tcprelay); } // Pipe Server --> Target
|
||||
@ -999,7 +990,7 @@ function createMeshCore(agent)
|
||||
if (tunnels[this.httprequest.index] == null) return; // Stop duplicate calls.
|
||||
//sendConsoleText("Tunnel #" + this.httprequest.index + " closed.", this.httprequest.sessionid);
|
||||
delete tunnels[this.httprequest.index];
|
||||
|
||||
|
||||
/*
|
||||
// Close the watcher if required
|
||||
if (this.httprequest.watcher != undefined) {
|
||||
@ -1031,7 +1022,7 @@ function createMeshCore(agent)
|
||||
function onTunnelData(data) {
|
||||
//console.log("OnTunnelData");
|
||||
//sendConsoleText('OnTunnelData, ' + data.length + ', ' + typeof data + ', ' + data);
|
||||
|
||||
|
||||
// If this is upload data, save it to file
|
||||
if (this.httprequest.uploadFile) {
|
||||
try { fs.writeSync(this.httprequest.uploadFile, data); } catch (e) { this.write(new Buffer(JSON.stringify({ action: 'uploaderror' }))); return; } // Write to the file, if there is a problem, error out.
|
||||
@ -1068,17 +1059,14 @@ function createMeshCore(agent)
|
||||
return;
|
||||
}
|
||||
|
||||
this.end = function ()
|
||||
{
|
||||
if (process.platform == "win32")
|
||||
{
|
||||
this.end = function () {
|
||||
if (process.platform == "win32") {
|
||||
// Unpipe the web socket
|
||||
this.unpipe(this.httprequest._term);
|
||||
if (this.httprequest._term) { this.httprequest._term.unpipe(this); }
|
||||
|
||||
// Unpipe the WebRTC channel if needed (This will also be done when the WebRTC channel ends).
|
||||
if (this.rtcchannel)
|
||||
{
|
||||
if (this.rtcchannel) {
|
||||
this.rtcchannel.unpipe(this.httprequest._term);
|
||||
if (this.httprequest._term) { this.httprequest._term.unpipe(this.rtcchannel); }
|
||||
}
|
||||
@ -1086,27 +1074,21 @@ function createMeshCore(agent)
|
||||
// Clean up
|
||||
if (this.httprequest._term) { this.httprequest._term.end(); }
|
||||
this.httprequest._term = null;
|
||||
} else
|
||||
{
|
||||
} else {
|
||||
// TODO!!
|
||||
}
|
||||
};
|
||||
|
||||
// Remote terminal using native pipes
|
||||
if (process.platform == "win32")
|
||||
{
|
||||
try
|
||||
{
|
||||
if ((this.httprequest.protocol == 6) && (require('win-terminal').PowerShellCapable() == true))
|
||||
{
|
||||
if (process.platform == "win32") {
|
||||
try {
|
||||
if ((this.httprequest.protocol == 6) && (require('win-terminal').PowerShellCapable() == true)) {
|
||||
this.httprequest._term = require('win-terminal').StartPowerShell(80, 25);
|
||||
} else
|
||||
{
|
||||
} else {
|
||||
this.httprequest._term = require('win-terminal').Start(80, 25);
|
||||
}
|
||||
}
|
||||
catch(e)
|
||||
{
|
||||
catch (e) {
|
||||
MeshServerLog('Failed to start remote terminal session, ' + e.toString() + ' (' + this.httprequest.remoteaddr + ')', this.httprequest);
|
||||
this.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: e.toString() }));
|
||||
this.end();
|
||||
@ -1115,14 +1097,11 @@ function createMeshCore(agent)
|
||||
this.httprequest._term.pipe(this, { dataTypeSkip: 1 });
|
||||
this.pipe(this.httprequest._term, { dataTypeSkip: 1, end: false });
|
||||
this.prependListener('end', function () { this.httprequest._term.end(function () { console.log('Terminal was closed'); }); });
|
||||
} else
|
||||
{
|
||||
if (fs.existsSync("/bin/bash"))
|
||||
{
|
||||
} else {
|
||||
if (fs.existsSync("/bin/bash")) {
|
||||
this.httprequest.process = childProcess.execFile("/bin/bash", ["bash", "-i"], { type: childProcess.SpawnTypes.TERM });
|
||||
if (process.platform == 'linux') { this.httprequest.process.stdin.write("alias ls='ls --color=auto'\nclear\n"); }
|
||||
} else
|
||||
{
|
||||
} else {
|
||||
this.httprequest.process = childProcess.execFile("/bin/sh", ["sh"], { type: childProcess.SpawnTypes.TERM });
|
||||
if (process.platform == 'linux') { this.httprequest.process.stdin.write("stty erase ^H\nalias ls='ls --color=auto'\nPS1='\\u@\\h:\\w\\$ '\nclear\n"); }
|
||||
}
|
||||
@ -1139,7 +1118,7 @@ function createMeshCore(agent)
|
||||
// User Consent Prompt is required
|
||||
// Send a console message back using the console channel, "\n" is supported.
|
||||
this.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: 'Waiting for user to grant access...' }));
|
||||
var pr = require('message-box').create('MeshCentral', this.httprequest.username + ' requesting Terminal Access. Grant access?', 10);
|
||||
var pr = require('message-box').create('MeshCentral', this.httprequest.username + ' requesting Terminal Access. Grant access?', 30);
|
||||
pr.ws = this;
|
||||
this.pause();
|
||||
|
||||
@ -1175,8 +1154,7 @@ function createMeshCore(agent)
|
||||
this.removeAllListeners('data');
|
||||
this.on('data', onTunnelControlData);
|
||||
//this.write('MeshCore Terminal Hello');
|
||||
} else if (this.httprequest.protocol == 2)
|
||||
{
|
||||
} else if (this.httprequest.protocol == 2) {
|
||||
// Check user access rights for desktop
|
||||
if (((this.httprequest.rights & MESHRIGHT_REMOTECONTROL) == 0) && ((this.httprequest.rights & MESHRIGHT_REMOTEVIEW) == 0)) {
|
||||
// Disengage this tunnel, user does not have the rights to do this!!
|
||||
@ -1210,7 +1188,7 @@ function createMeshCore(agent)
|
||||
{
|
||||
// Display a toast message. This may not be supported on all platforms.
|
||||
// try { require('toaster').Toast('MeshCentral', 'Remote Desktop Control Ended.'); } catch (ex) { }
|
||||
|
||||
|
||||
this.httprequest.desktop.kvm.end();
|
||||
if(this.httprequest.desktop.kvm.connectionBar)
|
||||
{
|
||||
@ -1266,18 +1244,16 @@ function createMeshCore(agent)
|
||||
}
|
||||
|
||||
// Perform notification if needed. Toast messages may not be supported on all platforms.
|
||||
if (this.httprequest.consent && (this.httprequest.consent & 8))
|
||||
{
|
||||
if (this.httprequest.consent && (this.httprequest.consent & 8)) {
|
||||
// User Consent Prompt is required
|
||||
// Send a console message back using the console channel, "\n" is supported.
|
||||
this.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: 'Waiting for user to grant access...' }));
|
||||
var pr = require('message-box').create('MeshCentral', this.httprequest.username + ' requesting KVM Access. Grant access?', 10);
|
||||
var pr = require('message-box').create('MeshCentral', this.httprequest.username + ' requesting KVM Access. Grant access?', 30);
|
||||
pr.ws = this;
|
||||
this.pause();
|
||||
|
||||
pr.then(
|
||||
function ()
|
||||
{
|
||||
function () {
|
||||
// Success
|
||||
MeshServerLog('Starting remote desktop after local user accepted (' + this.ws.httprequest.remoteaddr + ')', this.ws.httprequest);
|
||||
this.ws.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: null }));
|
||||
@ -1310,15 +1286,13 @@ function createMeshCore(agent)
|
||||
this.ws.httprequest.desktop.kvm.pipe(this.ws, { dataTypeSkip: 1 });
|
||||
this.ws.resume();
|
||||
},
|
||||
function (e)
|
||||
{
|
||||
function (e) {
|
||||
// User Consent Denied/Failed
|
||||
MeshServerLog('Failed to start remote desktop after local user rejected (' + this.ws.httprequest.remoteaddr + ')', this.ws.httprequest);
|
||||
this.ws.end(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: e.toString() }));
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
else {
|
||||
// User Consent Prompt is not required
|
||||
if (this.httprequest.consent && (this.httprequest.consent & 1))
|
||||
{
|
||||
@ -1369,12 +1343,11 @@ function createMeshCore(agent)
|
||||
}
|
||||
|
||||
// Perform notification if needed. Toast messages may not be supported on all platforms.
|
||||
if (this.httprequest.consent && (this.httprequest.consent & 32))
|
||||
{
|
||||
if (this.httprequest.consent && (this.httprequest.consent & 32)) {
|
||||
// User Consent Prompt is required
|
||||
// Send a console message back using the console channel, "\n" is supported.
|
||||
this.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: 'Waiting for user to grant access...' }));
|
||||
var pr = require('message-box').create('MeshCentral', this.httprequest.username + ' requesting remote file access. Grant access?', 10);
|
||||
var pr = require('message-box').create('MeshCentral', this.httprequest.username + ' requesting remote file access. Grant access?', 30);
|
||||
pr.ws = this;
|
||||
this.pause();
|
||||
|
||||
@ -1447,7 +1420,7 @@ function createMeshCore(agent)
|
||||
var response = getDirectoryInfo(cmd.path);
|
||||
if (cmd.reqid != undefined) { response.reqid = cmd.reqid; }
|
||||
this.write(new Buffer(JSON.stringify(response)));
|
||||
|
||||
|
||||
/*
|
||||
// Start the directory watcher
|
||||
if ((cmd.path != '') && (samepath == false)) {
|
||||
@ -1565,6 +1538,23 @@ function createMeshCore(agent)
|
||||
// Unknown action, ignore it.
|
||||
break;
|
||||
}
|
||||
} else if (this.httprequest.protocol == 7) { // plugin data exchange
|
||||
var cmd = null;
|
||||
try { cmd = JSON.parse(data); } catch (e) { };
|
||||
if (cmd == null) { return; }
|
||||
if ((cmd.ctrlChannel == '102938') || ((cmd.type == 'offer') && (cmd.sdp != null))) { onTunnelControlData(cmd, this); return; } // If this is control data, handle it now.
|
||||
if (cmd.action == undefined) return;
|
||||
|
||||
switch (cmd.action) {
|
||||
case 'plugin': {
|
||||
try { require(cmd.plugin).consoleaction(cmd, null, null, this); } catch (e) { throw e; }
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
// probably shouldn't happen, but just in case this feature is expanded
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
//sendConsoleText("Got tunnel #" + this.httprequest.index + " data: " + data, this.httprequest.sessionid);
|
||||
}
|
||||
@ -1639,12 +1629,10 @@ function createMeshCore(agent)
|
||||
} else if (obj.type == 'webrtc0') { // Browser indicates we can start WebRTC switch-over.
|
||||
if (ws.httprequest.protocol == 1) { // Terminal
|
||||
// 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
|
||||
if (process.platform == 'win32')
|
||||
{
|
||||
if (process.platform == 'win32') {
|
||||
ws.httprequest._term.unpipe(ws);
|
||||
}
|
||||
else
|
||||
{
|
||||
else {
|
||||
ws.httprequest.process.stdout.unpipe(ws);
|
||||
ws.httprequest.process.stderr.unpipe(ws);
|
||||
}
|
||||
@ -1661,13 +1649,11 @@ function createMeshCore(agent)
|
||||
} else if (obj.type == 'webrtc1') {
|
||||
if ((ws.httprequest.protocol == 1) || (ws.httprequest.protocol == 6)) { // Terminal
|
||||
// Switch the user input from websocket to webrtc at this point.
|
||||
if (process.platform == 'win32')
|
||||
{
|
||||
if (process.platform == 'win32') {
|
||||
ws.unpipe(ws.httprequest._term);
|
||||
ws.rtcchannel.pipe(ws.httprequest._term, { dataTypeSkip: 1 }); // 0 = Binary, 1 = Text.
|
||||
}
|
||||
else
|
||||
{
|
||||
else {
|
||||
ws.unpipe(ws.httprequest.process.stdin);
|
||||
ws.rtcchannel.pipe(ws.httprequest.process.stdin, { dataTypeSkip: 1 }); // 0 = Binary, 1 = Text.
|
||||
}
|
||||
@ -1682,12 +1668,10 @@ function createMeshCore(agent)
|
||||
} else if (obj.type == 'webrtc2') {
|
||||
// Other side received websocket end of data marker, start sending data on WebRTC channel
|
||||
if ((ws.httprequest.protocol == 1) || (ws.httprequest.protocol == 6)) { // Terminal
|
||||
if (process.platform == 'win32')
|
||||
{
|
||||
if (process.platform == 'win32') {
|
||||
ws.httprequest._term.pipe(ws.webrtc.rtcchannel, { dataTypeSkip: 1, end: false }); // 0 = Binary, 1 = Text.
|
||||
}
|
||||
else
|
||||
{
|
||||
else {
|
||||
ws.httprequest.process.stdout.pipe(ws.webrtc.rtcchannel, { dataTypeSkip: 1, end: false }); // 0 = Binary, 1 = Text.
|
||||
ws.httprequest.process.stderr.pipe(ws.webrtc.rtcchannel, { dataTypeSkip: 1, end: false }); // 0 = Binary, 1 = Text.
|
||||
}
|
||||
@ -1727,7 +1711,7 @@ function createMeshCore(agent)
|
||||
// Console state
|
||||
var consoleWebSockets = {};
|
||||
var consoleHttpRequest = null;
|
||||
|
||||
|
||||
// Console HTTP response
|
||||
function consoleHttpResponse(response) {
|
||||
response.data = function (data) { sendConsoleText(rstr2hex(buf2rstr(data)), this.sessionid); consoleHttpRequest = null; }
|
||||
@ -1770,7 +1754,7 @@ function createMeshCore(agent)
|
||||
var response = null;
|
||||
switch (cmd) {
|
||||
case 'help': { // Displays available commands
|
||||
var fin = '', f = '', availcommands = 'help,info,osinfo,args,print,type,dbget,dbset,dbcompact,eval,parseuri,httpget,nwslist,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';
|
||||
var fin = '', f = '', availcommands = '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';
|
||||
availcommands = availcommands.split(',').sort();
|
||||
while (availcommands.length > 0) {
|
||||
if (f.length > 100) { fin += (f + ',\r\n'); f = ''; }
|
||||
@ -1875,8 +1859,7 @@ function createMeshCore(agent)
|
||||
break;
|
||||
}
|
||||
case 'toast': {
|
||||
if (args['_'].length < 1) { response = 'Proper usage: toast "message"'; } else
|
||||
{
|
||||
if (args['_'].length < 1) { response = 'Proper usage: toast "message"'; } else {
|
||||
require('toaster').Toast('MeshCentral', args['_'][0]).then(sendConsoleText, sendConsoleText);
|
||||
}
|
||||
break;
|
||||
@ -1889,7 +1872,7 @@ function createMeshCore(agent)
|
||||
case 'ps': {
|
||||
processManager.getProcesses(function (plist) {
|
||||
var x = '';
|
||||
for (var i in plist) { x += i + ', ' + plist[i].cmd + ((plist[i].user) ? (', ' + plist[i].user):'') + '\r\n'; }
|
||||
for (var i in plist) { x += i + ', ' + plist[i].cmd + ((plist[i].user) ? (', ' + plist[i].user) : '') + '\r\n'; }
|
||||
sendConsoleText(x, sessionid);
|
||||
});
|
||||
break;
|
||||
@ -1923,14 +1906,11 @@ function createMeshCore(agent)
|
||||
break;
|
||||
}
|
||||
case 'dump':
|
||||
if (args['_'].length < 1)
|
||||
{
|
||||
if (args['_'].length < 1) {
|
||||
response = 'Proper usage: dump [on/off/status]'; // Display correct command usage
|
||||
}
|
||||
else
|
||||
{
|
||||
switch(args['_'][0].toLowerCase())
|
||||
{
|
||||
else {
|
||||
switch (args['_'][0].toLowerCase()) {
|
||||
case 'on':
|
||||
process.coreDumpLocation = process.platform == 'win32' ? process.execPath.replace('.exe', '.dmp') : (process.execPath + '.dmp');
|
||||
response = 'enabled';
|
||||
@ -1940,12 +1920,10 @@ function createMeshCore(agent)
|
||||
response = 'disabled';
|
||||
break;
|
||||
case 'status':
|
||||
if (process.coreDumpLocation)
|
||||
{
|
||||
if (process.coreDumpLocation) {
|
||||
response = 'Core Dump: [ENABLED' + (require('fs').existsSync(process.coreDumpLocation) ? (', (DMP file exists)]') : (']'));
|
||||
}
|
||||
else
|
||||
{
|
||||
else {
|
||||
response = 'Core Dump: [DISABLED]';
|
||||
}
|
||||
break;
|
||||
@ -1965,18 +1943,14 @@ function createMeshCore(agent)
|
||||
}
|
||||
case 'uninstallagent':
|
||||
var agentName = process.platform == 'win32' ? 'Mesh Agent' : 'meshagent';
|
||||
if (!require('service-manager').manager.getService(agentName).isMe())
|
||||
{
|
||||
if (!require('service-manager').manager.getService(agentName).isMe()) {
|
||||
response = 'Uininstall failed, this instance is not the service instance';
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
else {
|
||||
try {
|
||||
diagnosticAgent_uninstall();
|
||||
}
|
||||
catch(x)
|
||||
{
|
||||
catch (x) {
|
||||
}
|
||||
var js = "require('service-manager').manager.getService('" + agentName + "').stop(); require('service-manager').manager.uninstallService('" + agentName + "'); process.exit();";
|
||||
this.child = require('child_process').execFile(process.execPath, [process.platform == 'win32' ? (process.execPath.split('\\').pop()) : (process.execPath.split('/').pop()), '-b64exec', Buffer.from(js).toString('base64')], { type: 4, detached: true });
|
||||
@ -2155,7 +2129,7 @@ function createMeshCore(agent)
|
||||
if (httprequest != null) {
|
||||
httprequest.upgrade = onWebSocketUpgrade;
|
||||
httprequest.on('error', function (e) { sendConsoleText('ERROR: ' + JSON.stringify(e)); });
|
||||
|
||||
|
||||
var index = 1;
|
||||
while (consoleWebSockets[index]) { index++; }
|
||||
httprequest.sessionid = sessionid;
|
||||
@ -2343,40 +2317,31 @@ function createMeshCore(agent)
|
||||
}
|
||||
case 'diagnostic':
|
||||
{
|
||||
if (!mesh.DAIPC.listening)
|
||||
{
|
||||
if (!mesh.DAIPC.listening) {
|
||||
response = 'Unable to bind to Diagnostic IPC, most likely because the path (' + process.cwd() + ') is not on a local file system';
|
||||
break;
|
||||
}
|
||||
var diag = diagnosticAgent_installCheck();
|
||||
if (diag)
|
||||
{
|
||||
if (args['_'].length == 1 && args['_'][0] == 'uninstall')
|
||||
{
|
||||
if (diag) {
|
||||
if (args['_'].length == 1 && args['_'][0] == 'uninstall') {
|
||||
diagnosticAgent_uninstall();
|
||||
response = 'Diagnostic Agent uninstalled';
|
||||
}
|
||||
else
|
||||
{
|
||||
else {
|
||||
response = 'Diagnostic Agent installed at: ' + diag.appLocation();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (args['_'].length == 1 && args['_'][0] == 'install')
|
||||
{
|
||||
else {
|
||||
if (args['_'].length == 1 && args['_'][0] == 'install') {
|
||||
diag = diagnosticAgent_installCheck(true);
|
||||
if (diag)
|
||||
{
|
||||
if (diag) {
|
||||
response = 'Diagnostic agent was installed at: ' + diag.appLocation();
|
||||
}
|
||||
else
|
||||
{
|
||||
else {
|
||||
response = 'Diagnostic agent installation failed';
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
else {
|
||||
response = 'Diagnostic Agent Not installed. To install: diagnostic install';
|
||||
}
|
||||
}
|
||||
@ -2384,27 +2349,27 @@ function createMeshCore(agent)
|
||||
break;
|
||||
}
|
||||
case 'apf': {
|
||||
if (meshCoreObj.intelamt!==null) {
|
||||
if (meshCoreObj.intelamt !== null) {
|
||||
if (args['_'].length == 1) {
|
||||
if (args['_'][0] == 'on') {
|
||||
response = 'Starting APF tunnel'
|
||||
var apfarg = {
|
||||
mpsurl: mesh.ServerUrl.replace('agent.ashx','apf.ashx'),
|
||||
mpsuser: Buffer.from(mesh.ServerInfo.MeshID,'hex').toString('base64').substring(0,16),
|
||||
mpspass: Buffer.from(mesh.ServerInfo.MeshID,'hex').toString('base64').substring(0,16),
|
||||
mpsurl: mesh.ServerUrl.replace('agent.ashx', 'apf.ashx'),
|
||||
mpsuser: Buffer.from(mesh.ServerInfo.MeshID, 'hex').toString('base64').substring(0, 16),
|
||||
mpspass: Buffer.from(mesh.ServerInfo.MeshID, 'hex').toString('base64').substring(0, 16),
|
||||
mpskeepalive: 60000,
|
||||
clientname: require('os').hostname(),
|
||||
clientaddress: '127.0.0.1',
|
||||
clientuuid: meshCoreObj.intelamt.uuid
|
||||
};
|
||||
var tobj = { debug: false }; //
|
||||
apftunnel= require('apfclient')(tobj,apfarg);
|
||||
apftunnel = require('apfclient')(tobj, apfarg);
|
||||
try {
|
||||
apftunnel.connect();
|
||||
response += "..success";
|
||||
} catch (e) {
|
||||
response += JSON.stringify(e);
|
||||
}
|
||||
}
|
||||
} else if (args['_'][0] == 'off') {
|
||||
response = 'Stopping APF tunnel';
|
||||
try {
|
||||
@ -2413,19 +2378,34 @@ function createMeshCore(agent)
|
||||
} catch (e) {
|
||||
response += JSON.stringify(e);
|
||||
}
|
||||
apftunnel=null;
|
||||
apftunnel = null;
|
||||
} else {
|
||||
response = 'Invalid command.\r\nCmd syntax: apf on|off';
|
||||
}
|
||||
} else {
|
||||
response = 'APF tunnel is '+ (apftunnel == null ? 'off': 'on' );
|
||||
} else {
|
||||
response = 'APF tunnel is ' + (apftunnel == null ? 'off' : 'on');
|
||||
}
|
||||
|
||||
|
||||
} else {
|
||||
response = 'APF tunnel requires Intel AMT';
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'plugin': {
|
||||
if (typeof args['_'][0] == 'string') {
|
||||
try {
|
||||
// Pass off the action to the plugin
|
||||
// for plugin creators, you'll want to have a plugindir/modules_meshcore/plugin.js
|
||||
// to control the output / actions here.
|
||||
response = require(args['_'][0]).consoleaction(args, rights, sessionid, mesh);
|
||||
} catch (e) {
|
||||
response = 'There was an error in the plugin (' + e + ')';
|
||||
}
|
||||
} else {
|
||||
response = 'Proper usage: plugin [pluginName] [args].';
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: { // This is an unknown command, return an error message
|
||||
response = 'Unknown command \"' + cmd + '\", type \"help\" for list of avaialble commands.';
|
||||
break;
|
||||
@ -2434,7 +2414,7 @@ function createMeshCore(agent)
|
||||
} catch (e) { response = 'Command returned an exception error: ' + e; console.log(e); }
|
||||
if (response != null) { sendConsoleText(response, sessionid); }
|
||||
}
|
||||
|
||||
|
||||
// Send a mesh agent console command
|
||||
function sendConsoleText(text, sessionid) {
|
||||
if (typeof text == 'object') { text = JSON.stringify(text); }
|
||||
@ -2443,7 +2423,7 @@ function createMeshCore(agent)
|
||||
|
||||
// Called before the process exits
|
||||
//process.exit = function (code) { console.log("Exit with code: " + code.toString()); }
|
||||
|
||||
|
||||
// Called when the server connection state changes
|
||||
function handleServerConnection(state) {
|
||||
meshServerConnectionState = state;
|
||||
@ -2468,13 +2448,13 @@ function createMeshCore(agent)
|
||||
if (selfInfoUpdateTimer == null) { selfInfoUpdateTimer = setInterval(sendPeriodicServerUpdate, 1200000); } // 20 minutes
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Update the server with the latest network interface information
|
||||
var sendNetworkUpdateNagleTimer = null;
|
||||
function sendNetworkUpdateNagle() { if (sendNetworkUpdateNagleTimer != null) { clearTimeout(sendNetworkUpdateNagleTimer); sendNetworkUpdateNagleTimer = null; } sendNetworkUpdateNagleTimer = setTimeout(sendNetworkUpdate, 5000); }
|
||||
function sendNetworkUpdate(force) {
|
||||
sendNetworkUpdateNagleTimer = null;
|
||||
|
||||
|
||||
// Update the network interfaces information data
|
||||
var netInfo = mesh.NetInfo;
|
||||
if (netInfo) {
|
||||
@ -2483,7 +2463,7 @@ function createMeshCore(agent)
|
||||
if ((force == true) || (clearGatewayMac(netInfoStr) != clearGatewayMac(lastNetworkInfo))) { mesh.SendCommand(netInfo); lastNetworkInfo = netInfoStr; }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Called periodically to check if we need to send updates to the server
|
||||
function sendPeriodicServerUpdate(flags) {
|
||||
if (meshServerConnectionState == 0) return; // Not connected to server, do nothing.
|
||||
@ -2538,8 +2518,10 @@ function createMeshCore(agent)
|
||||
//if (process.platform == 'win32') { try { pr = require('win-info').pendingReboot(); } catch (ex) { pr = null; } } // Pending reboot
|
||||
if ((meshCoreObj.av == null) || (JSON.stringify(meshCoreObj.av) != JSON.stringify(av))) { meshCoreObj.av = av; mesh.SendCommand(meshCoreObj); }
|
||||
}
|
||||
|
||||
// TODO: add plugin hook here
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Starting function
|
||||
obj.start = function () {
|
||||
@ -2572,16 +2554,16 @@ function createMeshCore(agent)
|
||||
//userSession.on('unlocked', function (user) { sendConsoleText('[' + (user.Domain ? user.Domain + '\\' : '') + user.Username + '] has UNLOCKED the desktop'); });
|
||||
} catch (ex) { }
|
||||
}
|
||||
|
||||
|
||||
obj.stop = function () {
|
||||
mesh.AddCommandHandler(null);
|
||||
mesh.AddConnectHandler(null);
|
||||
}
|
||||
|
||||
|
||||
function onWebSocketClosed() { sendConsoleText("WebSocket #" + this.httprequest.index + " closed.", this.httprequest.sessionid); delete consoleWebSockets[this.httprequest.index]; }
|
||||
function onWebSocketData(data) { sendConsoleText("Got WebSocket #" + this.httprequest.index + " data: " + data, this.httprequest.sessionid); }
|
||||
function onWebSocketSendOk() { sendConsoleText("WebSocket #" + this.index + " SendOK.", this.sessionid); }
|
||||
|
||||
|
||||
function onWebSocketUpgrade(response, s, head) {
|
||||
sendConsoleText("WebSocket #" + this.index + " connected.", this.sessionid);
|
||||
this.s = s;
|
||||
|
360
agents/meshcore.min.js
vendored
360
agents/meshcore.min.js
vendored
@ -37,12 +37,10 @@ var MESHRIGHT_NOFILES = 1024;
|
||||
var MESHRIGHT_NOAMT = 2048;
|
||||
var MESHRIGHT_LIMITEDINPUT = 4096;
|
||||
|
||||
function createMeshCore(agent)
|
||||
{
|
||||
function createMeshCore(agent) {
|
||||
var obj = {};
|
||||
|
||||
if (process.platform == 'darwin' && !process.versions)
|
||||
{
|
||||
if (process.platform == 'darwin' && !process.versions) {
|
||||
// This is an older MacOS Agent, so we'll need to check the service definition so that Auto-Update will function correctly
|
||||
var child = require('child_process').execFile('/bin/sh', ['sh']);
|
||||
child.stdout.str = '';
|
||||
@ -51,21 +49,18 @@ function createMeshCore(agent)
|
||||
child.stdin.write(" if(c[1]==\"dict\"){ split(a[2], d, \"</dict>\"); if(split(d[1], truval, \"<true/>\")>1) { split(truval[1], kn1, \"<key>\"); split(kn1[2], kn2, \"</key>\"); print kn2[1]; } }");
|
||||
child.stdin.write(" else { split(c[1], ka, \"/\"); if(ka[1]==\"true\") {print \"ALWAYS\";} } }'\nexit\n");
|
||||
child.waitExit();
|
||||
if (child.stdout.str.trim() == 'Crashed')
|
||||
{
|
||||
if (child.stdout.str.trim() == 'Crashed') {
|
||||
child = require('child_process').execFile('/bin/sh', ['sh']);
|
||||
child.stdout.str = '';
|
||||
child.stdout.on('data', function (chunk) { this.str += chunk.toString(); });
|
||||
child.stdin.write("launchctl list | grep 'meshagent' | awk '{ if($3==\"meshagent\"){print $1;}}'\nexit\n");
|
||||
child.waitExit();
|
||||
|
||||
if (parseInt(child.stdout.str.trim()) == process.pid)
|
||||
{
|
||||
if (parseInt(child.stdout.str.trim()) == process.pid) {
|
||||
// The currently running MeshAgent is us, so we can continue with the update
|
||||
var plist = require('fs').readFileSync('/Library/LaunchDaemons/meshagent_osx64_LaunchDaemon.plist').toString();
|
||||
var tokens = plist.split('<key>KeepAlive</key>');
|
||||
if (tokens[1].split('>')[0].split('<')[1] == 'dict')
|
||||
{
|
||||
if (tokens[1].split('>')[0].split('<')[1] == 'dict') {
|
||||
var tmp = tokens[1].split('</dict>');
|
||||
tmp.shift();
|
||||
tokens[1] = '\n <true/>' + tmp.join('</dict>');
|
||||
@ -125,10 +120,8 @@ function createMeshCore(agent)
|
||||
if (process.platform != 'win32') { try { require('fs').unlinkSync(process.cwd() + '/DAIPC'); } catch (ee) { } }
|
||||
obj.DAIPC.IPCPATH = process.platform == 'win32' ? ('\\\\.\\pipe\\' + require('_agentNodeId')() + '-DAIPC') : (process.cwd() + '/DAIPC');
|
||||
try { obj.DAIPC.listen({ path: obj.DAIPC.IPCPATH }); } catch (e) { }
|
||||
obj.DAIPC.on('connection', function (c)
|
||||
{
|
||||
c._send = function (j)
|
||||
{
|
||||
obj.DAIPC.on('connection', function (c) {
|
||||
c._send = function (j) {
|
||||
var data = JSON.stringify(j);
|
||||
var packet = Buffer.alloc(data.length + 4);
|
||||
packet.writeUInt32LE(data.length + 4, 0);
|
||||
@ -138,32 +131,26 @@ function createMeshCore(agent)
|
||||
this._daipc = c;
|
||||
c.parent = this;
|
||||
c.on('end', function () { console.log('Connection Closed'); this.parent._daipc = null; });
|
||||
c.on('data', function (chunk)
|
||||
{
|
||||
c.on('data', function (chunk) {
|
||||
if (chunk.length < 4) { this.unshift(chunk); return; }
|
||||
var len = chunk.readUInt32LE(0);
|
||||
if (len > 8192) { this.parent._daipc = null; this.end(); return; }
|
||||
if (chunk.length < len) { this.unshift(chunk); return; }
|
||||
|
||||
|
||||
var data = chunk.slice(4, len);
|
||||
try
|
||||
{
|
||||
try {
|
||||
data = JSON.parse(data.toString());
|
||||
}
|
||||
catch(de)
|
||||
{
|
||||
catch (de) {
|
||||
this.parent._daipc = null; this.end(); return;
|
||||
}
|
||||
|
||||
|
||||
if (!data.cmd) { this.parent._daipc = null; this.end(); return; }
|
||||
|
||||
try
|
||||
{
|
||||
switch(data.cmd)
|
||||
{
|
||||
try {
|
||||
switch (data.cmd) {
|
||||
case 'query':
|
||||
switch(data.value)
|
||||
{
|
||||
switch (data.value) {
|
||||
case 'connection':
|
||||
data.result = require('MeshAgent').ConnectedServer;
|
||||
this._send(data);
|
||||
@ -176,32 +163,26 @@ function createMeshCore(agent)
|
||||
return;
|
||||
}
|
||||
}
|
||||
catch(xe)
|
||||
{
|
||||
catch (xe) {
|
||||
this.parent._daipc = null; this.end(); return;
|
||||
}
|
||||
});
|
||||
});
|
||||
function diagnosticAgent_uninstall()
|
||||
{
|
||||
function diagnosticAgent_uninstall() {
|
||||
require('service-manager').manager.uninstallService('meshagentDiagnostic');
|
||||
require('task-scheduler').delete('meshagentDiagnostic/periodicStart');
|
||||
};
|
||||
function diagnosticAgent_installCheck(install)
|
||||
{
|
||||
try
|
||||
{
|
||||
function diagnosticAgent_installCheck(install) {
|
||||
try {
|
||||
var diag = require('service-manager').manager.getService('meshagentDiagnostic');
|
||||
return (diag);
|
||||
}
|
||||
catch (e)
|
||||
{
|
||||
catch (e) {
|
||||
}
|
||||
if (!install) { return (null); }
|
||||
|
||||
var svc = null;
|
||||
try
|
||||
{
|
||||
try {
|
||||
require('service-manager').manager.installService(
|
||||
{
|
||||
name: 'meshagentDiagnostic',
|
||||
@ -213,8 +194,7 @@ function createMeshCore(agent)
|
||||
});
|
||||
svc = require('service-manager').manager.getService('meshagentDiagnostic');
|
||||
}
|
||||
catch (e)
|
||||
{
|
||||
catch (e) {
|
||||
return (null);
|
||||
}
|
||||
var proxyConfig = require('global-tunnel').proxyConfig;
|
||||
@ -227,12 +207,10 @@ function createMeshCore(agent)
|
||||
ddb.Put('MeshServer', require('MeshAgent').ServerInfo.ServerUri);
|
||||
if (cert.root.pfx) { ddb.Put('SelfNodeCert', cert.root.pfx); }
|
||||
if (cert.tls) { ddb.Put('SelfNodeTlsCert', cert.tls.pfx); }
|
||||
if (proxyConfig)
|
||||
{
|
||||
if (proxyConfig) {
|
||||
ddb.Put('WebProxy', proxyConfig.host + ':' + proxyConfig.port);
|
||||
}
|
||||
else
|
||||
{
|
||||
else {
|
||||
ddb.Put('ignoreProxyFile', '1');
|
||||
}
|
||||
|
||||
@ -242,7 +220,7 @@ function createMeshCore(agent)
|
||||
delete ddb;
|
||||
|
||||
// Set a recurrent task, to run the Diagnostic Agent every 2 days
|
||||
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'});
|
||||
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' });
|
||||
//require('task-scheduler').create({ name: 'meshagentDiagnostic/periodicStart', daily: '1', time: '17:16', service: 'meshagentDiagnostic' });
|
||||
|
||||
return (svc);
|
||||
@ -281,7 +259,7 @@ function createMeshCore(agent)
|
||||
}
|
||||
obj.borderManager = new borderController();
|
||||
*/
|
||||
|
||||
|
||||
// MeshAgent JavaScript Core Module. This code is sent to and running on the mesh agent.
|
||||
var meshCoreObj = { "action": "coreinfo", "value": "MeshCore v6", "caps": 14 }; // Capability bitmask: 1 = Desktop, 2 = Terminal, 4 = Files, 8 = Console, 16 = JavaScript, 32 = Temporary Agent, 64 = Recovery Agent
|
||||
|
||||
@ -397,7 +375,7 @@ function createMeshCore(agent)
|
||||
});
|
||||
}
|
||||
} catch (ex) { sendConsoleText("ex1: " + ex); }
|
||||
|
||||
|
||||
// Try to load up the WIFI scanner
|
||||
try {
|
||||
var wifiScannerLib = require('wifi-scanner');
|
||||
@ -436,7 +414,7 @@ function createMeshCore(agent)
|
||||
}
|
||||
catch (e) { return false; }
|
||||
}
|
||||
|
||||
|
||||
// Remove all Gateway MAC addresses for interface list. This is useful because the gateway MAC is not always populated reliably.
|
||||
function clearGatewayMac(str) {
|
||||
if (str == null) return null;
|
||||
@ -444,7 +422,7 @@ function createMeshCore(agent)
|
||||
for (var i in x.netif) { if (x.netif[i].gatewaymac) { delete x.netif[i].gatewaymac } }
|
||||
return JSON.stringify(x);
|
||||
}
|
||||
|
||||
|
||||
function getIpLocationData(func) {
|
||||
// Get the location information for the cache if possible
|
||||
var publicLocationInfo = db.Get('publicLocationInfo');
|
||||
@ -483,7 +461,7 @@ function createMeshCore(agent)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Polyfill String.endsWith
|
||||
if (!String.prototype.endsWith) {
|
||||
String.prototype.endsWith = function (searchString, position) {
|
||||
@ -494,7 +472,7 @@ function createMeshCore(agent)
|
||||
return lastIndex !== -1 && lastIndex === position;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
// Polyfill path.join
|
||||
obj.path = {
|
||||
join: function () {
|
||||
@ -513,19 +491,19 @@ function createMeshCore(agent)
|
||||
return x.join('/');
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// Replace a string with a number if the string is an exact number
|
||||
function toNumberIfNumber(x) { if ((typeof x == 'string') && (+parseInt(x) === x)) { x = parseInt(x); } return x; }
|
||||
|
||||
|
||||
// Convert decimal to hex
|
||||
function char2hex(i) { return (i + 0x100).toString(16).substr(-2).toUpperCase(); }
|
||||
|
||||
|
||||
// Convert a raw string to a hex string
|
||||
function rstr2hex(input) { var r = '', i; for (i = 0; i < input.length; i++) { r += char2hex(input.charCodeAt(i)); } return r; }
|
||||
|
||||
|
||||
// Convert a buffer into a string
|
||||
function buf2rstr(buf) { var r = ''; for (var i = 0; i < buf.length; i++) { r += String.fromCharCode(buf[i]); } return r; }
|
||||
|
||||
|
||||
// Convert a hex string to a raw string // TODO: Do this using Buffer(), will be MUCH faster
|
||||
function hex2rstr(d) {
|
||||
if (typeof d != "string" || d.length == 0) return '';
|
||||
@ -533,7 +511,7 @@ function createMeshCore(agent)
|
||||
while (t = m.shift()) r += String.fromCharCode('0x' + t);
|
||||
return r
|
||||
}
|
||||
|
||||
|
||||
// Convert an object to string with all functions
|
||||
function objToString(x, p, pad, ret) {
|
||||
if (ret == undefined) ret = '';
|
||||
@ -548,17 +526,17 @@ function createMeshCore(agent)
|
||||
for (var i in x) { if (i != '_ObjectID') { r += (addPad(p + 2, pad) + i + ': ' + objToString(x[i], p + 2, pad, ret) + (ret ? '\r\n' : ' ')); } }
|
||||
return r + addPad(p, pad) + '}';
|
||||
}
|
||||
|
||||
|
||||
// Return p number of spaces
|
||||
function addPad(p, ret) { var r = ''; for (var i = 0; i < p; i++) { r += ret; } return r; }
|
||||
|
||||
|
||||
// Split a string taking into account the quoats. Used for command line parsing
|
||||
function splitArgs(str) {
|
||||
var myArray = [], myRegexp = /[^\s"]+|"([^"]*)"/gi;
|
||||
do { var match = myRegexp.exec(str); if (match != null) { myArray.push(match[1] ? match[1] : match[0]); } } while (match != null);
|
||||
return myArray;
|
||||
}
|
||||
|
||||
|
||||
// Parse arguments string array into an object
|
||||
function parseArgs(argv) {
|
||||
var results = { '_': [] }, current = null;
|
||||
@ -574,7 +552,7 @@ function createMeshCore(agent)
|
||||
if (current != null) { results[current] = true; }
|
||||
return results;
|
||||
}
|
||||
|
||||
|
||||
// Get server target url with a custom path
|
||||
function getServerTargetUrl(path) {
|
||||
var x = mesh.ServerUrl;
|
||||
@ -585,13 +563,13 @@ function createMeshCore(agent)
|
||||
if (x == null) return null;
|
||||
return x.protocol + '//' + x.host + ':' + x.port + '/' + path;
|
||||
}
|
||||
|
||||
|
||||
// Get server url. If the url starts with "*/..." change it, it not use the url as is.
|
||||
function getServerTargetUrlEx(url) {
|
||||
if (url.substring(0, 2) == '*/') { return getServerTargetUrl(url.substring(2)); }
|
||||
return url;
|
||||
}
|
||||
|
||||
|
||||
// Send a wake-on-lan packet
|
||||
function sendWakeOnLan(hexMac) {
|
||||
var count = 0;
|
||||
@ -600,7 +578,7 @@ function createMeshCore(agent)
|
||||
var magic = 'FFFFFFFFFFFF';
|
||||
for (var x = 1; x <= 16; ++x) { magic += hexMac; }
|
||||
var magicbin = Buffer.from(magic, 'hex');
|
||||
|
||||
|
||||
for (var adapter in interfaces) {
|
||||
if (interfaces.hasOwnProperty(adapter)) {
|
||||
for (var i = 0; i < interfaces[adapter].length; ++i) {
|
||||
@ -618,7 +596,7 @@ function createMeshCore(agent)
|
||||
} catch (e) { }
|
||||
return count;
|
||||
}
|
||||
|
||||
|
||||
// Handle a mesh agent command
|
||||
function handleServerCommand(data) {
|
||||
if (typeof data == 'object') {
|
||||
@ -722,7 +700,7 @@ function createMeshCore(agent)
|
||||
// Open a local web browser and return success/fail
|
||||
MeshServerLog('Opening: ' + data.url, data);
|
||||
sendConsoleText('OpenURL: ' + data.url);
|
||||
if (data.url) { mesh.SendCommand({ "action": "msg", "type":"openUrl", "url": data.url, "sessionid": data.sessionid, "success": (openUserDesktopUrl(data.url) != null) }); }
|
||||
if (data.url) { mesh.SendCommand({ "action": "msg", "type": "openUrl", "url": data.url, "sessionid": data.sessionid, "success": (openUserDesktopUrl(data.url) != null) }); }
|
||||
break;
|
||||
}
|
||||
case 'getclip': {
|
||||
@ -798,6 +776,7 @@ function createMeshCore(agent)
|
||||
// Display a toast message
|
||||
if (data.title && data.msg) {
|
||||
MeshServerLog('Displaying toast message, title=' + data.title + ', message=' + data.msg, data);
|
||||
data.msg = data.msg.split('\r').join('\\r').split('\n').join('\\n');
|
||||
try { require('toaster').Toast(data.title, data.msg); } catch (ex) { }
|
||||
}
|
||||
break;
|
||||
@ -830,13 +809,25 @@ function createMeshCore(agent)
|
||||
}
|
||||
case 'ping': { mesh.SendCommand('{"action":"pong"}'); break; }
|
||||
case 'pong': { break; }
|
||||
case 'plugin': {
|
||||
if (typeof data.pluginaction == 'string') {
|
||||
try {
|
||||
MeshServerLog('Plugin called', data);
|
||||
// Not yet implemented
|
||||
// require(data.plugin.name).serveraction(data);
|
||||
} catch (e) {
|
||||
MeshServerLog('Error calling plugin', data);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
// Unknown action, ignore it.
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Called when a file changed in the file system
|
||||
/*
|
||||
function onFileWatcher(a, b) {
|
||||
@ -888,8 +879,8 @@ function createMeshCore(agent)
|
||||
pr.then(defragResult, defragResult);
|
||||
} else {
|
||||
*/
|
||||
results.hash = require('SHA384Stream').create().syncHash(JSON.stringify(results)).toString('hex');
|
||||
func(results);
|
||||
results.hash = require('SHA384Stream').create().syncHash(JSON.stringify(results)).toString('hex');
|
||||
func(results);
|
||||
//}
|
||||
} catch (ex) { func(null, ex); }
|
||||
}
|
||||
@ -933,7 +924,7 @@ function createMeshCore(agent)
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
|
||||
// Tunnel callback operations
|
||||
function onTunnelUpgrade(response, s, head) {
|
||||
this.s = s;
|
||||
@ -989,7 +980,7 @@ function createMeshCore(agent)
|
||||
peerTunnel.s.first = true;
|
||||
peerTunnel.s.resume();
|
||||
}
|
||||
|
||||
|
||||
// Called when we get data from the server for a TCP relay (We have to skip the first received 'c' and pipe the rest)
|
||||
function onTcpRelayServerTunnelData(data) {
|
||||
if (this.first == true) { this.first = false; this.pipe(this.tcprelay); } // Pipe Server --> Target
|
||||
@ -999,7 +990,7 @@ function createMeshCore(agent)
|
||||
if (tunnels[this.httprequest.index] == null) return; // Stop duplicate calls.
|
||||
//sendConsoleText("Tunnel #" + this.httprequest.index + " closed.", this.httprequest.sessionid);
|
||||
delete tunnels[this.httprequest.index];
|
||||
|
||||
|
||||
/*
|
||||
// Close the watcher if required
|
||||
if (this.httprequest.watcher != undefined) {
|
||||
@ -1031,7 +1022,7 @@ function createMeshCore(agent)
|
||||
function onTunnelData(data) {
|
||||
//console.log("OnTunnelData");
|
||||
//sendConsoleText('OnTunnelData, ' + data.length + ', ' + typeof data + ', ' + data);
|
||||
|
||||
|
||||
// If this is upload data, save it to file
|
||||
if (this.httprequest.uploadFile) {
|
||||
try { fs.writeSync(this.httprequest.uploadFile, data); } catch (e) { this.write(new Buffer(JSON.stringify({ action: 'uploaderror' }))); return; } // Write to the file, if there is a problem, error out.
|
||||
@ -1068,17 +1059,14 @@ function createMeshCore(agent)
|
||||
return;
|
||||
}
|
||||
|
||||
this.end = function ()
|
||||
{
|
||||
if (process.platform == "win32")
|
||||
{
|
||||
this.end = function () {
|
||||
if (process.platform == "win32") {
|
||||
// Unpipe the web socket
|
||||
this.unpipe(this.httprequest._term);
|
||||
if (this.httprequest._term) { this.httprequest._term.unpipe(this); }
|
||||
|
||||
// Unpipe the WebRTC channel if needed (This will also be done when the WebRTC channel ends).
|
||||
if (this.rtcchannel)
|
||||
{
|
||||
if (this.rtcchannel) {
|
||||
this.rtcchannel.unpipe(this.httprequest._term);
|
||||
if (this.httprequest._term) { this.httprequest._term.unpipe(this.rtcchannel); }
|
||||
}
|
||||
@ -1086,27 +1074,21 @@ function createMeshCore(agent)
|
||||
// Clean up
|
||||
if (this.httprequest._term) { this.httprequest._term.end(); }
|
||||
this.httprequest._term = null;
|
||||
} else
|
||||
{
|
||||
} else {
|
||||
// TODO!!
|
||||
}
|
||||
};
|
||||
|
||||
// Remote terminal using native pipes
|
||||
if (process.platform == "win32")
|
||||
{
|
||||
try
|
||||
{
|
||||
if ((this.httprequest.protocol == 6) && (require('win-terminal').PowerShellCapable() == true))
|
||||
{
|
||||
if (process.platform == "win32") {
|
||||
try {
|
||||
if ((this.httprequest.protocol == 6) && (require('win-terminal').PowerShellCapable() == true)) {
|
||||
this.httprequest._term = require('win-terminal').StartPowerShell(80, 25);
|
||||
} else
|
||||
{
|
||||
} else {
|
||||
this.httprequest._term = require('win-terminal').Start(80, 25);
|
||||
}
|
||||
}
|
||||
catch(e)
|
||||
{
|
||||
catch (e) {
|
||||
MeshServerLog('Failed to start remote terminal session, ' + e.toString() + ' (' + this.httprequest.remoteaddr + ')', this.httprequest);
|
||||
this.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: e.toString() }));
|
||||
this.end();
|
||||
@ -1115,14 +1097,11 @@ function createMeshCore(agent)
|
||||
this.httprequest._term.pipe(this, { dataTypeSkip: 1 });
|
||||
this.pipe(this.httprequest._term, { dataTypeSkip: 1, end: false });
|
||||
this.prependListener('end', function () { this.httprequest._term.end(function () { console.log('Terminal was closed'); }); });
|
||||
} else
|
||||
{
|
||||
if (fs.existsSync("/bin/bash"))
|
||||
{
|
||||
} else {
|
||||
if (fs.existsSync("/bin/bash")) {
|
||||
this.httprequest.process = childProcess.execFile("/bin/bash", ["bash", "-i"], { type: childProcess.SpawnTypes.TERM });
|
||||
if (process.platform == 'linux') { this.httprequest.process.stdin.write("alias ls='ls --color=auto'\nclear\n"); }
|
||||
} else
|
||||
{
|
||||
} else {
|
||||
this.httprequest.process = childProcess.execFile("/bin/sh", ["sh"], { type: childProcess.SpawnTypes.TERM });
|
||||
if (process.platform == 'linux') { this.httprequest.process.stdin.write("stty erase ^H\nalias ls='ls --color=auto'\nPS1='\\u@\\h:\\w\\$ '\nclear\n"); }
|
||||
}
|
||||
@ -1139,7 +1118,7 @@ function createMeshCore(agent)
|
||||
// User Consent Prompt is required
|
||||
// Send a console message back using the console channel, "\n" is supported.
|
||||
this.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: 'Waiting for user to grant access...' }));
|
||||
var pr = require('message-box').create('MeshCentral', this.httprequest.username + ' requesting Terminal Access. Grant access?', 10);
|
||||
var pr = require('message-box').create('MeshCentral', this.httprequest.username + ' requesting Terminal Access. Grant access?', 30);
|
||||
pr.ws = this;
|
||||
this.pause();
|
||||
|
||||
@ -1175,8 +1154,7 @@ function createMeshCore(agent)
|
||||
this.removeAllListeners('data');
|
||||
this.on('data', onTunnelControlData);
|
||||
//this.write('MeshCore Terminal Hello');
|
||||
} else if (this.httprequest.protocol == 2)
|
||||
{
|
||||
} else if (this.httprequest.protocol == 2) {
|
||||
// Check user access rights for desktop
|
||||
if (((this.httprequest.rights & MESHRIGHT_REMOTECONTROL) == 0) && ((this.httprequest.rights & MESHRIGHT_REMOTEVIEW) == 0)) {
|
||||
// Disengage this tunnel, user does not have the rights to do this!!
|
||||
@ -1208,7 +1186,7 @@ function createMeshCore(agent)
|
||||
if (this.desktop.kvm.connectionCount == 0) {
|
||||
// Display a toast message. This may not be supported on all platforms.
|
||||
// try { require('toaster').Toast('MeshCentral', 'Remote Desktop Control Ended.'); } catch (ex) { }
|
||||
|
||||
|
||||
this.httprequest.desktop.kvm.end();
|
||||
}
|
||||
};
|
||||
@ -1224,18 +1202,16 @@ function createMeshCore(agent)
|
||||
}
|
||||
|
||||
// Perform notification if needed. Toast messages may not be supported on all platforms.
|
||||
if (this.httprequest.consent && (this.httprequest.consent & 8))
|
||||
{
|
||||
if (this.httprequest.consent && (this.httprequest.consent & 8)) {
|
||||
// User Consent Prompt is required
|
||||
// Send a console message back using the console channel, "\n" is supported.
|
||||
this.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: 'Waiting for user to grant access...' }));
|
||||
var pr = require('message-box').create('MeshCentral', this.httprequest.username + ' requesting KVM Access. Grant access?', 10);
|
||||
var pr = require('message-box').create('MeshCentral', this.httprequest.username + ' requesting KVM Access. Grant access?', 30);
|
||||
pr.ws = this;
|
||||
this.pause();
|
||||
|
||||
pr.then(
|
||||
function ()
|
||||
{
|
||||
function () {
|
||||
// Success
|
||||
MeshServerLog('Starting remote desktop after local user accepted (' + this.ws.httprequest.remoteaddr + ')', this.ws.httprequest);
|
||||
this.ws.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: null }));
|
||||
@ -1246,15 +1222,13 @@ function createMeshCore(agent)
|
||||
this.ws.httprequest.desktop.kvm.pipe(this.ws, { dataTypeSkip: 1 });
|
||||
this.ws.resume();
|
||||
},
|
||||
function (e)
|
||||
{
|
||||
function (e) {
|
||||
// User Consent Denied/Failed
|
||||
MeshServerLog('Failed to start remote desktop after local user rejected (' + this.ws.httprequest.remoteaddr + ')', this.ws.httprequest);
|
||||
this.ws.end(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: e.toString() }));
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
else {
|
||||
// User Consent Prompt is not required
|
||||
if (this.httprequest.consent && (this.httprequest.consent & 1)) {
|
||||
// User Notifications is required
|
||||
@ -1282,12 +1256,11 @@ function createMeshCore(agent)
|
||||
}
|
||||
|
||||
// Perform notification if needed. Toast messages may not be supported on all platforms.
|
||||
if (this.httprequest.consent && (this.httprequest.consent & 32))
|
||||
{
|
||||
if (this.httprequest.consent && (this.httprequest.consent & 32)) {
|
||||
// User Consent Prompt is required
|
||||
// Send a console message back using the console channel, "\n" is supported.
|
||||
this.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: 'Waiting for user to grant access...' }));
|
||||
var pr = require('message-box').create('MeshCentral', this.httprequest.username + ' requesting remote file access. Grant access?', 10);
|
||||
var pr = require('message-box').create('MeshCentral', this.httprequest.username + ' requesting remote file access. Grant access?', 30);
|
||||
pr.ws = this;
|
||||
this.pause();
|
||||
|
||||
@ -1360,7 +1333,7 @@ function createMeshCore(agent)
|
||||
var response = getDirectoryInfo(cmd.path);
|
||||
if (cmd.reqid != undefined) { response.reqid = cmd.reqid; }
|
||||
this.write(new Buffer(JSON.stringify(response)));
|
||||
|
||||
|
||||
/*
|
||||
// Start the directory watcher
|
||||
if ((cmd.path != '') && (samepath == false)) {
|
||||
@ -1478,6 +1451,23 @@ function createMeshCore(agent)
|
||||
// Unknown action, ignore it.
|
||||
break;
|
||||
}
|
||||
} else if (this.httprequest.protocol == 7) { // plugin data exchange
|
||||
var cmd = null;
|
||||
try { cmd = JSON.parse(data); } catch (e) { };
|
||||
if (cmd == null) { return; }
|
||||
if ((cmd.ctrlChannel == '102938') || ((cmd.type == 'offer') && (cmd.sdp != null))) { onTunnelControlData(cmd, this); return; } // If this is control data, handle it now.
|
||||
if (cmd.action == undefined) return;
|
||||
|
||||
switch (cmd.action) {
|
||||
case 'plugin': {
|
||||
try { require(cmd.plugin).consoleaction(cmd, null, null, this); } catch (e) { throw e; }
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
// probably shouldn't happen, but just in case this feature is expanded
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
//sendConsoleText("Got tunnel #" + this.httprequest.index + " data: " + data, this.httprequest.sessionid);
|
||||
}
|
||||
@ -1552,12 +1542,10 @@ function createMeshCore(agent)
|
||||
} else if (obj.type == 'webrtc0') { // Browser indicates we can start WebRTC switch-over.
|
||||
if (ws.httprequest.protocol == 1) { // Terminal
|
||||
// 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
|
||||
if (process.platform == 'win32')
|
||||
{
|
||||
if (process.platform == 'win32') {
|
||||
ws.httprequest._term.unpipe(ws);
|
||||
}
|
||||
else
|
||||
{
|
||||
else {
|
||||
ws.httprequest.process.stdout.unpipe(ws);
|
||||
ws.httprequest.process.stderr.unpipe(ws);
|
||||
}
|
||||
@ -1574,13 +1562,11 @@ function createMeshCore(agent)
|
||||
} else if (obj.type == 'webrtc1') {
|
||||
if ((ws.httprequest.protocol == 1) || (ws.httprequest.protocol == 6)) { // Terminal
|
||||
// Switch the user input from websocket to webrtc at this point.
|
||||
if (process.platform == 'win32')
|
||||
{
|
||||
if (process.platform == 'win32') {
|
||||
ws.unpipe(ws.httprequest._term);
|
||||
ws.rtcchannel.pipe(ws.httprequest._term, { dataTypeSkip: 1 }); // 0 = Binary, 1 = Text.
|
||||
}
|
||||
else
|
||||
{
|
||||
else {
|
||||
ws.unpipe(ws.httprequest.process.stdin);
|
||||
ws.rtcchannel.pipe(ws.httprequest.process.stdin, { dataTypeSkip: 1 }); // 0 = Binary, 1 = Text.
|
||||
}
|
||||
@ -1595,12 +1581,10 @@ function createMeshCore(agent)
|
||||
} else if (obj.type == 'webrtc2') {
|
||||
// Other side received websocket end of data marker, start sending data on WebRTC channel
|
||||
if ((ws.httprequest.protocol == 1) || (ws.httprequest.protocol == 6)) { // Terminal
|
||||
if (process.platform == 'win32')
|
||||
{
|
||||
if (process.platform == 'win32') {
|
||||
ws.httprequest._term.pipe(ws.webrtc.rtcchannel, { dataTypeSkip: 1, end: false }); // 0 = Binary, 1 = Text.
|
||||
}
|
||||
else
|
||||
{
|
||||
else {
|
||||
ws.httprequest.process.stdout.pipe(ws.webrtc.rtcchannel, { dataTypeSkip: 1, end: false }); // 0 = Binary, 1 = Text.
|
||||
ws.httprequest.process.stderr.pipe(ws.webrtc.rtcchannel, { dataTypeSkip: 1, end: false }); // 0 = Binary, 1 = Text.
|
||||
}
|
||||
@ -1640,7 +1624,7 @@ function createMeshCore(agent)
|
||||
// Console state
|
||||
var consoleWebSockets = {};
|
||||
var consoleHttpRequest = null;
|
||||
|
||||
|
||||
// Console HTTP response
|
||||
function consoleHttpResponse(response) {
|
||||
response.data = function (data) { sendConsoleText(rstr2hex(buf2rstr(data)), this.sessionid); consoleHttpRequest = null; }
|
||||
@ -1683,7 +1667,7 @@ function createMeshCore(agent)
|
||||
var response = null;
|
||||
switch (cmd) {
|
||||
case 'help': { // Displays available commands
|
||||
var fin = '', f = '', availcommands = 'help,info,osinfo,args,print,type,dbget,dbset,dbcompact,eval,parseuri,httpget,nwslist,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';
|
||||
var fin = '', f = '', availcommands = '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';
|
||||
availcommands = availcommands.split(',').sort();
|
||||
while (availcommands.length > 0) {
|
||||
if (f.length > 100) { fin += (f + ',\r\n'); f = ''; }
|
||||
@ -1788,8 +1772,7 @@ function createMeshCore(agent)
|
||||
break;
|
||||
}
|
||||
case 'toast': {
|
||||
if (args['_'].length < 1) { response = 'Proper usage: toast "message"'; } else
|
||||
{
|
||||
if (args['_'].length < 1) { response = 'Proper usage: toast "message"'; } else {
|
||||
require('toaster').Toast('MeshCentral', args['_'][0]).then(sendConsoleText, sendConsoleText);
|
||||
}
|
||||
break;
|
||||
@ -1802,7 +1785,7 @@ function createMeshCore(agent)
|
||||
case 'ps': {
|
||||
processManager.getProcesses(function (plist) {
|
||||
var x = '';
|
||||
for (var i in plist) { x += i + ', ' + plist[i].cmd + ((plist[i].user) ? (', ' + plist[i].user):'') + '\r\n'; }
|
||||
for (var i in plist) { x += i + ', ' + plist[i].cmd + ((plist[i].user) ? (', ' + plist[i].user) : '') + '\r\n'; }
|
||||
sendConsoleText(x, sessionid);
|
||||
});
|
||||
break;
|
||||
@ -1836,14 +1819,11 @@ function createMeshCore(agent)
|
||||
break;
|
||||
}
|
||||
case 'dump':
|
||||
if (args['_'].length < 1)
|
||||
{
|
||||
if (args['_'].length < 1) {
|
||||
response = 'Proper usage: dump [on/off/status]'; // Display correct command usage
|
||||
}
|
||||
else
|
||||
{
|
||||
switch(args['_'][0].toLowerCase())
|
||||
{
|
||||
else {
|
||||
switch (args['_'][0].toLowerCase()) {
|
||||
case 'on':
|
||||
process.coreDumpLocation = process.platform == 'win32' ? process.execPath.replace('.exe', '.dmp') : (process.execPath + '.dmp');
|
||||
response = 'enabled';
|
||||
@ -1853,12 +1833,10 @@ function createMeshCore(agent)
|
||||
response = 'disabled';
|
||||
break;
|
||||
case 'status':
|
||||
if (process.coreDumpLocation)
|
||||
{
|
||||
if (process.coreDumpLocation) {
|
||||
response = 'Core Dump: [ENABLED' + (require('fs').existsSync(process.coreDumpLocation) ? (', (DMP file exists)]') : (']'));
|
||||
}
|
||||
else
|
||||
{
|
||||
else {
|
||||
response = 'Core Dump: [DISABLED]';
|
||||
}
|
||||
break;
|
||||
@ -1878,18 +1856,14 @@ function createMeshCore(agent)
|
||||
}
|
||||
case 'uninstallagent':
|
||||
var agentName = process.platform == 'win32' ? 'Mesh Agent' : 'meshagent';
|
||||
if (!require('service-manager').manager.getService(agentName).isMe())
|
||||
{
|
||||
if (!require('service-manager').manager.getService(agentName).isMe()) {
|
||||
response = 'Uininstall failed, this instance is not the service instance';
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
else {
|
||||
try {
|
||||
diagnosticAgent_uninstall();
|
||||
}
|
||||
catch(x)
|
||||
{
|
||||
catch (x) {
|
||||
}
|
||||
var js = "require('service-manager').manager.getService('" + agentName + "').stop(); require('service-manager').manager.uninstallService('" + agentName + "'); process.exit();";
|
||||
this.child = require('child_process').execFile(process.execPath, [process.platform == 'win32' ? (process.execPath.split('\\').pop()) : (process.execPath.split('/').pop()), '-b64exec', Buffer.from(js).toString('base64')], { type: 4, detached: true });
|
||||
@ -2068,7 +2042,7 @@ function createMeshCore(agent)
|
||||
if (httprequest != null) {
|
||||
httprequest.upgrade = onWebSocketUpgrade;
|
||||
httprequest.on('error', function (e) { sendConsoleText('ERROR: ' + JSON.stringify(e)); });
|
||||
|
||||
|
||||
var index = 1;
|
||||
while (consoleWebSockets[index]) { index++; }
|
||||
httprequest.sessionid = sessionid;
|
||||
@ -2256,40 +2230,31 @@ function createMeshCore(agent)
|
||||
}
|
||||
case 'diagnostic':
|
||||
{
|
||||
if (!mesh.DAIPC.listening)
|
||||
{
|
||||
if (!mesh.DAIPC.listening) {
|
||||
response = 'Unable to bind to Diagnostic IPC, most likely because the path (' + process.cwd() + ') is not on a local file system';
|
||||
break;
|
||||
}
|
||||
var diag = diagnosticAgent_installCheck();
|
||||
if (diag)
|
||||
{
|
||||
if (args['_'].length == 1 && args['_'][0] == 'uninstall')
|
||||
{
|
||||
if (diag) {
|
||||
if (args['_'].length == 1 && args['_'][0] == 'uninstall') {
|
||||
diagnosticAgent_uninstall();
|
||||
response = 'Diagnostic Agent uninstalled';
|
||||
}
|
||||
else
|
||||
{
|
||||
else {
|
||||
response = 'Diagnostic Agent installed at: ' + diag.appLocation();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (args['_'].length == 1 && args['_'][0] == 'install')
|
||||
{
|
||||
else {
|
||||
if (args['_'].length == 1 && args['_'][0] == 'install') {
|
||||
diag = diagnosticAgent_installCheck(true);
|
||||
if (diag)
|
||||
{
|
||||
if (diag) {
|
||||
response = 'Diagnostic agent was installed at: ' + diag.appLocation();
|
||||
}
|
||||
else
|
||||
{
|
||||
else {
|
||||
response = 'Diagnostic agent installation failed';
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
else {
|
||||
response = 'Diagnostic Agent Not installed. To install: diagnostic install';
|
||||
}
|
||||
}
|
||||
@ -2297,27 +2262,27 @@ function createMeshCore(agent)
|
||||
break;
|
||||
}
|
||||
case 'apf': {
|
||||
if (meshCoreObj.intelamt!==null) {
|
||||
if (meshCoreObj.intelamt !== null) {
|
||||
if (args['_'].length == 1) {
|
||||
if (args['_'][0] == 'on') {
|
||||
response = 'Starting APF tunnel'
|
||||
var apfarg = {
|
||||
mpsurl: mesh.ServerUrl.replace('agent.ashx','apf.ashx'),
|
||||
mpsuser: Buffer.from(mesh.ServerInfo.MeshID,'hex').toString('base64').substring(0,16),
|
||||
mpspass: Buffer.from(mesh.ServerInfo.MeshID,'hex').toString('base64').substring(0,16),
|
||||
mpsurl: mesh.ServerUrl.replace('agent.ashx', 'apf.ashx'),
|
||||
mpsuser: Buffer.from(mesh.ServerInfo.MeshID, 'hex').toString('base64').substring(0, 16),
|
||||
mpspass: Buffer.from(mesh.ServerInfo.MeshID, 'hex').toString('base64').substring(0, 16),
|
||||
mpskeepalive: 60000,
|
||||
clientname: require('os').hostname(),
|
||||
clientaddress: '127.0.0.1',
|
||||
clientuuid: meshCoreObj.intelamt.uuid
|
||||
};
|
||||
var tobj = { debug: false }; //
|
||||
apftunnel= require('apfclient')(tobj,apfarg);
|
||||
apftunnel = require('apfclient')(tobj, apfarg);
|
||||
try {
|
||||
apftunnel.connect();
|
||||
response += "..success";
|
||||
} catch (e) {
|
||||
response += JSON.stringify(e);
|
||||
}
|
||||
}
|
||||
} else if (args['_'][0] == 'off') {
|
||||
response = 'Stopping APF tunnel';
|
||||
try {
|
||||
@ -2326,19 +2291,34 @@ function createMeshCore(agent)
|
||||
} catch (e) {
|
||||
response += JSON.stringify(e);
|
||||
}
|
||||
apftunnel=null;
|
||||
apftunnel = null;
|
||||
} else {
|
||||
response = 'Invalid command.\r\nCmd syntax: apf on|off';
|
||||
}
|
||||
} else {
|
||||
response = 'APF tunnel is '+ (apftunnel == null ? 'off': 'on' );
|
||||
} else {
|
||||
response = 'APF tunnel is ' + (apftunnel == null ? 'off' : 'on');
|
||||
}
|
||||
|
||||
|
||||
} else {
|
||||
response = 'APF tunnel requires Intel AMT';
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'plugin': {
|
||||
if (typeof args['_'][0] == 'string') {
|
||||
try {
|
||||
// Pass off the action to the plugin
|
||||
// for plugin creators, you'll want to have a plugindir/modules_meshcore/plugin.js
|
||||
// to control the output / actions here.
|
||||
response = require(args['_'][0]).consoleaction(args, rights, sessionid, mesh);
|
||||
} catch (e) {
|
||||
response = 'There was an error in the plugin (' + e + ')';
|
||||
}
|
||||
} else {
|
||||
response = 'Proper usage: plugin [pluginName] [args].';
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: { // This is an unknown command, return an error message
|
||||
response = 'Unknown command \"' + cmd + '\", type \"help\" for list of avaialble commands.';
|
||||
break;
|
||||
@ -2347,7 +2327,7 @@ function createMeshCore(agent)
|
||||
} catch (e) { response = 'Command returned an exception error: ' + e; console.log(e); }
|
||||
if (response != null) { sendConsoleText(response, sessionid); }
|
||||
}
|
||||
|
||||
|
||||
// Send a mesh agent console command
|
||||
function sendConsoleText(text, sessionid) {
|
||||
if (typeof text == 'object') { text = JSON.stringify(text); }
|
||||
@ -2356,7 +2336,7 @@ function createMeshCore(agent)
|
||||
|
||||
// Called before the process exits
|
||||
//process.exit = function (code) { console.log("Exit with code: " + code.toString()); }
|
||||
|
||||
|
||||
// Called when the server connection state changes
|
||||
function handleServerConnection(state) {
|
||||
meshServerConnectionState = state;
|
||||
@ -2381,13 +2361,13 @@ function createMeshCore(agent)
|
||||
if (selfInfoUpdateTimer == null) { selfInfoUpdateTimer = setInterval(sendPeriodicServerUpdate, 1200000); } // 20 minutes
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Update the server with the latest network interface information
|
||||
var sendNetworkUpdateNagleTimer = null;
|
||||
function sendNetworkUpdateNagle() { if (sendNetworkUpdateNagleTimer != null) { clearTimeout(sendNetworkUpdateNagleTimer); sendNetworkUpdateNagleTimer = null; } sendNetworkUpdateNagleTimer = setTimeout(sendNetworkUpdate, 5000); }
|
||||
function sendNetworkUpdate(force) {
|
||||
sendNetworkUpdateNagleTimer = null;
|
||||
|
||||
|
||||
// Update the network interfaces information data
|
||||
var netInfo = mesh.NetInfo;
|
||||
if (netInfo) {
|
||||
@ -2396,7 +2376,7 @@ function createMeshCore(agent)
|
||||
if ((force == true) || (clearGatewayMac(netInfoStr) != clearGatewayMac(lastNetworkInfo))) { mesh.SendCommand(netInfo); lastNetworkInfo = netInfoStr; }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Called periodically to check if we need to send updates to the server
|
||||
function sendPeriodicServerUpdate(flags) {
|
||||
if (meshServerConnectionState == 0) return; // Not connected to server, do nothing.
|
||||
@ -2451,8 +2431,10 @@ function createMeshCore(agent)
|
||||
//if (process.platform == 'win32') { try { pr = require('win-info').pendingReboot(); } catch (ex) { pr = null; } } // Pending reboot
|
||||
if ((meshCoreObj.av == null) || (JSON.stringify(meshCoreObj.av) != JSON.stringify(av))) { meshCoreObj.av = av; mesh.SendCommand(meshCoreObj); }
|
||||
}
|
||||
|
||||
// TODO: add plugin hook here
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Starting function
|
||||
obj.start = function () {
|
||||
@ -2485,16 +2467,16 @@ function createMeshCore(agent)
|
||||
//userSession.on('unlocked', function (user) { sendConsoleText('[' + (user.Domain ? user.Domain + '\\' : '') + user.Username + '] has UNLOCKED the desktop'); });
|
||||
} catch (ex) { }
|
||||
}
|
||||
|
||||
|
||||
obj.stop = function () {
|
||||
mesh.AddCommandHandler(null);
|
||||
mesh.AddConnectHandler(null);
|
||||
}
|
||||
|
||||
|
||||
function onWebSocketClosed() { sendConsoleText("WebSocket #" + this.httprequest.index + " closed.", this.httprequest.sessionid); delete consoleWebSockets[this.httprequest.index]; }
|
||||
function onWebSocketData(data) { sendConsoleText("Got WebSocket #" + this.httprequest.index + " data: " + data, this.httprequest.sessionid); }
|
||||
function onWebSocketSendOk() { sendConsoleText("WebSocket #" + this.index + " SendOK.", this.sessionid); }
|
||||
|
||||
|
||||
function onWebSocketUpgrade(response, s, head) {
|
||||
sendConsoleText("WebSocket #" + this.index + " connected.", this.sessionid);
|
||||
this.s = s;
|
||||
|
96
db.js
96
db.js
@ -607,10 +607,20 @@ module.exports.CreateDB = function (parent, func) {
|
||||
setupFunctions(func); // Completed setup of NeDB
|
||||
}
|
||||
|
||||
// Check the object names for a "."
|
||||
function checkObjectNames(r, tag) {
|
||||
if (typeof r != 'object') return;
|
||||
for (var i in r) {
|
||||
if (i.indexOf('.') >= 0) { throw('BadDbName (' + tag + '): ' + JSON.stringify(r)); }
|
||||
checkObjectNames(r[i], tag);
|
||||
}
|
||||
}
|
||||
|
||||
function setupFunctions(func) {
|
||||
if (obj.databaseType == 3) {
|
||||
// Database actions on the main collection (MongoDB)
|
||||
obj.Set = function (data, func) {
|
||||
checkObjectNames(data, 'x1'); // DEBUG CHECKING
|
||||
obj.file.replaceOne({ _id: data._id }, performTypedRecordEncrypt(data), { upsert: true }, func);
|
||||
};
|
||||
obj.Get = function (id, func) {
|
||||
@ -640,14 +650,29 @@ module.exports.CreateDB = function (parent, func) {
|
||||
obj.Remove = function (id) { obj.file.deleteOne({ _id: id }); };
|
||||
obj.RemoveAll = function (func) { obj.file.deleteMany({}, { multi: true }, func); };
|
||||
obj.RemoveAllOfType = function (type, func) { obj.file.deleteMany({ type: type }, { multi: true }, func); };
|
||||
obj.InsertMany = function (data, func) { obj.file.insertMany(data, func); };
|
||||
obj.InsertMany = function (data, func) {
|
||||
checkObjectNames(data, 'x2'); // DEBUG CHECKING
|
||||
obj.file.insertMany(data, func);
|
||||
};
|
||||
obj.RemoveMeshDocuments = function (id) { obj.file.deleteMany({ meshid: id }, { multi: true }); obj.file.deleteOne({ _id: 'nt' + id }); };
|
||||
obj.MakeSiteAdmin = function (username, domain) { obj.Get('user/' + domain + '/' + username, function (err, docs) { if (docs.length == 1) { docs[0].siteadmin = 0xFFFFFFFF; obj.Set(docs[0]); } }); };
|
||||
obj.MakeSiteAdmin = function (username, domain) {
|
||||
obj.Get('user/' + domain + '/' + username, function (err, docs) {
|
||||
if (docs.length == 1) {
|
||||
checkObjectNames(docs[0], 'x3'); // DEBUG CHECKING
|
||||
docs[0].siteadmin = 0xFFFFFFFF; obj.Set(docs[0]);
|
||||
}
|
||||
});
|
||||
};
|
||||
obj.DeleteDomain = function (domain, func) { obj.file.deleteMany({ domain: domain }, { multi: true }, func); };
|
||||
obj.SetUser = function (user) { var u = Clone(user); if (u.subscriptions) { delete u.subscriptions; } obj.Set(u); };
|
||||
obj.SetUser = function (user) {
|
||||
checkObjectNames(user, 'x4'); // DEBUG CHECKING
|
||||
var u = Clone(user);
|
||||
if (u.subscriptions) { delete u.subscriptions; } obj.Set(u);
|
||||
};
|
||||
obj.dispose = function () { for (var x in obj) { if (obj[x].close) { obj[x].close(); } delete obj[x]; } };
|
||||
obj.getLocalAmtNodes = function (func) { obj.file.find({ type: 'node', host: { $exists: true, $ne: null }, intelamt: { $exists: true } }).toArray(func); };
|
||||
obj.getAmtUuidNode = function (meshid, uuid, func) { obj.file.find({ type: 'node', meshid: meshid, 'intelamt.uuid': uuid }).toArray(func); };
|
||||
obj.getAmtUuidMeshNode = function (meshid, uuid, func) { obj.file.find({ type: 'node', meshid: meshid, 'intelamt.uuid': uuid }).toArray(func); };
|
||||
obj.getAmtUuidNode = function (uuid, func) { obj.file.find({ type: 'node', 'intelamt.uuid': uuid }).toArray(func); };
|
||||
|
||||
// TODO: Starting in MongoDB 4.0.3, you should use countDocuments() instead of count() that is deprecated. We should detect MongoDB version and switch.
|
||||
// https://docs.mongodb.com/manual/reference/method/db.collection.countDocuments/
|
||||
@ -656,7 +681,10 @@ module.exports.CreateDB = function (parent, func) {
|
||||
|
||||
// Database actions on the events collection
|
||||
obj.GetAllEvents = function (func) { obj.eventsfile.find({}).toArray(func); };
|
||||
obj.StoreEvent = function (event) { obj.eventsfile.insertOne(event); };
|
||||
obj.StoreEvent = function (event) {
|
||||
checkObjectNames(event, 'x5'); // DEBUG CHECKING
|
||||
obj.eventsfile.insertOne(event);
|
||||
};
|
||||
obj.GetEvents = function (ids, domain, func) { obj.eventsfile.find({ domain: domain, ids: { $in: ids } }).project({ type: 0, _id: 0, domain: 0, ids: 0, node: 0 }).sort({ time: -1 }).toArray(func); };
|
||||
obj.GetEventsWithLimit = function (ids, domain, limit, func) { obj.eventsfile.find({ domain: domain, ids: { $in: ids } }).project({ type: 0, _id: 0, domain: 0, ids: 0, node: 0 }).sort({ time: -1 }).limit(limit).toArray(func); };
|
||||
obj.GetUserEvents = function (ids, domain, username, func) { obj.eventsfile.find({ domain: domain, $or: [{ ids: { $in: ids } }, { username: username }] }).project({ type: 0, _id: 0, domain: 0, ids: 0, node: 0 }).sort({ time: -1 }).toArray(func); };
|
||||
@ -668,18 +696,27 @@ module.exports.CreateDB = function (parent, func) {
|
||||
|
||||
// Database actions on the power collection
|
||||
obj.getAllPower = function (func) { obj.powerfile.find({}).toArray(func); };
|
||||
obj.storePowerEvent = function (event, multiServer, func) { if (multiServer != null) { event.server = multiServer.serverid; } obj.powerfile.insertOne(event, func); };
|
||||
obj.storePowerEvent = function (event, multiServer, func) {
|
||||
checkObjectNames(event, 'x6'); // DEBUG CHECKING
|
||||
if (multiServer != null) { event.server = multiServer.serverid; } obj.powerfile.insertOne(event, func);
|
||||
};
|
||||
obj.getPowerTimeline = function (nodeid, func) { obj.powerfile.find({ nodeid: { $in: ['*', nodeid] } }).project({ _id: 0, nodeid: 0, s: 0 }).sort({ time: 1 }).toArray(func); };
|
||||
obj.removeAllPowerEvents = function () { obj.powerfile.deleteMany({}, { multi: true }); };
|
||||
obj.removeAllPowerEventsForNode = function (nodeid) { obj.powerfile.deleteMany({ nodeid: nodeid }, { multi: true }); };
|
||||
|
||||
// Database actions on the SMBIOS collection
|
||||
obj.SetSMBIOS = function (smbios, func) { obj.smbiosfile.updateOne({ _id: smbios._id }, { $set: smbios }, { upsert: true }, func); };
|
||||
obj.SetSMBIOS = function (smbios, func) {
|
||||
checkObjectNames(smbios, 'x7'); // DEBUG CHECKING
|
||||
obj.smbiosfile.updateOne({ _id: smbios._id }, { $set: smbios }, { upsert: true }, func);
|
||||
};
|
||||
obj.RemoveSMBIOS = function (id) { obj.smbiosfile.deleteOne({ _id: id }); };
|
||||
obj.GetSMBIOS = function (id, func) { obj.smbiosfile.find({ _id: id }).toArray(func); };
|
||||
|
||||
// Database actions on the Server Stats collection
|
||||
obj.SetServerStats = function (data, func) { obj.serverstatsfile.insertOne(data, func); };
|
||||
obj.SetServerStats = function (data, func) {
|
||||
checkObjectNames(data, 'x8'); // DEBUG CHECKING
|
||||
obj.serverstatsfile.insertOne(data, func);
|
||||
};
|
||||
obj.GetServerStats = function (hours, func) { var t = new Date(); t.setTime(t.getTime() - (60 * 60 * 1000 * hours)); obj.serverstatsfile.find({ time: { $gt: t } }, { _id: 0, cpu: 0 }).toArray(func); };
|
||||
|
||||
// Read a configuration file from the database
|
||||
@ -706,7 +743,11 @@ module.exports.CreateDB = function (parent, func) {
|
||||
}
|
||||
} else {
|
||||
// Database actions on the main collection (NeDB and MongoJS)
|
||||
obj.Set = function (data, func) { var xdata = performTypedRecordEncrypt(data); obj.file.update({ _id: xdata._id }, xdata, { upsert: true }, func); };
|
||||
obj.Set = function (data, func) {
|
||||
checkObjectNames(data, 'x9'); // DEBUG CHECKING
|
||||
var xdata = performTypedRecordEncrypt(data);
|
||||
obj.file.update({ _id: xdata._id }, xdata, { upsert: true }, func);
|
||||
};
|
||||
obj.Get = function (id, func) {
|
||||
if (arguments.length > 2) {
|
||||
var parms = [func];
|
||||
@ -734,19 +775,36 @@ module.exports.CreateDB = function (parent, func) {
|
||||
obj.Remove = function (id) { obj.file.remove({ _id: id }); };
|
||||
obj.RemoveAll = function (func) { obj.file.remove({}, { multi: true }, func); };
|
||||
obj.RemoveAllOfType = function (type, func) { obj.file.remove({ type: type }, { multi: true }, func); };
|
||||
obj.InsertMany = function (data, func) { obj.file.insert(data, func); };
|
||||
obj.InsertMany = function (data, func) {
|
||||
checkObjectNames(data, 'x10'); // DEBUG CHECKING
|
||||
obj.file.insert(data, func);
|
||||
};
|
||||
obj.RemoveMeshDocuments = function (id) { obj.file.remove({ meshid: id }, { multi: true }); obj.file.remove({ _id: 'nt' + id }); };
|
||||
obj.MakeSiteAdmin = function (username, domain) { obj.Get('user/' + domain + '/' + username, function (err, docs) { if (docs.length == 1) { docs[0].siteadmin = 0xFFFFFFFF; obj.Set(docs[0]); } }); };
|
||||
obj.MakeSiteAdmin = function (username, domain) {
|
||||
obj.Get('user/' + domain + '/' + username, function (err, docs) {
|
||||
if (docs.length == 1) {
|
||||
checkObjectNames(docs[0], 'x11'); // DEBUG CHECKING
|
||||
docs[0].siteadmin = 0xFFFFFFFF; obj.Set(docs[0]);
|
||||
}
|
||||
});
|
||||
};
|
||||
obj.DeleteDomain = function (domain, func) { obj.file.remove({ domain: domain }, { multi: true }, func); };
|
||||
obj.SetUser = function (user) { var u = Clone(user); if (u.subscriptions) { delete u.subscriptions; } obj.Set(u); };
|
||||
obj.SetUser = function (user) {
|
||||
checkObjectNames(user, 'x12'); // DEBUG CHECKING
|
||||
var u = Clone(user); if (u.subscriptions) { delete u.subscriptions; } obj.Set(u);
|
||||
};
|
||||
obj.dispose = function () { for (var x in obj) { if (obj[x].close) { obj[x].close(); } delete obj[x]; } };
|
||||
obj.getLocalAmtNodes = function (func) { obj.file.find({ type: 'node', host: { $exists: true, $ne: null }, intelamt: { $exists: true } }, func); };
|
||||
obj.getAmtUuidNode = function (meshid, uuid, func) { obj.file.find({ type: 'node', meshid: meshid, 'intelamt.uuid': uuid }, func); };
|
||||
obj.getAmtUuidMeshNode = function (meshid, uuid, func) { obj.file.find({ type: 'node', meshid: meshid, 'intelamt.uuid': uuid }, func); };
|
||||
obj.getAmtUuidNode = function (uuid, func) { obj.file.find({ type: 'node', 'intelamt.uuid': uuid }, func); };
|
||||
obj.isMaxType = function (max, type, domainid, func) { if (max == null) { func(false); } else { obj.file.count({ type: type, domain: domainid }, function (err, count) { func((err != null) || (count > max), count); }); } }
|
||||
|
||||
// Database actions on the events collection
|
||||
obj.GetAllEvents = function (func) { obj.eventsfile.find({}, func); };
|
||||
obj.StoreEvent = function (event) { obj.eventsfile.insert(event); };
|
||||
obj.StoreEvent = function (event) {
|
||||
checkObjectNames(event, 'x13'); // DEBUG CHECKING
|
||||
obj.eventsfile.insert(event);
|
||||
};
|
||||
obj.GetEvents = function (ids, domain, func) { if (obj.databaseType == 1) { obj.eventsfile.find({ domain: domain, ids: { $in: ids } }, { _id: 0, domain: 0, ids: 0, node: 0 }).sort({ time: -1 }).exec(func); } else { obj.eventsfile.find({ domain: domain, ids: { $in: ids } }, { type: 0, _id: 0, domain: 0, ids: 0, node: 0 }).sort({ time: -1 }, func); } };
|
||||
obj.GetEventsWithLimit = function (ids, domain, limit, func) { if (obj.databaseType == 1) { obj.eventsfile.find({ domain: domain, ids: { $in: ids } }, { _id: 0, domain: 0, ids: 0, node: 0 }).sort({ time: -1 }).limit(limit).exec(func); } else { obj.eventsfile.find({ domain: domain, ids: { $in: ids } }, { type: 0, _id: 0, domain: 0, ids: 0, node: 0 }).sort({ time: -1 }).limit(limit, func); } };
|
||||
obj.GetUserEvents = function (ids, domain, username, func) {
|
||||
@ -770,7 +828,10 @@ module.exports.CreateDB = function (parent, func) {
|
||||
|
||||
// Database actions on the power collection
|
||||
obj.getAllPower = function (func) { obj.powerfile.find({}, func); };
|
||||
obj.storePowerEvent = function (event, multiServer, func) { if (multiServer != null) { event.server = multiServer.serverid; } obj.powerfile.insert(event, func); };
|
||||
obj.storePowerEvent = function (event, multiServer, func) {
|
||||
checkObjectNames(event, 'x14'); // DEBUG CHECKING
|
||||
if (multiServer != null) { event.server = multiServer.serverid; } obj.powerfile.insert(event, func);
|
||||
};
|
||||
obj.getPowerTimeline = function (nodeid, func) { if (obj.databaseType == 1) { obj.powerfile.find({ nodeid: { $in: ['*', nodeid] } }, { _id: 0, nodeid: 0, s: 0 }).sort({ time: 1 }).exec(func); } else { obj.powerfile.find({ nodeid: { $in: ['*', nodeid] } }, { _id: 0, nodeid: 0, s: 0 }).sort({ time: 1 }, func); } };
|
||||
obj.removeAllPowerEvents = function () { obj.powerfile.remove({}, { multi: true }); };
|
||||
obj.removeAllPowerEventsForNode = function (nodeid) { obj.powerfile.remove({ nodeid: nodeid }, { multi: true }); };
|
||||
@ -781,7 +842,10 @@ module.exports.CreateDB = function (parent, func) {
|
||||
obj.GetSMBIOS = function (id, func) { obj.smbiosfile.find({ _id: id }, func); };
|
||||
|
||||
// Database actions on the Server Stats collection
|
||||
obj.SetServerStats = function (data, func) { obj.serverstatsfile.insert(data, func); };
|
||||
obj.SetServerStats = function (data, func) {
|
||||
checkObjectNames(data, 'x15'); // DEBUG CHECKING
|
||||
obj.serverstatsfile.insert(data, func);
|
||||
};
|
||||
obj.GetServerStats = function (hours, func) { var t = new Date(); t.setTime(t.getTime() - (60 * 60 * 1000 * hours)); obj.serverstatsfile.find({ time: { $gt: t } }, { _id: 0, cpu: 0 }, func); };
|
||||
|
||||
// Read a configuration file from the database
|
||||
|
88
meshagent.js
88
meshagent.js
@ -526,7 +526,7 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) {
|
||||
if (adminUser.links == null) adminUser.links = {};
|
||||
adminUser.links[obj.dbMeshKey] = { rights: 0xFFFFFFFF };
|
||||
db.SetUser(adminUser);
|
||||
parent.parent.DispatchEvent(['*', obj.dbMeshKey, adminUser._id], obj, { etype: 'mesh', username: adminUser.name, meshid: obj.dbMeshKey, name: meshname, mtype: 2, desc: '', action: 'createmesh', links: links, msg: 'Mesh created: ' + obj.meshid, domain: domain.id });
|
||||
parent.parent.DispatchEvent(['*', obj.dbMeshKey, adminUser._id], obj, { etype: 'mesh', username: adminUser.name, meshid: obj.dbMeshKey, name: meshname, mtype: 2, desc: '', action: 'createmesh', msg: 'Mesh created: ' + obj.meshid, domain: domain.id });
|
||||
}
|
||||
} else {
|
||||
if ((mesh != null) && (mesh.deleted != null) && (mesh.links)) {
|
||||
@ -547,7 +547,7 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) {
|
||||
}
|
||||
|
||||
// Send out an event indicating this mesh was "created"
|
||||
parent.parent.DispatchEvent(ids, obj, { etype: 'mesh', meshid: mesh._id, name: mesh.name, mtype: mesh.mtype, desc: mesh.desc, action: 'createmesh', links: mesh.links, msg: 'Mesh undeleted: ' + mesh._id, domain: domain.id });
|
||||
parent.parent.DispatchEvent(ids, obj, { etype: 'mesh', meshid: mesh._id, name: mesh.name, mtype: mesh.mtype, desc: mesh.desc, action: 'createmesh', msg: 'Mesh undeleted: ' + mesh._id, domain: domain.id });
|
||||
|
||||
// Mark the mesh as active
|
||||
delete mesh.deleted;
|
||||
@ -617,7 +617,7 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) {
|
||||
adminUser.links[obj.meshid] = { rights: 0xFFFFFFFF };
|
||||
//adminUser.subscriptions = parent.subscribe(adminUser._id, ws);
|
||||
db.SetUser(user);
|
||||
parent.parent.DispatchEvent(['*', meshid, user._id], obj, { etype: 'mesh', username: user.name, meshid: obj.meshid, name: obj.meshid, mtype: 2, desc: '', action: 'createmesh', links: links, msg: 'Mesh created: ' + obj.meshid, domain: domain.id });
|
||||
parent.parent.DispatchEvent(['*', meshid, user._id], obj, { etype: 'mesh', username: user.name, meshid: obj.meshid, name: obj.meshid, mtype: 2, desc: '', action: 'createmesh', msg: 'Mesh created: ' + obj.meshid, domain: domain.id });
|
||||
}
|
||||
}
|
||||
|
||||
@ -1064,76 +1064,8 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) {
|
||||
switch (command.action) {
|
||||
case 'msg':
|
||||
{
|
||||
// Route a message.
|
||||
// If this command has a sessionid, that is the target.
|
||||
if (command.sessionid != null) {
|
||||
if (typeof command.sessionid != 'string') break;
|
||||
var splitsessionid = command.sessionid.split('/');
|
||||
// Check that we are in the same domain and the user has rights over this node.
|
||||
if ((splitsessionid[0] == 'user') && (splitsessionid[1] == domain.id)) {
|
||||
// Check if this user has rights to get this message
|
||||
//if (mesh.links[user._id] == null || ((mesh.links[user._id].rights & 16) == 0)) return; // TODO!!!!!!!!!!!!!!!!!!!!!
|
||||
|
||||
// See if the session is connected. If so, go ahead and send this message to the target node
|
||||
var ws = parent.wssessions2[command.sessionid];
|
||||
if (ws != null) {
|
||||
command.nodeid = obj.dbNodeKey; // Set the nodeid, required for responses.
|
||||
delete command.sessionid; // Remove the sessionid, since we are sending to that sessionid, so it's implyed.
|
||||
try { ws.send(JSON.stringify(command)); } catch (ex) { }
|
||||
} else if (parent.parent.multiServer != null) {
|
||||
// See if we can send this to a peer server
|
||||
var serverid = parent.wsPeerSessions2[command.sessionid];
|
||||
if (serverid != null) {
|
||||
command.fromNodeid = obj.dbNodeKey;
|
||||
parent.parent.multiServer.DispatchMessageSingleServer(command, serverid);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (command.userid != null) { // If this command has a userid, that is the target.
|
||||
if (typeof command.userid != 'string') break;
|
||||
var splituserid = command.userid.split('/');
|
||||
// Check that we are in the same domain and the user has rights over this node.
|
||||
if ((splituserid[0] == 'user') && (splituserid[1] == domain.id)) {
|
||||
// Check if this user has rights to get this message
|
||||
//if (mesh.links[user._id] == null || ((mesh.links[user._id].rights & 16) == 0)) return; // TODO!!!!!!!!!!!!!!!!!!!!!
|
||||
|
||||
// See if the session is connected
|
||||
var sessions = parent.wssessions[command.userid];
|
||||
|
||||
// Go ahead and send this message to the target node
|
||||
if (sessions != null) {
|
||||
command.nodeid = obj.dbNodeKey; // Set the nodeid, required for responses.
|
||||
delete command.userid; // Remove the userid, since we are sending to that userid, so it's implyed.
|
||||
for (i in sessions) { sessions[i].send(JSON.stringify(command)); }
|
||||
}
|
||||
|
||||
if (parent.parent.multiServer != null) {
|
||||
// TODO: Add multi-server support
|
||||
}
|
||||
}
|
||||
} else { // Route this command to the mesh
|
||||
command.nodeid = obj.dbNodeKey;
|
||||
var cmdstr = JSON.stringify(command);
|
||||
for (var userid in parent.wssessions) { // Find all connected users for this mesh and send the message
|
||||
var user = parent.users[userid];
|
||||
if ((user != null) && (user.links != null)) {
|
||||
var rights = user.links[obj.dbMeshKey];
|
||||
if (rights != null) { // TODO: Look at what rights are needed for message routing
|
||||
var xsessions = parent.wssessions[userid];
|
||||
// Send the message to all users on this server
|
||||
for (i in xsessions) { try { xsessions[i].send(cmdstr); } catch (e) { } }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Send the message to all users of other servers
|
||||
if (parent.parent.multiServer != null) {
|
||||
delete command.nodeid;
|
||||
command.fromNodeid = obj.dbNodeKey;
|
||||
command.meshid = obj.dbMeshKey;
|
||||
parent.parent.multiServer.DispatchMessage(command);
|
||||
}
|
||||
}
|
||||
// Route a message
|
||||
parent.routeAgentCommand(command, obj.domain.id, obj.dbNodeKey, obj.dbMeshKey);
|
||||
break;
|
||||
}
|
||||
case 'coreinfo':
|
||||
@ -1345,6 +1277,16 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) {
|
||||
});
|
||||
break;
|
||||
}
|
||||
case 'plugin': {
|
||||
if (typeof command.plugin != 'string') break;
|
||||
try {
|
||||
var pluginHandler = require('./pluginHandler.js').pluginHandler(parent.parent);
|
||||
pluginHandler.plugins[command.plugin].serveraction(command, obj, parent);
|
||||
} catch (e) {
|
||||
console.log('Error loading plugin handler (' + e + ')');
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
parent.agentStats.unknownAgentActionCount++;
|
||||
console.log('Unknown agent action (' + obj.remoteaddrport + '): ' + command.action + '.');
|
||||
|
@ -31,6 +31,7 @@ function CreateMeshCentralServer(config, args) {
|
||||
obj.swarmserver = null;
|
||||
obj.mailserver = null;
|
||||
obj.amtEventHandler = null;
|
||||
obj.pluginHandler = null;
|
||||
obj.amtScanner = null;
|
||||
obj.meshScanner = null;
|
||||
obj.letsencrypt = null;
|
||||
@ -702,6 +703,11 @@ function CreateMeshCentralServer(config, args) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Start plugin manager if configuration allows this.
|
||||
if ((obj.config) && (obj.config.settings) && (obj.config.settings.plugins != null)) {
|
||||
obj.pluginHandler = require("./pluginHandler.js").pluginHandler(obj);
|
||||
}
|
||||
|
||||
// Load the default meshcore and meshcmd
|
||||
obj.updateMeshCore();
|
||||
obj.updateMeshCmd();
|
||||
@ -865,6 +871,9 @@ function CreateMeshCentralServer(config, args) {
|
||||
// Dispatch an event that the server is now running
|
||||
obj.DispatchEvent(['*'], obj, { etype: 'server', action: 'started', msg: 'Server started' });
|
||||
|
||||
// Plugin hook. Need to run something at server startup? This is the place.
|
||||
if (obj.pluginHandler) { obj.pluginHandler.callHook("server_startup"); }
|
||||
|
||||
// Load the login cookie encryption key from the database if allowed
|
||||
if ((obj.config) && (obj.config.settings) && (obj.config.settings.allowlogintoken == true)) {
|
||||
obj.db.Get('LoginCookieEncryptionKey', function (err, docs) {
|
||||
@ -1348,6 +1357,9 @@ function CreateMeshCentralServer(config, args) {
|
||||
}
|
||||
}
|
||||
|
||||
// Add plugins to cores
|
||||
if (obj.pluginHandler) { obj.pluginHandler.addMeshCoreModules(modulesAdd); }
|
||||
|
||||
// Merge the cores and compute the hashes
|
||||
for (var i in modulesAdd) {
|
||||
if ((i == 'windows-recovery') || (i == 'linux-recovery')) {
|
||||
|
@ -198,7 +198,11 @@ module.exports.CreateMeshRelay = function (parent, ws, req, domain, user, cookie
|
||||
if (xdevicename2 != null) { metadata.devicename = xdevicename2; }
|
||||
var firstBlock = JSON.stringify(metadata);
|
||||
recordingEntry(fd, 1, ((req.query.browser) ? 2 : 0), firstBlock, function () {
|
||||
relayinfo.peer1.ws.logfile = ws.logfile = { fd: fd, lock: false };
|
||||
try { relayinfo.peer1.ws.logfile = ws.logfile = { fd: fd, lock: false }; } catch (ex) {
|
||||
try { ws.send('c'); } catch (ex) { } // Send connect to both peers, 'cr' indicates the session is being recorded.
|
||||
try { relayinfo.peer1.ws.send('c'); } catch (ex) { }
|
||||
return;
|
||||
}
|
||||
try { ws.send('cr'); } catch (ex) { } // Send connect to both peers, 'cr' indicates the session is being recorded.
|
||||
try { relayinfo.peer1.ws.send('cr'); } catch (ex) { }
|
||||
});
|
||||
|
33
meshuser.js
33
meshuser.js
@ -320,7 +320,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
|
||||
var httpport = ((args.aliasport != null) ? args.aliasport : args.port);
|
||||
|
||||
// Build server information object
|
||||
var serverinfo = { name: domain.dns ? domain.dns : parent.certificates.CommonName, mpsname: parent.certificates.AmtMpsName, mpsport: mpsport, mpspass: args.mpspass, port: httpport, emailcheck: ((parent.parent.mailserver != null) && (domain.auth != 'sspi') && (domain.auth != 'ldap') && (args.lanonly != true) && (parent.certificates.CommonName != null) && (parent.certificates.CommonName.indexOf('.') != -1)), domainauth: ((domain.auth == 'sspi') || (domain.auth == 'ldap')) };
|
||||
var serverinfo = { name: domain.dns ? domain.dns : parent.certificates.CommonName, mpsname: parent.certificates.AmtMpsName, mpsport: mpsport, mpspass: args.mpspass, port: httpport, emailcheck: ((parent.parent.mailserver != null) && (domain.auth != 'sspi') && (domain.auth != 'ldap') && (args.lanonly != true) && (parent.certificates.CommonName != null) && (parent.certificates.CommonName.indexOf('.') != -1)), domainauth: ((domain.auth == 'sspi') || (domain.auth == 'ldap')), serverTime: Date.now() };
|
||||
serverinfo.tlshash = Buffer.from(parent.webCertificateHashs[domain.id], 'binary').toString('hex').toUpperCase(); // SHA384 of server HTTPS certificate
|
||||
if ((parent.parent.config.domains[domain.id].amtacmactivation != null) && (parent.parent.config.domains[domain.id].amtacmactivation.acmmatch != null)) {
|
||||
var matchingDomains = [];
|
||||
@ -1110,7 +1110,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
|
||||
if (mesh.links[deluser._id] != null) { delete mesh.links[deluser._id]; parent.db.Set(common.escapeLinksFieldName(mesh)); }
|
||||
// Notify mesh change
|
||||
change = 'Removed user ' + deluser.name + ' from group ' + mesh.name;
|
||||
var event = { etype: 'mesh', userid: user._id, username: user.name, meshid: mesh._id, name: mesh.name, mtype: mesh.mtype, desc: mesh.desc, action: 'meshchange', links: mesh.links, msg: change, domain: domain.id };
|
||||
var event = { etype: 'mesh', userid: user._id, username: user.name, meshid: mesh._id, name: mesh.name, mtype: mesh.mtype, desc: mesh.desc, action: 'meshchange', msg: change, domain: domain.id };
|
||||
if (db.changeStream) { event.noact = 1; } // If DB change stream is active, don't use this event to change the mesh. Another event will come.
|
||||
parent.parent.DispatchEvent(['*', mesh._id, deluser._id, user._id], obj, event);
|
||||
}
|
||||
@ -1682,7 +1682,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
|
||||
parent.parent.DispatchEvent(targets, obj, event);
|
||||
|
||||
// Event the device group creation
|
||||
var event = { etype: 'mesh', userid: user._id, username: user.name, meshid: meshid, name: command.meshname, mtype: command.meshtype, desc: command.desc, action: 'createmesh', links: links, msg: 'Device group created: ' + command.meshname, domain: domain.id };
|
||||
var event = { etype: 'mesh', userid: user._id, username: user.name, meshid: meshid, name: command.meshname, mtype: command.meshtype, desc: command.desc, action: 'createmesh', msg: 'Device group created: ' + command.meshname, domain: domain.id };
|
||||
parent.parent.DispatchEvent(['*', meshid, user._id], obj, event); // Even if DB change stream is active, this event must be acted upon.
|
||||
|
||||
try { ws.send(JSON.stringify({ action: 'createmesh', responseid: command.responseid, result: 'ok', meshid: meshid })); } catch (ex) { }
|
||||
@ -1763,7 +1763,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
|
||||
if ((common.validateInt(command.consent) == true) && (command.consent != mesh.consent)) { if (change != '') change += ' and consent changed'; else change += 'Group "' + mesh.name + '" consent changed'; mesh.consent = command.consent; }
|
||||
if (change != '') {
|
||||
db.Set(common.escapeLinksFieldName(mesh));
|
||||
var event = { etype: 'mesh', userid: user._id, username: user.name, meshid: mesh._id, name: mesh.name, mtype: mesh.mtype, desc: mesh.desc, flags: mesh.flags, consent: mesh.consent, action: 'meshchange', links: mesh.links, msg: change, domain: domain.id };
|
||||
var event = { etype: 'mesh', userid: user._id, username: user.name, meshid: mesh._id, name: mesh.name, mtype: mesh.mtype, desc: mesh.desc, flags: mesh.flags, consent: mesh.consent, action: 'meshchange', msg: change, domain: domain.id };
|
||||
if (db.changeStream) { event.noact = 1; } // If DB change stream is active, don't use this event to change the mesh. Another event will come.
|
||||
parent.parent.DispatchEvent(['*', mesh._id, user._id], obj, event);
|
||||
}
|
||||
@ -1808,7 +1808,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
|
||||
db.Set(common.escapeLinksFieldName(mesh));
|
||||
|
||||
// Notify mesh change
|
||||
var event = { etype: 'mesh', username: newuser.name, userid: command.userid, meshid: mesh._id, name: mesh.name, mtype: mesh.mtype, desc: mesh.desc, action: 'meshchange', links: mesh.links, msg: 'Added user ' + newuser.name + ' to mesh ' + mesh.name, domain: domain.id };
|
||||
var event = { etype: 'mesh', username: newuser.name, userid: command.userid, meshid: mesh._id, name: mesh.name, mtype: mesh.mtype, desc: mesh.desc, action: 'meshchange', msg: 'Added user ' + newuser.name + ' to mesh ' + mesh.name, domain: domain.id };
|
||||
if (db.changeStream) { event.noact = 1; } // If DB change stream is active, don't use this event to change the mesh. Another event will come.
|
||||
parent.parent.DispatchEvent(['*', mesh._id, user._id, newuserid], obj, event);
|
||||
removedCount++;
|
||||
@ -1870,9 +1870,9 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
|
||||
// Notify mesh change
|
||||
var event;
|
||||
if (deluser != null) {
|
||||
event = { etype: 'mesh', username: user.name, userid: deluser.name, meshid: mesh._id, name: mesh.name, mtype: mesh.mtype, desc: mesh.desc, action: 'meshchange', links: mesh.links, msg: 'Removed user ' + deluser.name + ' from group ' + mesh.name, domain: domain.id };
|
||||
event = { etype: 'mesh', username: user.name, userid: deluser.name, meshid: mesh._id, name: mesh.name, mtype: mesh.mtype, desc: mesh.desc, action: 'meshchange', msg: 'Removed user ' + deluser.name + ' from group ' + mesh.name, domain: domain.id };
|
||||
} else {
|
||||
event = { etype: 'mesh', username: user.name, userid: (deluserid.split('/')[2]), meshid: mesh._id, name: mesh.name, mtype: mesh.mtype, desc: mesh.desc, action: 'meshchange', links: mesh.links, msg: 'Removed user ' + (deluserid.split('/')[2]) + ' from group ' + mesh.name, domain: domain.id };
|
||||
event = { etype: 'mesh', username: user.name, userid: (deluserid.split('/')[2]), meshid: mesh._id, name: mesh.name, mtype: mesh.mtype, desc: mesh.desc, action: 'meshchange', msg: 'Removed user ' + (deluserid.split('/')[2]) + ' from group ' + mesh.name, domain: domain.id };
|
||||
}
|
||||
parent.parent.DispatchEvent(['*', mesh._id, user._id, command.userid], obj, event);
|
||||
if (command.responseid != null) { try { ws.send(JSON.stringify({ action: 'removemeshuser', responseid: command.responseid, result: 'ok' })); } catch (ex) { } }
|
||||
@ -1913,7 +1913,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
|
||||
db.Set(common.escapeLinksFieldName(mesh));
|
||||
var amtpolicy2 = common.Clone(amtpolicy);
|
||||
delete amtpolicy2.password;
|
||||
var event = { etype: 'mesh', userid: user._id, username: user.name, meshid: mesh._id, amt: amtpolicy2, action: 'meshchange', links: mesh.links, msg: change, domain: domain.id };
|
||||
var event = { etype: 'mesh', userid: user._id, username: user.name, meshid: mesh._id, amt: amtpolicy2, action: 'meshchange', msg: change, domain: domain.id };
|
||||
if (db.changeStream) { event.noact = 1; } // If DB change stream is active, don't use this event to change the mesh. Another event will come.
|
||||
parent.parent.DispatchEvent(['*', mesh._id, user._id], obj, event);
|
||||
|
||||
@ -2014,6 +2014,12 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
|
||||
agentSession.sendUpdatedIntelAmtPolicy(); // Send the new Intel AMT policy
|
||||
}
|
||||
|
||||
// If any MQTT sessions are connected on this server, switch it now.
|
||||
if (parent.parent.mqttbroker != null) { parent.parent.mqttbroker.changeDeviceMesh(node._id, command.meshid); }
|
||||
|
||||
// If any CIRA sessions are connected on this server, switch it now.
|
||||
if (parent.parent.mpsserver != null) { parent.parent.mpsserver.changeDeviceMesh(node._id, command.meshid); }
|
||||
|
||||
// Add the connection state
|
||||
const state = parent.parent.GetConnectivityState(node._id);
|
||||
if (state) {
|
||||
@ -3001,6 +3007,17 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'plugin': {
|
||||
command.userid = user._id;
|
||||
|
||||
if (command.routeToNode === true) {
|
||||
routeCommandToNode(command);
|
||||
} else {
|
||||
// TODO
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
// Unknown user action
|
||||
console.log('Unknown action from user ' + user.name + ': ' + command.action + '.');
|
||||
|
142
mpsserver.js
142
mpsserver.js
@ -20,7 +20,7 @@ module.exports.CreateMpsServer = function (parent, db, args, certificates) {
|
||||
obj.db = db;
|
||||
obj.args = args;
|
||||
obj.certificates = certificates;
|
||||
obj.ciraConnections = {};
|
||||
obj.ciraConnections = {}; // NodeID --> Socket
|
||||
var tlsSessionStore = {}; // Store TLS session information for quick resume.
|
||||
var tlsSessionStoreCount = 0; // Number of cached TLS session information in store.
|
||||
const constants = (require('crypto').constants ? require('crypto').constants : require('constants')); // require('constants') is deprecated in Node 11.10, use require('crypto').constants instead.
|
||||
@ -272,41 +272,26 @@ module.exports.CreateMpsServer = function (parent, db, args, certificates) {
|
||||
socket.tag.connectTime = Date.now();
|
||||
socket.tag.host = '';
|
||||
|
||||
// Fetch the mesh
|
||||
obj.db.Get(socket.tag.meshid, function (err, meshes) {
|
||||
if ((meshes != null) && (meshes.length === 1)) {
|
||||
var mesh = meshes[0];
|
||||
obj.db.Get(socket.tag.nodeid, function (err, nodes) {
|
||||
if ((nodes == null) || (nodes.length !== 1)) {
|
||||
if (mesh.mtype == 1) {
|
||||
// Check if we already have too many devices for this domain
|
||||
if (domain.limits && (typeof domain.limits.maxdevices == 'number')) {
|
||||
db.isMaxType(domain.limits.maxdevices, 'node', domain.id, function (ismax, count) {
|
||||
if (ismax == true) {
|
||||
// Too many devices in this domain.
|
||||
maxDomainDevicesReached++;
|
||||
console.log('Too many devices on this domain to accept the CIRA connection. meshid: ' + socket.tag.meshid);
|
||||
socket.end();
|
||||
} else {
|
||||
// We are under the limit, create the new device.
|
||||
// Node is not in the database, add it. Credentials will be empty until added by the user.
|
||||
var device = { type: 'node', mtype: 1, _id: socket.tag.nodeid, meshid: socket.tag.meshid, name: socket.tag.name, host: null, domain: domainid, intelamt: { user: '', pass: '', tls: 0, state: 2 } };
|
||||
obj.db.Set(device);
|
||||
|
||||
// Event the new node
|
||||
addedTlsDeviceCount++;
|
||||
var device2 = common.Clone(device);
|
||||
if (device2.intelamt.pass != null) delete device2.intelamt.pass; // Remove the Intel AMT password before eventing this.
|
||||
var change = 'CIRA added device ' + socket.tag.name + ' to mesh ' + mesh.name;
|
||||
obj.parent.DispatchEvent(['*', socket.tag.meshid], obj, { etype: 'node', action: 'addnode', node: device2, msg: change, domain: domainid });
|
||||
|
||||
// Add the connection to the MPS connection list
|
||||
obj.ciraConnections[socket.tag.nodeid] = socket;
|
||||
obj.parent.SetConnectivityState(socket.tag.meshid, socket.tag.nodeid, socket.tag.connectTime, 2, 7); // TODO: Right now report power state as "present" (7) until we can poll.
|
||||
}
|
||||
});
|
||||
return;
|
||||
// Fetch the node
|
||||
obj.db.Get(socket.tag.nodeid, function (err, nodes) {
|
||||
if ((nodes == null) || (nodes.length !== 1)) {
|
||||
var mesh = obj.parent.webserver.meshes[socket.tag.meshid];
|
||||
if (mesh == null) {
|
||||
unknownTlsMeshIdCount++;
|
||||
console.log('ERROR: Intel AMT CIRA connected with unknown groupid: ' + socket.tag.meshid);
|
||||
socket.end();
|
||||
return;
|
||||
} else if (mesh.mtype == 1) {
|
||||
// Check if we already have too many devices for this domain
|
||||
if (domain.limits && (typeof domain.limits.maxdevices == 'number')) {
|
||||
db.isMaxType(domain.limits.maxdevices, 'node', domain.id, function (ismax, count) {
|
||||
if (ismax == true) {
|
||||
// Too many devices in this domain.
|
||||
maxDomainDevicesReached++;
|
||||
console.log('Too many devices on this domain to accept the CIRA connection. meshid: ' + socket.tag.meshid);
|
||||
socket.end();
|
||||
} else {
|
||||
// We are under the limit, create the new device.
|
||||
// Node is not in the database, add it. Credentials will be empty until added by the user.
|
||||
var device = { type: 'node', mtype: 1, _id: socket.tag.nodeid, meshid: socket.tag.meshid, name: socket.tag.name, host: null, domain: domainid, intelamt: { user: '', pass: '', tls: 0, state: 2 } };
|
||||
obj.db.Set(device);
|
||||
@ -317,30 +302,42 @@ module.exports.CreateMpsServer = function (parent, db, args, certificates) {
|
||||
if (device2.intelamt.pass != null) delete device2.intelamt.pass; // Remove the Intel AMT password before eventing this.
|
||||
var change = 'CIRA added device ' + socket.tag.name + ' to mesh ' + mesh.name;
|
||||
obj.parent.DispatchEvent(['*', socket.tag.meshid], obj, { etype: 'node', action: 'addnode', node: device2, msg: change, domain: domainid });
|
||||
}
|
||||
} else {
|
||||
// New CIRA connection for unknown node, disconnect.
|
||||
unknownTlsNodeCount++;
|
||||
console.log('CIRA connection for unknown node with incorrect group type. meshid: ' + socket.tag.meshid);
|
||||
socket.end();
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
// Node is already present
|
||||
var node = nodes[0];
|
||||
if ((node.intelamt != null) && (node.intelamt.state == 2)) { socket.tag.host = node.intelamt.host; }
|
||||
}
|
||||
|
||||
// Add the connection to the MPS connection list
|
||||
obj.ciraConnections[socket.tag.nodeid] = socket;
|
||||
obj.parent.SetConnectivityState(socket.tag.meshid, socket.tag.nodeid, socket.tag.connectTime, 2, 7); // TODO: Right now report power state as "present" (7) until we can poll.
|
||||
});
|
||||
// Add the connection to the MPS connection list
|
||||
obj.ciraConnections[socket.tag.nodeid] = socket;
|
||||
obj.parent.SetConnectivityState(socket.tag.meshid, socket.tag.nodeid, socket.tag.connectTime, 2, 7); // TODO: Right now report power state as "present" (7) until we can poll.
|
||||
}
|
||||
});
|
||||
return;
|
||||
} else {
|
||||
// Node is not in the database, add it. Credentials will be empty until added by the user.
|
||||
var device = { type: 'node', mtype: 1, _id: socket.tag.nodeid, meshid: socket.tag.meshid, name: socket.tag.name, host: null, domain: domainid, intelamt: { user: '', pass: '', tls: 0, state: 2 } };
|
||||
obj.db.Set(device);
|
||||
|
||||
// Event the new node
|
||||
addedTlsDeviceCount++;
|
||||
var device2 = common.Clone(device);
|
||||
if (device2.intelamt.pass != null) delete device2.intelamt.pass; // Remove the Intel AMT password before eventing this.
|
||||
var change = 'CIRA added device ' + socket.tag.name + ' to mesh ' + mesh.name;
|
||||
obj.parent.DispatchEvent(['*', socket.tag.meshid], obj, { etype: 'node', action: 'addnode', node: device2, msg: change, domain: domainid });
|
||||
}
|
||||
} else {
|
||||
// New CIRA connection for unknown node, disconnect.
|
||||
unknownTlsNodeCount++;
|
||||
console.log('CIRA connection for unknown node with incorrect group type. meshid: ' + socket.tag.meshid);
|
||||
socket.end();
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
unknownTlsMeshIdCount++;
|
||||
console.log('ERROR: Intel AMT CIRA connected with unknown groupid: ' + socket.tag.meshid);
|
||||
socket.end();
|
||||
return;
|
||||
// Node is already present
|
||||
var node = nodes[0];
|
||||
socket.tag.meshid = node.meshid; // Correct the MeshID if the node has moved.
|
||||
if ((node.intelamt != null) && (node.intelamt.state == 2)) { socket.tag.host = node.intelamt.host; }
|
||||
}
|
||||
|
||||
// Add the connection to the MPS connection list
|
||||
obj.ciraConnections[socket.tag.nodeid] = socket;
|
||||
obj.parent.SetConnectivityState(socket.tag.meshid, socket.tag.nodeid, socket.tag.connectTime, 2, 7); // TODO: Right now report power state as "present" (7) until we can poll.
|
||||
});
|
||||
} else {
|
||||
// This node connected without certificate authentication, use password auth
|
||||
@ -410,7 +407,7 @@ module.exports.CreateMpsServer = function (parent, db, args, certificates) {
|
||||
if (usernameLen != 16) { badUserNameLengthCount++; parent.debug('mps', 'Username length not 16', username, password); SendUserAuthFail(socket); return -1; }
|
||||
var meshIdStart = '/' + username, mesh = null;
|
||||
if (obj.parent.webserver.meshes) { for (var i in obj.parent.webserver.meshes) { if (obj.parent.webserver.meshes[i]._id.replace(/\@/g, 'X').replace(/\$/g, 'X').indexOf(meshIdStart) > 0) { mesh = obj.parent.webserver.meshes[i]; break; } } }
|
||||
if (mesh == null) { meshNotFoundCount++; parent.debug('mps', 'Mesh not found', username, password); SendUserAuthFail(socket); return -1; }
|
||||
if (mesh == null) { meshNotFoundCount++; parent.debug('mps', 'Device group not found', username, password); SendUserAuthFail(socket); return -1; }
|
||||
|
||||
// If this is a agent-less mesh, use the device guid 3 times as ID.
|
||||
if (mesh.mtype == 1) {
|
||||
@ -470,6 +467,7 @@ module.exports.CreateMpsServer = function (parent, db, args, certificates) {
|
||||
} else {
|
||||
// Node is already present
|
||||
var node = nodes[0];
|
||||
socket.tag.meshid = node.meshid; // Correct the MeshID if the node has moved.
|
||||
if ((node.intelamt != null) && (node.intelamt.state == 2)) { socket.tag.host = node.intelamt.host; }
|
||||
}
|
||||
|
||||
@ -480,8 +478,8 @@ module.exports.CreateMpsServer = function (parent, db, args, certificates) {
|
||||
});
|
||||
} else if (mesh.mtype == 2) { // If this is a agent mesh, search the mesh for this device UUID
|
||||
// Intel AMT GUID (socket.tag.SystemId) will be used to search the node
|
||||
obj.db.getAmtUuidNode(mesh._id, socket.tag.SystemId, function (err, nodes) { // TODO: May need to optimize this request with indexes
|
||||
if ((nodes == null) || (nodes.length !== 1)) {
|
||||
obj.db.getAmtUuidNode(socket.tag.SystemId, function (err, nodes) { // TODO: May need to optimize this request with indexes
|
||||
if ((nodes == null) || (nodes.length === 0) || (obj.parent.webserver.meshes == null)) {
|
||||
// New CIRA connection for unknown node, disconnect.
|
||||
unknownNodeCount++;
|
||||
console.log('CIRA connection for unknown node. groupid: ' + mesh._id + ', uuid: ' + socket.tag.SystemId);
|
||||
@ -489,11 +487,27 @@ module.exports.CreateMpsServer = function (parent, db, args, certificates) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Looking at nodes that match this UUID, select one in the same domain and mesh type.
|
||||
var node = null;
|
||||
for (var i in nodes) {
|
||||
if (mesh.domain == nodes[i].domain) {
|
||||
var nodemesh = obj.parent.webserver.meshes[nodes[i].meshid];
|
||||
if ((nodemesh != null) && (nodemesh.mtype == 2)) { node = nodes[i]; }
|
||||
}
|
||||
}
|
||||
|
||||
if (node == null) {
|
||||
// New CIRA connection for unknown node, disconnect.
|
||||
unknownNodeCount++;
|
||||
console.log('CIRA connection for unknown node. candidate(s): ' + nodes.length + ', groupid: ' + mesh._id + ', uuid: ' + socket.tag.SystemId);
|
||||
socket.end();
|
||||
return;
|
||||
}
|
||||
|
||||
// Node is present
|
||||
var node = nodes[0];
|
||||
if ((node.intelamt != null) && (node.intelamt.state == 2)) { socket.tag.host = node.intelamt.host; }
|
||||
socket.tag.nodeid = node._id;
|
||||
socket.tag.meshid = mesh._id;
|
||||
socket.tag.meshid = node.meshid; // Correct the MeshID if the node has moved.
|
||||
socket.tag.connectTime = Date.now();
|
||||
|
||||
// Add the connection to the MPS connection list
|
||||
@ -920,6 +934,12 @@ module.exports.CreateMpsServer = function (parent, db, args, certificates) {
|
||||
});
|
||||
}
|
||||
|
||||
// Change a node to a new meshid, this is called when a node changes groups.
|
||||
obj.changeDeviceMesh = function (nodeid, newMeshId) {
|
||||
var socket = obj.ciraConnections[nodeid];
|
||||
if ((socket != null) && (socket.tag != null)) { socket.tag.meshid = newMeshId; }
|
||||
}
|
||||
|
||||
function guidToStr(g) { return g.substring(6, 8) + g.substring(4, 6) + g.substring(2, 4) + g.substring(0, 2) + "-" + g.substring(10, 12) + g.substring(8, 10) + "-" + g.substring(14, 16) + g.substring(12, 14) + "-" + g.substring(16, 20) + "-" + g.substring(20); }
|
||||
|
||||
return obj;
|
||||
|
@ -58,6 +58,7 @@ module.exports.CreateMQTTBroker = function (parent, db, args) {
|
||||
// Set the client nodeid and meshid
|
||||
client.xdbNodeKey = 'node/' + xdomainid + '/' + xnodeid;
|
||||
client.xdbMeshKey = 'mesh/' + xdomainid + '/' + xmeshid;
|
||||
client.xdomainid = xdomainid;
|
||||
|
||||
// Check if this node exists in the database
|
||||
db.Get(client.xdbNodeKey, function (err, nodes) {
|
||||
@ -107,7 +108,7 @@ module.exports.CreateMQTTBroker = function (parent, db, args) {
|
||||
aedes.authorizePublish = function (client, packet, callback) {
|
||||
// Handle a published message
|
||||
obj.parent.debug("mqtt", "AuthorizePublish, " + client.conn.xtransport + "://" + cleanRemoteAddr(client.conn.xip));
|
||||
handleMessage(client.xdbNodeKey, client.xdbMeshKey, packet.topic, packet.payload);
|
||||
handleMessage(client.xdbNodeKey, client.xdbMeshKey, client.xdomainid, packet.topic, packet.payload);
|
||||
// We don't accept that any client message be published, so don't call the callback.
|
||||
}
|
||||
|
||||
@ -128,9 +129,9 @@ module.exports.CreateMQTTBroker = function (parent, db, args) {
|
||||
}
|
||||
|
||||
// Handle messages coming from clients
|
||||
function handleMessage(nodeid, meshid, topic, message) {
|
||||
function handleMessage(nodeid, meshid, domainid, topic, message) {
|
||||
// Handle messages here
|
||||
if (topic == 'console') { routeMessage({ action: 'msg', type: 'console', value: message.toString(), source: 'MQTT' }, nodeid, meshid); return; } // Handle console messages
|
||||
if (topic == 'console') { parent.webserver.routeAgentCommand({ action: 'msg', type: 'console', value: message.toString(), source: 'MQTT' }, domainid, nodeid, meshid); return; } // Handle console messages
|
||||
|
||||
//console.log('handleMessage', nodeid, topic, message.toString());
|
||||
//obj.publish(nodeid, 'echoTopic', "Echo: " + message.toString());
|
||||
@ -139,78 +140,10 @@ module.exports.CreateMQTTBroker = function (parent, db, args) {
|
||||
// Clean a IPv6 address that encodes a IPv4 address
|
||||
function cleanRemoteAddr(addr) { if (typeof addr != 'string') { return null; } if (addr.indexOf('::ffff:') == 0) { return addr.substring(7); } else { return addr; } }
|
||||
|
||||
// Route a message
|
||||
function routeMessage(command, dbNodeKey, dbMeshKey) {
|
||||
// Route a message.
|
||||
// If this command has a sessionid, that is the target.
|
||||
if (command.sessionid != null) {
|
||||
if (typeof command.sessionid != 'string') return;
|
||||
var splitsessionid = command.sessionid.split('/');
|
||||
// Check that we are in the same domain and the user has rights over this node.
|
||||
if ((splitsessionid[0] == 'user') && (splitsessionid[1] == domain.id)) {
|
||||
// Check if this user has rights to get this message
|
||||
//if (mesh.links[user._id] == null || ((mesh.links[user._id].rights & 16) == 0)) return; // TODO!!!!!!!!!!!!!!!!!!!!!
|
||||
|
||||
// See if the session is connected. If so, go ahead and send this message to the target node
|
||||
var ws = parent.webserver.wssessions2[command.sessionid];
|
||||
if (ws != null) {
|
||||
command.nodeid = dbNodeKey; // Set the nodeid, required for responses.
|
||||
delete command.sessionid; // Remove the sessionid, since we are sending to that sessionid, so it's implyed.
|
||||
try { ws.send(JSON.stringify(command)); } catch (ex) { }
|
||||
} else if (parent.multiServer != null) {
|
||||
// See if we can send this to a peer server
|
||||
var serverid = parent.webserver.wsPeerSessions2[command.sessionid];
|
||||
if (serverid != null) {
|
||||
command.fromNodeid = dbNodeKey;
|
||||
parent.multiServer.DispatchMessageSingleServer(command, serverid);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (command.userid != null) { // If this command has a userid, that is the target.
|
||||
if (typeof command.userid != 'string') return;
|
||||
var splituserid = command.userid.split('/');
|
||||
// Check that we are in the same domain and the user has rights over this node.
|
||||
if ((splituserid[0] == 'user') && (splituserid[1] == domain.id)) {
|
||||
// Check if this user has rights to get this message
|
||||
//if (mesh.links[user._id] == null || ((mesh.links[user._id].rights & 16) == 0)) return; // TODO!!!!!!!!!!!!!!!!!!!!!
|
||||
|
||||
// See if the session is connected
|
||||
var sessions = parent.webserver.wssessions[command.userid];
|
||||
|
||||
// Go ahead and send this message to the target node
|
||||
if (sessions != null) {
|
||||
command.nodeid = dbNodeKey; // Set the nodeid, required for responses.
|
||||
delete command.userid; // Remove the userid, since we are sending to that userid, so it's implyed.
|
||||
for (i in sessions) { sessions[i].send(JSON.stringify(command)); }
|
||||
}
|
||||
|
||||
if (parent.multiServer != null) {
|
||||
// TODO: Add multi-server support
|
||||
}
|
||||
}
|
||||
} else { // Route this command to the mesh
|
||||
command.nodeid = dbNodeKey;
|
||||
var cmdstr = JSON.stringify(command);
|
||||
for (var userid in parent.webserver.wssessions) { // Find all connected users for this mesh and send the message
|
||||
var user = parent.webserver.users[userid];
|
||||
if ((user != null) && (user.links != null)) {
|
||||
var rights = user.links[dbMeshKey];
|
||||
if (rights != null) { // TODO: Look at what rights are needed for message routing
|
||||
var xsessions = parent.webserver.wssessions[userid];
|
||||
// Send the message to all users on this server
|
||||
for (i in xsessions) { try { xsessions[i].send(cmdstr); } catch (e) { } }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Send the message to all users of other servers
|
||||
if (parent.multiServer != null) {
|
||||
delete command.nodeid;
|
||||
command.fromNodeid = dbNodeKey;
|
||||
command.meshid = dbMeshKey;
|
||||
parent.multiServer.DispatchMessage(command);
|
||||
}
|
||||
}
|
||||
// Change a node to a new meshid
|
||||
obj.changeDeviceMesh = function(nodeid, newMeshId) {
|
||||
var nodes = obj.connections[nodeid];
|
||||
if (nodes != null) { for (var i in nodes) { nodes[i].xdbMeshKey = newMeshId; } }
|
||||
}
|
||||
|
||||
return obj;
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "meshcentral",
|
||||
"version": "0.4.1-x",
|
||||
"version": "0.4.2-e",
|
||||
"keywords": [
|
||||
"Remote Management",
|
||||
"Intel AMT",
|
||||
|
158
pluginHandler.js
Normal file
158
pluginHandler.js
Normal file
@ -0,0 +1,158 @@
|
||||
/**
|
||||
* @description MeshCentral plugin module
|
||||
* @author Ryan Blenis
|
||||
* @copyright
|
||||
* @license Apache-2.0
|
||||
* @version v0.0.1
|
||||
*/
|
||||
|
||||
/*xjslint node: true */
|
||||
/*xjslint plusplus: true */
|
||||
/*xjslint maxlen: 256 */
|
||||
/*jshint node: true */
|
||||
/*jshint strict: false */
|
||||
/*jshint esversion: 6 */
|
||||
"use strict";
|
||||
|
||||
module.exports.pluginHandler = function (parent) {
|
||||
var obj = {};
|
||||
|
||||
obj.fs = require('fs');
|
||||
obj.path = require('path');
|
||||
obj.parent = parent;
|
||||
obj.pluginPath = obj.parent.path.join(obj.parent.datapath, 'plugins');
|
||||
obj.plugins = {};
|
||||
obj.exports = {};
|
||||
obj.loadList = obj.parent.config.settings.plugins.list;
|
||||
|
||||
if (typeof obj.loadList != 'object') {
|
||||
obj.loadList = {};
|
||||
console.log('Plugin list not specified, please fix configuration file.');
|
||||
return null;
|
||||
}
|
||||
|
||||
obj.loadList.forEach(function (plugin, index) {
|
||||
if (obj.fs.existsSync(obj.pluginPath + '/' + plugin)) {
|
||||
try {
|
||||
obj.plugins[plugin] = require(obj.pluginPath + '/' + plugin + '/' + plugin + '.js')[plugin](obj);
|
||||
obj.exports[plugin] = obj.plugins[plugin].exports;
|
||||
} catch (e) {
|
||||
console.log("Error loading plugin: " + plugin + " (" + e + "). It has been disabled.", e.stack);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
obj.prepExports = function () {
|
||||
var str = 'function() {\r\n';
|
||||
str += ' var obj = {};\r\n';
|
||||
|
||||
for (const p of Object.keys(obj.plugins)) {
|
||||
str += ' obj.' + p + ' = {};\r\n';
|
||||
for (const l of Object.values(obj.exports[p])) {
|
||||
str += ' obj.' + p + '.' + l + ' = ' + obj.plugins[p][l].toString() + '\r\n';
|
||||
}
|
||||
}
|
||||
|
||||
str += `obj.onDeviceRefeshEnd = function(nodeid, panel, refresh, event) {
|
||||
for (const p of Object.keys(obj)) {
|
||||
if (typeof obj[p].onDeviceRefreshEnd == 'function') {
|
||||
obj[p].onDeviceRefreshEnd(nodeid, panel, refresh, event);
|
||||
}
|
||||
}
|
||||
};
|
||||
obj.registerPluginTab = function(pluginRegInfo) {
|
||||
var d = pluginRegInfo();
|
||||
if (!Q(d.tabId)) {
|
||||
QA('p19headers', '<span onclick="return pluginHandler.callPluginPage(\\''+d.tabId+'\\');">'+d.tabTitle+'</span>');
|
||||
}
|
||||
};
|
||||
obj.callPluginPage = function(id) {
|
||||
var pages = Q('p19pages').querySelectorAll("#p19pages>div");
|
||||
for (const i of pages) { i.style.display = 'none'; }
|
||||
QV(id, true);
|
||||
};
|
||||
return obj; };`;
|
||||
return str;
|
||||
}
|
||||
|
||||
obj.callHook = function (hookName, ...args) {
|
||||
for (var p in obj.plugins) {
|
||||
if (typeof obj.plugins[p][hookName] == 'function') {
|
||||
try {
|
||||
obj.plugins[p][hookName](args);
|
||||
} catch (e) {
|
||||
console.log('Error ocurred while running plugin hook' + p + ':' + hookName + ' (' + e + ')');
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
obj.addMeshCoreModules = function (modulesAdd) {
|
||||
for (var plugin in obj.plugins) {
|
||||
var moduleDirPath = null;
|
||||
var modulesDir = null;
|
||||
//if (obj.args.minifycore !== false) { try { moduleDirPath = obj.path.join(obj.pluginPath, 'modules_meshcore_min'); modulesDir = obj.fs.readdirSync(moduleDirPath); } catch (e) { } } // Favor minified modules if present.
|
||||
if (modulesDir == null) { try { moduleDirPath = obj.path.join(obj.pluginPath, plugin + '/modules_meshcore'); modulesDir = obj.fs.readdirSync(moduleDirPath); } catch (e) { } } // Use non-minified mofules.
|
||||
if (modulesDir != null) {
|
||||
for (var i in modulesDir) {
|
||||
if (modulesDir[i].toLowerCase().endsWith('.js')) {
|
||||
var moduleName = modulesDir[i].substring(0, modulesDir[i].length - 3);
|
||||
if (moduleName.endsWith('.min')) { moduleName = moduleName.substring(0, moduleName.length - 4); } // Remove the ".min" for ".min.js" files.
|
||||
var moduleData = ['try { addModule("', moduleName, '", "', obj.parent.escapeCodeString(obj.fs.readFileSync(obj.path.join(moduleDirPath, modulesDir[i])).toString('binary')), '"); addedModules.push("', moduleName, '"); } catch (e) { }\r\n'];
|
||||
|
||||
// Merge this module
|
||||
// NOTE: "smbios" module makes some non-AI Linux segfault, only include for IA platforms.
|
||||
if (moduleName.startsWith('amt-') || (moduleName == 'smbios')) {
|
||||
// Add to IA / Intel AMT cores only
|
||||
modulesAdd['windows-amt'].push(...moduleData);
|
||||
modulesAdd['linux-amt'].push(...moduleData);
|
||||
} else if (moduleName.startsWith('win-')) {
|
||||
// Add to Windows cores only
|
||||
modulesAdd['windows-amt'].push(...moduleData);
|
||||
} else if (moduleName.startsWith('linux-')) {
|
||||
// Add to Linux cores only
|
||||
modulesAdd['linux-amt'].push(...moduleData);
|
||||
modulesAdd['linux-noamt'].push(...moduleData);
|
||||
} else {
|
||||
// Add to all cores
|
||||
modulesAdd['windows-amt'].push(...moduleData);
|
||||
modulesAdd['linux-amt'].push(...moduleData);
|
||||
modulesAdd['linux-noamt'].push(...moduleData);
|
||||
}
|
||||
|
||||
// Merge this module to recovery modules if needed
|
||||
if (modulesAdd['windows-recovery'] != null) {
|
||||
if ((moduleName == 'win-console') || (moduleName == 'win-message-pump') || (moduleName == 'win-terminal')) {
|
||||
modulesAdd['windows-recovery'].push(...moduleData);
|
||||
}
|
||||
}
|
||||
|
||||
// Merge this module to agent recovery modules if needed
|
||||
if (modulesAdd['windows-agentrecovery'] != null) {
|
||||
if ((moduleName == 'win-console') || (moduleName == 'win-message-pump') || (moduleName == 'win-terminal')) {
|
||||
modulesAdd['windows-agentrecovery'].push(...moduleData);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
obj.deviceViewPanel = function () {
|
||||
var panel = {};
|
||||
for (var p in obj.plugins) {
|
||||
if (typeof obj.plugins[p][hookName] == 'function') {
|
||||
try {
|
||||
panel[p].header = obj.plugins[p].on_device_header();
|
||||
panel[p].content = obj.plugins[p].on_device_page();
|
||||
} catch (e) {
|
||||
console.log('Error ocurred while getting plugin views ' + p + ':' + ' (' + e + ')');
|
||||
}
|
||||
}
|
||||
}
|
||||
return panel;
|
||||
}
|
||||
|
||||
return obj;
|
||||
};
|
@ -75,8 +75,8 @@ Command line arguments on any platform:
|
||||
| ------------------------------------- | -----------
|
||||
| --notls | Use HTTP instead of HTTPS for the main web server.
|
||||
| --user [username] | Always login as [username] if the account exists.
|
||||
| --port [number] | Web server port number (default to 443).
|
||||
| --mpsport [number] | Intel AMT server port number (default to 4433).
|
||||
| --port [number] | Web server port number (default is 443).
|
||||
| --mpsport [number] | Intel AMT server port number (default is 4433).
|
||||
| --redirport [number] | Redirection web server, redirects users to the HTTPS server (default to 80).
|
||||
| --exactports | Server must run with correct ports or exit.
|
||||
| --cert [name], (country), (org) | Create a web server certificate with a server name. Country and organization can optionaly be set.
|
||||
|
File diff suppressed because one or more lines are too long
@ -122,6 +122,7 @@
|
||||
<td tabindex=0 id=MainDevInfo class="topbar_td style3x" onclick=go(17,event) onkeypress="if (event.key == 'Enter') go(17)">Details</td>
|
||||
<td tabindex=0 id=MainDevAmt class="topbar_td style3x" onclick=go(14,event) onkeypress="if (event.key == 'Enter') go(14)">Intel® AMT</td>
|
||||
<td tabindex=0 id=MainDevConsole class="topbar_td style3x" onclick=go(15,event) onkeypress="if (event.key == 'Enter') go(15)">Console</td>
|
||||
<td tabindex=0 id=MainDevPlugins class="topbar_td style3x" onclick=go(19,event) onkeypress="if (event.key == 'Enter') go(19)">Plugins</td>
|
||||
<td class="topbar_td_end style3"> </td>
|
||||
</tr>
|
||||
</table>
|
||||
@ -847,6 +848,26 @@
|
||||
</div>
|
||||
<div id=p41events style=""></div>
|
||||
</div>
|
||||
<div id=p19 style="display:none">
|
||||
<h1>Plugins - <span id=p19deviceName></span></h1>
|
||||
<style>
|
||||
#p19headers {
|
||||
padding-right: 7px;
|
||||
padding-bottom: 10px;
|
||||
font-weight: bold;
|
||||
border-bottom: 1px dotted blue;
|
||||
}
|
||||
#p19headers > span:nth-child(n+2) {
|
||||
border-left: 1px solid black;
|
||||
}
|
||||
#p19headers > span {
|
||||
padding-left: 4px;
|
||||
padding-right: 4px;
|
||||
}
|
||||
</style>
|
||||
<div id="p19headers"></div>
|
||||
<div id=p19pages></div>
|
||||
</div>
|
||||
<br id="column_l_bottomgap" />
|
||||
</div>
|
||||
<div id="footer">
|
||||
@ -1014,6 +1035,9 @@
|
||||
var nightMode = (getstore('_nightMode', '0') == '1');
|
||||
var sessionActivity = Date.now();
|
||||
var updateSessionTimer = null;
|
||||
var pluginHandlerBuilder = {{{pluginHandler}}};
|
||||
var pluginHandler = null;
|
||||
if (pluginHandlerBuilder != null) { pluginHandler = new pluginHandlerBuilder(); }
|
||||
|
||||
// Console Message Display Timers
|
||||
var p11DeskConsoleMsgTimer = null;
|
||||
@ -1447,6 +1471,7 @@
|
||||
case 'serverinfo': {
|
||||
serverinfo = message.serverinfo;
|
||||
if (serverinfo.timeout) { setInterval(checkIdleSessionTimeout, 10000); checkIdleSessionTimeout(); }
|
||||
if (debugmode == 1) { console.log("Server time: ", printDateTime(new Date(serverinfo.serverTime))); }
|
||||
break;
|
||||
}
|
||||
case 'userinfo': {
|
||||
@ -2304,6 +2329,11 @@
|
||||
QH('p0span', message.msg);
|
||||
break;
|
||||
}
|
||||
case 'plugin': {
|
||||
if ((pluginHandler == null) || (typeof message.plugin != 'string')) break;
|
||||
try { pluginHandler[message.plugin][message.method](server, message); } catch (e) { console.log('Error loading plugin handler ('+ e + ')'); }
|
||||
break;
|
||||
}
|
||||
default:
|
||||
//console.log('Unknown message.action', message.action);
|
||||
break;
|
||||
@ -4144,6 +4174,7 @@
|
||||
QH('p15deviceName', 'Console - ' + nname);
|
||||
QH('p16deviceName', nname);
|
||||
QH('p17deviceName', nname);
|
||||
QH('p19deviceName', nname);
|
||||
|
||||
// Node attributes
|
||||
var x = '<table style=width:100%>';
|
||||
@ -4355,6 +4386,7 @@
|
||||
QV('MainDevFiles', ((mesh.mtype == 2) && ((node.agent == null) || (node.agent.caps == null) || ((node.agent.caps & 4) != 0))) && (meshrights & 8) && fileAccess);
|
||||
QV('MainDevAmt', (node.intelamt != null) && ((node.intelamt.state == 2) || (node.conn & 2)) && (meshrights & 8) && amtAccess);
|
||||
QV('MainDevConsole', (consoleRights && (mesh.mtype == 2) && ((node.agent == null) || (node.agent.caps == null) || ((node.agent.caps & 8) != 0))) && (meshrights & 8));
|
||||
QV('MainDevPlugins', pluginHandler != null);
|
||||
QV('p15uploadCore', (node.agent != null) && (node.agent.caps != null) && ((node.agent.caps & 16) != 0));
|
||||
QH('p15coreName', ((node.agent != null) && (node.agent.core != null))?node.agent.core:'');
|
||||
|
||||
@ -4399,6 +4431,9 @@
|
||||
p11clearConsoleMsg();
|
||||
p12clearConsoleMsg();
|
||||
p13clearConsoleMsg();
|
||||
|
||||
// Device refresh plugin handler
|
||||
if (pluginHandler != null) { pluginHandler.onDeviceRefeshEnd(nodeid, panel, refresh, event); }
|
||||
}
|
||||
setupDesktop(); // Always refresh the desktop, even if we are on the same device, we need to do some canvas switching.
|
||||
if (!panel) panel = 10;
|
||||
@ -6476,7 +6511,7 @@
|
||||
if ((consoleNode.conn & 16) != 0) { onlineText += ', MQTT is online' }
|
||||
QH('p15statetext', onlineText);
|
||||
QE('p15consoleText', online);
|
||||
QE('p15uploadCore', online);
|
||||
QE('p15uploadCore', ((consoleNode.conn & 1) != 0));
|
||||
QV('p15outputselecttd', (consoleNode.conn & 17) == 17);
|
||||
} else {
|
||||
QH('p15statetext', 'Access Denied');
|
||||
@ -6502,7 +6537,7 @@
|
||||
var consoleHistory = [];
|
||||
function p15consoleSend(e) {
|
||||
if (e && e.keyCode != 13) return;
|
||||
var v = Q('p15consoleText').value, t = '<div style=color:green>> ' + v + '<br/></div>';
|
||||
var v = Q('p15consoleText').value, t = '<div style=color:green>> ' + EscapeHtml(v) + '<br/></div>';
|
||||
|
||||
if (xxcurrentView == 115) {
|
||||
// Send the command to the server - TODO: In the future, we may support multiple servers.
|
||||
@ -6539,7 +6574,7 @@
|
||||
function p15consoleReceive(node, data, source) {
|
||||
if (node === 'serverconsole') {
|
||||
// Server console data
|
||||
data = '<div>' + EscapeHtml(data) + '</div>'
|
||||
data = '<div>' + data + '</div>'
|
||||
consoleServerText += data;
|
||||
if (consoleNode == 'server') {
|
||||
Q('p15agentConsoleText').innerHTML += data;
|
||||
@ -6547,7 +6582,7 @@
|
||||
}
|
||||
} else {
|
||||
// Agent console data
|
||||
if (source == 'MQTT') { data = '<div style=color:red>MQTT> ' + EscapeHtml(data) + '<br/></div>'; } else { data = '<div>' + EscapeHtml(data) + '</div>' }
|
||||
if (source == 'MQTT') { data = '<div style=color:red>MQTT> ' + EscapeHtml(data) + '<br/></div>'; } else { data = '<div>' + data + '</div>' }
|
||||
if (node.consoleText == null) { node.consoleText = data; } else { node.consoleText += data; }
|
||||
if (consoleNode == node) {
|
||||
Q('p15agentConsoleText').innerHTML += data;
|
||||
@ -9131,16 +9166,14 @@
|
||||
|
||||
// If we are going to panel 0 in "full screen mode", hide the left bar.
|
||||
QV('topbar', x != 0);
|
||||
if ((x == 0) && (webPageFullScreen)) {
|
||||
QC('body').add("arg_hide");
|
||||
}
|
||||
if ((x == 0) && (webPageFullScreen)) { QC('body').add("arg_hide"); }
|
||||
|
||||
QV('MainSubMenuSpan', x >= 10 && x < 20);
|
||||
QV('UserDummyMenuSpan', (x < 10) && (x != 6) && webPageFullScreen);
|
||||
QV('MeshSubMenuSpan', x >= 20 && x < 30);
|
||||
QV('UserSubMenuSpan', x >= 30 && x < 40);
|
||||
QV('ServerSubMenuSpan', x == 6 || x == 115 || x == 40 || x == 41);
|
||||
var panels = { 10: 'MainDev', 11: 'MainDevDesktop', 12: 'MainDevTerminal', 13: 'MainDevFiles', 14: 'MainDevAmt', 15: 'MainDevConsole', 16: 'MainDevEvents', 17: 'MainDevInfo', 20: 'MeshGeneral', 30: 'UserGeneral', 31: 'UserEvents', 6: 'ServerGeneral', 40: 'ServerStats', 41: 'ServerTrace', 115: 'ServerConsole' };
|
||||
var panels = { 10: 'MainDev', 11: 'MainDevDesktop', 12: 'MainDevTerminal', 13: 'MainDevFiles', 14: 'MainDevAmt', 15: 'MainDevConsole', 16: 'MainDevEvents', 17: 'MainDevInfo', 19: 'MainDevPlugins', 20: 'MeshGeneral', 30: 'UserGeneral', 31: 'UserEvents', 6: 'ServerGeneral', 40: 'ServerStats', 41: 'ServerTrace', 115: 'ServerConsole' };
|
||||
for (var i in panels) {
|
||||
QC(panels[i]).remove('style3x');
|
||||
QC(panels[i]).remove('style3sel');
|
||||
|
82
webserver.js
82
webserver.js
@ -1229,7 +1229,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
|
||||
if (mesh.links[userid] != null) { delete mesh.links[userid]; obj.db.Set(obj.common.escapeLinksFieldName(mesh)); }
|
||||
// Notify mesh change
|
||||
var change = 'Removed user ' + user.name + ' from group ' + mesh.name;
|
||||
obj.parent.DispatchEvent(['*', mesh._id, user._id, userid], obj, { etype: 'mesh', userid: user._id, username: user.name, meshid: mesh._id, name: mesh.name, mtype: mesh.mtype, desc: mesh.desc, action: 'meshchange', links: mesh.links, msg: change, domain: domain.id });
|
||||
obj.parent.DispatchEvent(['*', mesh._id, user._id, userid], obj, { etype: 'mesh', userid: user._id, username: user.name, meshid: mesh._id, name: mesh.name, mtype: mesh.mtype, desc: mesh.desc, action: 'meshchange', msg: change, domain: domain.id });
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1517,12 +1517,12 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
|
||||
|
||||
// Clean up the U2F challenge if needed
|
||||
if (req.session.u2fchallenge) { delete req.session.u2fchallenge; };
|
||||
|
||||
|
||||
// Fetch the web state
|
||||
parent.debug('web', 'handleRootRequestEx: success.');
|
||||
obj.db.Get('ws' + user._id, function (err, states) {
|
||||
var webstate = (states.length == 1) ? states[0].state : '';
|
||||
res.render(getRenderPage('default', req), { authCookie: authCookie, viewmode: viewmode, currentNode: currentNode, logoutControl: logoutcontrol, title: domain.title, title2: domain.title2, extitle: encodeURIComponent(domain.title), extitle2: encodeURIComponent(domain.title2), domainurl: domain.url, domain: domain.id, debuglevel: parent.debugLevel, serverDnsName: obj.getWebServerName(domain), serverRedirPort: args.redirport, serverPublicPort: httpsPort, noServerBackup: (args.noserverbackup == 1 ? 1 : 0), features: features, sessiontime: args.sessiontime, mpspass: args.mpspass, passRequirements: passRequirements, webcerthash: Buffer.from(obj.webCertificateFullHashs[domain.id], 'binary').toString('base64').replace(/\+/g, '@').replace(/\//g, '$'), footer: (domain.footer == null) ? '' : domain.footer, webstate: encodeURIComponent(webstate) });
|
||||
res.render(getRenderPage('default', req), { authCookie: authCookie, viewmode: viewmode, currentNode: currentNode, logoutControl: logoutcontrol, title: domain.title, title2: domain.title2, extitle: encodeURIComponent(domain.title), extitle2: encodeURIComponent(domain.title2), domainurl: domain.url, domain: domain.id, debuglevel: parent.debugLevel, serverDnsName: obj.getWebServerName(domain), serverRedirPort: args.redirport, serverPublicPort: httpsPort, noServerBackup: (args.noserverbackup == 1 ? 1 : 0), features: features, sessiontime: args.sessiontime, mpspass: args.mpspass, passRequirements: passRequirements, webcerthash: Buffer.from(obj.webCertificateFullHashs[domain.id], 'binary').toString('base64').replace(/\+/g, '@').replace(/\//g, '$'), footer: (domain.footer == null) ? '' : domain.footer, webstate: encodeURIComponent(webstate), pluginHandler: (parent.pluginHandler == null)?'null':parent.pluginHandler.prepExports() });
|
||||
});
|
||||
} else {
|
||||
// Send back the login application
|
||||
@ -1697,7 +1697,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
|
||||
|
||||
// Render the messenger application.
|
||||
function handleMessengerRequest(req, res) {
|
||||
const domain = checkUserIpAddress(req, res);
|
||||
const domain = getDomain(req);
|
||||
if (domain == null) { parent.debug('web', 'handleMessengerRequest: no domain'); res.sendStatus(404); return; }
|
||||
parent.debug('web', 'handleMessengerRequest()');
|
||||
|
||||
@ -3751,6 +3751,80 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Route a command from a agent. domainid, nodeid and meshid are the values of the source agent.
|
||||
obj.routeAgentCommand = function (command, domainid, nodeid, meshid) {
|
||||
// Route a message.
|
||||
// If this command has a sessionid, that is the target.
|
||||
if (command.sessionid != null) {
|
||||
if (typeof command.sessionid != 'string') return;
|
||||
var splitsessionid = command.sessionid.split('/');
|
||||
// Check that we are in the same domain and the user has rights over this node.
|
||||
if ((splitsessionid[0] == 'user') && (splitsessionid[1] == domainid)) {
|
||||
// Check if this user has rights to get this message
|
||||
//if (mesh.links[user._id] == null || ((mesh.links[user._id].rights & 16) == 0)) return; // TODO!!!!!!!!!!!!!!!!!!!!!
|
||||
|
||||
// See if the session is connected. If so, go ahead and send this message to the target node
|
||||
var ws = obj.wssessions2[command.sessionid];
|
||||
if (ws != null) {
|
||||
command.nodeid = nodeid; // Set the nodeid, required for responses.
|
||||
delete command.sessionid; // Remove the sessionid, since we are sending to that sessionid, so it's implyed.
|
||||
try { ws.send(JSON.stringify(command)); } catch (ex) { }
|
||||
} else if (parent.multiServer != null) {
|
||||
// See if we can send this to a peer server
|
||||
var serverid = obj.wsPeerSessions2[command.sessionid];
|
||||
if (serverid != null) {
|
||||
command.fromNodeid = nodeid;
|
||||
parent.multiServer.DispatchMessageSingleServer(command, serverid);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (command.userid != null) { // If this command has a userid, that is the target.
|
||||
if (typeof command.userid != 'string') return;
|
||||
var splituserid = command.userid.split('/');
|
||||
// Check that we are in the same domain and the user has rights over this node.
|
||||
if ((splituserid[0] == 'user') && (splituserid[1] == domainid)) {
|
||||
// Check if this user has rights to get this message
|
||||
//if (mesh.links[user._id] == null || ((mesh.links[user._id].rights & 16) == 0)) return; // TODO!!!!!!!!!!!!!!!!!!!!!
|
||||
|
||||
// See if the session is connected
|
||||
var sessions = obj.wssessions[command.userid];
|
||||
|
||||
// Go ahead and send this message to the target node
|
||||
if (sessions != null) {
|
||||
command.nodeid = nodeid; // Set the nodeid, required for responses.
|
||||
delete command.userid; // Remove the userid, since we are sending to that userid, so it's implyed.
|
||||
for (i in sessions) { sessions[i].send(JSON.stringify(command)); }
|
||||
}
|
||||
|
||||
if (parent.multiServer != null) {
|
||||
// TODO: Add multi-server support
|
||||
}
|
||||
}
|
||||
} else { // Route this command to the mesh
|
||||
command.nodeid = nodeid;
|
||||
var cmdstr = JSON.stringify(command);
|
||||
for (var userid in obj.wssessions) { // Find all connected users for this mesh and send the message
|
||||
var user = obj.users[userid];
|
||||
if ((user != null) && (user.links != null)) {
|
||||
var rights = user.links[meshid];
|
||||
if (rights != null) { // TODO: Look at what rights are needed for message routing
|
||||
var xsessions = obj.wssessions[userid];
|
||||
// Send the message to all users on this server
|
||||
for (i in xsessions) { try { xsessions[i].send(cmdstr); } catch (e) { } }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Send the message to all users of other servers
|
||||
if (parent.multiServer != null) {
|
||||
delete command.nodeid;
|
||||
command.fromNodeid = nodeid;
|
||||
command.meshid = meshid;
|
||||
parent.multiServer.DispatchMessage(command);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Return true if a mobile browser is detected.
|
||||
// This code comes from "http://detectmobilebrowsers.com/" and was modified, This is free and unencumbered software released into the public domain. For more information, please refer to the http://unlicense.org/
|
||||
function isMobileBrowser(req) {
|
||||
|
Loading…
Reference in New Issue
Block a user