Partial work for host-based ACM AMT activation.

This commit is contained in:
Ylian Saint-Hilaire 2021-03-03 23:49:53 -08:00
parent cfb9af8609
commit 423daaf19d
11 changed files with 143 additions and 15 deletions

View File

@ -1174,6 +1174,14 @@ function configureJsonControl(data) {
amtMei.on('error', function (e) { settings.apftunnel.sendMeiDeactivationState(1); }); amtMei.on('error', function (e) { settings.apftunnel.sendMeiDeactivationState(1); });
amtMei.unprovision(1, function (status) { settings.apftunnel.sendMeiDeactivationState(status); }); // 0 = Success amtMei.unprovision(1, function (status) { settings.apftunnel.sendMeiDeactivationState(status); }); // 0 = Success
break; break;
case 'startTlsHostConfig': // Request start of host based TLS ACM activation
var amtMeiModule, amtMei;
try { amtMeiModule = require('amt-mei'); amtMei = new amtMeiModule(); } catch (ex) { settings.apftunnel.sendStartTlsHostConfigResponse({ state: -103 }); break; }
amtMei.on('error', function (e) { settings.apftunnel.sendStartTlsHostConfigResponse({ state: -104 }); });
amtMei.startConfigurationHBased(Buffer.from(data.hash, 'hex'), data.hostVpn, data.dnsSuffixList, function (response) {
settings.apftunnel.sendStartTlsHostConfigResponse(response);
});
break;
case 'close': // Close the CIRA-LMS connection case 'close': // Close the CIRA-LMS connection
exit(0); exit(0);
break; break;

View File

@ -1197,6 +1197,11 @@ function handleServerCommand(data) {
amtMei.unprovision(1, function (status) { if (apftunnel) apftunnel.sendMeiDeactivationState(status); }); // 0 = Success amtMei.unprovision(1, function (status) { if (apftunnel) apftunnel.sendMeiDeactivationState(status); }); // 0 = Success
} }
if (data.action == 'close') { try { apftunnel.disconnect(); } catch (e) { } apftunnel = null; } // Close the CIRA-LMS connection if (data.action == 'close') { try { apftunnel.disconnect(); } catch (e) { } apftunnel = null; } // Close the CIRA-LMS connection
if (data.action == 'startTlsHostConfig') { // Request start of host based TLS ACM activation
amt.startConfigurationHBased(Buffer.from(data.hash, 'hex'), data.hostVpn, data.dnsSuffixList, function (response) {
apftunnel.sendStartTlsHostConfigResponse(response);
});
}
} }
apftunnel.onChannelClosed = function () { addAmtEvent('LMS tunnel closed.'); apftunnel = null; } apftunnel.onChannelClosed = function () { addAmtEvent('LMS tunnel closed.'); apftunnel = null; }
try { apftunnel.connect(); } catch (ex) { } try { apftunnel.connect(); } catch (ex) { }

View File

@ -183,6 +183,7 @@ function CreateAPFClient(parent, args) {
obj.updateMeiState = function (state) { SendJsonControl(obj.forwardClient.ws, { action: 'meiState', value: state }); } obj.updateMeiState = function (state) { SendJsonControl(obj.forwardClient.ws, { action: 'meiState', value: state }); }
obj.sendMeiDeactivationState = function (state) { SendJsonControl(obj.forwardClient.ws, { action: 'deactivate', value: state }); } obj.sendMeiDeactivationState = function (state) { SendJsonControl(obj.forwardClient.ws, { action: 'deactivate', value: state }); }
obj.sendStartTlsHostConfigResponse = function (state) { SendJsonControl(obj.forwardClient.ws, { action: 'startTlsHostConfig', value: state }); }
function SendJsonControl(socket, o) { function SendJsonControl(socket, o) {
var data = JSON.stringify(o) var data = JSON.stringify(o)

View File

@ -419,8 +419,8 @@ function amt_heci() {
}, this, callback, optional); }, this, callback, optional);
} }
this.startConfigurationHBased = function startConfigurationHBased(certHash, hostVpn, dnsSuffixList, callback) { this.startConfigurationHBased = function startConfigurationHBased(certHash, hostVpn, dnsSuffixList, func) {
if ((certHash == null) || ((certHash.length != 32) && (certHash.length != 48))) { throw "Bad certHash"; } if ((certHash == null) || ((certHash.length != 32) && (certHash.length != 48))) { func({ status: -101 }); }
var optional = []; var optional = [];
for (var i = 4; i < arguments.length; ++i) { optional.push(arguments[i]); } for (var i = 4; i < arguments.length; ++i) { optional.push(arguments[i]); }
@ -447,8 +447,23 @@ function amt_heci() {
opt.unshift({ status: header.Status }); opt.unshift({ status: header.Status });
} }
fn.apply(this, opt); fn.apply(this, opt);
}, callback, optional); }, func, optional);
} }
} }
module.exports = amt_heci; module.exports = amt_heci;
/*
AMT_STATUS_SUCCESS = 0,
AMT_STATUS_INTERNAL_ERROR = 1,
AMT_STATUS_INVALID_AMT_MODE = 3,
AMT_STATUS_INVALID_MESSAGE_LENGTH = 4,
AMT_STATUS_MAX_LIMIT_REACHED = 23,
AMT_STATUS_INVALID_PARAMETER = 36,
AMT_STATUS_RNG_GENERATION_IN_PROGRESS = 47,
AMT_STATUS_RNG_NOT_READY = 48,
AMT_STATUS_CERTIFICATE_NOT_READY = 49,
AMT_STATUS_INVALID_HANDLE = 2053
AMT_STATUS_NOT_FOUND = 2068,
*/

View File

@ -183,6 +183,7 @@ function CreateAPFClient(parent, args) {
obj.updateMeiState = function (state) { SendJsonControl(obj.forwardClient.ws, { action: 'meiState', value: state }); } obj.updateMeiState = function (state) { SendJsonControl(obj.forwardClient.ws, { action: 'meiState', value: state }); }
obj.sendMeiDeactivationState = function (state) { SendJsonControl(obj.forwardClient.ws, { action: 'deactivate', value: state }); } obj.sendMeiDeactivationState = function (state) { SendJsonControl(obj.forwardClient.ws, { action: 'deactivate', value: state }); }
obj.sendStartTlsHostConfigResponse = function (state) { SendJsonControl(obj.forwardClient.ws, { action: 'startTlsHostConfig', value: state }); }
function SendJsonControl(socket, o) { function SendJsonControl(socket, o) {
var data = JSON.stringify(o) var data = JSON.stringify(o)

View File

@ -157,6 +157,12 @@ function AmtManager(agent, db, isdebug) {
} }
} }
// Start host based ACM activation with TLS
obj.startConfigurationHBased = function startConfigurationHBased(certHash, hostVpn, dnsSuffixList, func) {
if ((amtMei == null) || (amtMeiState < 2)) { if (func != null) { func({ status: -100 }); } return; }
amtMei.startConfigurationHBased(certHash, hostVpn, dnsSuffixList, func);
}
} }
module.exports = AmtManager; module.exports = AmtManager;

View File

@ -419,8 +419,8 @@ function amt_heci() {
}, this, callback, optional); }, this, callback, optional);
} }
this.startConfigurationHBased = function startConfigurationHBased(certHash, hostVpn, dnsSuffixList, callback) { this.startConfigurationHBased = function startConfigurationHBased(certHash, hostVpn, dnsSuffixList, func) {
if ((certHash == null) || ((certHash.length != 32) && (certHash.length != 48))) { throw "Bad certHash"; } if ((certHash == null) || ((certHash.length != 32) && (certHash.length != 48))) { func({ status: -101 }); }
var optional = []; var optional = [];
for (var i = 4; i < arguments.length; ++i) { optional.push(arguments[i]); } for (var i = 4; i < arguments.length; ++i) { optional.push(arguments[i]); }
@ -447,8 +447,23 @@ function amt_heci() {
opt.unshift({ status: header.Status }); opt.unshift({ status: header.Status });
} }
fn.apply(this, opt); fn.apply(this, opt);
}, callback, optional); }, func, optional);
} }
} }
module.exports = amt_heci; module.exports = amt_heci;
/*
AMT_STATUS_SUCCESS = 0,
AMT_STATUS_INTERNAL_ERROR = 1,
AMT_STATUS_INVALID_AMT_MODE = 3,
AMT_STATUS_INVALID_MESSAGE_LENGTH = 4,
AMT_STATUS_MAX_LIMIT_REACHED = 23,
AMT_STATUS_INVALID_PARAMETER = 36,
AMT_STATUS_RNG_GENERATION_IN_PROGRESS = 47,
AMT_STATUS_RNG_NOT_READY = 48,
AMT_STATUS_CERTIFICATE_NOT_READY = 49,
AMT_STATUS_INVALID_HANDLE = 2053
AMT_STATUS_NOT_FOUND = 2068,
*/

View File

@ -246,6 +246,10 @@ module.exports.CreateAmtManager = function (parent) {
delete dev.pendingUpdatedMeiState; delete dev.pendingUpdatedMeiState;
attemptInitialContact(dev); attemptInitialContact(dev);
break; break;
case 'startTlsHostConfig':
if (dev.acmTlsInfo == null) break;
console.log(jsondata); // TODO: Start TLS activation.
break;
} }
} }
@ -345,6 +349,9 @@ module.exports.CreateAmtManager = function (parent) {
if (typeof dev.mpsConnection.tag.meiState['ProvisioningState'] == 'number') { if (typeof dev.mpsConnection.tag.meiState['ProvisioningState'] == 'number') {
dev.intelamt.state = dev.aquired.state = dev.mpsConnection.tag.meiState['ProvisioningState']; dev.intelamt.state = dev.aquired.state = dev.mpsConnection.tag.meiState['ProvisioningState'];
} }
if ((typeof dev.mpsConnection.tag.meiState['Versions'] == 'object') && (typeof dev.mpsConnection.tag.meiState['Versions']['AMT'] == 'string')) {
dev.intelamt.ver = dev.aquired.version = dev.mpsConnection.tag.meiState['Versions']['AMT'];
}
if (typeof dev.mpsConnection.tag.meiState['Flags'] == 'number') { if (typeof dev.mpsConnection.tag.meiState['Flags'] == 'number') {
const flags = dev.intelamt.flags = dev.mpsConnection.tag.meiState['Flags']; const flags = dev.intelamt.flags = dev.mpsConnection.tag.meiState['Flags'];
if (flags & 2) { dev.aquired.controlMode = 1; } // CCM if (flags & 2) { dev.aquired.controlMode = 1; } // CCM
@ -459,6 +466,9 @@ module.exports.CreateAmtManager = function (parent) {
dev.amtstack.BatchEnum(null, ['*AMT_GeneralSettings', '*IPS_HostBasedSetupService'], attemptLocalConnectResponse); dev.amtstack.BatchEnum(null, ['*AMT_GeneralSettings', '*IPS_HostBasedSetupService'], attemptLocalConnectResponse);
break; break;
case 3: // Local LAN case 3: // Local LAN
// Check if Intel AMT is activated. If not, stop here.
if ((dev.intelamt == null) || ((dev.intelamt.state != null) && (dev.intelamt.state != 2))) { removeAmtDevice(dev); return; }
// Handle the case where the Intel AMT local scanner found the device (connType 3) // Handle the case where the Intel AMT local scanner found the device (connType 3)
parent.debug('amt', dev.name, "Attempt Initial Local Contact", dev.connType, dev.host); parent.debug('amt', dev.name, "Attempt Initial Local Contact", dev.connType, dev.host);
if (typeof dev.host != 'string') { removeAmtDevice(dev); return; } // Local connection not valid if (typeof dev.host != 'string') { removeAmtDevice(dev); return; } // Local connection not valid
@ -1666,7 +1676,15 @@ module.exports.CreateAmtManager = function (parent) {
deactivateIntelAmtCCM(dev); deactivateIntelAmtCCM(dev);
} else { } else {
// We are not activated now, go to ACM directly. // We are not activated now, go to ACM directly.
activateIntelAmtAcm(dev, mesh.amt.password, acminfo); // If this is Intel AMT 14 or better, we are going to attempt a host-based end-to-end TLS activation.
if (typeof dev.intelamt.ver == 'string') { var verSplit = dev.intelamt.ver.split('.'); if (verSplit.length >= 3) { dev.aquired.majorver = parseInt(verSplit[0]); dev.aquired.minorver = parseInt(verSplit[1]); } }
if (dev.aquired.majorver >= 14) {
// Perform host-based TLS ACM activation
activateIntelAmtTlsAcm(dev, mesh.amt.password, acminfo);
} else {
// Perform host-based ACM activation
activateIntelAmtAcm(dev, mesh.amt.password, acminfo);
}
} }
} }
} }
@ -1769,6 +1787,22 @@ module.exports.CreateAmtManager = function (parent) {
return null; // Did not find a match return null; // Did not find a match
} }
// Attempt Intel AMT TLS ACM activation
function activateIntelAmtTlsAcm(dev, password, acminfo) {
// Generate a random Intel AMT password if needed
if ((password == null) || (password == '')) { password = getRandomAmtPassword(); }
dev.temp = { pass: password, acminfo: acminfo };
// Get our ACM activation certificate chain
var acmTlsInfo = parent.certificateOperations.getAcmCertChain(parent.config.domains[dev.domainid], dev.temp.acminfo.fqdn, dev.temp.acminfo.hash);
if (acmTlsInfo.error == 1) { dev.consoleMsg(acmTlsInfo.errorText); removeAmtDevice(dev); return; }
dev.acmTlsInfo = acmTlsInfo;
// Send the MEI command to enable TLS connections
dev.consoleMsg("Performing TLS ACM activation...");
dev.controlMsg({ action: 'startTlsHostConfig', hash: acmTlsInfo.hash, hostVpn: false, dnsSuffixList: null });
}
// Attempt Intel AMT ACM activation // Attempt Intel AMT ACM activation
function activateIntelAmtAcm(dev, password, acminfo) { function activateIntelAmtAcm(dev, password, acminfo) {
// Generate a random Intel AMT password if needed // Generate a random Intel AMT password if needed

View File

@ -287,15 +287,18 @@ module.exports.CreateAmtScanner = function (parent) {
obj.changeConnectState = function (tag, minorVersion, majorVersion, provisioningState, openPort, dualPorts, rinfo, user) { obj.changeConnectState = function (tag, minorVersion, majorVersion, provisioningState, openPort, dualPorts, rinfo, user) {
//var provisioningStates = { 0: 'Pre', 1: 'in', 2: 'Post' }; //var provisioningStates = { 0: 'Pre', 1: 'in', 2: 'Post' };
//var provisioningStateStr = provisioningStates[provisioningState]; //var provisioningStateStr = provisioningStates[provisioningState];
//console.log('Intel AMT ' + majorVersion + '.' + minorVersion + ', ' + provisioningStateStr + '-Provisioning at ' + rinfo.address + ', Open Ports: [' + openPort + '], tag: ' + tag); //console.log('Intel AMT ' + majorVersion + '.' + minorVersion + ', ' + provisioningStateStr + '-Provisioning at ' + rinfo.address + ', Open Ports: [' + openPort + '], tag: ' + tag + ', dualPorts: ' + dualPorts);
var scaninfo = obj.scanTableTags[tag]; var scaninfo = obj.scanTableTags[tag];
if (scaninfo != undefined) { if (scaninfo != undefined) {
scaninfo.lastpong = Date.now(); scaninfo.lastpong = Date.now();
if (scaninfo.state == 0) { if (scaninfo.state == 0) {
scaninfo.state = 1; scaninfo.state = 1;
scaninfo.nodeinfo.intelamt.tls = (((openPort == 16993) || (dualPorts == true)) ? 1 : 0); if ((openPort == 16993) || (dualPorts == true)) { scaninfo.nodeinfo.intelamt.tls = 1; }
scaninfo.nodeinfo.intelamt.ver = majorVersion + '.' + minorVersion; else if (openPort == 16992) { scaninfo.nodeinfo.intelamt.tls = 0; }
scaninfo.nodeinfo.intelamt.state = provisioningState; if (majorVersion > 0) { // Older versions of Intel AMT report the AMT version.
scaninfo.nodeinfo.intelamt.ver = majorVersion + '.' + minorVersion;
scaninfo.nodeinfo.intelamt.state = provisioningState;
}
obj.parent.SetConnectivityState(scaninfo.nodeinfo.meshid, scaninfo.nodeinfo._id, scaninfo.lastpong, 4, 7); // Report power state as "present" (7). obj.parent.SetConnectivityState(scaninfo.nodeinfo.meshid, scaninfo.nodeinfo._id, scaninfo.lastpong, 4, 7); // Report power state as "present" (7).
obj.changeAmtState(scaninfo.nodeinfo._id, scaninfo.nodeinfo.intelamt.ver, provisioningState, scaninfo.nodeinfo.intelamt.tls); obj.changeAmtState(scaninfo.nodeinfo._id, scaninfo.nodeinfo.intelamt.ver, provisioningState, scaninfo.nodeinfo.intelamt.tls);
if (obj.parent.amtManager != null) { obj.parent.amtManager.startAmtManagement(scaninfo.nodeinfo._id, 3, scaninfo.nodeinfo.host); } if (obj.parent.amtManager != null) { obj.parent.amtManager.startAmtManagement(scaninfo.nodeinfo._id, 3, scaninfo.nodeinfo.host); }

View File

@ -28,6 +28,42 @@ module.exports.CertificateOperations = function (parent) {
const TopLevelDomainExtendedSupport = { 'net': 2, 'com': 2, 'arpa': 3, 'org': 2, 'gov': 2, 'edu': 2, 'de': 2, 'fr': 3, 'cn': 3, 'nl': 3, 'br': 3, 'mx': 3, 'uk': 3, 'pl': 3, 'tw': 3, 'ca': 3, 'fi': 3, 'be': 3, 'ru': 3, 'se': 3, 'ch': 2, 'dk': 2, 'ar': 3, 'es': 3, 'no': 3, 'at': 3, 'in': 3, 'tr': 3, 'cz': 2, 'ro': 3, 'hu': 3, 'nz': 3, 'pt': 3, 'il': 3, 'gr': 3, 'co': 3, 'ie': 3, 'za': 3, 'th': 3, 'sg': 3, 'hk': 3, 'cl': 2, 'lt': 3, 'id': 3, 'hr': 3, 'ee': 3, 'bg': 3, 'ua': 2 }; const TopLevelDomainExtendedSupport = { 'net': 2, 'com': 2, 'arpa': 3, 'org': 2, 'gov': 2, 'edu': 2, 'de': 2, 'fr': 3, 'cn': 3, 'nl': 3, 'br': 3, 'mx': 3, 'uk': 3, 'pl': 3, 'tw': 3, 'ca': 3, 'fi': 3, 'be': 3, 'ru': 3, 'se': 3, 'ch': 2, 'dk': 2, 'ar': 3, 'es': 3, 'no': 3, 'at': 3, 'in': 3, 'tr': 3, 'cz': 2, 'ro': 3, 'hu': 3, 'nz': 3, 'pt': 3, 'il': 3, 'gr': 3, 'co': 3, 'ie': 3, 'za': 3, 'th': 3, 'sg': 3, 'hk': 3, 'cl': 2, 'lt': 3, 'id': 3, 'hr': 3, 'ee': 3, 'bg': 3, 'ua': 2 };
// Sign a Intel AMT TLS ACM activation request
obj.getAcmCertChain = function (domain, fqdn, hash) {
if ((domain == null) || (domain.amtacmactivation == null) || (domain.amtacmactivation.certs == null) || (fqdn == null) || (hash == null)) return { action: 'acmactivate', error: 1, errorText: 'Invalid arguments' };
if (parent.common.validateString(fqdn, 4, 256) == false) return { action: 'acmactivate', error: 1, errorText: "Invalid FQDN argument." };
if (parent.common.validateString(hash, 16, 256) == false) return { action: 'acmactivate', error: 1, errorText: "Invalid hash argument." };
// Look for the signing certificate
var signkey = null, certChain = null, hashAlgo = null, certIndex = null;
for (var i in domain.amtacmactivation.certs) {
const certEntry = domain.amtacmactivation.certs[i];
if ((certEntry.sha256 == hash) && ((certEntry.cn == '*') || (certEntry.cn == fqdn))) { hashAlgo = 'sha256'; signkey = certEntry.key; certChain = certEntry.certs; certIndex = i; break; }
if ((certEntry.sha1 == hash) && ((certEntry.cn == '*') || (certEntry.cn == fqdn))) { hashAlgo = 'sha1'; signkey = certEntry.key; certChain = certEntry.certs; certIndex = i; break; }
}
if (signkey == null) return { action: 'acmactivate', error: 2, errorText: "No signing certificate found." }; // Did not find a match.
// If the matching certificate our wildcard root cert, we can use the root to match any FQDN
if (domain.amtacmactivation.certs[certIndex].cn == '*') {
// Create a leaf certificate that matches the FQDN we want
// TODO: This is an expensive operation, work on ways to pre-generate or cache this leaf certificate.
var rootcert = { cert: domain.amtacmactivation.certs[certIndex].rootcert, key: obj.pki.privateKeyFromPem(domain.amtacmactivation.certs[certIndex].key) };
var leafcert = obj.IssueWebServerCertificate(rootcert, false, fqdn, 'mc', 'Intel(R) Client Setup Certificate', { serverAuth: true, '2.16.840.1.113741.1.2.3': true }, false);
// Setup the certificate chain and key
certChain = [obj.pki.certificateToPem(leafcert.cert), obj.pki.certificateToPem(domain.amtacmactivation.certs[certIndex].rootcert)];
signkey = obj.pki.privateKeyToPem(leafcert.key);
} else {
// Make sure the cert chain is in PEM format
var certChain2 = [];
for (var i in certChain) { certChain2.push("-----BEGIN CERTIFICATE-----\r\n" + certChain[i] + "\r\n-----END CERTIFICATE-----\r\n"); }
certChain = certChain2;
}
// Hash the leaf certificate and return the certificate chain and signing key
return { action: 'acmactivate', certs: certChain, signkey: signkey, hash: obj.getCertHash(certChain[0]) };
}
// Sign a Intel AMT ACM activation request // Sign a Intel AMT ACM activation request
obj.signAcmRequest = function (domain, request, user, pass, ipport, nodeid, meshid, computerName, agentId) { obj.signAcmRequest = function (domain, request, user, pass, ipport, nodeid, meshid, computerName, agentId) {
if ((domain == null) || (domain.amtacmactivation == null) || (domain.amtacmactivation.certs == null) || (request == null) || (request.nonce == null) || (request.realm == null) || (request.fqdn == null) || (request.hash == null)) return { 'action': 'acmactivate', 'error': 1, 'errorText': 'Invalid arguments' }; if ((domain == null) || (domain.amtacmactivation == null) || (domain.amtacmactivation.certs == null) || (request == null) || (request.nonce == null) || (request.realm == null) || (request.fqdn == null) || (request.hash == null)) return { 'action': 'acmactivate', 'error': 1, 'errorText': 'Invalid arguments' };

View File

@ -913,6 +913,7 @@ module.exports.CreateMpsServer = function (parent, db, args, certificates) {
var jsondata = null, jsondatastr = data.substring(5, 5 + jsondatalen); var jsondata = null, jsondatastr = data.substring(5, 5 + jsondatalen);
try { jsondata = JSON.parse(jsondatastr); } catch (ex) { } try { jsondata = JSON.parse(jsondatastr); } catch (ex) { }
if ((jsondata == null) || (typeof jsondata.action != 'string')) return; if ((jsondata == null) || (typeof jsondata.action != 'string')) return;
parent.debug('mpscmd', '--> JSON_CONTROL', jsondata.action);
switch (jsondata.action) { switch (jsondata.action) {
case 'connType': case 'connType':
if ((socket.tag.connType != 0) || (socket.tag.SystemId != null)) return; // Once set, the connection type can't be changed. if ((socket.tag.connType != 0) || (socket.tag.SystemId != null)) return; // Once set, the connection type can't be changed.
@ -930,6 +931,10 @@ module.exports.CreateMpsServer = function (parent, db, args, certificates) {
if (socket.tag.connType != 2) break; // Only accept MEI state on CIRA-LMS connection if (socket.tag.connType != 2) break; // Only accept MEI state on CIRA-LMS connection
if (obj.parent.amtManager != null) { obj.parent.amtManager.mpsControlMessage(socket.tag.nodeid, socket, socket.tag.connType, jsondata); } if (obj.parent.amtManager != null) { obj.parent.amtManager.mpsControlMessage(socket.tag.nodeid, socket, socket.tag.connType, jsondata); }
break; break;
case 'startTlsHostConfig':
if (socket.tag.connType != 2) break; // Only accept MEI state on CIRA-LMS connection
if (obj.parent.amtManager != null) { obj.parent.amtManager.mpsControlMessage(socket.tag.nodeid, socket, socket.tag.connType, jsondata); }
break;
} }
return 5 + jsondatalen; return 5 + jsondatalen;
} }
@ -956,8 +961,7 @@ module.exports.CreateMpsServer = function (parent, db, args, certificates) {
obj.SendJsonControl = function(socket, data) { obj.SendJsonControl = function(socket, data) {
if (socket.tag.connType == 0) return; // This command is valid only for connections that are not really CIRA. if (socket.tag.connType == 0) return; // This command is valid only for connections that are not really CIRA.
parent.debug('mpscmd', '<-- JSON_CONTROL'); if (typeof data == 'object') { parent.debug('mpscmd', '<-- JSON_CONTROL', data.action); data = JSON.stringify(data); } else { parent.debug('mpscmd', '<-- JSON_CONTROL'); }
if (typeof data == 'object') { data = JSON.stringify(data); }
Write(socket, String.fromCharCode(APFProtocol.JSON_CONTROL) + common.IntToStr(data.length) + data); Write(socket, String.fromCharCode(APFProtocol.JSON_CONTROL) + common.IntToStr(data.length) + data);
} }