swarmserver.js 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434
  1. /**
  2. * @description MeshCentral v1 legacy Swarm Server, used to update agents and get them on MeshCentral2
  3. * @author Ylian Saint-Hilaire
  4. * @copyright Intel Corporation 2018-2022
  5. * @license Apache-2.0
  6. * @version v0.0.1
  7. */
  8. /*jslint node: true */
  9. /*jshint node: true */
  10. /*jshint strict:false */
  11. /*jshint -W097 */
  12. /*jshint esversion: 6 */
  13. "use strict";
  14. // Construct a legacy Swarm Server server object
  15. module.exports.CreateSwarmServer = function (parent, db, args, certificates) {
  16. var obj = {};
  17. obj.parent = parent;
  18. obj.db = db;
  19. obj.args = args;
  20. obj.certificates = certificates;
  21. obj.legacyAgentConnections = {};
  22. obj.migrationAgents = {};
  23. obj.agentActionCount = {};
  24. obj.stats = { blockedConnect: 0, connectCount: 0, clientCertConnectCount: 0, noCertConnectCount: 0, bytesIn: 0, bytesOut: 0, httpGetRequest: 0, pushedAgents: {}, close: 0, onclose: 0, agentType: {} }
  25. const tls = require('tls');
  26. const forge = require('node-forge');
  27. const common = require('./common.js');
  28. const LegacyMeshProtocol = {
  29. NODEPUSH: 1, // Used to send a node block to another peer.
  30. NODEPULL: 2, // Used to send a pull block to another peer.
  31. NODENOTIFY: 3, // Used to indicate the node ID to other peers.
  32. NODECHALLENGE: 4, // Used to challenge a node identity.
  33. NODECRESPONSE: 5, // Used to respond to a node challenge.
  34. TARGETSTATUS: 6, // Used to send the peer connection status list.
  35. LOCALEVENT: 7, // Used to send local events to subscribers.
  36. AESCRYPTO: 8, // Used to send an encrypted block of data.
  37. SESSIONKEY: 9, // Used to send a session key to a remote node.
  38. SYNCSTART: 10, // Used to send kick off the SYNC request, send the start NodeID.
  39. SYNCMETADATA: 11, // Used to send a sequence of NodeID & serial numbers.
  40. SYNCREQUEST: 12, // Used to send a sequence of NodeID's to request.
  41. NODEID: 13, // Used to send the NodeID in the clear. Used for multicast.
  42. AGENTID: 14, // Used to send the AgentID & version to the other node.
  43. PING: 15, // Used to query a target for the presence of the mesh agent (PB_NODEID response expected).
  44. SETUPADMIN: 16, // Used to set the trusted mesh identifier, this code can only be used from local settings file.
  45. POLICY: 17, // Used to send a policy block to another peer.
  46. POLICYSECRET: 18, // Used to encode the PKCS12 private key of a policy block.
  47. EVENTMASK: 19, // Used by the mesh service to change the event mask.
  48. RECONNECT: 20, // Used by the mesh service to indicate disconnect & reconnection after n seconds.
  49. GETSTATE: 21, // Used by the mesh service to obtain agent state.
  50. CERTENCRYPTED: 22, // Used to send a certificate encrypted message to a node.
  51. GETCOOKIE: 23, // Used to request a certificate encryption anti-replay cookie.
  52. COOKIE: 24, // Used to carry an anti-replay cookie to a requestor.
  53. SESSIONCKEY: 25, // Used to send a session key to a remote console.
  54. INTERFACE: 26, // Used to send a local interface blob to a management console.
  55. MULTICAST: 27, // Used by the mesh service to cause the agent to send a multicast.
  56. SELFEXE: 28, // Used to transfer our own agent executable.
  57. LEADERBADGE: 29, // User to send a leadership badge.
  58. NODEINFO: 30, // Used to indicate a block information update to the web service.
  59. TARGETEVENT: 31, // Used to send a single target update event.
  60. DEBUG: 33, // Used to send debug information to web service.
  61. TCPRELAY: 34, // Used to operate mesh leader TCP relay sockets
  62. CERTSIGNED: 35, // Used to send a certificate signed message to a node.
  63. ERRORCODE: 36, // Used to notify of an error.
  64. MESSAGE: 37, // Used to route messages between nodes.
  65. CMESSAGE: 38, // Used to embed a interface identifier along with a PB_MESSAGE.
  66. EMESSAGE: 39, // Used to embed a target encryption certificate along with a MESSAGE or CMESSAGE.
  67. SEARCH: 40, // Used to send a custom search to one or more remote nodes.
  68. MESSAGERELAY: 41, // Used by no-certificate consoles to send hopping messages to nodes.
  69. USERINPUT: 42, // Used to send user keyboard input to a target computer
  70. APPID: 43, // Used to send a block of data to a specific application identifier.
  71. APPSUBSCRIBE: 44, // Used to perform local app subscription to an agent.
  72. APPDIRECT: 45, // Used to send message directly to remote applications.
  73. APPREQACK: 46, // Used to request an ack message.
  74. APPACK: 47, // Used to ack a received message.
  75. SERVERECHO: 48, // Server will echo this message, used for testing.
  76. KVMINFO: 49, // Used to send local KVM secondary process information to mesh agent.
  77. REMOTEWAKE: 50, // Used to send remote wake information to server.
  78. NEWCONNECTTOKEN: 51, // Used to send a new connection token to the Swarm Server.
  79. WIFISCAN: 52, // Used to send visible WIFI AP's to the server.
  80. AMTPROVISIONING: 53, // Used by the agent to send Intel AMT provisioning information to the server.
  81. ANDROIDCOMMAND: 54, // Send a Android OS specific command (Android only).
  82. NODEAPPDATA: 55, // Used to send application specific data block to the server for storage.
  83. PROXY: 56, // Used to indicate the currently used proxy setting string.
  84. FILEOPERATION: 57, // Used to perform short file operations.
  85. APPSUBSCRIBERS: 58, // Used request and send to the mesh server the list of subscribed applications
  86. CUSTOM: 100, // Message containing application specific data.
  87. USERAUTH: 1000, // Authenticate a user to the swarm server.
  88. USERMESH: 1001, // Request or return the mesh list for this console.
  89. USERMESHS: 1002, // Send mesh overview information to the console.
  90. USERNODES: 1003, // Send node overview information to the console.
  91. JUSERMESHS: 1004, // Send mesh overview information to the console in JSON format.
  92. JUSERNODES: 1005, // Send node overview information to the console in JSON format.
  93. USERPOWERSTATE: 1006, // Used to send a power command from the console to the server.
  94. JMESHPOWERTIMELINE: 1007, // Send the power timeline for all nodes in a mesh.
  95. JMESHPOWERSUMMARY: 1008, // Send the power summary for sum of all nodes in a mesh.
  96. USERCOMMAND: 1009, // Send a user admin text command to and from the server.
  97. POWERBLOCK: 1010, // Request/Response of block of power state information.
  98. MESHACCESSCHANGE: 1011, // Notify a console of a change in accessible meshes.
  99. COOKIEAUTH: 1012, // Authenticate a user using a crypto cookie.
  100. NODESTATECHANGE: 1013, // Indicates a node has changed power state.
  101. JUSERNODE: 1014, // Send node overview information to the console in JSON format.
  102. AMTWSMANEVENT: 1015, // Intel AMT WSMAN event sent to consoles.
  103. ROUTINGCOOKIE: 1016, // Used by a console to request a routing cookie.
  104. JCOLLABORATION: 1017, // Request/send back JSON collaboration state.
  105. JRELATIONS: 1018, // Request/send back JSON relations state.
  106. SETCOLLABSTATE: 1019, // Set the collaboration state for this session.
  107. ADDRELATION: 1020, // Request that a new relation be added.
  108. DELETERELATION: 1021, // Request a relation be deleted.
  109. ACCEPTRELATION: 1022, // Request relation invitation be accepted.
  110. RELATIONCHANGEEVENT: 1023, // Notify that a relation has changed.
  111. COLLBCHANGEEVENT: 1024, // Notify that a collaboration state has change.
  112. MULTICONSOLEMESSAGE: 1025, // Send a message to one or more console id's.
  113. CONSOLEID: 1026, // Notify a console of it's console id.
  114. CHANGERELATIONDATA: 1027, // Request that relation data be changed.
  115. SETUSERDATA: 1028, // Set user data
  116. GETUSERDATA: 1029, // Get user data
  117. SERVERAUTH: 1030, // Used to verify the certificate of the server
  118. USERAUTH2: 1031, // Authenticate a user to the swarm server (Uses SHA1 SALT)
  119. GUESTREMOTEDESKTOP: 2001, // Guest usage: Remote Desktop
  120. GUESTWEBRTCMESH: 2002 // Guest usage: WebRTC Mesh
  121. };
  122. obj.server = tls.createServer({ key: certificates.swarmserver.key, cert: certificates.swarmserver.cert, requestCert: true, rejectUnauthorized: false }, onConnection);
  123. obj.server.listen(args.swarmport, function () { console.log('MeshCentral Legacy Swarm Server running on ' + certificates.CommonName + ':' + args.swarmport + '.'); obj.parent.updateServerState('swarm-port', args.swarmport); }).on('error', function (err) { console.error('ERROR: MeshCentral Swarm Server server port ' + args.swarmport + ' is not available.'); if (args.exactports) { process.exit(); } });
  124. loadMigrationAgents();
  125. // Load all migration agents along with full executable in memory
  126. function loadMigrationAgents() {
  127. var migrationAgentsDir = null, migrationAgentsPath = obj.parent.path.join(obj.parent.datapath, 'migrationagents');
  128. try { migrationAgentsDir = obj.parent.fs.readdirSync(migrationAgentsPath); } catch (e) { }
  129. if (migrationAgentsDir != null) {
  130. for (var i in migrationAgentsDir) {
  131. if (migrationAgentsDir[i].toLowerCase().startsWith('meshagent-')) {
  132. var migrationAgentName = obj.parent.path.join(migrationAgentsPath, migrationAgentsDir[i]);
  133. var agentInfo = migrationAgentsDir[i].substring(10).split('.');
  134. var agentVersion = parseInt(agentInfo[0]);
  135. var agentArch = parseInt(agentInfo[1]);
  136. if (obj.migrationAgents[agentArch] == null) { obj.migrationAgents[agentArch] = {}; }
  137. if (obj.migrationAgents[agentArch][agentVersion] == null) { obj.migrationAgents[agentArch][agentVersion] = { arch: agentArch, ver: agentVersion, path: migrationAgentName }; }
  138. }
  139. }
  140. }
  141. }
  142. function onData(data) {
  143. if (this.relaySocket) { var ps = this; try { this.relaySocket.write(data, 'binary', function () { ps.resume(); }); } catch (ex) { } return; }
  144. if (args.swarmdebug) { var buf = Buffer.from(data, "binary"); console.log('SWARM <-- (' + buf.length + '):' + buf.toString('hex')); } // Print out received bytes
  145. obj.stats.bytesIn += data.length;
  146. this.tag.accumulator += data;
  147. // Detect if this is an HTTPS request, if it is, return a simple answer and disconnect. This is useful for debugging access to the MPS port.
  148. if (this.tag.first == true) {
  149. if (this.tag.accumulator.length < 3) return;
  150. if ((this.tag.accumulator.substring(0, 3) == 'GET') || (this.tag.accumulator.substring(0, 3) == 'POS')) {
  151. obj.stats.httpGetRequest++;
  152. /*console.log("Swarm Connection, HTTP GET detected: " + socket.remoteAddress);*/
  153. //socket.write('HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nConnection: close\r\n\r\n<!DOCTYPE html><html><head><meta charset="UTF-8"></head><body>MeshCentral2 legacy swarm server.<br />MeshCentral1 mesh agents should connect here for updates.</body></html>');
  154. //socket.end();
  155. // Relay this connection to the main TLS port
  156. this.pause();
  157. var relaySocket = tls.connect(obj.args.port, { rejectUnauthorized: false }, function () { this.write(this.parentSocket.tag.accumulator); this.parentSocket.resume(); });
  158. relaySocket.on('data', function (data) { try { var rs = this; this.pause(); this.parentSocket.write(data, 'binary', function () { rs.resume(); }); } catch (ex) { } });
  159. relaySocket.on('error', function (err) { try { this.parentSocket.end(); } catch (ex) { } });
  160. relaySocket.on('end', function () { try { this.parentSocket.end(); } catch (ex) { } });
  161. this.relaySocket = relaySocket;
  162. relaySocket.parentSocket = this;
  163. return;
  164. }
  165. this.tag.first = false;
  166. }
  167. // A client certificate is required
  168. if ((this.tag.clientCert == null) || (this.tag.clientCert.subject == null)) {
  169. /*console.log("Swarm Connection, no client cert: " + socket.remoteAddress);*/
  170. this.write('HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\nConnection: close\r\n\r\nMeshCentral2 legacy swarm server.\r\nNo client certificate given.');
  171. //this.end(); // If we don't close the connection, it may lead to less reconnection traffic.
  172. return;
  173. }
  174. try {
  175. // Parse all of the agent binary command data we can
  176. var l = 0;
  177. do { l = ProcessCommand(this); if (l > 0) { this.tag.accumulator = this.tag.accumulator.substring(l); } } while (l > 0);
  178. if (l < 0) { this.end(); }
  179. } catch (e) {
  180. console.log(e);
  181. }
  182. }
  183. // Process one AFP command
  184. function ProcessCommand(socket) {
  185. if (socket.tag.accumulator.length < 4) return 0;
  186. var cmd = common.ReadShort(socket.tag.accumulator, 0);
  187. var len = common.ReadShort(socket.tag.accumulator, 2);
  188. if (len > socket.tag.accumulator.length) return 0;
  189. var data = socket.tag.accumulator.substring(4, len);
  190. //console.log('Swarm: Cmd=' + cmd + ', Len=' + len + '.');
  191. switch (cmd) {
  192. case LegacyMeshProtocol.NODEPUSH: {
  193. parent.debug('swarmcmd', 'NODEPUSH');
  194. var nodeblock = obj.decodeNodeBlock(data);
  195. if ((nodeblock != null) && (nodeblock.agenttype != null) && (nodeblock.agentversion != null)) {
  196. if (socket.pingTimer == null) { socket.pingTimer = setInterval(function () { obj.SendCommand(socket, LegacyMeshProtocol.PING); }, 20000); }
  197. parent.debug('swarmcmd', 'NODEPUSH:' + JSON.stringify(nodeblock));
  198. // Log the agent type
  199. if (obj.stats.agenttype[nodeblock.agenttype] == null) { obj.stats.agenttype[nodeblock.agenttype] = 1; } else { obj.stats.agenttype[nodeblock.agenttype]++; }
  200. // Check if this agent is asking of updates over and over again.
  201. var actionCount = obj.agentActionCount[nodeblock.nodeidhex];
  202. if (actionCount == null) { actionCount = 0; }
  203. if (actionCount > 2) {
  204. // Already tried to update this agent two times, something is not right.
  205. //console.log('SWARM: ' + actionCount + ' update actions on ' + nodeblock.nodeidhex + ', holding.');
  206. } else {
  207. // Figure out what is the next agent version we need.
  208. var nextAgentVersion = 0;
  209. if (nodeblock.agentversion < 201) { nextAgentVersion = 201; } // If less then 201, move to transitional MC1 agent.
  210. if (nodeblock.agentversion == 201) { nextAgentVersion = 202; } // If at 201, move to first MC2 agent.
  211. // See if we need to start the agent update
  212. if ((nextAgentVersion > 0) && (obj.migrationAgents[nodeblock.agenttype] != null) && (obj.migrationAgents[nodeblock.agenttype][nextAgentVersion] != null)) {
  213. // Start the update
  214. socket.tag.update = obj.migrationAgents[nodeblock.agenttype][nextAgentVersion];
  215. socket.tag.updatePtr = 0;
  216. //console.log('Performing legacy agent update from ' + nodeblock.agentversion + '.' + nodeblock.agenttype + ' to ' + socket.tag.update.ver + '.' + socket.tag.update.arch + ' on ' + nodeblock.agentname + '.');
  217. // Update stats
  218. if (obj.stats.pushedAgents[nodeblock.agenttype] == null) { obj.stats.pushedAgents[nodeblock.agenttype] = {}; }
  219. if (obj.stats.pushedAgents[nodeblock.agenttype][nextAgentVersion] == null) { obj.stats.pushedAgents[nodeblock.agenttype][nextAgentVersion] = 1; } else { obj.stats.pushedAgents[nodeblock.agenttype][nextAgentVersion]++; }
  220. // Start the agent download using the task limiter so not to flood the server. Low priority task
  221. obj.parent.taskLimiter.launch(function (socket, taskid, taskLimiterQueue) {
  222. if (socket.xclosed == 1) {
  223. // Socket is closed, do nothing
  224. obj.parent.taskLimiter.completed(taskid); // Indicate this task complete
  225. } else {
  226. // Start the agent update
  227. socket.tag.taskid = taskid;
  228. obj.SendCommand(socket, LegacyMeshProtocol.GETSTATE, common.IntToStr(5) + common.IntToStr(0)); // agent.SendQuery(5, 0); // Start the agent download
  229. }
  230. }, socket, 2);
  231. } else {
  232. //console.log('No legacy agent update for ' + nodeblock.agentversion + '.' + nodeblock.agenttype + ' on ' + nodeblock.agentname + '.');
  233. }
  234. }
  235. // Mark this agent
  236. obj.agentActionCount[nodeblock.nodeidhex] = ++actionCount;
  237. }
  238. break;
  239. }
  240. case LegacyMeshProtocol.AMTPROVISIONING: {
  241. parent.debug('swarmcmd', 'AMTPROVISIONING');
  242. obj.SendCommand(socket, LegacyMeshProtocol.AMTPROVISIONING, common.ShortToStr(1));
  243. break;
  244. }
  245. case LegacyMeshProtocol.GETSTATE: {
  246. parent.debug('swarmcmd', 'GETSTATE');
  247. if (len < 12) break;
  248. var statecmd = common.ReadInt(data, 0);
  249. //var statesync = common.ReadInt(data, 4);
  250. switch (statecmd) {
  251. case 6: { // Ask for agent block
  252. if (socket.tag.update != null) {
  253. // Send an agent block
  254. if (socket.tag.update.binary == null) { socket.tag.update.binary = obj.parent.fs.readFileSync(socket.tag.update.path); }
  255. var l = Math.min(socket.tag.update.binary.length - socket.tag.updatePtr, 16384);
  256. obj.SendCommand(socket, LegacyMeshProtocol.GETSTATE, common.IntToStr(6) + common.IntToStr(socket.tag.updatePtr) + socket.tag.update.binary.toString('binary', socket.tag.updatePtr, socket.tag.updatePtr + l)); // agent.SendQuery(6, AgentFileLen + AgentBlock);
  257. parent.debug('swarmcmd', 'Sending agent block, ptr = ' + socket.tag.updatePtr + ', len = ' + l);
  258. socket.tag.updatePtr += l;
  259. if (socket.tag.updatePtr >= socket.tag.update.binary.length) {
  260. // Send end-of-transfer
  261. obj.SendCommand(socket, LegacyMeshProtocol.GETSTATE, common.IntToStr(7) + common.IntToStr(socket.tag.update.binary.length)); //agent.SendQuery(7, AgentFileLen);
  262. parent.debug('swarmcmd', 'Sending end of agent, ptr = ' + socket.tag.updatePtr);
  263. obj.parent.taskLimiter.completed(socket.tag.taskid); // Indicate this task complete
  264. delete socket.tag.taskid;
  265. delete socket.tag.update;
  266. delete socket.tag.updatePtr;
  267. }
  268. }
  269. break;
  270. }
  271. default: {
  272. // All other state commands from the legacy agent must be ignored.
  273. break;
  274. }
  275. }
  276. break;
  277. }
  278. case LegacyMeshProtocol.APPSUBSCRIBERS: {
  279. parent.debug('swarmcmd', 'APPSUBSCRIBERS');
  280. break;
  281. }
  282. default: {
  283. parent.debug('swarmcmd', 'Unknown command: ' + cmd + ' of len ' + len + '.');
  284. }
  285. }
  286. return len;
  287. }
  288. // Called when a legacy agent connects to this server
  289. function onConnection(socket) {
  290. // Check for blocked IP address
  291. if (checkSwarmIpAddress(socket, obj.args.swarmallowedip) == false) { obj.stats.blockedConnect++; parent.debug('swarm', "New blocked agent connection"); return; }
  292. obj.stats.connectCount++;
  293. socket.tag = { first: true, clientCert: socket.getPeerCertificate(true), accumulator: "" };
  294. parent.debug('swarm', 'New legacy agent connection');
  295. if ((socket.tag.clientCert == null) || (socket.tag.clientCert.subject == null)) { obj.stats.noCertConnectCount++; } else { obj.stats.clientCertConnectCount++; }
  296. socket.addListener("data", onData);
  297. socket.addListener("close", function () {
  298. obj.stats.onclose++;
  299. parent.debug('swarm', 'Connection closed');
  300. // Perform aggressive cleanup
  301. if (this.relaySocket) { try { this.relaySocket.end(); this.relaySocket.removeAllListeners(["data", "end", "error"]); delete this.relaySocket; } catch (ex) { } }
  302. if (this.pingTimer != null) { clearInterval(this.pingTimer); delete this.pingTimer; }
  303. if (this.tag && (typeof this.tag.taskid == 'number')) {
  304. obj.parent.taskLimiter.completed(this.tag.taskid); // Indicate this task complete
  305. delete this.tag.taskid;
  306. }
  307. if (this.tag) {
  308. if (this.tag.accumulator) { delete this.tag.accumulator; }
  309. if (this.tag.clientCert) { delete this.tag.clientCert; }
  310. delete this.tag;
  311. }
  312. this.removeAllListeners([ "data", "close", "error" ]);
  313. });
  314. socket.addListener("error", function () {
  315. //console.log("Swarm Error: " + socket.remoteAddress);
  316. });
  317. }
  318. function getTagClass(data, tagClass, type) {
  319. if ((data == null) || (data.value == null)) return;
  320. for (var i in data.value) {
  321. //console.log(JSON.stringify(data.value[i]));
  322. if ((data.value[i].tagClass == tagClass) && (data.value[i].type == type)) {
  323. return data.value[i];
  324. }
  325. }
  326. }
  327. // Decode a node push block
  328. obj.decodeNodeBlock = function (data) {
  329. try {
  330. // Traverse the DER to get the raw data (Not sure if this works all the time)
  331. var info = {}, ptr = 68, der = forge.asn1.fromDer(forge.util.createBuffer(data, 'binary'));
  332. der = getTagClass(der, 128, 0);
  333. der = getTagClass(der, 0, 16);
  334. der = getTagClass(der, 0, 16);
  335. der = getTagClass(der, 128, 0);
  336. der = getTagClass(der, 0, 4);
  337. var binarydata = der.value;
  338. // Get the basic header values
  339. info.certhashhex = common.rstr2hex(binarydata.substring(0, 32)); // Hash of the complete mesh agent certificate
  340. info.nodeidhex = common.rstr2hex(binarydata.substring(32, 64)); // Old mesh agent nodeid
  341. info.serialNumber = common.ReadIntX(binarydata, 64); // Block serial number
  342. // Got thru the sub-blocks
  343. while (ptr < binarydata.length) {
  344. var btyp = common.ReadShort(binarydata, ptr), blen = common.ReadShort(binarydata, ptr + 2), bdata = binarydata.substring(ptr + 4, ptr + 4 + blen);
  345. switch (btyp) {
  346. case 1: { // PBST_COMPUTERINFO
  347. info.agenttype = common.ReadShortX(bdata, 0);
  348. info.agentbuild = common.ReadShortX(bdata, 2);
  349. info.agentversion = common.ReadIntX(bdata, 4);
  350. info.agentname = bdata.substring(8, 64 + 8);
  351. var xx = info.agentname.indexOf('\u0000');
  352. if (xx >= 0) { info.agentname = info.agentname.substring(0, xx); }
  353. info.agentosdesc = bdata.substring(64 + 8, 64 + 64 + 8);
  354. xx = info.agentosdesc.indexOf('\u0000');
  355. if (xx >= 0) { info.agentosdesc = info.agentosdesc.substring(0, xx); }
  356. return info;
  357. }
  358. default: {
  359. // All other commands from the legacy agent must be ignored.
  360. break;
  361. }
  362. }
  363. ptr += blen;
  364. }
  365. return info;
  366. } catch (e) { }
  367. return null;
  368. };
  369. // Disconnect legacy agent connection
  370. obj.close = function (socket) {
  371. obj.stats.close++;
  372. try { socket.close(); } catch (e) { }
  373. socket.xclosed = 1;
  374. };
  375. obj.SendCommand = function (socket, cmdid, data) {
  376. if (data == null) { data = ''; }
  377. Write(socket, common.ShortToStr(cmdid) + common.ShortToStr(data.length + 4) + data);
  378. };
  379. function Write(socket, data) {
  380. obj.stats.bytesOut += data.length;
  381. if (args.swarmdebug) {
  382. // Print out sent bytes
  383. var buf = Buffer.from(data, "binary");
  384. console.log('SWARM --> (' + buf.length + '):' + buf.toString('hex'));
  385. socket.write(buf);
  386. } else {
  387. socket.write(Buffer.from(data, "binary"));
  388. }
  389. }
  390. // Check if the source IP address is allowed for a given allowed list, return false if not
  391. function checkSwarmIpAddress(socket, allowedIpList) {
  392. if (allowedIpList == null) { return true; }
  393. try {
  394. var ip = socket.remoteAddress;
  395. if (ip) { for (var i = 0; i < allowedIpList.length; i++) { if (require('ipcheck').match(ip, allowedIpList[i])) { return true; } } }
  396. } catch (e) { console.log(e); }
  397. return false;
  398. }
  399. return obj;
  400. };