diff --git a/agents/meshcore.js b/agents/meshcore.js index 21191447..bf63d739 100644 --- a/agents/meshcore.js +++ b/agents/meshcore.js @@ -40,6 +40,7 @@ var MESHRIGHT_LIMITEVENTS = 8192; var MESHRIGHT_CHATNOTIFY = 16384; var MESHRIGHT_UNINSTALL = 32768; var MESHRIGHT_NODESKTOP = 65536; + if (require('MeshAgent').ARCHID == null) { var id = null; @@ -58,7 +59,8 @@ if (require('MeshAgent').ARCHID == null) if (id != null) { Object.defineProperty(require('MeshAgent'), 'ARCHID', { value: id }); } } -function createMeshCore(agent) { +function createMeshCore(agent) +{ var obj = {}; var agentFileHttpRequests = {}; // Currently active agent HTTPS GET requests from the server. var agentFileHttpPendingRequests = []; // Pending HTTPS GET requests from the server. @@ -67,7 +69,8 @@ function createMeshCore(agent) { if (process.platform == 'win32' && require('user-sessions').isRoot()) { // Check the Agent Uninstall MetaData for correctness, as the installer may have written an incorrect value - try { + try + { var writtenSize = 0, actualSize = Math.floor(require('fs').statSync(process.execPath).size / 1024); try { writtenSize = require('win-registry').QueryKey(require('win-registry').HKEY.LocalMachine, 'Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\MeshCentralAgent', 'EstimatedSize'); } catch (e) { } if (writtenSize != actualSize) { try { require('win-registry').WriteKey(require('win-registry').HKEY.LocalMachine, 'Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\MeshCentralAgent', 'EstimatedSize', actualSize); } catch (e) { } } @@ -91,7 +94,8 @@ function createMeshCore(agent) { } catch (e) { } } - 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 = ''; @@ -100,18 +104,21 @@ function createMeshCore(agent) { child.stdin.write(" if(c[1]==\"dict\"){ split(a[2], d, \"\"); if(split(d[1], truval, \"\")>1) { split(truval[1], kn1, \"\"); split(kn1[2], kn2, \"\"); 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('KeepAlive'); - if (tokens[1].split('>')[0].split('<')[1] == 'dict') { + if (tokens[1].split('>')[0].split('<')[1] == 'dict') + { var tmp = tokens[1].split(''); tmp.shift(); tokens[1] = '\n ' + tmp.join(''); @@ -167,7 +174,8 @@ function createMeshCore(agent) { } // Add an Intel AMT event to the log - function addAmtEvent(msg) { + function addAmtEvent(msg) + { if (obj.amtevents == null) { obj.amtevents = []; } var d = new Date(); obj.amtevents.push(zeroPad(d.getHours(), 2) + ':' + zeroPad(d.getMinutes(), 2) + ':' + zeroPad(d.getSeconds(), 2) + ', ' + msg); @@ -182,8 +190,10 @@ function createMeshCore(agent) { obj.DAIPC.IPCPATH = process.platform == 'win32' ? ('\\\\.\\pipe\\' + require('_agentNodeId')() + '-DAIPC') : (process.cwd() + '/DAIPC'); try { obj.DAIPC.listen({ path: obj.DAIPC.IPCPATH, writableAll: true, maxConnections: 5 }); } catch (e) { } obj.DAIPC._daipc = []; - 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); @@ -193,7 +203,8 @@ function createMeshCore(agent) { this._daipc.push(c); c.parent = this; c.on('end', function () { removeRegisteredApp(this); }); - 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) { removeRegisteredApp(this); this.end(); return; } @@ -203,8 +214,10 @@ function createMeshCore(agent) { try { data = JSON.parse(data.toString()); } catch (e) { } if ((data == null) || (typeof data.cmd != 'string')) return; - try { - switch (data.cmd) { + try + { + switch (data.cmd) + { case 'requesthelp': if (this._registered == null) return; sendConsoleText('Request Help (' + this._registered + '): ' + data.value); @@ -219,7 +232,8 @@ function createMeshCore(agent) { try { mesh.SendCommand({ action: 'sessions', type: 'help', value: {} }); } catch (e) { } break; case 'register': - if (typeof data.value == 'string') { + if (typeof data.value == 'string') + { this._registered = data.value; var apps = {}; apps[data.value] = 1; @@ -235,12 +249,13 @@ function createMeshCore(agent) { this._send(data); break; case 'descriptors': - require('ChainViewer').getSnapshot().then(function (f) { + require('ChainViewer').getSnapshot().then(function (f) + { this.tag.payload.result = f; this.tag.ipc._send(this.tag.payload); }).parentPromise.tag = { ipc: this, payload: data }; break; - case 'timerinfo': + case 'timerinfo': data.result = require('ChainViewer').getTimerInfo(); this._send(data); break; @@ -256,10 +271,11 @@ function createMeshCore(agent) { this._send({ cmd: 'sessions', sessions: tunnelUserCount }); break; case 'meshToolInfo': - try { mesh.SendCommand({ action: 'meshToolInfo', name: data.name, hash: data.hash, cookie: data.cookie?true:false, pipe: true }); } catch (e) { } + try { mesh.SendCommand({ action: 'meshToolInfo', name: data.name, hash: data.hash, cookie: data.cookie ? true : false, pipe: true }); } catch (e) { } break; case 'console': - if (debugConsole) { + if (debugConsole) + { var args = splitArgs(data.value); processConsoleCommand(args[0].toLowerCase(), parseArgs(args), 0, 'pipe'); } @@ -271,24 +287,28 @@ function createMeshCore(agent) { }); // Send current sessions to registered apps - function broadcastSessionsToRegisteredApps(x) { + function broadcastSessionsToRegisteredApps(x) + { broadcastToRegisteredApps({ cmd: 'sessions', sessions: tunnelUserCount }); } // Send this object to all registered local applications - function broadcastToRegisteredApps(x) { + function broadcastToRegisteredApps(x) + { if ((obj.DAIPC == null) || (obj.DAIPC._daipc == null)) return; for (var i in obj.DAIPC._daipc) { if (obj.DAIPC._daipc[i]._registered != null) { obj.DAIPC._daipc[i]._send(x); } } } // Send this object to a specific registered local applications - function sendToRegisteredApp(appid, x) { + function sendToRegisteredApp(appid, x) + { if ((obj.DAIPC == null) || (obj.DAIPC._daipc == null)) return; for (var i in obj.DAIPC._daipc) { if (obj.DAIPC._daipc[i]._registered == appid) { obj.DAIPC._daipc[i]._send(x); } } } // Send list of registered apps to the server - function updateRegisteredAppsToServer() { + function updateRegisteredAppsToServer() + { if ((obj.DAIPC == null) || (obj.DAIPC._daipc == null)) return; var apps = {}; for (var i in obj.DAIPC._daipc) { if (apps[obj.DAIPC._daipc[i]._registered] == null) { apps[obj.DAIPC._daipc[i]._registered] = 1; } else { apps[obj.DAIPC._daipc[i]._registered]++; } } @@ -296,26 +316,32 @@ function createMeshCore(agent) { } // Remove a registered app - function removeRegisteredApp(pipe) { + function removeRegisteredApp(pipe) + { for (var i = obj.DAIPC._daipc.length - 1; i >= 0; i--) { if (obj.DAIPC._daipc[i] === pipe) { obj.DAIPC._daipc.splice(i, 1); } } if (pipe._registered != null) updateRegisteredAppsToServer(); } - 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', @@ -327,7 +353,8 @@ function createMeshCore(agent) { }); svc = require('service-manager').manager.getService('meshagentDiagnostic'); } - catch (e) { + catch (e) + { return (null); } var proxyConfig = require('global-tunnel').proxyConfig; @@ -340,9 +367,11 @@ 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'); } @@ -359,18 +388,24 @@ function createMeshCore(agent) { } // Monitor the file 'batterystate.txt' in the agent's folder and sends battery update when this file is changed. - if ((require('fs').existsSync(process.cwd() + 'batterystate.txt')) && (require('fs').watch != null)) { + if ((require('fs').existsSync(process.cwd() + 'batterystate.txt')) && (require('fs').watch != null)) + { // Setup manual battery monitoring - require('MeshAgent')._batteryFileWatcher = require('fs').watch(process.cwd(), function () { + require('MeshAgent')._batteryFileWatcher = require('fs').watch(process.cwd(), function () + { if (require('MeshAgent')._batteryFileTimer != null) return; - require('MeshAgent')._batteryFileTimer = setTimeout(function () { - try { + require('MeshAgent')._batteryFileTimer = setTimeout(function () + { + try + { require('MeshAgent')._batteryFileTimer = null; var data = null; try { data = require('fs').readFileSync(process.cwd() + 'batterystate.txt').toString(); } catch (e) { } - if ((data != null) && (data.length < 10)) { + if ((data != null) && (data.length < 10)) + { data = data.split(','); - if ((data.length == 2) && ((data[0] == 'ac') || (data[0] == 'dc'))) { + if ((data.length == 2) && ((data[0] == 'ac') || (data[0] == 'dc'))) + { var level = parseInt(data[1]); if ((level >= 0) && (level <= 100)) { require('MeshAgent').SendCommand({ action: 'battery', state: data[0], level: level }); } } @@ -378,24 +413,31 @@ function createMeshCore(agent) { } catch (e) { } }, 1000); }); - } else { + } else + { // Setup normal battery monitoring - if (require('identifiers').isBatteryPowered && require('identifiers').isBatteryPowered()) { - require('MeshAgent')._battLevelChanged = function _battLevelChanged(val) { + if (require('identifiers').isBatteryPowered && require('identifiers').isBatteryPowered()) + { + require('MeshAgent')._battLevelChanged = function _battLevelChanged(val) + { _battLevelChanged.self._currentBatteryLevel = val; _battLevelChanged.self.SendCommand({ action: 'battery', state: _battLevelChanged.self._currentPowerState, level: val }); }; require('MeshAgent')._battLevelChanged.self = require('MeshAgent'); - require('MeshAgent')._powerChanged = function _powerChanged(val) { + require('MeshAgent')._powerChanged = function _powerChanged(val) + { _powerChanged.self._currentPowerState = (val == 'AC' ? 'ac' : 'dc'); _powerChanged.self.SendCommand({ action: 'battery', state: (val == 'AC' ? 'ac' : 'dc'), level: _powerChanged.self._currentBatteryLevel }); }; require('MeshAgent')._powerChanged.self = require('MeshAgent'); - require('MeshAgent').on('Connected', function (status) { - if (status == 0) { + require('MeshAgent').on('Connected', function (status) + { + if (status == 0) + { require('power-monitor').removeListener('acdc', this._powerChanged); require('power-monitor').removeListener('batteryLevel', this._battLevelChanged); - } else { + } else + { require('power-monitor').on('acdc', this._powerChanged); require('power-monitor').on('batteryLevel', this._battLevelChanged); } @@ -445,12 +487,16 @@ function createMeshCore(agent) { try { require('os').name().then(function (v) { meshCoreObj.osdesc = v; meshCoreObjChanged(); }); } catch (e) { } // Setup logged in user monitoring (THIS IS BROKEN IN WIN7) - try { + try + { var userSession = require('user-sessions'); - userSession.on('changed', function onUserSessionChanged() { - userSession.enumerateUsers().then(function (users) { + userSession.on('changed', function onUserSessionChanged() + { + userSession.enumerateUsers().then(function (users) + { var u = [], a = users.Active; - for (var i = 0; i < a.length; i++) { + for (var i = 0; i < a.length; i++) + { var un = a[i].Domain ? (a[i].Domain + '\\' + a[i].Username) : (a[i].Username); if (u.indexOf(un) == -1) { u.push(un); } // Only push users in the list once. } @@ -482,9 +528,11 @@ function createMeshCore(agent) { var tunnelUserCount = { terminal: {}, files: {}, tcp: {}, udp: {}, msg: {} }; // List of userid->count sessions for terminal, files and TCP/UDP routing // Add to the server event log - function MeshServerLog(msg, state) { + function MeshServerLog(msg, state) + { if (typeof msg == 'string') { msg = { action: 'log', msg: msg }; } else { msg.action = 'log'; } - if (state) { + if (state) + { if (state.userid) { msg.userid = state.userid; } if (state.username) { msg.username = state.username; } if (state.sessionid) { msg.sessionid = state.sessionid; } @@ -494,9 +542,11 @@ function createMeshCore(agent) { } // Add to the server event log, use internationalized events - function MeshServerLogEx(id, args, msg, state) { + function MeshServerLogEx(id, args, msg, state) + { var msg = { action: 'log', msgid: id, msgArgs: args, msg: msg }; - if (state) { + if (state) + { if (state.userid) { msg.userid = state.userid; } if (state.username) { msg.username = state.username; } if (state.sessionid) { msg.sessionid = state.sessionid; } @@ -510,13 +560,16 @@ function createMeshCore(agent) { sha = require('SHA256Stream'); mesh = require('MeshAgent'); childProcess = require('child_process'); - if (mesh.hasKVM == 1) { // if the agent is compiled with KVM support + if (mesh.hasKVM == 1) + { // if the agent is compiled with KVM support // Check if this computer supports a desktop try { - if ((process.platform == 'win32') || (process.platform == 'darwin') || (require('monitor-info').kvm_x11_support)) { + if ((process.platform == 'win32') || (process.platform == 'darwin') || (require('monitor-info').kvm_x11_support)) + { meshCoreObj.caps |= 1; meshCoreObjChanged(); - } else if (process.platform == 'linux' || process.platform == 'freebsd') { + } else if (process.platform == 'linux' || process.platform == 'freebsd') + { require('monitor-info').on('kvmSupportDetected', function (value) { meshCoreObj.caps |= 1; meshCoreObjChanged(); }); } } catch (e) { } @@ -536,12 +589,16 @@ function createMeshCore(agent) { // Fetch the SMBios Tables var SMBiosTables = null; var SMBiosTablesRaw = null; - try { + try + { var SMBiosModule = null; try { SMBiosModule = require('smbios'); } catch (e) { } - if (SMBiosModule != null) { - SMBiosModule.get(function (data) { - if (data != null) { + if (SMBiosModule != null) + { + SMBiosModule.get(function (data) + { + if (data != null) + { SMBiosTablesRaw = data; SMBiosTables = require('smbios').parse(data) if (mesh.isControlChannelConnected) { mesh.SendCommand({ action: 'smbios', value: SMBiosTablesRaw }); } @@ -562,7 +619,8 @@ function createMeshCore(agent) { } catch (e) { sendConsoleText("ex1: " + e); } // Try to load up the WIFI scanner - try { + try + { var wifiScannerLib = require('wifi-scanner'); wifiScanner = new wifiScannerLib(); wifiScanner.on('accessPoint', function (data) { sendConsoleText("wifiScanner: " + data); }); @@ -571,28 +629,36 @@ function createMeshCore(agent) { // Get our location (lat/long) using our public IP address var getIpLocationDataExInProgress = false; var getIpLocationDataExCounts = [0, 0]; - function getIpLocationDataEx(func) { + function getIpLocationDataEx(func) + { if (getIpLocationDataExInProgress == true) { return false; } - try { + try + { getIpLocationDataExInProgress = true; getIpLocationDataExCounts[0]++; var options = http.parseUri("http://ipinfo.io/json"); options.method = 'GET'; - http.request(options, function (resp) { - if (resp.statusCode == 200) { + http.request(options, function (resp) + { + if (resp.statusCode == 200) + { var geoData = ''; resp.data = function (geoipdata) { geoData += geoipdata; }; - resp.end = function () { + resp.end = function () + { var location = null; - try { - if (typeof geoData == 'string') { + try + { + if (typeof geoData == 'string') + { var result = JSON.parse(geoData); if (result.ip && result.loc) { location = result; } } } catch (e) { } if (func) { getIpLocationDataExCounts[1]++; func(location); } } - } else { func(null); } + } else + { func(null); } getIpLocationDataExInProgress = false; }).end(); return true; @@ -601,45 +667,57 @@ function createMeshCore(agent) { } // Remove all Gateway MAC addresses for interface list. This is useful because the gateway MAC is not always populated reliably. - function clearGatewayMac(str) { + function clearGatewayMac(str) + { if (str == null) return null; var x = JSON.parse(str); for (var i in x.netif) { if (x.netif[i].gatewaymac) { delete x.netif[i].gatewaymac } } return JSON.stringify(x); } - function getIpLocationData(func) { + function getIpLocationData(func) + { // Get the location information for the cache if possible var publicLocationInfo = db.Get('publicLocationInfo'); if (publicLocationInfo != null) { publicLocationInfo = JSON.parse(publicLocationInfo); } - if (publicLocationInfo == null) { + if (publicLocationInfo == null) + { // Nothing in the cache, fetch the data - getIpLocationDataEx(function (locationData) { - if (locationData != null) { + getIpLocationDataEx(function (locationData) + { + if (locationData != null) + { publicLocationInfo = {}; publicLocationInfo.netInfoStr = lastNetworkInfo; publicLocationInfo.locationData = locationData; var x = db.Put('publicLocationInfo', JSON.stringify(publicLocationInfo)); // Save to database if (func) func(locationData); // Report the new location - } else { + } else + { if (func) func(null); // Report no location } }); - } else { + } else + { // Check the cache - if (clearGatewayMac(publicLocationInfo.netInfoStr) == clearGatewayMac(lastNetworkInfo)) { + if (clearGatewayMac(publicLocationInfo.netInfoStr) == clearGatewayMac(lastNetworkInfo)) + { // Cache match if (func) func(publicLocationInfo.locationData); - } else { + } else + { // Cache mismatch - getIpLocationDataEx(function (locationData) { - if (locationData != null) { + getIpLocationDataEx(function (locationData) + { + if (locationData != null) + { publicLocationInfo = {}; publicLocationInfo.netInfoStr = lastNetworkInfo; publicLocationInfo.locationData = locationData; var x = db.Put('publicLocationInfo', JSON.stringify(publicLocationInfo)); // Save to database if (func) func(locationData); // Report the new location - } else { + } else + { if (func) func(publicLocationInfo.locationData); // Can't get new location, report the old location } }); @@ -648,8 +726,10 @@ function createMeshCore(agent) { } // Polyfill String.endsWith - if (!String.prototype.endsWith) { - String.prototype.endsWith = function (searchString, position) { + if (!String.prototype.endsWith) + { + String.prototype.endsWith = function (searchString, position) + { var subjectString = this.toString(); if (typeof position !== 'number' || !isFinite(position) || Math.floor(position) !== position || position > subjectString.length) { position = subjectString.length; } position -= searchString.length; @@ -660,13 +740,17 @@ function createMeshCore(agent) { // Polyfill path.join obj.path = { - join: function () { + join: function () + { var x = []; - for (var i in arguments) { + for (var i in arguments) + { var w = arguments[i]; - if (w != null) { + if (w != null) + { while (w.endsWith('/') || w.endsWith('\\')) { w = w.substring(0, w.length - 1); } - if (i != 0) { + if (i != 0) + { while (w.startsWith('/') || w.startsWith('\\')) { w = w.substring(1); } } x.push(w); @@ -690,7 +774,8 @@ function createMeshCore(agent) { 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) { + function hex2rstr(d) + { if (typeof d != "string" || d.length == 0) return ''; var r = '', m = ('' + d).match(/../g), t; while (t = m.shift()) r += String.fromCharCode('0x' + t); @@ -698,7 +783,8 @@ function createMeshCore(agent) { } // Convert an object to string with all functions - function objToString(x, p, pad, ret) { + function objToString(x, p, pad, ret) + { if (ret == undefined) ret = ''; if (p == undefined) p = 0; if (x == null) { return '[null]'; } @@ -716,21 +802,26 @@ function createMeshCore(agent) { 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) { + 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) { + function parseArgs(argv) + { var results = { '_': [] }, current = null; - for (var i = 1, len = argv.length; i < len; i++) { + for (var i = 1, len = argv.length; i < len; i++) + { var x = argv[i]; - if (x.length > 2 && x[0] == '-' && x[1] == '-') { + if (x.length > 2 && x[0] == '-' && x[1] == '-') + { if (current != null) { results[current] = true; } current = x.substring(2); - } else { + } else + { if (current != null) { results[current] = toNumberIfNumber(x); current = null; } else { results['_'].push(toNumberIfNumber(x)); } } } @@ -739,7 +830,8 @@ function createMeshCore(agent) { } // Get server target url with a custom path - function getServerTargetUrl(path) { + function getServerTargetUrl(path) + { var x = mesh.ServerUrl; //sendConsoleText("mesh.ServerUrl: " + mesh.ServerUrl); if (x == null) { return null; } @@ -750,27 +842,35 @@ function createMeshCore(agent) { } // Get server url. If the url starts with "*/..." change it, it not use the url as is. - function getServerTargetUrlEx(url) { + function getServerTargetUrlEx(url) + { if (url.substring(0, 2) == '*/') { return getServerTargetUrl(url.substring(2)); } return url; } // Send a wake-on-lan packet - function sendWakeOnLan(hexMac) { + function sendWakeOnLan(hexMac) + { hexMac = hexMac.split(':').join(''); var count = 0; - try { + try + { var interfaces = require('os').networkInterfaces(); 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) { + for (var adapter in interfaces) + { + if (interfaces.hasOwnProperty(adapter)) + { + for (var i = 0; i < interfaces[adapter].length; ++i) + { var addr = interfaces[adapter][i]; - if ((addr.family == 'IPv4') && (addr.mac != '00:00:00:00:00:00')) { - try { + if ((addr.family == 'IPv4') && (addr.mac != '00:00:00:00:00:00')) + { + try + { var socket = require('dgram').createSocket({ type: 'udp4' }); socket.bind({ address: addr.address }); socket.setBroadcast(true); @@ -790,14 +890,22 @@ function createMeshCore(agent) { } // Handle a mesh agent command - function handleServerCommand(data) { - if (typeof data == 'object') { + function handleServerCommand(data) + { + if (typeof data == 'object') + { // If this is a console command, parse it and call the console handler - switch (data.action) { + switch (data.action) + { + case 'agentupdate': + agentUpdate_Start(data.url, { hash: data.hash, tlshash: data.servertlshash }); + break; case 'msg': { - switch (data.type) { + switch (data.type) + { case 'console': { // Process a console command - if (data.value && data.sessionid) { + if (data.value && data.sessionid) + { MeshServerLogEx(17, [data.value], "Processing console command: " + data.value, data); var args = splitArgs(data.value); processConsoleCommand(args[0].toLowerCase(), parseArgs(args), data.rights, data.sessionid); @@ -805,10 +913,12 @@ function createMeshCore(agent) { break; } case 'tunnel': { - if (data.value != null) { // Process a new tunnel connection request + if (data.value != null) + { // Process a new tunnel connection request // Create a new tunnel object var xurl = getServerTargetUrlEx(data.value); - if (xurl != null) { + if (xurl != null) + { xurl = xurl.split('$').join('%24').split('@').join('%40'); // Escape the $ and @ characters var woptions = http.parseUri(xurl); woptions.perMessageDeflate = false; @@ -816,7 +926,8 @@ function createMeshCore(agent) { // Perform manual server TLS certificate checking based on the certificate hash given by the server. woptions.rejectUnauthorized = 0; - woptions.checkServerIdentity = function checkServerIdentity(certs) { + woptions.checkServerIdentity = function checkServerIdentity(certs) + { // If the tunnel certificate matches the control channel certificate, accept the connection try { if (require('MeshAgent').ServerInfo.ControlChannelCertificate.digest == certs[0].digest) return; } catch (ex) { } try { if (require('MeshAgent').ServerInfo.ControlChannelCertificate.fingerprint == certs[0].fingerprint) return; } catch (ex) { } @@ -859,7 +970,8 @@ function createMeshCore(agent) { } case 'messagebox': { // Display a message box - if (data.title && data.msg) { + if (data.title && data.msg) + { MeshServerLogEx(18, [data.title, data.msg], "Displaying message box, title=" + data.title + ", message=" + data.msg, data); data.msg = data.msg.split('\r').join('\\r').split('\n').join('\\n'); try { require('message-box').create(data.title, data.msg, 120); } catch (e) { } @@ -868,8 +980,10 @@ function createMeshCore(agent) { } case 'ps': { // Return the list of running processes - if (data.sessionid) { - processManager.getProcesses(function (plist) { + if (data.sessionid) + { + processManager.getProcesses(function (plist) + { mesh.SendCommand({ action: 'msg', type: 'ps', value: JSON.stringify(plist), sessionid: data.sessionid }); }); } @@ -877,7 +991,8 @@ function createMeshCore(agent) { } case 'pskill': { // Kill a process - if (data.value) { + if (data.value) + { MeshServerLogEx(19, [data.value], "Killing process " + data.value, data); try { process.kill(data.value); } catch (e) { sendConsoleText("pskill: " + JSON.stringify(e)); } } @@ -892,7 +1007,8 @@ function createMeshCore(agent) { } case 'serviceStop': { // Stop a service - try { + try + { var service = require('service-manager').manager.getService(data.serviceName); if (service != null) { service.stop(); } } catch (e) { } @@ -900,7 +1016,8 @@ function createMeshCore(agent) { } case 'serviceStart': { // Start a service - try { + try + { var service = require('service-manager').manager.getService(data.serviceName); if (service != null) { service.start(); } } catch (e) { } @@ -908,7 +1025,8 @@ function createMeshCore(agent) { } case 'serviceRestart': { // Restart a service - try { + try + { var service = require('service-manager').manager.getService(data.serviceName); if (service != null) { service.restart(); } } catch (e) { } @@ -917,12 +1035,16 @@ function createMeshCore(agent) { case 'deskBackground': { // Toggle desktop background - try { - if (process.platform == 'win32') { + try + { + if (process.platform == 'win32') + { var stype = require('user-sessions').getProcessOwnerName(process.pid).tsid == 0 ? 1 : 0; var sid = undefined; - if (stype == 1) { - if (require('MeshAgent')._tsid != null) { + if (stype == 1) + { + if (require('MeshAgent')._tsid != null) + { stype = 5; sid = require('MeshAgent')._tsid; } @@ -938,13 +1060,15 @@ function createMeshCore(agent) { child.stdout.str = ''; child.stdout.on('data', function (c) { this.str += c.toString(); }); child.stderr.on('data', function () { }); child.waitExit(); - } else { + } else + { var id = require('user-sessions').consoleUid(); var current = require('linux-gnome-helpers').getDesktopWallpaper(id); if (current != '/dev/null') { require('MeshAgent')._wallpaper = current; } require('linux-gnome-helpers').setDesktopWallpaper(id, current != '/dev/null' ? undefined : require('MeshAgent')._wallpaper); } - } catch (e) { + } catch (e) + { sendConsoleText(e); } break; @@ -959,16 +1083,22 @@ function createMeshCore(agent) { case 'getclip': { // Send the load clipboard back to the user //sendConsoleText('getClip: ' + JSON.stringify(data)); - if (require('MeshAgent').isService) { - require('clipboard').dispatchRead().then(function (str) { - if (str) { + if (require('MeshAgent').isService) + { + require('clipboard').dispatchRead().then(function (str) + { + if (str) + { MeshServerLogEx(21, [str.length], "Getting clipboard content, " + str.length + " byte(s)", data); mesh.SendCommand({ action: 'msg', type: 'getclip', sessionid: data.sessionid, data: str, tag: data.tag }); } }); - } else { - require("clipboard").read().then(function (str) { - if (str) { + } else + { + require("clipboard").read().then(function (str) + { + if (str) + { MeshServerLogEx(21, [str.length], "Getting clipboard content, " + str.length + " byte(s)", data); mesh.SendCommand({ action: 'msg', type: 'getclip', sessionid: data.sessionid, data: str, tag: data.tag }); } @@ -979,7 +1109,8 @@ function createMeshCore(agent) { case 'setclip': { // Set the load clipboard to a user value //sendConsoleText('setClip: ' + JSON.stringify(data)); - if (typeof data.data == 'string') { + if (typeof data.data == 'string') + { MeshServerLogEx(22, [data.data.length], "Setting clipboard content, " + data.data.length + " byte(s)", data); if (require('MeshAgent').isService) { require('clipboard').dispatchWrite(data.data); } else { require("clipboard")(data.data); } // Set the clipboard mesh.SendCommand({ action: 'msg', type: 'setclip', sessionid: data.sessionid, success: true }); @@ -1014,7 +1145,8 @@ function createMeshCore(agent) { break; } case 'acmactivate': { - if (amt != null) { + if (amt != null) + { MeshServerLogEx(23, null, "Attempting Intel AMT ACM mode activation", data); amt.setAcmResponse(data); } @@ -1032,17 +1164,21 @@ function createMeshCore(agent) { // data.runAsUser: 0=Agent,1=UserOrAgent,2=UserOnly var options = {}; - if (data.runAsUser > 0) { + if (data.runAsUser > 0) + { try { options.uid = require('user-sessions').consoleUid(); } catch (e) { } options.type = require('child_process').SpawnTypes.TERM; } - if (data.runAsUser == 2) { + if (data.runAsUser == 2) + { if (options.uid == null) break; if (((require('user-sessions').minUid != null) && (options.uid < require('user-sessions').minUid()))) break; // This command can only run as user. } - if (process.platform == 'win32') { - if (data.type == 1) { + if (process.platform == 'win32') + { + if (data.type == 1) + { // Windows command shell mesh.cmdchild = require('child_process').execFile(process.env['windir'] + '\\system32\\cmd.exe', ['cmd'], options); mesh.cmdchild.descriptorMetadata = 'UserCommandsShell'; @@ -1050,7 +1186,8 @@ function createMeshCore(agent) { mesh.cmdchild.stderr.on('data', function (c) { sendConsoleText(c.toString()); }); mesh.cmdchild.stdin.write(data.cmds + '\r\nexit\r\n'); mesh.cmdchild.on('exit', function () { sendConsoleText("Run commands completed."); delete mesh.cmdchild; }); - } else if (data.type == 2) { + } else if (data.type == 2) + { // Windows Powershell mesh.cmdchild = require('child_process').execFile(process.env['windir'] + '\\System32\\WindowsPowerShell\\v1.0\\powershell.exe', ['powershell', '-noprofile', '-nologo', '-command', '-'], options); mesh.cmdchild.descriptorMetadata = 'UserCommandsPowerShell'; @@ -1059,7 +1196,8 @@ function createMeshCore(agent) { mesh.cmdchild.stdin.write(data.cmds + '\r\nexit\r\n'); mesh.cmdchild.on('exit', function () { sendConsoleText("Run commands completed."); delete mesh.cmdchild; }); } - } else if (data.type == 3) { + } else if (data.type == 3) + { // Linux shell mesh.cmdchild = require('child_process').execFile('/bin/sh', ['sh'], options); mesh.cmdchild.descriptorMetadata = 'UserCommandsShell'; @@ -1081,7 +1219,8 @@ function createMeshCore(agent) { { } - if (require('service-manager').manager.getService(agentName).isMe()) { + if (require('service-manager').manager.getService(agentName).isMe()) + { try { diagnosticAgent_uninstall(); } catch (e) { } var js = "require('service-manager').manager.getService('" + agentName + "').stop(); require('service-manager').manager.uninstallService('" + agentName + "'); process.exit();"; this.child = require('child_process').execFile(process.execPath, [process.platform == 'win32' ? (process.execPath.split('\\').pop()) : (process.execPath.split('/').pop()), '-b64exec', Buffer.from(js).toString('base64')], { type: 4, detached: true }); @@ -1089,7 +1228,8 @@ function createMeshCore(agent) { break; case 'poweraction': { // Server telling us to execute a power action - if ((mesh.ExecPowerState != undefined) && (data.actiontype)) { + if ((mesh.ExecPowerState != undefined) && (data.actiontype)) + { var forced = 0; if (data.forced == 1) { forced = 1; } data.actiontype = parseInt(data.actiontype); @@ -1107,7 +1247,8 @@ function createMeshCore(agent) { } case 'toast': { // Display a toast message - if (data.title && data.msg) { + if (data.title && data.msg) + { MeshServerLogEx(26, [data.title, data.msg], "Displaying toast message, title=" + data.title + ", message=" + data.msg, data); data.msg = data.msg.split('\r').join('\\r').split('\n').join('\\n'); try { require('toaster').Toast(data.title, data.msg); } catch (e) { } @@ -1124,7 +1265,8 @@ function createMeshCore(agent) { case 'amtconfig': { // Perform Intel AMT activation and/or configuration if ((apftunnel != null) || (amt == null) || (typeof data.user != 'string') || (typeof data.pass != 'string')) break; - amt.getMeiState(15, function (state) { + amt.getMeiState(15, function (state) + { if ((apftunnel != null) || (amt == null)) return; if ((state == null) || (state.ProvisioningState == null)) return; if ((state.UUID == null) || (state.UUID.length != 36)) return; // Bad UUID @@ -1141,10 +1283,12 @@ function createMeshCore(agent) { }; addAmtEvent('LMS tunnel start.'); apftunnel = require('amt-apfclient')({ debug: false }, apfarg); - apftunnel.onJsonControl = function (data) { + apftunnel.onJsonControl = function (data) + { if (data.action == 'console') { addAmtEvent(data.msg); } // Add console message to AMT event log if (data.action == 'mestate') { amt.getMeiState(15, function (state) { apftunnel.updateMeiState(state); }); } // Update the MEI state - if (data.action == 'deactivate') { // Request CCM deactivation + if (data.action == 'deactivate') + { // Request CCM deactivation var amtMeiModule, amtMei; try { amtMeiModule = require('amt-mei'); amtMei = new amtMeiModule(); } catch (ex) { if (apftunnel) apftunnel.sendMeiDeactivationState(1); return; } amtMei.on('error', function (e) { if (apftunnel) apftunnel.sendMeiDeactivationState(1); }); @@ -1164,7 +1308,8 @@ function createMeshCore(agent) { } case 'sysinfo': { // Fetch system information - getSystemInformation(function (results) { + getSystemInformation(function (results) + { if ((results != null) && (data.hash != results.hash)) { mesh.SendCommand({ action: 'sysinfo', sessionid: this.sessionid, data: results }); } }); break; @@ -1177,14 +1322,18 @@ function createMeshCore(agent) { } case 'coredump': // Set the current agent coredump situation. - if (data.value === true) { - if (process.platform == 'win32') { + if (data.value === true) + { + if (process.platform == 'win32') + { // TODO: This replace() below is not ideal, would be better to remove the .exe at the end instead of replace. process.coreDumpLocation = process.execPath.replace('.exe', '.dmp'); - } else { + } else + { process.coreDumpLocation = (process.cwd() != '//') ? (process.cwd() + 'core') : null; } - } else if (data.value === false) { + } else if (data.value === false) + { process.coreDumpLocation = null; } break; @@ -1193,15 +1342,18 @@ function createMeshCore(agent) { var r = { action: 'getcoredump', value: (process.coreDumpLocation != null) }; var coreDumpPath = null; if (process.platform == 'win32') { coreDumpPath = process.coreDumpLocation; } else { coreDumpPath = (process.cwd() != '//') ? fs.existsSync(process.cwd() + 'core') : null; } - if ((coreDumpPath != null) && (fs.existsSync(coreDumpPath))) { - try { + if ((coreDumpPath != null) && (fs.existsSync(coreDumpPath))) + { + try + { var coredate = fs.statSync(coreDumpPath).mtime; var coretime = new Date(coredate).getTime(); var agenttime = new Date(fs.statSync(process.execPath).mtime).getTime(); if (coretime > agenttime) { r.exists = (db.Get('CoreDumpTime') != coredate); } } catch (ex) { } } - if (r.exists == true) { + if (r.exists == true) + { r.agenthashhex = getSHA384FileHash(process.execPath).toString('hex'); // Hash of current agent r.corehashhex = getSHA384FileHash(coreDumpPath).toString('hex'); // Hash of core dump file } @@ -1222,7 +1374,8 @@ function createMeshCore(agent) { } // Agent just get a file from the server and save it locally. - function serverFetchFile() { + function serverFetchFile() + { if ((Object.keys(agentFileHttpRequests).length > 4) || (agentFileHttpPendingRequests.length == 0)) return; // No more than 4 active HTTPS requests to the server. var data = agentFileHttpPendingRequests.shift(); if ((data.overwrite !== true) && fs.existsSync(data.path)) return; // Don't overwrite an existing file. @@ -1233,7 +1386,8 @@ function createMeshCore(agent) { // Perform manual server TLS certificate checking based on the certificate hash given by the server. agentFileHttpOptions.rejectUnauthorized = 0; - agentFileHttpOptions.checkServerIdentity = function checkServerIdentity(certs) { + agentFileHttpOptions.checkServerIdentity = function checkServerIdentity(certs) + { // If the tunnel certificate matches the control channel certificate, accept the connection try { if (require('MeshAgent').ServerInfo.ControlChannelCertificate.digest == certs[0].digest) return; } catch (ex) { } try { if (require('MeshAgent').ServerInfo.ControlChannelCertificate.fingerprint == certs[0].fingerprint) return; } catch (ex) { } @@ -1244,9 +1398,11 @@ function createMeshCore(agent) { if (agentFileHttpOptions == null) return; var agentFileHttpRequest = http.request(agentFileHttpOptions, - function (response) { + function (response) + { response.xparent = this; - try { + try + { response.xfile = fs.createWriteStream(this.xpath, { flags: 'wbN' }) response.pipe(response.xfile); response.end = function () { delete agentFileHttpRequests[this.xparent.xurlpath]; delete this.xparent; serverFetchFile(); } @@ -1269,10 +1425,13 @@ function createMeshCore(agent) { } */ - function getSystemInformation(func) { - try { + function getSystemInformation(func) + { + try + { var results = { hardware: require('identifiers').get() }; // Hardware info - if (results.hardware && results.hardware.windows) { + if (results.hardware && results.hardware.windows) + { // Remove extra entries and things that change quickly var x = results.hardware.windows.osinfo; try { delete x.FreePhysicalMemory; } catch (e) { } @@ -1282,7 +1441,8 @@ function createMeshCore(agent) { try { delete x.MaxProcessMemorySize; } catch (e) { } try { delete x.TotalVirtualMemorySize; } catch (e) { } try { delete x.TotalVisibleMemorySize; } catch (e) { } - try { + try + { if (results.hardware.windows.memory) { for (var i in results.hardware.windows.memory) { delete results.hardware.windows.memory[i].Node; } } if (results.hardware.windows.osinfo) { delete results.hardware.windows.osinfo.Node; } if (results.hardware.windows.partitions) { for (var i in results.hardware.windows.partitions) { delete results.hardware.windows.partitions[i].Node; } } @@ -1318,35 +1478,46 @@ function createMeshCore(agent) { } // Get a formated response for a given directory path - function getDirectoryInfo(reqpath) { + function getDirectoryInfo(reqpath) + { var response = { path: reqpath, dir: [] }; - if (((reqpath == undefined) || (reqpath == '')) && (process.platform == 'win32')) { + if (((reqpath == undefined) || (reqpath == '')) && (process.platform == 'win32')) + { // List all the drives in the root, or the root itself var results = null; try { results = fs.readDrivesSync(); } catch (e) { } // TODO: Anyway to get drive total size and free space? Could draw a progress bar. - if (results != null) { - for (var i = 0; i < results.length; ++i) { + if (results != null) + { + for (var i = 0; i < results.length; ++i) + { var drive = { n: results[i].name, t: 1 }; if (results[i].type == 'REMOVABLE') { drive.dt = 'removable'; } // TODO: See if this is USB/CDROM or something else, we can draw icons. response.dir.push(drive); } } - } else { + } else + { // List all the files and folders in this path if (reqpath == '') { reqpath = '/'; } var results = null, xpath = obj.path.join(reqpath, '*'); //if (process.platform == "win32") { xpath = xpath.split('/').join('\\'); } try { results = fs.readdirSync(xpath); } catch (e) { } - if (results != null) { - for (var i = 0; i < results.length; ++i) { - if ((results[i] != '.') && (results[i] != '..')) { + if (results != null) + { + for (var i = 0; i < results.length; ++i) + { + if ((results[i] != '.') && (results[i] != '..')) + { var stat = null, p = obj.path.join(reqpath, results[i]); //if (process.platform == "win32") { p = p.split('/').join('\\'); } try { stat = fs.statSync(p); } catch (e) { } // TODO: Get file size/date - if ((stat != null) && (stat != undefined)) { - if (stat.isDirectory() == true) { + if ((stat != null) && (stat != undefined)) + { + if (stat.isDirectory() == true) + { response.dir.push({ n: results[i], t: 2, d: stat.mtime }); - } else { + } else + { response.dir.push({ n: results[i], t: 3, s: stat.size, d: stat.mtime }); } } @@ -1358,7 +1529,8 @@ function createMeshCore(agent) { } // Tunnel callback operations - function onTunnelUpgrade(response, s, head) { + function onTunnelUpgrade(response, s, head) + { this.s = s; s.httprequest = this; s.end = onTunnelClosed; @@ -1377,7 +1549,8 @@ function createMeshCore(agent) { //sendConsoleText('onTunnelUpgrade - ' + this.tcpport + ' - ' + this.udpport); - if (this.tcpport != null) { + if (this.tcpport != null) + { // This is a TCP relay connection, pause now and try to connect to the target. s.pause(); s.data = onTcpRelayServerTunnelData; @@ -1387,12 +1560,14 @@ function createMeshCore(agent) { s.tcprelay.peerindex = this.index; // Add the TCP session to the count and update the server - if (s.httprequest.userid != null) { + if (s.httprequest.userid != null) + { if (tunnelUserCount.tcp[s.httprequest.userid] == null) { tunnelUserCount.tcp[s.httprequest.userid] = 1; } else { tunnelUserCount.tcp[s.httprequest.userid]++; } try { mesh.SendCommand({ action: 'sessions', type: 'tcp', value: tunnelUserCount.tcp }); } catch (e) { } broadcastSessionsToRegisteredApps(); } - } if (this.udpport != null) { + } if (this.udpport != null) + { // This is a UDP relay connection, get the UDP socket setup. // TODO: *************** s.data = onUdpRelayServerTunnelData; s.udprelay = require('dgram').createSocket({ type: 'udp4' }); @@ -1404,34 +1579,41 @@ function createMeshCore(agent) { s.udprelay.first = true; // Add the UDP session to the count and update the server - if (s.httprequest.userid != null) { + if (s.httprequest.userid != null) + { if (tunnelUserCount.udp[s.httprequest.userid] == null) { tunnelUserCount.udp[s.httprequest.userid] = 1; } else { tunnelUserCount.udp[s.httprequest.userid]++; } try { mesh.SendCommand({ action: 'sessions', type: 'udp', value: tunnelUserCount.tcp }); } catch (e) { } broadcastSessionsToRegisteredApps(); } - } else { + } else + { // This is a normal connect for KVM/Terminal/Files s.data = onTunnelData; } } // Called when UDP relay data is received // TODO**** - function onUdpRelayTargetTunnelConnect(data) { + function onUdpRelayTargetTunnelConnect(data) + { var peerTunnel = tunnels[this.peerindex]; peerTunnel.s.write(data); } // 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 onUdpRelayServerTunnelData(data) { - if (this.udprelay.first === true) { + function onUdpRelayServerTunnelData(data) + { + if (this.udprelay.first === true) + { delete this.udprelay.first; // Skip the first 'c' that is received. - } else { + } else + { this.udprelay.send(data, parseInt(this.udprelay.udpport), this.udprelay.udpaddr ? this.udprelay.udpaddr : '127.0.0.1'); } } // Called when the TCP relay target is connected - function onTcpRelayTargetTunnelConnect() { + function onTcpRelayTargetTunnelConnect() + { var peerTunnel = tunnels[this.peerindex]; this.pipe(peerTunnel.s); // Pipe Target --> Server peerTunnel.s.first = true; @@ -1439,24 +1621,30 @@ function createMeshCore(agent) { } // 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; + function onTcpRelayServerTunnelData(data) + { + if (this.first == true) + { + this.first = false; this.pipe(this.tcprelay, { dataTypeSkip: 1 }); // Pipe Server --> Target (don't pipe text type websocket frames) } } - function onTunnelClosed() { + function onTunnelClosed() + { var tunnel = tunnels[this.httprequest.index]; if (tunnel == null) return; // Stop duplicate calls. // If this is a routing session, clean up and send the new session counts. - if (this.httprequest.userid != null) { - if (this.httprequest.tcpport != null) { + if (this.httprequest.userid != null) + { + if (this.httprequest.tcpport != null) + { if (tunnelUserCount.tcp[this.httprequest.userid] != null) { tunnelUserCount.tcp[this.httprequest.userid]--; if (tunnelUserCount.tcp[this.httprequest.userid] <= 0) { delete tunnelUserCount.tcp[this.httprequest.userid]; } } try { mesh.SendCommand({ action: 'sessions', type: 'tcp', value: tunnelUserCount.tcp }); } catch (e) { } broadcastSessionsToRegisteredApps(); - } else if (this.httprequest.udpport != null) { + } else if (this.httprequest.udpport != null) + { if (tunnelUserCount.udp[this.httprequest.userid] != null) { tunnelUserCount.udp[this.httprequest.userid]--; if (tunnelUserCount.udp[this.httprequest.userid] <= 0) { delete tunnelUserCount.udp[this.httprequest.userid]; } } try { mesh.SendCommand({ action: 'sessions', type: 'udp', value: tunnelUserCount.udp }); } catch (e) { } broadcastSessionsToRegisteredApps(); @@ -1464,7 +1652,8 @@ function createMeshCore(agent) { } // Sent tunnel statistics to the server, only send this if compression was used. - if ((this.bytesSent_uncompressed) && (this.bytesSent_uncompressed.toString() != this.bytesSent_actual.toString())) { + if ((this.bytesSent_uncompressed) && (this.bytesSent_uncompressed.toString() != this.bytesSent_actual.toString())) + { mesh.SendCommand({ action: 'tunnelCloseStats', url: tunnel.url, @@ -1497,7 +1686,8 @@ function createMeshCore(agent) { if (this.httprequest.downloadFile) { delete this.httprequest.downloadFile; } // Clean up WebRTC - if (this.webrtc != null) { + if (this.webrtc != null) + { if (this.webrtc.rtcchannel) { try { this.webrtc.rtcchannel.close(); } catch (e) { } this.webrtc.rtcchannel.removeAllListeners('data'); this.webrtc.rtcchannel.removeAllListeners('end'); delete this.webrtc.rtcchannel; } if (this.webrtc.websocket) { delete this.webrtc.websocket; } try { this.webrtc.close(); } catch (e) { } @@ -1511,17 +1701,21 @@ function createMeshCore(agent) { this.removeAllListeners('data'); } function onTunnelSendOk() { /*sendConsoleText("Tunnel #" + this.index + " SendOK.", this.sessionid);*/ } - function onTunnelData(data) { + 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) && (typeof data == 'object') && (data[0] != 123)) { + if ((this.httprequest.uploadFile) && (typeof data == 'object') && (data[0] != 123)) + { // Save the data to file being uploaded. - if (data[0] == 0) { + if (data[0] == 0) + { // If data starts with zero, skip the first byte. This is used to escape binary file data from JSON. try { fs.writeSync(this.httprequest.uploadFile, data, 1, data.length - 1); } catch (e) { sendConsoleText('FileUpload Error'); this.write(Buffer.from(JSON.stringify({ action: 'uploaderror' }))); return; } // Write to the file, if there is a problem, error out. - } else { + } else + { // If data does not start with zero, save as-is. try { fs.writeSync(this.httprequest.uploadFile, data); } catch (e) { sendConsoleText('FileUpload Error'); this.write(Buffer.from(JSON.stringify({ action: 'uploaderror' }))); return; } // Write to the file, if there is a problem, error out. } @@ -1529,19 +1723,22 @@ function createMeshCore(agent) { return; } - if (this.httprequest.state == 0) { + if (this.httprequest.state == 0) + { // Check if this is a relay connection if ((data == 'c') || (data == 'cr')) { this.httprequest.state = 1; /*sendConsoleText("Tunnel #" + this.httprequest.index + " now active", this.httprequest.sessionid);*/ } } else { // Handle tunnel data - if (this.httprequest.protocol == 0) { // 1 = Terminal (admin), 2 = Desktop, 5 = Files, 6 = PowerShell (admin), 7 = Plugin Data Exchange, 8 = Terminal (user), 9 = PowerShell (user), 10 = FileTransfer + if (this.httprequest.protocol == 0) + { // 1 = Terminal (admin), 2 = Desktop, 5 = Files, 6 = PowerShell (admin), 7 = Plugin Data Exchange, 8 = Terminal (user), 9 = PowerShell (user), 10 = FileTransfer // Take a look at the protocol if ((data.length > 3) && (data[0] == '{')) { onTunnelControlData(data, this); return; } this.httprequest.protocol = parseInt(data); if (typeof this.httprequest.protocol != 'number') { this.httprequest.protocol = 0; } - if (this.httprequest.protocol == 10) { + if (this.httprequest.protocol == 10) + { // // Basic file transfer // @@ -1549,12 +1746,14 @@ function createMeshCore(agent) { if ((process.platform != 'win32') && (this.httprequest.xoptions.file.startsWith('/') == false)) { this.httprequest.xoptions.file = '/' + this.httprequest.xoptions.file; } try { stats = require('fs').statSync(this.httprequest.xoptions.file) } catch (e) { } try { if (stats) { this.httprequest.downloadFile = fs.createReadStream(this.httprequest.xoptions.file, { flags: 'rbN' }); } } catch (e) { } - if (this.httprequest.downloadFile) { + if (this.httprequest.downloadFile) + { //sendConsoleText('BasicFileTransfer, ok, ' + this.httprequest.xoptions.file + ', ' + JSON.stringify(stats)); this.write(JSON.stringify({ op: 'ok', size: stats.size })); this.httprequest.downloadFile.pipe(this); this.httprequest.downloadFile.end = function () { } - } else { + } else + { //sendConsoleText('BasicFileTransfer, cancel, ' + this.httprequest.xoptions.file); this.write(JSON.stringify({ op: 'cancel' })); } @@ -1566,7 +1765,8 @@ function createMeshCore(agent) { // // Check user access rights for terminal - if (((this.httprequest.rights & MESHRIGHT_REMOTECONTROL) == 0) || ((this.httprequest.rights != 0xFFFFFFFF) && ((this.httprequest.rights & MESHRIGHT_NOTERMINAL) != 0))) { + if (((this.httprequest.rights & MESHRIGHT_REMOTECONTROL) == 0) || ((this.httprequest.rights != 0xFFFFFFFF) && ((this.httprequest.rights & MESHRIGHT_NOTERMINAL) != 0))) + { // Disengage this tunnel, user does not have the rights to do this!! this.httprequest.protocol = 999999; this.httprequest.s.end(); @@ -1628,7 +1828,8 @@ function createMeshCore(agent) { { this.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: "Waiting for user to grant access...", msgid: 1 })); var consentMessage = this.httprequest.username + " requesting remote terminal access. Grant access?", consentTitle = 'MeshCentral'; - if (this.httprequest.soptions != null) { + if (this.httprequest.soptions != null) + { if (this.httprequest.soptions.consentTitle != null) { consentTitle = this.httprequest.soptions.consentTitle; } if (this.httprequest.soptions.consentMsgTerminal != null) { consentMessage = this.httprequest.soptions.consentMsgTerminal.replace('{0}', this.httprequest.realname).replace('{1}', this.httprequest.username); } } @@ -1665,7 +1866,7 @@ function createMeshCore(agent) { this.httprequest.connectionPromise.ws = this.that; // Start Terminal - if(process.platform == 'win32') + if (process.platform == 'win32') { try { @@ -1836,7 +2037,8 @@ function createMeshCore(agent) { { // User Notifications is required var notifyMessage = this.ws.httprequest.username + " started a remote terminal session.", notifyTitle = "MeshCentral"; - if (this.ws.httprequest.soptions != null) { + if (this.ws.httprequest.soptions != null) + { if (this.ws.httprequest.soptions.notifyTitle != null) { notifyTitle = this.ws.httprequest.soptions.notifyTitle; } if (this.ws.httprequest.soptions.notifyMsgTerminal != null) { notifyMessage = this.ws.httprequest.soptions.notifyMsgTerminal.replace('{0}', this.ws.httprequest.realname).replace('{1}', this.ws.httprequest.username); } } @@ -1855,7 +2057,7 @@ function createMeshCore(agent) { // DO NOT start terminal this.that.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: e.toString(), msgid: 2 })); this.that.end(); - }); + }); } else if (this.httprequest.protocol == 2) { @@ -1864,7 +2066,8 @@ function createMeshCore(agent) { // // Check user access rights for desktop - if ((((this.httprequest.rights & MESHRIGHT_REMOTECONTROL) == 0) && ((this.httprequest.rights & MESHRIGHT_REMOTEVIEW) == 0)) || ((this.httprequest.rights != 0xFFFFFFFF) && ((this.httprequest.rights & MESHRIGHT_NODESKTOP) != 0))) { + if ((((this.httprequest.rights & MESHRIGHT_REMOTECONTROL) == 0) && ((this.httprequest.rights & MESHRIGHT_REMOTEVIEW) == 0)) || ((this.httprequest.rights != 0xFFFFFFFF) && ((this.httprequest.rights & MESHRIGHT_NODESKTOP) != 0))) + { // Disengage this tunnel, user does not have the rights to do this!! this.httprequest.protocol = 999999; this.httprequest.s.end(); @@ -1890,7 +2093,8 @@ function createMeshCore(agent) { // Send a metadata update to all desktop sessions var users = {}; - if (this.httprequest.desktop.kvm.tunnels != null) { + if (this.httprequest.desktop.kvm.tunnels != null) + { for (var i in this.httprequest.desktop.kvm.tunnels) { try { var userid = this.httprequest.desktop.kvm.tunnels[i].httprequest.userid; if (users[userid] == null) { users[userid] = 1; } else { users[userid]++; } } catch (e) { } } for (var i in this.httprequest.desktop.kvm.tunnels) { try { this.httprequest.desktop.kvm.tunnels[i].write(JSON.stringify({ ctrlChannel: '102938', type: 'metadata', users: users })); } catch (e) { } } tunnelUserCount.desktop = users; @@ -1898,7 +2102,8 @@ function createMeshCore(agent) { broadcastSessionsToRegisteredApps(); } - this.end = function () { + this.end = function () + { --this.desktop.kvm.connectionCount; // Remove ourself from the list of remote desktop session @@ -1907,7 +2112,8 @@ function createMeshCore(agent) { // Send a metadata update to all desktop sessions var users = {}; - if (this.httprequest.desktop.kvm.tunnels != null) { + if (this.httprequest.desktop.kvm.tunnels != null) + { for (var i in this.httprequest.desktop.kvm.tunnels) { try { var userid = this.httprequest.desktop.kvm.tunnels[i].httprequest.userid; if (users[userid] == null) { users[userid] = 1; } else { users[userid]++; } } catch (e) { } } for (var i in this.httprequest.desktop.kvm.tunnels) { try { this.httprequest.desktop.kvm.tunnels[i].write(JSON.stringify({ ctrlChannel: '102938', type: 'metadata', users: users })); } catch (e) { } } tunnelUserCount.desktop = users; @@ -1921,7 +2127,7 @@ function createMeshCore(agent) { this.unpipe(this.httprequest.desktop.kvm); this.httprequest.desktop.kvm.unpipe(this); } - catch(e) { } + catch (e) { } // Unpipe the WebRTC channel if needed (This will also be done when the WebRTC channel ends). if (this.rtcchannel) @@ -1931,34 +2137,41 @@ function createMeshCore(agent) { this.rtcchannel.unpipe(this.httprequest.desktop.kvm); this.httprequest.desktop.kvm.unpipe(this.rtcchannel); } - catch(e) { } + catch (e) { } } // Place wallpaper back if needed // TODO - if (this.desktop.kvm.connectionCount == 0) { + if (this.desktop.kvm.connectionCount == 0) + { // Display a toast message. This may not be supported on all platforms. // try { require('toaster').Toast('MeshCentral', 'Remote Desktop Control Ended.'); } catch (e) { } this.httprequest.desktop.kvm.end(); - if (this.httprequest.desktop.kvm.connectionBar) { + if (this.httprequest.desktop.kvm.connectionBar) + { this.httprequest.desktop.kvm.connectionBar.removeAllListeners('close'); this.httprequest.desktop.kvm.connectionBar.close(); this.httprequest.desktop.kvm.connectionBar = null; } - } else { - for (var i in this.httprequest.desktop.kvm.users) { - if ((this.httprequest.desktop.kvm.users[i] == this.httprequest.username) && this.httprequest.desktop.kvm.connectionBar) { + } else + { + for (var i in this.httprequest.desktop.kvm.users) + { + if ((this.httprequest.desktop.kvm.users[i] == this.httprequest.username) && this.httprequest.desktop.kvm.connectionBar) + { for (var j in this.httprequest.desktop.kvm.rusers) { if (this.httprequest.desktop.kvm.rusers[j] == this.httprequest.realname) { this.httprequest.desktop.kvm.rusers.splice(j, 1); break; } } this.httprequest.desktop.kvm.users.splice(i, 1); this.httprequest.desktop.kvm.connectionBar.removeAllListeners('close'); this.httprequest.desktop.kvm.connectionBar.close(); this.httprequest.desktop.kvm.connectionBar = require('notifybar-desktop')(this.httprequest.privacybartext.replace('{0}', this.httprequest.desktop.kvm.users.join(', ')).replace('{1}', this.httprequest.desktop.kvm.rusers.join(', ')), require('MeshAgent')._tsid); this.httprequest.desktop.kvm.connectionBar.httprequest = this.httprequest; - this.httprequest.desktop.kvm.connectionBar.on('close', function () { + this.httprequest.desktop.kvm.connectionBar.on('close', function () + { MeshServerLogEx(29, null, "Remote Desktop Connection forcefully closed by local user (" + this.httprequest.remoteaddr + ")", this.httprequest); - for (var i in this.httprequest.desktop.kvm._pipedStreams) { + for (var i in this.httprequest.desktop.kvm._pipedStreams) + { this.httprequest.desktop.kvm._pipedStreams[i].end(); } this.httprequest.desktop.kvm.end(); @@ -1968,22 +2181,26 @@ function createMeshCore(agent) { } } }; - if (this.httprequest.desktop.kvm.hasOwnProperty('connectionCount')) { + if (this.httprequest.desktop.kvm.hasOwnProperty('connectionCount')) + { this.httprequest.desktop.kvm.connectionCount++; this.httprequest.desktop.kvm.rusers.push(this.httprequest.realname); this.httprequest.desktop.kvm.users.push(this.httprequest.username); this.httprequest.desktop.kvm.rusers.sort(); this.httprequest.desktop.kvm.users.sort(); - } else { + } else + { this.httprequest.desktop.kvm.connectionCount = 1; this.httprequest.desktop.kvm.rusers = [this.httprequest.realname]; this.httprequest.desktop.kvm.users = [this.httprequest.username]; } - if ((this.httprequest.rights == 0xFFFFFFFF) || (((this.httprequest.rights & MESHRIGHT_REMOTECONTROL) != 0) && ((this.httprequest.rights & MESHRIGHT_REMOTEVIEW) == 0))) { + if ((this.httprequest.rights == 0xFFFFFFFF) || (((this.httprequest.rights & MESHRIGHT_REMOTECONTROL) != 0) && ((this.httprequest.rights & MESHRIGHT_REMOTEVIEW) == 0))) + { // If we have remote control rights, pipe the KVM input this.pipe(this.httprequest.desktop.kvm, { dataTypeSkip: 1, end: false }); // 0 = Binary, 1 = Text. Pipe the Browser --> KVM input. - } else { + } else + { // We need to only pipe non-mouse & non-keyboard inputs. //sendConsoleText('Warning: No Remote Desktop Input Rights.'); // TODO!!! @@ -1996,7 +2213,8 @@ function createMeshCore(agent) { // Send a console message back using the console channel, "\n" is supported. this.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: "Waiting for user to grant access...", msgid: 1 })); var consentMessage = this.httprequest.realname + " requesting remote desktop access. Grant access?", consentTitle = 'MeshCentral'; - if (this.httprequest.soptions != null) { + if (this.httprequest.soptions != null) + { if (this.httprequest.soptions.consentTitle != null) { consentTitle = this.httprequest.soptions.consentTitle; } if (this.httprequest.soptions.consentMsgDesktop != null) { consentMessage = this.httprequest.soptions.consentMsgDesktop.replace('{0}', this.httprequest.realname).replace('{1}', this.httprequest.username); } } @@ -2004,7 +2222,7 @@ function createMeshCore(agent) { pr.ws = this; this.pause(); this._consentpromise = pr; - this.prependOnceListener('end', function () { if (this._consentpromise && this._consentpromise.close) { this._consentpromise.close(); }}); + this.prependOnceListener('end', function () { if (this._consentpromise && this._consentpromise.close) { this._consentpromise.close(); } }); pr.then( function () { @@ -2012,35 +2230,45 @@ function createMeshCore(agent) { this.ws._consentpromise = null; MeshServerLogEx(30, null, "Starting remote desktop after local user accepted (" + this.ws.httprequest.remoteaddr + ")", this.ws.httprequest); this.ws.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: null, msgid: 0 })); - if (this.ws.httprequest.consent && (this.ws.httprequest.consent & 1)) { + if (this.ws.httprequest.consent && (this.ws.httprequest.consent & 1)) + { // User Notifications is required var notifyMessage = this.ws.httprequest.realname + " started a remote desktop session.", notifyTitle = "MeshCentral"; - if (this.ws.httprequest.soptions != null) { + if (this.ws.httprequest.soptions != null) + { if (this.ws.httprequest.soptions.notifyTitle != null) { notifyTitle = this.ws.httprequest.soptions.notifyTitle; } if (this.ws.httprequest.soptions.notifyMsgDesktop != null) { notifyMessage = this.ws.httprequest.soptions.notifyMsgDesktop.replace('{0}', this.ws.httprequest.realname).replace('{1}', this.ws.httprequest.username); } } try { require('toaster').Toast(notifyTitle, notifyMessage, tsid); } catch (e) { } } - if (this.ws.httprequest.consent && (this.ws.httprequest.consent & 0x40)) { + if (this.ws.httprequest.consent && (this.ws.httprequest.consent & 0x40)) + { // Connection Bar is required - if (this.ws.httprequest.desktop.kvm.connectionBar) { + if (this.ws.httprequest.desktop.kvm.connectionBar) + { this.ws.httprequest.desktop.kvm.connectionBar.removeAllListeners('close'); this.ws.httprequest.desktop.kvm.connectionBar.close(); } - try { + try + { this.ws.httprequest.desktop.kvm.connectionBar = require('notifybar-desktop')(this.ws.httprequest.privacybartext.replace('{0}', this.ws.httprequest.desktop.kvm.users.join(', ')).replace('{1}', this.ws.httprequest.desktop.kvm.rusers.join(', ')), require('MeshAgent')._tsid); MeshServerLogEx(31, null, "Remote Desktop Connection Bar Activated/Updated (" + this.ws.httprequest.remoteaddr + ")", this.ws.httprequest); } - catch (e) { - if (process.platform != 'darwin') { + catch (e) + { + if (process.platform != 'darwin') + { MeshServerLogEx(32, null, "Remote Desktop Connection Bar Failed or Not Supported (" + this.ws.httprequest.remoteaddr + ")", this.ws.httprequest); } } - if (this.ws.httprequest.desktop.kvm.connectionBar) { + if (this.ws.httprequest.desktop.kvm.connectionBar) + { this.ws.httprequest.desktop.kvm.connectionBar.httprequest = this.ws.httprequest; - this.ws.httprequest.desktop.kvm.connectionBar.on('close', function () { + this.ws.httprequest.desktop.kvm.connectionBar.on('close', function () + { MeshServerLogEx(33, null, "Remote Desktop Connection forcefully closed by local user (" + this.httprequest.remoteaddr + ")", this.httprequest); - for (var i in this.httprequest.desktop.kvm._pipedStreams) { + for (var i in this.httprequest.desktop.kvm._pipedStreams) + { this.httprequest.desktop.kvm._pipedStreams[i].end(); } this.httprequest.desktop.kvm.end(); @@ -2057,38 +2285,49 @@ function createMeshCore(agent) { MeshServerLogEx(34, null, "Failed to start remote desktop after local user rejected (" + this.ws.httprequest.remoteaddr + ")", this.ws.httprequest); this.ws.end(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: e.toString(), msgid: 2 })); }); - } else { + } else + { // User Consent Prompt is not required - if (this.httprequest.consent && (this.httprequest.consent & 1)) { + if (this.httprequest.consent && (this.httprequest.consent & 1)) + { // User Notifications is required MeshServerLogEx(35, null, "Started remote desktop with toast notification (" + this.httprequest.remoteaddr + ")", this.httprequest); var notifyMessage = this.httprequest.realname + " started a remote desktop session.", notifyTitle = "MeshCentral"; - if (this.httprequest.soptions != null) { + if (this.httprequest.soptions != null) + { if (this.httprequest.soptions.notifyTitle != null) { notifyTitle = this.httprequest.soptions.notifyTitle; } if (this.httprequest.soptions.notifyMsgDesktop != null) { notifyMessage = this.httprequest.soptions.notifyMsgDesktop.replace('{0}', this.httprequest.realname).replace('{1}', this.httprequest.username); } } try { require('toaster').Toast(notifyTitle, notifyMessage, tsid); } catch (e) { } - } else { + } else + { MeshServerLogEx(36, null, "Started remote desktop without notification (" + this.httprequest.remoteaddr + ")", this.httprequest); } - if (this.httprequest.consent && (this.httprequest.consent & 0x40)) { + if (this.httprequest.consent && (this.httprequest.consent & 0x40)) + { // Connection Bar is required - if (this.httprequest.desktop.kvm.connectionBar) { + if (this.httprequest.desktop.kvm.connectionBar) + { this.httprequest.desktop.kvm.connectionBar.removeAllListeners('close'); this.httprequest.desktop.kvm.connectionBar.close(); } - try { + try + { this.httprequest.desktop.kvm.connectionBar = require('notifybar-desktop')(this.httprequest.privacybartext.replace('{0}', this.httprequest.desktop.kvm.rusers.join(', ')).replace('{1}', this.httprequest.desktop.kvm.users.join(', ')), require('MeshAgent')._tsid); MeshServerLogEx(37, null, "Remote Desktop Connection Bar Activated/Updated (" + this.httprequest.remoteaddr + ")", this.httprequest); } - catch (e) { + catch (e) + { MeshServerLogEx(38, null, "Remote Desktop Connection Bar Failed or not Supported (" + this.httprequest.remoteaddr + ")", this.httprequest); } - if (this.httprequest.desktop.kvm.connectionBar) { + if (this.httprequest.desktop.kvm.connectionBar) + { this.httprequest.desktop.kvm.connectionBar.httprequest = this.httprequest; - this.httprequest.desktop.kvm.connectionBar.on('close', function () { + this.httprequest.desktop.kvm.connectionBar.on('close', function () + { MeshServerLogEx(39, null, "Remote Desktop Connection forcefully closed by local user (" + this.httprequest.remoteaddr + ")", this.httprequest); - for (var i in this.httprequest.desktop.kvm._pipedStreams) { + for (var i in this.httprequest.desktop.kvm._pipedStreams) + { this.httprequest.desktop.kvm._pipedStreams[i].end(); } this.httprequest.desktop.kvm.end(); @@ -2102,13 +2341,15 @@ function createMeshCore(agent) { this.on('data', onTunnelControlData); //this.write('MeshCore KVM Hello!1'); - } else if (this.httprequest.protocol == 5) { + } else if (this.httprequest.protocol == 5) + { // // Remote Files // // Check user access rights for files - if (((this.httprequest.rights & MESHRIGHT_REMOTECONTROL) == 0) || ((this.httprequest.rights != 0xFFFFFFFF) && ((this.httprequest.rights & MESHRIGHT_NOFILES) != 0))) { + if (((this.httprequest.rights & MESHRIGHT_REMOTECONTROL) == 0) || ((this.httprequest.rights != 0xFFFFFFFF) && ((this.httprequest.rights & MESHRIGHT_NOFILES) != 0))) + { // Disengage this tunnel, user does not have the rights to do this!! this.httprequest.protocol = 999999; this.httprequest.s.end(); @@ -2119,15 +2360,18 @@ function createMeshCore(agent) { this.descriptorMetadata = "Remote Files"; // Add the files session to the count to update the server - if (this.httprequest.userid != null) { + if (this.httprequest.userid != null) + { if (tunnelUserCount.files[this.httprequest.userid] == null) { tunnelUserCount.files[this.httprequest.userid] = 1; } else { tunnelUserCount.files[this.httprequest.userid]++; } try { mesh.SendCommand({ action: 'sessions', type: 'files', value: tunnelUserCount.files }); } catch (e) { } broadcastSessionsToRegisteredApps(); } - this.end = function () { + this.end = function () + { // Remove the files session from the count to update the server - if (this.httprequest.userid != null) { + if (this.httprequest.userid != null) + { if (tunnelUserCount.files[this.httprequest.userid] != null) { tunnelUserCount.files[this.httprequest.userid]--; if (tunnelUserCount.files[this.httprequest.userid] <= 0) { delete tunnelUserCount.files[this.httprequest.userid]; } } try { mesh.SendCommand({ action: 'sessions', type: 'files', value: tunnelUserCount.files }); } catch (e) { } broadcastSessionsToRegisteredApps(); @@ -2135,12 +2379,14 @@ 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...", msgid: 1 })); var consentMessage = this.httprequest.realname + " requesting remote file Access. Grant access?", consentTitle = 'MeshCentral'; - if (this.httprequest.soptions != null) { + if (this.httprequest.soptions != null) + { if (this.httprequest.soptions.consentTitle != null) { consentTitle = this.httprequest.soptions.consentTitle; } if (this.httprequest.soptions.consentMsgFiles != null) { consentMessage = this.httprequest.soptions.consentMsgFiles.replace('{0}', this.httprequest.realname).replace('{1}', this.httprequest.username); } } @@ -2156,10 +2402,12 @@ function createMeshCore(agent) { this.ws._consentpromise = null; MeshServerLogEx(40, null, "Starting remote files after local user accepted (" + this.ws.httprequest.remoteaddr + ")", this.ws.httprequest); this.ws.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: null })); - if (this.ws.httprequest.consent && (this.ws.httprequest.consent & 4)) { + if (this.ws.httprequest.consent && (this.ws.httprequest.consent & 4)) + { // User Notifications is required var notifyMessage = this.ws.httprequest.realname + " started a remote file session.", notifyTitle = "MeshCentral"; - if (this.ws.httprequest.soptions != null) { + if (this.ws.httprequest.soptions != null) + { if (this.ws.httprequest.soptions.notifyTitle != null) { notifyTitle = this.ws.httprequest.soptions.notifyTitle; } if (this.ws.httprequest.soptions.notifyMsgFiles != null) { notifyMessage = this.ws.httprequest.soptions.notifyMsgFiles.replace('{0}', this.ws.httprequest.realname).replace('{1}', this.ws.httprequest.username); } } @@ -2174,18 +2422,22 @@ function createMeshCore(agent) { MeshServerLogEx(41, null, "Failed to start remote files after local user rejected (" + this.ws.httprequest.remoteaddr + ")", this.ws.httprequest); this.ws.end(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: e.toString(), msgid: 2 })); }); - } else { + } else + { // User Consent Prompt is not required - if (this.httprequest.consent && (this.httprequest.consent & 4)) { + if (this.httprequest.consent && (this.httprequest.consent & 4)) + { // User Notifications is required MeshServerLogEx(42, null, "Started remote files with toast notification (" + this.httprequest.remoteaddr + ")", this.httprequest); var notifyMessage = this.httprequest.realname + " started a remote file session.", notifyTitle = "MeshCentral"; - if (this.httprequest.soptions != null) { + if (this.httprequest.soptions != null) + { if (this.httprequest.soptions.notifyTitle != null) { notifyTitle = this.httprequest.soptions.notifyTitle; } if (this.httprequest.soptions.notifyMsgFiles != null) { notifyMessage = this.httprequest.soptions.notifyMsgFiles.replace('{0}', this.httprequest.realname).replace('{1}', this.httprequest.username); } } try { require('toaster').Toast(notifyTitle, notifyMessage); } catch (e) { } - } else { + } else + { MeshServerLogEx(43, null, "Started remote files without notification (" + this.httprequest.remoteaddr + ")", this.httprequest); } this.resume(); @@ -2194,18 +2446,23 @@ function createMeshCore(agent) { // Setup files // NOP } - } else if (this.httprequest.protocol == 1) { + } else if (this.httprequest.protocol == 1) + { // Send data into terminal stdin //this.write(data); // Echo back the keys (Does not seem to be a good idea) - } else if (this.httprequest.protocol == 2) { + } else if (this.httprequest.protocol == 2) + { // Send data into remote desktop - if (this.httprequest.desktop.state == 0) { + if (this.httprequest.desktop.state == 0) + { this.write(Buffer.from(String.fromCharCode(0x11, 0xFE, 0x00, 0x00, 0x4D, 0x45, 0x53, 0x48, 0x00, 0x00, 0x00, 0x00, 0x02))); this.httprequest.desktop.state = 1; - } else { + } else + { this.httprequest.desktop.write(data); } - } else if (this.httprequest.protocol == 5) { + } else if (this.httprequest.protocol == 5) + { // Process files commands var cmd = null; try { cmd = JSON.parse(data); } catch (e) { }; @@ -2216,7 +2473,8 @@ function createMeshCore(agent) { if ((cmd.path != null) && (process.platform != 'win32') && (cmd.path[0] != '/')) { cmd.path = '/' + cmd.path; } // Add '/' to paths on non-windows //console.log(objToString(cmd, 0, ' ')); - switch (cmd.action) { + switch (cmd.action) + { case 'ls': { /* // Close the watcher if required @@ -2253,15 +2511,20 @@ function createMeshCore(agent) { } case 'rm': { // Delete, possibly recursive delete - for (var i in cmd.delfiles) { + for (var i in cmd.delfiles) + { var p = obj.path.join(cmd.path, cmd.delfiles[i]), delcount = 0; try { delcount = deleteFolderRecursive(p, cmd.rec); } catch (e) { } - if ((delcount == 1) && !cmd.rec) { + if ((delcount == 1) && !cmd.rec) + { MeshServerLogEx(45, [p], "Delete: \"" + p + "\"", this.httprequest); - } else { - if (cmd.rec) { + } else + { + if (cmd.rec) + { MeshServerLogEx(46, [p, delcount], "Delete recursive: \"" + p + "\", " + delcount + " element(s) removed", this.httprequest); - } else { + } else + { MeshServerLogEx(47, [p, delcount], "Delete: \"" + p + "\", " + delcount + " element(s) removed", this.httprequest); } } @@ -2271,9 +2534,11 @@ function createMeshCore(agent) { case 'markcoredump': { // If we are asking for the coredump file, set the right path. var coreDumpPath = null; - if (process.platform == 'win32') { + if (process.platform == 'win32') + { if (fs.existsSync(process.coreDumpLocation)) { coreDumpPath = process.coreDumpLocation; } - } else { + } else + { if ((process.cwd() != '//') && fs.existsSync(process.cwd() + 'core')) { coreDumpPath = process.cwd() + 'core'; } } if (coreDumpPath != null) { db.Put('CoreDumpTime', require('fs').statSync(coreDumpPath).mtime); } @@ -2305,24 +2570,30 @@ function createMeshCore(agent) { case 'download': { // Download a file var sendNextBlock = 0; - if (cmd.sub == 'start') { // Setup the download - if ((cmd.path == null) && (cmd.ask == 'coredump')) { // If we are asking for the coredump file, set the right path. - if (process.platform == 'win32') { + if (cmd.sub == 'start') + { // Setup the download + if ((cmd.path == null) && (cmd.ask == 'coredump')) + { // If we are asking for the coredump file, set the right path. + if (process.platform == 'win32') + { if (fs.existsSync(process.coreDumpLocation)) { cmd.path = process.coreDumpLocation; } - } else { + } else + { if ((process.cwd() != '//') && fs.existsSync(process.cwd() + 'core')) { cmd.path = process.cwd() + 'core'; } } } - MeshServerLogEx((cmd.ask == 'coredump')?104:49, [cmd.path], 'Download: \"' + cmd.path + '\"', this.httprequest); + MeshServerLogEx((cmd.ask == 'coredump') ? 104 : 49, [cmd.path], 'Download: \"' + cmd.path + '\"', this.httprequest); if ((cmd.path == null) || (this.filedownload != null)) { this.write({ action: 'download', sub: 'cancel', id: this.filedownload.id }); delete this.filedownload; } this.filedownload = { id: cmd.id, path: cmd.path, ptr: 0 } try { this.filedownload.f = fs.openSync(this.filedownload.path, 'rbN'); } catch (e) { this.write({ action: 'download', sub: 'cancel', id: this.filedownload.id }); delete this.filedownload; } if (this.filedownload) { this.write({ action: 'download', sub: 'start', id: cmd.id }); } - } else if ((this.filedownload != null) && (cmd.id == this.filedownload.id)) { // Download commands + } else if ((this.filedownload != null) && (cmd.id == this.filedownload.id)) + { // Download commands if (cmd.sub == 'startack') { sendNextBlock = ((typeof cmd.ack == 'number') ? cmd.ack : 8); } else if (cmd.sub == 'stop') { delete this.filedownload; } else if (cmd.sub == 'ack') { sendNextBlock = 1; } } // Send the next download block(s) - while (sendNextBlock > 0) { + while (sendNextBlock > 0) + { sendNextBlock--; var buf = Buffer.alloc(16384); var len = fs.readSync(this.filedownload.f, buf, 4, 16380, null); @@ -2332,28 +2603,28 @@ function createMeshCore(agent) { } break; } - /* - case 'download': { - // Packet download of a file, agent to browser - if (cmd.path == undefined) break; - var filepath = cmd.name ? obj.path.join(cmd.path, cmd.name) : cmd.path; - //console.log('Download: ' + filepath); - try { this.httprequest.downloadFile = fs.openSync(filepath, 'rbN'); } catch (e) { this.write(Buffer.from(JSON.stringify({ action: 'downloaderror', reqid: cmd.reqid }))); break; } - this.httprequest.downloadFileId = cmd.reqid; - this.httprequest.downloadFilePtr = 0; - if (this.httprequest.downloadFile) { this.write(Buffer.from(JSON.stringify({ action: 'downloadstart', reqid: this.httprequest.downloadFileId }))); } - break; - } - case 'download2': { - // Stream download of a file, agent to browser - if (cmd.path == undefined) break; - var filepath = cmd.name ? obj.path.join(cmd.path, cmd.name) : cmd.path; - try { this.httprequest.downloadFile = fs.createReadStream(filepath, { flags: 'rbN' }); } catch (e) { console.log(e); } - this.httprequest.downloadFile.pipe(this); - this.httprequest.downloadFile.end = function () { } - break; - } - */ + /* + case 'download': { + // Packet download of a file, agent to browser + if (cmd.path == undefined) break; + var filepath = cmd.name ? obj.path.join(cmd.path, cmd.name) : cmd.path; + //console.log('Download: ' + filepath); + try { this.httprequest.downloadFile = fs.openSync(filepath, 'rbN'); } catch (e) { this.write(Buffer.from(JSON.stringify({ action: 'downloaderror', reqid: cmd.reqid }))); break; } + this.httprequest.downloadFileId = cmd.reqid; + this.httprequest.downloadFilePtr = 0; + if (this.httprequest.downloadFile) { this.write(Buffer.from(JSON.stringify({ action: 'downloadstart', reqid: this.httprequest.downloadFileId }))); } + break; + } + case 'download2': { + // Stream download of a file, agent to browser + if (cmd.path == undefined) break; + var filepath = cmd.name ? obj.path.join(cmd.path, cmd.name) : cmd.path; + try { this.httprequest.downloadFile = fs.createReadStream(filepath, { flags: 'rbN' }); } catch (e) { console.log(e); } + this.httprequest.downloadFile.pipe(this); + this.httprequest.downloadFile.end = function () { } + break; + } + */ case 'upload': { // Upload a file, browser to agent if (this.httprequest.uploadFile != null) { fs.closeSync(this.httprequest.uploadFile); delete this.httprequest.uploadFile; } @@ -2368,7 +2639,8 @@ function createMeshCore(agent) { } case 'uploaddone': { // Indicates that an upload is done - if (this.httprequest.uploadFile) { + if (this.httprequest.uploadFile) + { fs.closeSync(this.httprequest.uploadFile); this.write(Buffer.from(JSON.stringify({ action: 'uploaddone', reqid: this.httprequest.uploadFileid }))); // Indicate that we closed the file. delete this.httprequest.uploadFile; @@ -2379,7 +2651,8 @@ function createMeshCore(agent) { } case 'uploadcancel': { // Indicates that an upload is canceled - if (this.httprequest.uploadFile) { + if (this.httprequest.uploadFile) + { fs.closeSync(this.httprequest.uploadFile); fs.unlinkSync(this.httprequest.uploadFilePath); this.write(Buffer.from(JSON.stringify({ action: 'uploadcancel', reqid: this.httprequest.uploadFileid }))); // Indicate that we closed the file. @@ -2391,7 +2664,8 @@ function createMeshCore(agent) { } case 'copy': { // Copy a bunch of files from scpath to dspath - for (var i in cmd.names) { + for (var i in cmd.names) + { var sc = obj.path.join(cmd.scpath, cmd.names[i]), ds = obj.path.join(cmd.dspath, cmd.names[i]); MeshServerLogEx(51, [sc, ds], 'Copy: \"' + sc + '\" to \"' + ds + '\"', this.httprequest); if (sc != ds) { try { fs.copyFileSync(sc, ds); } catch (e) { } } @@ -2400,7 +2674,8 @@ function createMeshCore(agent) { } case 'move': { // Move a bunch of files from scpath to dspath - for (var i in cmd.names) { + for (var i in cmd.names) + { var sc = obj.path.join(cmd.scpath, cmd.names[i]), ds = obj.path.join(cmd.dspath, cmd.names[i]); MeshServerLogEx(52, [sc, ds], 'Move: \"' + sc + '\" to \"' + ds + '\"', this.httprequest); if (sc != ds) { try { fs.copyFileSync(sc, ds); fs.unlinkSync(sc); } catch (e) { } } @@ -2423,7 +2698,8 @@ function createMeshCore(agent) { delete this.zipcancel; var out = require('fs').createWriteStream(ofile, { flags: 'wb' }); out.xws = this; - out.on('close', function () { + out.on('close', function () + { this.xws.write(Buffer.from(JSON.stringify({ action: 'dialogmessage', msg: null }))); this.xws.write(Buffer.from(JSON.stringify({ action: 'refresh' }))); if (this.xws.zipcancel === true) { fs.unlinkSync(this.xws.zipfile); } // Delete the complete file. @@ -2445,14 +2721,16 @@ function createMeshCore(agent) { // Unknown action, ignore it. break; } - } else if (this.httprequest.protocol == 7) { // Plugin data exchange + } 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) { + switch (cmd.action) + { case 'plugin': { try { require(cmd.plugin).consoleaction(cmd, null, null, this); } catch (e) { throw e; } break; @@ -2468,15 +2746,21 @@ function createMeshCore(agent) { } // Delete a directory with a files and directories within it - function deleteFolderRecursive(path, rec) { + function deleteFolderRecursive(path, rec) + { var count = 0; - if (fs.existsSync(path)) { - if (rec == true) { - fs.readdirSync(obj.path.join(path, '*')).forEach(function (file, index) { + if (fs.existsSync(path)) + { + if (rec == true) + { + fs.readdirSync(obj.path.join(path, '*')).forEach(function (file, index) + { var curPath = obj.path.join(path, file); - if (fs.statSync(curPath).isDirectory()) { // recurse + if (fs.statSync(curPath).isDirectory()) + { // recurse count += deleteFolderRecursive(curPath, true); - } else { // delete file + } else + { // delete file fs.unlinkSync(curPath); count++; } @@ -2489,11 +2773,13 @@ function createMeshCore(agent) { }; // Called when receiving control data on WebRTC - function onTunnelWebRTCControlData(data) { + function onTunnelWebRTCControlData(data) + { if (typeof data != 'string') return; var obj; try { obj = JSON.parse(data); } catch (e) { sendConsoleText('Invalid control JSON on WebRTC: ' + data); return; } - if (obj.type == 'close') { + if (obj.type == 'close') + { //sendConsoleText('Tunnel #' + this.xrtc.websocket.tunnel.index + ' WebRTC control close'); try { this.close(); } catch (e) { } try { this.xrtc.close(); } catch (e) { } @@ -2501,7 +2787,8 @@ function createMeshCore(agent) { } // Called when receiving control data on websocket - function onTunnelControlData(data, ws) { + function onTunnelControlData(data, ws) + { var obj; if (ws == null) { ws = this; } if (typeof data == 'string') { try { obj = JSON.parse(data); } catch (e) { sendConsoleText('Invalid control JSON: ' + data); return; } } @@ -2509,12 +2796,16 @@ function createMeshCore(agent) { //sendConsoleText('onTunnelControlData(' + ws.httprequest.protocol + '): ' + JSON.stringify(data)); //console.log('onTunnelControlData: ' + JSON.stringify(data)); - if (obj.action) { - switch (obj.action) { + if (obj.action) + { + switch (obj.action) + { case 'lock': { // Lock the current user out of the desktop - try { - if (process.platform == 'win32') { + try + { + if (process.platform == 'win32') + { MeshServerLogEx(53, null, "Locking remote user out of desktop", ws.httprequest); var child = require('child_process'); child.execFile(process.env['windir'] + '\\system32\\cmd.exe', ['/c', 'RunDll32.exe user32.dll,LockWorkStation'], { type: 1 }); @@ -2529,7 +2820,8 @@ function createMeshCore(agent) { return; } - switch (obj.type) { + switch (obj.type) + { case 'options': { // These are additional connection options passed in the control channel. //sendConsoleText('options: ' + JSON.stringify(obj)); @@ -2564,18 +2856,23 @@ function createMeshCore(agent) { break; } case 'webrtc0': { // Browser indicates we can start WebRTC switch-over. - if (ws.httprequest.protocol == 1) { // Terminal + 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); } - } else if (ws.httprequest.protocol == 2) { // Desktop + } else if (ws.httprequest.protocol == 2) + { // Desktop // This is a KVM data stream, unpipe the KVM now and indicate to the other side that KVM data will no longer be received over WebSocket ws.httprequest.desktop.kvm.unpipe(ws); - } else { + } else + { // Switch things around so all WebRTC data goes to onTunnelData(). ws.rtcchannel.httprequest = ws.httprequest; ws.rtcchannel.removeAllListeners('data'); @@ -2585,17 +2882,21 @@ function createMeshCore(agent) { break; } case 'webrtc1': { - if ((ws.httprequest.protocol == 1) || (ws.httprequest.protocol == 6)) { // Terminal + 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. } ws.resume(); // Resume the websocket to keep receiving control data - } else if (ws.httprequest.protocol == 2) { // Desktop + } else if (ws.httprequest.protocol == 2) + { // Desktop // Switch the user input from websocket to webrtc at this point. ws.unpipe(ws.httprequest.desktop.kvm); try { ws.webrtc.rtcchannel.pipe(ws.httprequest.desktop.kvm, { dataTypeSkip: 1, end: false }); } catch (e) { sendConsoleText('EX2'); } // 0 = Binary, 1 = Text. @@ -2606,14 +2907,18 @@ function createMeshCore(agent) { } case '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 ((ws.httprequest.protocol == 1) || (ws.httprequest.protocol == 6)) + { // Terminal + 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. } - } else if (ws.httprequest.protocol == 2) { // Desktop + } else if (ws.httprequest.protocol == 2) + { // Desktop ws.httprequest.desktop.kvm.pipe(ws.webrtc.rtcchannel, { dataTypeSkip: 1 }); // 0 = Binary, 1 = Text. } break; @@ -2625,7 +2930,8 @@ function createMeshCore(agent) { ws.webrtc.websocket = ws; ws.webrtc.on('connected', function () { /*sendConsoleText('Tunnel #' + this.websocket.tunnel.index + ' WebRTC connected');*/ }); ws.webrtc.on('disconnected', function () { /*sendConsoleText('Tunnel #' + this.websocket.tunnel.index + ' WebRTC disconnected');*/ }); - ws.webrtc.on('dataChannel', function (rtcchannel) { + ws.webrtc.on('dataChannel', function (rtcchannel) + { //sendConsoleText('WebRTC Datachannel open, protocol: ' + this.websocket.httprequest.protocol); rtcchannel.maxFragmentSize = 32768; rtcchannel.xrtc = this; @@ -2633,7 +2939,8 @@ function createMeshCore(agent) { this.rtcchannel = rtcchannel; this.websocket.rtcchannel = rtcchannel; this.websocket.rtcchannel.on('data', onTunnelWebRTCControlData); - this.websocket.rtcchannel.on('end', function () { + this.websocket.rtcchannel.on('end', function () + { // The WebRTC channel closed, unpipe the KVM now. This is also done when the web socket closes. //sendConsoleText('Tunnel #' + this.websocket.tunnel.index + ' WebRTC data channel closed'); if (this.websocket.desktop && this.websocket.desktop.kvm) @@ -2672,17 +2979,21 @@ function createMeshCore(agent) { var consoleHttpRequest = null; // Console HTTP response - function consoleHttpResponse(response) { + function consoleHttpResponse(response) + { response.data = function (data) { sendConsoleText(rstr2hex(buf2rstr(data)), this.sessionid); consoleHttpRequest = null; } response.close = function () { sendConsoleText('httprequest.response.close', this.sessionid); consoleHttpRequest = null; } }; // Open a web browser to a specified URL on current user's desktop - function openUserDesktopUrl(url) { + function openUserDesktopUrl(url) + { if ((url.toLowerCase().startsWith('http://') == false) && (url.toLowerCase().startsWith('https://') == false)) { return null; } var child = null; - try { - switch (process.platform) { + try + { + switch (process.platform) + { case 'win32': var user = require('user-sessions').getUsername(require('user-sessions').consoleUid()); child = require('child_process').execFile(process.env['windir'] + '\\system32\\cmd.exe', ['cmd']); @@ -2709,20 +3020,24 @@ function createMeshCore(agent) { } // Process a mesh agent console command - function processConsoleCommand(cmd, args, rights, sessionid) { - try { + function processConsoleCommand(cmd, args, rights, sessionid) + { + try + { var response = null; - switch (cmd) { + switch (cmd) + { case 'help': { // Displays available commands - var fin = '', f = '', availcommands = 'msh,timerinfo,coreinfo,coredump,service,fdsnapshot,fdcount,startupoptions,alert,agentsize,versions,help,info,osinfo,args,print,type,dbkeys,dbget,dbset,dbcompact,eval,parseuri,httpget,nwslist,plugin,wsconnect,wssend,wsclose,notify,ls,ps,kill,netinfo,location,power,wakeonlan,setdebug,smbios,rawsmbios,toast,lock,users,openurl,getscript,getclip,setclip,log,av,cpuinfo,sysinfo,apf,scanwifi,wallpaper,agentmsg'; + var fin = '', f = '', availcommands = 'agentupdate,msh,timerinfo,coreinfo,coredump,service,fdsnapshot,fdcount,startupoptions,alert,agentsize,versions,help,info,osinfo,args,print,type,dbkeys,dbget,dbset,dbcompact,eval,parseuri,httpget,nwslist,plugin,wsconnect,wssend,wsclose,notify,ls,ps,kill,netinfo,location,power,wakeonlan,setdebug,smbios,rawsmbios,toast,lock,users,openurl,getscript,getclip,setclip,log,av,cpuinfo,sysinfo,apf,scanwifi,wallpaper,agentmsg'; if (process.platform == 'win32') { availcommands += ',safemode,wpfhwacceleration,uac'; } if (amt != null) { availcommands += ',amt,amtconfig,amtevents'; } - if (process.platform != 'freebsd') { availcommands += ',vm';} + if (process.platform != 'freebsd') { availcommands += ',vm'; } if (require('MeshAgent').maxKvmTileSize != null) { availcommands += ',kvmmode'; } try { require('zip-reader'); availcommands += ',zip,unzip'; } catch (e) { } availcommands = availcommands.split(',').sort(); - while (availcommands.length > 0) { + while (availcommands.length > 0) + { if (f.length > 90) { fin += (f + ',\r\n'); f = ''; } f += (((f != '') ? ', ' : ' ') + availcommands.shift()); } @@ -2731,93 +3046,11 @@ function createMeshCore(agent) { break; } case 'agentupdate': - if (require('MeshAgent').ARCHID == null) - { - response = 'Unable to initiate update, agent ARCHID is not defined'; - break; - } - if (this._selfupdate != null) - { - response = "Self update already in progress..."; - } - else - { - var agentfilename = process.execPath.split(process.platform == 'win32' ? '\\' : '/').pop(); - var name = require('MeshAgent').serviceName; - if (name == null) { name = process.platform == 'win32' ? 'Mesh Agent' : 'meshagent'; } - try - { - var s = require('service-manager').manager.getService(name); - if (s.isMe()) - { - sendConsoleText('Service check SUCCESS'); - } - else - { - s.close(); - throw ('not a service'); - break; - } - if(process.platform=='win32') {s.close();} - } - catch (zz) - { - response = 'This is not a service instance'; - break; - } - sendConsoleText('Downloading update...'); - var options = require('http').parseUri(require('MeshAgent').ServerUrl); - options.protocol = 'https:'; - options.path = ('/meshagents?id=' + require('MeshAgent').ARCHID); - options.rejectUnauthorized = false; - this._selfupdate = require('https').get(options); - this._selfupdate.on('response', function (img) - { - this._file = require('fs').createWriteStream(agentfilename + '.update', {flags: 'wb'}); - this._filehash = require('SHA384Stream').create(); - this._filehash.on('hash', function (h) - { - sendConsoleText('Download complete. HASH=' + h.toString('hex')); - if(process.platform == 'win32') - { - this.child = require('child_process').execFile(process.env['windir'] + '\\system32\\cmd.exe', - ['/C wmic service "' + name + '" call stopservice && copy "' + process.cwd() + agentfilename + '.update" "' + process.execPath + '" && wmic service "' + name + '" call startservice && erase "' + process.cwd() + agentfilename + '.update"'], { type: 4 | 0x8000 }); - } - else - { - // remove binary - require('fs').unlinkSync(process.execPath); - - // copy update - require('fs').copyFileSync(process.cwd() + agentfilename + '.update', process.execPath); - - // erase update - require('fs').unlinkSync(process.cwd() + agentfilename + '.update'); - - // add execute permissions - var m = require('fs').statSync(process.execPath).mode; - m |= (require('fs').CHMOD_MODES.S_IXUSR | require('fs').CHMOD_MODES.S_IXGRP | require('fs').CHMOD_MODES.S_IXOTH); - require('fs').chmodSync(process.execPath, m); - - sendConsoleText('Restarting service...'); - try - { - // restart service - var s = require('service-manager').manager.getService(name); - s.restart(); - } - catch(zz) - { - sendConsoleText('Error restarting service'); - } - } - }); - img.pipe(this._file); - img.pipe(this._filehash); - }); - this._selfupdate.on('error', function (e) { sendConsoleText('Error fetching update'); }); - } - + require('MeshAgent').SendCommand({ action: 'agentupdate' }); + break; + case 'agentupdateex': + // Perform an direct agent update without requesting any information from the server, this should not typically be used. + agentUpdate_Start(null, { session: sessionid }); break; case 'msh': response = JSON.stringify(_MSH(), null, 2); @@ -2850,21 +3083,26 @@ function createMeshCore(agent) { break; } case 'agentmsg': { - if (args['_'].length == 0) { + if (args['_'].length == 0) + { response = "Proper usage:\r\n agentmsg add \"[message]\" [iconIndex]\r\n agentmsg remove [index]\r\n agentmsg list"; // Display usage - } else { - if ((args['_'][0] == 'add') && (args['_'].length > 1)) { + } else + { + if ((args['_'][0] == 'add') && (args['_'].length > 1)) + { var msgIndex = 1, iconIndex = 0; while (tunnelUserCount.msg[msgIndex] != null) { msgIndex++; } if (args['_'].length >= 3) { try { iconIndex = parseInt(args['_'][2]); } catch (e) { } } if (typeof iconIndex != 'number') { iconIndex = 0; } tunnelUserCount.msg[msgIndex] = { msg: args['_'][1], icon: iconIndex }; response = 'Agent message ' + msgIndex + ' added.'; - } else if ((args['_'][0] == 'remove') && (args['_'].length > 1)) { + } else if ((args['_'][0] == 'remove') && (args['_'].length > 1)) + { var msgIndex = 0; try { msgIndex = parseInt(args['_'][1]); } catch (x) { } if (tunnelUserCount.msg[msgIndex] == null) { response = "Message not found."; } else { delete tunnelUserCount.msg[msgIndex]; response = "Message removed."; } - } else if (args['_'][0] == 'list') { + } else if (args['_'][0] == 'list') + { response = JSON.stringify(tunnelUserCount.msg, null, 2); } try { mesh.SendCommand({ action: 'sessions', type: 'msg', value: tunnelUserCount.msg }); } catch (x) { } @@ -2879,9 +3117,11 @@ function createMeshCore(agent) { break; } case 'coredump': - if (args['_'].length != 1) { + if (args['_'].length != 1) + { response = "Proper usage: coredump on|off|status|clear"; // Display usage - } else { + } else + { switch (args['_'][0].toLowerCase()) { case 'on': @@ -2894,15 +3134,20 @@ function createMeshCore(agent) { break; case 'status': response = 'coredump is: ' + ((process.coreDumpLocation == null) ? 'off' : 'on'); - if (process.coreDumpLocation != null) { - if (process.platform == 'win32') { - if (fs.existsSync(process.coreDumpLocation)) { + if (process.coreDumpLocation != null) + { + if (process.platform == 'win32') + { + if (fs.existsSync(process.coreDumpLocation)) + { response += '\r\n CoreDump present at: ' + process.coreDumpLocation; response += '\r\n CoreDump Time: ' + new Date(fs.statSync(process.coreDumpLocation).mtime).getTime(); response += '\r\n Agent Time : ' + new Date(fs.statSync(process.execPath).mtime).getTime(); } - } else { - if ((process.cwd() != '//') && fs.existsSync(process.cwd() + 'core')) { + } else + { + if ((process.cwd() != '//') && fs.existsSync(process.cwd() + 'core')) + { response += '\r\n CoreDump present at: ' + process.cwd() + 'core'; response += '\r\n CoreDump Time: ' + new Date(fs.statSync(process.cwd() + 'core').mtime).getTime(); response += '\r\n Agent Time : ' + new Date(fs.statSync(process.execPath).mtime).getTime(); @@ -2932,11 +3177,11 @@ function createMeshCore(agent) { { svcname = require('MeshAgent').serviceName; } - catch(x) + catch (x) { } var s = require('service-manager').manager.getService(svcname); - switch(args['_'][0].toLowerCase()) + switch (args['_'][0].toLowerCase()) { case 'status': response = 'Service ' + (s.isRunning() ? (s.isMe() ? '[SELF]' : '[RUNNING]') : ('[NOT RUNNING]')); @@ -2959,9 +3204,11 @@ function createMeshCore(agent) { } break; case 'zip': - if (args['_'].length == 0) { + if (args['_'].length == 0) + { response = "Proper usage: zip (output file name), input1 [, input n]"; // Display usage - } else { + } else + { var p = args['_'].join(' ').split(','); var ofile = p.shift(); sendConsoleText('Writing ' + ofile + '...'); @@ -2974,16 +3221,19 @@ function createMeshCore(agent) { } break; case 'unzip': - if (args['_'].length == 0) { + if (args['_'].length == 0) + { response = "Proper usage: unzip input, destination"; // Display usage - } else { + } else + { var p = args['_'].join(' ').split(','); if (p.length != 2) { response = "Proper usage: unzip input, destination"; break; } // Display usage var prom = require('zip-reader').read(p[0]); prom._dest = p[1]; prom.self = this; prom.sessionid = sessionid; - prom.then(function (zipped) { + prom.then(function (zipped) + { sendConsoleText('Extracting to ' + this._dest + '...', this.sessionid); zipped.extractAll(this._dest).then(function () { sendConsoleText('finished unzipping', this.sessionid); }, function (e) { sendConsoleText('Error unzipping: ' + e, this.sessionid); }).parentPromise.sessionid = this.sessionid; }, function (e) { sendConsoleText('Error unzipping: ' + e, this.sessionid); }); @@ -2991,11 +3241,13 @@ function createMeshCore(agent) { break; case 'setbattery': // require('MeshAgent').SendCommand({ action: 'battery', state: 'dc', level: 55 }); - if ((args['_'].length > 0) && ((args['_'][0] == 'ac') || (args['_'][0] == 'dc'))) { + if ((args['_'].length > 0) && ((args['_'][0] == 'ac') || (args['_'][0] == 'dc'))) + { var b = { action: 'battery', state: args['_'][0] }; if (args['_'].length == 2) { b.level = parseInt(args['_'][1]); } require('MeshAgent').SendCommand(b); - } else { + } else + { require('MeshAgent').SendCommand({ action: 'battery' }); } break; @@ -3024,7 +3276,7 @@ function createMeshCore(agent) { } else { - switch(args['_'][0].toUpperCase()) + switch (args['_'][0].toUpperCase()) { case 'GET': var secd = require('win-registry').QueryKey(require('win-registry').HKEY.LocalMachine, 'Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System', 'PromptOnSecureDesktop'); @@ -3047,7 +3299,7 @@ function createMeshCore(agent) { require('win-registry').WriteKey(require('win-registry').HKEY.LocalMachine, 'Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System', 'PromptOnSecureDesktop', 1); response = 'UAC mode changed to: Secure Desktop'; } - catch(e) + catch (e) { response = "Unable to change UAC Mode"; } @@ -3058,9 +3310,9 @@ function createMeshCore(agent) { } } break; - case 'vm': - response = 'Virtual Machine = ' + require('identifiers').isVM(); - break; + case 'vm': + response = 'Virtual Machine = ' + require('identifiers').isVM(); + break; case 'startupoptions': response = JSON.stringify(require('MeshAgent').getStartupOptions()); break; @@ -3071,72 +3323,81 @@ function createMeshCore(agent) { } else { - if(require('MeshAgent').maxKvmTileSize == 0) + if (require('MeshAgent').maxKvmTileSize == 0) { response = 'KVM Mode: Full JUMBO'; } - else + else { response = 'KVM Mode: ' + (require('MeshAgent').maxKvmTileSize <= 65500 ? 'NO JUMBO' : 'Partial JUMBO'); - response += (', TileLimit: ' + (require('MeshAgent').maxKvmTileSize < 1024 ? (require('MeshAgent').maxKvmTileSize + ' bytes') : (Math.round(require('MeshAgent').maxKvmTileSize/1024) + ' Kbytes'))); + response += (', TileLimit: ' + (require('MeshAgent').maxKvmTileSize < 1024 ? (require('MeshAgent').maxKvmTileSize + ' bytes') : (Math.round(require('MeshAgent').maxKvmTileSize / 1024) + ' Kbytes'))); } } break; case 'alert': - if (args['_'].length == 0) + if (args['_'].length == 0) { response = "Proper usage: alert TITLE, CAPTION [, TIMEOUT]"; // Display usage } else { var p = args['_'].join(' ').split(','); - if(p.length<2) + if (p.length < 2) { response = "Proper usage: alert TITLE, CAPTION [, TIMEOUT]"; // Display usage } else { - this._alert = require('message-box').create(p[0], p[1], p.length==3?parseInt(p[2]):9999,1); + this._alert = require('message-box').create(p[0], p[1], p.length == 3 ? parseInt(p[2]) : 9999, 1); } } break; case 'agentsize': var actualSize = Math.floor(require('fs').statSync(process.execPath).size / 1024); - if (process.platform == 'win32') { + if (process.platform == 'win32') + { // Check the Agent Uninstall MetaData for correctness, as the installer may have written an incorrect value var writtenSize = 0; try { writtenSize = require('win-registry').QueryKey(require('win-registry').HKEY.LocalMachine, 'Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\MeshCentralAgent', 'EstimatedSize'); } catch (e) { response = e; } - if (writtenSize != actualSize) { + if (writtenSize != actualSize) + { response = "Size updated from: " + writtenSize + " to: " + actualSize; try { require('win-registry').WriteKey(require('win-registry').HKEY.LocalMachine, 'Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\MeshCentralAgent', 'EstimatedSize', actualSize); } catch (e) { response = e; } - } else { response = "Agent Size: " + actualSize + " kb"; } - } else { response = "Agent Size: " + actualSize + " kb"; } + } else + { response = "Agent Size: " + actualSize + " kb"; } + } else + { response = "Agent Size: " + actualSize + " kb"; } break; case 'versions': response = JSON.stringify(process.versions, null, ' '); break; case 'wpfhwacceleration': if (process.platform != 'win32') { throw ("wpfhwacceleration setting is only supported on Windows"); } - if (args['_'].length != 1) { + if (args['_'].length != 1) + { response = "Proper usage: wpfhwacceleration (ON|OFF|STATUS)"; // Display usage } - else { + else + { var reg = require('win-registry'); var uname = require('user-sessions').getUsername(require('user-sessions').consoleUid()); var key = reg.usernameToUserKey(uname); - switch (args['_'][0].toUpperCase()) { + switch (args['_'][0].toUpperCase()) + { default: response = "Proper usage: wpfhwacceleration (ON|OFF|STATUS|DEFAULT)"; // Display usage break; case 'ON': - try { + try + { reg.WriteKey(reg.HKEY.Users, key + '\\SOFTWARE\\Microsoft\\Avalon.Graphics', 'DisableHWAcceleration', 0); response = "OK"; } catch (e) { response = "FAILED"; } break; case 'OFF': - try { + try + { reg.WriteKey(reg.HKEY.Users, key + '\\SOFTWARE\\Microsoft\\Avalon.Graphics', 'DisableHWAcceleration', 1); response = 'OK'; } catch (e) { response = 'FAILED'; } @@ -3154,59 +3415,76 @@ function createMeshCore(agent) { } break; case 'tsid': - if (process.platform == 'win32') { - if (args['_'].length != 1) { + if (process.platform == 'win32') + { + if (args['_'].length != 1) + { response = "TSID: " + (require('MeshAgent')._tsid == null ? "console" : require('MeshAgent')._tsid); - } else { + } else + { var i = parseInt(args['_'][0]); require('MeshAgent')._tsid = (isNaN(i) ? null : i); response = "TSID set to: " + (require('MeshAgent')._tsid == null ? "console" : require('MeshAgent')._tsid); } - } else { response = "TSID command only supported on Windows"; } + } else + { response = "TSID command only supported on Windows"; } break; case 'activeusers': - if (process.platform == 'win32') { + if (process.platform == 'win32') + { var p = require('user-sessions').enumerateUsers(); p.sessionid = sessionid; - p.then(function (u) { + p.then(function (u) + { var v = []; - for (var i in u) { + for (var i in u) + { if (u[i].State == 'Active') { v.push({ tsid: i, type: u[i].StationName, user: u[i].Username, domain: u[i].Domain }); } } sendConsoleText(JSON.stringify(v, null, 1), this.sessionid); }); - } else { response = "activeusers command only supported on Windows"; } + } else + { response = "activeusers command only supported on Windows"; } break; case 'wallpaper': - if (process.platform != 'win32' && !(process.platform == 'linux' && require('linux-gnome-helpers').available)) { + if (process.platform != 'win32' && !(process.platform == 'linux' && require('linux-gnome-helpers').available)) + { response = "wallpaper command not supported on this platform"; } - else { - if (args['_'].length != 1) { + else + { + if (args['_'].length != 1) + { response = 'Proper usage: wallpaper (GET|TOGGLE)'; // Display usage } - else { - switch (args['_'][0].toUpperCase()) { + else + { + switch (args['_'][0].toUpperCase()) + { default: response = 'Proper usage: wallpaper (GET|TOGGLE)'; // Display usage break; case 'GET': case 'TOGGLE': - if (process.platform == 'win32') { + if (process.platform == 'win32') + { var id = require('user-sessions').getProcessOwnerName(process.pid).tsid == 0 ? 1 : 0; var child = require('child_process').execFile(process.execPath, [process.execPath.split('\\').pop(), '-b64exec', 'dmFyIFNQSV9HRVRERVNLV0FMTFBBUEVSID0gMHgwMDczOwp2YXIgU1BJX1NFVERFU0tXQUxMUEFQRVIgPSAweDAwMTQ7CnZhciBHTSA9IHJlcXVpcmUoJ19HZW5lcmljTWFyc2hhbCcpOwp2YXIgdXNlcjMyID0gR00uQ3JlYXRlTmF0aXZlUHJveHkoJ3VzZXIzMi5kbGwnKTsKdXNlcjMyLkNyZWF0ZU1ldGhvZCgnU3lzdGVtUGFyYW1ldGVyc0luZm9BJyk7CgppZiAocHJvY2Vzcy5hcmd2Lmxlbmd0aCA9PSAzKQp7CiAgICB2YXIgdiA9IEdNLkNyZWF0ZVZhcmlhYmxlKDEwMjQpOwogICAgdXNlcjMyLlN5c3RlbVBhcmFtZXRlcnNJbmZvQShTUElfR0VUREVTS1dBTExQQVBFUiwgdi5fc2l6ZSwgdiwgMCk7CiAgICBjb25zb2xlLmxvZyh2LlN0cmluZyk7CiAgICBwcm9jZXNzLmV4aXQoKTsKfQplbHNlCnsKICAgIHZhciBuYiA9IEdNLkNyZWF0ZVZhcmlhYmxlKHByb2Nlc3MuYXJndlszXSk7CiAgICB1c2VyMzIuU3lzdGVtUGFyYW1ldGVyc0luZm9BKFNQSV9TRVRERVNLV0FMTFBBUEVSLCBuYi5fc2l6ZSwgbmIsIDApOwogICAgcHJvY2Vzcy5leGl0KCk7Cn0='], { type: id }); child.stdout.str = ''; child.stdout.on('data', function (c) { this.str += c.toString(); }); child.stderr.on('data', function () { }); child.waitExit(); var current = child.stdout.str.trim(); - if (args['_'][0].toUpperCase() == 'GET') { + if (args['_'][0].toUpperCase() == 'GET') + { response = current; break; } - if (current != '') { + if (current != '') + { require('MeshAgent')._wallpaper = current; response = 'Wallpaper cleared'; - } else { + } else + { response = 'Wallpaper restored'; } child = require('child_process').execFile(process.execPath, [process.execPath.split('\\').pop(), '-b64exec', 'dmFyIFNQSV9HRVRERVNLV0FMTFBBUEVSID0gMHgwMDczOwp2YXIgU1BJX1NFVERFU0tXQUxMUEFQRVIgPSAweDAwMTQ7CnZhciBHTSA9IHJlcXVpcmUoJ19HZW5lcmljTWFyc2hhbCcpOwp2YXIgdXNlcjMyID0gR00uQ3JlYXRlTmF0aXZlUHJveHkoJ3VzZXIzMi5kbGwnKTsKdXNlcjMyLkNyZWF0ZU1ldGhvZCgnU3lzdGVtUGFyYW1ldGVyc0luZm9BJyk7CgppZiAocHJvY2Vzcy5hcmd2Lmxlbmd0aCA9PSAzKQp7CiAgICB2YXIgdiA9IEdNLkNyZWF0ZVZhcmlhYmxlKDEwMjQpOwogICAgdXNlcjMyLlN5c3RlbVBhcmFtZXRlcnNJbmZvQShTUElfR0VUREVTS1dBTExQQVBFUiwgdi5fc2l6ZSwgdiwgMCk7CiAgICBjb25zb2xlLmxvZyh2LlN0cmluZyk7CiAgICBwcm9jZXNzLmV4aXQoKTsKfQplbHNlCnsKICAgIHZhciBuYiA9IEdNLkNyZWF0ZVZhcmlhYmxlKHByb2Nlc3MuYXJndlszXSk7CiAgICB1c2VyMzIuU3lzdGVtUGFyYW1ldGVyc0luZm9BKFNQSV9TRVRERVNLV0FMTFBBUEVSLCBuYi5fc2l6ZSwgbmIsIDApOwogICAgcHJvY2Vzcy5leGl0KCk7Cn0=', current != '' ? '""' : require('MeshAgent')._wallpaper], { type: id }); @@ -3214,17 +3492,21 @@ function createMeshCore(agent) { child.stderr.on('data', function () { }); child.waitExit(); } - else { + else + { var id = require('user-sessions').consoleUid(); var current = require('linux-gnome-helpers').getDesktopWallpaper(id); - if (args['_'][0].toUpperCase() == 'GET') { + if (args['_'][0].toUpperCase() == 'GET') + { response = current; break; } - if (current != '/dev/null') { + if (current != '/dev/null') + { require('MeshAgent')._wallpaper = current; response = 'Wallpaper cleared'; - } else { + } else + { response = 'Wallpaper restored'; } require('linux-gnome-helpers').setDesktopWallpaper(id, current != '/dev/null' ? undefined : require('MeshAgent')._wallpaper); @@ -3289,30 +3571,32 @@ function createMeshCore(agent) { } } break; - /* - case 'border': - { - if ((args['_'].length == 1) && (args['_'][0] == 'on')) { - if (meshCoreObj.users.length > 0) { - obj.borderManager.Start(meshCoreObj.users[0]); - response = 'Border blinking is on.'; + /* + case 'border': + { + if ((args['_'].length == 1) && (args['_'][0] == 'on')) { + if (meshCoreObj.users.length > 0) { + obj.borderManager.Start(meshCoreObj.users[0]); + response = 'Border blinking is on.'; + } else { + response = 'Cannot turn on border blinking, no logged in users.'; + } + } else if ((args['_'].length == 1) && (args['_'][0] == 'off')) { + obj.borderManager.Stop(); + response = 'Border blinking is off.'; } else { - response = 'Cannot turn on border blinking, no logged in users.'; + response = 'Proper usage: border "on|off"'; // Display correct command usage } - } else if ((args['_'].length == 1) && (args['_'][0] == 'off')) { - obj.borderManager.Stop(); - response = 'Border blinking is off.'; - } else { - response = 'Proper usage: border "on|off"'; // Display correct command usage } - } - break; - */ + break; + */ case 'av': - if (process.platform == 'win32') { + if (process.platform == 'win32') + { // Windows Command: "wmic /Namespace:\\root\SecurityCenter2 Path AntiVirusProduct get /FORMAT:CSV" response = JSON.stringify(require('win-info').av(), null, 1); - } else { + } else + { response = 'Not supported on the platform'; } break; @@ -3320,21 +3604,27 @@ function createMeshCore(agent) { if (args['_'].length != 1) { response = 'Proper usage: log "sample text"'; } else { MeshServerLog(args['_'][0]); response = 'ok'; } break; case 'getclip': - if (require('MeshAgent').isService) { + if (require('MeshAgent').isService) + { require('clipboard').dispatchRead().then(function (str) { sendConsoleText(str, sessionid); }); - } else { + } else + { require("clipboard").read().then(function (str) { sendConsoleText(str, sessionid); }); } break; case 'setclip': { - if (args['_'].length != 1) { + if (args['_'].length != 1) + { response = 'Proper usage: setclip "sample text"'; - } else { - if (require('MeshAgent').isService) { + } else + { + if (require('MeshAgent').isService) + { require('clipboard').dispatchWrite(args['_'][0]); response = 'Setting clipboard to: "' + args['_'][0] + '"'; } - else { + else + { require("clipboard")(args['_'][0]); response = 'Setting clipboard to: "' + args['_'][0] + '"'; } } @@ -3352,10 +3642,12 @@ function createMeshCore(agent) { } case 'toast': { if (args['_'].length < 1) { response = 'Proper usage: toast "message"'; } else { - if (require('MeshAgent')._tsid == null) { + if (require('MeshAgent')._tsid == null) + { require('toaster').Toast('MeshCentral', args['_'][0]).then(sendConsoleText, sendConsoleText); } - else { + else + { require('toaster').Toast('MeshCentral', args['_'][0], require('MeshAgent')._tsid).then(sendConsoleText, sendConsoleText); } } @@ -3367,7 +3659,8 @@ function createMeshCore(agent) { break; } case 'ps': { - processManager.getProcesses(function (plist) { + processManager.getProcesses(function (plist) + { var x = ''; for (var i in plist) { x += i + ((plist[i].user) ? (', ' + plist[i].user) : '') + ', ' + plist[i].cmd + '\r\n'; } sendConsoleText(x, sessionid); @@ -3375,9 +3668,11 @@ function createMeshCore(agent) { break; } case 'kill': { - if ((args['_'].length < 1)) { + if ((args['_'].length < 1)) + { response = 'Proper usage: kill [pid]'; // Display correct command usage - } else { + } else + { process.kill(parseInt(args['_'][0])); response = 'Killed process ' + args['_'][0] + '.'; } @@ -3390,10 +3685,13 @@ function createMeshCore(agent) { case 'rawsmbios': { if (SMBiosTablesRaw == null) { response = 'SMBios tables not available.'; } else { response = ''; - for (var i in SMBiosTablesRaw) { + for (var i in SMBiosTablesRaw) + { var header = false; - for (var j in SMBiosTablesRaw[i]) { - if (SMBiosTablesRaw[i][j].length > 0) { + for (var j in SMBiosTablesRaw[i]) + { + if (SMBiosTablesRaw[i][j].length > 0) + { if (header == false) { response += ('Table type #' + i + ((require('smbios').smTableTypes[i] == null) ? '' : (', ' + require('smbios').smTableTypes[i]))) + '\r\n'; header = true; } response += (' ' + SMBiosTablesRaw[i][j].toString('hex')) + '\r\n'; } @@ -3403,9 +3701,11 @@ function createMeshCore(agent) { break; } case 'eval': { // Eval JavaScript - if (args['_'].length < 1) { + if (args['_'].length < 1) + { response = 'Proper usage: eval "JavaScript code"'; // Display correct command usage - } else { + } else + { response = JSON.stringify(mesh.eval(args['_'][0])); // This can only be run by trusted administrator. } break; @@ -3420,18 +3720,22 @@ function createMeshCore(agent) { { } - 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 { + } else + { try { diagnosticAgent_uninstall(); } catch (e) { } var js = "require('service-manager').manager.getService('" + agentName + "').stop(); require('service-manager').manager.uninstallService('" + agentName + "'); process.exit();"; this.child = require('child_process').execFile(process.execPath, [process.platform == 'win32' ? (process.execPath.split('\\').pop()) : (process.execPath.split('/').pop()), '-b64exec', Buffer.from(js).toString('base64')], { type: 4, detached: true }); } break; case 'notify': { // Send a notification message to the mesh - if (args['_'].length != 1) { + if (args['_'].length != 1) + { response = 'Proper usage: notify "message" [--session]'; // Display correct command usage - } else { + } else + { var notification = { action: 'msg', type: 'notify', value: args['_'][0], tag: 'console' }; if (args.session) { notification.sessionid = sessionid; } // If "--session" is specified, notify only this session, if not, the server will notify the mesh mesh.SendCommand(notification); // no sessionid or userid specified, notification will go to the entire mesh @@ -3443,15 +3747,18 @@ function createMeshCore(agent) { // CPU & memory utilization pr = require('sysinfo').cpuUtilization(); pr.sessionid = sessionid; - pr.then(function (data) { + pr.then(function (data) + { sendConsoleText(JSON.stringify({ cpu: data, memory: require('sysinfo').memUtilization() }, null, 1), this.sessionid); - }, function (e) { + }, function (e) + { sendConsoleText(e); }); break; } case 'sysinfo': { // Return system information - getSystemInformation(function (results, err) { + getSystemInformation(function (results, err) + { if (results == null) { sendConsoleText(err, this.sessionid); } else { sendConsoleText(JSON.stringify(results, null, 1), this.sessionid); } @@ -3459,7 +3766,7 @@ function createMeshCore(agent) { break; } case 'info': { // Return information about the agent and agent core module - response = 'Current Core: ' + meshCoreObj.value + '\r\nAgent Time: ' + Date() + '.\r\nUser Rights: 0x' + rights.toString(16) + '.\r\nPlatform: ' + process.platform + '.\r\nCapabilities: ' + meshCoreObj.caps + '.\r\nServer URL: ' + mesh.ServerUrl + '.'; + response = 'Current Core: ' + meshCoreObj.value + '\r\nAgent Time: ' + Date() + '.\r\nUser Rights: 0x' + rights.toString(16) + '.\r\nPlatform: ' + process.platform + '.\r\nCapabilities: ' + meshCoreObj.caps + '.\r\nServer URL: ' + mesh.ServerUrl + '.'; if (amt != null) { response += '\r\nBuilt-in LMS: ' + ['Disabled', 'Connecting..', 'Connected'][amt.lmsstate] + '.'; } if (meshCoreObj.osdesc) { response += '\r\nOS: ' + meshCoreObj.osdesc + '.'; } response += '\r\nModules: ' + addedModules.join(', ') + '.'; @@ -3473,10 +3780,12 @@ function createMeshCore(agent) { case 'osinfo': { // Return the operating system information var i = 1; if (args['_'].length > 0) { i = parseInt(args['_'][0]); if (i > 8) { i = 8; } response = 'Calling ' + i + ' times.'; } - for (var j = 0; j < i; j++) { + for (var j = 0; j < i; j++) + { var pr = require('os').name(); pr.sessionid = sessionid; - pr.then(function (v) { + pr.then(function (v) + { sendConsoleText("OS: " + v + (process.platform == 'win32' ? (require('win-virtual-terminal').supported ? ' [ConPTY: YES]' : ' [ConPTY: NO]') : ''), this.sessionid); }); } @@ -3494,9 +3803,11 @@ function createMeshCore(agent) { break; } case 'type': { // Returns the content of a file - if (args['_'].length == 0) { + if (args['_'].length == 0) + { response = 'Proper usage: type (filepath) [maxlength]'; // Display correct command usage - } else { + } else + { var max = 4096; if ((args['_'].length > 1) && (typeof args['_'][1] == 'number')) { max = args['_'][1]; } if (max > 4096) max = 4096; @@ -3515,18 +3826,22 @@ function createMeshCore(agent) { } case 'dbget': { // Return the data store value for a given key if (db == null) { response = 'Database not accessible.'; break; } - if (args['_'].length != 1) { + if (args['_'].length != 1) + { response = 'Proper usage: dbget (key)'; // Display the value for a given database key - } else { + } else + { response = db.Get(args['_'][0]); } break; } case 'dbset': { // Set a data store key and value pair if (db == null) { response = 'Database not accessible.'; break; } - if (args['_'].length != 2) { + if (args['_'].length != 2) + { response = 'Proper usage: dbset (key) (value)'; // Set a database key - } else { + } else + { var r = db.Put(args['_'][0], args['_'][1]); response = 'Key set: ' + r; } @@ -3539,20 +3854,27 @@ function createMeshCore(agent) { break; } case 'httpget': { - if (consoleHttpRequest != null) { + if (consoleHttpRequest != null) + { response = 'HTTP operation already in progress.'; - } else { - if (args['_'].length != 1) { + } else + { + if (args['_'].length != 1) + { response = 'Proper usage: httpget (url)'; - } else { + } else + { var options = http.parseUri(args['_'][0]); options.method = 'GET'; - if (options == null) { + if (options == null) + { response = 'Invalid url.'; - } else { + } else + { try { consoleHttpRequest = http.request(options, consoleHttpResponse); } catch (e) { response = 'Invalid HTTP GET request'; } consoleHttpRequest.sessionid = sessionid; - if (consoleHttpRequest != null) { + if (consoleHttpRequest != null) + { consoleHttpRequest.end(); response = 'HTTPGET ' + options.protocol + '//' + options.host + ':' + options.port + options.path; } @@ -3563,7 +3885,8 @@ function createMeshCore(agent) { } case 'wslist': { // List all web sockets response = ''; - for (var i in consoleWebSockets) { + for (var i in consoleWebSockets) + { var httprequest = consoleWebSockets[i]; response += 'Websocket #' + i + ', ' + httprequest.url + '\r\n'; } @@ -3571,16 +3894,20 @@ function createMeshCore(agent) { break; } case 'wsconnect': { // Setup a web socket - if (args['_'].length == 0) { + if (args['_'].length == 0) + { response = 'Proper usage: wsconnect (url)\r\nFor example: wsconnect wss://localhost:443/meshrelay.ashx?id=abc'; // Display correct command usage - } else { + } else + { var httprequest = null; - try { + try + { var options = http.parseUri(args['_'][0].split('$').join('%24').split('@').join('%40')); // Escape the $ and @ characters in the URL options.rejectUnauthorized = 0; httprequest = http.request(options); } catch (e) { response = 'Invalid HTTP websocket request'; } - if (httprequest != null) { + if (httprequest != null) + { httprequest.upgrade = onWebSocketUpgrade; httprequest.on('error', function (e) { sendConsoleText("ERROR: Unable to connect to: " + this.url + ", " + JSON.stringify(e)); }); @@ -3596,34 +3923,43 @@ function createMeshCore(agent) { break; } case 'wssend': { // Send data on a web socket - if (args['_'].length == 0) { + if (args['_'].length == 0) + { response = 'Proper usage: wssend (socketnumber)\r\n'; // Display correct command usage - for (var i in consoleWebSockets) { + for (var i in consoleWebSockets) + { var httprequest = consoleWebSockets[i]; response += 'Websocket #' + i + ', ' + httprequest.url + '\r\n'; } - } else { + } else + { var i = parseInt(args['_'][0]); var httprequest = consoleWebSockets[i]; - if (httprequest != undefined) { + if (httprequest != undefined) + { httprequest.s.write(args['_'][1]); response = 'ok'; - } else { + } else + { response = 'Invalid web socket number'; } } break; } case 'wsclose': { // Close a websocket - if (args['_'].length == 0) { + if (args['_'].length == 0) + { response = 'Proper usage: wsclose (socketnumber)'; // Display correct command usage - } else { + } else + { var i = parseInt(args['_'][0]); var httprequest = consoleWebSockets[i]; - if (httprequest != undefined) { + if (httprequest != undefined) + { if (httprequest.s != null) { httprequest.s.end(); } else { httprequest.end(); } response = 'ok'; - } else { + } else + { response = 'Invalid web socket number'; } } @@ -3641,12 +3977,15 @@ function createMeshCore(agent) { if (args['_'].length > 0) { xpath = obj.path.join(args['_'][0], '*'); } response = 'List of ' + xpath + '\r\n'; var results = fs.readdirSync(xpath); - for (var i = 0; i < results.length; ++i) { + for (var i = 0; i < results.length; ++i) + { var stat = null, p = obj.path.join(args['_'][0], results[i]); try { stat = fs.statSync(p); } catch (e) { } - if ((stat == null) || (stat == undefined)) { + if ((stat == null) || (stat == undefined)) + { response += (results[i] + "\r\n"); - } else { + } else + { response += (results[i] + " " + ((stat.isDirectory()) ? "(Folder)" : "(File)") + "\r\n"); } } @@ -3662,13 +4001,16 @@ function createMeshCore(agent) { break; } case 'amt': { // Show Intel AMT status - if (amt != null) { - amt.getMeiState(9, function (state) { + if (amt != null) + { + amt.getMeiState(9, function (state) + { var resp = "Intel AMT not detected."; if (state != null) { resp = objToString(state, 0, ' ', true); } sendConsoleText(resp, sessionid); }); - } else { + } else + { response = "Intel AMT not detected."; } break; @@ -3679,9 +4021,11 @@ function createMeshCore(agent) { break; } case 'wakeonlan': { // Send wake-on-lan - if ((args['_'].length != 1) || (args['_'][0].length != 12)) { + if ((args['_'].length != 1) || (args['_'][0].length != 12)) + { response = 'Proper usage: wakeonlan [mac], for example "wakeonlan 010203040506".'; - } else { + } else + { var count = sendWakeOnLan(args['_'][0]); response = 'Sent wake-on-lan on ' + count + ' interface(s).'; } @@ -3692,12 +4036,16 @@ function createMeshCore(agent) { break; } case 'power': { // Execute a power action on this computer - if (mesh.ExecPowerState == undefined) { + if (mesh.ExecPowerState == undefined) + { response = 'Power command not supported on this agent.'; - } else { - if ((args['_'].length == 0) || isNaN(Number(args['_'][0]))) { + } else + { + if ((args['_'].length == 0) || isNaN(Number(args['_'][0]))) + { response = 'Proper usage: power (actionNumber), where actionNumber is:\r\n LOGOFF = 1\r\n SHUTDOWN = 2\r\n REBOOT = 3\r\n SLEEP = 4\r\n HIBERNATE = 5\r\n DISPLAYON = 6\r\n KEEPAWAKE = 7\r\n BEEP = 8\r\n CTRLALTDEL = 9\r\n VIBRATE = 13\r\n FLASH = 14'; // Display correct command usage - } else { + } else + { var r = mesh.ExecPowerState(Number(args['_'][0]), Number(args['_'][1])); response = 'Power action executed with return code: ' + r + '.'; } @@ -3705,7 +4053,8 @@ function createMeshCore(agent) { break; } case 'location': { - getIpLocationData(function (location) { + getIpLocationData(function (location) + { sendConsoleText(objToString({ action: 'iplocation', type: 'publicip', value: location }, 0, ' ')); }); break; @@ -3715,10 +4064,12 @@ function createMeshCore(agent) { break; } case 'scanwifi': { - if (wifiScanner != null) { + if (wifiScanner != null) + { var wifiPresent = wifiScanner.hasWireless; if (wifiPresent) { response = "Perfoming Wifi scan..."; wifiScanner.Scan(); } else { response = "Wifi absent."; } - } else { response = "Wifi module not present."; } + } else + { response = "Wifi module not present."; } break; } case 'modules': { @@ -3731,40 +4082,51 @@ function createMeshCore(agent) { break; } case 'getscript': { - if (args['_'].length != 1) { + if (args['_'].length != 1) + { response = "Proper usage: getscript [scriptNumber]."; - } else { + } else + { mesh.SendCommand({ action: 'getScript', type: args['_'][0] }); } break; } 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'; } } @@ -3778,7 +4140,8 @@ function createMeshCore(agent) { case 'amtconfig': { if (amt == null) { response = "Intel AMT not detected."; break; } if (apftunnel != null) { response = "Intel AMT server tunnel already active"; break; } - amt.getMeiState(15, function (state) { + amt.getMeiState(15, function (state) + { var rx = ''; if ((state == null) || (state.ProvisioningState == null)) { rx = "Intel AMT not ready for configuration."; } else { var apfarg = { @@ -3792,15 +4155,19 @@ function createMeshCore(agent) { conntype: 2, // 0 = CIRA, 1 = Relay, 2 = LMS. The correct value is 2 since we are performing an LMS relay, other values for testing. meiState: state // MEI state will be passed to MPS server }; - if ((state.UUID == null) || (state.UUID.length != 36)) { + if ((state.UUID == null) || (state.UUID.length != 36)) + { rx = "Unable to get Intel AMT UUID"; - } else { + } else + { addAmtEvent('User LMS tunnel start.'); apftunnel = require('amt-apfclient')({ debug: false }, apfarg); - apftunnel.onJsonControl = function (data) { + apftunnel.onJsonControl = function (data) + { if (data.action == 'console') { addAmtEvent(data.msg); require('MeshAgent').SendCommand({ action: 'msg', type: 'console', value: data.msg }); } // Display a console message if (data.action == 'mestate') { amt.getMeiState(15, function (state) { apftunnel.updateMeiState(state); }); } // Update the MEI state - if (data.action == 'deactivate') { // Request CCM deactivation + if (data.action == 'deactivate') + { // Request CCM deactivation var amtMeiModule, amtMei; try { amtMeiModule = require('amt-mei'); amtMei = new amtMeiModule(); } catch (ex) { apftunnel.sendMeiDeactivationState(1); return; } amtMei.on('error', function (e) { apftunnel.sendMeiDeactivationState(1); }); @@ -3809,10 +4176,12 @@ function createMeshCore(agent) { if (data.action == 'close') { try { apftunnel.disconnect(); } catch (e) { } apftunnel = null; } // Close the CIRA-LMS connection } apftunnel.onChannelClosed = function () { addAmtEvent('User LMS tunnel closed.'); apftunnel = null; } - try { + try + { apftunnel.connect(); rx = "Started Intel AMT configuration"; - } catch (ex) { + } catch (ex) + { rx = JSON.stringify(ex); } } @@ -3822,14 +4191,17 @@ function createMeshCore(agent) { break; } case 'apf': { - if (meshCoreObj.intelamt !== null) { - if (args['_'].length == 1) { + if (meshCoreObj.intelamt !== null) + { + if (args['_'].length == 1) + { var connType = -1, connTypeStr = args['_'][0].toLowerCase(); if (connTypeStr == 'lms') { connType = 2; } if (connTypeStr == 'relay') { connType = 1; } if (connTypeStr == 'cira') { connType = 0; } if (connTypeStr == 'off') { connType = -2; } - if (connType >= 0) { // Connect + if (connType >= 0) + { // Connect var apfarg = { mpsurl: mesh.ServerUrl.replace('agent.ashx', 'apf.ashx'), mpsuser: Buffer.from(mesh.ServerInfo.MeshID, 'hex').toString('base64').substring(0, 16), @@ -3840,52 +4212,67 @@ function createMeshCore(agent) { clientuuid: meshCoreObj.intelamt.uuid, conntype: connType // 0 = CIRA, 1 = Relay, 2 = LMS. The correct value is 2 since we are performing an LMS relay, other values for testing. }; - if ((apfarg.clientuuid == null) || (apfarg.clientuuid.length != 36)) { + if ((apfarg.clientuuid == null) || (apfarg.clientuuid.length != 36)) + { response = "Unable to get Intel AMT UUID: " + apfarg.clientuuid; - } else { + } else + { apftunnel = require('amt-apfclient')({ debug: false }, apfarg); - apftunnel.onJsonControl = function (data) { + apftunnel.onJsonControl = function (data) + { if (data.action == 'console') { require('MeshAgent').SendCommand({ action: 'msg', type: 'console', value: data.msg }); } if (data.action == 'close') { try { apftunnel.disconnect(); } catch (e) { } apftunnel = null; } } apftunnel.onChannelClosed = function () { apftunnel = null; } - try { + try + { apftunnel.connect(); response = "Started APF tunnel"; - } catch (e) { + } catch (e) + { response = JSON.stringify(e); } } - } else if (connType == -2) { // Disconnect - try { + } else if (connType == -2) + { // Disconnect + try + { apftunnel.disconnect(); response = "Stopped APF tunnel"; - } catch (e) { + } catch (e) + { response = JSON.stringify(e); } apftunnel = null; - } else { + } else + { response = "Invalid command.\r\nUse: apf lms|relay|cira|off"; } - } else { + } else + { response = "APF tunnel is " + (apftunnel == null ? "off" : "on") + "\r\nUse: apf lms|relay|cira|off"; } - } else { + } else + { response = "APF tunnel requires Intel AMT"; } break; } case 'plugin': { - if (typeof args['_'][0] == 'string') { - try { + 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) { + } catch (e) + { response = "There was an error in the plugin (" + e + ")"; } - } else { + } else + { response = "Proper usage: plugin [pluginName] [args]."; } break; @@ -3900,23 +4287,183 @@ function createMeshCore(agent) { } // Send a mesh agent console command - function sendConsoleText(text, sessionid) { + function sendConsoleText(text, sessionid) + { if (typeof text == 'object') { text = JSON.stringify(text); } if (debugConsole && ((sessionid == null) || (sessionid == 'pipe'))) { broadcastToRegisteredApps({ cmd: 'console', value: text }); } if (sessionid != 'pipe') { require('MeshAgent').SendCommand({ action: 'msg', type: 'console', value: text, sessionid: sessionid }); } } + // Send a mesh agent message to server, placing a bubble/badge on the agent device + function sendAgentMessage(msg, icon) + { + if (sendAgentMessage.messages == null) + { + sendAgentMessage.messages = {}; + sendAgentMessage.nextid = 1; + } + sendAgentMessage.messages[sendAgentMessage.nextid++] = { msg: msg, icon: icon }; + require('MeshAgent').SendCommand({ action: 'sessions', type: 'msg', value: sendAgentMessage.messages }); + } + + // Start a JavaScript based Agent Self-Update + function agentUpdate_Start(updateurl, updateoptions) + { + // If this value is null + var sessionid = updateoptions != null ? updateoptions.session : null; // If this is null, messages will be broadcast. Otherwise they will be unicasted + + if (this._selfupdate != null) + { + // We were already called, so we will ignore this duplicate request + if (sessionid != null) { sendConsoleText('Self update already in progress...', sessionid); } + } + else + { + if (require('MeshAgent').ARCHID == null && updateurl == null) + { + // This agent doesn't have the ability to tell us which ARCHID it is, so we don't know which agent to pull + sendConsoleText('Unable to initiate update, agent ARCHID is not defined', sessionid); + } + else + { + var agentfilename = process.execPath.split(process.platform == 'win32' ? '\\' : '/').pop(); // Local File Name, ie: MeshAgent.exe + var name = require('MeshAgent').serviceName; + if (name == null) { name = process.platform == 'win32' ? 'Mesh Agent' : 'meshagent'; } // This is an older agent that doesn't expose the service name, so use the default + try + { + var s = require('service-manager').manager.getService(name); + if (!s.isMe()) + { + if (process.platform == 'win32') { s.close(); } + sendConsoleText('Self Update cannot continue, this agent is not an instance of (' + name + ')', sessionid); + return; + } + if (process.platform == 'win32') { s.close(); } + } + catch (zz) + { + sendConsoleText('Self Update Failed because this agent is not an instance of (' + name + ')', sessionid); + sendAgentMessage('Self Update Failed because this agent is not an instance of (' + name + ')', 3); + return; + } + + sendConsoleText('Downloading update...', sessionid); + var options = require('http').parseUri(updateurl != null ? updateurl : require('MeshAgent').ServerUrl); + options.protocol = 'https:'; + if (updateurl == null) { options.path = ('/meshagents?id=' + require('MeshAgent').ARCHID); } + options.rejectUnauthorized = false; + options.checkServerIdentity = function checkServerIdentity(certs) + { + // If the tunnel certificate matches the control channel certificate, accept the connection + try { if (require('MeshAgent').ServerInfo.ControlChannelCertificate.digest == certs[0].digest) return; } catch (ex) { } + try { if (require('MeshAgent').ServerInfo.ControlChannelCertificate.fingerprint == certs[0].fingerprint) return; } catch (ex) { } + + // Check that the certificate is the one expected by the server, fail if not. + if (checkServerIdentity.servertlshash == null) + { + sendConsoleText('Self Update failed, because the url cannot be verified', sessionid); + sendAgentMessage('Self Update failed, because the url cannot be verified', 3); + throw new Error('BadCert'); + } + if ((checkServerIdentity.servertlshash != null) && (checkServerIdentity.servertlshash.toLowerCase() != certs[0].digest.split(':').join('').toLowerCase())) + { + sendConsoleText('Self Update failed, because the supplied certificate does not match', sessionid); + sendAgentMessage('Self Update failed, because the supplied certificate does not match', 3); + throw new Error('BadCert') + } + } + options.checkServerIdentity.servertlshash = (updateoptions != null ? updateoptions.tlshash : null); + this._selfupdate = require('https').get(options); + this._selfupdate.on('error', function (e) + { + sendConsoleText('Self Update failed, because there was a problem trying to download the update', sessionid); + sendAgentMessage('Self Update failed, because there was a problem trying to download the update', 3); + }); + this._selfupdate.on('response', function (img) + { + this._file = require('fs').createWriteStream(agentfilename + '.update', { flags: 'wb' }); + this._filehash = require('SHA384Stream').create(); + this._filehash.on('hash', function (h) + { + if (updateoptions != null && updateoptions.hash != null) + { + if (updateoptions.hash.toLowerCase() == h.toString('hex').toLowerCase()) + { + sendConsoleText('Download complete. HASH verified.', sessionid); + } + else + { + sendConsoleText('Self Update FAILED because the downloaded agent FAILED hash check', sessionid); + sendAgentMessage('Self Update FAILED because the downloaded agent FAILED hash check', 3); + return; + } + } + else + { + sendConsoleText('Download complete. HASH=' + h.toString('hex'), sessionid); + } + + sendConsoleText('Updating and restarting agent...', sessionid); + if (process.platform == 'win32') + { + // Use _wexecve() equivalent to perform the update + this.child = require('child_process').execFile(process.env['windir'] + '\\system32\\cmd.exe', + ['/C wmic service "' + name + '" call stopservice && copy "' + process.cwd() + agentfilename + '.update" "' + process.execPath + '" && wmic service "' + name + '" call startservice && erase "' + process.cwd() + agentfilename + '.update"'], { type: 4 | 0x8000 }); + } + else + { + // remove binary + require('fs').unlinkSync(process.execPath); + + // copy update + require('fs').copyFileSync(process.cwd() + agentfilename + '.update', process.execPath); + + // erase update + require('fs').unlinkSync(process.cwd() + agentfilename + '.update'); + + // add execute permissions + var m = require('fs').statSync(process.execPath).mode; + m |= (require('fs').CHMOD_MODES.S_IXUSR | require('fs').CHMOD_MODES.S_IXGRP | require('fs').CHMOD_MODES.S_IXOTH); + require('fs').chmodSync(process.execPath, m); + + sendConsoleText('Restarting service...', sessionid); + try + { + // restart service + var s = require('service-manager').manager.getService(name); + s.restart(); + } + catch (zz) + { + sendConsoleText('Self Update encountered an error trying to restart service', sessionid); + sendAgentMessage('Self Update encountered an error trying to restart service', 3); + } + } + }); + img.pipe(this._file); + img.pipe(this._filehash); + }); + } + } + } + + + + // 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) { + function handleServerConnection(state) + { meshServerConnectionState = state; - if (meshServerConnectionState == 0) { + if (meshServerConnectionState == 0) + { // Server disconnected if (selfInfoUpdateTimer != null) { clearInterval(selfInfoUpdateTimer); selfInfoUpdateTimer = null; } lastSelfInfo = null; - } else { + } else + { // Server connected, send mesh core information var oldNodeId = db.Get('OldNodeId'); if (oldNodeId != null) { mesh.SendCommand({ action: 'mc1migration', oldnodeid: oldNodeId }); } @@ -3931,10 +4478,11 @@ function createMeshCore(agent) { { selfInfoUpdateTimer = setInterval(sendPeriodicServerUpdate, 1200000); // 20 minutes selfInfoUpdateTimer.metadata = 'meshcore (InfoUpdate Timer)'; - } + } // Send any state messages - if (Object.keys(tunnelUserCount.msg).length > 0) { + if (Object.keys(tunnelUserCount.msg).length > 0) + { try { mesh.SendCommand({ action: 'sessions', type: 'msg', value: tunnelUserCount.msg }); } catch (e) { } broadcastSessionsToRegisteredApps(); } @@ -3950,12 +4498,14 @@ function createMeshCore(agent) { // 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) { + function sendNetworkUpdate(force) + { sendNetworkUpdateNagleTimer = null; // Update the network interfaces information data var netInfo = { netif2: require('os').networkInterfaces() }; - if (netInfo.netif2) { + if (netInfo.netif2) + { netInfo.action = 'netinfo'; var netInfoStr = JSON.stringify(netInfo); if ((force == true) || (clearGatewayMac(netInfoStr) != clearGatewayMac(lastNetworkInfo))) { mesh.SendCommand(netInfo); lastNetworkInfo = netInfoStr; } @@ -3963,14 +4513,17 @@ function createMeshCore(agent) { } // Called periodically to check if we need to send updates to the server - function sendPeriodicServerUpdate(flags, force) { + function sendPeriodicServerUpdate(flags, force) + { if (meshServerConnectionState == 0) return; // Not connected to server, do nothing. if (!flags) { flags = 0xFFFFFFFF; } // If we have a connected MEI, get Intel ME information - if ((flags & 1) && (amt != null) && (amt.state == 2)) { + if ((flags & 1) && (amt != null) && (amt.state == 2)) + { delete meshCoreObj.intelamt; - amt.getMeiState(9, function (meinfo) { + amt.getMeiState(9, function (meinfo) + { meshCoreObj.intelamt = meinfo; meshCoreObj.intelamt.microlms = amt.lmsstate; meshCoreObjChanged(); @@ -3981,17 +4534,20 @@ function createMeshCore(agent) { if (flags & 2) { sendNetworkUpdateNagle(false); } // Update anti-virus information - if ((flags & 4) && (process.platform == 'win32')) { + if ((flags & 4) && (process.platform == 'win32')) + { // Windows Command: "wmic /Namespace:\\root\SecurityCenter2 Path AntiVirusProduct get /FORMAT:CSV" try { meshCoreObj.av = require('win-info').av(); meshCoreObjChanged(); } catch (e) { av = null; } // Antivirus //if (process.platform == 'win32') { try { meshCoreObj.pr = require('win-info').pendingReboot(); meshCoreObjChanged(); } catch (e) { meshCoreObj.pr = null; } } // Pending reboot } // Send available data right now - if (force) { + if (force) + { meshCoreObj = sortObjRec(meshCoreObj); var x = JSON.stringify(meshCoreObj); - if (x != LastPeriodicServerUpdate) { + if (x != LastPeriodicServerUpdate) + { LastPeriodicServerUpdate = x; mesh.SendCommand(meshCoreObj); } @@ -4002,11 +4558,13 @@ function createMeshCore(agent) { var LastPeriodicServerUpdate = null; var PeriodicServerUpdateNagleTimer = null; function meshCoreObjChanged() { if (PeriodicServerUpdateNagleTimer == null) { PeriodicServerUpdateNagleTimer = setTimeout(meshCoreObjChangedEx, 500); } } - function meshCoreObjChangedEx() { + function meshCoreObjChangedEx() + { PeriodicServerUpdateNagleTimer = null; meshCoreObj = sortObjRec(meshCoreObj); var x = JSON.stringify(meshCoreObj); - if (x != LastPeriodicServerUpdate) { + if (x != LastPeriodicServerUpdate) + { try { LastPeriodicServerUpdate = x; mesh.SendCommand(meshCoreObj); } catch (ex) { } } } @@ -4015,13 +4573,15 @@ function createMeshCore(agent) { function sortObj(o) { return Object.keys(o).sort().reduce(function (result, key) { result[key] = o[key]; return result; }, {}); } // Starting function - obj.start = function () { + obj.start = function () + { // Setup the mesh agent event handlers mesh.AddCommandHandler(handleServerCommand); mesh.AddConnectHandler(handleServerConnection); } - obj.stop = function () { + obj.stop = function () + { mesh.AddCommandHandler(null); mesh.AddConnectHandler(null); } @@ -4030,7 +4590,8 @@ function createMeshCore(agent) { 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) { + function onWebSocketUpgrade(response, s, head) + { sendConsoleText("WebSocket #" + this.index + " connected.", this.sessionid); this.s = s; s.httprequest = this; @@ -4044,9 +4605,11 @@ function createMeshCore(agent) { // // Startup for Duktape only. This file is not intended to run in NodeJS. // -try { +try +{ mainMeshCore = createMeshCore(); mainMeshCore.start(null); -} catch (e) { +} catch (e) +{ require('MeshAgent').SendCommand({ action: 'msg', type: 'console', value: "uncaughtException2: " + ex }); } diff --git a/agents/recoverycore.js b/agents/recoverycore.js index b66cde58..e70a6406 100644 --- a/agents/recoverycore.js +++ b/agents/recoverycore.js @@ -49,47 +49,48 @@ function sendAgentMessage(msg, icon) require('MeshAgent').SendCommand({ action: 'sessions', type: 'msg', value: sendAgentMessage.messages }); } +// Start a JavaScript based Agent Self-Update function agentUpdate_Start(updateurl, updateoptions) { - var sessionid = updateoptions != null ? updateoptions.session : null; + // If this value is null + var sessionid = updateoptions != null ? updateoptions.session : null; // If this is null, messages will be broadcast. Otherwise they will be unicasted if (this._selfupdate != null) { + // We were already called, so we will ignore this duplicate request if (sessionid != null) { sendConsoleText('Self update already in progress...', sessionid); } } else { if (require('MeshAgent').ARCHID == null && updateurl == null) { - if (sessionid != null) { sendConsoleText('Unable to initiate update, agent ARCHID is not defined', sessionid); } + // This agent doesn't have the ability to tell us which ARCHID it is, so we don't know which agent to pull + sendConsoleText('Unable to initiate update, agent ARCHID is not defined', sessionid); } else { - var agentfilename = process.execPath.split(process.platform == 'win32' ? '\\' : '/').pop(); + var agentfilename = process.execPath.split(process.platform == 'win32' ? '\\' : '/').pop(); // Local File Name, ie: MeshAgent.exe var name = require('MeshAgent').serviceName; - if (name == null) { name = process.platform == 'win32' ? 'Mesh Agent' : 'meshagent'; } + if (name == null) { name = process.platform == 'win32' ? 'Mesh Agent' : 'meshagent'; } // This is an older agent that doesn't expose the service name, so use the default try { var s = require('service-manager').manager.getService(name); if (!s.isMe()) { if (process.platform == 'win32') { s.close(); } - if (sessionid != null) { sendConsoleText('Service check FAILED', sessionid); } + sendConsoleText('Self Update cannot continue, this agent is not an instance of (' + name + ')', sessionid); return; } if (process.platform == 'win32') { s.close(); } } catch (zz) { - if (sessionid != null) { sendConsoleText('Service check FAILED', sessionid); } - else - { - sendAgentMessage('Self Update Failed, because this agent is not running as a service', 3); - } + sendConsoleText('Self Update Failed because this agent is not an instance of (' + name + ')', sessionid); + sendAgentMessage('Self Update Failed because this agent is not an instance of (' + name + ')', 3); return; } - if (sessionid != null) { sendConsoleText('Downloading update...', sessionid); } + sendConsoleText('Downloading update...', sessionid); var options = require('http').parseUri(updateurl != null ? updateurl : require('MeshAgent').ServerUrl); options.protocol = 'https:'; if (updateurl == null) { options.path = ('/meshagents?id=' + require('MeshAgent').ARCHID); } @@ -103,26 +104,14 @@ function agentUpdate_Start(updateurl, updateoptions) // Check that the certificate is the one expected by the server, fail if not. if (checkServerIdentity.servertlshash == null) { - if(sessionid!=null) - { - sendConsoleText('Self Update failed, because the url cannot be verified', sessionid); - } - else - { - sendAgentMessage('Self Update failed, because the url cannot be verified', 3); - } + sendConsoleText('Self Update failed, because the url cannot be verified', sessionid); + sendAgentMessage('Self Update failed, because the url cannot be verified', 3); throw new Error('BadCert'); } if ((checkServerIdentity.servertlshash != null) && (checkServerIdentity.servertlshash.toLowerCase() != certs[0].digest.split(':').join('').toLowerCase())) { - if (sessionid != null) - { - sendConsoleText('Self Update failed, because the supplied certificate does not match', sessionid); - } - else - { - sendAgentMessage('Self Update failed, because the supplied certificate does not match', 3); - } + sendConsoleText('Self Update failed, because the supplied certificate does not match', sessionid); + sendAgentMessage('Self Update failed, because the supplied certificate does not match', 3); throw new Error('BadCert') } } @@ -130,11 +119,8 @@ function agentUpdate_Start(updateurl, updateoptions) this._selfupdate = require('https').get(options); this._selfupdate.on('error', function (e) { - if (sessionid != null) { sendConsoleText('Error fetching update', sessionid); } - else - { - sendAgentMessage('Self Update failed, because there was a problem trying to download the update', 3); - } + sendConsoleText('Self Update failed, because there was a problem trying to download the update', sessionid); + sendAgentMessage('Self Update failed, because there was a problem trying to download the update', 3); }); this._selfupdate.on('response', function (img) { @@ -146,26 +132,24 @@ function agentUpdate_Start(updateurl, updateoptions) { if (updateoptions.hash.toLowerCase() == h.toString('hex').toLowerCase()) { - if (sessionid != null) { sendConsoleText('Download complete. HASH verified.', sessionid); } + sendConsoleText('Download complete. HASH verified.', sessionid); } else { - if (sessionid != null) { sendConsoleText('Download complete. HASH FAILED.', sessionid); } - else - { - sendAgentMessage('Self Update FAILED because the downloaded agent FAILED hash check', 3); - } + sendConsoleText('Self Update FAILED because the downloaded agent FAILED hash check', sessionid); + sendAgentMessage('Self Update FAILED because the downloaded agent FAILED hash check', 3); return; } } else { - if (sessionid != null) { sendConsoleText('Download complete. HASH=' + h.toString('hex'), sessionid); } + sendConsoleText('Download complete. HASH=' + h.toString('hex'), sessionid); } - if (sessionid != null) { sendConsoleText('Updating and restarting agent...', sessionid); } + sendConsoleText('Updating and restarting agent...', sessionid); if (process.platform == 'win32') { + // Use _wexecve() equivalent to perform the update this.child = require('child_process').execFile(process.env['windir'] + '\\system32\\cmd.exe', ['/C wmic service "' + name + '" call stopservice && copy "' + process.cwd() + agentfilename + '.update" "' + process.execPath + '" && wmic service "' + name + '" call startservice && erase "' + process.cwd() + agentfilename + '.update"'], { type: 4 | 0x8000 }); } @@ -185,7 +169,7 @@ function agentUpdate_Start(updateurl, updateoptions) m |= (require('fs').CHMOD_MODES.S_IXUSR | require('fs').CHMOD_MODES.S_IXGRP | require('fs').CHMOD_MODES.S_IXOTH); require('fs').chmodSync(process.execPath, m); - if (sessionid != null) { sendConsoleText('Restarting service...', sessionid); } + sendConsoleText('Restarting service...', sessionid); try { // restart service @@ -194,11 +178,8 @@ function agentUpdate_Start(updateurl, updateoptions) } catch (zz) { - if (sessionid != null) { sendConsoleText('Error restarting service', sessionid); } - else - { - sendAgentMessage('Self Update encountered an error trying to restart service', 3); - } + sendConsoleText('Self Update encountered an error trying to restart service', sessionid); + sendAgentMessage('Self Update encountered an error trying to restart service', 3); } } }); @@ -209,7 +190,6 @@ function agentUpdate_Start(updateurl, updateoptions) } } - // Return p number of spaces function addPad(p, ret) { var r = ''; for (var i = 0; i < p; i++) { r += ret; } return r; } @@ -643,7 +623,7 @@ function processConsoleCommand(cmd, args, rights, sessionid) { var response = null; switch (cmd) { case 'help': - response = "Available commands are: osinfo, dbkeys, dbget, dbset, dbcompact, netinfo, versions, agentupdate."; + response = "Available commands are: agentupdate, dbkeys, dbget, dbset, dbcompact, netinfo, osinfo, versions."; break; case 'versions': response = JSON.stringify(process.versions, null, ' ');