1
0
mirror of https://github.com/Ylianst/MeshCommander synced 2025-12-05 21:53:19 +00:00
Files
MeshCommander/amt-scanner-0.1.0.js
Ylian Saint-Hilaire 3deafe34ef first commit
2020-03-09 11:11:06 -07:00

235 lines
10 KiB
JavaScript

/**
* @description Meshcentral Intel AMT Local Scanner
* @author Ylian Saint-Hilaire
* @version v0.0.1
*/
// Construct a Intel AMT Scanner object
var CreateAmtScanner = function (func) {
var obj = {};
obj.active = false;
obj.dgram = require('dgram');
obj.servers = {};
obj.rserver = {};
obj.rpacket = null;
obj.tagToId = {}; // Tag --> { lastpong: time, id: NodeId }
obj.scanTable = {}; // Handle --> ScanInfo : { lastping: time, lastpong: time, nodeinfo:{node} }
obj.scanTableTags = {}; // Tag --> ScanInfo
obj.pendingSends = []; // We was to stagger the sends using a 10ms timer
obj.pendingSendTimer = null;
obj.mainTimer = null;
obj.nextTag = 0;
obj.computerPresenceFunc = func;
var PeriodicScanTime = 30000; // Interval between scan sweeps
var PeriodicScanTimeout = 65000; // After this time, timeout the device.
// Build a RMCP packet with a given tag field
obj.buildRmcpPing = function (tag) {
var packet = new Buffer(hex2rstr('06000006000011BE80000000'), 'ascii');
packet[9] = tag;
return packet;
}
// Start scanning for local network Intel AMT computers
obj.start = function () {
if (obj.active == false) {
obj.active = true;
obj.performScan();
obj.mainTimer = setInterval(obj.performScan, PeriodicScanTime);
}
}
// Stop scanning for local network Intel AMT computers
obj.stop = function () {
obj.active = false;
for (var i in obj.servers) { obj.servers[i].close(); } // Stop all servers
obj.servers = {};
if (obj.mainTimer != null) { clearInterval(obj.mainTimer); obj.mainTimer = null; }
}
// Scan for Intel AMT computers using network multicast
obj.performRangeScan = function (id, rangestr, func) {
if (obj.rpacket == null) { obj.rpacket = obj.buildRmcpPing(0); }
var range = obj.parseIpv4Range(rangestr);
//console.log(obj.IPv4NumToStr(range.min), obj.IPv4NumToStr(range.max));
if (range == null || (range.min > range.max)) return false;
var rangeinfo = { id: id, range: rangestr, min: range.min, max: range.max, results: {}, resultCount: 0 };
obj.rserver[id] = rangeinfo;
rangeinfo.server = obj.dgram.createSocket("udp4");
rangeinfo.server.bind(0);
rangeinfo.server.on('error', function(err) { console.log(err); });
rangeinfo.server.on('message', function(data, rinfo) { obj.parseRmcpPacket(data, rinfo, 0, func, rangeinfo); });
rangeinfo.server.on('listening', function () {
for (var i = rangeinfo.min; i <= rangeinfo.max; i++) {
rangeinfo.server.send(obj.rpacket, 0, obj.rpacket.length, 623, obj.IPv4NumToStr(i));
rangeinfo.server.send(obj.rpacket, 0, obj.rpacket.length, 623, obj.IPv4NumToStr(i));
}
});
rangeinfo.timer = setTimeout(function () {
if (rangeinfo.resultCount == 0) { func(obj, null); }
rangeinfo.server.close();
delete rangeinfo.server;
delete rangeinfo;
}, 5000);
return true;
}
// Parse range, used to parse "ip", "ip/mask" or "ip-ip" notation.
// Return the start and end value of the scan
obj.parseIpv4Range = function (range) {
if (range == undefined || range == null) return null;
var x = range.split('-');
if (x.length == 2) { return { min: obj.parseIpv4Addr(x[0]), max: obj.parseIpv4Addr(x[1]) }; }
x = range.split('/');
if (x.length == 2) {
var ip = obj.parseIpv4Addr(x[0]), masknum = parseInt(x[1]), mask = 0;
if (masknum < 16) return null;
for (var i = 0; i < (32 - masknum) ; i++) { mask = (mask << 1); mask++; }
return { min: ip & (0xFFFFFFFF - mask), max: (ip & (0xFFFFFFFF - mask)) + mask };
}
x = obj.parseIpv4Addr(range);
if (x == null) return null;
return { min: x, max: x };
}
// Parse IP address. Takes a
obj.parseIpv4Addr = function (addr) {
var x = addr.split('.');
if (x.length == 4) { return (parseInt(x[0]) << 24) + (parseInt(x[1]) << 16) + (parseInt(x[2]) << 8) + (parseInt(x[3]) << 0); }
return null;
}
// IP address number to string
obj.IPv4NumToStr = function (num) {
return ((num >> 24) & 0xFF) + '.' + ((num >> 16) & 0xFF) + '.' + ((num >> 8) & 0xFF) + '.' + (num & 0xFF);
}
// Scan the list of all computers
obj.performScan = function () {
if (obj.active == false || computerlist == undefined) return;
for (var i in obj.scanTable) { obj.scanTable[i].present = false; }
if (computerlist.length > 0) {
for (var i in computerlist) {
var computer = computerlist[i];
var host = computer.host.toLowerCase();
if ((host != '127.0.0.1') && (host != '::1') && (host != 'localhost') && (host.split(':').length == 1)) {
var scaninfo = obj.scanTable[computer.h];
if (scaninfo == undefined) {
var tag = obj.nextTag++;
obj.scanTableTags[tag] = obj.scanTable[computer.h] = scaninfo = { computer: computer, present: true, tag: tag, scanstate: -1, lastpong: Date.now() };
} else {
scaninfo.present = true;
var delta = Date.now() - scaninfo.lastpong;
if ((delta > PeriodicScanTimeout) && (scaninfo.scanstate != 0)) { // More than the timeout without a response, mark the node as unknown state
scaninfo.scanstate = 0;
if (obj.computerPresenceFunc) { obj.computerPresenceFunc(scaninfo.computer.h, false); }
}
}
// Start scanning this node
scaninfo.lastping = Date.now();
obj.checkAmtPresence(computer.host, scaninfo.tag);
}
}
}
for (var i in obj.scanTable) {
if (obj.scanTable[i].present == false) {
// Stop scanning this node
delete obj.scanTableTags[obj.scanTable[i].tag];
delete obj.scanTable[i];
}
}
}
// Check the presense of a specific Intel AMT computer
obj.checkAmtPresence = function (host, tag) {
var serverid = Math.floor(tag / 255);
var servertag = (tag % 255);
var packet = obj.buildRmcpPing(servertag);
var server = obj.servers[serverid];
if (server == undefined) {
// Start new server
server = obj.dgram.createSocket('udp4');
server.on('error', function(err) { });
server.on('message', function (data, rinfo) { obj.parseRmcpPacket(data, rinfo, serverid, obj.changeConnectState, null); });
server.on('listening', function() { obj.pendingSends.push([ server, packet, host ]); if (obj.pendingSendTimer == null) { obj.pendingSendTimer = setInterval(obj.sendPendingPacket, 10); } });
server.bind(0);
obj.servers[serverid] = server;
} else {
// Use existing server
obj.pendingSends.push([ server, packet, host ]);
if (obj.pendingSendTimer == null) { obj.pendingSendTimer = setInterval(obj.sendPendingPacket, 10); }
}
}
// Send a pending RMCP packet
obj.sendPendingPacket = function () {
try {
var p = obj.pendingSends.shift();
if (p != undefined) {
p[0].send(p[1], 0, p[1].length, 623, p[2]);
p[0].send(p[1], 0, p[1].length, 623, p[2]);
} else {
clearInterval(obj.pendingSendTimer);
obj.pendingSendTimer = null;
}
} catch (e) { }
}
// Parse RMCP packet
obj.parseRmcpPacket = function (data, rinfo, serverid, func, rangeinfo) {
if (data == null || data.length < 20) return;
if (((data[12] == 0) || (data[13] != 0) || (data[14] != 1) || (data[15] != 0x57)) && (data[21] & 32)) {
var result = { tag: (serverid * 255) + data[9], ver: ((data[18] >> 4) & 0x0F) + '.' + (data[18] & 0x0F), state: (data[19] & 0x03), port: ((data[16] * 256) + data[17]), dualport: (((data[19] & 0x04) != 0) ? true : false), rinfo: rinfo };
result.tls = (((result.openPort == 16993) || (result.dualPorts == true)) ? 1 : 0)
if (rangeinfo != null) {
if ((result.state <= 2) && (!rangeinfo.results[rinfo.address])) {
rangeinfo.results[rinfo.address] = result;
rangeinfo.resultCount++;
obj.reverseLookup(rinfo.address, function (err, domains) {
result.domains = domains;
func(obj, result, rangeinfo);
});
}
} else {
func(obj, result, rangeinfo);
}
}
}
// Use the RMCP packet to change the computer state
obj.changeConnectState = function (obj, result, rangeinfo) {
var scaninfo = obj.scanTableTags[result.tag];
if (scaninfo) {
scaninfo.lastpong = Date.now();
if (scaninfo.scanstate != 1) {
scaninfo.scanstate = 1;
if (obj.computerPresenceFunc) obj.computerPresenceFunc(scaninfo.computer.h, true);
}
}
}
// Perform DNS reverse lookup
obj.reverseLookup = function(ip, callback) {
var callbackCalled = false, timer = null;
var doCallback = function (err, domains) {
if (err) domains = [ip];
if (callbackCalled) return;
callbackCalled = true;
if (timer != null) {
// Cancelling timer to safe few seconds
clearTimeout(timer);
timer = null;
}
callback(err, domains);
}
// throw failed upon timeout
timer = setTimeout(function () {
timer = null; // Ensure timer is nullified
doCallback(new Error("Timeout exceeded"), null);
}, 3000);
require("dns").reverse(ip, doCallback);
}
return obj;
}