mirror of
https://github.com/Ylianst/MeshCentral.git
synced 2024-12-27 15:45:53 -05:00
Many Intel AMT improvements.
This commit is contained in:
parent
8fe014d33d
commit
4594b4f6f6
@ -100,7 +100,6 @@
|
|||||||
<Compile Include="amt\amt-wsman.js" />
|
<Compile Include="amt\amt-wsman.js" />
|
||||||
<Compile Include="amt\amt-xml.js" />
|
<Compile Include="amt\amt-xml.js" />
|
||||||
<Compile Include="amt\amt.js" />
|
<Compile Include="amt\amt.js" />
|
||||||
<Compile Include="apfserver.js" />
|
|
||||||
<Compile Include="exeHandler.js" />
|
<Compile Include="exeHandler.js" />
|
||||||
<Compile Include="letsencrypt.js" />
|
<Compile Include="letsencrypt.js" />
|
||||||
<Compile Include="mcrec.js" />
|
<Compile Include="mcrec.js" />
|
||||||
|
@ -3507,15 +3507,20 @@ function createMeshCore(agent) {
|
|||||||
mpskeepalive: 60000,
|
mpskeepalive: 60000,
|
||||||
clientname: require('os').hostname(),
|
clientname: require('os').hostname(),
|
||||||
clientaddress: '127.0.0.1',
|
clientaddress: '127.0.0.1',
|
||||||
clientuuid: meshCoreObj.intelamt.uuid
|
clientuuid: meshCoreObj.intelamt.uuid,
|
||||||
|
conntype: 2 // 0 = CIRA, 1 = Relay, 2 = LMS. The correct value is 2 since we are performing an LMS relay, other values for testing.
|
||||||
};
|
};
|
||||||
var tobj = { debug: false }; //
|
if ((apfarg.clientuuid == null) || (apfarg.clientuuid.length != 36)) {
|
||||||
apftunnel = require('apfclient')(tobj, apfarg);
|
response = "Unable to get Intel AMT UUID: " + apfarg.clientuuid;
|
||||||
try {
|
} else {
|
||||||
apftunnel.connect();
|
var tobj = { debug: false };
|
||||||
response += "..success";
|
apftunnel = require('apfclient')(tobj, apfarg);
|
||||||
} catch (e) {
|
try {
|
||||||
response += JSON.stringify(e);
|
apftunnel.connect();
|
||||||
|
response += "...success";
|
||||||
|
} catch (e) {
|
||||||
|
response += JSON.stringify(e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else if (args['_'][0] == 'off') {
|
} else if (args['_'][0] == 'off') {
|
||||||
response = "Stopping APF tunnel";
|
response = "Stopping APF tunnel";
|
||||||
@ -3640,18 +3645,10 @@ function createMeshCore(agent) {
|
|||||||
{
|
{
|
||||||
switch(amt.lmsstate)
|
switch(amt.lmsstate)
|
||||||
{
|
{
|
||||||
case 0:
|
case 0: intelamt.microlms = 'DISABLED'; break;
|
||||||
intelamt.microlms = 'DISABLED'
|
case 1: intelamt.microlms = 'CONNECTING'; break;
|
||||||
break;
|
case 2: intelamt.microlms = 'CONNECTED'; break;
|
||||||
case 1:
|
default: intelamt.microlms = 'unknown'; break;
|
||||||
intelamt.microlms = 'CONNECTING'
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
intelamt.microlms = 'CONNECTED'
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
intelamt.microlms = 'unknown'
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var p = false;
|
var p = false;
|
||||||
|
@ -7,6 +7,8 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
function CreateAPFClient(parent, args) {
|
function CreateAPFClient(parent, args) {
|
||||||
|
if ((args.clientuuid == null) || (args.clientuuid.length != 36)) return null; // Require a UUID if this exact length
|
||||||
|
|
||||||
var obj = {};
|
var obj = {};
|
||||||
obj.parent = parent;
|
obj.parent = parent;
|
||||||
obj.args = args;
|
obj.args = args;
|
||||||
@ -57,7 +59,7 @@ function CreateAPFClient(parent, args) {
|
|||||||
|
|
||||||
// Intel AMT forwarded port list for non-TLS mode
|
// Intel AMT forwarded port list for non-TLS mode
|
||||||
//var pfwd_ports = [16992, 623, 16994, 5900];
|
//var pfwd_ports = [16992, 623, 16994, 5900];
|
||||||
var pfwd_ports = [ 16992 ];
|
var pfwd_ports = [ 16992, 16993 ];
|
||||||
|
|
||||||
// protocol definitions
|
// protocol definitions
|
||||||
var APFProtocol = {
|
var APFProtocol = {
|
||||||
@ -81,7 +83,8 @@ function CreateAPFClient(parent, args) {
|
|||||||
KEEPALIVE_REQUEST: 208,
|
KEEPALIVE_REQUEST: 208,
|
||||||
KEEPALIVE_REPLY: 209,
|
KEEPALIVE_REPLY: 209,
|
||||||
KEEPALIVE_OPTIONS_REQUEST: 210,
|
KEEPALIVE_OPTIONS_REQUEST: 210,
|
||||||
KEEPALIVE_OPTIONS_REPLY: 211
|
KEEPALIVE_OPTIONS_REPLY: 211,
|
||||||
|
MESH_CONNECTION_TYPE: 250 // This is a Mesh specific command that instructs the server of the connection type: 1 = Relay, 2 = LMS.
|
||||||
}
|
}
|
||||||
|
|
||||||
var APFDisconnectCode = {
|
var APFDisconnectCode = {
|
||||||
@ -160,13 +163,18 @@ function CreateAPFClient(parent, args) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
obj.state = CIRASTATE.INITIAL;
|
obj.state = CIRASTATE.INITIAL;
|
||||||
|
if (typeof obj.args.conntype == 'number') { SendConnectionType(obj.forwardClient.ws, obj.args.conntype); }
|
||||||
SendProtocolVersion(obj.forwardClient.ws, obj.args.clientuuid);
|
SendProtocolVersion(obj.forwardClient.ws, obj.args.clientuuid);
|
||||||
SendServiceRequest(obj.forwardClient.ws, 'auth@amt.intel.com');
|
SendServiceRequest(obj.forwardClient.ws, 'auth@amt.intel.com');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function SendConnectionType(socket, type) {
|
||||||
|
socket.write(String.fromCharCode(APFProtocol.MESH_CONNECTION_TYPE) + IntToStr(type));
|
||||||
|
Debug("APF: Send connection type " + type);
|
||||||
|
}
|
||||||
|
|
||||||
function SendProtocolVersion(socket, uuid) {
|
function SendProtocolVersion(socket, uuid) {
|
||||||
var buuid = strToGuid(uuid);
|
var data = String.fromCharCode(APFProtocol.PROTOCOLVERSION) + IntToStr(1) + IntToStr(0) + IntToStr(0) + hex2rstr(strToGuid(uuid)) + binzerostring(64);
|
||||||
var data = String.fromCharCode(APFProtocol.PROTOCOLVERSION) + '' + IntToStr(1) + IntToStr(0) + IntToStr(0) + hex2rstr(buuid) + binzerostring(64);
|
|
||||||
socket.write(data);
|
socket.write(data);
|
||||||
Debug("APF: Send protocol version 1 0 " + uuid);
|
Debug("APF: Send protocol version 1 0 " + uuid);
|
||||||
obj.cirastate = CIRASTATE.PROTOCOL_VERSION_SENT;
|
obj.cirastate = CIRASTATE.PROTOCOL_VERSION_SENT;
|
||||||
|
@ -113,11 +113,10 @@ module.exports.CreateAmtRedirect = function (module, domain, user, webserver, me
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
// If Intel AMT CIRA connection is available, use it
|
// If Intel AMT CIRA connection is available, use it
|
||||||
if (((conn & 2) != 0) && (meshcentral.mpsserver.ciraConnections[nodeid] != null)) {
|
var ciraconn = meshcentral.mpsserver.GetConnectionToNode(nodeid, null, true); // Request an OOB connection
|
||||||
|
if (ciraconn != null) {
|
||||||
Debug(1, 'Opening Intel AMT CIRA transport connection to ' + nodeid + '.');
|
Debug(1, 'Opening Intel AMT CIRA transport connection to ' + nodeid + '.');
|
||||||
|
|
||||||
var ciraconn = meshcentral.mpsserver.ciraConnections[nodeid];
|
|
||||||
|
|
||||||
// Compute target port, look at the CIRA port mappings, if non-TLS is allowed, use that, if not use TLS
|
// Compute target port, look at the CIRA port mappings, if non-TLS is allowed, use that, if not use TLS
|
||||||
var port = 16995;
|
var port = 16995;
|
||||||
if (ciraconn.tag.boundPorts.indexOf(16994) >= 0) port = 16994; // RELEASE: Always use non-TLS mode if available within CIRA
|
if (ciraconn.tag.boundPorts.indexOf(16994) >= 0) port = 16994; // RELEASE: Always use non-TLS mode if available within CIRA
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
// Construct a MeshServer object
|
// Construct a MeshServer object
|
||||||
var CreateWsmanComm = function (host, port, user, pass, tls, tlsoptions, transportServer) {
|
var CreateWsmanComm = function (host, port, user, pass, tls, tlsoptions, ciraConnection) {
|
||||||
//console.log('CreateWsmanComm', host, port, user, pass, tls, tlsoptions);
|
//console.log('CreateWsmanComm', host, port, user, pass, tls, tlsoptions);
|
||||||
|
|
||||||
var obj = {};
|
var obj = {};
|
||||||
@ -38,7 +38,7 @@ var CreateWsmanComm = function (host, port, user, pass, tls, tlsoptions, transpo
|
|||||||
obj.pass = pass;
|
obj.pass = pass;
|
||||||
obj.xtls = tls;
|
obj.xtls = tls;
|
||||||
obj.xtlsoptions = tlsoptions;
|
obj.xtlsoptions = tlsoptions;
|
||||||
obj.transportServer = transportServer; // This can be a CIRA or APF server, if null, local sockets are used as transport.
|
obj.ciraConnection = ciraConnection; // This can be a CIRA or APF server, if null, local sockets are used as transport.
|
||||||
obj.xtlsFingerprint;
|
obj.xtlsFingerprint;
|
||||||
obj.xtlsCertificate = null;
|
obj.xtlsCertificate = null;
|
||||||
obj.xtlsCheck = 0; // 0 = No TLS, 1 = CA Checked, 2 = Pinned, 3 = Untrusted
|
obj.xtlsCheck = 0; // 0 = No TLS, 1 = CA Checked, 2 = Pinned, 3 = Untrusted
|
||||||
@ -166,9 +166,9 @@ var CreateWsmanComm = function (host, port, user, pass, tls, tlsoptions, transpo
|
|||||||
obj.socketState = 1;
|
obj.socketState = 1;
|
||||||
obj.kerberosDone = 0;
|
obj.kerberosDone = 0;
|
||||||
|
|
||||||
if (obj.transportServer != null) {
|
if (obj.ciraConnection != null) {
|
||||||
// Setup a new channel using the transport server (CIRA or APF)
|
// Setup a new channel using the CIRA/Relay/LMS connection
|
||||||
obj.socket = obj.transportServer.SetupChannelToNode(obj.host, obj.port);
|
obj.socket = obj.ciraConnection.SetupChannel(obj.port);
|
||||||
if (obj.socket == null) {
|
if (obj.socket == null) {
|
||||||
try { obj.xxOnSocketClosed(); } catch (e) { }
|
try { obj.xxOnSocketClosed(); } catch (e) { }
|
||||||
} else {
|
} else {
|
||||||
@ -229,7 +229,7 @@ var CreateWsmanComm = function (host, port, user, pass, tls, tlsoptions, transpo
|
|||||||
obj.xxOnSocketConnected = function () {
|
obj.xxOnSocketConnected = function () {
|
||||||
if (obj.socket == null) return;
|
if (obj.socket == null) return;
|
||||||
// check TLS certificate for webrelay and direct only
|
// check TLS certificate for webrelay and direct only
|
||||||
if ((obj.transportServer == null) && (obj.xtls == 1)) {
|
if ((obj.ciraConnection == null) && (obj.xtls == 1)) {
|
||||||
obj.xtlsCertificate = obj.socket.getPeerCertificate();
|
obj.xtlsCertificate = obj.socket.getPeerCertificate();
|
||||||
|
|
||||||
// ###BEGIN###{Certificates}
|
// ###BEGIN###{Certificates}
|
||||||
@ -348,7 +348,7 @@ var CreateWsmanComm = function (host, port, user, pass, tls, tlsoptions, transpo
|
|||||||
if (isNaN(s)) s = 500;
|
if (isNaN(s)) s = 500;
|
||||||
if (s == 401 && ++(obj.authcounter) < 3) {
|
if (s == 401 && ++(obj.authcounter) < 3) {
|
||||||
obj.challengeParams = obj.parseDigest(header['www-authenticate']); // Set the digest parameters, after this, the socket will close and we will auto-retry
|
obj.challengeParams = obj.parseDigest(header['www-authenticate']); // Set the digest parameters, after this, the socket will close and we will auto-retry
|
||||||
if (obj.transportServer == null) { obj.socket.end(); } else { obj.socket.close(); }
|
if (obj.ciraConnection == null) { obj.socket.end(); } else { obj.socket.close(); }
|
||||||
} else {
|
} else {
|
||||||
var r = obj.pendingAjaxCall.shift();
|
var r = obj.pendingAjaxCall.shift();
|
||||||
if (r == null || r.length < 1) { console.log("pendingAjaxCall error, " + r); return; }
|
if (r == null || r.length < 1) { console.log("pendingAjaxCall error, " + r); return; }
|
||||||
@ -365,7 +365,7 @@ var CreateWsmanComm = function (host, port, user, pass, tls, tlsoptions, transpo
|
|||||||
//obj.Debug("xxOnSocketClosed");
|
//obj.Debug("xxOnSocketClosed");
|
||||||
obj.socketState = 0;
|
obj.socketState = 0;
|
||||||
if (obj.socket != null) {
|
if (obj.socket != null) {
|
||||||
if (obj.transportServer == null) { obj.socket.destroy(); } else { obj.socket.close(); }
|
if (obj.ciraConnection == null) { obj.socket.destroy(); } else { obj.socket.close(); }
|
||||||
obj.socket = null;
|
obj.socket = null;
|
||||||
}
|
}
|
||||||
if (obj.pendingAjaxCall.length > 0) {
|
if (obj.pendingAjaxCall.length > 0) {
|
||||||
@ -376,7 +376,7 @@ var CreateWsmanComm = function (host, port, user, pass, tls, tlsoptions, transpo
|
|||||||
|
|
||||||
obj.xxOnSocketTimeout = function () {
|
obj.xxOnSocketTimeout = function () {
|
||||||
if (obj.socket != null) {
|
if (obj.socket != null) {
|
||||||
if (obj.transportServer == null) { obj.socket.destroy(); } else { obj.socket.close(); }
|
if (obj.ciraConnection == null) { obj.socket.destroy(); } else { obj.socket.close(); }
|
||||||
obj.socket = null;
|
obj.socket = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
170
amtmanager.js
170
amtmanager.js
@ -41,67 +41,72 @@ module.exports.CreateAmtManager = function(parent) {
|
|||||||
// Handle server events
|
// Handle server events
|
||||||
// TODO: Only manage devices with connections to this server. In a multi-server setup, we don't want multiple managers talking to the same device.
|
// TODO: Only manage devices with connections to this server. In a multi-server setup, we don't want multiple managers talking to the same device.
|
||||||
obj.HandleEvent = function (source, event, ids, id) {
|
obj.HandleEvent = function (source, event, ids, id) {
|
||||||
// React to nodes connecting and disconnecting
|
switch (event.action) {
|
||||||
if (event.action == 'nodeconnect') {
|
case 'nodeconnect': { // React to nodes connecting and disconnecting
|
||||||
if ((event.conn & 14) != 0) { // connectType: Bitmask, 1 = MeshAgent, 2 = Intel AMT CIRA, 4 = Intel AMT local, 8 = Intel AMT Relay, 16 = MQTT
|
// See if we have an existing device we manage
|
||||||
//if ((event.conn & 2) == 0) return // Debug: Only look at CIRA connections
|
|
||||||
|
|
||||||
// We have an OOB connection to Intel AMT, update our information
|
|
||||||
var dev = obj.amtDevices[event.nodeid];
|
var dev = obj.amtDevices[event.nodeid];
|
||||||
if (dev == null) { obj.amtDevices[event.nodeid] = dev = { conn: event.conn }; fetchIntelAmtInformation(event.nodeid); } else { dev.conn = event.conn; }
|
|
||||||
/*
|
|
||||||
} else if (((event.conn & 1) != 0) && (parent.webserver != null)) {
|
|
||||||
// We have an agent connection without OOB, check if this agent supports Intel AMT
|
|
||||||
var agent = parent.webserver.wsagents[event.nodeid];
|
|
||||||
if ((agent == null) || (agent.agentInfo == null) || (parent.meshAgentsArchitectureNumbers[agent.agentInfo.agentId].amt == false)) { removeDevice(event.nodeid); return; }
|
|
||||||
var dev = obj.amtDevices[event.nodeid];
|
|
||||||
if (dev == null) { obj.amtDevices[event.nodeid] = dev = { conn: event.conn }; fetchIntelAmtInformation(event.nodeid); } else { dev.conn = event.conn; }
|
|
||||||
*/
|
|
||||||
} else {
|
|
||||||
removeDevice(event.nodeid);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// React to node being removed
|
// If the connection type we are using is not longer valid, remove our managed device.
|
||||||
if (event.action == 'removenode') { removeDevice(event.nodeid); }
|
if ((dev != null) && (dev.conntype != null) && ((dev.conntype & event.conn) == 0)) { removeDevice(event.nodeid); dev = null; }
|
||||||
|
|
||||||
// React to node wakeup command, perform Intel AMT wake if possible
|
// Create or update a managed device
|
||||||
if ((event.action == 'wakedevices') && (Array.isArray(event.nodeids))) {
|
if ((event.conn & 14) != 0) { // connectType: Bitmask, 1 = MeshAgent, 2 = Intel AMT CIRA, 4 = Intel AMT local, 8 = Intel AMT Relay, 16 = MQTT
|
||||||
for (var i in event.nodeids) { performPowerAction(event.nodeids[i], 2); }
|
|
||||||
}
|
|
||||||
|
|
||||||
// React to changes in a node
|
|
||||||
if (event.action == 'changenode') {
|
|
||||||
if (event.amtchange === 1) {
|
|
||||||
// A change occured in the Intel AMT credentials, we need to reset the connection
|
|
||||||
removeDevice(event.nodeid);
|
|
||||||
|
|
||||||
// Check if the agent is connected
|
|
||||||
var constate = parent.GetConnectivityState(event.nodeid);
|
|
||||||
if (constate == null) return; // No OOB connectivity, exit now.
|
|
||||||
|
|
||||||
if ((constate & 14) != 0) { // connectType: Bitmask, 1 = MeshAgent, 2 = Intel AMT CIRA, 4 = Intel AMT local, 8 = Intel AMT Relay, 16 = MQTT
|
|
||||||
// We have an OOB connection to Intel AMT, update our information
|
// We have an OOB connection to Intel AMT, update our information
|
||||||
var dev = obj.amtDevices[event.nodeid];
|
if (dev == null) {
|
||||||
if (dev == null) { obj.amtDevices[event.nodeid] = dev = { conn: constate }; fetchIntelAmtInformation(event.nodeid); } else { dev.conn = constate; }
|
obj.amtDevices[event.nodeid] = dev = { conn: event.conn }; fetchIntelAmtInformation(event.nodeid);
|
||||||
|
} else {
|
||||||
|
dev.conn = event.conn;
|
||||||
|
}
|
||||||
} else if (((event.conn & 1) != 0) && (parent.webserver != null)) {
|
} else if (((event.conn & 1) != 0) && (parent.webserver != null)) {
|
||||||
// We have an agent connection without OOB, check if this agent supports Intel AMT
|
// We have an agent connection without CIRA/Local/Relay, check if this agent supports Intel AMT
|
||||||
var agent = parent.webserver.wsagents[event.nodeid];
|
var agent = parent.webserver.wsagents[event.nodeid];
|
||||||
if ((agent == null) || (agent.agentInfo == null) || (parent.meshAgentsArchitectureNumbers[agent.agentInfo.agentId].amt == false)) { removeDevice(event.nodeid); return; }
|
if ((agent != null) && (agent.agentInfo != null) && (parent.meshAgentsArchitectureNumbers[agent.agentInfo.agentId].amt == true)) {
|
||||||
var dev = obj.amtDevices[event.nodeid];
|
// We could turn on LMS relay at this point.
|
||||||
if (dev == null) { obj.amtDevices[event.nodeid] = dev = { conn: constate }; fetchIntelAmtInformation(event.nodeid); } else { dev.conn = constate; }
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
var dev = obj.amtDevices[event.nodeid];
|
|
||||||
if (dev != null) {
|
|
||||||
var amtchange = 0;
|
|
||||||
if (dev.name != event.node.name) { dev.name = event.node.name; }
|
|
||||||
if (dev.host != event.node.host) {
|
|
||||||
dev.host = event.node.host;
|
|
||||||
// The host has changed, if we are connected to this device locally, we need to reset.
|
|
||||||
if ((dev.conn & 4) != 0) { removeDevice(dev.nodeid); return; } // We are going to wait for the AMT scanned to find this device again.
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'removenode': { // React to node being removed
|
||||||
|
removeDevice(event.nodeid);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'wakedevices': { // React to node wakeup command, perform Intel AMT wake if possible
|
||||||
|
if (Array.isArray(event.nodeids)) { for (var i in event.nodeids) { performPowerAction(event.nodeids[i], 2); } }
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'changenode': { // React to changes in a node
|
||||||
|
if (event.amtchange === 1) {
|
||||||
|
// A change occured in the Intel AMT credentials, we need to reset the connection
|
||||||
|
removeDevice(event.nodeid);
|
||||||
|
|
||||||
|
// Check if the agent is connected
|
||||||
|
var constate = parent.GetConnectivityState(event.nodeid);
|
||||||
|
if (constate == null) return; // No OOB connectivity, exit now.
|
||||||
|
|
||||||
|
if ((constate & 14) != 0) { // connectType: Bitmask, 1 = MeshAgent, 2 = Intel AMT CIRA, 4 = Intel AMT local, 8 = Intel AMT Relay, 16 = MQTT
|
||||||
|
// We have an OOB connection to Intel AMT, update our information
|
||||||
|
var dev = obj.amtDevices[event.nodeid];
|
||||||
|
if (dev == null) { obj.amtDevices[event.nodeid] = dev = { conn: constate }; fetchIntelAmtInformation(event.nodeid); } else { dev.conn = constate; }
|
||||||
|
} else if (((event.conn & 1) != 0) && (parent.webserver != null)) {
|
||||||
|
// We have an agent connection without OOB, check if this agent supports Intel AMT
|
||||||
|
var agent = parent.webserver.wsagents[event.nodeid];
|
||||||
|
if ((agent == null) || (agent.agentInfo == null) || (parent.meshAgentsArchitectureNumbers[agent.agentInfo.agentId].amt == false)) { removeDevice(event.nodeid); return; }
|
||||||
|
var dev = obj.amtDevices[event.nodeid];
|
||||||
|
if (dev == null) { obj.amtDevices[event.nodeid] = dev = { conn: constate }; fetchIntelAmtInformation(event.nodeid); } else { dev.conn = constate; }
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
var dev = obj.amtDevices[event.nodeid];
|
||||||
|
if (dev != null) {
|
||||||
|
var amtchange = 0;
|
||||||
|
if (dev.name != event.node.name) { dev.name = event.node.name; }
|
||||||
|
if (dev.host != event.node.host) {
|
||||||
|
dev.host = event.node.host;
|
||||||
|
// The host has changed, if we are connected to this device locally, we need to reset.
|
||||||
|
if ((dev.conn & 4) != 0) { removeDevice(dev.nodeid); return; } // We are going to wait for the AMT scanned to find this device again.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -186,23 +191,24 @@ module.exports.CreateAmtManager = function(parent) {
|
|||||||
if (node.host) { dev.host = node.host.toLowerCase(); }
|
if (node.host) { dev.host = node.host.toLowerCase(); }
|
||||||
dev.meshid = node.meshid;
|
dev.meshid = node.meshid;
|
||||||
dev.intelamt = node.intelamt;
|
dev.intelamt = node.intelamt;
|
||||||
attemptInitialContact(nodeid, dev);
|
attemptInitialContact(dev);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Attempt to perform initial contact with Intel AMT
|
// Attempt to perform initial contact with Intel AMT
|
||||||
function attemptInitialContact(nodeid, dev) {
|
function attemptInitialContact(dev) {
|
||||||
if (dev == null) { dev = obj.amtDevices[nodeid]; }
|
|
||||||
if (dev == null) return;
|
if (dev == null) return;
|
||||||
|
//console.log('attemptInitialContact', dev.name);
|
||||||
|
|
||||||
if ((dev.acctry == null) && ((typeof dev.intelamt.user != 'string') || (typeof dev.intelamt.pass != 'string'))) {
|
if ((dev.acctry == null) && ((typeof dev.intelamt.user != 'string') || (typeof dev.intelamt.pass != 'string'))) {
|
||||||
if (obj.amtAdminAccounts.length > 0) { dev.acctry = 0; } else { return; }
|
if (obj.amtAdminAccounts.length > 0) { dev.acctry = 0; } else { return; }
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle the case where the Intel AMT CIRA is connected (conn & 2)
|
// Handle the case where the Intel AMT CIRA is connected (conn & 2)
|
||||||
|
// In this connection type, we look at the port bindings to see if we need to do TLS or not.
|
||||||
if ((dev.conn & 2) != 0) {
|
if ((dev.conn & 2) != 0) {
|
||||||
// Check to see if CIRA is connected on this server.
|
// Check to see if CIRA is connected on this server.
|
||||||
var ciraconn = parent.mpsserver.ciraConnections[dev.nodeid];
|
var ciraconn = parent.mpsserver.GetConnectionToNode(dev.nodeid, null, 0); // Select the CIRA connection
|
||||||
if ((ciraconn == null) || (ciraconn.tag == null) || (ciraconn.tag.boundPorts == null)) { removeDevice(dev.nodeid); return; } // CIRA connection is not on this server, no need to deal with this device anymore.
|
if ((ciraconn == null) || (ciraconn.tag == null) || (ciraconn.tag.boundPorts == null)) { removeDevice(dev.nodeid); return; } // CIRA connection is not on this server, no need to deal with this device anymore.
|
||||||
|
|
||||||
// See what user/pass to try.
|
// See what user/pass to try.
|
||||||
@ -218,11 +224,12 @@ module.exports.CreateAmtManager = function(parent) {
|
|||||||
// Connect now
|
// Connect now
|
||||||
//console.log('CIRA-Connect', (dotls == 1)?"TLS":"NoTLS", dev.name, dev.host, user, pass);
|
//console.log('CIRA-Connect', (dotls == 1)?"TLS":"NoTLS", dev.name, dev.host, user, pass);
|
||||||
var comm;
|
var comm;
|
||||||
|
dotls = 0; // TODO: We don't support TLS with CIRA/Relay/LMS connections yet. Remove this when we do.
|
||||||
if (dotls == 1) {
|
if (dotls == 1) {
|
||||||
comm = CreateWsmanComm(dev.nodeid, 16993, user, pass, 1, null, parent.mpsserver); // Perform TLS
|
comm = CreateWsmanComm(dev.nodeid, 16993, user, pass, 1, null, ciraconn); // Perform TLS
|
||||||
comm.xtlsFingerprint = 0; // Perform no certificate checking
|
comm.xtlsFingerprint = 0; // Perform no certificate checking
|
||||||
} else {
|
} else {
|
||||||
comm = CreateWsmanComm(dev.nodeid, 16992, user, pass, 0, null, parent.mpsserver); // No TLS
|
comm = CreateWsmanComm(dev.nodeid, 16992, user, pass, 0, null, ciraconn); // No TLS
|
||||||
}
|
}
|
||||||
var wsstack = WsmanStackCreateService(comm);
|
var wsstack = WsmanStackCreateService(comm);
|
||||||
dev.amtstack = AmtStackCreateService(wsstack);
|
dev.amtstack = AmtStackCreateService(wsstack);
|
||||||
@ -234,12 +241,45 @@ module.exports.CreateAmtManager = function(parent) {
|
|||||||
return; // If CIRA is connected, don't try any other methods.
|
return; // If CIRA is connected, don't try any other methods.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handle the case where the Intel AMT relay is connected (conn & 8)
|
||||||
|
if ((dev.conn & 8) != 0) {
|
||||||
|
// Check to see if CIRA is connected on this server.
|
||||||
|
var ciraconn = parent.mpsserver.GetConnectionToNode(dev.nodeid, null, 1); // Select a relay connection
|
||||||
|
if ((ciraconn == null) || (ciraconn.tag == null) || (ciraconn.tag.boundPorts == null)) { removeDevice(dev.nodeid); return; } // CIRA connection is not on this server, no need to deal with this device anymore.
|
||||||
|
|
||||||
|
// See what user/pass to try.
|
||||||
|
var user = null, pass = null;
|
||||||
|
if (dev.acctry == null) { user = dev.intelamt.user; pass = dev.intelamt.pass; } else { user = obj.amtAdminAccounts[dev.acctry].user; pass = obj.amtAdminAccounts[dev.acctry].pass; }
|
||||||
|
|
||||||
|
// Connect now
|
||||||
|
var comm;
|
||||||
|
dev.tlsfail = true; // TODO: We don't support TLS with CIRA/Relay/LMS connections yet. Remove this when we do.
|
||||||
|
if (dev.tlsfail !== true) {
|
||||||
|
//console.log('Relay-Connect', "TLS", dev.name, dev.host, user, pass);
|
||||||
|
comm = CreateWsmanComm(dev.nodeid, 16993, user, pass, 1, null, ciraconn); // Perform TLS
|
||||||
|
comm.xtlsFingerprint = 0; // Perform no certificate checking
|
||||||
|
} else {
|
||||||
|
//console.log('Relay-Connect', "NoTLS", dev.name, dev.host, user, pass);
|
||||||
|
comm = CreateWsmanComm(dev.nodeid, 16992, user, pass, 0, null, ciraconn); // No TLS
|
||||||
|
}
|
||||||
|
var wsstack = WsmanStackCreateService(comm);
|
||||||
|
dev.amtstack = AmtStackCreateService(wsstack);
|
||||||
|
dev.amtstack.dev = dev;
|
||||||
|
obj.activeLocalConnections[dev.host] = dev;
|
||||||
|
dev.amtstack.BatchEnum(null, ['*AMT_GeneralSettings', '*IPS_HostBasedSetupService'], attemptLocalConnectResponse);
|
||||||
|
dev.conntype = 8; // Relay
|
||||||
|
|
||||||
|
return; // If relay is connected, don't try any other methods.
|
||||||
|
}
|
||||||
|
|
||||||
// Handle the case where the Intel AMT local scanner found the device (conn & 4)
|
// Handle the case where the Intel AMT local scanner found the device (conn & 4)
|
||||||
if (((dev.conn & 4) != 0) && (typeof dev.host == 'string')) {
|
if (((dev.conn & 4) != 0) && (typeof dev.host == 'string')) {
|
||||||
// Since we don't allow two or more connections to the same host, check if a pending connection is active.
|
// Since we don't allow two or more connections to the same host, check if a pending connection is active.
|
||||||
if (obj.activeLocalConnections[dev.host] != null) {
|
if (obj.activeLocalConnections[dev.host] != null) {
|
||||||
// Active connection, hold and try later.
|
// Active connection, hold and try later.
|
||||||
setTimeout(function () { attemptInitialContact(nodeid); }, 5000);
|
var tryAgainFunc = function tryAgainFunc() { if (obj.amtDevices[tryAgainFunc.dev.nodeid] != null) { attemptInitialContact(tryAgainFunc.dev); } }
|
||||||
|
tryAgainFunc.dev = dev;
|
||||||
|
setTimeout(tryAgainFunc, 5000);
|
||||||
} else {
|
} else {
|
||||||
// No active connections, see what user/pass to try.
|
// No active connections, see what user/pass to try.
|
||||||
var user = null, pass = null;
|
var user = null, pass = null;
|
||||||
@ -248,11 +288,11 @@ module.exports.CreateAmtManager = function(parent) {
|
|||||||
// Connect now
|
// Connect now
|
||||||
var comm;
|
var comm;
|
||||||
if (dev.tlsfail !== true) {
|
if (dev.tlsfail !== true) {
|
||||||
//console.log('Connect', "TLS", dev.name, dev.host, user, pass);
|
//console.log('Direct-Connect', "TLS", dev.name, dev.host, user, pass);
|
||||||
comm = CreateWsmanComm(dev.host, 16993, user, pass, 1); // Always try with TLS first
|
comm = CreateWsmanComm(dev.host, 16993, user, pass, 1); // Always try with TLS first
|
||||||
comm.xtlsFingerprint = 0; // Perform no certificate checking
|
comm.xtlsFingerprint = 0; // Perform no certificate checking
|
||||||
} else {
|
} else {
|
||||||
//console.log('Connect', "NoTLS", dev.name, dev.host, user, pass);
|
//console.log('Direct-Connect', "NoTLS", dev.name, dev.host, user, pass);
|
||||||
comm = CreateWsmanComm(dev.host, 16992, user, pass, 0); // Try without TLS
|
comm = CreateWsmanComm(dev.host, 16992, user, pass, 0); // Try without TLS
|
||||||
}
|
}
|
||||||
var wsstack = WsmanStackCreateService(comm);
|
var wsstack = WsmanStackCreateService(comm);
|
||||||
@ -301,11 +341,11 @@ module.exports.CreateAmtManager = function(parent) {
|
|||||||
// We got a bad response
|
// We got a bad response
|
||||||
if ((dev.conntype == 1) && (dev.tlsfail !== true) && (status == 408)) {
|
if ((dev.conntype == 1) && (dev.tlsfail !== true) && (status == 408)) {
|
||||||
// TLS error on a local connection, try again without TLS
|
// TLS error on a local connection, try again without TLS
|
||||||
dev.tlsfail = true; attemptInitialContact(dev.nodeid, dev); return;
|
dev.tlsfail = true; attemptInitialContact(dev); return;
|
||||||
} else if (status == 401) {
|
} else if (status == 401) {
|
||||||
// Authentication error, see if we can use alternative credentials
|
// Authentication error, see if we can use alternative credentials
|
||||||
if ((dev.acctry == null) && (obj.amtAdminAccounts.length > 0)) { dev.acctry = 0; attemptInitialContact(dev.nodeid, dev); return; }
|
if ((dev.acctry == null) && (obj.amtAdminAccounts.length > 0)) { dev.acctry = 0; attemptInitialContact(dev); return; }
|
||||||
if ((dev.acctry != null) && (obj.amtAdminAccounts.length > (dev.acctry + 1))) { dev.acctry++; attemptInitialContact(dev.nodeid, dev); return; }
|
if ((dev.acctry != null) && (obj.amtAdminAccounts.length > (dev.acctry + 1))) { dev.acctry++; attemptInitialContact(dev); return; }
|
||||||
|
|
||||||
// We are unable to authenticate to this device, clear Intel AMT credentials.
|
// We are unable to authenticate to this device, clear Intel AMT credentials.
|
||||||
ClearDeviceCredentials(dev);
|
ClearDeviceCredentials(dev);
|
||||||
|
@ -164,8 +164,8 @@ module.exports.CreateAmtScanner = function (parent) {
|
|||||||
if (err == null && docs.length > 0) {
|
if (err == null && docs.length > 0) {
|
||||||
for (var i in docs) {
|
for (var i in docs) {
|
||||||
var doc = docs[i], host = doc.host.toLowerCase();
|
var doc = docs[i], host = doc.host.toLowerCase();
|
||||||
const ciraConnection = obj.parent.mpsserver ? obj.parent.mpsserver.ciraConnections[doc._id] : null;
|
const ciraConnections = obj.parent.mpsserver ? obj.parent.mpsserver.GetConnectionToNode(doc._id, null, true) : null; // See if any OOB connections are present
|
||||||
if ((host != '127.0.0.1') && (host != '::1') && (host.toLowerCase() != 'localhost') && (ciraConnection == null)) {
|
if ((host != '127.0.0.1') && (host != '::1') && (host.toLowerCase() != 'localhost') && (ciraConnections == null)) {
|
||||||
var scaninfo = obj.scanTable[doc._id];
|
var scaninfo = obj.scanTable[doc._id];
|
||||||
if (scaninfo == undefined) {
|
if (scaninfo == undefined) {
|
||||||
var tag = obj.nextTag++;
|
var tag = obj.nextTag++;
|
||||||
|
@ -79,7 +79,7 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) {
|
|||||||
const state = parent.parent.GetConnectivityState(obj.dbNodeKey);
|
const state = parent.parent.GetConnectivityState(obj.dbNodeKey);
|
||||||
if ((state != null) && (state.connectivity != null)) {
|
if ((state != null) && (state.connectivity != null)) {
|
||||||
if ((state.connectivity & 1) != 0) { parent.wsagents[obj.dbNodeKey].close(); } // Disconnect mesh agent
|
if ((state.connectivity & 1) != 0) { parent.wsagents[obj.dbNodeKey].close(); } // Disconnect mesh agent
|
||||||
if ((state.connectivity & 2) != 0) { parent.parent.mpsserver.close(parent.parent.mpsserver.ciraConnections[obj.dbNodeKey]); } // Disconnect CIRA connection
|
if ((state.connectivity & 2) != 0) { parent.parent.mpsserver.closeAllForNode(obj.dbNodeKey); } // Disconnect CIRA connection
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Update the last connect time
|
// Update the last connect time
|
||||||
|
@ -1422,7 +1422,10 @@ function CreateMeshCentralServer(config, args) {
|
|||||||
rs: obj.webserver.relaySessionCount
|
rs: obj.webserver.relaySessionCount
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
if (obj.mpsserver != null) { data.conn.am = Object.keys(obj.mpsserver.ciraConnections).length; }
|
if (obj.mpsserver != null) {
|
||||||
|
data.conn.am = 0;
|
||||||
|
for (var i in obj.mpsserver.ciraConnections) { data.conn.am += obj.mpsserver.ciraConnections[i].length; }
|
||||||
|
}
|
||||||
if (obj.firstStats === true) { delete obj.firstStats; data.first = true; }
|
if (obj.firstStats === true) { delete obj.firstStats; data.first = true; }
|
||||||
obj.db.SetServerStats(data); // Save the stats to the database
|
obj.db.SetServerStats(data); // Save the stats to the database
|
||||||
obj.DispatchEvent(['*'], obj, { action: 'servertimelinestats', data: data }); // Event the server stats
|
obj.DispatchEvent(['*'], obj, { action: 'servertimelinestats', data: data }); // Event the server stats
|
||||||
|
30
meshuser.js
30
meshuser.js
@ -406,7 +406,10 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
|
|||||||
RelayCount: Object.keys(parent.wsrelays).length
|
RelayCount: Object.keys(parent.wsrelays).length
|
||||||
};
|
};
|
||||||
if (parent.relaySessionErrorCount != 0) { serverStats.RelayErrors = parent.relaySessionErrorCount; }
|
if (parent.relaySessionErrorCount != 0) { serverStats.RelayErrors = parent.relaySessionErrorCount; }
|
||||||
if (parent.parent.mpsserver != null) { serverStats.ConnectedIntelAMT = Object.keys(parent.parent.mpsserver.ciraConnections).length; }
|
if (parent.parent.mpsserver != null) {
|
||||||
|
serverStats.ConnectedIntelAMT = 0;
|
||||||
|
for (var i in parent.parent.mpsserver.ciraConnections) { serverStats.ConnectedIntelAMT += parent.parent.mpsserver.ciraConnections[i].length; }
|
||||||
|
}
|
||||||
|
|
||||||
// Take a look at agent errors
|
// Take a look at agent errors
|
||||||
var agentstats = parent.getAgentStats();
|
var agentstats = parent.getAgentStats();
|
||||||
@ -657,7 +660,9 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
|
|||||||
docs[i].conn = state.connectivity;
|
docs[i].conn = state.connectivity;
|
||||||
docs[i].pwr = state.powerState;
|
docs[i].pwr = state.powerState;
|
||||||
if ((state.connectivity & 1) != 0) { var agent = parent.wsagents[docs[i]._id]; if (agent != null) { docs[i].agct = agent.connectTime; } }
|
if ((state.connectivity & 1) != 0) { var agent = parent.wsagents[docs[i]._id]; if (agent != null) { docs[i].agct = agent.connectTime; } }
|
||||||
if ((state.connectivity & 2) != 0) { var cira = parent.parent.mpsserver.ciraConnections[docs[i]._id]; if (cira != null) { docs[i].cict = cira.tag.connectTime; } }
|
|
||||||
|
// Use the connection time of the CIRA/Relay connection
|
||||||
|
if ((state.connectivity & 2) != 0) { var cira = parent.parent.mpsserver.GetConnectionToNode(docs[i]._id, null, true); if (cira != null) { docs[i].cict = cira[0].tag.connectTime; } }
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compress the meshid's
|
// Compress the meshid's
|
||||||
@ -1062,6 +1067,21 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case 'mps': { // List all MPS connections and types.
|
||||||
|
if (parent.parent.mpsserver == null) {
|
||||||
|
r = 'MPS not enabled.';
|
||||||
|
} else {
|
||||||
|
const connectionTypes = ['CIRA', 'Relay', 'LMS'];
|
||||||
|
for (var nodeid in parent.parent.mpsserver.ciraConnections) {
|
||||||
|
r += nodeid;
|
||||||
|
var connections = parent.parent.mpsserver.ciraConnections[nodeid];
|
||||||
|
for (var i in connections) { r += ', ' + connectionTypes[connections[i].tag.connType]; }
|
||||||
|
r += '\r\n';
|
||||||
|
}
|
||||||
|
if (r == '') { r = 'MPS has not connections.'; }
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
case 'dbstats': {
|
case 'dbstats': {
|
||||||
parent.parent.db.getStats(function (stats) {
|
parent.parent.db.getStats(function (stats) {
|
||||||
var r2 = '';
|
var r2 = '';
|
||||||
@ -3458,7 +3478,9 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
|
|||||||
node.conn = state.connectivity;
|
node.conn = state.connectivity;
|
||||||
node.pwr = state.powerState;
|
node.pwr = state.powerState;
|
||||||
if ((state.connectivity & 1) != 0) { var agent = parent.wsagents[node._id]; if (agent != null) { node.agct = agent.connectTime; } }
|
if ((state.connectivity & 1) != 0) { var agent = parent.wsagents[node._id]; if (agent != null) { node.agct = agent.connectTime; } }
|
||||||
if ((state.connectivity & 2) != 0) { var cira = parent.parent.mpsserver.ciraConnections[node._id]; if (cira != null) { node.cict = cira.tag.connectTime; } }
|
|
||||||
|
// Uuse the connection time of the CIRA/Relay connection
|
||||||
|
if ((state.connectivity & 2) != 0) { var cira = parent.parent.mpsserver.GetConnectionToNode(node._id, null, true); if (cira != null) { node.cict = cira[0].tag.connectTime; } }
|
||||||
}
|
}
|
||||||
|
|
||||||
// Event the node change
|
// Event the node change
|
||||||
@ -3527,7 +3549,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
|
|||||||
var state = parent.parent.GetConnectivityState(nodeid);
|
var state = parent.parent.GetConnectivityState(nodeid);
|
||||||
if ((state != null) && (state.connectivity != null)) {
|
if ((state != null) && (state.connectivity != null)) {
|
||||||
if ((state.connectivity & 1) != 0) { parent.wsagents[nodeid].close(); } // Disconnect mesh agent
|
if ((state.connectivity & 1) != 0) { parent.wsagents[nodeid].close(); } // Disconnect mesh agent
|
||||||
if ((state.connectivity & 2) != 0) { parent.parent.mpsserver.close(parent.parent.mpsserver.ciraConnections[nodeid]); } // Disconnect CIRA connection
|
if ((state.connectivity & 2) != 0) { parent.parent.mpsserver.closeAllForNode(nodeid); } // Disconnect CIRA/Relay/LMS connections
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
170
mpsserver.js
170
mpsserver.js
@ -22,7 +22,7 @@ module.exports.CreateMpsServer = function (parent, db, args, certificates) {
|
|||||||
obj.db = db;
|
obj.db = db;
|
||||||
obj.args = args;
|
obj.args = args;
|
||||||
obj.certificates = certificates;
|
obj.certificates = certificates;
|
||||||
obj.ciraConnections = {}; // NodeID --> Socket
|
obj.ciraConnections = {}; // NodeID --> [ Socket ]
|
||||||
var tlsSessionStore = {}; // Store TLS session information for quick resume.
|
var tlsSessionStore = {}; // Store TLS session information for quick resume.
|
||||||
var tlsSessionStoreCount = 0; // Number of cached TLS session information in store.
|
var tlsSessionStoreCount = 0; // Number of cached TLS session information in store.
|
||||||
const constants = (require('crypto').constants ? require('crypto').constants : require('constants')); // require('constants') is deprecated in Node 11.10, use require('crypto').constants instead.
|
const constants = (require('crypto').constants ? require('crypto').constants : require('constants')); // require('constants') is deprecated in Node 11.10, use require('crypto').constants instead.
|
||||||
@ -79,7 +79,8 @@ module.exports.CreateMpsServer = function (parent, db, args, certificates) {
|
|||||||
KEEPALIVE_REQUEST: 208,
|
KEEPALIVE_REQUEST: 208,
|
||||||
KEEPALIVE_REPLY: 209,
|
KEEPALIVE_REPLY: 209,
|
||||||
KEEPALIVE_OPTIONS_REQUEST: 210,
|
KEEPALIVE_OPTIONS_REQUEST: 210,
|
||||||
KEEPALIVE_OPTIONS_REPLY: 211
|
KEEPALIVE_OPTIONS_REPLY: 211,
|
||||||
|
MESH_CONNECTION_TYPE: 250 // This is a Mesh specific command that instructs the server of the connection type: 1 = Relay, 2 = LMS.
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -142,22 +143,71 @@ module.exports.CreateMpsServer = function (parent, db, args, certificates) {
|
|||||||
var socketErrorCount = 0;
|
var socketErrorCount = 0;
|
||||||
var maxDomainDevicesReached = 0;
|
var maxDomainDevicesReached = 0;
|
||||||
|
|
||||||
// Delay setting the connectivity state by 300ms to allow time for CIRA port mappings to be established
|
// Add a CIRA connection to the connection list
|
||||||
// Report power state as "present" (7) until Intel AMT manager starts polling for power state.
|
function addCiraConnection(socket) {
|
||||||
function delayedSetConnectivityState(meshid, nodeid, connectTime, connType) {
|
// Check if there is already a connection of the same type
|
||||||
if (nodeid.startsWith('*')) return; // Don't set connectivity state for Intel AMT self agent relay
|
var sameType = false, connections = obj.ciraConnections[socket.tag.nodeid];
|
||||||
var f = function setConnFunc() { if (obj.ciraConnections[setConnFunc.nodeid] != null) { obj.parent.SetConnectivityState(setConnFunc.meshid, setConnFunc.nodeid, setConnFunc.connectTime, setConnFunc.connType, 7); } }
|
if (connections != null) { for (var i in connections) { var conn = connections[i]; if (conn.tag.connType === socket.tag.connType) { sameType = true; } } }
|
||||||
f.nodeid = nodeid;
|
|
||||||
f.meshid = meshid;
|
// Add this connection to the connections list
|
||||||
f.connectTime = connectTime;
|
if (connections == null) { obj.ciraConnections[socket.tag.nodeid] = [socket]; } else { obj.ciraConnections[socket.tag.nodeid].push(socket); }
|
||||||
f.connType = connType;
|
if ((socket.tag.connType != 0) && (socket.tag.connType != 1)) return; // If not a CIRA or Relay connection, we don't indicate a connection state change
|
||||||
|
|
||||||
|
// Update connectivity state
|
||||||
|
// Report the new state of a CIRA/Relay/LMS connection after a short delay. This is to wait for the connection to have the bounded ports setup before we advertise this new connection.
|
||||||
|
socket.xxStartHold = 1;
|
||||||
|
var f = function setConnFunc() {
|
||||||
|
delete setConnFunc.socket.xxStartHold;
|
||||||
|
const ciraArray = obj.ciraConnections[setConnFunc.socket.tag.nodeid];
|
||||||
|
if ((ciraArray != null) && ((ciraArray.indexOf(setConnFunc.socket) >= 0))) { // Check if this connection is still present
|
||||||
|
if (setConnFunc.socket.tag.connType == 0) {
|
||||||
|
// Intel AMT CIRA connection. This connection indicates the remote device is present.
|
||||||
|
obj.parent.SetConnectivityState(setConnFunc.socket.tag.meshid, setConnFunc.socket.tag.nodeid, setConnFunc.socket.tag.connectTime, 2, 7); // 7 = Present
|
||||||
|
} else if (setConnFunc.socket.tag.connType == 1) {
|
||||||
|
// Intel AMT Relay connection. This connection does not give any information about the remote device's power state.
|
||||||
|
obj.parent.SetConnectivityState(setConnFunc.socket.tag.meshid, setConnFunc.socket.tag.nodeid, setConnFunc.socket.tag.connectTime, 8, 0); // 0 = Unknown
|
||||||
|
} else if (setConnFunc.socket.tag.connType == 2) {
|
||||||
|
// Intel AMT LMS connection. We don't notify of these connections except telling the Intel AMT manager about them.
|
||||||
|
// TODO: Notify AMT manager
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
f.socket = socket;
|
||||||
setTimeout(f, 300);
|
setTimeout(f, 300);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Remove a CIRA connection from the connection list
|
||||||
|
function removeCiraConnection(socket) {
|
||||||
|
// Remove the connection from the list if present.
|
||||||
|
const ciraArray = obj.ciraConnections[socket.tag.nodeid];
|
||||||
|
if (ciraArray == null) return;
|
||||||
|
var i = ciraArray.indexOf(socket);
|
||||||
|
if (i == -1) return;
|
||||||
|
ciraArray.splice(i, 1);
|
||||||
|
if (ciraArray.length == 0) { delete obj.ciraConnections[socket.tag.nodeid]; } else { obj.ciraConnections[socket.tag.nodeid] = ciraArray; }
|
||||||
|
|
||||||
|
// If we are removing a connection during the hold period, don't clear any state since it was never set.
|
||||||
|
if (socket.xxStartHold == 1) return;
|
||||||
|
|
||||||
|
// Check if there is already a connection of the same type
|
||||||
|
var sameType = false, connections = obj.ciraConnections[socket.tag.nodeid];
|
||||||
|
if (connections != null) { for (var i in connections) { var conn = connections[i]; if (conn.tag.connType === socket.tag.connType) { sameType = true; } } }
|
||||||
|
if (sameType == true) return; // if there is a connection of the same type, don't change the connection state.
|
||||||
|
|
||||||
|
// Update connectivity state
|
||||||
|
if (socket.tag.connType == 0) {
|
||||||
|
obj.parent.ClearConnectivityState(socket.tag.meshid, socket.tag.nodeid, 2); // CIRA
|
||||||
|
} else if (socket.tag.connType == 1) {
|
||||||
|
obj.parent.ClearConnectivityState(socket.tag.meshid, socket.tag.nodeid, 8); // Relay
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Return statistics about this MPS server
|
// Return statistics about this MPS server
|
||||||
obj.getStats = function () {
|
obj.getStats = function () {
|
||||||
|
var ciraConnectionCount = 0;
|
||||||
|
for (var i in obj.ciraConnections) { ciraConnectionCount += obj.ciraConnections[i].length; }
|
||||||
return {
|
return {
|
||||||
ciraConnections: Object.keys(obj.ciraConnections).length,
|
ciraConnections: ciraConnectionCount,
|
||||||
tlsSessionStore: Object.keys(tlsSessionStore).length,
|
tlsSessionStore: Object.keys(tlsSessionStore).length,
|
||||||
connectionCount: connectionCount,
|
connectionCount: connectionCount,
|
||||||
userAuthRequestCount: userAuthRequestCount,
|
userAuthRequestCount: userAuthRequestCount,
|
||||||
@ -224,8 +274,11 @@ module.exports.CreateMpsServer = function (parent, db, args, certificates) {
|
|||||||
|
|
||||||
obj.onWebSocketConnection = function (socket) {
|
obj.onWebSocketConnection = function (socket) {
|
||||||
connectionCount++;
|
connectionCount++;
|
||||||
// connType: 2 = CIRA, 8 = Relay
|
// connType: 0 = CIRA, 1 = Relay, 2 = LMS
|
||||||
socket.tag = { first: true, connType: 2, clientCert: null, accumulator: '', activetunnels: 0, boundPorts: [], websocket: true, socket: socket, host: null, nextchannelid: 4, channels: {}, nextsourceport: 0 };
|
socket.tag = { first: true, connType: 0, clientCert: null, accumulator: '', activetunnels: 0, boundPorts: [], websocket: true, socket: socket, host: null, nextchannelid: 4, channels: {}, nextsourceport: 0 };
|
||||||
|
socket.SetupChannel = function SetupChannel(targetport) { return SetupChannel.parent.SetupChannel(SetupChannel.conn, targetport); }
|
||||||
|
socket.SetupChannel.parent = obj;
|
||||||
|
socket.SetupChannel.conn = socket;
|
||||||
socket.websocket = 1;
|
socket.websocket = 1;
|
||||||
parent.debug('mps', "New CIRA websocket connection");
|
parent.debug('mps', "New CIRA websocket connection");
|
||||||
|
|
||||||
@ -245,8 +298,7 @@ module.exports.CreateMpsServer = function (parent, db, args, certificates) {
|
|||||||
socket.addListener('close', function () {
|
socket.addListener('close', function () {
|
||||||
socketClosedCount++;
|
socketClosedCount++;
|
||||||
parent.debug('mps', "CIRA websocket closed", this.tag.meshid, this.tag.nodeid);
|
parent.debug('mps', "CIRA websocket closed", this.tag.meshid, this.tag.nodeid);
|
||||||
try { delete obj.ciraConnections[socket.tag.nodeid]; } catch (e) { }
|
removeCiraConnection(socket);
|
||||||
if (!this.tag.nodeid.startsWith('*')) { obj.parent.ClearConnectivityState(this.tag.meshid, this.tag.nodeid, this.tag.connType); }
|
|
||||||
});
|
});
|
||||||
|
|
||||||
socket.addListener('error', function (e) {
|
socket.addListener('error', function (e) {
|
||||||
@ -258,12 +310,15 @@ module.exports.CreateMpsServer = function (parent, db, args, certificates) {
|
|||||||
// Called when a new TLS/TCP connection is accepted
|
// Called when a new TLS/TCP connection is accepted
|
||||||
function onConnection(socket) {
|
function onConnection(socket) {
|
||||||
connectionCount++;
|
connectionCount++;
|
||||||
// connType: 2 = CIRA, 8 = Relay
|
// connType: 0 = CIRA, 1 = Relay, 2 = LMS
|
||||||
if (obj.args.mpstlsoffload) {
|
if (obj.args.mpstlsoffload) {
|
||||||
socket.tag = { first: true, connType: 2, clientCert: null, accumulator: '', activetunnels: 0, boundPorts: [], socket: socket, host: null, nextchannelid: 4, channels: {}, nextsourceport: 0 };
|
socket.tag = { first: true, connType: 0, clientCert: null, accumulator: '', activetunnels: 0, boundPorts: [], socket: socket, host: null, nextchannelid: 4, channels: {}, nextsourceport: 0 };
|
||||||
} else {
|
} else {
|
||||||
socket.tag = { first: true, connType: 2, clientCert: socket.getPeerCertificate(true), accumulator: '', activetunnels: 0, boundPorts: [], socket: socket, host: null, nextchannelid: 4, channels: {}, nextsourceport: 0 };
|
socket.tag = { first: true, connType: 0, clientCert: socket.getPeerCertificate(true), accumulator: '', activetunnels: 0, boundPorts: [], socket: socket, host: null, nextchannelid: 4, channels: {}, nextsourceport: 0 };
|
||||||
}
|
}
|
||||||
|
socket.SetupChannel = function SetupChannel(targetport) { return SetupChannel.parent.SetupChannel(SetupChannel.conn, targetport); }
|
||||||
|
socket.SetupChannel.parent = obj;
|
||||||
|
socket.SetupChannel.conn = socket;
|
||||||
socket.setEncoding('binary');
|
socket.setEncoding('binary');
|
||||||
parent.debug('mps', "New CIRA connection");
|
parent.debug('mps', "New CIRA connection");
|
||||||
|
|
||||||
@ -274,8 +329,7 @@ module.exports.CreateMpsServer = function (parent, db, args, certificates) {
|
|||||||
socket.addListener('close', function () {
|
socket.addListener('close', function () {
|
||||||
socketClosedCount++;
|
socketClosedCount++;
|
||||||
parent.debug('mps', 'CIRA connection closed');
|
parent.debug('mps', 'CIRA connection closed');
|
||||||
try { delete obj.ciraConnections[socket.tag.nodeid]; } catch (e) { }
|
removeCiraConnection(socket);
|
||||||
obj.parent.ClearConnectivityState(socket.tag.meshid, socket.tag.nodeid, socket.tag.connType);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
socket.addListener('error', function (e) {
|
socket.addListener('error', function (e) {
|
||||||
@ -382,8 +436,7 @@ module.exports.CreateMpsServer = function (parent, db, args, certificates) {
|
|||||||
obj.parent.DispatchEvent(['*', socket.tag.meshid], obj, { etype: 'node', action: 'addnode', node: parent.webserver.CloneSafeNode(device), msg: change, domain: domainid });
|
obj.parent.DispatchEvent(['*', socket.tag.meshid], obj, { etype: 'node', action: 'addnode', node: parent.webserver.CloneSafeNode(device), msg: change, domain: domainid });
|
||||||
|
|
||||||
// Add the connection to the MPS connection list
|
// Add the connection to the MPS connection list
|
||||||
obj.ciraConnections[socket.tag.nodeid] = socket;
|
addCiraConnection(socket);
|
||||||
delayedSetConnectivityState(socket.tag.meshid, socket.tag.nodeid, socket.tag.connectTime, socket.tag.connType);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
@ -412,8 +465,7 @@ module.exports.CreateMpsServer = function (parent, db, args, certificates) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Add the connection to the MPS connection list
|
// Add the connection to the MPS connection list
|
||||||
obj.ciraConnections[socket.tag.nodeid] = socket;
|
addCiraConnection(socket);
|
||||||
delayedSetConnectivityState(socket.tag.meshid, socket.tag.nodeid, socket.tag.connectTime, socket.tag.connType);
|
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
// This node connected without certificate authentication, use password auth
|
// This node connected without certificate authentication, use password auth
|
||||||
@ -521,8 +573,7 @@ module.exports.CreateMpsServer = function (parent, db, args, certificates) {
|
|||||||
obj.parent.DispatchEvent(['*', socket.tag.meshid], obj, { etype: 'node', action: 'addnode', node: parent.webserver.CloneSafeNode(device), msg: change, domain: mesh.domain });
|
obj.parent.DispatchEvent(['*', socket.tag.meshid], obj, { etype: 'node', action: 'addnode', node: parent.webserver.CloneSafeNode(device), msg: change, domain: mesh.domain });
|
||||||
|
|
||||||
// Add the connection to the MPS connection list
|
// Add the connection to the MPS connection list
|
||||||
obj.ciraConnections[socket.tag.nodeid] = socket;
|
addCiraConnection(socket);
|
||||||
delayedSetConnectivityState(socket.tag.meshid, socket.tag.nodeid, socket.tag.connectTime, socket.tag.connType);
|
|
||||||
SendUserAuthSuccess(socket); // Notify the auth success on the CIRA connection
|
SendUserAuthSuccess(socket); // Notify the auth success on the CIRA connection
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -545,8 +596,7 @@ module.exports.CreateMpsServer = function (parent, db, args, certificates) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Add the connection to the MPS connection list
|
// Add the connection to the MPS connection list
|
||||||
obj.ciraConnections[socket.tag.nodeid] = socket;
|
addCiraConnection(socket);
|
||||||
delayedSetConnectivityState(socket.tag.meshid, socket.tag.nodeid, socket.tag.connectTime, socket.tag.connType);
|
|
||||||
SendUserAuthSuccess(socket); // Notify the auth success on the CIRA connection
|
SendUserAuthSuccess(socket); // Notify the auth success on the CIRA connection
|
||||||
});
|
});
|
||||||
} else if (mesh.mtype == 2) { // If this is a agent mesh, search the mesh for this device UUID
|
} else if (mesh.mtype == 2) { // If this is a agent mesh, search the mesh for this device UUID
|
||||||
@ -584,8 +634,7 @@ module.exports.CreateMpsServer = function (parent, db, args, certificates) {
|
|||||||
socket.tag.connectTime = Date.now();
|
socket.tag.connectTime = Date.now();
|
||||||
|
|
||||||
// Add the connection to the MPS connection list
|
// Add the connection to the MPS connection list
|
||||||
obj.ciraConnections[socket.tag.nodeid] = socket;
|
addCiraConnection(socket);
|
||||||
delayedSetConnectivityState(socket.tag.meshid, socket.tag.nodeid, socket.tag.connectTime, socket.tag.connType);
|
|
||||||
SendUserAuthSuccess(socket); // Notify the auth success on the CIRA connection
|
SendUserAuthSuccess(socket); // Notify the auth success on the CIRA connection
|
||||||
});
|
});
|
||||||
} else { // Unknown mesh type
|
} else { // Unknown mesh type
|
||||||
@ -818,10 +867,17 @@ module.exports.CreateMpsServer = function (parent, db, args, certificates) {
|
|||||||
var ReasonCode = common.ReadInt(data, 1);
|
var ReasonCode = common.ReadInt(data, 1);
|
||||||
disconnectCommandCount++;
|
disconnectCommandCount++;
|
||||||
parent.debug('mpscmd', '--> DISCONNECT', ReasonCode);
|
parent.debug('mpscmd', '--> DISCONNECT', ReasonCode);
|
||||||
try { delete obj.ciraConnections[socket.tag.nodeid]; } catch (e) { }
|
removeCiraConnection(socket);
|
||||||
obj.parent.ClearConnectivityState(socket.tag.meshid, socket.tag.nodeid, socket.tag.connType);
|
|
||||||
return 7;
|
return 7;
|
||||||
}
|
}
|
||||||
|
case APFProtocol.MESH_CONNECTION_TYPE: // This is a Mesh specific command to indicate the connect type.
|
||||||
|
{
|
||||||
|
if (len < 5) return 0;
|
||||||
|
if ((socket.tag.connType == 0) && (socket.tag.SystemId == null)) { // Once set, the connection type can't be changed.
|
||||||
|
socket.tag.connType = common.ReadInt(data, 1); // 0 = CIRA, 1 = Relay, 2 = LMS
|
||||||
|
}
|
||||||
|
return 5;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
parent.debug('mpscmd', '--> Unknown CIRA command: ' + cmd);
|
parent.debug('mpscmd', '--> Unknown CIRA command: ' + cmd);
|
||||||
@ -833,8 +889,14 @@ module.exports.CreateMpsServer = function (parent, db, args, certificates) {
|
|||||||
// Disconnect CIRA tunnel
|
// Disconnect CIRA tunnel
|
||||||
obj.close = function (socket) {
|
obj.close = function (socket) {
|
||||||
try { socket.end(); } catch (e) { }
|
try { socket.end(); } catch (e) { }
|
||||||
try { delete obj.ciraConnections[socket.tag.nodeid]; } catch (e) { }
|
removeCiraConnection(socket);
|
||||||
obj.parent.ClearConnectivityState(socket.tag.meshid, socket.tag.nodeid, socket.tag.connType);
|
};
|
||||||
|
|
||||||
|
// Disconnect all CIRA tunnel for a given NodeId
|
||||||
|
obj.closeAllForNode = function (nodeid) {
|
||||||
|
var connections = obj.ciraConnections[nodeid];
|
||||||
|
if (connections == null) return;
|
||||||
|
for (var i in connections) { obj.close(connections[i]); }
|
||||||
};
|
};
|
||||||
|
|
||||||
function SendServiceAccept(socket, service) {
|
function SendServiceAccept(socket, service) {
|
||||||
@ -926,18 +988,36 @@ module.exports.CreateMpsServer = function (parent, db, args, certificates) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Setup a new channel to a nodeid
|
// Returns a CIRA/Relay/LMS connection to a nodeid, use the best possible connection, CIRA first, Relay second, LMS third.
|
||||||
obj.SetupChannelToNode = function (nodeid, targetport) {
|
// if oob is set to true, don't allow an LMS connection.
|
||||||
var ciraconn = obj.ciraConnections[nodeid];
|
obj.GetConnectionToNode = function (nodeid, targetport, oob) {
|
||||||
if (ciraconn == null) return null;
|
var connectionArray = obj.ciraConnections[nodeid];
|
||||||
return obj.SetupChannel(ciraconn, targetport);
|
if (connectionArray == null) return null;
|
||||||
|
var selectConn = null;
|
||||||
|
// Select the best connection, which is the one with the lowest connType value.
|
||||||
|
for (var i in connectionArray) {
|
||||||
|
var conn = connectionArray[i];
|
||||||
|
if ((oob === true) && (conn.tag.connType == 2)) continue; // If an OOB connection is required, don't allow LMS connections.
|
||||||
|
if ((typeof oob === 'number') && (conn.tag.connType !== oob)) continue; // if OOB specifies an exact connection type, filter on this type.
|
||||||
|
if ((targetport != null) && (conn.tag.boundPorts.indexOf(targetport) == -1)) continue; // This connection does not route to the target port.
|
||||||
|
if ((selectConn == null) || (conn.tag.connType < selectConn.tag.connType)) { selectConn = conn; }
|
||||||
|
}
|
||||||
|
return selectConn;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup a new channel to a nodeid, use the best possible connection, CIRA first, Relay second, LMS third.
|
||||||
|
// if oob is set to true, don't allow an LMS connection.
|
||||||
|
obj.SetupChannelToNode = function (nodeid, targetport, oob) {
|
||||||
|
var conn = obj.GetConnectionToNode(nodeid, targetport, oob);
|
||||||
|
if (conn == null) return null;
|
||||||
|
return obj.SetupChannel(conn, targetport);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Setup a new channel
|
// Setup a new channel
|
||||||
obj.SetupChannel = function (socket, targetport) {
|
obj.SetupChannel = function (socket, targetport) {
|
||||||
var sourceport = (socket.tag.nextsourceport++ % 30000) + 1024;
|
var sourceport = (socket.tag.nextsourceport++ % 30000) + 1024;
|
||||||
var cirachannel = { targetport: targetport, channelid: socket.tag.nextchannelid++, socket: socket, state: 1, sendcredits: 0, amtpendingcredits: 0, amtCiraWindow: 0, ciraWindow: 32768 };
|
var cirachannel = { targetport: targetport, channelid: socket.tag.nextchannelid++, socket: socket, state: 1, sendcredits: 0, amtpendingcredits: 0, amtCiraWindow: 0, ciraWindow: 32768 };
|
||||||
SendChannelOpen(socket, false, cirachannel.channelid, cirachannel.ciraWindow, socket.tag.host, targetport, "1.2.3.4", sourceport);
|
SendChannelOpen(socket, false, cirachannel.channelid, cirachannel.ciraWindow, socket.tag.host, targetport, '1.2.3.4', sourceport);
|
||||||
|
|
||||||
// This function writes data to this CIRA channel
|
// This function writes data to this CIRA channel
|
||||||
cirachannel.write = function (data) {
|
cirachannel.write = function (data) {
|
||||||
@ -1015,8 +1095,12 @@ module.exports.CreateMpsServer = function (parent, db, args, certificates) {
|
|||||||
|
|
||||||
// Change a node to a new meshid, this is called when a node changes groups.
|
// Change a node to a new meshid, this is called when a node changes groups.
|
||||||
obj.changeDeviceMesh = function (nodeid, newMeshId) {
|
obj.changeDeviceMesh = function (nodeid, newMeshId) {
|
||||||
var socket = obj.ciraConnections[nodeid];
|
var connectionArray = obj.ciraConnections[nodeid];
|
||||||
if ((socket != null) && (socket.tag != null)) { socket.tag.meshid = newMeshId; }
|
if (connectionArray == null) return;
|
||||||
|
for (var i in connectionArray) {
|
||||||
|
var socket = connectionArray[i];
|
||||||
|
if ((socket != null) && (socket.tag != null)) { socket.tag.meshid = newMeshId; }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Called when handling incoming HTTP data
|
// Called when handling incoming HTTP data
|
||||||
|
@ -3375,11 +3375,11 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// If Intel AMT CIRA connection is available, use it
|
// If Intel AMT CIRA connection is available, use it
|
||||||
if (((conn & 2) != 0) && (parent.mpsserver.ciraConnections[req.query.host] != null)) {
|
var ciraconn = parent.mpsserver.GetConnectionToNode(req.query.host, null, false);
|
||||||
|
if (ciraconn != null) {
|
||||||
parent.debug('web', 'Opening relay CIRA channel connection to ' + req.query.host + '.');
|
parent.debug('web', 'Opening relay CIRA channel connection to ' + req.query.host + '.');
|
||||||
|
|
||||||
var ciraconn = parent.mpsserver.ciraConnections[req.query.host];
|
// TODO: If ciraconn is a relay connection, we can't detect the TLS state like this.
|
||||||
|
|
||||||
// Compute target port, look at the CIRA port mappings, if non-TLS is allowed, use that, if not use TLS
|
// Compute target port, look at the CIRA port mappings, if non-TLS is allowed, use that, if not use TLS
|
||||||
var port = 16993;
|
var port = 16993;
|
||||||
//if (node.intelamt.tls == 0) port = 16992; // DEBUG: Allow TLS flag to set TLS mode within CIRA
|
//if (node.intelamt.tls == 0) port = 16992; // DEBUG: Allow TLS flag to set TLS mode within CIRA
|
||||||
|
Loading…
Reference in New Issue
Block a user