2Copyright 2020-2021 Intel Corporation
4Licensed under the Apache License, Version 2.0 (the "License");
5you may not use this file except in compliance with the License.
6You may obtain a copy of the License at
8 http://www.apache.org/licenses/LICENSE-2.0
10Unless required by applicable law or agreed to in writing, software
11distributed under the License is distributed on an "AS IS" BASIS,
12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13See the License for the specific language governing permissions and
14limitations under the License.
16@description Intel AMT WSMAN communication module for NodeJS
17@author Ylian Saint-Hilaire
23/*jshint strict:false */
25/*jshint esversion: 6 */
28// Construct a WSMAN stack communication object
29var CreateWsmanComm = function (host, port, user, pass, tls, tlsoptions, mpsConnection) {
30 //console.log('CreateWsmanComm', host, port, user, pass, tls, tlsoptions);
33 obj.PendingAjax = []; // List of pending AJAX calls. When one frees up, another will start.
34 obj.ActiveAjaxCount = 0; // Number of currently active AJAX calls
35 obj.MaxActiveAjaxCount = 1; // Maximum number of activate AJAX calls at the same time.
36 obj.FailAllError = 0; // Set this to non-zero to fail all AJAX calls with that error status, 999 causes responses to be silent.
37 obj.challengeParams = null;
41 obj.net = require('net');
42 obj.tls = require('tls');
43 obj.crypto = require('crypto');
44 obj.constants = require('constants');
48 obj.amtVersion = null;
50 obj.Address = '/wsman';
51 obj.cnonce = obj.crypto.randomBytes(16).toString('hex'); // Generate a random client nonce
58 obj.xtlsoptions = tlsoptions;
59 obj.mpsConnection = mpsConnection; // Link to a MPS connection, this can be CIRA, Relay or LMS. If null, local sockets are used as transport.
61 obj.xtlsCertificate = null;
62 obj.xtlsCheck = 0; // 0 = No TLS, 1 = CA Checked, 2 = Pinned, 3 = Untrusted
63 obj.xtlsSkipHostCheck = 0;
65 obj.xtlsDataReceived = false;
66 obj.digestRealmMatch = null;
67 obj.digestRealm = null;
70 obj.Debug = function (msg) { console.log(msg); }
72 // Used to add TLS to a steam
73 function SerialTunnel(options) {
74 var obj = new require('stream').Duplex(options);
75 obj.forwardwrite = null;
76 obj.updateBuffer = function (chunk) { try { this.push(chunk); } catch (ex) { } };
77 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
78 obj._read = function (size) { }; // Push nothing, anything to read should be pushed from updateBuffer()
83 // pri = priority, if set to 1, the call is high priority and put on top of the stack.
84 obj.PerformAjax = function (postdata, callback, tag, pri, url, action) {
85 if ((obj.ActiveAjaxCount == 0 || ((obj.ActiveAjaxCount < obj.MaxActiveAjaxCount) && (obj.challengeParams != null))) && obj.PendingAjax.length == 0) {
86 // There are no pending AJAX calls, perform the call now.
87 obj.PerformAjaxEx(postdata, callback, tag, url, action);
89 // If this is a high priority call, put this call in front of the array, otherwise put it in the back.
90 if (pri == 1) { obj.PendingAjax.unshift([postdata, callback, tag, url, action]); } else { obj.PendingAjax.push([postdata, callback, tag, url, action]); }
95 obj.PerformNextAjax = function () {
96 if (obj.ActiveAjaxCount >= obj.MaxActiveAjaxCount || obj.PendingAjax.length == 0) return;
97 var x = obj.PendingAjax.shift();
98 obj.PerformAjaxEx(x[0], x[1], x[2], x[3], x[4]);
99 obj.PerformNextAjax();
103 obj.PerformAjaxEx = function (postdata, callback, tag, url, action) {
104 if (obj.FailAllError != 0) { obj.gotNextMessagesError({ status: obj.FailAllError }, 'error', null, [postdata, callback, tag, url, action]); return; }
105 if (!postdata) postdata = '';
106 //obj.Debug('SEND: ' + postdata); // DEBUG
108 obj.ActiveAjaxCount++;
109 return obj.PerformAjaxExNodeJS(postdata, callback, tag, url, action);
112 // NODE.js specific private method
113 obj.pendingAjaxCall = [];
115 // NODE.js specific private method
116 obj.PerformAjaxExNodeJS = function (postdata, callback, tag, url, action) { obj.PerformAjaxExNodeJS2(postdata, callback, tag, url, action, 5); }
118 // NODE.js specific private method
119 obj.PerformAjaxExNodeJS2 = function (postdata, callback, tag, url, action, retry) {
120 if ((retry <= 0) || (obj.FailAllError != 0)) {
121 // Too many retry, fail here.
122 obj.ActiveAjaxCount--;
123 if (obj.FailAllError != 999) obj.gotNextMessages(null, 'error', { status: ((obj.FailAllError == 0) ? 408 : obj.FailAllError) }, [postdata, callback, tag, url, action]); // 408 is timeout error
124 obj.PerformNextAjax();
127 obj.pendingAjaxCall.push([postdata, callback, tag, url, action, retry]);
128 if (obj.socketState == 0) { obj.xxConnectHttpSocket(); }
129 else if (obj.socketState == 2) { obj.sendRequest(postdata, url, action); }
132 // NODE.js specific private method
133 obj.sendRequest = function (postdata, url, action) {
134 url = url ? url : '/wsman';
135 action = action ? action : 'POST';
136 var h = action + ' ' + url + ' HTTP/1.1\r\n';
137 if (obj.challengeParams != null) {
138 obj.digestRealm = obj.challengeParams['realm'];
139 if (obj.digestRealmMatch && (obj.digestRealm != obj.digestRealmMatch)) {
140 obj.FailAllError = 997; // Cause all new responses to be silent. 997 = Digest Realm check error
141 obj.CancelAllQueries(997);
145 if ((obj.user == '*') && (kerberos != null)) {
147 if (obj.kerberosDone == 0) {
148 var ticketName = 'HTTP' + ((obj.tls == 1) ? 'S' : '') + '/' + ((obj.pass == '') ? (obj.host + ':' + obj.port) : obj.pass);
149 // Ask for the new Kerberos ticket
150 //console.log('kerberos.getTicket', ticketName);
151 var ticketReturn = kerberos.getTicket(ticketName);
152 if (ticketReturn.returnCode == 0 || ticketReturn.returnCode == 0x90312) {
153 h += 'Authorization: Negotiate ' + ticketReturn.ticket + '\r\n';
154 if (process.platform.indexOf('win') >= 0) {
155 // Clear kerberos tickets on both 32 and 64bit Windows platforms
156 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); }
159 console.log('Unexpected Kerberos error code: ' + ticketReturn.returnCode);
161 obj.kerberosDone = 1;
163 } else if (obj.challengeParams != null) {
164 var response = hex_md5(hex_md5(obj.user + ':' + obj.challengeParams['realm'] + ':' + obj.pass) + ':' + obj.challengeParams['nonce'] + ':' + nonceHex(obj.noncecounter) + ':' + obj.cnonce + ':' + obj.challengeParams['qop'] + ':' + hex_md5(action + ':' + url + ((obj.challengeParams['qop'] == 'auth-int') ? (':' + hex_md5(postdata)) : '')));
165 h += 'Authorization: ' + obj.renderDigest({ 'username': obj.user, 'realm': obj.challengeParams['realm'], 'nonce': obj.challengeParams['nonce'], 'uri': url, 'qop': obj.challengeParams['qop'], 'response': response, 'nc': nonceHex(obj.noncecounter++), 'cnonce': obj.cnonce }) + '\r\n';
167 h += 'Host: ' + obj.host + ':' + obj.port + '\r\nContent-Length: ' + postdata.length + '\r\n\r\n' + postdata; // Use Content-Length
168 //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
170 //console.log('SEND: ' + h); // Display send packet
173 // Parse the HTTP digest header and return a list of key & values.
174 obj.parseDigest = function (header) { return correctedQuoteSplit(header.substring(7)).reduce(function (obj, s) { var parts = s.trim().split('='); obj[parts[0]] = parts[1].replace(new RegExp('\"', 'g'), ''); return obj; }, {}) }
176 // Split a string on quotes but do not do it when in quotes
177 function correctedQuoteSplit(str) { return str.split(',').reduce(function (a, c) { if (a.ic) { a.st[a.st.length - 1] += ',' + c } else { a.st.push(c) } if (c.split('"').length % 2 == 0) { a.ic = !a.ic } return a; }, { st: [], ic: false }).st }
178 function nonceHex(v) { var s = ('00000000' + v.toString(16)); return s.substring(s.length - 8); }
180 // NODE.js specific private method
181 obj.renderDigest = function (params) {
182 var paramsnames = [];
183 for (var i in params) { paramsnames.push(i); }
184 return 'Digest ' + paramsnames.reduce(function (s1, ii) { return s1 + ',' + (((ii == 'nc') || (ii == 'qop')) ? (ii + '=' + params[ii]) : (ii + '="' + params[ii] + '"')); }, '').substring(1);
187 // NODE.js specific private method
188 obj.xxConnectHttpSocket = function () {
189 //obj.Debug("xxConnectHttpSocket");
190 obj.socketParseState = 0;
191 obj.socketAccumulator = '';
192 obj.socketHeader = null;
195 obj.kerberosDone = 0;
197 if (obj.mpsConnection != null) {
199 // Setup a new channel using the CIRA/Relay/LMS connection
200 obj.socket = obj.mpsConnection.SetupChannel(obj.port);
201 if (obj.socket == null) { obj.xxOnSocketClosed(); return; }
203 // Connect without TLS
204 obj.socket.onData = function (ccon, data) { obj.xxOnSocketData(data); }
205 obj.socket.onStateChange = function (ccon, state) {
208 obj.socketParseState = 0;
209 obj.socketAccumulator = '';
210 obj.socketHeader = null;
213 obj.xxOnSocketClosed();
214 } else if (state == 2) {
215 // Channel open success
216 obj.xxOnSocketConnected();
220 // Setup a new channel using the CIRA/Relay/LMS connection
221 obj.cirasocket = obj.mpsConnection.SetupChannel(obj.port);
222 if (obj.cirasocket == null) { obj.xxOnSocketClosed(); return; }
225 var ser = new SerialTunnel();
227 // let's chain up the TLSSocket <-> SerialTunnel <-> CIRA APF (chnl)
228 // Anything that needs to be forwarded by SerialTunnel will be encapsulated by chnl write
229 ser.forwardwrite = function (msg) { try { obj.cirasocket.write(msg); } catch (ex) { } }; // TLS ---> CIRA
231 // When APF tunnel return something, update SerialTunnel buffer
232 obj.cirasocket.onData = function (ciraconn, data) { if (data.length > 0) { try { ser.updateBuffer(Buffer.from(data, 'binary')); } catch (e) { } } }; // CIRA ---> TLS
234 // Handle CIRA tunnel state change
235 obj.cirasocket.onStateChange = function (ciraconn, state) {
236 if (state == 0) { obj.xxOnSocketClosed(); }
238 // TLSSocket to encapsulate TLS communication, which then tunneled via SerialTunnel an then wrapped through CIRA APF
239 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 | obj.constants.SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION, rejectUnauthorized: false };
240 if (obj.xtlsMethod == 1) {
241 options.secureProtocol = 'TLSv1_method';
243 options.minVersion = 'TLSv1';
245 if (obj.xtlsoptions) {
246 if (obj.xtlsoptions.ca) { options.ca = obj.xtlsoptions.ca; }
247 if (obj.xtlsoptions.cert) { options.cert = obj.xtlsoptions.cert; }
248 if (obj.xtlsoptions.key) { options.key = obj.xtlsoptions.key; }
251 obj.socket = obj.tls.connect(obj.port, obj.host, options, obj.xxOnSocketConnected);
252 obj.socket.setEncoding('binary');
253 obj.socket.setTimeout(60000); // Set socket idle timeout
254 obj.socket.on('error', function (ex) { obj.xtlsMethod = 1 - obj.xtlsMethod; });
255 obj.socket.on('close', obj.xxOnSocketClosed);
256 obj.socket.on('timeout', obj.destroy);
258 // Decrypted tunnel from TLS communcation to be forwarded to websocket
259 obj.socket.on('data', function (data) { try { obj.xxOnSocketData(data.toString('binary')); } catch (e) { } }); // AMT/TLS ---> WS
261 // If TLS is on, forward it through TLSSocket
262 obj.forwardclient = obj.socket;
263 obj.forwardclient.xtls = 1;
270 // Direct connect without TLS
271 obj.socket = new obj.net.Socket();
272 obj.socket.setEncoding('binary');
273 obj.socket.setTimeout(60000); // Set socket idle timeout
274 obj.socket.on('data', obj.xxOnSocketData);
275 obj.socket.on('close', obj.xxOnSocketClosed);
276 obj.socket.on('timeout', obj.destroy);
277 obj.socket.on('error', obj.xxOnSocketClosed);
278 obj.socket.connect(obj.port, obj.host, obj.xxOnSocketConnected);
280 // Direct connect with TLS
281 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 | obj.constants.SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION, rejectUnauthorized: false };
282 if (obj.xtlsMethod == 1) {
283 options.secureProtocol = 'TLSv1_method';
285 options.minVersion = 'TLSv1';
287 if (obj.xtlsoptions) {
288 if (obj.xtlsoptions.ca) { options.ca = obj.xtlsoptions.ca; }
289 if (obj.xtlsoptions.cert) { options.cert = obj.xtlsoptions.cert; }
290 if (obj.xtlsoptions.key) { options.key = obj.xtlsoptions.key; }
292 obj.socket = obj.tls.connect(obj.port, obj.host, options, obj.xxOnSocketConnected);
293 obj.socket.setEncoding('binary');
294 obj.socket.setTimeout(28000); // Set socket idle timeout of 28 seconds
295 obj.socket.on('data', obj.xxOnSocketData);
296 obj.socket.on('close', obj.xxOnSocketClosed);
297 obj.socket.on('timeout', obj.destroy);
298 obj.socket.on('error', function (ex) { if (ex.message && ex.message.indexOf('sslv3 alert bad record mac') >= 0) { obj.xtlsMethod = 1 - obj.xtlsMethod; } });
300 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?
304 // Get the certificate of Intel AMT
305 obj.getPeerCertificate = function () { if (obj.xtls == 1) { return obj.socket.getPeerCertificate(); } return null; }
306 obj.getPeerCertificateFingerprint = function () { if (obj.xtls == 1) { return obj.socket.getPeerCertificate().fingerprint.split(':').join('').toLowerCase(); } return null; }
308 // Check if the certificate matched the certificate hash.
309 function checkCertHash(cert, hash) {
310 // Check not required
311 if (hash == 0) return true;
314 if (cert.fingerprint.split(':').join('').toLowerCase() == hash) return true;
317 if ((hash.length == 64) && (obj.crypto.createHash('sha256').update(cert.raw).digest('hex') == hash)) { return true; }
320 if ((hash.length == 96) && (obj.crypto.createHash('sha384').update(cert.raw).digest('hex') == hash)) { return true; }
325 // NODE.js specific private method
326 obj.xxOnSocketConnected = function () {
327 if (obj.socket == null) return;
328 // check TLS certificate for webrelay and direct only
330 obj.xtlsCertificate = obj.socket.getPeerCertificate();
332 // Setup the forge certificate check
334 if ((obj.xtlsoptions != null) && (obj.xtlsoptions.ca != null)) {
335 var forgeCert = forge.pki.certificateFromAsn1(forge.asn1.fromDer(atob(obj.xtlsCertificate.raw.toString('base64'))));
336 var caStore = forge.pki.createCaStore(obj.xtlsoptions.ca);
337 // Got thru all certificates in the store and look for a match.
338 for (var i in caStore.certs) {
340 var c = caStore.certs[i], verified = false;
341 try { verified = c.verify(forgeCert); } catch (e) { }
342 if (verified == true) { camatch = c; }
345 // We found a match, check that the CommonName matches the hostname
346 if ((obj.xtlsSkipHostCheck == 0) && (camatch != 0)) {
347 amtcertname = forgeCert.subject.getField('CN').value;
348 if (amtcertname.toLowerCase() != obj.host.toLowerCase()) { camatch = 0; }
351 if ((camatch == 0) && (checkCertHash(obj.xtlsCertificate, obj.xtlsFingerprint) == false)) {
352 obj.FailAllError = 998; // Cause all new responses to be silent. 998 = TLS Certificate check error
353 obj.CancelAllQueries(998);
356 if ((obj.xtlsFingerprint == 0) && (camatch == 0)) { obj.xtlsCheck = 3; } else { obj.xtlsCheck = (camatch == 0) ? 2 : 1; }
357 } else { obj.xtlsCheck = 0; }
359 obj.socketParseState = 0;
360 for (i in obj.pendingAjaxCall) { obj.sendRequest(obj.pendingAjaxCall[i][0], obj.pendingAjaxCall[i][3], obj.pendingAjaxCall[i][4]); }
363 // NODE.js specific private method
364 obj.xxOnSocketData = function (data) {
365 //console.log('RECV: ' + data);
366 obj.xtlsDataReceived = true;
367 if (typeof data === 'object') {
368 // This is an ArrayBuffer, convert it to a string array (used in IE)
369 var binary = "", bytes = new Uint8Array(data), length = bytes.byteLength;
370 for (var i = 0; i < length; i++) { binary += String.fromCharCode(bytes[i]); }
373 else if (typeof data !== 'string') return;
375 obj.socketAccumulator += data;
377 //console.log('ACC(' + obj.socketAccumulator + '): ' + obj.socketAccumulator);
378 if (obj.socketParseState == 0) {
379 var headersize = obj.socketAccumulator.indexOf('\r\n\r\n');
380 if (headersize < 0) return;
381 //obj.Debug("Header: "+obj.socketAccumulator.substring(0, headersize)); // Display received HTTP header
382 obj.socketHeader = obj.socketAccumulator.substring(0, headersize).split('\r\n');
383 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); } } }
384 obj.socketAccumulator = obj.socketAccumulator.substring(headersize + 4);
385 obj.socketParseState = 1;
387 obj.socketXHeader = { Directive: obj.socketHeader[0].split(' ') };
388 for (i in obj.socketHeader) {
390 var x2 = obj.socketHeader[i].indexOf(':');
391 obj.socketXHeader[obj.socketHeader[i].substring(0, x2).toLowerCase()] = obj.socketHeader[i].substring(x2 + 2);
395 if (obj.socketParseState == 1) {
397 if ((obj.socketXHeader['connection'] != undefined) && (obj.socketXHeader['connection'].toLowerCase() == 'close') && ((obj.socketXHeader["transfer-encoding"] == undefined) || (obj.socketXHeader["transfer-encoding"].toLowerCase() != 'chunked'))) {
398 // The body ends with a close, in this case, we will only process the header
400 } else if (obj.socketXHeader['content-length'] != undefined) {
401 // The body length is specified by the content-length
402 csize = parseInt(obj.socketXHeader['content-length']);
403 if (obj.socketAccumulator.length < csize) return;
404 var data = obj.socketAccumulator.substring(0, csize);
405 obj.socketAccumulator = obj.socketAccumulator.substring(csize);
406 obj.socketData = data;
409 // The body is chunked
410 var clen = obj.socketAccumulator.indexOf('\r\n');
411 if (clen < 0) return; // Chunk length not found, exit now and get more data.
412 // Chunk length if found, lets see if we can get the data.
413 csize = parseInt(obj.socketAccumulator.substring(0, clen), 16);
414 if (obj.socketAccumulator.length < clen + 2 + csize + 2) return;
415 // We got a chunk with all of the data, handle the chunck now.
416 var data = obj.socketAccumulator.substring(clen + 2, clen + 2 + csize);
417 obj.socketAccumulator = obj.socketAccumulator.substring(clen + 2 + csize + 2);
418 try { obj.socketData += data; } catch (ex) { console.log(ex, typeof data, data.length); }
421 //obj.Debug("xxOnSocketData DONE: (" + obj.socketData.length + "): " + obj.socketData);
422 obj.xxProcessHttpResponse(obj.socketXHeader, obj.socketData);
423 obj.socketParseState = 0;
424 obj.socketHeader = null;
430 // NODE.js specific private method
431 obj.xxProcessHttpResponse = function (header, data) {
432 //obj.Debug("xxProcessHttpResponse: " + header.Directive[1]);
434 var s = parseInt(header.Directive[1]);
435 if (isNaN(s)) s = 500;
436 if (s == 401 && ++(obj.authcounter) < 3) {
437 obj.challengeParams = obj.parseDigest(header['www-authenticate']); // Set the digest parameters, after this, the socket will close and we will auto-retry
438 if (obj.challengeParams['qop'] != null) {
439 var qopList = obj.challengeParams['qop'].split(',');
440 for (var i in qopList) { qopList[i] = qopList[i].trim(); }
441 if (qopList.indexOf('auth-int') >= 0) { obj.challengeParams['qop'] = 'auth-int'; } else { obj.challengeParams['qop'] = 'auth'; }
443 if (obj.mpsConnection == null) { obj.socket.end(); } else { obj.socket.close(); }
445 var r = obj.pendingAjaxCall.shift();
446 if ((r == null) || (r.length < 1)) { /*console.log("pendingAjaxCall error, " + r);*/ return; } // Get a response without any pending requests.
447 //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.
449 obj.ActiveAjaxCount--;
450 obj.gotNextMessages(data, 'success', { status: s }, r);
451 obj.PerformNextAjax();
455 // NODE.js specific private method
456 obj.xxOnSocketClosed = function () {
457 //obj.Debug("xxOnSocketClosed");
459 if (obj.socket != null) {
460 if (obj.socket.removeAllListeners) {
461 // Do not remove the error handler since it may still get triggered.
462 obj.socket.removeAllListeners('data');
463 obj.socket.removeAllListeners('close');
464 obj.socket.removeAllListeners('timeout');
467 if (obj.mpsConnection == null) {
468 obj.socket.destroy();
470 if (obj.cirasocket != null) { obj.cirasocket.close(); } else { obj.socket.close(); }
474 obj.cirasocket = null;
476 if (obj.pendingAjaxCall.length > 0) {
477 var r = obj.pendingAjaxCall.shift(), retry = r[5];
478 setTimeout(function () { obj.PerformAjaxExNodeJS2(r[0], r[1], r[2], r[3], r[4], --retry) }, 500); // Wait half a second and try again
482 obj.destroy = function () {
483 if (obj.socket != null) {
484 if (obj.socket.removeAllListeners) {
485 // Do not remove the error handler since it may still get triggered.
486 obj.socket.removeAllListeners('data');
487 obj.socket.removeAllListeners('close');
488 obj.socket.removeAllListeners('timeout');
491 if (obj.mpsConnection == null) {
492 obj.socket.destroy();
494 if (obj.cirasocket != null) { obj.cirasocket.close(); } else { obj.socket.close(); }
498 delete obj.cirasocket;
503 // NODE.js specific private method
504 obj.xxSend = function (x) {
505 //console.log('xxSend', x);
506 if (obj.socketState == 2) { obj.socket.write(Buffer.from(x, 'binary')); }
509 // Cancel all pending queries with given status
510 obj.CancelAllQueries = function (s) {
511 obj.FailAllError = s;
512 while (obj.PendingAjax.length > 0) { var x = obj.PendingAjax.shift(); x[1](null, s, x[2]); }
517 obj.gotNextMessages = function (data, status, request, callArgs) {
518 if (obj.FailAllError == 999) return;
519 if (obj.FailAllError != 0) { try { callArgs[1](null, obj.FailAllError, callArgs[2]); } catch (ex) { console.error(ex); } return; }
520 if (request.status != 200) { try { callArgs[1](null, request.status, callArgs[2]); } catch (ex) { console.error(ex); } return; }
521 try { callArgs[1](data, 200, callArgs[2]); } catch (ex) { console.error(ex); }
525 obj.gotNextMessagesError = function (request, status, errorThrown, callArgs) {
526 if (obj.FailAllError == 999) return;
527 if (obj.FailAllError != 0) { try { callArgs[1](null, obj.FailAllError, callArgs[2]); } catch (ex) { console.error(ex); } return; }
528 try { callArgs[1](obj, null, { Header: { HttpError: request.status } }, request.status, callArgs[2]); } catch (ex) { console.error(ex); }
532 function hex_md5(str) { return obj.crypto.createHash('md5').update(str).digest('hex'); }
537module.exports = CreateWsmanComm;