2017-08-28 19:27:45 +03:00
/ * *
2018-01-04 23:15:21 +03:00
* @ description MeshCentral Intel ( R ) AMT MPS server
2017-08-28 19:27:45 +03:00
* @ author Ylian Saint - Hilaire
2019-01-04 03:22:15 +03:00
* @ copyright Intel Corporation 2018 - 2019
2018-01-04 23:15:21 +03:00
* @ license Apache - 2.0
2017-08-28 19:27:45 +03:00
* @ version v0 . 0.1
* /
2018-08-30 03:40:30 +03:00
/*jslint node: true */
/*jshint node: true */
/*jshint strict:false */
/*jshint -W097 */
/*jshint esversion: 6 */
"use strict" ;
2018-08-27 22:24:15 +03:00
2017-08-28 19:27:45 +03:00
// Construct a Intel AMT MPS server object
module . exports . CreateMpsServer = function ( parent , db , args , certificates ) {
var obj = { } ;
obj . parent = parent ;
obj . db = db ;
2017-09-25 21:00:57 +03:00
obj . args = args ;
2017-08-28 19:27:45 +03:00
obj . certificates = certificates ;
obj . ciraConnections = { } ;
2019-01-06 01:15:14 +03:00
var tlsSessionStore = { } ; // Store TLS session information for quick resume.
var tlsSessionStoreCount = 0 ; // Number of cached TLS session information in store.
2019-02-27 02:49:44 +03:00
const constants = ( require ( 'crypto' ) . constants ? require ( 'crypto' ) . constants : require ( 'constants' ) ) ; // require('constants') is deprecated in Node 11.10, use require('crypto').constants instead.
2018-08-30 03:40:30 +03:00
const common = require ( "./common.js" ) ;
const net = require ( "net" ) ;
const tls = require ( "tls" ) ;
2019-01-06 01:15:14 +03:00
const MAX _IDLE = 90000 ; // 90 seconds max idle time, higher than the typical KEEP-ALIVE periode of 60 seconds
2017-09-25 21:00:57 +03:00
2018-11-07 06:41:21 +03:00
if ( obj . args . mpstlsoffload ) {
2017-09-25 21:00:57 +03:00
obj . server = net . createServer ( onConnection ) ;
} else {
2019-05-03 03:32:11 +03:00
// Note that in oder to support older Intel AMT CIRA connections, we have to turn on TLSv1.
obj . server = tls . createServer ( { key : certificates . mps . key , cert : certificates . mps . cert , minVersion : 'TLSv1' , requestCert : true , rejectUnauthorized : false , ciphers : "HIGH:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!SRP:!CAMELLIA" , secureOptions : constants . SSL _OP _NO _SSLv2 | constants . SSL _OP _NO _SSLv3 | constants . SSL _OP _NO _COMPRESSION } , onConnection ) ;
2019-01-06 01:15:14 +03:00
//obj.server.on('secureConnection', function () { /*console.log('tlsServer secureConnection');*/ });
//obj.server.on('error', function () { console.log('MPS tls server error'); });
obj . server . on ( 'newSession' , function ( id , data , cb ) { if ( tlsSessionStoreCount > 1000 ) { tlsSessionStoreCount = 0 ; tlsSessionStore = { } ; } tlsSessionStore [ id . toString ( 'hex' ) ] = data ; tlsSessionStoreCount ++ ; cb ( ) ; } ) ;
obj . server . on ( 'resumeSession' , function ( id , cb ) { cb ( null , tlsSessionStore [ id . toString ( 'hex' ) ] || null ) ; } ) ;
2017-09-25 21:00:57 +03:00
}
2018-08-30 03:40:30 +03:00
obj . server . listen ( args . mpsport , function ( ) { console . log ( "MeshCentral Intel(R) AMT server running on " + certificates . AmtMpsName + ":" + args . mpsport + ( ( args . mpsaliasport != null ) ? ( ", alias port " + args . mpsaliasport ) : "" ) + "." ) ; } ) . on ( "error" , function ( err ) { console . error ( "ERROR: MeshCentral Intel(R) AMT server port " + args . mpsport + " is not available." ) ; if ( args . exactports ) { process . exit ( ) ; } } ) ;
2018-12-22 01:39:26 +03:00
obj . server . on ( 'tlsClientError' , function ( err , tlssocket ) { if ( args . mpsdebug ) { var remoteAddress = tlssocket . remoteAddress ; if ( tlssocket . remoteFamily == 'IPv6' ) { remoteAddress = '[' + remoteAddress + ']' ; } console . log ( 'MPS:Invalid TLS connection from ' + remoteAddress + ':' + tlssocket . remotePort + '.' ) ; } } ) ;
2018-08-30 03:40:30 +03:00
obj . parent . updateServerState ( "mps-port" , args . mpsport ) ;
obj . parent . updateServerState ( "mps-name" , certificates . AmtMpsName ) ;
if ( args . mpsaliasport != null ) { obj . parent . updateServerState ( "mps-alias-port" , args . mpsaliasport ) ; }
2018-03-14 22:10:13 +03:00
2018-05-04 03:21:59 +03:00
const APFProtocol = {
2017-08-28 19:27:45 +03:00
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
2018-08-30 03:40:30 +03:00
} ;
/ *
2018-05-04 03:21:59 +03:00
const APFDisconnectCode = {
2017-08-28 19:27:45 +03:00
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
2018-08-30 03:40:30 +03:00
} ;
2018-05-04 03:21:59 +03:00
const APFChannelOpenFailCodes = {
2017-08-28 19:27:45 +03:00
ADMINISTRATIVELY _PROHIBITED : 1 ,
CONNECT _FAILED : 2 ,
UNKNOWN _CHANNEL _TYPE : 3 ,
RESOURCE _SHORTAGE : 4 ,
2018-08-30 03:40:30 +03:00
} ;
* /
2018-05-04 03:21:59 +03:00
const APFChannelOpenFailureReasonCode = {
2017-08-28 19:27:45 +03:00
AdministrativelyProhibited : 1 ,
ConnectFailed : 2 ,
UnknownChannelType : 3 ,
ResourceShortage : 4 ,
2018-08-30 03:40:30 +03:00
} ;
2019-05-02 01:02:03 +03:00
// Stat counters
var connectionCount = 0 ;
var userAuthRequestCount = 0 ;
var incorrectPasswordCount = 0 ;
var meshNotFoundCount = 0 ;
var unknownTlsNodeCount = 0 ;
var unknownTlsMeshIdCount = 0 ;
var addedTlsDeviceCount = 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 ;
2019-06-06 01:24:07 +03:00
var maxDomainDevicesReached = 0 ;
2019-05-02 01:02:03 +03:00
// Return statistics about this MPS server
obj . getStats = function ( ) {
return {
ciraConnections : Object . keys ( obj . ciraConnections ) . length ,
tlsSessionStore : Object . keys ( tlsSessionStore ) . length ,
connectionCount : connectionCount ,
userAuthRequestCount : userAuthRequestCount ,
incorrectPasswordCount : incorrectPasswordCount ,
meshNotFoundCount : meshNotFoundCount ,
unknownTlsNodeCount : unknownTlsNodeCount ,
unknownTlsMeshIdCount : unknownTlsMeshIdCount ,
addedTlsDeviceCount : addedTlsDeviceCount ,
unknownNodeCount : unknownNodeCount ,
unknownMeshIdCount : unknownMeshIdCount ,
addedDeviceCount : addedDeviceCount ,
ciraTimeoutCount : ciraTimeoutCount ,
protocolVersionCount : protocolVersionCount ,
badUserNameLengthCount : badUserNameLengthCount ,
channelOpenCount : channelOpenCount ,
channelOpenConfirmCount : channelOpenConfirmCount ,
channelOpenFailCount : channelOpenFailCount ,
channelCloseCount : channelCloseCount ,
disconnectCommandCount : disconnectCommandCount ,
socketClosedCount : socketClosedCount ,
2019-06-06 01:24:07 +03:00
socketErrorCount : socketErrorCount ,
2019-10-04 22:18:56 +03:00
maxDomainDevicesReached : maxDomainDevicesReached
2019-05-02 01:02:03 +03:00
} ;
}
2019-10-04 22:18:56 +03:00
// Required for TLS piping to MQTT broker
2019-10-03 03:57:18 +03:00
function SerialTunnel ( options ) {
var obj = new require ( 'stream' ) . Duplex ( options ) ;
obj . forwardwrite = null ;
obj . updateBuffer = function ( chunk ) { this . push ( chunk ) ; } ;
obj . _write = function ( chunk , encoding , callback ) { if ( obj . forwardwrite != null ) { obj . forwardwrite ( chunk ) ; } else { console . err ( "Failed to fwd _write." ) ; } if ( callback ) callback ( ) ; } ; // Pass data written to forward
obj . _read = function ( size ) { } ; // Push nothing, anything to read should be pushed from updateBuffer()
return obj ;
2019-10-04 22:18:56 +03:00
}
2019-10-03 03:57:18 +03:00
2019-10-04 22:18:56 +03:00
// Return's the length of an MQTT packet
2019-10-03 03:57:18 +03:00
function getMQTTPacketLength ( chunk ) {
var packet _len = 0 ;
2019-10-04 22:18:56 +03:00
if ( chunk . readUInt8 ( 0 ) == 16 ) {
if ( chunk . readUInt8 ( 1 ) < 128 ) {
2019-10-03 03:57:18 +03:00
packet _len += chunk . readUInt8 ( 1 ) + 2 ;
} else {
// continuation bit, get real value and do next
packet _len += ( chunk . readUInt8 ( 1 ) & 0x7F ) + 2 ;
if ( chunk . readUInt8 ( 2 ) < 128 ) {
packet _len += 1 + chunk . readUInt8 ( 2 ) * 128 ;
} else {
packet _len += 1 + ( chunk . readUInt8 ( 2 ) & 0x7F ) * 128 ;
if ( chunk . readUInt8 ( 3 ) < 128 ) {
packet _len += 1 + chunk . readUInt8 ( 3 ) * 128 * 128 ;
} else {
packet _len += 1 + ( chunk . readUInt8 ( 3 ) & 0x7F ) * 128 * 128 ;
if ( chunk . readUInt8 ( 4 ) < 128 ) {
packet _len += 1 + chunk . readUInt8 ( 4 ) * 128 * 128 * 128 ;
} else {
2019-10-04 22:18:56 +03:00
packet _len += 1 + ( chunk . readUInt8 ( 4 ) & 0x7F ) * 128 * 128 * 128 ;
2019-10-03 03:57:18 +03:00
}
2019-10-04 22:18:56 +03:00
}
2019-10-03 03:57:18 +03:00
}
}
}
return packet _len ;
}
2019-10-04 22:18:56 +03:00
// Called when a new TLS/TCP connection is accepted
2017-08-28 19:27:45 +03:00
function onConnection ( socket ) {
2019-05-02 01:02:03 +03:00
connectionCount ++ ;
2018-11-07 06:41:21 +03:00
if ( obj . args . mpstlsoffload ) {
2017-09-25 21:00:57 +03:00
socket . tag = { first : true , 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 } ;
}
2018-08-30 03:40:30 +03:00
socket . setEncoding ( "binary" ) ;
2019-08-23 01:31:39 +03:00
parent . debug ( 'mps' , "New CIRA connection" ) ;
2018-05-03 21:09:29 +03:00
// Setup the CIRA keep alive timer
2018-05-04 03:21:59 +03:00
socket . setTimeout ( MAX _IDLE ) ;
2019-08-23 01:31:39 +03:00
socket . on ( "timeout" , ( ) => { ciraTimeoutCount ++ ; parent . debug ( 'mps' , "CIRA timeout, disconnecting." ) ; try { socket . end ( ) ; } catch ( e ) { } } ) ;
2018-05-03 21:09:29 +03:00
2017-08-28 19:27:45 +03:00
socket . addListener ( "data" , function ( data ) {
2019-01-03 05:03:34 +03:00
if ( args . mpsdebug ) { var buf = Buffer . from ( data , "binary" ) ; console . log ( "MPS <-- (" + buf . length + "):" + buf . toString ( 'hex' ) ) ; } // Print out received bytes
2017-08-28 19:27:45 +03:00
socket . tag . accumulator += data ;
2018-08-30 03:40:30 +03:00
2017-08-28 19:27:45 +03:00
// Detect if this is an HTTPS request, if it is, return a simple answer and disconnect. This is useful for debugging access to the MPS port.
if ( socket . tag . first == true ) {
if ( socket . tag . accumulator . length < 3 ) return ;
//if (!socket.tag.clientCert.subject) { console.log("MPS Connection, no client cert: " + socket.remoteAddress); socket.write('HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\nConnection: close\r\n\r\nMeshCentral2 MPS server.\r\nNo client certificate given.'); socket.end(); return; }
2018-12-22 01:39:26 +03:00
if ( socket . tag . accumulator . substring ( 0 , 3 ) == "GET" ) { if ( args . mpsdebug ) { console . log ( "MPS Connection, HTTP GET detected: " + socket . remoteAddress ) ; } socket . write ( "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nConnection: close\r\n\r\n<!DOCTYPE html><html><head><meta charset=\"UTF-8\"></head><body>MeshCentral2 MPS server.<br />Intel® AMT computers should connect here.</body></html>" ) ; socket . end ( ) ; return ; }
2019-10-04 22:18:56 +03:00
// If the MQTT broker is active, look for inbound MQTT connections
if ( parent . mqttbroker != null ) {
var chunk = Buffer . from ( socket . tag . accumulator , "binary" ) ;
var packet _len = 0 ;
if ( chunk . readUInt8 ( 0 ) == 16 ) { packet _len = getMQTTPacketLength ( chunk ) ; }
if ( chunk . readUInt8 ( 0 ) == 16 && ( socket . tag . accumulator . length < packet _len ) ) return ; // Minimum MQTT detection
// check if it is MQTT, need more initial packet to probe
if ( chunk . readUInt8 ( 0 ) == 16 && ( ( chunk . slice ( 4 , 8 ) . toString ( ) === "MQTT" ) || ( chunk . slice ( 5 , 9 ) . toString ( ) === "MQTT" )
|| ( chunk . slice ( 6 , 10 ) . toString ( ) === "MQTT" ) || ( chunk . slice ( 7 , 11 ) . toString ( ) === "MQTT" ) ) ) {
parent . debug ( "mps" , "MQTT connection detected." ) ;
socket . removeAllListeners ( "data" ) ;
socket . removeAllListeners ( "close" ) ;
socket . setNoDelay ( true ) ;
socket . serialtunnel = SerialTunnel ( ) ;
2019-10-05 03:24:30 +03:00
socket . serialtunnel . xtransport = 'mps' ;
socket . serialtunnel . xip = socket . remoteAddress ;
socket . on ( "data" , function ( b ) { socket . serialtunnel . updateBuffer ( Buffer . from ( b , "binary" ) ) } ) ;
2019-10-04 22:18:56 +03:00
socket . serialtunnel . forwardwrite = function ( b ) { socket . write ( b , "binary" ) }
2019-10-05 03:24:30 +03:00
socket . on ( "close" , function ( ) { socket . serialtunnel . emit ( "end" ) ; } ) ;
2019-10-04 22:18:56 +03:00
// Pass socket wrapper to the MQTT broker
parent . mqttbroker . handle ( socket . serialtunnel ) ;
socket . unshift ( socket . tag . accumulator ) ;
return ;
}
2019-10-03 03:57:18 +03:00
}
2019-10-04 22:18:56 +03:00
2017-08-28 19:27:45 +03:00
socket . tag . first = false ;
2018-08-30 03:40:30 +03:00
2017-08-28 19:27:45 +03:00
// Setup this node with certificate authentication
if ( socket . tag . clientCert && socket . tag . clientCert . subject && socket . tag . clientCert . subject . O && socket . tag . clientCert . subject . O . length == 64 ) {
// This is a node where the MeshID is indicated within the CIRA certificate
var domainid = '' , meshid ;
var xx = socket . tag . clientCert . subject . O . split ( '/' ) ;
2017-10-16 03:36:06 +03:00
if ( xx . length == 1 ) { meshid = xx [ 0 ] ; } else { domainid = xx [ 0 ] . toLowerCase ( ) ; meshid = xx [ 1 ] ; }
2017-08-28 19:27:45 +03:00
2019-06-06 01:24:07 +03:00
// Check the incoming domain
var domain = obj . parent . config . domains [ domainid ] ;
if ( domain == null ) { console . log ( 'CIRA connection for invalid domain. meshid: ' + meshid ) ; socket . end ( ) ; return ; }
socket . tag . domain = domain ;
2017-08-28 19:27:45 +03:00
socket . tag . domainid = domainid ;
socket . tag . meshid = 'mesh/' + domainid + '/' + meshid ;
2017-10-16 03:36:06 +03:00
socket . tag . nodeid = 'node/' + domainid + '/' + require ( 'crypto' ) . createHash ( 'sha384' ) . update ( common . hex2rstr ( socket . tag . clientCert . modulus , 'binary' ) ) . digest ( 'base64' ) . replace ( /\+/g , '@' ) . replace ( /\//g , '$' ) ;
2017-08-28 19:27:45 +03:00
socket . tag . name = socket . tag . clientCert . subject . CN ;
socket . tag . connectTime = Date . now ( ) ;
socket . tag . host = '' ;
// Fetch the mesh
obj . db . Get ( socket . tag . meshid , function ( err , meshes ) {
2019-05-21 04:03:14 +03:00
if ( ( meshes != null ) && ( meshes . length === 1 ) ) {
2017-08-28 19:27:45 +03:00
var mesh = meshes [ 0 ] ;
obj . db . Get ( socket . tag . nodeid , function ( err , nodes ) {
2019-05-21 04:03:14 +03:00
if ( ( nodes == null ) || ( nodes . length !== 1 ) ) {
2017-08-28 19:27:45 +03:00
if ( mesh . mtype == 1 ) {
2019-06-06 01:24:07 +03:00
// 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' , domain . id , 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 : domainid , intelamt : { user : '' , pass : '' , tls : 0 , state : 2 } } ;
obj . db . Set ( device ) ;
// Event the new node
addedTlsDeviceCount ++ ;
var device2 = common . Clone ( device ) ;
if ( device2 . intelamt . pass != null ) delete device2 . intelamt . pass ; // Remove the Intel AMT password before eventing this.
var change = 'CIRA added device ' + socket . tag . name + ' to mesh ' + mesh . name ;
obj . parent . DispatchEvent ( [ '*' , socket . tag . meshid ] , obj , { etype : 'node' , action : 'addnode' , node : device2 , msg : change , domain : domainid } ) ;
// 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.
}
} ) ;
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 : domainid , intelamt : { user : '' , pass : '' , tls : 0 , state : 2 } } ;
obj . db . Set ( device ) ;
// Event the new node
addedTlsDeviceCount ++ ;
var device2 = common . Clone ( device ) ;
if ( device2 . intelamt . pass != null ) delete device2 . intelamt . pass ; // Remove the Intel AMT password before eventing this.
var change = 'CIRA added device ' + socket . tag . name + ' to mesh ' + mesh . name ;
obj . parent . DispatchEvent ( [ '*' , socket . tag . meshid ] , obj , { etype : 'node' , action : 'addnode' , node : device2 , msg : change , domain : domainid } ) ;
}
2017-08-28 19:27:45 +03:00
} else {
// New CIRA connection for unknown node, disconnect.
2019-05-02 01:02:03 +03:00
unknownTlsNodeCount ++ ;
2019-03-05 10:48:45 +03:00
console . log ( 'CIRA connection for unknown node with incorrect group type. meshid: ' + socket . tag . meshid ) ;
2017-08-28 19:27:45 +03:00
socket . end ( ) ;
return ;
}
} else {
// Node is already present
var node = nodes [ 0 ] ;
2019-03-05 10:48:45 +03:00
if ( ( node . intelamt != null ) && ( node . intelamt . state == 2 ) ) { socket . tag . host = node . intelamt . host ; }
2017-08-28 19:27:45 +03:00
}
// 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.
} ) ;
} else {
2019-05-02 01:02:03 +03:00
unknownTlsMeshIdCount ++ ;
2019-03-05 10:48:45 +03:00
console . log ( 'ERROR: Intel AMT CIRA connected with unknown groupid: ' + socket . tag . meshid ) ;
2017-08-28 19:27:45 +03:00
socket . end ( ) ;
return ;
}
} ) ;
} else {
// This node connected without certificate authentication, use password auth
//console.log('Intel AMT CIRA connected without certificate authentication');
}
}
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 . end ( ) ; }
} catch ( e ) {
console . log ( e ) ;
}
} ) ;
2017-10-15 09:22:19 +03:00
2017-08-28 19:27:45 +03:00
// 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 ;
2019-08-23 01:31:39 +03:00
parent . debug ( 'mpscmd' , 'KEEPALIVE_REQUEST' ) ;
2017-08-28 19:27:45 +03:00
SendKeepAliveReply ( socket , common . ReadInt ( data , 1 ) ) ;
return 5 ;
}
case APFProtocol . KEEPALIVE _REPLY : {
if ( len < 5 ) return 0 ;
2019-08-23 01:31:39 +03:00
parent . debug ( 'mpscmd' , 'KEEPALIVE_REPLY' ) ;
2017-08-28 19:27:45 +03:00
return 5 ;
}
case APFProtocol . PROTOCOLVERSION : {
if ( len < 93 ) return 0 ;
2019-05-02 01:02:03 +03:00
protocolVersionCount ++ ;
2017-08-28 19:27:45 +03:00
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 ( ) ;
2019-08-23 01:31:39 +03:00
parent . debug ( 'mpscmd' , 'PROTOCOLVERSION' , socket . tag . MajorVersion , socket . tag . MinorVersion , socket . tag . SystemId ) ;
2017-08-28 19:27:45 +03:00
return 93 ;
}
case APFProtocol . USERAUTH _REQUEST : {
if ( len < 13 ) return 0 ;
2019-05-02 01:02:03 +03:00
userAuthRequestCount ++ ;
2017-08-28 19:27:45 +03:00
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 ) ;
}
2018-04-29 01:36:10 +03:00
//console.log('MPS:USERAUTH_REQUEST user=' + username + ', service=' + serviceName + ', method=' + methodName + ', password=' + password);
2019-08-23 01:31:39 +03:00
parent . debug ( 'mpscmd' , 'USERAUTH_REQUEST user=' + username + ', service=' + serviceName + ', method=' + methodName + ', password=' + password ) ;
2017-08-28 19:27:45 +03:00
// Check the CIRA password
2019-08-23 01:31:39 +03:00
if ( ( args . mpspass != null ) && ( password != args . mpspass ) ) { incorrectPasswordCount ++ ; parent . debug ( 'mps' , 'Incorrect password' , username , password ) ; SendUserAuthFail ( socket ) ; return - 1 ; }
2017-08-28 19:27:45 +03:00
2017-10-16 03:36:06 +03:00
// Check the CIRA username, which should be the start of the MeshID.
2019-08-23 01:31:39 +03:00
if ( usernameLen != 16 ) { badUserNameLengthCount ++ ; parent . debug ( 'mps' , 'Username length not 16' , username , password ) ; SendUserAuthFail ( socket ) ; return - 1 ; }
2019-02-21 02:26:27 +03:00
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 ; } } }
2019-08-23 01:31:39 +03:00
if ( mesh == null ) { meshNotFoundCount ++ ; parent . debug ( 'mps' , 'Mesh not found' , username , password ) ; SendUserAuthFail ( socket ) ; return - 1 ; }
2017-08-28 19:27:45 +03:00
2019-02-21 02:26:27 +03:00
// 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 , '$' ) ;
2019-06-06 01:24:07 +03:00
var domain = obj . parent . config . domains [ mesh . domain ] ;
socket . tag . domain = domain ;
socket . tag . domainid = mesh . domain ;
2019-02-21 02:26:27 +03:00
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 ( ) ;
2017-08-28 19:27:45 +03:00
2019-02-21 02:26:27 +03:00
obj . db . Get ( socket . tag . nodeid , function ( err , nodes ) {
2019-05-21 04:03:14 +03:00
if ( ( nodes == null ) || ( nodes . length !== 1 ) ) {
2019-06-06 01:24:07 +03:00
// 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 device2 = common . Clone ( device ) ;
if ( device2 . intelamt . pass != null ) delete device2 . intelamt . pass ; // Remove the Intel AMT password before eventing this.
var change = 'CIRA added device ' + socket . tag . name + ' to group ' + mesh . name ;
obj . parent . DispatchEvent ( [ '*' , socket . tag . meshid ] , obj , { etype : 'node' , action : 'addnode' , node : device2 , msg : change , domain : mesh . domain } ) ;
// 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.
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 device2 = common . Clone ( device ) ;
if ( device2 . intelamt . pass != null ) delete device2 . intelamt . pass ; // Remove the Intel AMT password before eventing this.
var change = 'CIRA added device ' + socket . tag . name + ' to group ' + mesh . name ;
obj . parent . DispatchEvent ( [ '*' , socket . tag . meshid ] , obj , { etype : 'node' , action : 'addnode' , node : device2 , msg : change , domain : mesh . domain } ) ;
}
2019-02-21 02:26:27 +03:00
} else {
// Node is already present
2017-08-28 19:27:45 +03:00
var node = nodes [ 0 ] ;
2019-03-05 10:48:45 +03:00
if ( ( node . intelamt != null ) && ( node . intelamt . state == 2 ) ) { socket . tag . host = node . intelamt . host ; }
2019-02-21 02:26:27 +03:00
}
2017-08-28 19:27:45 +03:00
2019-02-21 02:26:27 +03:00
// 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.
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 ( mesh . _id , socket . tag . SystemId , function ( err , nodes ) { // TODO: May need to optimize this request with indexes
2019-05-21 04:03:14 +03:00
if ( ( nodes == null ) || ( nodes . length !== 1 ) ) {
2019-02-21 02:26:27 +03:00
// New CIRA connection for unknown node, disconnect.
2019-05-02 01:02:03 +03:00
unknownNodeCount ++ ;
2019-03-18 05:34:52 +03:00
console . log ( 'CIRA connection for unknown node. groupid: ' + mesh . _id + ', uuid: ' + socket . tag . SystemId ) ;
2019-02-21 02:26:27 +03:00
socket . end ( ) ;
return ;
}
// Node is present
var node = nodes [ 0 ] ;
2019-03-05 10:48:45 +03:00
if ( ( node . intelamt != null ) && ( node . intelamt . state == 2 ) ) { socket . tag . host = node . intelamt . host ; }
2019-02-21 02:26:27 +03:00
socket . tag . nodeid = node . _id ;
socket . tag . meshid = mesh . _id ;
socket . tag . connectTime = Date . now ( ) ;
// 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.
SendUserAuthSuccess ( socket ) ; // Notify the auth success on the CIRA connection
} ) ;
} else { // Unknown mesh type
// New CIRA connection for unknown node, disconnect.
2019-05-02 01:02:03 +03:00
unknownMeshIdCount ++ ;
2019-03-05 10:48:45 +03:00
console . log ( 'CIRA connection to a unknown group type. groupid: ' + socket . tag . meshid ) ;
2019-02-21 02:26:27 +03:00
socket . end ( ) ;
return ;
}
2017-08-28 19:27:45 +03:00
return 18 + usernameLen + serviceNameLen + methodNameLen + passwordLen ;
}
case APFProtocol . SERVICE _REQUEST : {
if ( len < 5 ) return 0 ;
2018-08-30 03:40:30 +03:00
var xserviceNameLen = common . ReadInt ( data , 1 ) ;
if ( len < 5 + xserviceNameLen ) return 0 ;
var xserviceName = data . substring ( 5 , 5 + xserviceNameLen ) ;
2019-08-23 01:31:39 +03:00
parent . debug ( 'mpscmd' , 'SERVICE_REQUEST' , xserviceName ) ;
2018-08-30 03:40:30 +03:00
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 ;
2017-08-28 19:27:45 +03:00
}
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 ) ;
2018-08-30 03:40:30 +03:00
//var wantResponse = data.charCodeAt(5 + requestLen);
2017-08-28 19:27:45 +03:00
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 ) ;
2019-08-23 01:31:39 +03:00
parent . debug ( 'mpscmd' , 'GLOBAL_REQUEST' , request , addr + ':' + port ) ;
2019-03-05 10:48:45 +03:00
ChangeHostname ( socket , addr , socket . tag . SystemId ) ;
2017-08-28 19:27:45 +03:00
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 ) ;
2019-08-23 01:31:39 +03:00
parent . debug ( 'mpscmd' , 'GLOBAL_REQUEST' , request , addr + ':' + port ) ;
2017-08-28 19:27:45 +03:00
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 ;
2019-08-23 01:31:39 +03:00
parent . debug ( 'mpscmd' , 'GLOBAL_REQUEST' , request , addr + ':' + port , oaddr + ':' + oport , datalen ) ;
2017-08-28 19:27:45 +03:00
// TODO
2017-10-15 09:22:19 +03:00
return 26 + requestLen + addrLen + oaddrLen + datalen ;
2017-08-28 19:27:45 +03:00
}
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 ) ;
2018-08-30 03:40:30 +03:00
2019-05-02 01:02:03 +03:00
channelOpenCount ++ ;
2019-08-23 01:31:39 +03:00
parent . debug ( 'mpscmd' , 'CHANNEL_OPEN' , ChannelType , SenderChannel , WindowSize , Target + ':' + TargetPort , Source + ':' + SourcePort ) ;
2017-08-28 19:27:45 +03:00
// 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 ;
}
2018-08-30 03:40:30 +03:00
2017-08-28 19:27:45 +03:00
/ *
// 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 ;
}
2018-08-30 03:40:30 +03:00
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 ] ;
2019-03-05 10:48:45 +03:00
if ( cirachannel == null ) { /*console.log("MPS Error in CHANNEL_OPEN_CONFIRMATION: Unable to find channelid " + RecipientChannel);*/ return 17 ; }
2018-08-30 03:40:30 +03:00
cirachannel . amtchannelid = SenderChannel ;
cirachannel . sendcredits = cirachannel . amtCiraWindow = WindowSize ;
2019-05-02 01:02:03 +03:00
channelOpenConfirmCount ++ ;
2019-08-23 01:31:39 +03:00
parent . debug ( 'mpscmd' , 'CHANNEL_OPEN_CONFIRMATION' , RecipientChannel , SenderChannel , WindowSize ) ;
2018-08-30 03:40:30 +03:00
if ( cirachannel . closing == 1 ) {
// Close this channel
SendChannelClose ( cirachannel . socket , cirachannel . amtchannelid ) ;
} else {
cirachannel . state = 2 ;
// Send any pending data
2019-03-05 10:48:45 +03:00
if ( cirachannel . sendBuffer != null ) {
2018-08-30 03:40:30 +03:00
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 ) ;
2019-05-02 01:02:03 +03:00
channelOpenFailCount ++ ;
2019-08-23 01:31:39 +03:00
parent . debug ( 'mpscmd' , 'CHANNEL_OPEN_FAILURE' , RecipientChannel , ReasonCode ) ;
2018-08-30 03:40:30 +03:00
var cirachannel = socket . tag . channels [ RecipientChannel ] ;
2019-03-05 10:48:45 +03:00
if ( cirachannel == null ) { console . log ( "MPS Error in CHANNEL_OPEN_FAILURE: Unable to find channelid " + RecipientChannel ) ; return 17 ; }
2018-08-30 03:40:30 +03:00
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 ) ;
2019-05-02 01:02:03 +03:00
channelCloseCount ++ ;
2019-08-23 01:31:39 +03:00
parent . debug ( 'mpscmd' , 'CHANNEL_CLOSE' , RecipientChannel ) ;
2018-08-30 03:40:30 +03:00
var cirachannel = socket . tag . channels [ RecipientChannel ] ;
2019-03-05 10:48:45 +03:00
if ( cirachannel == null ) { console . log ( "MPS Error in CHANNEL_CLOSE: Unable to find channelid " + RecipientChannel ) ; return 5 ; }
2018-08-30 03:40:30 +03:00
socket . tag . activetunnels -- ;
if ( cirachannel . state > 0 ) {
cirachannel . state = 0 ;
if ( cirachannel . onStateChange ) { cirachannel . onStateChange ( cirachannel , cirachannel . state ) ; }
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 ] ;
2019-03-05 10:48:45 +03:00
if ( cirachannel == null ) { console . log ( "MPS Error in CHANNEL_WINDOW_ADJUST: Unable to find channelid " + RecipientChannel ) ; return 9 ; }
2018-08-30 03:40:30 +03:00
cirachannel . sendcredits += ByteToAdd ;
2019-08-23 01:31:39 +03:00
parent . debug ( 'mpscmd' , 'CHANNEL_WINDOW_ADJUST' , RecipientChannel , ByteToAdd , cirachannel . sendcredits ) ;
2019-03-05 10:48:45 +03:00
if ( cirachannel . state == 2 && cirachannel . sendBuffer != null ) {
2018-08-30 03:40:30 +03:00
// Compute how much data we can send
2017-08-28 19:27:45 +03:00
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 ;
}
}
2018-08-30 03:40:30 +03:00
return 9 ;
2017-08-28 19:27:45 +03:00
}
2018-08-30 03:40:30 +03:00
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 ;
2019-08-23 01:31:39 +03:00
parent . debug ( 'mpscmddata' , 'CHANNEL_DATA' , RecipientChannel , LengthOfData ) ;
2018-08-30 03:40:30 +03:00
var cirachannel = socket . tag . channels [ RecipientChannel ] ;
2019-03-05 10:48:45 +03:00
if ( cirachannel == null ) { console . log ( "MPS Error in CHANNEL_DATA: Unable to find channelid " + RecipientChannel ) ; return 9 + LengthOfData ; }
2018-08-30 03:40:30 +03:00
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 ;
2017-08-28 19:27:45 +03:00
}
2018-08-30 03:40:30 +03:00
return 9 + LengthOfData ;
2017-08-28 19:27:45 +03:00
}
2018-08-30 03:40:30 +03:00
case APFProtocol . DISCONNECT :
{
if ( len < 7 ) return 0 ;
var ReasonCode = common . ReadInt ( data , 1 ) ;
2019-05-02 01:02:03 +03:00
disconnectCommandCount ++ ;
2019-08-23 01:31:39 +03:00
parent . debug ( 'mpscmd' , 'DISCONNECT' , ReasonCode ) ;
2018-08-30 03:40:30 +03:00
try { delete obj . ciraConnections [ socket . tag . nodeid ] ; } catch ( e ) { }
obj . parent . ClearConnectivityState ( socket . tag . meshid , socket . tag . nodeid , 2 ) ;
return 7 ;
}
default :
{
2019-08-23 01:31:39 +03:00
parent . debug ( 'mpscmd' , 'Unknown CIRA command: ' + cmd ) ;
2018-08-30 03:40:30 +03:00
return - 1 ;
2017-08-28 19:27:45 +03:00
}
}
}
2018-08-30 03:40:30 +03:00
2017-08-28 19:27:45 +03:00
socket . addListener ( "close" , function ( ) {
2019-05-02 01:02:03 +03:00
socketClosedCount ++ ;
2019-08-23 01:31:39 +03:00
parent . debug ( 'mps' , 'CIRA connection closed' ) ;
2017-08-28 19:27:45 +03:00
try { delete obj . ciraConnections [ socket . tag . nodeid ] ; } catch ( e ) { }
obj . parent . ClearConnectivityState ( socket . tag . meshid , socket . tag . nodeid , 2 ) ;
} ) ;
2018-08-30 03:40:30 +03:00
2017-08-28 19:27:45 +03:00
socket . addListener ( "error" , function ( ) {
2019-05-02 01:02:03 +03:00
socketErrorCount ++ ;
2017-08-28 19:27:45 +03:00
//console.log("MPS Error: " + socket.remoteAddress);
} ) ;
}
2018-08-30 03:40:30 +03:00
2017-08-28 19:27:45 +03:00
// Disconnect CIRA tunnel
obj . close = function ( socket ) {
2018-05-03 02:19:45 +03:00
try { socket . end ( ) ; } catch ( e ) { }
2017-08-28 19:27:45 +03:00
try { delete obj . ciraConnections [ socket . tag . nodeid ] ; } catch ( e ) { }
obj . parent . ClearConnectivityState ( socket . tag . meshid , socket . tag . nodeid , 2 ) ;
2018-08-30 03:40:30 +03:00
} ;
2017-08-28 19:27:45 +03:00
function SendServiceAccept ( socket , service ) {
Write ( socket , String . fromCharCode ( APFProtocol . SERVICE _ACCEPT ) + common . IntToStr ( service . length ) + service ) ;
}
2018-08-30 03:40:30 +03:00
2017-08-28 19:27:45 +03:00
function SendTcpForwardSuccessReply ( socket , port ) {
Write ( socket , String . fromCharCode ( APFProtocol . REQUEST _SUCCESS ) + common . IntToStr ( port ) ) ;
}
2018-08-30 03:40:30 +03:00
2017-08-28 19:27:45 +03:00
function SendTcpForwardCancelReply ( socket ) {
Write ( socket , String . fromCharCode ( APFProtocol . REQUEST _SUCCESS ) ) ;
}
2018-08-30 03:40:30 +03:00
/ *
2017-08-28 19:27:45 +03:00
function SendKeepAliveRequest ( socket , cookie ) {
Write ( socket , String . fromCharCode ( APFProtocol . KEEPALIVE _REQUEST ) + common . IntToStr ( cookie ) ) ;
}
2018-08-30 03:40:30 +03:00
* /
2017-08-28 19:27:45 +03:00
function SendKeepAliveReply ( socket , cookie ) {
Write ( socket , String . fromCharCode ( APFProtocol . KEEPALIVE _REPLY ) + common . IntToStr ( cookie ) ) ;
}
2018-08-30 03:40:30 +03:00
2017-08-28 19:27:45 +03:00
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 ) ) ;
}
2018-08-30 03:40:30 +03:00
/ *
2017-08-28 19:27:45 +03:00
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 ) ) ;
}
2018-08-30 03:40:30 +03:00
* /
2017-08-28 19:27:45 +03:00
function SendChannelOpen ( socket , direct , channelid , windowsize , target , targetport , source , sourceport ) {
var connectionType = ( ( direct == true ) ? "direct-tcpip" : "forwarded-tcpip" ) ;
2019-03-05 10:48:45 +03:00
if ( ( target == null ) || ( target == null ) ) target = '' ; // TODO: Reports of target being undefined that causes target.length to fail. This is a hack.
2017-08-28 19:27:45 +03:00
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 ) ) ;
}
2018-08-30 03:40:30 +03:00
2017-08-28 19:27:45 +03:00
function SendChannelClose ( socket , channelid ) {
Write ( socket , String . fromCharCode ( APFProtocol . CHANNEL _CLOSE ) + common . IntToStr ( channelid ) ) ;
}
2018-08-30 03:40:30 +03:00
2017-08-28 19:27:45 +03:00
function SendChannelData ( socket , channelid , data ) {
Write ( socket , String . fromCharCode ( APFProtocol . CHANNEL _DATA ) + common . IntToStr ( channelid ) + common . IntToStr ( data . length ) + data ) ;
}
2018-08-30 03:40:30 +03:00
2017-08-28 19:27:45 +03:00
function SendChannelWindowAdjust ( socket , channelid , bytestoadd ) {
2019-08-23 01:31:39 +03:00
parent . debug ( 'mpscmd' , 'SendChannelWindowAdjust' , channelid , bytestoadd ) ;
2017-08-28 19:27:45 +03:00
Write ( socket , String . fromCharCode ( APFProtocol . CHANNEL _WINDOW _ADJUST ) + common . IntToStr ( channelid ) + common . IntToStr ( bytestoadd ) ) ;
}
2018-08-30 03:40:30 +03:00
/ *
2017-08-28 19:27:45 +03:00
function SendDisconnect ( socket , reasonCode ) {
2018-08-30 03:40:30 +03:00
Write ( socket , String . fromCharCode ( APFProtocol . DISCONNECT ) + common . IntToStr ( reasonCode ) + common . ShortToStr ( 0 ) ) ;
2017-08-28 19:27:45 +03:00
}
2018-08-30 03:40:30 +03:00
* /
2017-08-28 19:27:45 +03:00
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 ( args . mpsdebug ) {
// Print out sent bytes
2019-01-03 05:03:34 +03:00
var buf = Buffer . from ( data , "binary" ) ;
2017-08-28 19:27:45 +03:00
console . log ( 'MPS --> (' + buf . length + '):' + buf . toString ( 'hex' ) ) ;
socket . write ( buf ) ;
} else {
2019-01-03 05:03:34 +03:00
socket . write ( Buffer . from ( data , "binary" ) ) ;
2017-08-28 19:27:45 +03:00
}
}
2018-08-30 03:40:30 +03:00
2017-08-28 19:27:45 +03:00
obj . SetupCiraChannel = 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 ) ;
2018-08-30 03:40:30 +03:00
2017-08-28 19:27:45 +03:00
// This function writes data to this CIRA channel
cirachannel . write = function ( data ) {
if ( cirachannel . state == 0 ) return false ;
2019-03-05 10:48:45 +03:00
if ( cirachannel . state == 1 || cirachannel . sendcredits == 0 || cirachannel . sendBuffer != null ) {
2018-07-27 02:31:43 +03:00
// Channel is connected, but we are out of credits. Add the data to the outbound buffer.
2019-03-05 10:48:45 +03:00
if ( cirachannel . sendBuffer == null ) { cirachannel . sendBuffer = data ; } else { cirachannel . sendBuffer += data ; }
2018-07-27 02:31:43 +03:00
return true ;
}
2017-08-28 19:27:45 +03:00
// 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
2019-09-21 03:21:58 +03:00
cirachannel . sendBuffer = data . toString ( 'binary' ) . substring ( cirachannel . sendcredits ) ;
SendChannelData ( cirachannel . socket , cirachannel . amtchannelid , data . toString ( 'binary' ) . substring ( 0 , cirachannel . sendcredits ) ) ;
2017-08-28 19:27:45 +03:00
cirachannel . sendcredits = 0 ;
return false ;
2018-08-30 03:40:30 +03:00
} ;
2017-08-28 19:27:45 +03:00
// This function closes this CIRA 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 ) ; }
2018-08-30 03:40:30 +03:00
} ;
2017-08-28 19:27:45 +03:00
socket . tag . channels [ cirachannel . channelid ] = cirachannel ;
return cirachannel ;
2018-08-30 03:40:30 +03:00
} ;
2017-08-28 19:27:45 +03:00
2019-03-05 10:48:45 +03:00
function ChangeHostname ( socket , host , systemid ) {
2018-10-11 23:29:50 +03:00
if ( socket . tag . host === host ) return ; // Nothing to change
2017-08-28 19:27:45 +03:00
socket . tag . host = host ;
2018-08-30 03:40:30 +03:00
2017-08-28 19:27:45 +03:00
// Change the device
obj . db . Get ( socket . tag . nodeid , function ( err , nodes ) {
2019-05-21 04:03:14 +03:00
if ( ( nodes == null ) || ( nodes . length !== 1 ) ) return ;
2017-08-28 19:27:45 +03:00
var node = nodes [ 0 ] ;
2018-06-05 23:28:07 +03:00
// See if any changes need to be made
2019-03-05 10:48:45 +03:00
if ( ( node . intelamt != null ) && ( node . intelamt . host == host ) && ( node . name != null ) && ( node . name != '' ) && ( node . intelamt . state == 2 ) ) return ;
2018-08-30 03:40:30 +03:00
2017-08-28 19:27:45 +03:00
// Get the mesh for this device
obj . db . Get ( node . meshid , function ( err , meshes ) {
2019-05-21 04:03:14 +03:00
if ( ( meshes == null ) || ( meshes . length !== 1 ) ) return ;
2017-08-28 19:27:45 +03:00
var mesh = meshes [ 0 ] ;
// Ready the node change event
var changes = [ 'host' ] , event = { etype : 'node' , action : 'changenode' , nodeid : node . _id } ;
event . msg = + ": " ;
2018-08-30 03:40:30 +03:00
2017-08-28 19:27:45 +03:00
// Make the change & save
2019-03-05 10:48:45 +03:00
if ( node . intelamt == null ) node . intelamt = { } ;
2017-08-28 19:27:45 +03:00
node . intelamt . host = host ;
2018-06-05 23:28:07 +03:00
node . intelamt . state = 2 ; // Set the state to activated, since this is pretty obvious, we have a CIRA connection.
2019-03-05 10:48:45 +03:00
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.
2017-08-28 19:27:45 +03:00
obj . db . Set ( node ) ;
2018-06-05 23:28:07 +03:00
2017-08-28 19:27:45 +03:00
// Event the node change
2019-03-05 10:48:45 +03:00
event . msg = 'CIRA changed device ' + node . name + ' from group ' + mesh . name + ': ' + changes . join ( ', ' ) ;
2017-08-28 19:27:45 +03:00
var node2 = common . Clone ( node ) ;
if ( node2 . intelamt && node2 . intelamt . pass ) delete node2 . intelamt . pass ; // Remove the Intel AMT password before eventing this.
event . node = node2 ;
2019-05-29 03:25:23 +03:00
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.
2017-08-28 19:27:45 +03:00
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 ) ; }
2018-08-30 03:40:30 +03:00
2017-08-28 19:27:45 +03:00
return obj ;
2018-08-30 03:40:30 +03:00
} ;