mirror of
https://github.com/Ylianst/MeshCentral.git
synced 2025-02-12 11:01:52 +00:00
Added support for temporary agents
This commit is contained in:
parent
fb55e44edf
commit
bcb7100ced
15 changed files with 73 additions and 44 deletions
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -377,6 +377,7 @@ function createMeshCore(agent) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
case 'wakeonlan': {
|
case 'wakeonlan': {
|
||||||
// Send wake-on-lan on all interfaces for all MAC addresses in data.macs array. The array is a list of HEX MAC addresses.
|
// Send wake-on-lan on all interfaces for all MAC addresses in data.macs array. The array is a list of HEX MAC addresses.
|
||||||
|
@ -1308,6 +1309,7 @@ function createMeshCore(agent) {
|
||||||
amtLms = new lme_heci();
|
amtLms = new lme_heci();
|
||||||
amtLms.on('error', function (e) { amtLmsState = 0; amtLms = null; });
|
amtLms.on('error', function (e) { amtLmsState = 0; amtLms = null; });
|
||||||
amtLms.on('connect', function () { amtLmsState = 2; });
|
amtLms.on('connect', function () { amtLmsState = 2; });
|
||||||
|
//amtLms.on('bind', function (map) { });
|
||||||
amtLms.on('notify', function (data, options, str) {
|
amtLms.on('notify', function (data, options, str) {
|
||||||
if (str != null) { sendConsoleText('Intel AMT LMS: ' + str); }
|
if (str != null) { sendConsoleText('Intel AMT LMS: ' + str); }
|
||||||
handleAmtNotification(data);
|
handleAmtNotification(data);
|
||||||
|
@ -1343,7 +1345,7 @@ function createMeshCore(agent) {
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
var xexports = null;
|
var xexports = null, mainMeshCore = null;
|
||||||
try { xexports = module.exports; } catch (e) { }
|
try { xexports = module.exports; } catch (e) { }
|
||||||
|
|
||||||
if (xexports != null) {
|
if (xexports != null) {
|
||||||
|
@ -1351,5 +1353,6 @@ if (xexports != null) {
|
||||||
module.exports.createMeshCore = createMeshCore;
|
module.exports.createMeshCore = createMeshCore;
|
||||||
} else {
|
} else {
|
||||||
// If we are not running in NodeJS, launch the core
|
// If we are not running in NodeJS, launch the core
|
||||||
createMeshCore().start(null);
|
mainMeshCore = createMeshCore();
|
||||||
|
mainMeshCore.start(null);
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,7 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
var MemoryStream = require('MemoryStream');
|
var MemoryStream = require('MemoryStream');
|
||||||
var lme_id = 0; // Our next channel identifier
|
var lme_id = 0; // Our next channel identifier
|
||||||
var lme_port_offset = 0; // Debug: Set this to "-100" to bind to 16892 & 16893 and IN_ADDRANY. This is for LMS debugging.
|
var lme_port_offset = 0; // Debug: Set this to "-100" to bind to 16892 & 16893 and IN_ADDRANY. This is for LMS debugging.
|
||||||
|
@ -37,7 +38,6 @@ var APF_CHANNEL_DATA = 94;
|
||||||
var APF_CHANNEL_CLOSE = 97;
|
var APF_CHANNEL_CLOSE = 97;
|
||||||
var APF_PROTOCOLVERSION = 192;
|
var APF_PROTOCOLVERSION = 192;
|
||||||
|
|
||||||
|
|
||||||
function lme_object() {
|
function lme_object() {
|
||||||
this.ourId = ++lme_id;
|
this.ourId = ++lme_id;
|
||||||
this.amtId = -1;
|
this.amtId = -1;
|
||||||
|
@ -379,16 +379,14 @@ function lme_heci(options) {
|
||||||
buffer.writeUInt32BE(0xFFFFFFFF, 13); // Reserved
|
buffer.writeUInt32BE(0xFFFFFFFF, 13); // Reserved
|
||||||
this.write(buffer);
|
this.write(buffer);
|
||||||
|
|
||||||
/*
|
//var buffer = Buffer.alloc(17);
|
||||||
var buffer = Buffer.alloc(17);
|
//buffer.writeUInt8(APF_CHANNEL_OPEN_FAILURE, 0);
|
||||||
buffer.writeUInt8(APF_CHANNEL_OPEN_FAILURE, 0);
|
//buffer.writeUInt32BE(channelSender, 1); // Intel AMT sender channel
|
||||||
buffer.writeUInt32BE(channelSender, 1); // Intel AMT sender channel
|
//buffer.writeUInt32BE(2, 5); // Reason code
|
||||||
buffer.writeUInt32BE(2, 5); // Reason code
|
//buffer.writeUInt32BE(0, 9); // Reserved
|
||||||
buffer.writeUInt32BE(0, 9); // Reserved
|
//buffer.writeUInt32BE(0, 13); // Reserved
|
||||||
buffer.writeUInt32BE(0, 13); // Reserved
|
//this.write(buffer);
|
||||||
this.write(buffer);
|
//console.log('Sent APF_CHANNEL_OPEN_FAILURE', channelSender);
|
||||||
console.log('Sent APF_CHANNEL_OPEN_FAILURE', channelSender);
|
|
||||||
*/
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -103,7 +103,7 @@ function _PutObjToBodyXml(resuri, putObj) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is a drop-in replacement to _turnToXml() that works without xml parser dependency.
|
// This is a drop-in replacement to _turnToXml() that works without xml parser dependency.
|
||||||
Object.defineProperty(Array.prototype, "peek", { value: function () { return (this.length > 0 ? this[this.length - 1] : null); } });
|
try { Object.defineProperty(Array.prototype, "peek", { value: function () { return (this.length > 0 ? this[this.length - 1] : null); } }); } catch (ex) { }
|
||||||
function _treeBuilder() {
|
function _treeBuilder() {
|
||||||
this.tree = [];
|
this.tree = [];
|
||||||
this.push = function (element) { this.tree.push(element); };
|
this.push = function (element) { this.tree.push(element); };
|
||||||
|
|
|
@ -14,6 +14,7 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
var MemoryStream = require('MemoryStream');
|
var MemoryStream = require('MemoryStream');
|
||||||
var lme_id = 0; // Our next channel identifier
|
var lme_id = 0; // Our next channel identifier
|
||||||
var lme_port_offset = 0; // Debug: Set this to "-100" to bind to 16892 & 16893 and IN_ADDRANY. This is for LMS debugging.
|
var lme_port_offset = 0; // Debug: Set this to "-100" to bind to 16892 & 16893 and IN_ADDRANY. This is for LMS debugging.
|
||||||
|
@ -37,7 +38,6 @@ var APF_CHANNEL_DATA = 94;
|
||||||
var APF_CHANNEL_CLOSE = 97;
|
var APF_CHANNEL_CLOSE = 97;
|
||||||
var APF_PROTOCOLVERSION = 192;
|
var APF_PROTOCOLVERSION = 192;
|
||||||
|
|
||||||
|
|
||||||
function lme_object() {
|
function lme_object() {
|
||||||
this.ourId = ++lme_id;
|
this.ourId = ++lme_id;
|
||||||
this.amtId = -1;
|
this.amtId = -1;
|
||||||
|
@ -52,7 +52,7 @@ function stream_bufferedWrite() {
|
||||||
var emitterUtils = require('events').inherits(this);
|
var emitterUtils = require('events').inherits(this);
|
||||||
this.buffer = [];
|
this.buffer = [];
|
||||||
this._readCheckImmediate = undefined;
|
this._readCheckImmediate = undefined;
|
||||||
|
this._ObjectID = "bufferedWriteStream";
|
||||||
// Writable Events
|
// Writable Events
|
||||||
emitterUtils.createEvent('close');
|
emitterUtils.createEvent('close');
|
||||||
emitterUtils.createEvent('drain');
|
emitterUtils.createEvent('drain');
|
||||||
|
@ -115,17 +115,19 @@ function lme_heci(options) {
|
||||||
emitterUtils.createEvent('error');
|
emitterUtils.createEvent('error');
|
||||||
emitterUtils.createEvent('connect');
|
emitterUtils.createEvent('connect');
|
||||||
emitterUtils.createEvent('notify');
|
emitterUtils.createEvent('notify');
|
||||||
|
emitterUtils.createEvent('bind');
|
||||||
|
|
||||||
if ((options != null) && (options.debug == true)) { lme_port_offset = -100; } // LMS debug mode
|
if ((options != null) && (options.debug == true)) { lme_port_offset = -100; } // LMS debug mode
|
||||||
|
|
||||||
var heci = require('heci');
|
var heci = require('heci');
|
||||||
this.INITIAL_RXWINDOW_SIZE = 4096;
|
this.INITIAL_RXWINDOW_SIZE = 4096;
|
||||||
|
|
||||||
|
this._ObjectID = "lme";
|
||||||
this._LME = heci.create();
|
this._LME = heci.create();
|
||||||
|
this._LME._binded = {};
|
||||||
this._LME.LMS = this;
|
this._LME.LMS = this;
|
||||||
this._LME.on('error', function (e) { this.LMS.emit('error', e); });
|
this._LME.on('error', function (e) { this.LMS.emit('error', e); });
|
||||||
this._LME.on('connect', function () {
|
this._LME.on('connect', function () {
|
||||||
this.LMS.emit('connect');
|
|
||||||
this.on('data', function (chunk) {
|
this.on('data', function (chunk) {
|
||||||
// this = HECI
|
// this = HECI
|
||||||
var cmd = chunk.readUInt8(0);
|
var cmd = chunk.readUInt8(0);
|
||||||
|
@ -166,7 +168,8 @@ function lme_heci(options) {
|
||||||
if (channel.localPort == port) { this.sockets[i].end(); delete this.sockets[i]; } // Close this socket
|
if (channel.localPort == port) { this.sockets[i].end(); delete this.sockets[i]; } // Close this socket
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (this[name][port] == null) { // Bind a new server socket if not already present
|
if (this[name][port] == null)
|
||||||
|
{ // Bind a new server socket if not already present
|
||||||
this[name][port] = require('net').createServer();
|
this[name][port] = require('net').createServer();
|
||||||
this[name][port].HECI = this;
|
this[name][port].HECI = this;
|
||||||
if (lme_port_offset == 0) {
|
if (lme_port_offset == 0) {
|
||||||
|
@ -178,6 +181,8 @@ function lme_heci(options) {
|
||||||
//console.log('New [' + socket.remoteFamily + '] TCP Connection on: ' + socket.remoteAddress + ' :' + socket.localPort);
|
//console.log('New [' + socket.remoteFamily + '] TCP Connection on: ' + socket.remoteAddress + ' :' + socket.localPort);
|
||||||
this.HECI.LMS.bindDuplexStream(socket, socket.remoteFamily, socket.localPort - lme_port_offset);
|
this.HECI.LMS.bindDuplexStream(socket, socket.remoteFamily, socket.localPort - lme_port_offset);
|
||||||
});
|
});
|
||||||
|
this._binded[port] = true;
|
||||||
|
this.LMS.emit('bind', this._binded);
|
||||||
}
|
}
|
||||||
var outBuffer = Buffer.alloc(5);
|
var outBuffer = Buffer.alloc(5);
|
||||||
outBuffer.writeUInt8(81, 0);
|
outBuffer.writeUInt8(81, 0);
|
||||||
|
@ -374,20 +379,21 @@ function lme_heci(options) {
|
||||||
buffer.writeUInt32BE(0xFFFFFFFF, 13); // Reserved
|
buffer.writeUInt32BE(0xFFFFFFFF, 13); // Reserved
|
||||||
this.write(buffer);
|
this.write(buffer);
|
||||||
|
|
||||||
/*
|
//var buffer = Buffer.alloc(17);
|
||||||
var buffer = Buffer.alloc(17);
|
//buffer.writeUInt8(APF_CHANNEL_OPEN_FAILURE, 0);
|
||||||
buffer.writeUInt8(APF_CHANNEL_OPEN_FAILURE, 0);
|
//buffer.writeUInt32BE(channelSender, 1); // Intel AMT sender channel
|
||||||
buffer.writeUInt32BE(channelSender, 1); // Intel AMT sender channel
|
//buffer.writeUInt32BE(2, 5); // Reason code
|
||||||
buffer.writeUInt32BE(2, 5); // Reason code
|
//buffer.writeUInt32BE(0, 9); // Reserved
|
||||||
buffer.writeUInt32BE(0, 9); // Reserved
|
//buffer.writeUInt32BE(0, 13); // Reserved
|
||||||
buffer.writeUInt32BE(0, 13); // Reserved
|
//this.write(buffer);
|
||||||
this.write(buffer);
|
//console.log('Sent APF_CHANNEL_OPEN_FAILURE', channelSender);
|
||||||
console.log('Sent APF_CHANNEL_OPEN_FAILURE', channelSender);
|
|
||||||
*/
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
this.LMS.emit('connect');
|
||||||
|
this.resume();
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
this.bindDuplexStream = function (duplexStream, remoteFamily, localPort) {
|
this.bindDuplexStream = function (duplexStream, remoteFamily, localPort) {
|
||||||
|
|
|
@ -92,11 +92,12 @@ function amt_heci() {
|
||||||
for (var i = 1; i < arguments.length; ++i) { optional.push(arguments[i]); }
|
for (var i = 1; i < arguments.length; ++i) { optional.push(arguments[i]); }
|
||||||
this.sendCommand(26, null, function (header, fn, opt) {
|
this.sendCommand(26, null, function (header, fn, opt) {
|
||||||
if (header.Status == 0) {
|
if (header.Status == 0) {
|
||||||
var i, CodeVersion = header.Data, val = { BiosVersion: CodeVersion.slice(0, this._amt.BiosVersionLen), Versions: [] }, v = CodeVersion.slice(this._amt.BiosVersionLen + 4);
|
var i, CodeVersion = header.Data, val = { BiosVersion: CodeVersion.slice(0, this._amt.BiosVersionLen).toString(), Versions: [] }, v = CodeVersion.slice(this._amt.BiosVersionLen + 4);
|
||||||
for (i = 0; i < CodeVersion.readUInt32LE(this._amt.BiosVersionLen) ; ++i) {
|
for (i = 0; i < CodeVersion.readUInt32LE(this._amt.BiosVersionLen) ; ++i) {
|
||||||
val.Versions[i] = { Description: v.slice(2, v.readUInt16LE(0) + 2).toString(), Version: v.slice(4 + this._amt.UnicodeStringLen, 4 + this._amt.UnicodeStringLen + v.readUInt16LE(2 + this._amt.UnicodeStringLen)).toString() };
|
val.Versions[i] = { Description: v.slice(2, v.readUInt16LE(0) + 2).toString(), Version: v.slice(4 + this._amt.UnicodeStringLen, 4 + this._amt.UnicodeStringLen + v.readUInt16LE(2 + this._amt.UnicodeStringLen)).toString() };
|
||||||
v = v.slice(4 + (2 * this._amt.UnicodeStringLen));
|
v = v.slice(4 + (2 * this._amt.UnicodeStringLen));
|
||||||
}
|
}
|
||||||
|
if (val.BiosVersion.indexOf('\0') > 0) { val.BiosVersion = val.BiosVersion.substring(0, val.BiosVersion.indexOf('\0')); }
|
||||||
opt.unshift(val);
|
opt.unshift(val);
|
||||||
} else {
|
} else {
|
||||||
opt.unshift(null);
|
opt.unshift(null);
|
||||||
|
|
|
@ -103,7 +103,7 @@ function _PutObjToBodyXml(resuri, putObj) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is a drop-in replacement to _turnToXml() that works without xml parser dependency.
|
// This is a drop-in replacement to _turnToXml() that works without xml parser dependency.
|
||||||
Object.defineProperty(Array.prototype, "peek", { value: function () { return (this.length > 0 ? this[this.length - 1] : null); } });
|
try { Object.defineProperty(Array.prototype, "peek", { value: function () { return (this.length > 0 ? this[this.length - 1] : null); } }); } catch (ex) { }
|
||||||
function _treeBuilder() {
|
function _treeBuilder() {
|
||||||
this.tree = [];
|
this.tree = [];
|
||||||
this.push = function (element) { this.tree.push(element); };
|
this.push = function (element) { this.tree.push(element); };
|
||||||
|
|
19
meshagent.js
19
meshagent.js
|
@ -48,6 +48,21 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) {
|
||||||
// Other clean up may be needed here
|
// Other clean up may be needed here
|
||||||
if (obj.unauth) { delete obj.unauth; }
|
if (obj.unauth) { delete obj.unauth; }
|
||||||
if (obj.agentUpdate != null) { obj.fs.close(obj.agentUpdate.fd); obj.agentUpdate = null; }
|
if (obj.agentUpdate != null) { obj.fs.close(obj.agentUpdate.fd); obj.agentUpdate = null; }
|
||||||
|
if (obj.agentInfo.capabilities & 0x20) { // This is a temporary agent, remote it
|
||||||
|
// Delete this node including network interface information and events
|
||||||
|
obj.db.Remove(obj.dbNodeKey);
|
||||||
|
obj.db.Remove('if' + obj.dbNodeKey);
|
||||||
|
|
||||||
|
// Event node deletion
|
||||||
|
obj.parent.parent.DispatchEvent(['*', obj.dbMeshKey], obj, { etype: 'node', action: 'removenode', nodeid: obj.dbNodeKey, domain: obj.domain.id, nolog: 1 })
|
||||||
|
|
||||||
|
// Disconnect all connections if needed
|
||||||
|
var state = obj.parent.parent.GetConnectivityState(obj.dbNodeKey);
|
||||||
|
if ((state != null) && (state.connectivity != null)) {
|
||||||
|
if ((state.connectivity & 1) != 0) { obj.parent.wsagents[obj.dbNodeKey].close(); } // Disconnect mesh agent
|
||||||
|
if ((state.connectivity & 2) != 0) { obj.parent.parent.mpsserver.close(obj.parent.parent.mpsserver.ciraConnections[obj.dbNodeKey]); } // Disconnect CIRA connection
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// When data is received from the mesh agent web socket
|
// When data is received from the mesh agent web socket
|
||||||
|
@ -431,6 +446,10 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) {
|
||||||
delete command.userid; // Remove the userid, since we are sending to that userid, so it's implyed.
|
delete command.userid; // Remove the userid, since we are sending to that userid, so it's implyed.
|
||||||
for (var i in sessions) { sessions[i].send(JSON.stringify(command)); }
|
for (var i in sessions) { sessions[i].send(JSON.stringify(command)); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (obj.parent.parent.multiServer != null) {
|
||||||
|
// TODO: Add multi-server support
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else { // Route this command to the mesh
|
} else { // Route this command to the mesh
|
||||||
command.nodeid = obj.dbNodeKey;
|
command.nodeid = obj.dbNodeKey;
|
||||||
|
|
|
@ -746,22 +746,24 @@ function CreateMeshCentralServer(config, args) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read meshcore.js and all .js files in the modules folder.
|
// Read meshcore.js and all .js files in the modules folder.
|
||||||
var moduleAdditions = 'var addedModules = [];', moduleAdditionsNoMei = 'var addedModules = [];', modulesDir = null;
|
var moduleAdditions = 'var addedModules = [];', moduleAdditionsNoMei = 'var addedModules = [];', meshCore = null, modulesDir = null;
|
||||||
var meshCore = obj.fs.readFileSync(obj.path.join(meshcorePath, 'meshcore.js')).toString();
|
try { meshCore = obj.fs.readFileSync(obj.path.join(meshcorePath, 'meshcore.js')).toString(); } catch (e) { }
|
||||||
try { modulesDir = obj.fs.readdirSync(obj.path.join(meshcorePath, 'modules_meshcore')); } catch (e) { }
|
if (meshCore != null) {
|
||||||
if (modulesDir != null) {
|
try { modulesDir = obj.fs.readdirSync(obj.path.join(meshcorePath, 'modules_meshcore')); } catch (e) { }
|
||||||
for (var i in modulesDir) {
|
if (modulesDir != null) {
|
||||||
if (modulesDir[i].toLowerCase().endsWith('.js')) {
|
for (var i in modulesDir) {
|
||||||
// Merge this module
|
if (modulesDir[i].toLowerCase().endsWith('.js')) {
|
||||||
var moduleName = modulesDir[i].substring(0, modulesDir[i].length - 3);
|
// Merge this module
|
||||||
var moduleDataB64 = obj.fs.readFileSync(obj.path.join(meshcorePath, 'modules_meshcore', modulesDir[i])).toString('base64');
|
var moduleName = modulesDir[i].substring(0, modulesDir[i].length - 3);
|
||||||
moduleAdditions += 'try { addModule("' + moduleName + '", Buffer.from("' + moduleDataB64 + '", "base64")); addedModules.push("' + moduleName + '"); } catch (e) { }\r\n';
|
var moduleDataB64 = obj.fs.readFileSync(obj.path.join(meshcorePath, 'modules_meshcore', modulesDir[i])).toString('base64');
|
||||||
if ((moduleName != 'amt_heci') && (moduleName != 'lme_heci') && (moduleName != 'amt-0.2.0.js') && (moduleName != 'amt-script-0.2.0.js') && (moduleName != 'amt-wsman-0.2.0.js') && (moduleName != 'amt-wsman-duk-0.2.0.js')) {
|
moduleAdditions += 'try { addModule("' + moduleName + '", Buffer.from("' + moduleDataB64 + '", "base64")); addedModules.push("' + moduleName + '"); } catch (e) { }\r\n';
|
||||||
moduleAdditionsNoMei += 'try { addModule("' + moduleName + '", Buffer.from("' + moduleDataB64 + '", "base64")); addedModules.push("' + moduleName + '"); } catch (e) { }\r\n';
|
if ((moduleName != 'amt_heci') && (moduleName != 'lme_heci') && (moduleName != 'amt-0.2.0.js') && (moduleName != 'amt-script-0.2.0.js') && (moduleName != 'amt-wsman-0.2.0.js') && (moduleName != 'amt-wsman-duk-0.2.0.js')) {
|
||||||
|
moduleAdditionsNoMei += 'try { addModule("' + moduleName + '", Buffer.from("' + moduleDataB64 + '", "base64")); addedModules.push("' + moduleName + '"); } catch (e) { }\r\n';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
} else { meshCore = ''; } // No meshcore.js
|
||||||
|
|
||||||
// Set the new default meshcore.js with and without MEI support
|
// Set the new default meshcore.js with and without MEI support
|
||||||
obj.defaultMeshCore = obj.common.IntToStr(0) + moduleAdditions + meshCore;
|
obj.defaultMeshCore = obj.common.IntToStr(0) + moduleAdditions + meshCore;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "meshcentral",
|
"name": "meshcentral",
|
||||||
"version": "0.1.6-c",
|
"version": "0.1.6-d",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"Remote Management",
|
"Remote Management",
|
||||||
"Intel AMT",
|
"Intel AMT",
|
||||||
|
|
Loading…
Reference in a new issue