| 1 | /** |
| 2 | * @description MeshCentral Intel(R) AMT Local Scanner |
| 3 | * @author Ylian Saint-Hilaire & Joko Sastriawan |
| 4 | * @copyright Intel Corporation 2018-2022 |
| 5 | * @license Apache-2.0 |
| 6 | * @version v0.0.1 |
| 7 | */ |
| 8 | |
| 9 | /*jslint node: true */ |
| 10 | /*jshint node: true */ |
| 11 | /*jshint strict:false */ |
| 12 | /*jshint -W097 */ |
| 13 | /*jshint esversion: 6 */ |
| 14 | "use strict"; |
| 15 | |
| 16 | // Construct a Intel AMT Scanner object |
| 17 | module.exports.CreateAmtScanner = function (parent) { |
| 18 | var obj = {}; |
| 19 | obj.active = false; |
| 20 | obj.parent = parent; |
| 21 | obj.net = require('net'); |
| 22 | obj.tls = require('tls'); |
| 23 | obj.dns = require('dns'); |
| 24 | obj.dgram = require('dgram'); |
| 25 | obj.common = require('./common.js'); |
| 26 | obj.servers = {}; |
| 27 | obj.rserver = {}; |
| 28 | obj.rpacket = null; |
| 29 | obj.tagToId = {}; // Tag --> { lastpong: time, id: NodeId } |
| 30 | obj.scanTable = {}; // NodeId --> ScanInfo : { lastping: time, lastpong: time, nodeinfo:{node} } |
| 31 | obj.scanTableTags = {}; // Tag --> ScanInfo |
| 32 | obj.pendingSends = []; // We was to stagger the sends using a 10ms timer |
| 33 | obj.pendingSendTimer = null; |
| 34 | obj.mainTimer = null; |
| 35 | obj.nextTag = 0; |
| 36 | const PeriodicScanTime = 30000; // Interval between scan sweeps |
| 37 | const PeriodicScanTimeout = 65000; // After this time, timeout the device. |
| 38 | const constants = (require('crypto').constants ? require('crypto').constants : require('constants')); // require('constants') is deprecated in Node 11.10, use require('crypto').constants instead. |
| 39 | |
| 40 | // Build a RMCP packet with a given tag field |
| 41 | obj.buildRmcpPing = function (tag) { |
| 42 | var packet = Buffer.from(obj.common.hex2rstr('06000006000011BE80000000'), 'ascii'); |
| 43 | packet[9] = tag; |
| 44 | return packet; |
| 45 | }; |
| 46 | |
| 47 | // Start scanning for local network Intel AMT computers |
| 48 | obj.start = function () { |
| 49 | obj.active = true; |
| 50 | obj.performScan(); |
| 51 | obj.mainTimer = setInterval(obj.performScan, PeriodicScanTime); |
| 52 | return obj; |
| 53 | }; |
| 54 | |
| 55 | // Stop scanning for local network Intel AMT computers |
| 56 | obj.stop = function () { |
| 57 | obj.active = false; |
| 58 | for (var i in obj.servers) { obj.servers[i].close(); } // Stop all servers |
| 59 | obj.servers = {}; |
| 60 | if (obj.mainTimer != null) { clearInterval(obj.mainTimer); obj.mainTimer = null; } |
| 61 | }; |
| 62 | |
| 63 | // Scan for Intel AMT computers using network multicast |
| 64 | obj.performRangeScan = function (userid, rangestr) { |
| 65 | if (obj.rpacket == null) { obj.rpacket = obj.buildRmcpPing(0); } |
| 66 | var range = obj.parseIpv4Range(rangestr); |
| 67 | //console.log(obj.IPv4NumToStr(range.min), obj.IPv4NumToStr(range.max)); |
| 68 | if (range == null || (range.min > range.max)) return false; |
| 69 | var rangeinfo = { id: userid, range: rangestr, min: range.min, max: range.max, results: {} }; |
| 70 | obj.rserver[userid] = rangeinfo; |
| 71 | rangeinfo.server = obj.dgram.createSocket("udp4"); |
| 72 | rangeinfo.server.bind(0); |
| 73 | rangeinfo.server.on('error', (err) => { console.log(err); }); |
| 74 | rangeinfo.server.on('message', function (data, rinfo) { obj.parseRmcpPacket(data, rinfo, 0, obj.reportMachineState, rangeinfo); }); |
| 75 | rangeinfo.server.on('listening', function() { for (var i = rangeinfo.min; i <= rangeinfo.max; i++) { rangeinfo.server.send(obj.rpacket, 623, obj.IPv4NumToStr(i)); } }); |
| 76 | rangeinfo.timer = setTimeout(function () { // ************************* USE OF OUTER VARS!!!!!!!!!!!!!!! |
| 77 | obj.parent.DispatchEvent(['*', userid], obj, { action: 'scanamtdevice', range: rangeinfo.range, results: rangeinfo.results, nolog: 1 }); |
| 78 | rangeinfo.server.close(); |
| 79 | delete rangeinfo.server; |
| 80 | }, 3000); |
| 81 | return true; |
| 82 | }; |
| 83 | |
| 84 | // Parse range, used to parse "ip", "ip/mask" or "ip-ip" notation. |
| 85 | // Return the start and end value of the scan |
| 86 | obj.parseIpv4Range = function (range) { |
| 87 | if (range == undefined || range == null) return null; |
| 88 | var x = range.split('-'); |
| 89 | if (x.length == 2) { return { min: obj.parseIpv4Addr(x[0]), max: obj.parseIpv4Addr(x[1]) }; } |
| 90 | x = range.split('/'); |
| 91 | if (x.length == 2) { |
| 92 | var ip = obj.parseIpv4Addr(x[0]), masknum = parseInt(x[1]), mask = 0; |
| 93 | if (masknum <= 16 || masknum > 32) return null; |
| 94 | masknum = 32 - masknum; |
| 95 | for (var i = 0; i < masknum; i++) { mask = (mask << 1); mask++; } |
| 96 | return { min: ip & (0xFFFFFFFF - mask), max: (ip & (0xFFFFFFFF - mask)) + mask }; |
| 97 | } |
| 98 | x = obj.parseIpv4Addr(range); |
| 99 | if (x == null) return null; |
| 100 | return { min: x, max: x }; |
| 101 | }; |
| 102 | |
| 103 | // Parse IP address. Takes a |
| 104 | obj.parseIpv4Addr = function (addr) { |
| 105 | var x = addr.split('.'); |
| 106 | if (x.length == 4) { return (parseInt(x[0]) << 24) + (parseInt(x[1]) << 16) + (parseInt(x[2]) << 8) + (parseInt(x[3]) << 0); } |
| 107 | return null; |
| 108 | }; |
| 109 | |
| 110 | // IP address number to string |
| 111 | obj.IPv4NumToStr = function (num) { |
| 112 | return ((num >> 24) & 0xFF) + '.' + ((num >> 16) & 0xFF) + '.' + ((num >> 8) & 0xFF) + '.' + (num & 0xFF); |
| 113 | }; |
| 114 | |
| 115 | /* |
| 116 | // Sample we could use to optimize DNS resolving, may not be needed at all. |
| 117 | obj.BatchResolvePendingMax = 1; |
| 118 | obj.BatchResolvePendingCount = 0; |
| 119 | obj.BatchResolveResults = {}; |
| 120 | obj.BatchResolve = function (hostname) { |
| 121 | var r = null; |
| 122 | hostname = hostname.toLowerCase(); |
| 123 | if ((hostname == '127.0.0.1') || (hostname == '::1') || (hostname == 'localhost')) return null; // Don't scan localhost |
| 124 | if (obj.net.isIP(hostname) > 0) return hostname; // This is an IP address, already resolved. |
| 125 | if (obj.BatchResolveResults[hostname]) { |
| 126 | if ((obj.BatchResolveResults[hostname].f == 0) || (obj.BatchResolveResults[hostname].f == -1)) { |
| 127 | // Already resolving this one or an error occured during resolve, re-check every 30 minutes. |
| 128 | if (((Date.now() - obj.BatchResolveResults[hostname].t) < 1800000) || (obj.BatchResolvePendingCount >= obj.BatchResolvePendingMax)) { return null; } |
| 129 | } else { |
| 130 | // We are to try to re-resolve every 30 minutes |
| 131 | if (((Date.now() - obj.BatchResolveResults[hostname].t) < 1800000) || (obj.BatchResolvePendingCount >= obj.BatchResolvePendingMax)) { return obj.BatchResolveResults[hostname].a; } |
| 132 | r = obj.BatchResolveResults[hostname].a; |
| 133 | } |
| 134 | } |
| 135 | if (obj.BatchResolvePendingCount >= obj.BatchResolvePendingMax) return null; // Don't resolve more than 10 names at any given time. |
| 136 | console.log('Resolve: ' + hostname); |
| 137 | obj.BatchResolvePendingCount++; |
| 138 | obj.BatchResolveResults[hostname] = { f: 0, t: Date.now() }; // Mark are resolving |
| 139 | obj.dns.lookup(hostname, (err, address, family) => { |
| 140 | obj.BatchResolvePendingCount--; |
| 141 | if (err != null) { |
| 142 | console.log('Resolve error: ' + hostname); |
| 143 | obj.BatchResolveResults[hostname] = { f: -1 }; // Mark this as a resolve error |
| 144 | } else { |
| 145 | console.log('Resolved: %s = %j, family: IPv%s', hostname, address, family); |
| 146 | obj.BatchResolveResults[hostname] = { a: address, f: family, t: Date.now() }; |
| 147 | } |
| 148 | }); |
| 149 | return r; |
| 150 | }; |
| 151 | */ |
| 152 | |
| 153 | obj.ResolveName = function (hostname, func) { |
| 154 | if ((hostname == '127.0.0.1') || (hostname == '::1') || (hostname == 'localhost')) { func(hostname, null); } // Don't scan localhost |
| 155 | if (obj.net.isIP(hostname) > 0) { func(hostname, hostname); return; } // This is an IP address, already resolved. |
| 156 | obj.dns.lookup(hostname, function (err, address, family) { if (err == null) { func(hostname, address); } else { func(hostname, null); } }); |
| 157 | }; |
| 158 | |
| 159 | // Look for all Intel AMT computers that may be locally reachable and poll their presence |
| 160 | obj.performScan = function () { |
| 161 | if (obj.active == false) { return false; } |
| 162 | obj.parent.db.getLocalAmtNodes(function (err, docs) { // TODO: handler more than 10 computer scan at the same time. DNS resolved may need to be a seperate module. |
| 163 | for (var i in obj.scanTable) { obj.scanTable[i].present = false; } |
| 164 | if (err == null && docs.length > 0) { |
| 165 | for (var i in docs) { |
| 166 | var doc = docs[i], host = doc.host.toLowerCase(); |
| 167 | const ciraConnections = obj.parent.mpsserver ? obj.parent.mpsserver.GetConnectionToNode(doc._id, null, true) : null; // See if any OOB connections are present |
| 168 | if ((host != '127.0.0.1') && (host != '::1') && (host.toLowerCase() != 'localhost') && (ciraConnections == null)) { |
| 169 | var scaninfo = obj.scanTable[doc._id]; |
| 170 | if (scaninfo == null) { |
| 171 | var tag = obj.nextTag++; |
| 172 | obj.scanTableTags[tag] = obj.scanTable[doc._id] = scaninfo = { nodeinfo: doc, present: true, tag: tag, state: 0 }; |
| 173 | //console.log('Scan ' + host + ', state=' + scaninfo.state + ', delta=' + delta); |
| 174 | } else { |
| 175 | scaninfo.present = true; |
| 176 | var delta = Date.now() - scaninfo.lastpong; |
| 177 | //console.log('Rescan ' + host + ', state=' + scaninfo.state + ', delta=' + delta); |
| 178 | if ((scaninfo.state == 1) && (delta >= PeriodicScanTimeout)) { |
| 179 | // More than 2 minutes without a response, mark the node as unknown state |
| 180 | scaninfo.state = 0; |
| 181 | obj.parent.ClearConnectivityState(scaninfo.nodeinfo.meshid, scaninfo.nodeinfo._id, 4, null, { name: doc.name }); // Clear connectivity state |
| 182 | if (obj.parent.amtManager != null) { obj.parent.amtManager.stopAmtManagement(scaninfo.nodeinfo._id, 3, scaninfo.nodeinfo.host); } |
| 183 | } else if ((scaninfo.tcp == null) && ((scaninfo.state == 0) || isNaN(delta) || (delta > PeriodicScanTime))) { |
| 184 | // More than 30 seconds without a response, try TCP detection |
| 185 | obj.checkTcpPresence(host, (doc.intelamt.tls == 1) ? 16993 : 16992, scaninfo, function (tag, result, version) { |
| 186 | // TODO: It is bad that "obj" is being accessed within this function. |
| 187 | if (result == false) return; |
| 188 | tag.lastpong = Date.now(); |
| 189 | if (tag.state == 0) { |
| 190 | tag.state = 1; |
| 191 | obj.parent.SetConnectivityState(tag.nodeinfo.meshid, tag.nodeinfo._id, tag.lastpong, 4, 7, null, { name: doc.name }); // Report power state as "present" (7). |
| 192 | if (version != null) { obj.changeAmtState(tag.nodeinfo._id, version, 2, tag.nodeinfo.intelamt.tls); } |
| 193 | if (obj.parent.amtManager != null) { obj.parent.amtManager.startAmtManagement(tag.nodeinfo._id, 3, tag.nodeinfo.host); } |
| 194 | } |
| 195 | }); |
| 196 | } |
| 197 | } |
| 198 | // Start scanning this node |
| 199 | scaninfo.lastping = Date.now(); |
| 200 | obj.checkAmtPresence(host, scaninfo.tag); |
| 201 | } |
| 202 | } |
| 203 | } |
| 204 | for (var i in obj.scanTable) { |
| 205 | if (obj.scanTable[i].present == false) { |
| 206 | // Stop scanning this node |
| 207 | delete obj.scanTableTags[obj.scanTable[i].tag]; |
| 208 | delete obj.scanTable[i]; |
| 209 | } |
| 210 | } |
| 211 | }); |
| 212 | return true; |
| 213 | }; |
| 214 | |
| 215 | // Look for all Intel AMT computers that may be locally reachable and poll their presence |
| 216 | obj.performSpecificScan = function (node) { |
| 217 | if ((node == null) || (node.host == null)) return; |
| 218 | var host = node.host.toLowerCase(); |
| 219 | const ciraConnections = obj.parent.mpsserver ? obj.parent.mpsserver.GetConnectionToNode(node._id, null, true) : null; // See if any OOB connections are present |
| 220 | if ((host != '127.0.0.1') && (host != '::1') && (host.toLowerCase() != 'localhost') && (ciraConnections == null)) { |
| 221 | obj.checkTcpPresence(host, (node.intelamt.tls == 1) ? 16993 : 16992, { nodeinfo: node }, function (tag, result, version) { |
| 222 | if ((result == true) && (obj.parent.amtManager != null)) { obj.parent.amtManager.startAmtManagement(tag.nodeinfo._id, 3, tag.nodeinfo.host); } |
| 223 | }); |
| 224 | } |
| 225 | }; |
| 226 | |
| 227 | // Check the presense of a specific Intel AMT computer using RMCP |
| 228 | obj.checkAmtPresence = function (host, tag) { obj.ResolveName(host, function (hostname, ip) { obj.checkAmtPresenceEx(ip, tag); }); }; |
| 229 | |
| 230 | // Check the presense of a specific Intel AMT computer using RMCP |
| 231 | obj.checkAmtPresenceEx = function (host, tag) { |
| 232 | if (host == null) return; |
| 233 | var serverid = Math.floor(tag / 255); |
| 234 | var servertag = (tag % 255); |
| 235 | var packet = obj.buildRmcpPing(servertag); |
| 236 | var server = obj.servers[serverid]; |
| 237 | if (server == undefined) { |
| 238 | // Start new server |
| 239 | server = obj.dgram.createSocket('udp4'); |
| 240 | server.on('error', (err) => { }); |
| 241 | server.on('message', (data, rinfo) => { obj.parseRmcpPacket(data, rinfo, serverid, obj.changeConnectState, null); }); |
| 242 | server.on('listening', () => { |
| 243 | obj.pendingSends.push([server, packet, host]); |
| 244 | if (obj.pendingSendTimer == null) { obj.pendingSendTimer = setInterval(obj.sendPendingPacket, 10); } |
| 245 | }); |
| 246 | server.bind(0); |
| 247 | obj.servers[serverid] = server; |
| 248 | } else { |
| 249 | // Use existing server |
| 250 | obj.pendingSends.push([server, packet, host]); |
| 251 | if (obj.pendingSendTimer == null) { obj.pendingSendTimer = setInterval(obj.sendPendingPacket, 10); } |
| 252 | } |
| 253 | }; |
| 254 | |
| 255 | // Send a pending RMCP packet |
| 256 | obj.sendPendingPacket = function () { |
| 257 | try { |
| 258 | var p = obj.pendingSends.shift(); |
| 259 | if (p != undefined) { |
| 260 | p[0].send(p[1], 623, p[2]); |
| 261 | p[0].send(p[1], 623, p[2]); |
| 262 | } else { |
| 263 | clearInterval(obj.pendingSendTimer); |
| 264 | obj.pendingSendTimer = null; |
| 265 | } |
| 266 | } catch (e) { } |
| 267 | }; |
| 268 | |
| 269 | // Parse RMCP packet |
| 270 | obj.parseRmcpPacket = function (data, rinfo, serverid, func, user) { |
| 271 | if (data == null || data.length < 20) return; |
| 272 | if (((data[12] == 0) || (data[13] != 0) || (data[14] != 1) || (data[15] != 0x57)) && (data[21] & 32)) { |
| 273 | var servertag = data[9]; |
| 274 | var tag = (serverid * 255) + servertag; |
| 275 | var minorVersion = data[18] & 0x0F; |
| 276 | var majorVersion = (data[18] >> 4) & 0x0F; |
| 277 | var provisioningState = data[19] & 0x03; // Pre = 0, In = 1, Post = 2 |
| 278 | |
| 279 | var openPort = (data[16] * 256) + data[17]; |
| 280 | var dualPorts = ((data[19] & 0x04) != 0) ? true : false; |
| 281 | var openPorts = [openPort]; |
| 282 | if (dualPorts == true) { openPorts = [16992, 16993]; } |
| 283 | if (provisioningState <= 2) { func(tag, minorVersion, majorVersion, provisioningState, openPort, dualPorts, rinfo, user); } |
| 284 | } |
| 285 | }; |
| 286 | |
| 287 | // Use the RMCP packet to change the computer state |
| 288 | obj.changeConnectState = function (tag, minorVersion, majorVersion, provisioningState, openPort, dualPorts, rinfo, user) { |
| 289 | //var provisioningStates = { 0: 'Pre', 1: 'in', 2: 'Post' }; |
| 290 | //var provisioningStateStr = provisioningStates[provisioningState]; |
| 291 | //console.log('Intel AMT ' + majorVersion + '.' + minorVersion + ', ' + provisioningStateStr + '-Provisioning at ' + rinfo.address + ', Open Ports: [' + openPort + '], tag: ' + tag + ', dualPorts: ' + dualPorts); |
| 292 | var scaninfo = obj.scanTableTags[tag]; |
| 293 | if (scaninfo != undefined) { |
| 294 | scaninfo.lastpong = Date.now(); |
| 295 | if (scaninfo.state == 0) { |
| 296 | scaninfo.state = 1; |
| 297 | if ((openPort == 16993) || (dualPorts == true)) { scaninfo.nodeinfo.intelamt.tls = 1; } |
| 298 | else if (openPort == 16992) { scaninfo.nodeinfo.intelamt.tls = 0; } |
| 299 | if (majorVersion > 0) { // Older versions of Intel AMT report the AMT version. |
| 300 | scaninfo.nodeinfo.intelamt.ver = majorVersion + '.' + minorVersion; |
| 301 | scaninfo.nodeinfo.intelamt.state = provisioningState; |
| 302 | } |
| 303 | obj.parent.SetConnectivityState(scaninfo.nodeinfo.meshid, scaninfo.nodeinfo._id, scaninfo.lastpong, 4, 7, null, { name: scaninfo.nodeinfo.name }); // Report power state as "present" (7). |
| 304 | obj.changeAmtState(scaninfo.nodeinfo._id, scaninfo.nodeinfo.intelamt.ver, provisioningState, scaninfo.nodeinfo.intelamt.tls); |
| 305 | if (obj.parent.amtManager != null) { obj.parent.amtManager.startAmtManagement(scaninfo.nodeinfo._id, 3, scaninfo.nodeinfo.host); } |
| 306 | } |
| 307 | } |
| 308 | }; |
| 309 | |
| 310 | // Use the RMCP packet to change the computer state |
| 311 | obj.reportMachineState = function (tag, minorVersion, majorVersion, provisioningState, openPort, dualPorts, rinfo, user) { |
| 312 | //var provisioningStates = { 0: 'Pre', 1: 'in', 2: 'Post' }; |
| 313 | //var provisioningStateStr = provisioningStates[provisioningState]; |
| 314 | //console.log(rinfo.address + ': Intel AMT ' + majorVersion + '.' + minorVersion + ', ' + provisioningStateStr + '-Provisioning, Open Ports: [' + openPorts.join(', ') + ']'); |
| 315 | obj.dns.reverse(rinfo.address, function (err, hostnames) { |
| 316 | if ((err == null) && (hostnames != null) && (hostnames.length > 0)) { |
| 317 | user.results[rinfo.address] = { ver: majorVersion + '.' + minorVersion, tls: (((openPort == 16993) || (dualPorts == true)) ? 1 : 0), state: provisioningState, hostname: hostnames[0], hosttype: 'host' }; |
| 318 | } else { |
| 319 | user.results[rinfo.address] = { ver: majorVersion + '.' + minorVersion, tls: (((openPort == 16993) || (dualPorts == true)) ? 1 : 0), state: provisioningState, hostname: rinfo.address, hosttype: 'addr' }; |
| 320 | } |
| 321 | }); |
| 322 | }; |
| 323 | |
| 324 | // Change Intel AMT information in the database and event the changes |
| 325 | obj.changeAmtState = function (nodeid, version, provisioningState, tls) { |
| 326 | //console.log('changeAmtState', nodeid, version, provisioningState, tls); |
| 327 | obj.parent.db.Get(nodeid, function (err, nodes) { |
| 328 | if (nodes.length != 1) return; |
| 329 | var node = nodes[0]; |
| 330 | |
| 331 | // Get the mesh for this device |
| 332 | obj.parent.db.Get(node.meshid, function (err, meshes) { |
| 333 | if (meshes.length != 1) return; |
| 334 | var mesh = meshes[0]; |
| 335 | |
| 336 | // Ready the node change event |
| 337 | var changes = [], event = { etype: 'node', action: 'changenode', nodeid: node._id }; |
| 338 | event.msg = +": "; |
| 339 | |
| 340 | // Make the change & save |
| 341 | var change = false; |
| 342 | if (node.intelamt == undefined) { node.intelamt = {}; } |
| 343 | if (node.intelamt.tls != tls) { node.intelamt.tls = tls; change = true; changes.push(tls == 1 ? 'TLS' : 'NoTLS'); } |
| 344 | if (obj.compareAmtVersionStr(node.intelamt.ver, version)) { node.intelamt.ver = version; change = true; changes.push('AMT Version ' + version); } |
| 345 | if (node.intelamt.state != provisioningState) { node.intelamt.state = provisioningState; change = true; changes.push('AMT State'); } |
| 346 | if (change == true) { |
| 347 | // Make the change in the database |
| 348 | obj.parent.db.Set(node); |
| 349 | |
| 350 | // Event the node change |
| 351 | event.msg = 'Intel® AMT changed device ' + node.name + ' from mesh ' + mesh.name + ': ' + changes.join(', '); |
| 352 | event.node = obj.parent.webserver.CloneSafeNode(node); |
| 353 | if (obj.parent.db.changeStream) { event.noact = 1; } // If DB change stream is active, don't use this event to change the node. Another event will come. |
| 354 | obj.parent.DispatchEvent(['*', node.meshid], obj, event); |
| 355 | } |
| 356 | }); |
| 357 | }); |
| 358 | }; |
| 359 | |
| 360 | // Return true if we should change the Intel AMT version number |
| 361 | obj.compareAmtVersionStr = function (oldVer, newVer) { |
| 362 | if (oldVer == newVer) return false; // Versions are same already, don't update. |
| 363 | if (newVer == undefined || newVer == null) return false; // New version is bad, don't update it. |
| 364 | if (oldVer == undefined || oldVer == null) return true; // Old version is no good anyway, update it. |
| 365 | var oldVerArr = oldVer.toString().split('.'); |
| 366 | var newVerArr = newVer.toString().split('.'); |
| 367 | if ((oldVerArr.length < 2) || (newVerArr.length < 2)) return false; |
| 368 | if ((oldVerArr[0] != newVerArr[0]) || (oldVerArr[1] != newVerArr[1])) return true; |
| 369 | if (newVerArr.length > oldVerArr.length) return true; |
| 370 | if ((newVerArr.length == 3) && (oldVerArr.length == 3) && (oldVerArr[2] != newVerArr[2])) return true; |
| 371 | return false; |
| 372 | }; |
| 373 | |
| 374 | // Check the presense of a specific Intel AMT computer using RMCP |
| 375 | obj.checkTcpPresence = function (host, port, scaninfo, func) { obj.ResolveName(host, function (hostname, ip) { obj.checkTcpPresenceEx(ip, port, scaninfo, func); }); }; |
| 376 | |
| 377 | // Check that we can connect TCP to a given port |
| 378 | obj.checkTcpPresenceEx = function (host, port, scaninfo, func) { |
| 379 | if (host == null) return; |
| 380 | //console.log('checkTcpPresence(' + host + ':' + port + ')'); |
| 381 | try { |
| 382 | var client; |
| 383 | if (port == 16992) { |
| 384 | // Connect using TCP |
| 385 | client = new obj.net.Socket(); |
| 386 | client.connect(port, host, function () { this.write('GET / HTTP/1.1\r\nhost: ' + host + '\r\n\r\n'); }); |
| 387 | } else { |
| 388 | // Connect using TLS, we will switch from default TLS to TLS1-only and back if we get a connection error to support older Intel AMT. |
| 389 | if (scaninfo.tlsoption == null) { scaninfo.tlsoption = 0; } |
| 390 | const tlsOptions = { rejectUnauthorized: false, ciphers: 'RSA+AES:!aNULL:!MD5:!DSS', secureOptions: constants.SSL_OP_NO_SSLv2 | constants.SSL_OP_NO_SSLv3 | constants.SSL_OP_NO_COMPRESSION | constants.SSL_OP_CIPHER_SERVER_PREFERENCE | constants.SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION }; |
| 391 | if (scaninfo.tlsoption == 1) { |
| 392 | tlsOptions.secureProtocol = 'TLSv1_method'; |
| 393 | } else { |
| 394 | tlsOptions.minVersion = 'TLSv1'; |
| 395 | } |
| 396 | client = obj.tls.connect(port, host, tlsOptions, function () { this.write('GET / HTTP/1.1\r\nhost: ' + host + '\r\n\r\n'); }); |
| 397 | } |
| 398 | client.scaninfo = scaninfo; |
| 399 | client.func = func; |
| 400 | client.port = port; |
| 401 | client.setTimeout(10000); |
| 402 | client.on('data', function (data) { var version = obj.getIntelAmtVersionFromHeaders(data.toString()); if (this.scaninfo.tcp != null) { delete this.scaninfo.tcp; try { this.destroy(); } catch (ex) { } this.func(this.scaninfo, version != null, version); } }); |
| 403 | client.on('error', function () { if (this.scaninfo.tlsoption == 0) { this.scaninfo.tlsoption = 1; } else if (this.scaninfo.tlsoption == 1) { this.scaninfo.tlsoption = 0; } if (this.scaninfo.tcp != null) { delete this.scaninfo.tcp; try { this.destroy(); } catch (ex) { } this.func(this.scaninfo, false); } }); |
| 404 | client.on('timeout', function () { if (this.scaninfo.tcp != null) { delete this.scaninfo.tcp; try { this.destroy(); } catch (ex) { } this.func(this.scaninfo, false); } }); |
| 405 | client.on('close', function () { if (this.scaninfo.tcp != null) { delete this.scaninfo.tcp; try { this.destroy(); } catch (ex) { } this.func(this.scaninfo, false); } }); |
| 406 | client.on('end', function () { if (this.scaninfo.tcp != null) { delete this.scaninfo.tcp; try { this.destroy(); } catch (ex) { } this.func(this.scaninfo, false); } }); |
| 407 | scaninfo.tcp = client; |
| 408 | } catch (ex) { console.log(ex); } |
| 409 | }; |
| 410 | |
| 411 | // Return the Intel AMT version from the HTTP headers. Return null if nothing is found. |
| 412 | obj.getIntelAmtVersionFromHeaders = function (headers) { |
| 413 | if (headers == null || headers.length == 0) return null; |
| 414 | var lines = headers.split('\r\n'); |
| 415 | for (var i in lines) { |
| 416 | // Look for the Intel AMT version |
| 417 | if (lines[i].substring(0, 46) == 'Server: Intel(R) Active Management Technology ') { |
| 418 | // We need to check that the Intel AMT version is correct, in the "a.b.c" format |
| 419 | var ver = lines[i].substring(46), splitver = ver.split('.'); |
| 420 | if ((splitver.length == 3 || splitver.length == 4) && ('' + parseInt(splitver[0]) === splitver[0]) && ('' + parseInt(splitver[1]) === splitver[1]) && ('' + parseInt(splitver[2]) === splitver[2])) { return (splitver[0] + '.' + splitver[1] + '.' + splitver[2]); } |
| 421 | } |
| 422 | } |
| 423 | return null; |
| 424 | }; |
| 425 | |
| 426 | //console.log(obj.getIntelAmtVersionFromHeaders("HTTP/1.1 303 See Other\r\nLocation: /logon.htm\r\nContent-Length: 0\r\nServer: Intel(R) Active Management Technology 7.1.91\r\n\r\n")); |
| 427 | |
| 428 | return obj; |
| 429 | }; |