| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495 |
- /*
- Copyright 2018-2021 Intel Corporation
- 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.
- */
- var MemoryStream = require('MemoryStream');
- var lme_id = 0; // Our next channel identifier
- var lme_port_offset = 0; // Debug: Set this to "-100" to bind to 16892 & 16893 and IN_ADDRANY. This is for LMS debugging.
- var lme_bindany = false; // If true, bind to all network interfaces, not just loopback.
- var xmlParser = null;
- try { xmlParser = require('amt-xml'); } catch (ex) { }
- // Documented in: https://software.intel.com/sites/manageability/AMT_Implementation_and_Reference_Guide/HTMLDocuments/MPSDocuments/Intel%20AMT%20Port%20Forwarding%20Protocol%20Reference%20Manual.pdf
- var APF_DISCONNECT = 1;
- var APF_SERVICE_REQUEST = 5;
- var APF_SERVICE_ACCEPT = 6;
- var APF_USERAUTH_REQUEST = 50;
- var APF_USERAUTH_FAILURE = 51;
- var APF_USERAUTH_SUCCESS = 52;
- var APF_GLOBAL_REQUEST = 80;
- var APF_REQUEST_SUCCESS = 81;
- var APF_REQUEST_FAILURE = 82;
- var APF_CHANNEL_OPEN = 90;
- var APF_CHANNEL_OPEN_CONFIRMATION = 91;
- var APF_CHANNEL_OPEN_FAILURE = 92;
- var APF_CHANNEL_WINDOW_ADJUST = 93;
- var APF_CHANNEL_DATA = 94;
- var APF_CHANNEL_CLOSE = 97;
- var APF_PROTOCOLVERSION = 192;
- function lme_object() {
- this.ourId = ++lme_id;
- this.amtId = -1;
- this.LME_CHANNEL_STATUS = 'LME_CS_FREE';
- this.txWindow = 0;
- this.rxWindow = 0;
- this.localPort = 0;
- this.errorCount = 0;
- }
- function stream_bufferedWrite() {
- var emitterUtils = require('events').inherits(this);
- this.buffer = [];
- this._readCheckImmediate = undefined;
- this._ObjectID = "bufferedWriteStream";
- // Writable Events
- emitterUtils.createEvent('close');
- emitterUtils.createEvent('drain');
- emitterUtils.createEvent('error');
- emitterUtils.createEvent('finish');
- emitterUtils.createEvent('pipe');
- emitterUtils.createEvent('unpipe');
-
- // Readable Events
- emitterUtils.createEvent('readable');
- this.isEmpty = function () {
- return (this.buffer.length == 0);
- };
- this.isWaiting = function () {
- return (this._readCheckImmediate == undefined);
- };
- this.write = function (chunk) {
- for (var args in arguments) { if (typeof (arguments[args]) == 'function') { this.once('drain', arguments[args]); break; } }
- var tmp = Buffer.alloc(chunk.length);
- chunk.copy(tmp);
- this.buffer.push({ offset: 0, data: tmp });
- this.emit('readable');
- return (this.buffer.length == 0 ? true : false);
- };
- this.read = function () {
- var size = arguments.length == 0 ? undefined : arguments[0];
- var bytesRead = 0;
- var list = [];
- while ((size == undefined || bytesRead < size) && this.buffer.length > 0) {
- var len = this.buffer[0].data.length - this.buffer[0].offset;
- var offset = this.buffer[0].offset;
-
- if (len > (size - bytesRead)) {
- // Only reading a subset
- list.push(this.buffer[0].data.slice(offset, offset + size - bytesRead));
- this.buffer[0].offset += (size - bytesRead);
- bytesRead += (size - bytesRead);
- } else {
- // Reading the entire thing
- list.push(this.buffer[0].data.slice(offset));
- bytesRead += len;
- this.buffer.shift();
- }
- }
- this._readCheckImmediate = setImmediate(function (buffered) {
- buffered._readCheckImmediate = undefined;
- if (buffered.buffer.length == 0) {
- buffered.emit('drain'); // Drained
- } else {
- buffered.emit('readable'); // Not drained
- }
- }, this);
- return (Buffer.concat(list));
- };
- }
- function lme_heci(options) {
- var emitterUtils = require('events').inherits(this);
- emitterUtils.createEvent('error');
- emitterUtils.createEvent('connect');
- emitterUtils.createEvent('notify');
- emitterUtils.createEvent('bind');
-
- this.on('newListener', function (name, func)
- {
- if (name == 'connect' && this._LME._connected == true) { func.call(this); }
- if (name == 'error' && this._LME._error !=null) { func.call(this, this._LME._error); }
- });
- if (options != null) {
- if (options.debug == true) { lme_port_offset = -100; } // LMS debug mode
- if (options.bindany == true) { lme_bindany = true; } // Bind to all ports
- }
- var heci = require('heci');
- this.INITIAL_RXWINDOW_SIZE = 4096;
-
- this._ObjectID = "lme";
- this._LME = heci.create();
- this._LME._connected = false;
- this._LME._error = null;
- this._LME.descriptorMetadata = "amt-lme";
- this._LME._binded = {};
- this._LME.LMS = this;
- this._LME.on('error', function (e) { this._error = e; this.LMS.emit('error', e); });
- this._LME.on('connect', function ()
- {
- this._connected = true;
- this._emitConnected = false;
- this.on('data', function (chunk) {
- // this = HECI
- var cmd = chunk.readUInt8(0);
- //console.log('LME Command ' + cmd + ', ' + chunk.length + ' byte(s).');
-
- switch (cmd) {
- default:
- console.log('Unhandled LME Command ' + cmd + ', ' + chunk.length + ' byte(s).');
- break;
- case APF_SERVICE_REQUEST:
- var nameLen = chunk.readUInt32BE(1);
- var name = chunk.slice(5, nameLen + 5);
- //console.log("Service Request for: " + name);
- if (name == '[email protected]' || name == '[email protected]') {
- var outBuffer = Buffer.alloc(5 + nameLen);
- outBuffer.writeUInt8(6, 0);
- outBuffer.writeUInt32BE(nameLen, 1);
- outBuffer.write(name.toString(), 5);
- this.write(outBuffer);
- //console.log('Answering APF_SERVICE_REQUEST');
- } else {
- //console.log('UNKNOWN APF_SERVICE_REQUEST');
- }
- break;
- case APF_GLOBAL_REQUEST:
- var nameLen = chunk.readUInt32BE(1);
- var name = chunk.slice(5, nameLen + 5).toString();
- switch (name) {
- case 'tcpip-forward':
- var len = chunk.readUInt32BE(nameLen + 6);
- var port = chunk.readUInt32BE(nameLen + 10 + len);
- //console.log("[" + chunk.length + "/" + len + "] APF_GLOBAL_REQUEST for: " + name + " on port " + port);
- if (this[name] == undefined) { this[name] = {}; }
- if (this[name][port] != null) { // Close the existing binding
- for (var i in this.sockets) {
- var channel = this.sockets[i];
- if (channel.localPort == port) { this.sockets[i].end(); delete this.sockets[i]; } // Close this socket
- }
- }
- if (this[name][port] == null)
- {
- try
- {
- // Bind a new server socket if not already present
- this[name][port] = require('net').createServer();
- this[name][port].descriptorMetadata = 'amt-lme (port: ' + port + ')';
- this[name][port].HECI = this;
- if (lme_port_offset == 0) {
- if (lme_bindany) {
- this[name][port].listen({ port: port }); // Bind all mode
- } else {
- this[name][port].listen({ port: port, host: '127.0.0.1' }); // Normal mode
- }
- } else {
- this[name][port].listen({ port: (port + lme_port_offset) }); // Debug mode
- }
- this[name][port].on('connection', function (socket) {
- //console.log('New [' + socket.remoteFamily + '] TCP Connection on: ' + socket.remoteAddress + ' :' + socket.localPort);
- this.HECI.LMS.bindDuplexStream(socket, socket.remoteFamily, socket.localPort - lme_port_offset);
- });
- this._binded[port] = true;
- if (!this._emitConnected)
- {
- this._emitConnected = true;
- this.LMS.emit('error', 'APF/BIND error');
- }
- this.LMS.emit('bind', this._binded);
- } catch (ex)
- {
- console.info1(ex, 'Port ' + port);
- if(!this._emitConnected)
- {
- this._emitConnected = true;
- this.LMS.emit('error', 'APF/BIND error');
- }
- }
- }
- var outBuffer = Buffer.alloc(5);
- outBuffer.writeUInt8(81, 0);
- outBuffer.writeUInt32BE(port, 1);
- this.write(outBuffer);
- break;
- case 'cancel-tcpip-forward':
- var outBuffer = Buffer.alloc(1);
- outBuffer.writeUInt8(APF_REQUEST_SUCCESS, 0);
- this.write(outBuffer);
- break;
- case '[email protected]':
- var outBuffer = Buffer.alloc(1);
- outBuffer.writeUInt8(APF_REQUEST_FAILURE, 0);
- this.write(outBuffer);
- break;
- default:
- //console.log("Unknown APF_GLOBAL_REQUEST for: " + name);
- break;
- }
- break;
- case APF_CHANNEL_OPEN_CONFIRMATION:
- var rChannel = chunk.readUInt32BE(1);
- var sChannel = chunk.readUInt32BE(5);
- var wSize = chunk.readUInt32BE(9);
- //console.log('rChannel/' + rChannel + ', sChannel/' + sChannel + ', wSize/' + wSize);
- if (this.sockets[rChannel] != undefined) {
- this.sockets[rChannel].lme.amtId = sChannel;
- this.sockets[rChannel].lme.rxWindow = wSize;
- this.sockets[rChannel].lme.txWindow = wSize;
- this.sockets[rChannel].lme.LME_CHANNEL_STATUS = 'LME_CS_CONNECTED';
- //console.log('LME_CS_CONNECTED');
- this.sockets[rChannel].bufferedStream = new stream_bufferedWrite();
- this.sockets[rChannel].bufferedStream.socket = this.sockets[rChannel];
- this.sockets[rChannel].bufferedStream.on('readable', function () {
- if (this.socket.lme.txWindow > 0) {
- var buffer = this.read(this.socket.lme.txWindow);
- var packet = Buffer.alloc(9 + buffer.length);
- packet.writeUInt8(APF_CHANNEL_DATA, 0);
- packet.writeUInt32BE(this.socket.lme.amtId, 1);
- packet.writeUInt32BE(buffer.length, 5);
- buffer.copy(packet, 9);
- this.socket.lme.txWindow -= buffer.length;
- this.socket.HECI.write(packet);
- }
- });
- this.sockets[rChannel].bufferedStream.on('drain', function () {
- this.socket.resume();
- });
- this.sockets[rChannel].on('data', function (chunk) {
- if (!this.bufferedStream.write(chunk)) { this.pause(); }
- });
- this.sockets[rChannel].on('end', function () {
- var outBuffer = Buffer.alloc(5);
- outBuffer.writeUInt8(APF_CHANNEL_CLOSE, 0);
- outBuffer.writeUInt32BE(this.lme.amtId, 1);
- this.HECI.write(outBuffer);
- });
- this.sockets[rChannel].resume();
- }
-
- break;
- case APF_PROTOCOLVERSION:
- var major = chunk.readUInt32BE(1);
- var minor = chunk.readUInt32BE(5);
- var reason = chunk.readUInt32BE(9);
- var outBuffer = Buffer.alloc(93);
- outBuffer.writeUInt8(192, 0);
- outBuffer.writeUInt32BE(1, 1);
- outBuffer.writeUInt32BE(0, 5);
- outBuffer.writeUInt32BE(reason, 9);
- //console.log('Answering PROTOCOL_VERSION');
- this.write(outBuffer);
- break;
- case APF_CHANNEL_WINDOW_ADJUST:
- var rChannelId = chunk.readUInt32BE(1);
- var bytesToAdd = chunk.readUInt32BE(5);
- if (this.sockets[rChannelId] != undefined) {
- this.sockets[rChannelId].lme.txWindow += bytesToAdd;
- if (!this.sockets[rChannelId].bufferedStream.isEmpty() && this.sockets[rChannelId].bufferedStream.isWaiting()) {
- this.sockets[rChannelId].bufferedStream.emit('readable');
- }
- } else {
- console.log('Unknown Recipient ID/' + rChannelId + ' for APF_CHANNEL_WINDOW_ADJUST');
- }
- break;
- case APF_CHANNEL_DATA:
- var rChannelId = chunk.readUInt32BE(1);
- var dataLen = chunk.readUInt32BE(5);
- var data = chunk.slice(9, 9 + dataLen);
- if ((this.sockets != null) && (this.sockets[rChannelId] != undefined)) {
- this.sockets[rChannelId].pendingBytes.push(data.length);
- this.sockets[rChannelId].write(data, function () {
- var written = this.pendingBytes.shift();
- //console.log('adjust', this.lme.amtId, written);
- var outBuffer = Buffer.alloc(9);
- outBuffer.writeUInt8(APF_CHANNEL_WINDOW_ADJUST, 0);
- outBuffer.writeUInt32BE(this.lme.amtId, 1);
- outBuffer.writeUInt32BE(written, 5);
- this.HECI.write(outBuffer);
- });
- } else if ((this.insockets != null) && (this.insockets[rChannelId] != undefined)) {
- var channel = this.insockets[rChannelId];
- if (channel.data == null) { channel.data = data.toString(); } else { channel.data += data.toString(); }
- channel.rxWindow += dataLen;
- //console.log('IN DATA', channel.rxWindow, channel.data.length, dataLen, channel.amtId, data.toString());
- var httpData = parseHttp(channel.data);
- if ((httpData != null) || (channel.data.length >= 8000)) {
- // Parse the WSMAN
- var notify = null;
- if (xmlParser != null) { try { notify = xmlParser.ParseWsman(httpData); } catch (e) { } }
- // Event the http data
- if (notify != null) { this.LMS.emit('notify', notify, channel.options, _lmsNotifyToCode(notify)); }
- // Send channel close
- var buffer = Buffer.alloc(5);
- buffer.writeUInt8(APF_CHANNEL_CLOSE, 0);
- buffer.writeUInt32BE(amtId, 1);
- this.write(buffer);
- } else {
- if (channel.rxWindow > 6000) {
- // Send window adjust
- var buffer = Buffer.alloc(9);
- buffer.writeUInt8(APF_CHANNEL_WINDOW_ADJUST, 0);
- buffer.writeUInt32BE(channel.amtId, 1);
- buffer.writeUInt32BE(channel.rxWindow, 5);
- this.write(buffer);
- channel.rxWindow = 0;
- }
- }
- } else {
- console.log('Unknown Recipient ID/' + rChannelId + ' for APF_CHANNEL_DATA');
- }
- break;
- case APF_CHANNEL_OPEN_FAILURE:
- var rChannelId = chunk.readUInt32BE(1);
- var reasonCode = chunk.readUInt32BE(5);
- if ((this.sockets != null) && (this.sockets[rChannelId] != undefined)) {
- this.sockets[rChannelId].end();
- delete this.sockets[rChannelId];
- } else if ((this.insockets != null) && (this.insockets[rChannelId] != undefined)) {
- delete this.insockets[rChannelId];
- } else {
- console.log('Unknown Recipient ID/' + rChannelId + ' for APF_CHANNEL_OPEN_FAILURE');
- }
- break;
- case APF_CHANNEL_CLOSE:
- var rChannelId = chunk.readUInt32BE(1);
- if ((this.sockets != null) && (this.sockets[rChannelId] != undefined)) {
- this.sockets[rChannelId].end();
- var amtId = this.sockets[rChannelId].lme.amtId;
- var buffer = Buffer.alloc(5);
- delete this.sockets[rChannelId];
-
- buffer.writeUInt8(APF_CHANNEL_CLOSE, 0); // ????????????????????????????
- buffer.writeUInt32BE(amtId, 1);
- this.write(buffer);
- } else if ((this.insockets != null) && (this.insockets[rChannelId] != undefined)) {
- delete this.insockets[rChannelId];
- // Should I send a close back????
- } else {
- console.log('Unknown Recipient ID/' + rChannelId + ' for APF_CHANNEL_CLOSE');
- }
- break;
- case APF_CHANNEL_OPEN:
- var nameLen = chunk.readUInt32BE(1);
- var name = chunk.slice(5, nameLen + 5).toString();
- var channelSender = chunk.readUInt32BE(nameLen + 5);
- var initialWindowSize = chunk.readUInt32BE(nameLen + 9);
- var hostToConnectLen = chunk.readUInt32BE(nameLen + 17);
- var hostToConnect = chunk.slice(nameLen + 21, nameLen + 21 + hostToConnectLen).toString();
- var portToConnect = chunk.readUInt32BE(nameLen + 21 + hostToConnectLen);
- var originatorIpLen = chunk.readUInt32BE(nameLen + 25 + hostToConnectLen);
- var originatorIp = chunk.slice(nameLen + 29 + hostToConnectLen, nameLen + 29 + hostToConnectLen + originatorIpLen).toString();
- var originatorPort = chunk.readUInt32BE(nameLen + 29 + hostToConnectLen + originatorIpLen);
- //console.log('APF_CHANNEL_OPEN', name, channelSender, initialWindowSize, 'From: ' + originatorIp + ':' + originatorPort, 'To: ' + hostToConnect + ':' + portToConnect);
- if (this.insockets == null) { this.insockets = {}; }
- var ourId = ++lme_id;
- var insocket = new lme_object();
- insocket.ourId = ourId;
- insocket.amtId = channelSender;
- insocket.txWindow = initialWindowSize;
- insocket.rxWindow = 0;
- insocket.options = { target: hostToConnect, targetPort: portToConnect, source: originatorIp, sourcePort: originatorPort };
- this.insockets[ourId] = insocket;
- var buffer = Buffer.alloc(17);
- buffer.writeUInt8(APF_CHANNEL_OPEN_CONFIRMATION, 0);
- buffer.writeUInt32BE(channelSender, 1); // Intel AMT sender channel
- buffer.writeUInt32BE(ourId, 5); // Our receiver channel id
- buffer.writeUInt32BE(4000, 9); // Initial Window Size
- buffer.writeUInt32BE(0xFFFFFFFF, 13); // Reserved
- this.write(buffer);
- /*
- var buffer = Buffer.alloc(17);
- buffer.writeUInt8(APF_CHANNEL_OPEN_FAILURE, 0);
- buffer.writeUInt32BE(channelSender, 1); // Intel AMT sender channel
- buffer.writeUInt32BE(2, 5); // Reason code
- buffer.writeUInt32BE(0, 9); // Reserved
- buffer.writeUInt32BE(0, 13); // Reserved
- this.write(buffer);
- console.log('Sent APF_CHANNEL_OPEN_FAILURE', channelSender);
- */
- break;
- }
- });
- //
- // Due to a change in behavior with AMT/11 (and possibly earlier), we are not going to emit 'connect' here, until
- // we can verify that the first APF/Channel can be bound. Older AMT, like AMT/7 only allowed a single LME connection, so we
- // used to emit connect here. However, newer AMT's will allow more than 1 LME connection, which will result in APF/Bind failure
- //
- //this.LMS.emit('connect');
- this.resume();
- });
-
- this.bindDuplexStream = function (duplexStream, remoteFamily, localPort) {
- var socket = duplexStream;
- //console.log('New [' + remoteFamily + '] Virtual Connection/' + socket.localPort);
- socket.pendingBytes = [];
- socket.HECI = this._LME;
- socket.LMS = this;
- socket.lme = new lme_object();
- socket.lme.Socket = socket;
- socket.localPort = localPort;
- var buffer = new MemoryStream();
- buffer.writeUInt8(0x5A);
- buffer.writeUInt32BE(15);
- buffer.write('forwarded-tcpip');
- buffer.writeUInt32BE(socket.lme.ourId);
- buffer.writeUInt32BE(this.INITIAL_RXWINDOW_SIZE);
- buffer.writeUInt32BE(0xFFFFFFFF);
- for (var i = 0; i < 2; ++i) {
- if (remoteFamily == 'IPv6') {
- buffer.writeUInt32BE(3);
- buffer.write('::1');
- } else {
- buffer.writeUInt32BE(9);
- buffer.write('127.0.0.1');
- }
- buffer.writeUInt32BE(localPort);
- }
- this._LME.write(buffer.buffer);
- if (this._LME.sockets == undefined) { this._LME.sockets = {}; }
- this._LME.sockets[socket.lme.ourId] = socket;
- socket.pause();
- };
-
- this._LME.connect(heci.GUIDS.LME, { noPipeline: 0 });
- }
- function parseHttp(httpData) {
- var i = httpData.indexOf('\r\n\r\n');
- if ((i == -1) || (httpData.length < (i + 2))) { return null; }
- var headers = require('http-headers')(httpData.substring(0, i), true);
- var contentLength = parseInt(headers['content-length']);
- if (httpData.length >= contentLength + i + 4) { return httpData.substring(i + 4, i + 4 + contentLength); }
- return null;
- }
- function _lmsNotifyToCode(notify) {
- if ((notify == null) || (notify.Body == null) || (notify.Body.MessageID == null)) return null;
- var msgid = notify.Body.MessageID;
- try { msgid += '-' + notify.Body.MessageArguments[0]; } catch (e) { }
- return msgid;
- }
- module.exports = lme_heci;
|