diff --git a/meshcentral.js b/meshcentral.js index 17f96280..98f09b44 100644 --- a/meshcentral.js +++ b/meshcentral.js @@ -826,7 +826,7 @@ function CreateMeshCentralServer(config, args) { obj.apfserver = require('./apfserver.js').CreateApfServer(obj, obj.db, obj.args); // Create MQTT Broker to hook into webserver and mpsserver - if (obj.config.mqtt != null) { obj.mqttbroker = require("./mqttbroker.js").CreateMQTTBroker(obj, obj.db, obj.args); } + if (obj.config.settings.mqtt != null) { obj.mqttbroker = require("./mqttbroker.js").CreateMQTTBroker(obj, obj.db, obj.args); } // Start the web server and if needed, the redirection web server. obj.webserver = require('./webserver.js').CreateWebServer(obj, obj.db, obj.args, obj.certificates); @@ -1100,9 +1100,9 @@ function CreateMeshCentralServer(config, args) { // meshId: mesh identifier of format mesh/domain/meshidhex // nodeId: node identifier of format node/domain/nodeidhex // connectTime: time of connection, milliseconds elapsed since the UNIX epoch. - // connectType: Bitmask, 1 = MeshAgent, 2 = Intel AMT CIRA, 4 = Intel AMT local. + // connectType: Bitmask, 1 = MeshAgent, 2 = Intel AMT CIRA, 4 = Intel AMT local, 8 = Intel AMT Relay, 16 = MQTT // powerState: Value, 0 = Unknown, 1 = S0 power on, 2 = S1 Sleep, 3 = S2 Sleep, 4 = S3 Sleep, 5 = S4 Hibernate, 6 = S5 Soft-Off, 7 = Present - //var connectTypeStrings = ['', 'MeshAgent', 'Intel AMT CIRA', '', 'Intel AMT local']; + //var connectTypeStrings = ['', 'MeshAgent', 'Intel AMT CIRA', '', 'Intel AMT local', '', '', '', 'Intel AMT Relay', '', '', '', '', '', '', '', 'MQTT']; //var powerStateStrings = ['Unknown', 'Powered', 'Sleep', 'Sleep', 'Deep Sleep', 'Hibernating', 'Soft-Off', 'Present']; obj.SetConnectivityState = function (meshid, nodeid, connectTime, connectType, powerState, serverid) { //console.log('SetConnectivity for ' + nodeid.substring(0, 16) + ', Type: ' + connectTypeStrings[connectType] + ', Power: ' + powerStateStrings[powerState] + (serverid == null ? ('') : (', ServerId: ' + serverid))); @@ -1829,7 +1829,7 @@ function mainStart() { if (require('os').platform() == 'win32') { modules.push('node-windows'); if (sspi == true) { modules.push('node-sspi'); } } // Add Windows modules if (ldap == true) { modules.push('ldapauth-fork'); } if (config.letsencrypt != null) { modules.push('greenlock'); modules.push('le-store-certbot'); modules.push('le-challenge-fs'); modules.push('le-acme-core'); } // Add Greenlock Modules - if (config.mqtt != null) { modules.push('mqtt'); modules.push('aedes'); } // Add MQTT Modules + if (config.settings.mqtt != null) { modules.push('aedes'); } // Add MQTT Modules if (config.settings.mongodb != null) { modules.push('mongodb'); } // Add MongoDB, official driver. else if (config.settings.xmongodb != null) { modules.push('mongojs'); } // Add MongoJS, old driver. if (config.smtp != null) { modules.push('nodemailer'); } // Add SMTP support diff --git a/mpsserver.js b/mpsserver.js index 0a6e8335..7135ecfd 100644 --- a/mpsserver.js +++ b/mpsserver.js @@ -238,9 +238,11 @@ module.exports.CreateMpsServer = function (parent, db, args, certificates) { socket.removeAllListeners("close"); socket.setNoDelay(true); socket.serialtunnel = SerialTunnel(); - socket.on('data', function (b) { socket.serialtunnel.updateBuffer(Buffer.from(b, 'binary')) }); + socket.serialtunnel.xtransport = 'mps'; + socket.serialtunnel.xip = socket.remoteAddress; + socket.on("data", function (b) { socket.serialtunnel.updateBuffer(Buffer.from(b, "binary")) }); socket.serialtunnel.forwardwrite = function (b) { socket.write(b, "binary") } - socket.on("close", function () { socket.serialtunnel.emit('end'); }); + socket.on("close", function () { socket.serialtunnel.emit("end"); }); // Pass socket wrapper to the MQTT broker parent.mqttbroker.handle(socket.serialtunnel); diff --git a/mqttbroker.js b/mqttbroker.js index 7836d0c3..42ea6cee 100644 --- a/mqttbroker.js +++ b/mqttbroker.js @@ -1,51 +1,101 @@ /** * @description MQTT broker reference implementation based on AEDES -* @author Joko Banu Sastriawan +* @author Joko Banu Sastriawan, Ylian Saint-Hilaire * @copyright Intel Corporation 2018-2019 * @license Apache-2.0 * @version v0.0.1 */ module.exports.CreateMQTTBroker = function (parent, db, args) { - - // Internal objects container + var obj = {} obj.parent = parent; obj.db = db; obj.args = args; obj.aedes = require("aedes")(); - - // argument parsing -- tbd - - // event handling and filtering - // authentication filter + obj.handle = obj.aedes.handle; + obj.connections = {}; // NodesID --> client array + + // Connection Authentication obj.aedes.authenticate = function (client, username, password, callback) { // TODO: add authentication handler - obj.parent.debug("mqtt", "Authentication with " + username + ":" + password); - callback(null, true); + obj.parent.debug("mqtt", "Authentication with " + username + ":" + password + ":" + client.id + ", " + client.conn.xtransport + "://" + cleanRemoteAddr(client.conn.xip)); + + var usersplit = username.split(':'); + if (usersplit.length != 5) { callback(null, false); return; } + + // Setup the identifiers + var xnodeid = usersplit[1]; + var xmeshid = usersplit[2]; + var xdomainid = usersplit[3]; + + // Convert meshid from HEX to Base64 if needed + if (xmeshid.length == 96) { xmeshid = Buffer.from(xmeshid, 'hex').toString('base64'); } + if ((xmeshid.length != 64) || (xnodeid.length != 64)) { callback(null, false); return; } + + client.xdbNodeKey = 'node/' + xdomainid + '/' + xnodeid; + client.xdbMeshKey = 'mesh/' + xdomainid + '/' + xmeshid; + + // Check if this node exists in the database + db.Get(client.xdbNodeKey, function (err, nodes) { + if ((nodes == null) || (nodes.length != 1)) { callback(null, false); return; } // Node does not exist + + // If this device now has a different meshid, fix it here. + client.xdbMeshKey = nodes[0].meshid; + + if (obj.connections[client.xdbNodeKey] == null) { + obj.connections[client.xdbNodeKey] = [client]; + parent.SetConnectivityState(client.xdbMeshKey, client.xdbNodeKey, Date.now(), 16, 7); // Indicate this node has a MQTT connection, 7 = Present state + } else { + obj.connections[client.xdbNodeKey].push(client); + } + + client.conn.parent = client; + client.conn.on('end', function () { + // client is "this.parent" + obj.parent.debug("mqtt", "Connection closed, " + this.parent.conn.xtransport + "://" + cleanRemoteAddr(this.parent.conn.xip)); + + // Remove this client from the connections list + if ((this.parent.xdbNodeKey != null) && (obj.connections[this.parent.xdbNodeKey] != null)) { + var clients = obj.connections[this.parent.xdbNodeKey], i = clients.indexOf(client); + if (i >= 0) { + if (clients.length == 1) { + delete obj.connections[this.parent.xdbNodeKey]; + parent.ClearConnectivityState(this.parent.xdbMeshKey, this.parent.xdbNodeKey, 16); // Remove the MQTT connection for this node + } else { clients.splice(i, 1); } + } + } + + this.parent.close(); + }); + callback(null, true); + }); } - - // check if a client can publish a packet + + // Check if a client can publish a packet obj.aedes.authorizePublish = function (client, packet, callback) { // TODO: add authorized publish control - obj.parent.debug("mqtt", "AuthorizePublish"); + obj.parent.debug("mqtt", "AuthorizePublish, " + client.conn.xtransport + "://" + cleanRemoteAddr(client.conn.xip)); callback(null); } - + // Check if a client can publish a packet obj.aedes.authorizeSubscribe = function (client, sub, callback) { // TODO: add subscription control here - obj.parent.debug("mqtt", "AuthorizeSubscribe"); + obj.parent.debug("mqtt", "AuthorizeSubscribe, " + client.conn.xtransport + "://" + cleanRemoteAddr(client.conn.xip)); callback(null, sub); } - + // Check if a client can publish a packet obj.aedes.authorizeForward = function (client, packet) { // TODO: add forwarding control - obj.parent.debug("mqtt", "AuthorizeForward"); + obj.parent.debug("mqtt", "AuthorizeForward, " + client.conn.xtransport + "://" + cleanRemoteAddr(client.conn.xip)); + //return packet; return packet; } - obj.handle = obj.aedes.handle; + // Clean a IPv6 address that encodes a IPv4 address + function cleanRemoteAddr(addr) { if (typeof addr != 'string') { return null; } if (addr.indexOf('::ffff:') == 0) { return addr.substring(7); } else { return addr; } } + return obj; } diff --git a/package.json b/package.json index dd3173b8..36e1b4b5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "meshcentral", - "version": "0.4.1-s", + "version": "0.4.1-t", "keywords": [ "Remote Management", "Intel AMT", diff --git a/views/default-min.handlebars b/views/default-min.handlebars index b7bf1f00..6c7f7d99 100644 --- a/views/default-min.handlebars +++ b/views/default-min.handlebars @@ -1 +1 @@ -
{{{logoutControl}}}
My Devices | My Account | My Events | My Files | My Users | My Server |
{{{logoutControl}}}
My Devices | My Account | My Events | My Files | My Users | My Server |