2020-10-14 22:01:26 +03:00
/ *
2021-01-10 01:31:09 +03:00
Copyright 2020 - 2021 Intel Corporation
2020-10-14 22:01:26 +03:00
Licensed under the Apache License , Version 2.0 ( the "License" ) ;
you may not use this file except in compliance with the License .
You may obtain a copy of the License at
http : //www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing , software
distributed under the License is distributed on an "AS IS" BASIS ,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND , either express or implied .
See the License for the specific language governing permissions and
limitations under the License .
@ description Intel AMT WSMAN communication module for NodeJS
@ author Ylian Saint - Hilaire
@ version v0 . 3.0
2019-09-19 22:25:13 +03:00
* /
2020-10-14 22:01:26 +03:00
/*jslint node: true */
/*jshint node: true */
/*jshint strict:false */
/*jshint -W097 */
/*jshint esversion: 6 */
"use strict" ;
// Construct a WSMAN stack communication object
2020-10-16 02:08:36 +03:00
var CreateWsmanComm = function ( host , port , user , pass , tls , tlsoptions , mpsConnection ) {
2019-09-19 22:25:13 +03:00
//console.log('CreateWsmanComm', host, port, user, pass, tls, tlsoptions);
var obj = { } ;
obj . PendingAjax = [ ] ; // List of pending AJAX calls. When one frees up, another will start.
obj . ActiveAjaxCount = 0 ; // Number of currently active AJAX calls
obj . MaxActiveAjaxCount = 1 ; // Maximum number of activate AJAX calls at the same time.
obj . FailAllError = 0 ; // Set this to non-zero to fail all AJAX calls with that error status, 999 causes responses to be silent.
obj . challengeParams = null ;
obj . noncecounter = 1 ;
obj . authcounter = 0 ;
obj . net = require ( 'net' ) ;
obj . tls = require ( 'tls' ) ;
obj . crypto = require ( 'crypto' ) ;
obj . constants = require ( 'constants' ) ;
obj . socket = null ;
obj . socketState = 0 ;
obj . kerberosDone = 0 ;
obj . amtVersion = null ;
2020-09-23 00:25:22 +03:00
obj . Address = '/wsman' ;
obj . challengeParams = null ;
obj . noncecounter = 1 ;
obj . authcounter = 0 ;
obj . cnonce = obj . crypto . randomBytes ( 16 ) . toString ( 'hex' ) ; // Generate a random client nonce
2019-09-19 22:25:13 +03:00
obj . host = host ;
obj . port = port ;
obj . user = user ;
obj . pass = pass ;
obj . xtls = tls ;
obj . xtlsoptions = tlsoptions ;
2020-10-16 02:08:36 +03:00
obj . mpsConnection = mpsConnection ; // Link to a MPS connection, this can be CIRA, Relay or LMS. If null, local sockets are used as transport.
2019-09-19 22:25:13 +03:00
obj . xtlsFingerprint ;
obj . xtlsCertificate = null ;
obj . xtlsCheck = 0 ; // 0 = No TLS, 1 = CA Checked, 2 = Pinned, 3 = Untrusted
obj . xtlsSkipHostCheck = 0 ;
obj . xtlsMethod = 0 ;
obj . xtlsDataReceived = false ;
obj . digestRealmMatch = null ;
obj . digestRealm = null ;
// Private method
obj . Debug = function ( msg ) { console . log ( msg ) ; }
2020-10-14 03:46:29 +03:00
// Used to add TLS to a steam
function SerialTunnel ( options ) {
var obj = new require ( 'stream' ) . Duplex ( options ) ;
obj . forwardwrite = null ;
2021-04-10 01:00:10 +03:00
obj . updateBuffer = function ( chunk ) { try { this . push ( chunk ) ; } catch ( ex ) { } } ;
2020-10-14 03:46:29 +03:00
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-09-19 22:25:13 +03:00
// Private method
// pri = priority, if set to 1, the call is high priority and put on top of the stack.
obj . PerformAjax = function ( postdata , callback , tag , pri , url , action ) {
if ( ( obj . ActiveAjaxCount == 0 || ( ( obj . ActiveAjaxCount < obj . MaxActiveAjaxCount ) && ( obj . challengeParams != null ) ) ) && obj . PendingAjax . length == 0 ) {
// There are no pending AJAX calls, perform the call now.
obj . PerformAjaxEx ( postdata , callback , tag , url , action ) ;
} else {
// If this is a high priority call, put this call in front of the array, otherwise put it in the back.
if ( pri == 1 ) { obj . PendingAjax . unshift ( [ postdata , callback , tag , url , action ] ) ; } else { obj . PendingAjax . push ( [ postdata , callback , tag , url , action ] ) ; }
}
}
// Private method
obj . PerformNextAjax = function ( ) {
if ( obj . ActiveAjaxCount >= obj . MaxActiveAjaxCount || obj . PendingAjax . length == 0 ) return ;
var x = obj . PendingAjax . shift ( ) ;
obj . PerformAjaxEx ( x [ 0 ] , x [ 1 ] , x [ 2 ] , x [ 3 ] , x [ 4 ] ) ;
obj . PerformNextAjax ( ) ;
}
// Private method
obj . PerformAjaxEx = function ( postdata , callback , tag , url , action ) {
if ( obj . FailAllError != 0 ) { obj . gotNextMessagesError ( { status : obj . FailAllError } , 'error' , null , [ postdata , callback , tag , url , action ] ) ; return ; }
2020-10-14 22:01:26 +03:00
if ( ! postdata ) postdata = '' ;
//obj.Debug('SEND: ' + postdata); // DEBUG
2019-09-19 22:25:13 +03:00
obj . ActiveAjaxCount ++ ;
return obj . PerformAjaxExNodeJS ( postdata , callback , tag , url , action ) ;
}
// NODE.js specific private method
obj . pendingAjaxCall = [ ] ;
// NODE.js specific private method
obj . PerformAjaxExNodeJS = function ( postdata , callback , tag , url , action ) { obj . PerformAjaxExNodeJS2 ( postdata , callback , tag , url , action , 5 ) ; }
// NODE.js specific private method
obj . PerformAjaxExNodeJS2 = function ( postdata , callback , tag , url , action , retry ) {
2020-10-07 05:47:28 +03:00
if ( ( retry <= 0 ) || ( obj . FailAllError != 0 ) ) {
2019-09-19 22:25:13 +03:00
// Too many retry, fail here.
obj . ActiveAjaxCount -- ;
if ( obj . FailAllError != 999 ) obj . gotNextMessages ( null , 'error' , { status : ( ( obj . FailAllError == 0 ) ? 408 : obj . FailAllError ) } , [ postdata , callback , tag , url , action ] ) ; // 408 is timeout error
obj . PerformNextAjax ( ) ;
return ;
}
obj . pendingAjaxCall . push ( [ postdata , callback , tag , url , action , retry ] ) ;
if ( obj . socketState == 0 ) { obj . xxConnectHttpSocket ( ) ; }
else if ( obj . socketState == 2 ) { obj . sendRequest ( postdata , url , action ) ; }
}
// NODE.js specific private method
obj . sendRequest = function ( postdata , url , action ) {
2020-10-14 22:01:26 +03:00
url = url ? url : '/wsman' ;
action = action ? action : 'POST' ;
var h = action + ' ' + url + ' HTTP/1.1\r\n' ;
2019-09-19 22:25:13 +03:00
if ( obj . challengeParams != null ) {
2020-10-14 22:01:26 +03:00
obj . digestRealm = obj . challengeParams [ 'realm' ] ;
2019-09-19 22:25:13 +03:00
if ( obj . digestRealmMatch && ( obj . digestRealm != obj . digestRealmMatch ) ) {
obj . FailAllError = 997 ; // Cause all new responses to be silent. 997 = Digest Realm check error
obj . CancelAllQueries ( 997 ) ;
return ;
}
}
if ( ( obj . user == '*' ) && ( kerberos != null ) ) {
// Kerberos Auth
if ( obj . kerberosDone == 0 ) {
var ticketName = 'HTTP' + ( ( obj . tls == 1 ) ? 'S' : '' ) + '/' + ( ( obj . pass == '' ) ? ( obj . host + ':' + obj . port ) : obj . pass ) ;
// Ask for the new Kerberos ticket
//console.log('kerberos.getTicket', ticketName);
var ticketReturn = kerberos . getTicket ( ticketName ) ;
if ( ticketReturn . returnCode == 0 || ticketReturn . returnCode == 0x90312 ) {
h += 'Authorization: Negotiate ' + ticketReturn . ticket + '\r\n' ;
if ( process . platform . indexOf ( 'win' ) >= 0 ) {
// Clear kerberos tickets on both 32 and 64bit Windows platforms
try { require ( 'child_process' ) . exec ( '%windir%\\system32\\klist purge' , function ( error , stdout , stderr ) { if ( error ) { require ( 'child_process' ) . exec ( '%windir%\\sysnative\\klist purge' , function ( error , stdout , stderr ) { if ( error ) { console . error ( 'Unable to purge kerberos tickets' ) ; } } ) ; } } ) ; } catch ( e ) { console . log ( e ) ; }
}
} else {
console . log ( 'Unexpected Kerberos error code: ' + ticketReturn . returnCode ) ;
}
obj . kerberosDone = 1 ;
}
} else if ( obj . challengeParams != null ) {
2021-03-14 06:33:01 +03:00
var response = hex _md5 ( hex _md5 ( obj . user + ':' + obj . challengeParams [ 'realm' ] + ':' + obj . pass ) + ':' + obj . challengeParams [ 'nonce' ] + ':' + obj . noncecounter + ':' + obj . cnonce + ':' + obj . challengeParams [ 'qop' ] + ':' + hex _md5 ( action + ':' + url + ( ( obj . challengeParams [ 'qop' ] == 'auth-int' ) ? ( ':' + hex _md5 ( postdata ) ) : '' ) ) ) ;
2020-10-14 22:01:26 +03:00
h += 'Authorization: ' + obj . renderDigest ( { 'username' : obj . user , 'realm' : obj . challengeParams [ 'realm' ] , 'nonce' : obj . challengeParams [ 'nonce' ] , 'uri' : url , 'qop' : obj . challengeParams [ 'qop' ] , 'response' : response , 'nc' : obj . noncecounter ++ , 'cnonce' : obj . cnonce } ) + '\r\n' ;
2019-09-19 22:25:13 +03:00
}
h += 'Host: ' + obj . host + ':' + obj . port + '\r\nContent-Length: ' + postdata . length + '\r\n\r\n' + postdata ; // Use Content-Length
//h += 'Host: ' + obj.host + ':' + obj.port + '\r\nTransfer-Encoding: chunked\r\n\r\n' + postdata.length.toString(16).toUpperCase() + '\r\n' + postdata + '\r\n0\r\n\r\n'; // Use Chunked-Encoding
obj . xxSend ( h ) ;
2020-10-14 22:01:26 +03:00
//console.log('SEND: ' + h); // Display send packet
2019-09-19 22:25:13 +03:00
}
// NODE.js specific private method
obj . parseDigest = function ( header ) {
var t = header . substring ( 7 ) . split ( ',' ) ;
2020-10-14 22:03:46 +03:00
for ( var i in t ) t [ i ] = t [ i ] . trim ( ) ;
2019-09-19 22:25:13 +03:00
return t . reduce ( function ( obj , s ) { var parts = s . split ( '=' ) ; obj [ parts [ 0 ] ] = parts [ 1 ] . replace ( new RegExp ( '\"' , 'g' ) , '' ) ; return obj ; } , { } )
}
// NODE.js specific private method
obj . renderDigest = function ( params ) {
var paramsnames = [ ] ;
2020-10-14 22:03:46 +03:00
for ( var i in params ) { paramsnames . push ( i ) ; }
2019-09-19 22:25:13 +03:00
return 'Digest ' + paramsnames . reduce ( function ( s1 , ii ) { return s1 + ',' + ii + '="' + params [ ii ] + '"' } , '' ) . substring ( 1 ) ;
}
// NODE.js specific private method
obj . xxConnectHttpSocket = function ( ) {
//obj.Debug("xxConnectHttpSocket");
obj . socketParseState = 0 ;
obj . socketAccumulator = '' ;
obj . socketHeader = null ;
obj . socketData = '' ;
obj . socketState = 1 ;
obj . kerberosDone = 0 ;
2020-10-16 02:08:36 +03:00
if ( obj . mpsConnection != null ) {
2020-10-14 03:46:29 +03:00
if ( obj . xtls != 1 ) {
// Setup a new channel using the CIRA/Relay/LMS connection
2020-10-16 02:08:36 +03:00
obj . socket = obj . mpsConnection . SetupChannel ( obj . port ) ;
2020-10-14 03:46:29 +03:00
if ( obj . socket == null ) { obj . xxOnSocketClosed ( ) ; return ; }
// Connect without TLS
2020-10-08 09:13:45 +03:00
obj . socket . onData = function ( ccon , data ) { obj . xxOnSocketData ( data ) ; }
obj . socket . onStateChange = function ( ccon , state ) {
if ( state == 0 ) {
// Channel closed
2020-04-09 02:21:27 +03:00
obj . socketParseState = 0 ;
obj . socketAccumulator = '' ;
obj . socketHeader = null ;
obj . socketData = '' ;
obj . socketState = 0 ;
2020-10-14 03:46:29 +03:00
obj . xxOnSocketClosed ( ) ;
2020-10-08 09:13:45 +03:00
} else if ( state == 2 ) {
// Channel open success
obj . xxOnSocketConnected ( ) ;
}
2020-04-09 02:21:27 +03:00
}
2020-10-14 03:46:29 +03:00
} else {
// Setup a new channel using the CIRA/Relay/LMS connection
2020-10-16 02:08:36 +03:00
obj . cirasocket = obj . mpsConnection . SetupChannel ( obj . port ) ;
2020-10-14 03:46:29 +03:00
if ( obj . cirasocket == null ) { obj . xxOnSocketClosed ( ) ; return ; }
// Connect with TLS
var ser = new SerialTunnel ( ) ;
// let's chain up the TLSSocket <-> SerialTunnel <-> CIRA APF (chnl)
// Anything that needs to be forwarded by SerialTunnel will be encapsulated by chnl write
2020-10-29 00:46:40 +03:00
ser . forwardwrite = function ( msg ) { try { obj . cirasocket . write ( msg ) ; } catch ( ex ) { } } ; // TLS ---> CIRA
2020-10-14 03:46:29 +03:00
// When APF tunnel return something, update SerialTunnel buffer
obj . cirasocket . onData = function ( ciraconn , data ) { if ( data . length > 0 ) { try { ser . updateBuffer ( Buffer . from ( data , 'binary' ) ) ; } catch ( e ) { } } } ; // CIRA ---> TLS
// Handle CIRA tunnel state change
obj . cirasocket . onStateChange = function ( ciraconn , state ) {
if ( state == 0 ) { obj . xxOnSocketClosed ( ) ; }
if ( state == 2 ) {
// TLSSocket to encapsulate TLS communication, which then tunneled via SerialTunnel an then wrapped through CIRA APF
var options = { socket : ser , ciphers : 'RSA+AES:!aNULL:!MD5:!DSS' , secureOptions : obj . constants . SSL _OP _NO _SSLv2 | obj . constants . SSL _OP _NO _SSLv3 | obj . constants . SSL _OP _NO _COMPRESSION | obj . constants . SSL _OP _CIPHER _SERVER _PREFERENCE , rejectUnauthorized : false } ;
2020-10-18 05:23:24 +03:00
if ( obj . xtlsMethod == 1 ) { options . secureProtocol = 'TLSv1_method' ; }
2020-10-14 03:46:29 +03:00
if ( obj . xtlsoptions ) {
2021-03-04 12:54:04 +03:00
if ( obj . xtlsoptions . ca ) { options . ca = obj . xtlsoptions . ca ; }
if ( obj . xtlsoptions . cert ) { options . cert = obj . xtlsoptions . cert ; }
if ( obj . xtlsoptions . key ) { options . key = obj . xtlsoptions . key ; }
2020-10-14 03:46:29 +03:00
}
2021-08-05 04:59:11 +03:00
2020-10-14 03:46:29 +03:00
obj . socket = obj . tls . connect ( obj . port , obj . host , options , obj . xxOnSocketConnected ) ;
obj . socket . setEncoding ( 'binary' ) ;
2021-01-07 11:11:02 +03:00
obj . socket . setTimeout ( 60000 ) ; // Set socket idle timeout
2020-10-18 05:23:24 +03:00
obj . socket . on ( 'error' , function ( ex ) { obj . xtlsMethod = 1 - obj . xtlsMethod ; } ) ;
2020-10-14 03:46:29 +03:00
obj . socket . on ( 'close' , obj . xxOnSocketClosed ) ;
2021-04-06 00:01:21 +03:00
obj . socket . on ( 'timeout' , obj . destroy ) ;
2020-10-14 03:46:29 +03:00
// Decrypted tunnel from TLS communcation to be forwarded to websocket
obj . socket . on ( 'data' , function ( data ) { try { obj . xxOnSocketData ( data . toString ( 'binary' ) ) ; } catch ( e ) { } } ) ; // AMT/TLS ---> WS
// If TLS is on, forward it through TLSSocket
obj . forwardclient = obj . socket ;
obj . forwardclient . xtls = 1 ;
}
} ;
2020-04-09 02:21:27 +03:00
}
} else {
// Direct connection
2019-09-19 22:25:13 +03:00
if ( obj . xtls != 1 ) {
2020-10-08 09:13:45 +03:00
// Direct connect without TLS
2019-09-19 22:25:13 +03:00
obj . socket = new obj . net . Socket ( ) ;
obj . socket . setEncoding ( 'binary' ) ;
2021-01-07 11:11:02 +03:00
obj . socket . setTimeout ( 60000 ) ; // Set socket idle timeout
2019-09-19 22:25:13 +03:00
obj . socket . on ( 'data' , obj . xxOnSocketData ) ;
obj . socket . on ( 'close' , obj . xxOnSocketClosed ) ;
2021-04-06 00:01:21 +03:00
obj . socket . on ( 'timeout' , obj . destroy ) ;
2020-10-08 02:41:14 +03:00
obj . socket . on ( 'error' , obj . xxOnSocketClosed ) ;
2019-09-19 22:25:13 +03:00
obj . socket . connect ( obj . port , obj . host , obj . xxOnSocketConnected ) ;
} else {
2020-10-08 09:13:45 +03:00
// Direct connect with TLS
2020-03-31 05:29:46 +03:00
var options = { ciphers : 'RSA+AES:!aNULL:!MD5:!DSS' , secureOptions : obj . constants . SSL _OP _NO _SSLv2 | obj . constants . SSL _OP _NO _SSLv3 | obj . constants . SSL _OP _NO _COMPRESSION | obj . constants . SSL _OP _CIPHER _SERVER _PREFERENCE , rejectUnauthorized : false } ;
if ( obj . xtlsMethod != 0 ) { options . secureProtocol = 'TLSv1_method' ; }
2019-09-19 22:25:13 +03:00
if ( obj . xtlsoptions ) {
2021-03-12 07:17:23 +03:00
if ( obj . xtlsoptions . ca ) { options . ca = obj . xtlsoptions . ca ; }
if ( obj . xtlsoptions . cert ) { options . cert = obj . xtlsoptions . cert ; }
if ( obj . xtlsoptions . key ) { options . key = obj . xtlsoptions . key ; }
2019-09-19 22:25:13 +03:00
}
2020-10-07 05:47:28 +03:00
obj . socket = obj . tls . connect ( obj . port , obj . host , options , obj . xxOnSocketConnected ) ;
2019-09-19 22:25:13 +03:00
obj . socket . setEncoding ( 'binary' ) ;
2021-01-07 11:11:02 +03:00
obj . socket . setTimeout ( 60000 ) ; // Set socket idle timeout
2019-09-19 22:25:13 +03:00
obj . socket . on ( 'data' , obj . xxOnSocketData ) ;
obj . socket . on ( 'close' , obj . xxOnSocketClosed ) ;
2021-04-06 00:01:21 +03:00
obj . socket . on ( 'timeout' , obj . destroy ) ;
2021-03-16 23:56:14 +03:00
obj . socket . on ( 'error' , function ( ex ) { if ( ex . message && ex . message . indexOf ( 'sslv3 alert bad record mac' ) >= 0 ) { obj . xtlsMethod = 1 - obj . xtlsMethod ; } } ) ;
2019-09-19 22:25:13 +03:00
}
obj . socket . setNoDelay ( true ) ; // Disable nagle. We will encode each WSMAN request as a single send block and want to send it at once. This may help Intel AMT handle pipelining?
}
}
// Get the certificate of Intel AMT
obj . getPeerCertificate = function ( ) { if ( obj . xtls == 1 ) { return obj . socket . getPeerCertificate ( ) ; } return null ; }
obj . getPeerCertificateFingerprint = function ( ) { if ( obj . xtls == 1 ) { return obj . socket . getPeerCertificate ( ) . fingerprint . split ( ':' ) . join ( '' ) . toLowerCase ( ) ; } return null ; }
2021-03-16 23:56:14 +03:00
// Check if the certificate matched the certificate hash.
function checkCertHash ( cert , hash ) {
// Check not required
if ( hash == 0 ) return true ;
// SHA1 compare
if ( cert . fingerprint . split ( ':' ) . join ( '' ) . toLowerCase ( ) == hash ) return true ;
// SHA256 compare
if ( ( hash . length == 64 ) && ( obj . crypto . createHash ( 'sha256' ) . update ( cert . raw ) . digest ( 'hex' ) == hash ) ) { return true ; }
// SHA384 compare
if ( ( hash . length == 96 ) && ( obj . crypto . createHash ( 'sha384' ) . update ( cert . raw ) . digest ( 'hex' ) == hash ) ) { return true ; }
return false ;
}
2019-09-19 22:25:13 +03:00
// NODE.js specific private method
obj . xxOnSocketConnected = function ( ) {
if ( obj . socket == null ) return ;
// check TLS certificate for webrelay and direct only
2020-10-14 03:46:29 +03:00
if ( obj . xtls == 1 ) {
2019-09-19 22:25:13 +03:00
obj . xtlsCertificate = obj . socket . getPeerCertificate ( ) ;
// Setup the forge certificate check
var camatch = 0 ;
2020-10-07 05:47:28 +03:00
if ( ( obj . xtlsoptions != null ) && ( obj . xtlsoptions . ca != null ) ) {
2019-09-19 22:25:13 +03:00
var forgeCert = forge . pki . certificateFromAsn1 ( forge . asn1 . fromDer ( atob ( obj . xtlsCertificate . raw . toString ( 'base64' ) ) ) ) ;
var caStore = forge . pki . createCaStore ( obj . xtlsoptions . ca ) ;
// Got thru all certificates in the store and look for a match.
for ( var i in caStore . certs ) {
if ( camatch == 0 ) {
var c = caStore . certs [ i ] , verified = false ;
try { verified = c . verify ( forgeCert ) ; } catch ( e ) { }
if ( verified == true ) { camatch = c ; }
}
}
// We found a match, check that the CommonName matches the hostname
if ( ( obj . xtlsSkipHostCheck == 0 ) && ( camatch != 0 ) ) {
amtcertname = forgeCert . subject . getField ( 'CN' ) . value ;
if ( amtcertname . toLowerCase ( ) != obj . host . toLowerCase ( ) ) { camatch = 0 ; }
}
}
2021-03-16 23:56:14 +03:00
if ( ( camatch == 0 ) && ( checkCertHash ( obj . xtlsCertificate , obj . xtlsFingerprint ) == false ) ) {
2019-09-19 22:25:13 +03:00
obj . FailAllError = 998 ; // Cause all new responses to be silent. 998 = TLS Certificate check error
obj . CancelAllQueries ( 998 ) ;
return ;
}
if ( ( obj . xtlsFingerprint == 0 ) && ( camatch == 0 ) ) { obj . xtlsCheck = 3 ; } else { obj . xtlsCheck = ( camatch == 0 ) ? 2 : 1 ; }
} else { obj . xtlsCheck = 0 ; }
obj . socketState = 2 ;
obj . socketParseState = 0 ;
for ( i in obj . pendingAjaxCall ) { obj . sendRequest ( obj . pendingAjaxCall [ i ] [ 0 ] , obj . pendingAjaxCall [ i ] [ 3 ] , obj . pendingAjaxCall [ i ] [ 4 ] ) ; }
}
// NODE.js specific private method
obj . xxOnSocketData = function ( data ) {
2020-10-14 22:01:26 +03:00
//console.log('RECV: ' + data);
2019-09-21 03:21:58 +03:00
obj . xtlsDataReceived = true ;
2019-09-19 22:25:13 +03:00
if ( typeof data === 'object' ) {
// This is an ArrayBuffer, convert it to a string array (used in IE)
var binary = "" , bytes = new Uint8Array ( data ) , length = bytes . byteLength ;
for ( var i = 0 ; i < length ; i ++ ) { binary += String . fromCharCode ( bytes [ i ] ) ; }
data = binary ;
}
else if ( typeof data !== 'string' ) return ;
obj . socketAccumulator += data ;
while ( true ) {
//console.log('ACC(' + obj.socketAccumulator + '): ' + obj.socketAccumulator);
if ( obj . socketParseState == 0 ) {
2020-10-14 22:01:26 +03:00
var headersize = obj . socketAccumulator . indexOf ( '\r\n\r\n' ) ;
2019-09-19 22:25:13 +03:00
if ( headersize < 0 ) return ;
2019-09-21 03:21:58 +03:00
//obj.Debug("Header: "+obj.socketAccumulator.substring(0, headersize)); // Display received HTTP header
2020-10-14 22:01:26 +03:00
obj . socketHeader = obj . socketAccumulator . substring ( 0 , headersize ) . split ( '\r\n' ) ;
2019-09-19 22:25:13 +03:00
if ( obj . amtVersion == null ) { for ( var i in obj . socketHeader ) { if ( obj . socketHeader [ i ] . indexOf ( 'Server: Intel(R) Active Management Technology ' ) == 0 ) { obj . amtVersion = obj . socketHeader [ i ] . substring ( 46 ) ; } } }
obj . socketAccumulator = obj . socketAccumulator . substring ( headersize + 4 ) ;
obj . socketParseState = 1 ;
obj . socketData = '' ;
obj . socketXHeader = { Directive : obj . socketHeader [ 0 ] . split ( ' ' ) } ;
for ( i in obj . socketHeader ) {
if ( i != 0 ) {
var x2 = obj . socketHeader [ i ] . indexOf ( ':' ) ;
obj . socketXHeader [ obj . socketHeader [ i ] . substring ( 0 , x2 ) . toLowerCase ( ) ] = obj . socketHeader [ i ] . substring ( x2 + 2 ) ;
}
}
}
if ( obj . socketParseState == 1 ) {
var csize = - 1 ;
2020-10-14 22:01:26 +03:00
if ( ( obj . socketXHeader [ 'connection' ] != undefined ) && ( obj . socketXHeader [ 'connection' ] . toLowerCase ( ) == 'close' ) && ( ( obj . socketXHeader [ "transfer-encoding" ] == undefined ) || ( obj . socketXHeader [ "transfer-encoding" ] . toLowerCase ( ) != 'chunked' ) ) ) {
2019-09-19 22:25:13 +03:00
// The body ends with a close, in this case, we will only process the header
csize = 0 ;
2020-10-14 22:01:26 +03:00
} else if ( obj . socketXHeader [ 'content-length' ] != undefined ) {
2019-09-19 22:25:13 +03:00
// The body length is specified by the content-length
2020-10-14 22:01:26 +03:00
csize = parseInt ( obj . socketXHeader [ 'content-length' ] ) ;
2019-09-19 22:25:13 +03:00
if ( obj . socketAccumulator . length < csize ) return ;
var data = obj . socketAccumulator . substring ( 0 , csize ) ;
obj . socketAccumulator = obj . socketAccumulator . substring ( csize ) ;
obj . socketData = data ;
csize = 0 ;
} else {
// The body is chunked
2020-10-14 22:01:26 +03:00
var clen = obj . socketAccumulator . indexOf ( '\r\n' ) ;
2019-09-19 22:25:13 +03:00
if ( clen < 0 ) return ; // Chunk length not found, exit now and get more data.
// Chunk length if found, lets see if we can get the data.
csize = parseInt ( obj . socketAccumulator . substring ( 0 , clen ) , 16 ) ;
if ( obj . socketAccumulator . length < clen + 2 + csize + 2 ) return ;
// We got a chunk with all of the data, handle the chunck now.
var data = obj . socketAccumulator . substring ( clen + 2 , clen + 2 + csize ) ;
obj . socketAccumulator = obj . socketAccumulator . substring ( clen + 2 + csize + 2 ) ;
2021-07-31 04:06:26 +03:00
try { obj . socketData += data ; } catch ( ex ) { console . log ( ex , typeof data , data . length ) ; }
2019-09-19 22:25:13 +03:00
}
if ( csize == 0 ) {
//obj.Debug("xxOnSocketData DONE: (" + obj.socketData.length + "): " + obj.socketData);
obj . xxProcessHttpResponse ( obj . socketXHeader , obj . socketData ) ;
obj . socketParseState = 0 ;
obj . socketHeader = null ;
}
}
}
}
// NODE.js specific private method
obj . xxProcessHttpResponse = function ( header , data ) {
//obj.Debug("xxProcessHttpResponse: " + header.Directive[1]);
var s = parseInt ( header . Directive [ 1 ] ) ;
if ( isNaN ( s ) ) s = 500 ;
if ( s == 401 && ++ ( obj . authcounter ) < 3 ) {
2019-09-21 03:21:58 +03:00
obj . challengeParams = obj . parseDigest ( header [ 'www-authenticate' ] ) ; // Set the digest parameters, after this, the socket will close and we will auto-retry
2021-03-14 06:33:01 +03:00
if ( obj . challengeParams [ 'qop' ] != null ) {
var qopList = obj . challengeParams [ 'qop' ] . split ( ',' ) ;
for ( var i in qopList ) { qopList [ i ] = qopList [ i ] . trim ( ) ; }
if ( qopList . indexOf ( 'auth-int' ) >= 0 ) { obj . challengeParams [ 'qop' ] = 'auth-int' ; } else { obj . challengeParams [ 'qop' ] = 'auth' ; }
}
2020-10-16 02:08:36 +03:00
if ( obj . mpsConnection == null ) { obj . socket . end ( ) ; } else { obj . socket . close ( ) ; }
2019-09-19 22:25:13 +03:00
} else {
var r = obj . pendingAjaxCall . shift ( ) ;
2020-10-23 07:46:32 +03:00
if ( ( r == null ) || ( r . length < 1 ) ) { /*console.log("pendingAjaxCall error, " + r);*/ return ; } // Get a response without any pending requests.
2019-09-19 22:25:13 +03:00
//if (s != 200) { obj.Debug("Error, status=" + s + "\r\n\r\nreq=" + r[0] + "\r\n\r\nresp=" + data); } // Debug: Display the request & response if something did not work.
obj . authcounter = 0 ;
obj . ActiveAjaxCount -- ;
obj . gotNextMessages ( data , 'success' , { status : s } , r ) ;
obj . PerformNextAjax ( ) ;
}
}
// NODE.js specific private method
2020-10-07 05:47:28 +03:00
obj . xxOnSocketClosed = function ( ) {
2019-09-19 22:25:13 +03:00
//obj.Debug("xxOnSocketClosed");
obj . socketState = 0 ;
2020-10-09 00:25:16 +03:00
if ( obj . socket != null ) {
2021-04-13 01:27:44 +03:00
if ( obj . socket . removeAllListeners ) {
// Do not remove the error handler since it may still get triggered.
obj . socket . removeAllListeners ( 'data' ) ;
obj . socket . removeAllListeners ( 'close' ) ;
obj . socket . removeAllListeners ( 'timeout' ) ;
}
2020-10-14 03:46:29 +03:00
try {
2020-10-16 02:08:36 +03:00
if ( obj . mpsConnection == null ) {
2020-10-14 03:46:29 +03:00
obj . socket . destroy ( ) ;
} else {
if ( obj . cirasocket != null ) { obj . cirasocket . close ( ) ; } else { obj . socket . close ( ) ; }
}
} catch ( ex ) { }
2020-10-09 00:25:16 +03:00
obj . socket = null ;
2020-10-14 03:46:29 +03:00
obj . cirasocket = null ;
2020-10-09 00:25:16 +03:00
}
2019-09-19 22:25:13 +03:00
if ( obj . pendingAjaxCall . length > 0 ) {
2020-10-07 05:47:28 +03:00
var r = obj . pendingAjaxCall . shift ( ) , retry = r [ 5 ] ;
2019-09-19 22:25:13 +03:00
setTimeout ( function ( ) { obj . PerformAjaxExNodeJS2 ( r [ 0 ] , r [ 1 ] , r [ 2 ] , r [ 3 ] , r [ 4 ] , -- retry ) } , 500 ) ; // Wait half a second and try again
}
}
2021-04-06 00:01:21 +03:00
obj . destroy = function ( ) {
2020-10-09 00:25:16 +03:00
if ( obj . socket != null ) {
2021-04-12 08:44:43 +03:00
if ( obj . socket . removeAllListeners ) {
// Do not remove the error handler since it may still get triggered.
obj . socket . removeAllListeners ( 'data' ) ;
obj . socket . removeAllListeners ( 'close' ) ;
obj . socket . removeAllListeners ( 'timeout' ) ;
}
2020-10-14 03:46:29 +03:00
try {
2020-10-16 02:08:36 +03:00
if ( obj . mpsConnection == null ) {
2020-10-14 03:46:29 +03:00
obj . socket . destroy ( ) ;
} else {
2021-04-06 00:01:21 +03:00
if ( obj . cirasocket != null ) { obj . cirasocket . close ( ) ; } else { obj . socket . close ( ) ; }
2020-10-14 03:46:29 +03:00
}
} catch ( ex ) { }
2021-04-06 00:01:21 +03:00
delete obj . socket ;
delete obj . cirasocket ;
obj . socketState = 0 ;
2020-10-09 00:25:16 +03:00
}
2020-10-07 05:47:28 +03:00
}
2019-09-19 22:25:13 +03:00
// NODE.js specific private method
obj . xxSend = function ( x ) {
2020-10-14 03:46:29 +03:00
//console.log('xxSend', x);
if ( obj . socketState == 2 ) { obj . socket . write ( Buffer . from ( x , 'binary' ) ) ; }
2019-09-19 22:25:13 +03:00
}
// Cancel all pending queries with given status
obj . CancelAllQueries = function ( s ) {
obj . FailAllError = s ;
while ( obj . PendingAjax . length > 0 ) { var x = obj . PendingAjax . shift ( ) ; x [ 1 ] ( null , s , x [ 2 ] ) ; }
2021-04-06 00:01:21 +03:00
obj . destroy ( ) ;
2019-09-19 22:25:13 +03:00
}
// Private method
obj . gotNextMessages = function ( data , status , request , callArgs ) {
if ( obj . FailAllError == 999 ) return ;
if ( obj . FailAllError != 0 ) { try { callArgs [ 1 ] ( null , obj . FailAllError , callArgs [ 2 ] ) ; } catch ( ex ) { console . error ( ex ) ; } return ; }
if ( request . status != 200 ) { try { callArgs [ 1 ] ( null , request . status , callArgs [ 2 ] ) ; } catch ( ex ) { console . error ( ex ) ; } return ; }
try { callArgs [ 1 ] ( data , 200 , callArgs [ 2 ] ) ; } catch ( ex ) { console . error ( ex ) ; }
}
// Private method
obj . gotNextMessagesError = function ( request , status , errorThrown , callArgs ) {
if ( obj . FailAllError == 999 ) return ;
if ( obj . FailAllError != 0 ) { try { callArgs [ 1 ] ( null , obj . FailAllError , callArgs [ 2 ] ) ; } catch ( ex ) { console . error ( ex ) ; } return ; }
try { callArgs [ 1 ] ( obj , null , { Header : { HttpError : request . status } } , request . status , callArgs [ 2 ] ) ; } catch ( ex ) { console . error ( ex ) ; }
}
2020-10-14 22:01:26 +03:00
// MD5 digest hash
function hex _md5 ( str ) { return obj . crypto . createHash ( 'md5' ) . update ( str ) . digest ( 'hex' ) ; }
2019-09-19 22:25:13 +03:00
return obj ;
}
module . exports = CreateWsmanComm ;