From bc4e07b5fe84c4480b7c809a82815cd37595dbeb Mon Sep 17 00:00:00 2001 From: Ylian Saint-Hilaire Date: Thu, 8 Oct 2020 19:47:24 -0700 Subject: [PATCH] Merged APF and MPS servers, improved APF client. --- agents/modules_meshcore/apfclient.js | 186 +++---- amtmanager.js | 4 +- apfserver.js | 734 ------------------------ meshagent.js | 2 +- meshcentral.js | 4 - meshuser.js | 5 +- mpsserver.js | 800 ++++++++++++++------------- webserver.js | 2 +- 8 files changed, 495 insertions(+), 1242 deletions(-) delete mode 100644 apfserver.js diff --git a/agents/modules_meshcore/apfclient.js b/agents/modules_meshcore/apfclient.js index 4dcb900c..1eacd2d7 100644 --- a/agents/modules_meshcore/apfclient.js +++ b/agents/modules_meshcore/apfclient.js @@ -6,60 +6,30 @@ * @version v0.0.1 */ -function CreateAPFClient(parent, args) { +function CreateAPFClient(parent, args) { var obj = {}; obj.parent = parent; obj.args = args; obj.http = require('http'); - //obj.common = require('common'); - obj.net = require('net'); + obj.net = require('net'); obj.forwardClient = null; obj.downlinks = {}; obj.pfwd_idx = 0; - // keep alive timer - obj.timer = null; - - // some function copied from common.js - function ReadInt(v, p) { - return (v.charCodeAt(p) * 0x1000000) + (v.charCodeAt(p + 1) << 16) + (v.charCodeAt(p + 2) << 8) + v.charCodeAt(p + 3); - }; // We use "*0x1000000" instead of "<<24" because the shift converts the number to signed int32. - - function IntToStr(v) { - return String.fromCharCode((v >> 24) & 0xFF, (v >> 16) & 0xFF, (v >> 8) & 0xFF, v & 0xFF); - }; + obj.timer = null; // Keep alive timer - function hex2rstr(d) { - var r = '', m = ('' + d).match(/../g), t; - while (t = m.shift()) { r += String.fromCharCode('0x' + t); } - return r; - }; + // Function copied from common.js + function ReadInt(v, p) { return (v.charCodeAt(p) * 0x1000000) + (v.charCodeAt(p + 1) << 16) + (v.charCodeAt(p + 2) << 8) + v.charCodeAt(p + 3); }; // We use "*0x1000000" instead of "<<24" because the shift converts the number to signed int32. + function IntToStr(v) { return String.fromCharCode((v >> 24) & 0xFF, (v >> 16) & 0xFF, (v >> 8) & 0xFF, v & 0xFF); }; + function hex2rstr(d) { var r = '', m = ('' + d).match(/../g), t; while (t = m.shift()) { r += String.fromCharCode('0x' + t); } return r; }; + function char2hex(i) { return (i + 0x100).toString(16).substr(-2).toUpperCase(); }; // Convert decimal to hex + function rstr2hex(input) { var r = '', i; for (i = 0; i < input.length; i++) { r += char2hex(input.charCodeAt(i)); } return r; }; // Convert a raw string to a hex string + function d2h(d) { return (d / 256 + 1 / 512).toString(16).substring(2, 4); } + function buf2hex(input) { var r = '', i; for (i = 0; i < input.length; i++) { r += d2h(input[i]); } return r; }; + function Debug(str) { if (obj.parent.debug) { console.log(str); } } + function guidToStr(g) { return g.substring(6, 8) + g.substring(4, 6) + g.substring(2, 4) + g.substring(0, 2) + "-" + g.substring(10, 12) + g.substring(8, 10) + "-" + g.substring(14, 16) + g.substring(12, 14) + "-" + g.substring(16, 20) + "-" + g.substring(20); } + function strToGuid(s) { s = s.replace(/-/g, ''); var ret = s.substring(6, 8) + s.substring(4, 6) + s.substring(2, 4) + s.substring(0, 2) + s.substring(10, 12) + s.substring(8, 10) + s.substring(14, 16) + s.substring(12, 14) + s.substring(16, 20) + s.substring(20); return ret; } + function binzerostring(len) { var res = ''; for (var l = 0; l < len; l++) { res += String.fromCharCode(0 & 0xFF); } return res; } - // Convert decimal to hex - function char2hex(i) { return (i + 0x100).toString(16).substr(-2).toUpperCase(); }; - - // Convert a raw string to a hex string - function rstr2hex(input) { - var r = '', i; - for (i = 0; i < input.length; i++) { r += char2hex(input.charCodeAt(i)); } - return r; - }; - - function d2h(d) { - return (d / 256 + 1 / 512).toString(16).substring(2, 4); - } - - function buf2hex(input) { - var r = '', i; - for (i = 0; i < input.length; i++) { r += d2h(input[i]); } - return r; - }; - - - function Debug(str) { - if (obj.parent.debug) { - console.log(str); - } - } // CIRA state var CIRASTATE = { INITIAL: 0, @@ -85,9 +55,10 @@ function CreateAPFClient(parent, args) { obj.RedirectStartKvm = String.fromCharCode(0x10, 0x01, 0x00, 0x00, 0x4b, 0x56, 0x4d, 0x52); obj.RedirectStartIder = String.fromCharCode(0x10, 0x00, 0x00, 0x00, 0x49, 0x44, 0x45, 0x52); + // Intel AMT forwarded port list for non-TLS mode + //var pfwd_ports = [16992, 623, 16994, 5900]; + var pfwd_ports = [ 16992 ]; - // AMT forwarded port list for non-TLS mode - var pfwd_ports = [16992, 623, 16994, 5900]; // protocol definitions var APFProtocol = { UNKNOWN: 0, @@ -152,7 +123,7 @@ function CreateAPFClient(parent, args) { Debug("APF Secure WebSocket connected."); //console.log(JSON.stringify(resp)); obj.forwardClient.tag = { accumulator: [] }; - obj.forwardClient.ws = ws; + obj.forwardClient.ws = ws; obj.forwardClient.ws.on('end', function () { Debug("APF: Connection is closing."); if (obj.timer != null) { @@ -162,12 +133,12 @@ function CreateAPFClient(parent, args) { }); obj.forwardClient.ws.on('data', function (data) { - obj.forwardClient.tag.accumulator += hex2rstr(buf2hex(data)); + obj.forwardClient.tag.accumulator += hex2rstr(buf2hex(data)); try { var len = 0; do { len = ProcessData(obj.forwardClient); - if (len > 0) { + if (len > 0) { obj.forwardClient.tag.accumulator = obj.forwardClient.tag.accumulator.slice(len); } if (obj.cirastate == CIRASTATE.FAILED) { @@ -178,44 +149,26 @@ function CreateAPFClient(parent, args) { } catch (e) { Debug(e); } - }); - + }); + obj.forwardClient.ws.on('error', function (e) { Debug("APF: Connection error, ending connecting."); if (obj.timer != null) { clearInterval(obj.timer); obj.timer = null; } - }); + }); obj.state = CIRASTATE.INITIAL; SendProtocolVersion(obj.forwardClient.ws, obj.args.clientuuid); SendServiceRequest(obj.forwardClient.ws, 'auth@amt.intel.com'); } - function guidToStr(g) { return g.substring(6, 8) + g.substring(4, 6) + g.substring(2, 4) + g.substring(0, 2) + "-" + g.substring(10, 12) + g.substring(8, 10) + "-" + g.substring(14, 16) + g.substring(12, 14) + "-" + g.substring(16, 20) + "-" + g.substring(20); } - function strToGuid(s) { - s = s.replace(/-/g, ''); - var ret = s.substring(6, 8) + s.substring(4, 6) + s.substring(2, 4) + s.substring(0, 2); - ret += s.substring(10, 12) + s.substring(8, 10) + s.substring(14, 16) + s.substring(12, 14) + s.substring(16, 20) + s.substring(20); - return ret; - } - - function binzerostring(len) { - var res=''; - for (var l=0; l< len ; l++) { - res+=String.fromCharCode(0 & 0xFF); - } - return res; - } - - - - 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(buuid) + binzerostring(64); - socket.write(data); - Debug("APF: Send protocol version 1 0 " + uuid); + socket.write(data); + Debug("APF: Send protocol version 1 0 " + uuid); obj.cirastate = CIRASTATE.PROTOCOL_VERSION_SENT; } @@ -267,7 +220,8 @@ function CreateAPFClient(parent, args) { var len = socket.tag.accumulator.length; var data = socket.tag.accumulator; if (len == 0) { return 0; } - // respond to MPS according to obj.cirastate + + // Respond to MPS according to obj.cirastate switch (cmd) { case APFProtocol.SERVICE_ACCEPT: { var slen = ReadInt(data, 1); @@ -325,39 +279,50 @@ function CreateAPFClient(parent, args) { } // Channel management case APFProtocol.CHANNEL_OPEN: { - //parse CHANNEL OPEN request + // Parse CHANNEL OPEN request var p_res = parseChannelOpen(data); Debug("APF: CHANNEL_OPEN request: " + JSON.stringify(p_res)); // Check if target port is in pfwd_ports if (pfwd_ports.indexOf(p_res.target_port) >= 0) { - // connect socket to that port - obj.downlinks[p_res.sender_chan] = obj.net.createConnection({ host: obj.args.clientaddress, port: p_res.target_port }, function () { - //obj.downlinks[p_res.sender_chan].setEncoding('binary');//assume everything is binary, not interpreting + // Connect socket to that port + var chan = obj.net.createConnection({ host: obj.args.clientaddress, port: p_res.target_port }, function () { + //require('MeshAgent').SendCommand({ action: 'msg', type: 'console', value: "CHANNEL_OPEN-open" }); + // obj.downlinks[p_res.sender_chan].setEncoding('binary');//assume everything is binary, not interpreting SendChannelOpenConfirm(socket.ws, p_res); }); - obj.downlinks[p_res.sender_chan].on('data', function (ddata) { - //Relay data to fordwardclient + // Setup flow control + chan.maxInWindow = p_res.window_size; // Oddly, we are using the same window size as the other side. + chan.curInWindow = 0; + + chan.on('data', function (ddata) { + // Relay data to fordwardclient + // TODO: Implement flow control SendChannelData(socket.ws, p_res.sender_chan, ddata.length, ddata); }); - obj.downlinks[p_res.sender_chan].on('error', function (e) { + chan.on('error', function (e) { Debug("Downlink connection error: " + e); }); - obj.downlinks[p_res.sender_chan].on('end', function () { - if (obj.downlinks[p_res.sender_chan]) { + chan.on('end', function () { + var chan = obj.downlinks[p_res.sender_chan]; + if (chan != null) { try { Debug("Socket ends."); SendChannelClose(socket.ws, p_res.sender_chan); - // add some delay before removing... otherwise race condition - setTimeout(function () { delete obj.downlinks[p_res.sender_chan];},100); + chan.xclosed = 1; + // Add some delay before removing... otherwise race condition + setTimeout(function () { delete obj.downlinks[p_res.sender_chan]; }, 100); } catch (e) { Debug("Downlink connection exception: " + e); } } }); + + obj.downlinks[p_res.sender_chan] = chan; } else { + // Not a supported port, fail the connection SendChannelOpenFailure(socket.ws, p_res); } return p_res.len; @@ -369,11 +334,12 @@ function CreateAPFClient(parent, args) { case APFProtocol.CHANNEL_CLOSE: { var rcpt_chan = ReadInt(data, 1); Debug("APF: CHANNEL_CLOSE: " + rcpt_chan); - SendChannelClose(socket.ws, rcpt_chan); - try { - obj.downlinks[rcpt_chan].end(); + var chan = obj.downlinks[rcpt_chan]; + if ((chan != null) && (chan.xclosed !== 1)) { + SendChannelClose(socket.ws, rcpt_chan); + try { obj.downlinks[rcpt_chan].end(); } catch (e) { } delete obj.downlinks[rcpt_chan]; - } catch (e) { } + } return 5; } case APFProtocol.CHANNEL_DATA: { @@ -381,11 +347,14 @@ function CreateAPFClient(parent, args) { var rcpt_chan = ReadInt(data, 1); var chan_data_len = ReadInt(data, 5); var chan_data = data.substring(9, 9 + chan_data_len); - if (obj.downlinks[rcpt_chan]) { + var chan = obj.downlinks[rcpt_chan]; + if (chan != null) { + chan.curInWindow += chan_data_len; try { - obj.downlinks[rcpt_chan].write(chan_data, 'binary', function () { + chan.write(chan_data, 'binary', function () { Debug("Write completed."); - SendChannelWindowAdjust(socket.ws, rcpt_chan, chan_data_len);//I have full window capacity now + // If the incoming window is over half used, send an adjust. + if (this.curInWindow > (this.maxInWindow / 2)) { SendChannelWindowAdjust(socket.ws, rcpt_chan, this.curInWindow); this.curInWindow = 0; } }); } catch (e) { Debug("Cannot forward data to downlink socket."); @@ -406,17 +375,7 @@ function CreateAPFClient(parent, args) { } function parseChannelOpen(data) { - var result = { - len: 0, //to be filled later - cmd: APFProtocol.CHANNEL_OPEN, - chan_type: "", //to be filled later - sender_chan: 0, //to be filled later - window_size: 0, //to be filled later - target_address: "", //to be filled later - target_port: 0, //to be filled later - origin_address: "", //to be filled later - origin_port: 0, //to be filled later - }; + var result = { cmd: APFProtocol.CHANNEL_OPEN }; var chan_type_slen = ReadInt(data, 1); result.chan_type = data.substring(5, 5 + chan_type_slen); result.sender_chan = ReadInt(data, 5 + chan_type_slen); @@ -430,12 +389,14 @@ function CreateAPFClient(parent, args) { result.len = 33 + chan_type_slen + c_len + o_len; return result; } + function SendChannelOpenFailure(socket, chan_data) { var data = String.fromCharCode(APFProtocol.CHANNEL_OPEN_FAILURE) + IntToStr(chan_data.sender_chan) + IntToStr(2) + IntToStr(0) + IntToStr(0); socket.write(data); Debug("APF: Send ChannelOpenFailure"); } + function SendChannelOpenConfirm(socket, chan_data) { var data = String.fromCharCode(APFProtocol.CHANNEL_OPEN_CONFIRMATION) + IntToStr(chan_data.sender_chan) + IntToStr(chan_data.sender_chan) + IntToStr(chan_data.window_size) + IntToStr(0xFFFFFFFF); @@ -472,25 +433,18 @@ function CreateAPFClient(parent, args) { } obj.cirastate = CIRASTATE.INITIAL; obj.pfwd_idx = 0; - + //obj.forwardClient = new obj.ws(obj.args.mpsurl, obj.tlsoptions); //obj.forwardClient.on("open", obj.onSecureConnect); - - var wsoptions = obj.http.parseUri(obj.args.mpsurl); + var wsoptions = obj.http.parseUri(obj.args.mpsurl); wsoptions.rejectUnauthorized = 0; - obj.forwardClient = obj.http.request(wsoptions); - obj.forwardClient.upgrade = obj.onSecureConnect; + obj.forwardClient = obj.http.request(wsoptions); + obj.forwardClient.upgrade = obj.onSecureConnect; obj.forwardClient.end(); // end request, trigger completion of HTTP request } - obj.disconnect = function () { - try { - obj.forwardClient.ws.end(); - } catch (e) { - Debug(e); - } - } + obj.disconnect = function () { try { obj.forwardClient.ws.end(); } catch (e) { Debug(e); } } return obj; } diff --git a/amtmanager.js b/amtmanager.js index f631a2ea..1acf0331 100644 --- a/amtmanager.js +++ b/amtmanager.js @@ -44,7 +44,7 @@ module.exports.CreateAmtManager = function(parent) { // React to nodes connecting and disconnecting if (event.action == 'nodeconnect') { if ((event.conn & 14) != 0) { // connectType: Bitmask, 1 = MeshAgent, 2 = Intel AMT CIRA, 4 = Intel AMT local, 8 = Intel AMT Relay, 16 = MQTT - //if ((event.conn & 2) == 0) return // Debug: Only look at CIRA connections ***************************** + //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]; @@ -266,7 +266,7 @@ module.exports.CreateAmtManager = function(parent) { } function attemptLocalConnectResponse(stack, name, responses, status) { - //console.log('attemptLocalConnectResponse', status); + //console.log('attemptLocalConnectResponse', status, stack.dev.name); // Release active connection to this host. delete obj.activeLocalConnections[stack.wsman.comm.host]; diff --git a/apfserver.js b/apfserver.js deleted file mode 100644 index 52eba685..00000000 --- a/apfserver.js +++ /dev/null @@ -1,734 +0,0 @@ -/** -* @description MeshCentral Intel(R) AMT APF over websocket server -* @author Ylian Saint-Hilaire/Joko Sastriawan -* @copyright Intel Corporation 2018-2020 -* @license Apache-2.0 -* @version v0.0.1 -*/ - -/*jslint node: true */ -/*jshint node: true */ -/*jshint strict:false */ -/*jshint -W097 */ -/*jshint esversion: 6 */ -"use strict"; - -// Construct a Intel AMT APF server object -module.exports.CreateApfServer = function (parent, db, args) { - var obj = {}; - obj.parent = parent; - obj.db = db; - obj.args = args; - obj.apfConnections = {}; - const constants = (require('crypto').constants ? require('crypto').constants : require('constants')); // require('constants') is deprecated in Node 11.10, use require('crypto').constants instead. - const common = require("./common.js"); - const net = require("net"); - const MAX_IDLE = 90000; // 90 seconds max idle time, higher than the typical KEEP-ALIVE periode of 60 seconds - - - const APFProtocol = { - UNKNOWN: 0, - DISCONNECT: 1, - SERVICE_REQUEST: 5, - SERVICE_ACCEPT: 6, - USERAUTH_REQUEST: 50, - USERAUTH_FAILURE: 51, - USERAUTH_SUCCESS: 52, - GLOBAL_REQUEST: 80, - REQUEST_SUCCESS: 81, - REQUEST_FAILURE: 82, - CHANNEL_OPEN: 90, - CHANNEL_OPEN_CONFIRMATION: 91, - CHANNEL_OPEN_FAILURE: 92, - CHANNEL_WINDOW_ADJUST: 93, - CHANNEL_DATA: 94, - CHANNEL_CLOSE: 97, - PROTOCOLVERSION: 192, - KEEPALIVE_REQUEST: 208, - KEEPALIVE_REPLY: 209, - KEEPALIVE_OPTIONS_REQUEST: 210, - KEEPALIVE_OPTIONS_REPLY: 211 - }; - - /* - const APFDisconnectCode = { - HOST_NOT_ALLOWED_TO_CONNECT: 1, - PROTOCOL_ERROR: 2, - KEY_EXCHANGE_FAILED: 3, - RESERVED: 4, - MAC_ERROR: 5, - COMPRESSION_ERROR: 6, - SERVICE_NOT_AVAILABLE: 7, - PROTOCOL_VERSION_NOT_SUPPORTED: 8, - HOST_KEY_NOT_VERIFIABLE: 9, - CONNECTION_LOST: 10, - BY_APPLICATION: 11, - TOO_MANY_CONNECTIONS: 12, - AUTH_CANCELLED_BY_USER: 13, - NO_MORE_AUTH_METHODS_AVAILABLE: 14, - INVALID_CREDENTIALS: 15, - CONNECTION_TIMED_OUT: 16, - BY_POLICY: 17, - TEMPORARILY_UNAVAILABLE: 18 - }; - - const APFChannelOpenFailCodes = { - ADMINISTRATIVELY_PROHIBITED: 1, - CONNECT_FAILED: 2, - UNKNOWN_CHANNEL_TYPE: 3, - RESOURCE_SHORTAGE: 4, - }; - */ - - const APFChannelOpenFailureReasonCode = { - AdministrativelyProhibited: 1, - ConnectFailed: 2, - UnknownChannelType: 3, - ResourceShortage: 4, - }; - - // Stat counters - var connectionCount = 0; - var userAuthRequestCount = 0; - var incorrectPasswordCount = 0; - var meshNotFoundCount = 0; - var unknownNodeCount = 0; - var unknownMeshIdCount = 0; - var addedDeviceCount = 0; - var ciraTimeoutCount = 0; - var protocolVersionCount = 0; - var badUserNameLengthCount = 0; - var channelOpenCount = 0; - var channelOpenConfirmCount = 0; - var channelOpenFailCount = 0; - var channelCloseCount = 0; - var disconnectCommandCount = 0; - var socketClosedCount = 0; - var socketErrorCount = 0; - var maxDomainDevicesReached = 0; - - // Return statistics about this APF server - obj.getStats = function () { - return { - apfConnections: Object.keys(obj.apfConnections).length, - connectionCount: connectionCount, - userAuthRequestCount: userAuthRequestCount, - incorrectPasswordCount: incorrectPasswordCount, - meshNotFoundCount: meshNotFoundCount, - unknownNodeCount: unknownNodeCount, - unknownMeshIdCount: unknownMeshIdCount, - addedDeviceCount: addedDeviceCount, - apfTimeoutCount: ciraTimeoutCount, - protocolVersionCount: protocolVersionCount, - badUserNameLengthCount: badUserNameLengthCount, - channelOpenCount: channelOpenCount, - channelOpenConfirmCount: channelOpenConfirmCount, - channelOpenFailCount: channelOpenFailCount, - channelCloseCount: channelCloseCount, - disconnectCommandCount: disconnectCommandCount, - socketClosedCount: socketClosedCount, - socketErrorCount: socketErrorCount, - maxDomainDevicesReached : maxDomainDevicesReached - }; - } - - obj.onConnection = function(socket) { - connectionCount++; - // treat APS over WS like tlsoffload APF - socket.tag = { first: true, clientCert: null, accumulator: "", activetunnels: 0, boundPorts: [], socket: socket, host: null, nextchannelid: 4, channels: {}, nextsourceport: 0 }; - parent.debug('apf', "New APF connection"); - parent.debug('apf',"WS Extensions:"+socket.extensions); - parent.debug('apf',"WS Binary type:"+socket.binaryType); - - //socket._socket.on('data', function(chunk) { console.log(chunk.toString('hex'))}); - - // Setup the APF keep alive timer - // Websocket does not have timout - // socket.setTimeout(MAX_IDLE); - //socket.on("timeout", () => { ciraTimeoutCount++; parent.debug('apf', "APF timeout, disconnecting."); try { socket.terminate(); } catch (e) { } }); - //use on message instead because of websocket - socket.on("message", function (data) { - // use the same debug flag like APF - if (obj.args.debug) { var buf = Buffer.from(data, "binary"); console.log("APF <-- (" + buf.length + "):" + buf.toString('hex')); } // Print out received bytes - socket.tag.accumulator += data.toString("binary"); // append as binary string - - - try { - // Parse all of the APF data we can - var l = 0; - do { l = ProcessCommand(socket); if (l > 0) { socket.tag.accumulator = socket.tag.accumulator.substring(l); } } while (l > 0); - if (l < 0) { socket.terminate(); } - } catch (e) { - console.log(e); - } - }); - - // Process one AFP command - function ProcessCommand(socket) { - var cmd = socket.tag.accumulator.charCodeAt(0); - var len = socket.tag.accumulator.length; - var data = socket.tag.accumulator; - if (len == 0) { return 0; } - - switch (cmd) { - case APFProtocol.KEEPALIVE_REQUEST: { - if (len < 5) return 0; - parent.debug('apfcmd', 'KEEPALIVE_REQUEST'); - SendKeepAliveReply(socket, common.ReadInt(data, 1)); - return 5; - } - case APFProtocol.KEEPALIVE_REPLY: { - if (len < 5) return 0; - parent.debug('apfcmd', 'KEEPALIVE_REPLY'); - return 5; - } - case APFProtocol.PROTOCOLVERSION: { - if (len < 93) return 0; - protocolVersionCount++; - socket.tag.MajorVersion = common.ReadInt(data, 1); - socket.tag.MinorVersion = common.ReadInt(data, 5); - socket.tag.SystemId = guidToStr(common.rstr2hex(data.substring(13, 29))).toLowerCase(); - parent.debug('apfcmd', 'PROTOCOLVERSION', socket.tag.MajorVersion, socket.tag.MinorVersion, socket.tag.SystemId); - return 93; - } - case APFProtocol.USERAUTH_REQUEST: { - if (len < 13) return 0; - userAuthRequestCount++; - var usernameLen = common.ReadInt(data, 1); - var username = data.substring(5, 5 + usernameLen); - var serviceNameLen = common.ReadInt(data, 5 + usernameLen); - var serviceName = data.substring(9 + usernameLen, 9 + usernameLen + serviceNameLen); - var methodNameLen = common.ReadInt(data, 9 + usernameLen + serviceNameLen); - var methodName = data.substring(13 + usernameLen + serviceNameLen, 13 + usernameLen + serviceNameLen + methodNameLen); - var passwordLen = 0, password = null; - if (methodName == 'password') { - passwordLen = common.ReadInt(data, 14 + usernameLen + serviceNameLen + methodNameLen); - password = data.substring(18 + usernameLen + serviceNameLen + methodNameLen, 18 + usernameLen + serviceNameLen + methodNameLen + passwordLen); - } - //console.log('APF:USERAUTH_REQUEST user=' + username + ', service=' + serviceName + ', method=' + methodName + ', password=' + password); - parent.debug('apfcmd', 'USERAUTH_REQUEST user=' + username + ', service=' + serviceName + ', method=' + methodName + ', password=' + password); - - // Check the APF password - if ((args.mpspass != null) && (password != args.mpspass)) { incorrectPasswordCount++; parent.debug('apf', 'Incorrect password', username, password); SendUserAuthFail(socket); return -1; } - - // Check the APF username, which should be the start of the MeshID. - if (usernameLen != 16) { badUserNameLengthCount++; parent.debug('apf', 'Username length not 16', username, password); SendUserAuthFail(socket); return -1; } - var meshIdStart = '/' + username, mesh = null; - if (obj.parent.webserver.meshes) { for (var i in obj.parent.webserver.meshes) { if (obj.parent.webserver.meshes[i]._id.replace(/\@/g, 'X').replace(/\$/g, 'X').indexOf(meshIdStart) > 0) { mesh = obj.parent.webserver.meshes[i]; break; } } } - if (mesh == null) { meshNotFoundCount++; parent.debug('apf', 'Mesh not found', username, password); SendUserAuthFail(socket); return -1; } - - // If this is a agent-less mesh, use the device guid 3 times as ID. - if (mesh.mtype == 1) { - // Intel AMT GUID (socket.tag.SystemId) will be used as NodeID - var systemid = socket.tag.SystemId.split('-').join(''); - var nodeid = Buffer.from(systemid + systemid + systemid, 'hex').toString('base64').replace(/\+/g, '@').replace(/\//g, '$'); - var domain = obj.parent.config.domains[mesh.domain]; - socket.tag.domain = domain; - socket.tag.domainid = mesh.domain; - socket.tag.name = ''; - socket.tag.nodeid = 'node/' + mesh.domain + '/' + nodeid; // Turn 16bit systemid guid into 48bit nodeid that is base64 encoded - socket.tag.meshid = mesh._id; - socket.tag.connectTime = Date.now(); - - obj.db.Get(socket.tag.nodeid, function (err, nodes) { - if ((nodes == null) || (nodes.length !== 1)) { - // Check if we already have too many devices for this domain - if (domain.limits && (typeof domain.limits.maxdevices == 'number')) { - db.isMaxType(domain.limits.maxdevices, 'node', mesh.domain, function (ismax, count) { - if (ismax == true) { - // Too many devices in this domain. - maxDomainDevicesReached++; - console.log('Too many devices on this domain to accept the APF connection. meshid: ' + socket.tag.meshid); - socket.terminate(); - } else { - // We are under the limit, create the new device. - // Node is not in the database, add it. Credentials will be empty until added by the user. - var device = { type: 'node', mtype: 1, _id: socket.tag.nodeid, meshid: socket.tag.meshid, name: socket.tag.name, host: null, domain: mesh.domain, intelamt: { user: '', pass: '', tls: 0, state: 2 } }; - obj.db.Set(device); - - // Event the new node - addedDeviceCount++; - var change = 'APF added device ' + socket.tag.name + ' to group ' + mesh.name; - 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 APF connection list - obj.apfConnections[socket.tag.nodeid] = socket; - // send connectivuty update type 8 for APF - obj.parent.SetConnectivityState(socket.tag.meshid, socket.tag.nodeid, socket.tag.connectTime, 8, 7); // TODO: Right now report power state as "present" (7) until we can poll. - SendUserAuthSuccess(socket); // Notify the auth success on the APF connection - } - }); - return; - } else { - // Node is not in the database, add it. Credentials will be empty until added by the user. - var device = { type: 'node', mtype: 1, _id: socket.tag.nodeid, meshid: socket.tag.meshid, name: socket.tag.name, host: null, domain: mesh.domain, intelamt: { user: '', pass: '', tls: 0, state: 2 } }; - obj.db.Set(device); - - // Event the new node - addedDeviceCount++; - var change = 'APF added device ' + socket.tag.name + ' to group ' + mesh.name; - obj.parent.DispatchEvent(['*', socket.tag.meshid], obj, { etype: 'node', action: 'addnode', node: parent.webserver.CloneSafeNode(device), msg: change, domain: mesh.domain }); - } - } else { - // Node is already present - var node = nodes[0]; - if ((node.intelamt != null) && (node.intelamt.state == 2)) { socket.tag.host = node.intelamt.host; } - } - - // Add the connection to the APF connection list - obj.apfConnections[socket.tag.nodeid] = socket; - obj.parent.SetConnectivityState(socket.tag.meshid, socket.tag.nodeid, socket.tag.connectTime, 8, 7); // TODO: Right now report power state as "present" (7) until we can poll. - SendUserAuthSuccess(socket); // Notify the auth success on the APF connection - }); - } else if (mesh.mtype == 2) { // If this is a agent mesh, search the mesh for this device UUID - // Intel AMT GUID (socket.tag.SystemId) will be used to search the node - obj.db.getAmtUuidNode(socket.tag.SystemId, function (err, nodes) { // TODO: May need to optimize this request with indexes - if ((nodes == null) || (nodes.length !== 1)) { - // New APF connection for unknown node, disconnect. - unknownNodeCount++; - console.log('APF connection for unknown node. groupid: ' + mesh._id + ', uuid: ' + socket.tag.SystemId); - socket.terminate(); - return; - } - - // Node is present - var node = nodes[0]; - if ((node.intelamt != null) && (node.intelamt.state == 2)) { socket.tag.host = node.intelamt.host; } - socket.tag.nodeid = node._id; - socket.tag.meshid = mesh._id; - socket.tag.connectTime = Date.now(); - - // Add the connection to the APF connection list - obj.apfConnections[socket.tag.nodeid] = socket; - obj.parent.SetConnectivityState(socket.tag.meshid, socket.tag.nodeid, socket.tag.connectTime, 8, 7); // TODO: Right now report power state as "present" (7) until we can poll. - SendUserAuthSuccess(socket); // Notify the auth success on the APF connection - }); - } else { // Unknown mesh type - // New APF connection for unknown node, disconnect. - unknownMeshIdCount++; - console.log('APF connection to a unknown group type. groupid: ' + socket.tag.meshid); - socket.terminate(); - return; - } - return 18 + usernameLen + serviceNameLen + methodNameLen + passwordLen; - } - case APFProtocol.SERVICE_REQUEST: { - if (len < 5) return 0; - var xserviceNameLen = common.ReadInt(data, 1); - if (len < 5 + xserviceNameLen) return 0; - var xserviceName = data.substring(5, 5 + xserviceNameLen); - parent.debug('apfcmd', 'SERVICE_REQUEST', xserviceName); - if (xserviceName == "pfwd@amt.intel.com") { SendServiceAccept(socket, "pfwd@amt.intel.com"); } - if (xserviceName == "auth@amt.intel.com") { SendServiceAccept(socket, "auth@amt.intel.com"); } - return 5 + xserviceNameLen; - } - case APFProtocol.GLOBAL_REQUEST: { - if (len < 14) return 0; - var requestLen = common.ReadInt(data, 1); - if (len < 14 + requestLen) return 0; - var request = data.substring(5, 5 + requestLen); - //var wantResponse = data.charCodeAt(5 + requestLen); - - if (request == "tcpip-forward") { - var addrLen = common.ReadInt(data, 6 + requestLen); - if (len < 14 + requestLen + addrLen) return 0; - var addr = data.substring(10 + requestLen, 10 + requestLen + addrLen); - var port = common.ReadInt(data, 10 + requestLen + addrLen); - parent.debug('apfcmd', 'GLOBAL_REQUEST', request, addr + ':' + port); - ChangeHostname(socket, addr, socket.tag.SystemId); - if (socket.tag.boundPorts.indexOf(port) == -1) { socket.tag.boundPorts.push(port); } - SendTcpForwardSuccessReply(socket, port); - return 14 + requestLen + addrLen; - } - - if (request == "cancel-tcpip-forward") { - var addrLen = common.ReadInt(data, 6 + requestLen); - if (len < 14 + requestLen + addrLen) return 0; - var addr = data.substring(10 + requestLen, 10 + requestLen + addrLen); - var port = common.ReadInt(data, 10 + requestLen + addrLen); - parent.debug('apfcmd', 'GLOBAL_REQUEST', request, addr + ':' + port); - var portindex = socket.tag.boundPorts.indexOf(port); - if (portindex >= 0) { socket.tag.boundPorts.splice(portindex, 1); } - SendTcpForwardCancelReply(socket); - return 14 + requestLen + addrLen; - } - - if (request == "udp-send-to@amt.intel.com") { - var addrLen = common.ReadInt(data, 6 + requestLen); - if (len < 26 + requestLen + addrLen) return 0; - var addr = data.substring(10 + requestLen, 10 + requestLen + addrLen); - var port = common.ReadInt(data, 10 + requestLen + addrLen); - var oaddrLen = common.ReadInt(data, 14 + requestLen + addrLen); - if (len < 26 + requestLen + addrLen + oaddrLen) return 0; - var oaddr = data.substring(18 + requestLen, 18 + requestLen + addrLen); - var oport = common.ReadInt(data, 18 + requestLen + addrLen + oaddrLen); - var datalen = common.ReadInt(data, 22 + requestLen + addrLen + oaddrLen); - if (len < 26 + requestLen + addrLen + oaddrLen + datalen) return 0; - parent.debug('apfcmd', 'GLOBAL_REQUEST', request, addr + ':' + port, oaddr + ':' + oport, datalen); - // TODO - return 26 + requestLen + addrLen + oaddrLen + datalen; - } - - return 6 + requestLen; - } - case APFProtocol.CHANNEL_OPEN: { - if (len < 33) return 0; - var ChannelTypeLength = common.ReadInt(data, 1); - if (len < (33 + ChannelTypeLength)) return 0; - - // Decode channel identifiers and window size - var ChannelType = data.substring(5, 5 + ChannelTypeLength); - var SenderChannel = common.ReadInt(data, 5 + ChannelTypeLength); - var WindowSize = common.ReadInt(data, 9 + ChannelTypeLength); - - // Decode the target - var TargetLen = common.ReadInt(data, 17 + ChannelTypeLength); - if (len < (33 + ChannelTypeLength + TargetLen)) return 0; - var Target = data.substring(21 + ChannelTypeLength, 21 + ChannelTypeLength + TargetLen); - var TargetPort = common.ReadInt(data, 21 + ChannelTypeLength + TargetLen); - - // Decode the source - var SourceLen = common.ReadInt(data, 25 + ChannelTypeLength + TargetLen); - if (len < (33 + ChannelTypeLength + TargetLen + SourceLen)) return 0; - var Source = data.substring(29 + ChannelTypeLength + TargetLen, 29 + ChannelTypeLength + TargetLen + SourceLen); - var SourcePort = common.ReadInt(data, 29 + ChannelTypeLength + TargetLen + SourceLen); - - channelOpenCount++; - parent.debug('apfcmd', 'CHANNEL_OPEN', ChannelType, SenderChannel, WindowSize, Target + ':' + TargetPort, Source + ':' + SourcePort); - - // Check if we understand this channel type - //if (ChannelType.toLowerCase() == "direct-tcpip") - { - // We don't understand this channel type, send an error back - SendChannelOpenFailure(socket, SenderChannel, APFChannelOpenFailureReasonCode.UnknownChannelType); - return 33 + ChannelTypeLength + TargetLen + SourceLen; - } - - /* - // This is a correct connection. Lets get it setup - var MeshAmtEventEndpoint = { ServerChannel: GetNextBindId(), AmtChannel: SenderChannel, MaxWindowSize: 2048, CurrentWindowSize:2048, SendWindow: WindowSize, InfoHeader: "Target: " + Target + ":" + TargetPort + ", Source: " + Source + ":" + SourcePort}; - // TODO: Connect this socket for a WSMAN event - SendChannelOpenConfirmation(socket, SenderChannel, MeshAmtEventEndpoint.ServerChannel, MeshAmtEventEndpoint.MaxWindowSize); - */ - - return 33 + ChannelTypeLength + TargetLen + SourceLen; - } - case APFProtocol.CHANNEL_OPEN_CONFIRMATION: - { - if (len < 17) return 0; - var RecipientChannel = common.ReadInt(data, 1); - var SenderChannel = common.ReadInt(data, 5); - var WindowSize = common.ReadInt(data, 9); - socket.tag.activetunnels++; - var cirachannel = socket.tag.channels[RecipientChannel]; - if (cirachannel == null) { /*console.log("APF Error in CHANNEL_OPEN_CONFIRMATION: Unable to find channelid " + RecipientChannel);*/ return 17; } - cirachannel.amtchannelid = SenderChannel; - cirachannel.sendcredits = cirachannel.amtCiraWindow = WindowSize; - channelOpenConfirmCount++; - parent.debug('apfcmd', 'CHANNEL_OPEN_CONFIRMATION', RecipientChannel, SenderChannel, WindowSize); - if (cirachannel.closing == 1) { - // Close this channel - SendChannelClose(cirachannel.socket, cirachannel.amtchannelid); - } else { - cirachannel.state = 2; - // Send any pending data - if (cirachannel.sendBuffer != null) { - if (cirachannel.sendBuffer.length <= cirachannel.sendcredits) { - // Send the entire pending buffer - SendChannelData(cirachannel.socket, cirachannel.amtchannelid, cirachannel.sendBuffer); - cirachannel.sendcredits -= cirachannel.sendBuffer.length; - delete cirachannel.sendBuffer; - if (cirachannel.onSendOk) { cirachannel.onSendOk(cirachannel); } - } else { - // Send a part of the pending buffer - SendChannelData(cirachannel.socket, cirachannel.amtchannelid, cirachannel.sendBuffer.substring(0, cirachannel.sendcredits)); - cirachannel.sendBuffer = cirachannel.sendBuffer.substring(cirachannel.sendcredits); - cirachannel.sendcredits = 0; - } - } - // Indicate the channel is open - if (cirachannel.onStateChange) { cirachannel.onStateChange(cirachannel, cirachannel.state); } - } - return 17; - } - case APFProtocol.CHANNEL_OPEN_FAILURE: - { - if (len < 17) return 0; - var RecipientChannel = common.ReadInt(data, 1); - var ReasonCode = common.ReadInt(data, 5); - channelOpenFailCount++; - parent.debug('apfcmd', 'CHANNEL_OPEN_FAILURE', RecipientChannel, ReasonCode); - var cirachannel = socket.tag.channels[RecipientChannel]; - if (cirachannel == null) { console.log("APF Error in CHANNEL_OPEN_FAILURE: Unable to find channelid " + RecipientChannel); return 17; } - if (cirachannel.state > 0) { - cirachannel.state = 0; - if (cirachannel.onStateChange) { cirachannel.onStateChange(cirachannel, cirachannel.state); } - delete socket.tag.channels[RecipientChannel]; - } - return 17; - } - case APFProtocol.CHANNEL_CLOSE: - { - if (len < 5) return 0; - var RecipientChannel = common.ReadInt(data, 1); - channelCloseCount++; - parent.debug('apfcmd', 'CHANNEL_CLOSE', RecipientChannel); - var cirachannel = socket.tag.channels[RecipientChannel]; - if (cirachannel == null) { console.log("APF Error in CHANNEL_CLOSE: Unable to find channelid " + RecipientChannel); return 5; } - socket.tag.activetunnels--; - if (cirachannel.state > 0) { - cirachannel.state = 0; - if (cirachannel.onStateChange) { cirachannel.onStateChange(cirachannel, cirachannel.state); } - SendChannelClose(cirachannel.socket, cirachannel.amtchannelid); - delete socket.tag.channels[RecipientChannel]; - } - return 5; - } - case APFProtocol.CHANNEL_WINDOW_ADJUST: - { - if (len < 9) return 0; - var RecipientChannel = common.ReadInt(data, 1); - var ByteToAdd = common.ReadInt(data, 5); - var cirachannel = socket.tag.channels[RecipientChannel]; - if (cirachannel == null) { console.log("APF Error in CHANNEL_WINDOW_ADJUST: Unable to find channelid " + RecipientChannel); return 9; } - cirachannel.sendcredits += ByteToAdd; - parent.debug('apfcmd', 'CHANNEL_WINDOW_ADJUST', RecipientChannel, ByteToAdd, cirachannel.sendcredits); - if (cirachannel.state == 2 && cirachannel.sendBuffer != null) { - // Compute how much data we can send - if (cirachannel.sendBuffer.length <= cirachannel.sendcredits) { - // Send the entire pending buffer - SendChannelData(cirachannel.socket, cirachannel.amtchannelid, cirachannel.sendBuffer); - cirachannel.sendcredits -= cirachannel.sendBuffer.length; - delete cirachannel.sendBuffer; - if (cirachannel.onSendOk) { cirachannel.onSendOk(cirachannel); } - } else { - // Send a part of the pending buffer - SendChannelData(cirachannel.socket, cirachannel.amtchannelid, cirachannel.sendBuffer.substring(0, cirachannel.sendcredits)); - cirachannel.sendBuffer = cirachannel.sendBuffer.substring(cirachannel.sendcredits); - cirachannel.sendcredits = 0; - } - } - return 9; - } - case APFProtocol.CHANNEL_DATA: - { - if (len < 9) return 0; - var RecipientChannel = common.ReadInt(data, 1); - var LengthOfData = common.ReadInt(data, 5); - if (len < (9 + LengthOfData)) return 0; - parent.debug('apfcmddata', 'CHANNEL_DATA', RecipientChannel, LengthOfData); - var cirachannel = socket.tag.channels[RecipientChannel]; - if (cirachannel == null) { console.log("APF Error in CHANNEL_DATA: Unable to find channelid " + RecipientChannel); return 9 + LengthOfData; } - cirachannel.amtpendingcredits += LengthOfData; - if (cirachannel.onData) cirachannel.onData(cirachannel, data.substring(9, 9 + LengthOfData)); - if (cirachannel.amtpendingcredits > (cirachannel.ciraWindow / 2)) { - SendChannelWindowAdjust(cirachannel.socket, cirachannel.amtchannelid, cirachannel.amtpendingcredits); // Adjust the buffer window - cirachannel.amtpendingcredits = 0; - } - return 9 + LengthOfData; - } - case APFProtocol.DISCONNECT: - { - if (len < 7) return 0; - var ReasonCode = common.ReadInt(data, 1); - disconnectCommandCount++; - parent.debug('apfcmd', 'DISCONNECT', ReasonCode); - try { delete obj.apfConnections[socket.tag.nodeid]; } catch (e) { } - obj.parent.ClearConnectivityState(socket.tag.meshid, socket.tag.nodeid, 8); - return 7; - } - default: - { - parent.debug('apfcmd', 'Unknown APF command: ' + cmd); - return -1; - } - } - } - - socket.addListener("close", function () { - socketClosedCount++; - parent.debug('apf', 'APF connection closed'); - try { delete obj.apfConnections[socket.tag.nodeid]; } catch (e) { } - obj.parent.ClearConnectivityState(socket.tag.meshid, socket.tag.nodeid, 8); - }); - - socket.addListener("error", function (e) { - socketErrorCount++; - console.log("APF Error: " + e); - }); - - } - - // Disconnect APF tunnel - obj.close = function (socket) { - try { socket.terminate(); } catch (e) { } - try { delete obj.apfConnections[socket.tag.nodeid]; } catch (e) { } - obj.parent.ClearConnectivityState(socket.tag.meshid, socket.tag.nodeid, 8); - }; - - function SendServiceAccept(socket, service) { - Write(socket, String.fromCharCode(APFProtocol.SERVICE_ACCEPT) + common.IntToStr(service.length) + service); - } - - function SendTcpForwardSuccessReply(socket, port) { - Write(socket, String.fromCharCode(APFProtocol.REQUEST_SUCCESS) + common.IntToStr(port)); - } - - function SendTcpForwardCancelReply(socket) { - Write(socket, String.fromCharCode(APFProtocol.REQUEST_SUCCESS)); - } - - /* - function SendKeepAliveRequest(socket, cookie) { - Write(socket, String.fromCharCode(APFProtocol.KEEPALIVE_REQUEST) + common.IntToStr(cookie)); - } - */ - - function SendKeepAliveReply(socket, cookie) { - Write(socket, String.fromCharCode(APFProtocol.KEEPALIVE_REPLY) + common.IntToStr(cookie)); - } - - function SendChannelOpenFailure(socket, senderChannel, reasonCode) { - Write(socket, String.fromCharCode(APFProtocol.CHANNEL_OPEN_FAILURE) + common.IntToStr(senderChannel) + common.IntToStr(reasonCode) + common.IntToStr(0) + common.IntToStr(0)); - } - - /* - function SendChannelOpenConfirmation(socket, recipientChannelId, senderChannelId, initialWindowSize) { - Write(socket, String.fromCharCode(APFProtocol.CHANNEL_OPEN_CONFIRMATION) + common.IntToStr(recipientChannelId) + common.IntToStr(senderChannelId) + common.IntToStr(initialWindowSize) + common.IntToStr(-1)); - } - */ - - function SendChannelOpen(socket, direct, channelid, windowsize, target, targetport, source, sourceport) { - var connectionType = ((direct == true) ? "direct-tcpip" : "forwarded-tcpip"); - if ((target == null) || (target == null)) target = ''; // TODO: Reports of target being undefined that causes target.length to fail. This is a hack. - Write(socket, String.fromCharCode(APFProtocol.CHANNEL_OPEN) + common.IntToStr(connectionType.length) + connectionType + common.IntToStr(channelid) + common.IntToStr(windowsize) + common.IntToStr(-1) + common.IntToStr(target.length) + target + common.IntToStr(targetport) + common.IntToStr(source.length) + source + common.IntToStr(sourceport)); - } - - function SendChannelClose(socket, channelid) { - Write(socket, String.fromCharCode(APFProtocol.CHANNEL_CLOSE) + common.IntToStr(channelid)); - } - - function SendChannelData(socket, channelid, data) { - Write(socket, String.fromCharCode(APFProtocol.CHANNEL_DATA) + common.IntToStr(channelid) + common.IntToStr(data.length) + data); - } - - function SendChannelWindowAdjust(socket, channelid, bytestoadd) { - parent.debug('apfcmd', 'SendChannelWindowAdjust', channelid, bytestoadd); - Write(socket, String.fromCharCode(APFProtocol.CHANNEL_WINDOW_ADJUST) + common.IntToStr(channelid) + common.IntToStr(bytestoadd)); - } - - /* - function SendDisconnect(socket, reasonCode) { - Write(socket, String.fromCharCode(APFProtocol.DISCONNECT) + common.IntToStr(reasonCode) + common.ShortToStr(0)); - } - */ - - function SendUserAuthFail(socket) { - Write(socket, String.fromCharCode(APFProtocol.USERAUTH_FAILURE) + common.IntToStr(8) + 'password' + common.ShortToStr(0)); - } - - function SendUserAuthSuccess(socket) { - Write(socket, String.fromCharCode(APFProtocol.USERAUTH_SUCCESS)); - } - - function Write(socket, data) { - if (obj.args.debug) { - // Print out sent bytes - var buf = Buffer.from(data, "binary"); - console.log('APF --> (' + buf.length + '):' + buf.toString('hex')); - socket.send(buf); - } else { - socket.send(Buffer.from(data, "binary")); - } - } - - // Setup a new channel to a nodeid - obj.SetupChannelToNode = function (nodeid, targetport) { - var apfconn = obj.apfConnections[nodeid]; - if (apfconn == null) return null; - return obj.SetupChannel(apfconn, targetport); - } - - obj.SetupChannel = function (socket, targetport) { - 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 }; - SendChannelOpen(socket, false, cirachannel.channelid, cirachannel.ciraWindow, socket.tag.host, targetport, "1.2.3.4", sourceport); - - // This function writes data to this APF channel - cirachannel.write = function (data) { - if (cirachannel.state == 0) return false; - if (cirachannel.state == 1 || cirachannel.sendcredits == 0 || cirachannel.sendBuffer != null) { - // Channel is connected, but we are out of credits. Add the data to the outbound buffer. - if (cirachannel.sendBuffer == null) { cirachannel.sendBuffer = data; } else { cirachannel.sendBuffer += data; } - return true; - } - // Compute how much data we can send - if (data.length <= cirachannel.sendcredits) { - // Send the entire message - SendChannelData(cirachannel.socket, cirachannel.amtchannelid, data); - cirachannel.sendcredits -= data.length; - return true; - } - // Send a part of the message - cirachannel.sendBuffer = data.substring(cirachannel.sendcredits); - SendChannelData(cirachannel.socket, cirachannel.amtchannelid, data.substring(0, cirachannel.sendcredits)); - cirachannel.sendcredits = 0; - return false; - }; - - // This function closes this APF channel - cirachannel.close = function () { - if (cirachannel.state == 0 || cirachannel.closing == 1) return; - if (cirachannel.state == 1) { cirachannel.closing = 1; cirachannel.state = 0; if (cirachannel.onStateChange) { cirachannel.onStateChange(cirachannel, cirachannel.state); } return; } - cirachannel.state = 0; - cirachannel.closing = 1; - SendChannelClose(cirachannel.socket, cirachannel.amtchannelid); - if (cirachannel.onStateChange) { cirachannel.onStateChange(cirachannel, cirachannel.state); } - }; - - socket.tag.channels[cirachannel.channelid] = cirachannel; - return cirachannel; - }; - - function ChangeHostname(socket, host, systemid) { - if (socket.tag.host === host) return; // Nothing to change - socket.tag.host = host; - - // Change the device - obj.db.Get(socket.tag.nodeid, function (err, nodes) { - if ((nodes == null) || (nodes.length !== 1)) return; - var node = nodes[0]; - - // See if any changes need to be made - if ((node.intelamt != null) && (node.intelamt.host == host) && (node.name != null) && (node.name != '') && (node.intelamt.state == 2)) return; - - // Get the mesh for this device - obj.db.Get(node.meshid, function (err, meshes) { - if ((meshes == null) || (meshes.length !== 1)) return; - var mesh = meshes[0]; - - // Ready the node change event - var changes = ['host'], event = { etype: 'node', action: 'changenode', nodeid: node._id }; - event.msg = +": "; - - // Make the change & save - if (node.intelamt == null) node.intelamt = {}; - node.intelamt.host = host; - node.intelamt.state = 2; // TODO: this is not real AMT state - if (((node.name == null) || (node.name == '')) && (host != null) && (host != '')) { node.name = host.split('.')[0]; } // If this system has no name, set it to the start of the domain name. - if (((node.name == null) || (node.name == '')) && (systemid != null)) { node.name = systemid; } // If this system still has no name, set it to the system GUID. - obj.db.Set(node); - - // Event the node change - event.msg = 'APF changed device ' + node.name + ' from group ' + mesh.name + ': ' + changes.join(', '); - event.node = parent.webserver.CloneSafeNode(node); - if (obj.db.changeStream) { event.noact = 1; } // If DB change stream is active, don't use this event to change the node. Another event will come. - obj.parent.DispatchEvent(['*', node.meshid], obj, event); - }); - }); - } - - function guidToStr(g) { return g.substring(6, 8) + g.substring(4, 6) + g.substring(2, 4) + g.substring(0, 2) + "-" + g.substring(10, 12) + g.substring(8, 10) + "-" + g.substring(14, 16) + g.substring(12, 14) + "-" + g.substring(16, 20) + "-" + g.substring(20); } - - return obj; -}; diff --git a/meshagent.js b/meshagent.js index 888e1b1b..854b0803 100644 --- a/meshagent.js +++ b/meshagent.js @@ -1475,7 +1475,7 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) { // Change the current core information string and event it function ChangeAgentCoreInfo(command) { - if (obj.agentInfo.capabilities & 0x40) return; + if ((obj.agentInfo == null) || (obj.agentInfo.capabilities & 0x40)) return; if ((command == null) || (command == null)) return; // Safety, should never happen. // If the device is pending a change, hold. diff --git a/meshcentral.js b/meshcentral.js index d23b2ad2..5b2d1d05 100644 --- a/meshcentral.js +++ b/meshcentral.js @@ -26,7 +26,6 @@ function CreateMeshCentralServer(config, args) { obj.webserver = null; obj.redirserver = null; obj.mpsserver = null; - obj.apfserver = null; obj.mqttbroker = null; obj.swarmserver = null; obj.mailserver = null; @@ -1318,9 +1317,6 @@ function CreateMeshCentralServer(config, args) { if ((obj.args.sessiontime != null) && ((typeof obj.args.sessiontime != 'number') || (obj.args.sessiontime < 1))) { delete obj.args.sessiontime; } if (!obj.args.sessionkey) { obj.args.sessionkey = buf.toString('hex').toUpperCase(); } - // Create APF server to hook into webserver - obj.apfserver = require('./apfserver.js').CreateApfServer(obj, obj.db, obj.args); - // Create MQTT Broker to hook into webserver and mpsserver if ((typeof obj.config.settings.mqtt == 'object') && (typeof obj.config.settings.mqtt.auth == 'object') && (typeof obj.config.settings.mqtt.auth.keyid == 'string') && (typeof obj.config.settings.mqtt.auth.key == 'string')) { obj.mqttbroker = require("./mqttbroker.js").CreateMQTTBroker(obj, obj.db, obj.args); } diff --git a/meshuser.js b/meshuser.js index 0e07dfa5..6e6e5874 100644 --- a/meshuser.js +++ b/meshuser.js @@ -4670,9 +4670,12 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use if ((state == null) || (state.connectivity & 4) == 0) break; } else if (command.mode == 2) { if (parent.parent.mpsserver.ciraConnections[command.nodeid] == null) break; - } else if (command.mode == 3) { + } + /* + else if (command.mode == 3) { if (parent.parent.apfserver.apfConnections[command.nodeid] == null) break; } + */ // Get the node and the rights for this node parent.GetNodeWithRights(domain, user, command.nodeid, function (node, rights, visible) { diff --git a/mpsserver.js b/mpsserver.js index 3a019112..381c50b0 100644 --- a/mpsserver.js +++ b/mpsserver.js @@ -144,11 +144,13 @@ module.exports.CreateMpsServer = function (parent, db, args, certificates) { // Delay setting the connectivity state by 300ms to allow time for CIRA port mappings to be established // Report power state as "present" (7) until Intel AMT manager starts polling for power state. - function delayedSetConnectivityState(meshid, nodeid, connectTime) { - var f = function setConnFunc() { if (obj.ciraConnections[setConnFunc.nodeid] != null) { obj.parent.SetConnectivityState(setConnFunc.meshid, setConnFunc.nodeid, setConnFunc.connectTime, 2, 7); } } + function delayedSetConnectivityState(meshid, nodeid, connectTime, connType) { + if (nodeid.startsWith('*')) return; // Don't set connectivity state for Intel AMT self agent relay + var f = function setConnFunc() { if (obj.ciraConnections[setConnFunc.nodeid] != null) { obj.parent.SetConnectivityState(setConnFunc.meshid, setConnFunc.nodeid, setConnFunc.connectTime, setConnFunc.connType, 7); } } f.nodeid = nodeid; f.meshid = meshid; f.connectTime = connectTime; + f.connType = connType; setTimeout(f, 300); } @@ -220,13 +222,47 @@ module.exports.CreateMpsServer = function (parent, db, args, certificates) { return packet_len; } + obj.onWebSocketConnection = function (socket) { + connectionCount++; + // connType: 2 = CIRA, 8 = Relay + socket.tag = { first: true, connType: 2, clientCert: null, accumulator: '', activetunnels: 0, boundPorts: [], websocket: true, socket: socket, host: null, nextchannelid: 4, channels: {}, nextsourceport: 0 }; + socket.websocket = 1; + parent.debug('mps', "New CIRA websocket connection"); + + socket.on('message', function (data) { + if (args.mpsdebug) { var buf = Buffer.from(data, 'binary'); console.log("MPS <-- (" + buf.length + "):" + buf.toString('hex')); } // Print out received bytes + this.tag.accumulator += data.toString('binary'); // Append as binary string + try { + // Parse all of the APF data we can + var l = 0; + do { l = ProcessCommand(this); if (l > 0) { this.tag.accumulator = this.tag.accumulator.substring(l); } } while (l > 0); + if (l < 0) { this.terminate(); } + } catch (e) { + console.log(e); + } + }); + + socket.addListener('close', function () { + socketClosedCount++; + parent.debug('mps', "CIRA websocket closed", this.tag.meshid, this.tag.nodeid); + try { delete obj.ciraConnections[socket.tag.nodeid]; } catch (e) { } + if (!this.tag.nodeid.startsWith('*')) { obj.parent.ClearConnectivityState(this.tag.meshid, this.tag.nodeid, this.tag.connType); } + }); + + socket.addListener('error', function (e) { + socketErrorCount++; + parent.debug('mps', "CIRA websocket connection error", e); + }); + } + // Called when a new TLS/TCP connection is accepted function onConnection(socket) { connectionCount++; + // connType: 2 = CIRA, 8 = Relay if (obj.args.mpstlsoffload) { - socket.tag = { first: true, clientCert: null, accumulator: '', activetunnels: 0, boundPorts: [], socket: socket, host: null, nextchannelid: 4, channels: {}, nextsourceport: 0 }; + socket.tag = { first: true, connType: 2, clientCert: null, accumulator: '', activetunnels: 0, boundPorts: [], socket: socket, host: null, nextchannelid: 4, channels: {}, nextsourceport: 0 }; } else { - socket.tag = { first: true, clientCert: socket.getPeerCertificate(true), accumulator: '', activetunnels: 0, boundPorts: [], socket: socket, host: null, nextchannelid: 4, channels: {}, nextsourceport: 0 }; + socket.tag = { first: true, connType: 2, clientCert: socket.getPeerCertificate(true), accumulator: '', activetunnels: 0, boundPorts: [], socket: socket, host: null, nextchannelid: 4, channels: {}, nextsourceport: 0 }; } socket.setEncoding('binary'); parent.debug('mps', "New CIRA connection"); @@ -235,6 +271,19 @@ module.exports.CreateMpsServer = function (parent, db, args, certificates) { socket.setTimeout(MAX_IDLE); socket.on('timeout', () => { ciraTimeoutCount++; parent.debug('mps', "CIRA timeout, disconnecting."); try { socket.end(); } catch (e) { } }); + socket.addListener('close', function () { + socketClosedCount++; + parent.debug('mps', 'CIRA connection closed'); + try { delete obj.ciraConnections[socket.tag.nodeid]; } catch (e) { } + obj.parent.ClearConnectivityState(socket.tag.meshid, socket.tag.nodeid, socket.tag.connType); + }); + + socket.addListener('error', function (e) { + socketErrorCount++; + parent.debug('mps', 'CIRA connection error', e); + //console.log("MPS Error: " + socket.remoteAddress); + }); + socket.addListener('data', function (data) { if (args.mpsdebug) { var buf = Buffer.from(data, 'binary'); console.log("MPS --> (" + buf.length + "):" + buf.toString('hex')); } // Print out received bytes socket.tag.accumulator += data; @@ -334,8 +383,7 @@ module.exports.CreateMpsServer = function (parent, db, args, certificates) { // Add the connection to the MPS connection list obj.ciraConnections[socket.tag.nodeid] = socket; - //obj.parent.SetConnectivityState(socket.tag.meshid, socket.tag.nodeid, socket.tag.connectTime, 2, 7); // TODO: Right now report power state as "present" (7) until we can poll. - delayedSetConnectivityState(socket.tag.meshid, socket.tag.nodeid, socket.tag.connectTime); + delayedSetConnectivityState(socket.tag.meshid, socket.tag.nodeid, socket.tag.connectTime, socket.tag.connType); } }); return; @@ -365,7 +413,7 @@ module.exports.CreateMpsServer = function (parent, db, args, certificates) { // Add the connection to the MPS connection list obj.ciraConnections[socket.tag.nodeid] = socket; - delayedSetConnectivityState(socket.tag.meshid, socket.tag.nodeid, socket.tag.connectTime); + delayedSetConnectivityState(socket.tag.meshid, socket.tag.nodeid, socket.tag.connectTime, socket.tag.connType); }); } else { // This node connected without certificate authentication, use password auth @@ -382,355 +430,294 @@ module.exports.CreateMpsServer = function (parent, db, args, certificates) { console.log(e); } }); + } - // Process one APF command - function ProcessCommand(socket) { - var cmd = socket.tag.accumulator.charCodeAt(0); - var len = socket.tag.accumulator.length; - var data = socket.tag.accumulator; - if (len == 0) { return 0; } + // Process one APF command + function ProcessCommand(socket) { + var cmd = socket.tag.accumulator.charCodeAt(0); + var len = socket.tag.accumulator.length; + var data = socket.tag.accumulator; + if (len == 0) { return 0; } - switch (cmd) { - case APFProtocol.KEEPALIVE_REQUEST: { - if (len < 5) return 0; - parent.debug('mpscmd', '--> KEEPALIVE_REQUEST'); - SendKeepAliveReply(socket, common.ReadInt(data, 1)); - return 5; + switch (cmd) { + case APFProtocol.KEEPALIVE_REQUEST: { + if (len < 5) return 0; + parent.debug('mpscmd', '--> KEEPALIVE_REQUEST'); + SendKeepAliveReply(socket, common.ReadInt(data, 1)); + return 5; + } + case APFProtocol.KEEPALIVE_REPLY: { + if (len < 5) return 0; + parent.debug('mpscmd', '--> KEEPALIVE_REPLY'); + return 5; + } + case APFProtocol.PROTOCOLVERSION: { + if (len < 93) return 0; + protocolVersionCount++; + socket.tag.MajorVersion = common.ReadInt(data, 1); + socket.tag.MinorVersion = common.ReadInt(data, 5); + socket.tag.SystemId = guidToStr(common.rstr2hex(data.substring(13, 29))).toLowerCase(); + parent.debug('mpscmd', '--> PROTOCOLVERSION', socket.tag.MajorVersion, socket.tag.MinorVersion, socket.tag.SystemId); + return 93; + } + case APFProtocol.USERAUTH_REQUEST: { + if (len < 13) return 0; + userAuthRequestCount++; + var usernameLen = common.ReadInt(data, 1); + var username = data.substring(5, 5 + usernameLen); + var serviceNameLen = common.ReadInt(data, 5 + usernameLen); + var serviceName = data.substring(9 + usernameLen, 9 + usernameLen + serviceNameLen); + var methodNameLen = common.ReadInt(data, 9 + usernameLen + serviceNameLen); + var methodName = data.substring(13 + usernameLen + serviceNameLen, 13 + usernameLen + serviceNameLen + methodNameLen); + var passwordLen = 0, password = null; + if (methodName == 'password') { + passwordLen = common.ReadInt(data, 14 + usernameLen + serviceNameLen + methodNameLen); + password = data.substring(18 + usernameLen + serviceNameLen + methodNameLen, 18 + usernameLen + serviceNameLen + methodNameLen + passwordLen); } - case APFProtocol.KEEPALIVE_REPLY: { - if (len < 5) return 0; - parent.debug('mpscmd', '--> KEEPALIVE_REPLY'); - return 5; - } - case APFProtocol.PROTOCOLVERSION: { - if (len < 93) return 0; - protocolVersionCount++; - socket.tag.MajorVersion = common.ReadInt(data, 1); - socket.tag.MinorVersion = common.ReadInt(data, 5); - socket.tag.SystemId = guidToStr(common.rstr2hex(data.substring(13, 29))).toLowerCase(); - parent.debug('mpscmd', '--> PROTOCOLVERSION', socket.tag.MajorVersion, socket.tag.MinorVersion, socket.tag.SystemId); - return 93; - } - case APFProtocol.USERAUTH_REQUEST: { - if (len < 13) return 0; - userAuthRequestCount++; - var usernameLen = common.ReadInt(data, 1); - var username = data.substring(5, 5 + usernameLen); - var serviceNameLen = common.ReadInt(data, 5 + usernameLen); - var serviceName = data.substring(9 + usernameLen, 9 + usernameLen + serviceNameLen); - var methodNameLen = common.ReadInt(data, 9 + usernameLen + serviceNameLen); - var methodName = data.substring(13 + usernameLen + serviceNameLen, 13 + usernameLen + serviceNameLen + methodNameLen); - var passwordLen = 0, password = null; - if (methodName == 'password') { - passwordLen = common.ReadInt(data, 14 + usernameLen + serviceNameLen + methodNameLen); - password = data.substring(18 + usernameLen + serviceNameLen + methodNameLen, 18 + usernameLen + serviceNameLen + methodNameLen + passwordLen); - } - //console.log('MPS:USERAUTH_REQUEST user=' + username + ', service=' + serviceName + ', method=' + methodName + ', password=' + password); - parent.debug('mpscmd', '--> USERAUTH_REQUEST user=' + username + ', service=' + serviceName + ', method=' + methodName + ', password=' + password); + //console.log('MPS:USERAUTH_REQUEST user=' + username + ', service=' + serviceName + ', method=' + methodName + ', password=' + password); + parent.debug('mpscmd', '--> USERAUTH_REQUEST user=' + username + ', service=' + serviceName + ', method=' + methodName + ', password=' + password); - // Check the CIRA password - if ((args.mpspass != null) && (password != args.mpspass)) { incorrectPasswordCount++; parent.debug('mps', 'Incorrect password', username, password); SendUserAuthFail(socket); return -1; } + // Check the CIRA password + if ((args.mpspass != null) && (password != args.mpspass)) { incorrectPasswordCount++; parent.debug('mps', 'Incorrect password', username, password); SendUserAuthFail(socket); return -1; } - // Check the CIRA username, which should be the start of the MeshID. - if (usernameLen != 16) { badUserNameLengthCount++; parent.debug('mps', 'Username length not 16', username, password); SendUserAuthFail(socket); return -1; } - var meshIdStart = '/' + username, mesh = null; - if (obj.parent.webserver.meshes) { for (var i in obj.parent.webserver.meshes) { if (obj.parent.webserver.meshes[i]._id.replace(/\@/g, 'X').replace(/\$/g, 'X').indexOf(meshIdStart) > 0) { mesh = obj.parent.webserver.meshes[i]; break; } } } - if (mesh == null) { meshNotFoundCount++; parent.debug('mps', 'Device group not found', username, password); SendUserAuthFail(socket); return -1; } + // Check the CIRA username, which should be the start of the MeshID. + if (usernameLen != 16) { badUserNameLengthCount++; parent.debug('mps', 'Username length not 16', username, password); SendUserAuthFail(socket); return -1; } + var meshIdStart = '/' + username, mesh = null; + if (obj.parent.webserver.meshes) { for (var i in obj.parent.webserver.meshes) { if (obj.parent.webserver.meshes[i]._id.replace(/\@/g, 'X').replace(/\$/g, 'X').indexOf(meshIdStart) > 0) { mesh = obj.parent.webserver.meshes[i]; break; } } } + if (mesh == null) { meshNotFoundCount++; parent.debug('mps', 'Device group not found', username, password); SendUserAuthFail(socket); return -1; } - // If this is a agent-less mesh, use the device guid 3 times as ID. - if (mesh.mtype == 1) { - // Intel AMT GUID (socket.tag.SystemId) will be used as NodeID - var systemid = socket.tag.SystemId.split('-').join(''); - var nodeid = Buffer.from(systemid + systemid + systemid, 'hex').toString('base64').replace(/\+/g, '@').replace(/\//g, '$'); - var domain = obj.parent.config.domains[mesh.domain]; - socket.tag.domain = domain; - socket.tag.domainid = mesh.domain; - socket.tag.name = ''; - socket.tag.nodeid = 'node/' + mesh.domain + '/' + nodeid; // Turn 16bit systemid guid into 48bit nodeid that is base64 encoded - socket.tag.meshid = mesh._id; + // If this is a agent-less mesh, use the device guid 3 times as ID. + if (mesh.mtype == 1) { + // Intel AMT GUID (socket.tag.SystemId) will be used as NodeID + var systemid = socket.tag.SystemId.split('-').join(''); + var nodeid = Buffer.from(systemid + systemid + systemid, 'hex').toString('base64').replace(/\+/g, '@').replace(/\//g, '$'); + var domain = obj.parent.config.domains[mesh.domain]; + socket.tag.domain = domain; + socket.tag.domainid = mesh.domain; + socket.tag.name = ''; + socket.tag.nodeid = 'node/' + mesh.domain + '/' + nodeid; // Turn 16bit systemid guid into 48bit nodeid that is base64 encoded + socket.tag.meshid = mesh._id; + socket.tag.connectTime = Date.now(); + + obj.db.Get(socket.tag.nodeid, function (err, nodes) { + if ((nodes == null) || (nodes.length !== 1)) { + // Check if we already have too many devices for this domain + if (domain.limits && (typeof domain.limits.maxdevices == 'number')) { + db.isMaxType(domain.limits.maxdevices, 'node', mesh.domain, function (ismax, count) { + if (ismax == true) { + // Too many devices in this domain. + maxDomainDevicesReached++; + console.log('Too many devices on this domain to accept the CIRA connection. meshid: ' + socket.tag.meshid); + socket.end(); + } else { + // We are under the limit, create the new device. + // Node is not in the database, add it. Credentials will be empty until added by the user. + var device = { type: 'node', mtype: 1, _id: socket.tag.nodeid, meshid: socket.tag.meshid, name: socket.tag.name, host: null, domain: mesh.domain, intelamt: { user: '', pass: '', tls: 0, state: 2 } }; + obj.db.Set(device); + + // Event the new node + addedDeviceCount++; + var change = 'CIRA added device ' + socket.tag.name + ' to group ' + mesh.name; + 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 + obj.ciraConnections[socket.tag.nodeid] = socket; + delayedSetConnectivityState(socket.tag.meshid, socket.tag.nodeid, socket.tag.connectTime, socket.tag.connType); + SendUserAuthSuccess(socket); // Notify the auth success on the CIRA connection + } + }); + return; + } else { + // Node is not in the database, add it. Credentials will be empty until added by the user. + var device = { type: 'node', mtype: 1, _id: socket.tag.nodeid, meshid: socket.tag.meshid, name: socket.tag.name, host: null, domain: mesh.domain, intelamt: { user: '', pass: '', tls: 0, state: 2 } }; + obj.db.Set(device); + + // Event the new node + addedDeviceCount++; + var change = 'CIRA added device ' + socket.tag.name + ' to group ' + mesh.name; + obj.parent.DispatchEvent(['*', socket.tag.meshid], obj, { etype: 'node', action: 'addnode', node: parent.webserver.CloneSafeNode(device), msg: change, domain: mesh.domain }); + } + } else { + // Node is already present + var node = nodes[0]; + socket.tag.meshid = node.meshid; // Correct the MeshID if the node has moved. + if ((node.intelamt != null) && (node.intelamt.state == 2)) { socket.tag.host = node.intelamt.host; } + } + + // Add the connection to the MPS connection list + obj.ciraConnections[socket.tag.nodeid] = socket; + delayedSetConnectivityState(socket.tag.meshid, socket.tag.nodeid, socket.tag.connectTime, socket.tag.connType); + 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 + // Intel AMT GUID (socket.tag.SystemId) will be used to search the node + obj.db.getAmtUuidNode(socket.tag.SystemId, function (err, nodes) { // TODO: May need to optimize this request with indexes + if ((nodes == null) || (nodes.length === 0) || (obj.parent.webserver.meshes == null)) { + // New CIRA connection for unknown node, disconnect. + unknownNodeCount++; + console.log('CIRA connection for unknown node. groupid: ' + mesh._id + ', uuid: ' + socket.tag.SystemId); + socket.end(); + return; + } + + // Looking at nodes that match this UUID, select one in the same domain and mesh type. + var node = null; + for (var i in nodes) { + if (mesh.domain == nodes[i].domain) { + var nodemesh = obj.parent.webserver.meshes[nodes[i].meshid]; + if ((nodemesh != null) && (nodemesh.mtype == 2)) { node = nodes[i]; } + } + } + + if (node == null) { + // New CIRA connection for unknown node, disconnect. + unknownNodeCount++; + console.log('CIRA connection for unknown node. candidate(s): ' + nodes.length + ', groupid: ' + mesh._id + ', uuid: ' + socket.tag.SystemId); + socket.end(); + return; + } + + // Node is present + if ((node.intelamt != null) && (node.intelamt.state == 2)) { socket.tag.host = node.intelamt.host; } + socket.tag.nodeid = node._id; + socket.tag.meshid = node.meshid; // Correct the MeshID if the node has moved. socket.tag.connectTime = Date.now(); - obj.db.Get(socket.tag.nodeid, function (err, nodes) { - if ((nodes == null) || (nodes.length !== 1)) { - // Check if we already have too many devices for this domain - if (domain.limits && (typeof domain.limits.maxdevices == 'number')) { - db.isMaxType(domain.limits.maxdevices, 'node', mesh.domain, function (ismax, count) { - if (ismax == true) { - // Too many devices in this domain. - maxDomainDevicesReached++; - console.log('Too many devices on this domain to accept the CIRA connection. meshid: ' + socket.tag.meshid); - socket.end(); - } else { - // We are under the limit, create the new device. - // Node is not in the database, add it. Credentials will be empty until added by the user. - var device = { type: 'node', mtype: 1, _id: socket.tag.nodeid, meshid: socket.tag.meshid, name: socket.tag.name, host: null, domain: mesh.domain, intelamt: { user: '', pass: '', tls: 0, state: 2 } }; - obj.db.Set(device); - - // Event the new node - addedDeviceCount++; - var change = 'CIRA added device ' + socket.tag.name + ' to group ' + mesh.name; - 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 - obj.ciraConnections[socket.tag.nodeid] = socket; - delayedSetConnectivityState(socket.tag.meshid, socket.tag.nodeid, socket.tag.connectTime); - SendUserAuthSuccess(socket); // Notify the auth success on the CIRA connection - } - }); - return; - } else { - // Node is not in the database, add it. Credentials will be empty until added by the user. - var device = { type: 'node', mtype: 1, _id: socket.tag.nodeid, meshid: socket.tag.meshid, name: socket.tag.name, host: null, domain: mesh.domain, intelamt: { user: '', pass: '', tls: 0, state: 2 } }; - obj.db.Set(device); - - // Event the new node - addedDeviceCount++; - var change = 'CIRA added device ' + socket.tag.name + ' to group ' + mesh.name; - obj.parent.DispatchEvent(['*', socket.tag.meshid], obj, { etype: 'node', action: 'addnode', node: parent.webserver.CloneSafeNode(device), msg: change, domain: mesh.domain }); - } - } else { - // Node is already present - var node = nodes[0]; - socket.tag.meshid = node.meshid; // Correct the MeshID if the node has moved. - if ((node.intelamt != null) && (node.intelamt.state == 2)) { socket.tag.host = node.intelamt.host; } - } - - // Add the connection to the MPS connection list - obj.ciraConnections[socket.tag.nodeid] = socket; - delayedSetConnectivityState(socket.tag.meshid, socket.tag.nodeid, socket.tag.connectTime); - 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 - // Intel AMT GUID (socket.tag.SystemId) will be used to search the node - obj.db.getAmtUuidNode(socket.tag.SystemId, function (err, nodes) { // TODO: May need to optimize this request with indexes - if ((nodes == null) || (nodes.length === 0) || (obj.parent.webserver.meshes == null)) { - // New CIRA connection for unknown node, disconnect. - unknownNodeCount++; - console.log('CIRA connection for unknown node. groupid: ' + mesh._id + ', uuid: ' + socket.tag.SystemId); - socket.end(); - return; - } - - // Looking at nodes that match this UUID, select one in the same domain and mesh type. - var node = null; - for (var i in nodes) { - if (mesh.domain == nodes[i].domain) { - var nodemesh = obj.parent.webserver.meshes[nodes[i].meshid]; - if ((nodemesh != null) && (nodemesh.mtype == 2)) { node = nodes[i]; } - } - } - - if (node == null) { - // New CIRA connection for unknown node, disconnect. - unknownNodeCount++; - console.log('CIRA connection for unknown node. candidate(s): ' + nodes.length + ', groupid: ' + mesh._id + ', uuid: ' + socket.tag.SystemId); - socket.end(); - return; - } - - // Node is present - if ((node.intelamt != null) && (node.intelamt.state == 2)) { socket.tag.host = node.intelamt.host; } - socket.tag.nodeid = node._id; - socket.tag.meshid = node.meshid; // Correct the MeshID if the node has moved. - socket.tag.connectTime = Date.now(); - - // Add the connection to the MPS connection list - obj.ciraConnections[socket.tag.nodeid] = socket; - delayedSetConnectivityState(socket.tag.meshid, socket.tag.nodeid, socket.tag.connectTime); - SendUserAuthSuccess(socket); // Notify the auth success on the CIRA connection - }); - } else { // Unknown mesh type - // New CIRA connection for unknown node, disconnect. - unknownMeshIdCount++; - console.log('CIRA connection to a unknown group type. groupid: ' + socket.tag.meshid); - socket.end(); - return; - } - return 18 + usernameLen + serviceNameLen + methodNameLen + passwordLen; + // Add the connection to the MPS connection list + obj.ciraConnections[socket.tag.nodeid] = socket; + delayedSetConnectivityState(socket.tag.meshid, socket.tag.nodeid, socket.tag.connectTime, socket.tag.connType); + SendUserAuthSuccess(socket); // Notify the auth success on the CIRA connection + }); + } else { // Unknown mesh type + // New CIRA connection for unknown node, disconnect. + unknownMeshIdCount++; + console.log('CIRA connection to a unknown group type. groupid: ' + socket.tag.meshid); + socket.end(); + return; } - case APFProtocol.SERVICE_REQUEST: { - if (len < 5) return 0; - var xserviceNameLen = common.ReadInt(data, 1); - if (len < 5 + xserviceNameLen) return 0; - var xserviceName = data.substring(5, 5 + xserviceNameLen); - parent.debug('mpscmd', '--> SERVICE_REQUEST', xserviceName); - if (xserviceName == "pfwd@amt.intel.com") { SendServiceAccept(socket, "pfwd@amt.intel.com"); } - if (xserviceName == "auth@amt.intel.com") { SendServiceAccept(socket, "auth@amt.intel.com"); } - return 5 + xserviceNameLen; + return 18 + usernameLen + serviceNameLen + methodNameLen + passwordLen; + } + case APFProtocol.SERVICE_REQUEST: { + if (len < 5) return 0; + var xserviceNameLen = common.ReadInt(data, 1); + if (len < 5 + xserviceNameLen) return 0; + var xserviceName = data.substring(5, 5 + xserviceNameLen); + parent.debug('mpscmd', '--> SERVICE_REQUEST', xserviceName); + if (xserviceName == "pfwd@amt.intel.com") { SendServiceAccept(socket, "pfwd@amt.intel.com"); } + if (xserviceName == "auth@amt.intel.com") { SendServiceAccept(socket, "auth@amt.intel.com"); } + return 5 + xserviceNameLen; + } + case APFProtocol.GLOBAL_REQUEST: { + if (len < 14) return 0; + var requestLen = common.ReadInt(data, 1); + if (len < 14 + requestLen) return 0; + var request = data.substring(5, 5 + requestLen); + //var wantResponse = data.charCodeAt(5 + requestLen); + + if (request == 'tcpip-forward') { + var addrLen = common.ReadInt(data, 6 + requestLen); + if (len < 14 + requestLen + addrLen) return 0; + var addr = data.substring(10 + requestLen, 10 + requestLen + addrLen); + var port = common.ReadInt(data, 10 + requestLen + addrLen); + parent.debug('mpscmd', '--> GLOBAL_REQUEST', request, addr + ':' + port); + ChangeHostname(socket, addr, socket.tag.SystemId); + if (socket.tag.boundPorts.indexOf(port) == -1) { socket.tag.boundPorts.push(port); } + SendTcpForwardSuccessReply(socket, port); + return 14 + requestLen + addrLen; } - case APFProtocol.GLOBAL_REQUEST: { - if (len < 14) return 0; - var requestLen = common.ReadInt(data, 1); - if (len < 14 + requestLen) return 0; - var request = data.substring(5, 5 + requestLen); - //var wantResponse = data.charCodeAt(5 + requestLen); - if (request == 'tcpip-forward') { - var addrLen = common.ReadInt(data, 6 + requestLen); - if (len < 14 + requestLen + addrLen) return 0; - var addr = data.substring(10 + requestLen, 10 + requestLen + addrLen); - var port = common.ReadInt(data, 10 + requestLen + addrLen); - parent.debug('mpscmd', '--> GLOBAL_REQUEST', request, addr + ':' + port); - ChangeHostname(socket, addr, socket.tag.SystemId); - if (socket.tag.boundPorts.indexOf(port) == -1) { socket.tag.boundPorts.push(port); } - SendTcpForwardSuccessReply(socket, port); - return 14 + requestLen + addrLen; - } - - if (request == 'cancel-tcpip-forward') { - var addrLen = common.ReadInt(data, 6 + requestLen); - if (len < 14 + requestLen + addrLen) return 0; - var addr = data.substring(10 + requestLen, 10 + requestLen + addrLen); - var port = common.ReadInt(data, 10 + requestLen + addrLen); - parent.debug('mpscmd', '--> GLOBAL_REQUEST', request, addr + ':' + port); - var portindex = socket.tag.boundPorts.indexOf(port); - if (portindex >= 0) { socket.tag.boundPorts.splice(portindex, 1); } - SendTcpForwardCancelReply(socket); - return 14 + requestLen + addrLen; - } - - if (request == 'udp-send-to@amt.intel.com') { - var addrLen = common.ReadInt(data, 6 + requestLen); - if (len < 26 + requestLen + addrLen) return 0; - var addr = data.substring(10 + requestLen, 10 + requestLen + addrLen); - var port = common.ReadInt(data, 10 + requestLen + addrLen); - var oaddrLen = common.ReadInt(data, 14 + requestLen + addrLen); - if (len < 26 + requestLen + addrLen + oaddrLen) return 0; - var oaddr = data.substring(18 + requestLen, 18 + requestLen + addrLen); - var oport = common.ReadInt(data, 18 + requestLen + addrLen + oaddrLen); - var datalen = common.ReadInt(data, 22 + requestLen + addrLen + oaddrLen); - if (len < 26 + requestLen + addrLen + oaddrLen + datalen) return 0; - parent.debug('mpscmd', '--> GLOBAL_REQUEST', request, addr + ':' + port, oaddr + ':' + oport, datalen); - // TODO - return 26 + requestLen + addrLen + oaddrLen + datalen; - } - - return 6 + requestLen; + if (request == 'cancel-tcpip-forward') { + var addrLen = common.ReadInt(data, 6 + requestLen); + if (len < 14 + requestLen + addrLen) return 0; + var addr = data.substring(10 + requestLen, 10 + requestLen + addrLen); + var port = common.ReadInt(data, 10 + requestLen + addrLen); + parent.debug('mpscmd', '--> GLOBAL_REQUEST', request, addr + ':' + port); + var portindex = socket.tag.boundPorts.indexOf(port); + if (portindex >= 0) { socket.tag.boundPorts.splice(portindex, 1); } + SendTcpForwardCancelReply(socket); + return 14 + requestLen + addrLen; } - case APFProtocol.CHANNEL_OPEN: { - if (len < 33) return 0; - var ChannelTypeLength = common.ReadInt(data, 1); - if (len < (33 + ChannelTypeLength)) return 0; - // Decode channel identifiers and window size - var ChannelType = data.substring(5, 5 + ChannelTypeLength); - var SenderChannel = common.ReadInt(data, 5 + ChannelTypeLength); - var WindowSize = common.ReadInt(data, 9 + ChannelTypeLength); + if (request == 'udp-send-to@amt.intel.com') { + var addrLen = common.ReadInt(data, 6 + requestLen); + if (len < 26 + requestLen + addrLen) return 0; + var addr = data.substring(10 + requestLen, 10 + requestLen + addrLen); + var port = common.ReadInt(data, 10 + requestLen + addrLen); + var oaddrLen = common.ReadInt(data, 14 + requestLen + addrLen); + if (len < 26 + requestLen + addrLen + oaddrLen) return 0; + var oaddr = data.substring(18 + requestLen, 18 + requestLen + addrLen); + var oport = common.ReadInt(data, 18 + requestLen + addrLen + oaddrLen); + var datalen = common.ReadInt(data, 22 + requestLen + addrLen + oaddrLen); + if (len < 26 + requestLen + addrLen + oaddrLen + datalen) return 0; + parent.debug('mpscmd', '--> GLOBAL_REQUEST', request, addr + ':' + port, oaddr + ':' + oport, datalen); + // TODO + return 26 + requestLen + addrLen + oaddrLen + datalen; + } - // Decode the target - var TargetLen = common.ReadInt(data, 17 + ChannelTypeLength); - if (len < (33 + ChannelTypeLength + TargetLen)) return 0; - var Target = data.substring(21 + ChannelTypeLength, 21 + ChannelTypeLength + TargetLen); - var TargetPort = common.ReadInt(data, 21 + ChannelTypeLength + TargetLen); + return 6 + requestLen; + } + case APFProtocol.CHANNEL_OPEN: { + if (len < 33) return 0; + var ChannelTypeLength = common.ReadInt(data, 1); + if (len < (33 + ChannelTypeLength)) return 0; - // Decode the source - var SourceLen = common.ReadInt(data, 25 + ChannelTypeLength + TargetLen); - if (len < (33 + ChannelTypeLength + TargetLen + SourceLen)) return 0; - var Source = data.substring(29 + ChannelTypeLength + TargetLen, 29 + ChannelTypeLength + TargetLen + SourceLen); - var SourcePort = common.ReadInt(data, 29 + ChannelTypeLength + TargetLen + SourceLen); + // Decode channel identifiers and window size + var ChannelType = data.substring(5, 5 + ChannelTypeLength); + var SenderChannel = common.ReadInt(data, 5 + ChannelTypeLength); + var WindowSize = common.ReadInt(data, 9 + ChannelTypeLength); - channelOpenCount++; - parent.debug('mpscmd', '--> CHANNEL_OPEN', ChannelType, SenderChannel, WindowSize, Target + ':' + TargetPort, Source + ':' + SourcePort); + // Decode the target + var TargetLen = common.ReadInt(data, 17 + ChannelTypeLength); + if (len < (33 + ChannelTypeLength + TargetLen)) return 0; + var Target = data.substring(21 + ChannelTypeLength, 21 + ChannelTypeLength + TargetLen); + var TargetPort = common.ReadInt(data, 21 + ChannelTypeLength + TargetLen); - // Check if we understand this channel type - //if (ChannelType.toLowerCase() == "direct-tcpip") - { - // We don't understand this channel type, send an error back - SendChannelOpenFailure(socket, SenderChannel, APFChannelOpenFailureReasonCode.UnknownChannelType); - return 33 + ChannelTypeLength + TargetLen + SourceLen; - } + // Decode the source + var SourceLen = common.ReadInt(data, 25 + ChannelTypeLength + TargetLen); + if (len < (33 + ChannelTypeLength + TargetLen + SourceLen)) return 0; + var Source = data.substring(29 + ChannelTypeLength + TargetLen, 29 + ChannelTypeLength + TargetLen + SourceLen); + var SourcePort = common.ReadInt(data, 29 + ChannelTypeLength + TargetLen + SourceLen); - /* - // This is a correct connection. Lets get it setup - var MeshAmtEventEndpoint = { ServerChannel: GetNextBindId(), AmtChannel: SenderChannel, MaxWindowSize: 2048, CurrentWindowSize:2048, SendWindow: WindowSize, InfoHeader: "Target: " + Target + ":" + TargetPort + ", Source: " + Source + ":" + SourcePort}; - // TODO: Connect this socket for a WSMAN event - SendChannelOpenConfirmation(socket, SenderChannel, MeshAmtEventEndpoint.ServerChannel, MeshAmtEventEndpoint.MaxWindowSize); - */ + channelOpenCount++; + parent.debug('mpscmd', '--> CHANNEL_OPEN', ChannelType, SenderChannel, WindowSize, Target + ':' + TargetPort, Source + ':' + SourcePort); + // Check if we understand this channel type + //if (ChannelType.toLowerCase() == "direct-tcpip") + { + // We don't understand this channel type, send an error back + SendChannelOpenFailure(socket, SenderChannel, APFChannelOpenFailureReasonCode.UnknownChannelType); return 33 + ChannelTypeLength + TargetLen + SourceLen; } - case APFProtocol.CHANNEL_OPEN_CONFIRMATION: - { - if (len < 17) return 0; - var RecipientChannel = common.ReadInt(data, 1); - var SenderChannel = common.ReadInt(data, 5); - var WindowSize = common.ReadInt(data, 9); - socket.tag.activetunnels++; - var cirachannel = socket.tag.channels[RecipientChannel]; - if (cirachannel == null) { /*console.log("MPS Error in CHANNEL_OPEN_CONFIRMATION: Unable to find channelid " + RecipientChannel);*/ return 17; } - cirachannel.amtchannelid = SenderChannel; - cirachannel.sendcredits = cirachannel.amtCiraWindow = WindowSize; - channelOpenConfirmCount++; - parent.debug('mpscmd', '--> CHANNEL_OPEN_CONFIRMATION', RecipientChannel, SenderChannel, WindowSize); - if (cirachannel.closing == 1) { - // Close this channel - SendChannelClose(cirachannel.socket, cirachannel.amtchannelid); - } else { - cirachannel.state = 2; - // Send any pending data - if (cirachannel.sendBuffer != null) { - if (cirachannel.sendBuffer.length <= cirachannel.sendcredits) { - // Send the entire pending buffer - SendChannelData(cirachannel.socket, cirachannel.amtchannelid, cirachannel.sendBuffer); - cirachannel.sendcredits -= cirachannel.sendBuffer.length; - delete cirachannel.sendBuffer; - if (cirachannel.onSendOk) { cirachannel.onSendOk(cirachannel); } - } else { - // Send a part of the pending buffer - SendChannelData(cirachannel.socket, cirachannel.amtchannelid, cirachannel.sendBuffer.substring(0, cirachannel.sendcredits)); - cirachannel.sendBuffer = cirachannel.sendBuffer.substring(cirachannel.sendcredits); - cirachannel.sendcredits = 0; - } - } - // Indicate the channel is open - if (cirachannel.onStateChange) { cirachannel.onStateChange(cirachannel, cirachannel.state); } - } - return 17; - } - case APFProtocol.CHANNEL_OPEN_FAILURE: - { - if (len < 17) return 0; - var RecipientChannel = common.ReadInt(data, 1); - var ReasonCode = common.ReadInt(data, 5); - channelOpenFailCount++; - parent.debug('mpscmd', '--> CHANNEL_OPEN_FAILURE', RecipientChannel, ReasonCode); - var cirachannel = socket.tag.channels[RecipientChannel]; - if (cirachannel == null) { console.log("MPS Error in CHANNEL_OPEN_FAILURE: Unable to find channelid " + RecipientChannel); return 17; } - if (cirachannel.state > 0) { - cirachannel.state = 0; - if (cirachannel.onStateChange) { cirachannel.onStateChange(cirachannel, cirachannel.state); } - delete socket.tag.channels[RecipientChannel]; - } - return 17; - } - case APFProtocol.CHANNEL_CLOSE: - { - if (len < 5) return 0; - var RecipientChannel = common.ReadInt(data, 1); - channelCloseCount++; - parent.debug('mpscmd', '--> CHANNEL_CLOSE', RecipientChannel); - var cirachannel = socket.tag.channels[RecipientChannel]; - if (cirachannel == null) { console.log("MPS Error in CHANNEL_CLOSE: Unable to find channelid " + RecipientChannel); return 5; } - socket.tag.activetunnels--; - if (cirachannel.state > 0) { - cirachannel.state = 0; - if (cirachannel.onStateChange) { cirachannel.onStateChange(cirachannel, cirachannel.state); } - SendChannelClose(cirachannel.socket, cirachannel.amtchannelid); - delete socket.tag.channels[RecipientChannel]; - } - return 5; - } - case APFProtocol.CHANNEL_WINDOW_ADJUST: - { - if (len < 9) return 0; - var RecipientChannel = common.ReadInt(data, 1); - var ByteToAdd = common.ReadInt(data, 5); - var cirachannel = socket.tag.channels[RecipientChannel]; - if (cirachannel == null) { console.log("MPS Error in CHANNEL_WINDOW_ADJUST: Unable to find channelid " + RecipientChannel); return 9; } - cirachannel.sendcredits += ByteToAdd; - parent.debug('mpscmd', '--> CHANNEL_WINDOW_ADJUST', RecipientChannel, ByteToAdd, cirachannel.sendcredits); - if (cirachannel.state == 2 && cirachannel.sendBuffer != null) { - // Compute how much data we can send + + /* + // This is a correct connection. Lets get it setup + var MeshAmtEventEndpoint = { ServerChannel: GetNextBindId(), AmtChannel: SenderChannel, MaxWindowSize: 2048, CurrentWindowSize:2048, SendWindow: WindowSize, InfoHeader: "Target: " + Target + ":" + TargetPort + ", Source: " + Source + ":" + SourcePort}; + // TODO: Connect this socket for a WSMAN event + SendChannelOpenConfirmation(socket, SenderChannel, MeshAmtEventEndpoint.ServerChannel, MeshAmtEventEndpoint.MaxWindowSize); + */ + + return 33 + ChannelTypeLength + TargetLen + SourceLen; + } + case APFProtocol.CHANNEL_OPEN_CONFIRMATION: + { + if (len < 17) return 0; + var RecipientChannel = common.ReadInt(data, 1); + var SenderChannel = common.ReadInt(data, 5); + var WindowSize = common.ReadInt(data, 9); + socket.tag.activetunnels++; + var cirachannel = socket.tag.channels[RecipientChannel]; + if (cirachannel == null) { /*console.log("MPS Error in CHANNEL_OPEN_CONFIRMATION: Unable to find channelid " + RecipientChannel);*/ return 17; } + cirachannel.amtchannelid = SenderChannel; + cirachannel.sendcredits = cirachannel.amtCiraWindow = WindowSize; + channelOpenConfirmCount++; + parent.debug('mpscmd', '--> CHANNEL_OPEN_CONFIRMATION', RecipientChannel, SenderChannel, WindowSize); + if (cirachannel.closing == 1) { + // Close this channel + SendChannelClose(cirachannel.socket, cirachannel.amtchannelid); + } else { + cirachannel.state = 2; + // Send any pending data + if (cirachannel.sendBuffer != null) { if (cirachannel.sendBuffer.length <= cirachannel.sendcredits) { // Send the entire pending buffer SendChannelData(cirachannel.socket, cirachannel.amtchannelid, cirachannel.sendBuffer); @@ -744,63 +731,110 @@ module.exports.CreateMpsServer = function (parent, db, args, certificates) { cirachannel.sendcredits = 0; } } - return 9; + // Indicate the channel is open + if (cirachannel.onStateChange) { cirachannel.onStateChange(cirachannel, cirachannel.state); } } - case APFProtocol.CHANNEL_DATA: - { - if (len < 9) return 0; - var RecipientChannel = common.ReadInt(data, 1); - var LengthOfData = common.ReadInt(data, 5); - if (len < (9 + LengthOfData)) return 0; - parent.debug('mpscmddata', '--> CHANNEL_DATA', RecipientChannel, LengthOfData); - var cirachannel = socket.tag.channels[RecipientChannel]; - if (cirachannel == null) { console.log("MPS Error in CHANNEL_DATA: Unable to find channelid " + RecipientChannel); return 9 + LengthOfData; } - cirachannel.amtpendingcredits += LengthOfData; - if (cirachannel.onData) cirachannel.onData(cirachannel, data.substring(9, 9 + LengthOfData)); - if (cirachannel.amtpendingcredits > (cirachannel.ciraWindow / 2)) { - SendChannelWindowAdjust(cirachannel.socket, cirachannel.amtchannelid, cirachannel.amtpendingcredits); // Adjust the buffer window - cirachannel.amtpendingcredits = 0; + return 17; + } + case APFProtocol.CHANNEL_OPEN_FAILURE: + { + if (len < 17) return 0; + var RecipientChannel = common.ReadInt(data, 1); + var ReasonCode = common.ReadInt(data, 5); + channelOpenFailCount++; + parent.debug('mpscmd', '--> CHANNEL_OPEN_FAILURE', RecipientChannel, ReasonCode); + var cirachannel = socket.tag.channels[RecipientChannel]; + if (cirachannel == null) { console.log("MPS Error in CHANNEL_OPEN_FAILURE: Unable to find channelid " + RecipientChannel); return 17; } + if (cirachannel.state > 0) { + cirachannel.state = 0; + if (cirachannel.onStateChange) { cirachannel.onStateChange(cirachannel, cirachannel.state); } + delete socket.tag.channels[RecipientChannel]; + } + return 17; + } + case APFProtocol.CHANNEL_CLOSE: + { + if (len < 5) return 0; + var RecipientChannel = common.ReadInt(data, 1); + channelCloseCount++; + parent.debug('mpscmd', '--> CHANNEL_CLOSE', RecipientChannel); + var cirachannel = socket.tag.channels[RecipientChannel]; + if (cirachannel == null) { console.log("MPS Error in CHANNEL_CLOSE: Unable to find channelid " + RecipientChannel); return 5; } + socket.tag.activetunnels--; + if (cirachannel.state > 0) { + cirachannel.state = 0; + if (cirachannel.onStateChange) { cirachannel.onStateChange(cirachannel, cirachannel.state); } + SendChannelClose(cirachannel.socket, cirachannel.amtchannelid); + delete socket.tag.channels[RecipientChannel]; + } + return 5; + } + case APFProtocol.CHANNEL_WINDOW_ADJUST: + { + if (len < 9) return 0; + var RecipientChannel = common.ReadInt(data, 1); + var ByteToAdd = common.ReadInt(data, 5); + var cirachannel = socket.tag.channels[RecipientChannel]; + if (cirachannel == null) { console.log("MPS Error in CHANNEL_WINDOW_ADJUST: Unable to find channelid " + RecipientChannel); return 9; } + cirachannel.sendcredits += ByteToAdd; + parent.debug('mpscmd', '--> CHANNEL_WINDOW_ADJUST', RecipientChannel, ByteToAdd, cirachannel.sendcredits); + if (cirachannel.state == 2 && cirachannel.sendBuffer != null) { + // Compute how much data we can send + if (cirachannel.sendBuffer.length <= cirachannel.sendcredits) { + // Send the entire pending buffer + SendChannelData(cirachannel.socket, cirachannel.amtchannelid, cirachannel.sendBuffer); + cirachannel.sendcredits -= cirachannel.sendBuffer.length; + delete cirachannel.sendBuffer; + if (cirachannel.onSendOk) { cirachannel.onSendOk(cirachannel); } + } else { + // Send a part of the pending buffer + SendChannelData(cirachannel.socket, cirachannel.amtchannelid, cirachannel.sendBuffer.substring(0, cirachannel.sendcredits)); + cirachannel.sendBuffer = cirachannel.sendBuffer.substring(cirachannel.sendcredits); + cirachannel.sendcredits = 0; } - return 9 + LengthOfData; } - case APFProtocol.DISCONNECT: - { - if (len < 7) return 0; - var ReasonCode = common.ReadInt(data, 1); - disconnectCommandCount++; - parent.debug('mpscmd', '--> DISCONNECT', ReasonCode); - try { delete obj.ciraConnections[socket.tag.nodeid]; } catch (e) { } - obj.parent.ClearConnectivityState(socket.tag.meshid, socket.tag.nodeid, 2); - return 7; + return 9; + } + case APFProtocol.CHANNEL_DATA: + { + if (len < 9) return 0; + var RecipientChannel = common.ReadInt(data, 1); + var LengthOfData = common.ReadInt(data, 5); + if (len < (9 + LengthOfData)) return 0; + parent.debug('mpscmddata', '--> CHANNEL_DATA', RecipientChannel, LengthOfData); + var cirachannel = socket.tag.channels[RecipientChannel]; + if (cirachannel == null) { console.log("MPS Error in CHANNEL_DATA: Unable to find channelid " + RecipientChannel); return 9 + LengthOfData; } + cirachannel.amtpendingcredits += LengthOfData; + if (cirachannel.onData) cirachannel.onData(cirachannel, data.substring(9, 9 + LengthOfData)); + if (cirachannel.amtpendingcredits > (cirachannel.ciraWindow / 2)) { + SendChannelWindowAdjust(cirachannel.socket, cirachannel.amtchannelid, cirachannel.amtpendingcredits); // Adjust the buffer window + cirachannel.amtpendingcredits = 0; } - default: - { - parent.debug('mpscmd', '--> Unknown CIRA command: ' + cmd); - return -1; - } - } + return 9 + LengthOfData; + } + case APFProtocol.DISCONNECT: + { + if (len < 7) return 0; + var ReasonCode = common.ReadInt(data, 1); + disconnectCommandCount++; + parent.debug('mpscmd', '--> DISCONNECT', ReasonCode); + try { delete obj.ciraConnections[socket.tag.nodeid]; } catch (e) { } + obj.parent.ClearConnectivityState(socket.tag.meshid, socket.tag.nodeid, socket.tag.connType); + return 7; + } + default: + { + parent.debug('mpscmd', '--> Unknown CIRA command: ' + cmd); + return -1; + } } - - socket.addListener('close', function () { - socketClosedCount++; - parent.debug('mps', 'CIRA connection closed'); - try { delete obj.ciraConnections[socket.tag.nodeid]; } catch (e) { } - obj.parent.ClearConnectivityState(socket.tag.meshid, socket.tag.nodeid, 2); - }); - - socket.addListener('error', function () { - socketErrorCount++; - parent.debug('mps', 'CIRA connection error'); - //console.log("MPS Error: " + socket.remoteAddress); - }); - } // Disconnect CIRA tunnel obj.close = function (socket) { try { socket.end(); } catch (e) { } try { delete obj.ciraConnections[socket.tag.nodeid]; } catch (e) { } - obj.parent.ClearConnectivityState(socket.tag.meshid, socket.tag.nodeid, 2); + obj.parent.ClearConnectivityState(socket.tag.meshid, socket.tag.nodeid, socket.tag.connType); }; function SendServiceAccept(socket, service) { @@ -886,9 +920,9 @@ module.exports.CreateMpsServer = function (parent, db, args, certificates) { // Print out sent bytes var buf = Buffer.from(data, 'binary'); console.log('MPS <-- (' + buf.length + '):' + buf.toString('hex')); - socket.write(buf); + if (socket.websocket == 1) { socket.send(buf); } else { socket.write(buf); } } else { - socket.write(Buffer.from(data, 'binary')); + if (socket.websocket == 1) { socket.send(Buffer.from(data, 'binary')); } else { socket.write(Buffer.from(data, 'binary')); } } } diff --git a/webserver.js b/webserver.js index 747805fd..eadd271e 100644 --- a/webserver.js +++ b/webserver.js @@ -4846,7 +4846,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { obj.app.post(url + 'uploadmeshcorefile.ashx', handleUploadMeshCoreFile); obj.app.get(url + 'userfiles/*', handleDownloadUserFiles); obj.app.ws(url + 'echo.ashx', handleEchoWebSocket); - obj.app.ws(url + 'apf.ashx', function (ws, req) { obj.parent.apfserver.onConnection(ws); }) + obj.app.ws(url + 'apf.ashx', function (ws, req) { obj.parent.mpsserver.onWebSocketConnection(ws); }) obj.app.get(url + 'webrelay.ashx', function (req, res) { res.send('Websocket connection expected'); }); obj.app.get(url + 'health.ashx', function (req, res) { res.send('ok'); }); // TODO: Perform more server checking. obj.app.ws(url + 'webrelay.ashx', function (ws, req) { PerformWSSessionAuth(ws, req, false, handleRelayWebSocket); });