diff --git a/agents/modules_meshcmd/serviceManager.js b/agents/modules_meshcmd/serviceManager.js index e4f694ba..9c7a55d9 100644 --- a/agents/modules_meshcmd/serviceManager.js +++ b/agents/modules_meshcmd/serviceManager.js @@ -87,22 +87,12 @@ function serviceManager() { } return admin; }; + this.getProgramFolder = function getProgramFolder() { + if ((require('os').arch() == 'x64') && (this.GM.PointerSize == 4)) { return process.env['ProgramFiles(x86)']; } // 64 bit Windows with 32 Bit App + return process.env['ProgramFiles']; // All other cases: 32 bit Windows or 64 bit App + }; this.getServiceFolder = function getServiceFolder() { - var destinationFolder = null; - if (require('os').arch() == 'x64') { - // 64 bit Windows - if (this.GM.PointerSize == 4) { - // 32 Bit App - destinationFolder = process.env['ProgramFiles(x86)']; - } else { - // 64 bit App - destinationFolder = process.env['ProgramFiles']; - } - } else { - // 32 bit Windows - destinationFolder = process.env['ProgramFiles']; - } - return (destinationFolder + '\\Open Source\\MeshCmd'); + return getProgramFolder() + '\\Open Source\\MeshCmd'; }; this.enumerateService = function () { @@ -192,8 +182,10 @@ function serviceManager() { if (!this.isAdmin()) { throw ('Installing as Service, requires administrator permissions.'); } // Before we start, we need to copy the binary to the right place - var folder = this.getServiceFolder(); - if (!require('fs').existsSync(folder)) { require('fs').mkdirSync(folder); } + var folder = this.getProgramFolder() + '\\Open Source'; + if (!require('fs').existsSync(folder)) { require('fs').mkdirSync(folder); } // Create the "Open Source" folder + folder += '\\MeshCmd'; + if (!require('fs').existsSync(folder)) { require('fs').mkdirSync(folder); } // Create the "MeshCmd" folder require('fs').copyFileSync(options.servicePath, folder + '\\' + options.name + '.exe'); options.servicePath = folder + '\\' + options.name + '.exe'; console.log('Installing to "' + options.servicePath + '"'); diff --git a/certoperations.js b/certoperations.js index ed49b77c..e87c0d88 100644 --- a/certoperations.js +++ b/certoperations.js @@ -484,7 +484,16 @@ module.exports.CertificateOperations = function () { if (acceleratorCreateCount > 0) { acceleratorCreateCount--; var accelerator = fork(program, [], { stdio: ['pipe', 'pipe', 'pipe', 'ipc'] }); - accelerator.on('message', function (message) { this.func(message); if (pendingAccelerator.length > 0) { accelerator.send(pendingAccelerator.shift()); } else { freeAccelerators.push(this); } }); + accelerator.accid = acceleratorCreateCount; + accelerator.on('message', function (message) { + this.func(this.tag, message); + delete this.tag; + if (pendingAccelerator.length > 0) { + var x = pendingAccelerator.shift(); + if (x.tag) { this.tag = x.tag; delete x.tag; } + accelerator.send(x); + } else { freeAccelerators.push(this); } + }); accelerator.send({ action: 'setState', certs: obj.acceleratorCertStore }); return accelerator; } @@ -499,21 +508,22 @@ module.exports.CertificateOperations = function () { } // Perform any RSA signature, just pass in the private key and data. - obj.acceleratorPerformSignature = function (privatekey, data, func) { + obj.acceleratorPerformSignature = function (privatekey, data, tag, func) { if (acceleratorTotalCount <= 1) { // No accelerators available if (typeof privatekey == 'number') { privatekey = obj.acceleratorCertStore[privatekey].key; } const sign = obj.crypto.createSign('SHA384'); sign.end(new Buffer(data, 'binary')); - func(sign.sign(privatekey).toString('binary')); + func(tag, sign.sign(privatekey).toString('binary')); } else { var acc = obj.getAccelerator(); if (acc == null) { // Add to pending accelerator workload - pendingAccelerator.push({ action: 'sign', key: privatekey, data: data }); + pendingAccelerator.push({ action: 'sign', key: privatekey, data: data, tag: tag }); } else { // Send to accelerator now acc.func = func; + acc.tag = tag; acc.send({ action: 'sign', key: privatekey, data: data }); } } diff --git a/meshagent.js b/meshagent.js index 0b8cc77c..533929fd 100644 --- a/meshagent.js +++ b/meshagent.js @@ -6,6 +6,8 @@ * @version v0.0.1 */ +var AgentConnectCount = 0; + // Construct a MeshAgent object, called upon connection module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) { var obj = {}; @@ -30,6 +32,7 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) { const agentUpdateBlockSize = 65520; obj.remoteaddr = obj.ws._socket.remoteAddress; obj.useSHA386 = false; + obj.agentConnectCount = ++AgentConnectCount; if (obj.remoteaddr.startsWith('::ffff:')) { obj.remoteaddr = obj.remoteaddr.substring(7); } ws._socket.setKeepAlive(true, 240000); // Set TCP keep alive, 4 minutes @@ -65,6 +68,7 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) { if ((state.connectivity & 2) != 0) { obj.parent.parent.mpsserver.close(obj.parent.parent.mpsserver.ciraConnections[obj.dbNodeKey]); } // Disconnect CIRA connection } } + delete obj.nodeid; } // When data is received from the mesh agent web socket @@ -183,15 +187,15 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) { obj.agentnonce = msg.substring(50); if (obj.useSwarmCert == true) { // Perform the hash signature using older swarm server certificate - obj.parent.parent.certificateOperations.acceleratorPerformSignature(1, msg.substring(2) + obj.nonce, function (signature) { + obj.parent.parent.certificateOperations.acceleratorPerformSignature(1, msg.substring(2) + obj.nonce, obj, function (obj2, signature) { // Send back our certificate + signature - obj.send(obj.common.ShortToStr(2) + obj.common.ShortToStr(obj.parent.swarmCertificateAsn1.length) + obj.parent.swarmCertificateAsn1 + signature); // Command 2, certificate + signature + obj2.send(obj2.common.ShortToStr(2) + obj2.common.ShortToStr(obj2.parent.swarmCertificateAsn1.length) + obj2.parent.swarmCertificateAsn1 + signature); // Command 2, certificate + signature }); } else { // Perform the hash signature using the server agent certificate - obj.parent.parent.certificateOperations.acceleratorPerformSignature(0, msg.substring(2) + obj.nonce, function (signature) { + obj.parent.parent.certificateOperations.acceleratorPerformSignature(0, msg.substring(2) + obj.nonce, obj, function (obj2, signature) { // Send back our certificate + signature - obj.send(obj.common.ShortToStr(2) + obj.common.ShortToStr(obj.parent.agentCertificateAsn1.length) + obj.parent.agentCertificateAsn1 + signature); // Command 2, certificate + signature + obj2.send(obj2.common.ShortToStr(2) + obj.common.ShortToStr(obj2.parent.agentCertificateAsn1.length) + obj2.parent.agentCertificateAsn1 + signature); // Command 2, certificate + signature }); } @@ -259,7 +263,9 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) { // Once we get all the information about an agent, run this to hook everything up to the server function completeAgentConnection() { - if (obj.authenticated =! 1 || obj.meshid == null) return; + if (obj.authenticated = !1 || obj.meshid == null || obj.pendingCompleteAgentConnection) return; + obj.pendingCompleteAgentConnection = true; + // Check that the mesh exists obj.db.Get(obj.dbMeshKey, function (err, meshes) { if (meshes.length == 0) { console.log('Agent connected with invalid domain/mesh, holding connection (' + obj.remoteaddr + ', ' + obj.dbMeshKey + ').'); return; } // If we disconnect, the agnet will just reconnect. We need to log this or tell agent to connect in a few hours. @@ -327,6 +333,7 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) { } // We are done, ready to communicate with this agent + delete obj.pendingCompleteAgentConnection; obj.authenticated = 2; // Command 4, inform mesh agent that it's authenticated. diff --git a/multiserver.js b/multiserver.js index f3279ce8..b9db2900 100644 --- a/multiserver.js +++ b/multiserver.js @@ -94,9 +94,9 @@ module.exports.CreateMultiServer = function (parent, args) { obj.servernonce = msg.substring(50); // Perform the hash signature using the server agent certificate - obj.parent.parent.certificateOperations.acceleratorPerformSignature(0, msg.substring(2) + obj.nonce, function (signature) { + obj.parent.parent.certificateOperations.acceleratorPerformSignature(0, msg.substring(2) + obj.nonce, obj, function (obj2, signature) { // Send back our certificate + signature - obj.ws.send(obj.common.ShortToStr(2) + obj.common.ShortToStr(obj.agentCertificateAsn1.length) + obj.agentCertificateAsn1 + signature); // Command 2, certificate + signature + obj2.ws.send(obj2.common.ShortToStr(2) + obj2.common.ShortToStr(obj2.agentCertificateAsn1.length) + obj2.agentCertificateAsn1 + signature); // Command 2, certificate + signature }); break; @@ -257,9 +257,9 @@ module.exports.CreateMultiServer = function (parent, args) { obj.peernonce = msg.substring(50); // Perform the hash signature using the server agent certificate - obj.parent.parent.certificateOperations.acceleratorPerformSignature(0, msg.substring(2) + obj.nonce, function (signature) { + obj.parent.parent.certificateOperations.acceleratorPerformSignature(0, msg.substring(2) + obj.nonce, obj, function (signature) { // Send back our certificate + signature - obj.send(obj.common.ShortToStr(2) + obj.common.ShortToStr(obj.agentCertificateAsn1.length) + obj.agentCertificateAsn1 + signature); // Command 2, certificate + signature + obj2.send(obj2.common.ShortToStr(2) + obj.common.ShortToStr(obj2.agentCertificateAsn1.length) + obj2.agentCertificateAsn1 + signature); // Command 2, certificate + signature }); // Check the peer server signature if we can diff --git a/package.json b/package.json index f0192d06..4bf43c1b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "meshcentral", - "version": "0.1.9-i", + "version": "0.1.9-j", "keywords": [ "Remote Management", "Intel AMT", diff --git a/views/default-mobile.handlebars b/views/default-mobile.handlebars index e7df8570..332116f7 100644 --- a/views/default-mobile.handlebars +++ b/views/default-mobile.handlebars @@ -1177,6 +1177,10 @@ // MY DEVICES // + // Since the update device call can be quite frequent, we can moderate it and only call it at most 5 times a second. + var updateDevicesTimer = null; + function updateDevices() { if (updateDevicesTimer != null) return; updateDevicesTimer = setTimeout(updateDevicesEx, 200); } + var sort = 0; var deviceHeaderId = 0; var deviceHeaderCount; @@ -1185,7 +1189,7 @@ var deviceHeaderTotal = 0; var deviceHeaders = {}; var deviceHeadersTitles = {}; - function updateDevices() { + function updateDevicesEx() { var r = '', c = 0, current = null, count = 0, displayedMeshes = {}, groups = {}, groupCount = {}; // 3 wide, list view or desktop view @@ -1196,6 +1200,11 @@ deviceHeadersTitles = {}; var current; + // Perform node sort + if (sort == 0) { nodes.sort(meshSort); } + else if (sort == 1) { nodes.sort(powerSort); } + else if (sort == 2) { if (showRealNames == true) { nodes.sort(deviceHostSort); } else { nodes.sort(deviceSort); } } + // Go thru the list of nodes and display them for (var i in nodes) { if (nodes[i].v == false) continue; @@ -1324,10 +1333,7 @@ function onSortSelectChange(skipsave) { sort = document.getElementById("sortselect").selectedIndex; if (!skipsave) { putstore("sort", sort); } - if (sort == 0) { nodes.sort(meshSort); } - if (sort == 1) { nodes.sort(powerSort); } - if (sort == 2) { if (showRealNames == true) { nodes.sort(deviceHostSort); } else { nodes.sort(deviceSort); } } - updateDevices(); + updateDevicesEx(); } function deviceHeaderSet() { diff --git a/views/default.handlebars b/views/default.handlebars index ed4adb5e..dd616097 100644 --- a/views/default.handlebars +++ b/views/default.handlebars @@ -842,7 +842,6 @@ // Display the page devices onSortSelectChange(); onSearchInputChanged(); - updateDevices(); // Setup upload drag & drop Q('p5filetable').addEventListener("drop", p5fileDragDrop, false); @@ -1494,11 +1493,16 @@ if (Q('viewselect').value == 3) { if ((e.keyCode === 8 && mapSearchFocus == 0) || e.keyCode === 27) { return haltEvent(e); } } } + // Since the update device call can be quite frequent, we can moderate it and only call it at most 5 times a second. + var updateDevicesTimer = null; + function updateDevices() { if (updateDevicesTimer != null) return; updateDevicesTimer = setTimeout(updateDevicesEx, 200); } + var deviceHeaderId = 0; var deviceHeaderCount; var deviceHeaders = {}; var oldviewmode = 0; - function updateDevices() { + function updateDevicesEx() { + if (updateDevicesTimer != null) { clearTimeout(updateDevicesTimer); updateDevicesTimer = null; } var r = '', c = 0, current = null, count = 0, displayedMeshes = {}, view = Q('viewselect').value, groups = {}, groupCount = {}; QV('xdevices', view < 4); QV('xdevicesmap', view == 4); @@ -1522,9 +1526,14 @@ deviceHeadersTitles = {}; var kvmDivs = []; + // Perform node sort + if (sort == 0) { nodes.sort(meshSort); } + else if (sort == 1) { nodes.sort(powerSort); } + else if (sort == 2) { if (showRealNames == true) { nodes.sort(deviceHostSort); } else { nodes.sort(deviceSort); } } + // Save the list of currently checked nodeid's var checkedNodeids = [], elements = document.getElementsByClassName("DeviceCheckbox"), checkcount = 0; - for (var i in elements) { if (elements[i].checked) { checkedNodeids.push(elements[i].value); } } + for (var i=0;i'; - current = nodes[i].pwr; + current = pwr; c = 0; } } else if (sort == 2) { @@ -1670,7 +1680,7 @@ // Re-check nodeid's var elements = document.getElementsByClassName("DeviceCheckbox"), checkcount = 0; - for (var i in elements) { elements[i].checked = (checkedNodeids.indexOf(elements[i].value) >= 0); } + for (var i=0;i= 0); } for (var i in deviceHeaders) { QH(i, deviceHeaders[i]); } for (var i in deviceHeadersTitles) { Q(i).title = deviceHeadersTitles[i]; } @@ -1862,7 +1872,7 @@ // Called when OK is pressed on the Intel AMT scanning box function addAmtScanToMeshEx(button, meshid) { var elements = document.getElementsByClassName("DevScanCheckbox"), checkcount = 0; - for (var i in elements) { + for (var i=0;i 0); } @@ -2063,14 +2073,14 @@ function selectallButtonFunction() { var elements = document.getElementsByClassName("DeviceCheckbox"), checkcount = 0; - for (var i in elements) { if (elements[i].checked) checkcount++; } - for (var i in elements) { elements[i].checked = (checkcount == 0); } + for (var i=0;i 0) { QE('GroupActionButton', true); Q('SelectAllButton').value = 'Select None'; @@ -2093,7 +2103,7 @@ // Get the list of checked devices, removes any duplicates. function getCheckedDevices() { var nodeids = [], elements = document.getElementsByClassName("DeviceCheckbox"), checkcount = 0; - for (var i in elements) { if (elements[i].checked) { if (elements[i].value) { var nid = elements[i].value.substring(6); if (nodeids.indexOf(nid) == -1) { nodeids.push(nid); } } } } + for (var i=0;i b.meshnamel) return 1; if (a.meshnamel < b.meshnamel) return -1; if (a.meshid == b.meshid) { if (showRealNames == true) { if (a.rnamel > b.rnamel) return 1; if (a.rnamel < b.rnamel) return -1; return 0; } else { if (a.namel > b.namel) return 1; if (a.namel < b.namel) return -1; return 0; } } return 0; } - function powerSort(a, b) { var ap = a.pwr?a.pwr:0; var bp = b.pwr?b.pwr:0; if (ap == bp) { if (showRealNames == true) { if (a.rnamel > b.rnamel) return 1; if (a.rnamel < b.rnamel) return -1; return 0; } else { if (a.namel > b.namel) return 1; if (a.namel < b.namel) return -1; return 0; } } if (ap > bp) return 1; if (ap < bp) return -1; return 0; } + function powerSort(a, b) { var ap = a.pwr?a.pwr:0; var bp = b.pwr?b.pwr:0; if (ap > bp) return -1; if (ap < bp) return 1; if (ap == bp) { if (showRealNames == true) { if (a.rnamel > b.rnamel) return 1; if (a.rnamel < b.rnamel) return -1; return 0; } else { if (a.namel > b.namel) return 1; if (a.namel < b.namel) return -1; return 0; } } return 0; } function deviceSort(a, b) { if (a.namel > b.namel) return 1; if (a.namel < b.namel) return -1; return 0; } function deviceHostSort(a, b) { if (a.rnamel > b.rnamel) return 1; if (a.rnamel < b.rnamel) return -1; return 0; } function onSearchFocus(x) { searchFocus = x; } @@ -2200,8 +2207,8 @@ function cmmeshaction(action) { var meshid = contextelement.attributes.onclick.value.substring(32, (32 + 69)); var elements = document.getElementsByClassName("DeviceCheckbox"); - if (action == 1) { for (var i in elements) { if (elements[i].attributes && elements[i].attributes.class.value.substring(0, 69) == meshid) { elements[i].checked = true; } } } - if (action == 2) { for (var i in elements) { if (elements[i].attributes && elements[i].attributes.class.value.substring(0, 69) == meshid) { elements[i].checked = false; } } } + if (action == 1) { for (var i=0;i= 30 && x < 40); QS('UserGeneral').backgroundColor = ((x == 30) ? "#003366" : "#808080"); QS('UserEvents').backgroundColor = ((x == 31) ? "#003366" : "#808080"); - if (x == 1) updateDevices(); + if (x == 1) updateDevicesEx(); } // Generic methods diff --git a/webserver.js b/webserver.js index d12f8b2d..2a345f9c 100644 --- a/webserver.js +++ b/webserver.js @@ -71,6 +71,7 @@ module.exports.CreateWebServer = function (parent, db, args, secret, certificate obj.userAllowedIp = args.userallowedip; // List of allowed IP addresses for users obj.tlsSniCredentials; obj.dnsDomains = {}; + //obj.agentConnCount = 0; // Mesh Rights const MESHRIGHT_EDITMESH = 1; @@ -1695,7 +1696,16 @@ module.exports.CreateWebServer = function (parent, db, args, secret, certificate obj.app.ws(url + 'meshrelay.ashx', function (ws, req) { try { obj.meshRelayHandler.CreateMeshRelay(obj, ws, req, getDomain(req)); } catch (e) { console.log(e); } }); // Receive mesh agent connections - obj.app.ws(url + 'agent.ashx', function (ws, req) { try { obj.meshAgentHandler.CreateMeshAgent(obj, obj.db, ws, req, obj.args, getDomain(req)); } catch (e) { console.log(e); } }); + obj.app.ws(url + 'agent.ashx', function (ws, req) { + //console.log(++obj.agentConnCount); + /* + var ip, port, type; + if (req.connection) { ip = req.connection.remoteAddress; port = req.connection.remotePort; type = 1; } // HTTP(S) request + else if (req._socket) { ip = req._socket.remoteAddress; port = req._socket.remotePort; type = 2; } // WebSocket request + console.log('AgentConnect', ip, port, type); + */ + try { obj.meshAgentHandler.CreateMeshAgent(obj, obj.db, ws, req, obj.args, getDomain(req)); } catch (e) { console.log(e); } + }); obj.app.get(url + 'stop', function (req, res) { res.send('Stopping Server, click here to login.'); setTimeout(function () { parent.Stop(); }, 500); });