Added tasklimiter and improvements to agent meshcore update system.

Ylian Saint-Hilaire committed Jan 16, 2021 at 23:35 UTC cd84af5cb9acb1dea8f8e1590acf4a0a6cae51b7
3 files changed +81 -40
agents/meshcore.js
+12 -9
@@ -905,7 +905,7 @@ function createMeshCore(agent)
905 switch (data.action)
906 {
907 case 'agentupdate':
908 - agentUpdate_Start(data.url, { hash: data.hash, tlshash: data.servertlshash });
908 + agentUpdate_Start(data.url, { hash: data.hash, tlshash: data.servertlshash, sessionid: data.sessionid });
909 break;
910 case 'msg': {
911 switch (data.type)
@@ -3053,11 +3053,11 @@ function createMeshCore(agent)
3053 break;
3054 }
3055 case 'agentupdate':
3056 - require('MeshAgent').SendCommand({ action: 'agentupdate' });
3056 + require('MeshAgent').SendCommand({ action: 'agentupdate', sessionid: sessionid });
3057 break;
3058 case 'agentupdateex':
3059 // Perform an direct agent update without requesting any information from the server, this should not typically be used.
3060 - agentUpdate_Start(null, { session: sessionid });
3060 + agentUpdate_Start(null, { sessionid: sessionid });
3061 break;
3062 case 'msh':
3063 response = JSON.stringify(_MSH(), null, 2);
@@ -4343,7 +4343,7 @@ function createMeshCore(agent)
4343 function agentUpdate_Start(updateurl, updateoptions)
4344 {
4345 // If this value is null
4346 - var sessionid = updateoptions != null ? updateoptions.session : null; // If this is null, messages will be broadcast. Otherwise they will be unicasted
4346 + var sessionid = (updateoptions != null) ? updateoptions.sessionid : null; // If this is null, messages will be broadcast. Otherwise they will be unicasted
4347
4348 if (this._selfupdate != null)
4349 {
@@ -4380,7 +4380,7 @@ function createMeshCore(agent)
4380 return;
4381 }
4382
4383 - sendConsoleText('Downloading update...', sessionid);
4383 + if (sessionid != null) { sendConsoleText('Downloading update...', sessionid); }
4384 var options = require('http').parseUri(updateurl != null ? updateurl : require('MeshAgent').ServerUrl);
4385 options.protocol = 'https:';
4386 if (updateurl == null) { options.path = ('/meshagents?id=' + require('MeshAgent').ARCHID); }
@@ -4422,7 +4422,7 @@ function createMeshCore(agent)
4422 {
4423 if (updateoptions.hash.toLowerCase() == h.toString('hex').toLowerCase())
4424 {
4425 - sendConsoleText('Download complete. HASH verified.', sessionid);
4425 + if (sessionid != null) { sendConsoleText('Download complete. HASH verified.', sessionid); }
4426 }
4427 else
4428 {
@@ -4433,10 +4433,13 @@ function createMeshCore(agent)
4433 }
4434 else
4435 {
4436 - sendConsoleText('Download complete. HASH=' + h.toString('hex'), sessionid);
4436 + if (sessionid != null) { sendConsoleText('Download complete. HASH=' + h.toString('hex'), sessionid); }
4437 }
4438
4439 - sendConsoleText('Updating and restarting agent...', sessionid);
4439 + // Send an indication to the server that we got the update download correctly.
4440 + try { mesh.SendCommand({ action: 'agentupdatedownloaded' }); } catch (e) { }
4441 +
4442 + if (sessionid != null) { sendConsoleText('Updating and restarting agent...', sessionid); }
4443 if (process.platform == 'win32')
4444 {
4445 // Use _wexecve() equivalent to perform the update
@@ -4458,7 +4461,7 @@ function createMeshCore(agent)
4461 m |= (require('fs').CHMOD_MODES.S_IXUSR | require('fs').CHMOD_MODES.S_IXGRP | require('fs').CHMOD_MODES.S_IXOTH);
4462 require('fs').chmodSync(process.execPath, m);
4463
4461 - sendConsoleText('Restarting service...', sessionid);
4464 + if (sessionid != null) { sendConsoleText('Restarting service...', sessionid); }
4465 try
4466 {
4467 // restart service
agents/recoverycore.js
+11 -8
@@ -86,7 +86,7 @@ function windows_execve(name, agentfilename, sessionid)
86 function agentUpdate_Start(updateurl, updateoptions)
87 {
88 // If this value is null
89 - var sessionid = updateoptions != null ? updateoptions.session : null; // If this is null, messages will be broadcast. Otherwise they will be unicasted
89 + var sessionid = (updateoptions != null) ? updateoptions.sessionid : null; // If this is null, messages will be broadcast. Otherwise they will be unicasted
90
91 if (this._selfupdate != null)
92 {
@@ -123,7 +123,7 @@ function agentUpdate_Start(updateurl, updateoptions)
123 return;
124 }
125
126 - sendConsoleText('Downloading update...', sessionid);
126 + if (sessionid != null) { sendConsoleText('Downloading update...', sessionid); }
127 var options = require('http').parseUri(updateurl != null ? updateurl : require('MeshAgent').ServerUrl);
128 options.protocol = 'https:';
129 if (updateurl == null) { options.path = ('/meshagents?id=' + require('MeshAgent').ARCHID); }
@@ -165,7 +165,7 @@ function agentUpdate_Start(updateurl, updateoptions)
165 {
166 if (updateoptions.hash.toLowerCase() == h.toString('hex').toLowerCase())
167 {
168 - sendConsoleText('Download complete. HASH verified.', sessionid);
168 + if (sessionid != null) { sendConsoleText('Download complete. HASH verified.', sessionid); }
169 }
170 else
171 {
@@ -179,7 +179,10 @@ function agentUpdate_Start(updateurl, updateoptions)
179 sendConsoleText('Download complete. HASH=' + h.toString('hex'), sessionid);
180 }
181
182 - sendConsoleText('Updating and restarting agent...', sessionid);
182 + // Send an indication to the server that we got the update download correctly.
183 + try { mesh.SendCommand({ action: 'agentupdatedownloaded' }); } catch (e) { }
184 +
185 + if (sessionid != null) { sendConsoleText('Updating and restarting agent...', sessionid); }
186 if (process.platform == 'win32')
187 {
188 // Use _wexecve() equivalent to perform the update
@@ -201,7 +204,7 @@ function agentUpdate_Start(updateurl, updateoptions)
204 m |= (require('fs').CHMOD_MODES.S_IXUSR | require('fs').CHMOD_MODES.S_IXGRP | require('fs').CHMOD_MODES.S_IXOTH);
205 require('fs').chmodSync(process.execPath, m);
206
204 - sendConsoleText('Restarting service...', sessionid);
207 + if (sessionid != null) { sendConsoleText('Restarting service...', sessionid); }
208 try
209 {
210 // restart service
@@ -406,7 +409,7 @@ require('MeshAgent').AddCommandHandler(function (data)
409 switch (data.action)
410 {
411 case 'agentupdate':
409 - agentUpdate_Start(data.url, { hash: data.hash, tlshash: data.servertlshash });
412 + agentUpdate_Start(data.url, { hash: data.hash, tlshash: data.servertlshash, sessionid: data.sessionid });
413 break;
414 case 'msg':
415 {
@@ -662,11 +665,11 @@ function processConsoleCommand(cmd, args, rights, sessionid) {
665 break;
666 case 'agentupdate':
667 // Request that the server send a agent update command
665 - require('MeshAgent').SendCommand({ action: 'agentupdate' });
668 + require('MeshAgent').SendCommand({ action: 'agentupdate', sessionid: sessionid });
669 break;
670 case 'agentupdateex':
671 // Perform an direct agent update without requesting any information from the server, this should not typically be used.
669 - agentUpdate_Start(null, { session: sessionid });
672 + agentUpdate_Start(null, { sessionid: sessionid });
673 break;
674 case 'osinfo': { // Return the operating system information
675 var i = 1;
meshagent.js
+58 -23
@@ -89,7 +89,7 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) {
89 // Set this agent as no longer authenticated
90 obj.authenticated = -1;
91
92 - // If we where updating the agent, clean that up.
92 + // If we where updating the agent using native method, clean that up.
93 if (obj.agentUpdate != null) {
94 if (obj.agentUpdate.fd) { try { parent.fs.close(obj.agentUpdate.fd); } catch (ex) { } }
95 parent.parent.taskLimiter.completed(obj.agentUpdate.taskid); // Indicate this task complete
@@ -97,6 +97,12 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) {
97 delete obj.agentUpdate;
98 }
99
100 + // If we where updating the agent meshcore method, clean that up.
101 + if (obj.agentCoreUpdateTaskId != null) {
102 + parent.parent.taskLimiter.completed(obj.agentCoreUpdateTaskId);
103 + delete obj.agentCoreUpdateTaskId;
104 + }
105 +
106 // Perform timer cleanup
107 if (obj.pingtimer) { clearInterval(obj.pingtimer); delete obj.pingtimer; }
108 if (obj.pongtimer) { clearInterval(obj.pongtimer); delete obj.pongtimer; }
@@ -238,7 +244,6 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) {
244 if (agentUpdateMethod === 2) { // Use meshcore agent update system
245 // Send the recovery core to the agent, if the agent is capable of running one
246 if (((obj.agentInfo.capabilities & 16) != 0) && (parent.parent.meshAgentsArchitectureNumbers[obj.agentInfo.agentId].core != null)) {
241 - //obj.agentCoreCheck = 1001;
247 obj.agentCoreUpdate = true;
248 obj.sendBinary(common.ShortToStr(11) + common.ShortToStr(0));
249 }
@@ -1152,17 +1157,26 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) {
1157 {
1158 if ((obj.agentCoreUpdate === true) && (obj.agentExeInfo != null)) {
1159 // Agent update. The recovery core was loaded in the agent, send a command to update the agent
1155 - var cmd = { action: 'agentupdate', url: obj.agentExeInfo.url, hash: obj.agentExeInfo.hashhex };
1156 - // Add the hash
1157 - if (obj.agentExeInfo.fileHash != null) { cmd.hash = obj.agentExeInfo.fileHashHex; } else { cmd.hash = obj.agentExeInfo.hashhex; }
1158 - // Add server TLS cert hash
1159 - if (parent.parent.args.ignoreagenthashcheck !== true) {
1160 - const tlsCertHash = parent.webCertificateFullHashs[domain.id];
1161 - if (tlsCertHash != null) { cmd.servertlshash = Buffer.from(tlsCertHash, 'binary').toString('hex'); }
1162 - }
1163 - // Send the agent update command
1164 - obj.send(JSON.stringify(cmd));
1165 - //delete obj.agentCoreUpdate;
1160 + parent.parent.taskLimiter.launch(function (argument, taskid, taskLimiterQueue) { // Medium priority task
1161 + // If agent disconnection, complete and exit now.
1162 + if (obj.authenticated != 2) { parent.parent.taskLimiter.completed(taskid); return; }
1163 +
1164 + // Agent update. The recovery core was loaded in the agent, send a command to update the agent
1165 + obj.agentCoreUpdateTaskId = taskid;
1166 + var cmd = { action: 'agentupdate', url: obj.agentExeInfo.url, hash: obj.agentExeInfo.hashhex };
1167 +
1168 + // Add the hash
1169 + if (obj.agentExeInfo.fileHash != null) { cmd.hash = obj.agentExeInfo.fileHashHex; } else { cmd.hash = obj.agentExeInfo.hashhex; }
1170 +
1171 + // Add server TLS cert hash
1172 + if (parent.parent.args.ignoreagenthashcheck !== true) {
1173 + const tlsCertHash = parent.webCertificateFullHashs[domain.id];
1174 + if (tlsCertHash != null) { cmd.servertlshash = Buffer.from(tlsCertHash, 'binary').toString('hex'); }
1175 + }
1176 +
1177 + // Send the agent update command
1178 + obj.send(JSON.stringify(cmd));
1179 + }, null, 1);
1180 } else {
1181 // Sent by the agent to update agent information
1182 ChangeAgentCoreInfo(command);
@@ -1456,17 +1470,38 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) {
1470 break;
1471 }
1472 case 'agentupdate': {
1459 - // Agent is requesting an agent update
1460 - var cmd = { action: 'agentupdate', url: obj.agentExeInfo.url, hash: obj.agentExeInfo.hashhex };
1461 - // Add the hash
1462 - if (obj.agentExeInfo.fileHash != null) { cmd.hash = obj.agentExeInfo.fileHashHex; } else { cmd.hash = obj.agentExeInfo.hashhex; }
1463 - // Add server TLS cert hash
1464 - if (parent.parent.args.ignoreagenthashcheck !== true) {
1465 - const tlsCertHash = parent.webCertificateFullHashs[domain.id];
1466 - if (tlsCertHash != null) { cmd.servertlshash = Buffer.from(tlsCertHash, 'binary').toString('hex'); }
1473 + var func = function agentUpdateFunc(argument, taskid, taskLimiterQueue) { // Medium priority task
1474 + // If agent disconnection, complete and exit now.
1475 + if (obj.authenticated != 2) { parent.parent.taskLimiter.completed(taskid); return; }
1476 +
1477 + // Agent is requesting an agent update
1478 + obj.agentCoreUpdateTaskId = taskid;
1479 + var cmd = { action: 'agentupdate', url: obj.agentExeInfo.url, hash: obj.agentExeInfo.hashhex, sessionid: agentUpdateFunc.sessionid };
1480 +
1481 + // Add the hash
1482 + if (obj.agentExeInfo.fileHash != null) { cmd.hash = obj.agentExeInfo.fileHashHex; } else { cmd.hash = obj.agentExeInfo.hashhex; }
1483 +
1484 + // Add server TLS cert hash
1485 + if (parent.parent.args.ignoreagenthashcheck !== true) {
1486 + const tlsCertHash = parent.webCertificateFullHashs[domain.id];
1487 + if (tlsCertHash != null) { cmd.servertlshash = Buffer.from(tlsCertHash, 'binary').toString('hex'); }
1488 + }
1489 +
1490 + // Send the agent update command
1491 + obj.send(JSON.stringify(cmd));
1492 + }
1493 + func.sessionid = command.sessionid;
1494 +
1495 + // Agent update. The recovery core was loaded in the agent, send a command to update the agent
1496 + parent.parent.taskLimiter.launch(func, null, 1);
1497 + break;
1498 + }
1499 + case 'agentupdatedownloaded': {
1500 + if (obj.agentCoreUpdateTaskId != null) {
1501 + // Indicate this udpate task is complete
1502 + parent.parent.taskLimiter.completed(obj.agentCoreUpdateTaskId);
1503 + delete obj.agentCoreUpdateTaskId;
1504 }
1468 - // Send the agent update command
1469 - obj.send(JSON.stringify(cmd));
1505 break;
1506 }
1507 default: {