| 1 | /* |
| 2 | Copyright 2018-2021 Intel Corporation |
| 3 | |
| 4 | Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | you may not use this file except in compliance with the License. |
| 6 | You may obtain a copy of the License at |
| 7 | |
| 8 | http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | |
| 10 | Unless required by applicable law or agreed to in writing, software |
| 11 | distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | See the License for the specific language governing permissions and |
| 14 | limitations under the License. |
| 15 | */ |
| 16 | |
| 17 | var MemoryStream = require('MemoryStream'); |
| 18 | var lme_id = 0; // Our next channel identifier |
| 19 | var lme_port_offset = 0; // Debug: Set this to "-100" to bind to 16892 & 16893 and IN_ADDRANY. This is for LMS debugging. |
| 20 | var lme_bindany = false; // If true, bind to all network interfaces, not just loopback. |
| 21 | var xmlParser = null; |
| 22 | try { xmlParser = require('amt-xml'); } catch (ex) { } |
| 23 | |
| 24 | // Documented in: https://software.intel.com/sites/manageability/AMT_Implementation_and_Reference_Guide/HTMLDocuments/MPSDocuments/Intel%20AMT%20Port%20Forwarding%20Protocol%20Reference%20Manual.pdf |
| 25 | var APF_DISCONNECT = 1; |
| 26 | var APF_SERVICE_REQUEST = 5; |
| 27 | var APF_SERVICE_ACCEPT = 6; |
| 28 | var APF_USERAUTH_REQUEST = 50; |
| 29 | var APF_USERAUTH_FAILURE = 51; |
| 30 | var APF_USERAUTH_SUCCESS = 52; |
| 31 | var APF_GLOBAL_REQUEST = 80; |
| 32 | var APF_REQUEST_SUCCESS = 81; |
| 33 | var APF_REQUEST_FAILURE = 82; |
| 34 | var APF_CHANNEL_OPEN = 90; |
| 35 | var APF_CHANNEL_OPEN_CONFIRMATION = 91; |
| 36 | var APF_CHANNEL_OPEN_FAILURE = 92; |
| 37 | var APF_CHANNEL_WINDOW_ADJUST = 93; |
| 38 | var APF_CHANNEL_DATA = 94; |
| 39 | var APF_CHANNEL_CLOSE = 97; |
| 40 | var APF_PROTOCOLVERSION = 192; |
| 41 | |
| 42 | |
| 43 | function lme_object() { |
| 44 | this.ourId = ++lme_id; |
| 45 | this.amtId = -1; |
| 46 | this.LME_CHANNEL_STATUS = 'LME_CS_FREE'; |
| 47 | this.txWindow = 0; |
| 48 | this.rxWindow = 0; |
| 49 | this.localPort = 0; |
| 50 | this.errorCount = 0; |
| 51 | } |
| 52 | |
| 53 | function stream_bufferedWrite() { |
| 54 | var emitterUtils = require('events').inherits(this); |
| 55 | this.buffer = []; |
| 56 | this._readCheckImmediate = undefined; |
| 57 | this._ObjectID = "bufferedWriteStream"; |
| 58 | // Writable Events |
| 59 | emitterUtils.createEvent('close'); |
| 60 | emitterUtils.createEvent('drain'); |
| 61 | emitterUtils.createEvent('error'); |
| 62 | emitterUtils.createEvent('finish'); |
| 63 | emitterUtils.createEvent('pipe'); |
| 64 | emitterUtils.createEvent('unpipe'); |
| 65 | |
| 66 | // Readable Events |
| 67 | emitterUtils.createEvent('readable'); |
| 68 | this.isEmpty = function () { |
| 69 | return (this.buffer.length == 0); |
| 70 | }; |
| 71 | this.isWaiting = function () { |
| 72 | return (this._readCheckImmediate == undefined); |
| 73 | }; |
| 74 | this.write = function (chunk) { |
| 75 | for (var args in arguments) { if (typeof (arguments[args]) == 'function') { this.once('drain', arguments[args]); break; } } |
| 76 | var tmp = Buffer.alloc(chunk.length); |
| 77 | chunk.copy(tmp); |
| 78 | this.buffer.push({ offset: 0, data: tmp }); |
| 79 | this.emit('readable'); |
| 80 | return (this.buffer.length == 0 ? true : false); |
| 81 | }; |
| 82 | this.read = function () { |
| 83 | var size = arguments.length == 0 ? undefined : arguments[0]; |
| 84 | var bytesRead = 0; |
| 85 | var list = []; |
| 86 | while ((size == undefined || bytesRead < size) && this.buffer.length > 0) { |
| 87 | var len = this.buffer[0].data.length - this.buffer[0].offset; |
| 88 | var offset = this.buffer[0].offset; |
| 89 | |
| 90 | if (len > (size - bytesRead)) { |
| 91 | // Only reading a subset |
| 92 | list.push(this.buffer[0].data.slice(offset, offset + size - bytesRead)); |
| 93 | this.buffer[0].offset += (size - bytesRead); |
| 94 | bytesRead += (size - bytesRead); |
| 95 | } else { |
| 96 | // Reading the entire thing |
| 97 | list.push(this.buffer[0].data.slice(offset)); |
| 98 | bytesRead += len; |
| 99 | this.buffer.shift(); |
| 100 | } |
| 101 | } |
| 102 | this._readCheckImmediate = setImmediate(function (buffered) { |
| 103 | buffered._readCheckImmediate = undefined; |
| 104 | if (buffered.buffer.length == 0) { |
| 105 | buffered.emit('drain'); // Drained |
| 106 | } else { |
| 107 | buffered.emit('readable'); // Not drained |
| 108 | } |
| 109 | }, this); |
| 110 | return (Buffer.concat(list)); |
| 111 | }; |
| 112 | } |
| 113 | |
| 114 | |
| 115 | function lme_heci(options) { |
| 116 | var emitterUtils = require('events').inherits(this); |
| 117 | emitterUtils.createEvent('error'); |
| 118 | emitterUtils.createEvent('connect'); |
| 119 | emitterUtils.createEvent('notify'); |
| 120 | emitterUtils.createEvent('bind'); |
| 121 | |
| 122 | this.on('newListener', function (name, func) |
| 123 | { |
| 124 | if (name == 'connect' && this._LME._connected == true) { func.call(this); } |
| 125 | if (name == 'error' && this._LME._error !=null) { func.call(this, this._LME._error); } |
| 126 | }); |
| 127 | if (options != null) { |
| 128 | if (options.debug == true) { lme_port_offset = -100; } // LMS debug mode |
| 129 | if (options.bindany == true) { lme_bindany = true; } // Bind to all ports |
| 130 | } |
| 131 | |
| 132 | var heci = require('heci'); |
| 133 | this.INITIAL_RXWINDOW_SIZE = 4096; |
| 134 | |
| 135 | this._ObjectID = "lme"; |
| 136 | this._LME = heci.create(); |
| 137 | this._LME._connected = false; |
| 138 | this._LME._error = null; |
| 139 | this._LME.descriptorMetadata = "amt-lme"; |
| 140 | this._LME._binded = {}; |
| 141 | this._LME.LMS = this; |
| 142 | this._LME.on('error', function (e) { this._error = e; this.LMS.emit('error', e); }); |
| 143 | this._LME.on('connect', function () |
| 144 | { |
| 145 | this._connected = true; |
| 146 | this._emitConnected = false; |
| 147 | this.on('data', function (chunk) { |
| 148 | // this = HECI |
| 149 | var cmd = chunk.readUInt8(0); |
| 150 | //console.log('LME Command ' + cmd + ', ' + chunk.length + ' byte(s).'); |
| 151 | |
| 152 | switch (cmd) { |
| 153 | default: |
| 154 | console.log('Unhandled LME Command ' + cmd + ', ' + chunk.length + ' byte(s).'); |
| 155 | break; |
| 156 | case APF_SERVICE_REQUEST: |
| 157 | var nameLen = chunk.readUInt32BE(1); |
| 158 | var name = chunk.slice(5, nameLen + 5); |
| 159 | //console.log("Service Request for: " + name); |
| 160 | if (name == 'pfwd@amt.intel.com' || name == 'auth@amt.intel.com') { |
| 161 | var outBuffer = Buffer.alloc(5 + nameLen); |
| 162 | outBuffer.writeUInt8(6, 0); |
| 163 | outBuffer.writeUInt32BE(nameLen, 1); |
| 164 | outBuffer.write(name.toString(), 5); |
| 165 | this.write(outBuffer); |
| 166 | //console.log('Answering APF_SERVICE_REQUEST'); |
| 167 | } else { |
| 168 | //console.log('UNKNOWN APF_SERVICE_REQUEST'); |
| 169 | } |
| 170 | break; |
| 171 | case APF_GLOBAL_REQUEST: |
| 172 | var nameLen = chunk.readUInt32BE(1); |
| 173 | var name = chunk.slice(5, nameLen + 5).toString(); |
| 174 | |
| 175 | switch (name) { |
| 176 | case 'tcpip-forward': |
| 177 | var len = chunk.readUInt32BE(nameLen + 6); |
| 178 | var port = chunk.readUInt32BE(nameLen + 10 + len); |
| 179 | //console.log("[" + chunk.length + "/" + len + "] APF_GLOBAL_REQUEST for: " + name + " on port " + port); |
| 180 | if (this[name] == undefined) { this[name] = {}; } |
| 181 | if (this[name][port] != null) { // Close the existing binding |
| 182 | for (var i in this.sockets) { |
| 183 | var channel = this.sockets[i]; |
| 184 | if (channel.localPort == port) { this.sockets[i].end(); delete this.sockets[i]; } // Close this socket |
| 185 | } |
| 186 | } |
| 187 | if (this[name][port] == null) |
| 188 | { |
| 189 | try |
| 190 | { |
| 191 | // Bind a new server socket if not already present |
| 192 | this[name][port] = require('net').createServer(); |
| 193 | this[name][port].descriptorMetadata = 'amt-lme (port: ' + port + ')'; |
| 194 | this[name][port].HECI = this; |
| 195 | if (lme_port_offset == 0) { |
| 196 | if (lme_bindany) { |
| 197 | this[name][port].listen({ port: port }); // Bind all mode |
| 198 | } else { |
| 199 | this[name][port].listen({ port: port, host: '127.0.0.1' }); // Normal mode |
| 200 | } |
| 201 | } else { |
| 202 | this[name][port].listen({ port: (port + lme_port_offset) }); // Debug mode |
| 203 | } |
| 204 | this[name][port].on('connection', function (socket) { |
| 205 | //console.log('New [' + socket.remoteFamily + '] TCP Connection on: ' + socket.remoteAddress + ' :' + socket.localPort); |
| 206 | this.HECI.LMS.bindDuplexStream(socket, socket.remoteFamily, socket.localPort - lme_port_offset); |
| 207 | }); |
| 208 | this._binded[port] = true; |
| 209 | if (!this._emitConnected) |
| 210 | { |
| 211 | this._emitConnected = true; |
| 212 | this.LMS.emit('error', 'APF/BIND error'); |
| 213 | } |
| 214 | this.LMS.emit('bind', this._binded); |
| 215 | } catch (ex) |
| 216 | { |
| 217 | console.info1(ex, 'Port ' + port); |
| 218 | if(!this._emitConnected) |
| 219 | { |
| 220 | this._emitConnected = true; |
| 221 | this.LMS.emit('error', 'APF/BIND error'); |
| 222 | } |
| 223 | } |
| 224 | } |
| 225 | var outBuffer = Buffer.alloc(5); |
| 226 | outBuffer.writeUInt8(81, 0); |
| 227 | outBuffer.writeUInt32BE(port, 1); |
| 228 | this.write(outBuffer); |
| 229 | break; |
| 230 | case 'cancel-tcpip-forward': |
| 231 | var outBuffer = Buffer.alloc(1); |
| 232 | outBuffer.writeUInt8(APF_REQUEST_SUCCESS, 0); |
| 233 | this.write(outBuffer); |
| 234 | break; |
| 235 | case 'udp-send-to@amt.intel.com': |
| 236 | var outBuffer = Buffer.alloc(1); |
| 237 | outBuffer.writeUInt8(APF_REQUEST_FAILURE, 0); |
| 238 | this.write(outBuffer); |
| 239 | break; |
| 240 | default: |
| 241 | //console.log("Unknown APF_GLOBAL_REQUEST for: " + name); |
| 242 | break; |
| 243 | } |
| 244 | break; |
| 245 | case APF_CHANNEL_OPEN_CONFIRMATION: |
| 246 | var rChannel = chunk.readUInt32BE(1); |
| 247 | var sChannel = chunk.readUInt32BE(5); |
| 248 | var wSize = chunk.readUInt32BE(9); |
| 249 | //console.log('rChannel/' + rChannel + ', sChannel/' + sChannel + ', wSize/' + wSize); |
| 250 | if (this.sockets[rChannel] != undefined) { |
| 251 | this.sockets[rChannel].lme.amtId = sChannel; |
| 252 | this.sockets[rChannel].lme.rxWindow = wSize; |
| 253 | this.sockets[rChannel].lme.txWindow = wSize; |
| 254 | this.sockets[rChannel].lme.LME_CHANNEL_STATUS = 'LME_CS_CONNECTED'; |
| 255 | //console.log('LME_CS_CONNECTED'); |
| 256 | this.sockets[rChannel].bufferedStream = new stream_bufferedWrite(); |
| 257 | this.sockets[rChannel].bufferedStream.socket = this.sockets[rChannel]; |
| 258 | this.sockets[rChannel].bufferedStream.on('readable', function () { |
| 259 | if (this.socket.lme.txWindow > 0) { |
| 260 | var buffer = this.read(this.socket.lme.txWindow); |
| 261 | var packet = Buffer.alloc(9 + buffer.length); |
| 262 | packet.writeUInt8(APF_CHANNEL_DATA, 0); |
| 263 | packet.writeUInt32BE(this.socket.lme.amtId, 1); |
| 264 | packet.writeUInt32BE(buffer.length, 5); |
| 265 | buffer.copy(packet, 9); |
| 266 | this.socket.lme.txWindow -= buffer.length; |
| 267 | this.socket.HECI.write(packet); |
| 268 | } |
| 269 | }); |
| 270 | this.sockets[rChannel].bufferedStream.on('drain', function () { |
| 271 | this.socket.resume(); |
| 272 | }); |
| 273 | this.sockets[rChannel].on('data', function (chunk) { |
| 274 | if (!this.bufferedStream.write(chunk)) { this.pause(); } |
| 275 | }); |
| 276 | this.sockets[rChannel].on('end', function () { |
| 277 | var outBuffer = Buffer.alloc(5); |
| 278 | outBuffer.writeUInt8(APF_CHANNEL_CLOSE, 0); |
| 279 | outBuffer.writeUInt32BE(this.lme.amtId, 1); |
| 280 | this.HECI.write(outBuffer); |
| 281 | }); |
| 282 | this.sockets[rChannel].resume(); |
| 283 | } |
| 284 | |
| 285 | break; |
| 286 | case APF_PROTOCOLVERSION: |
| 287 | var major = chunk.readUInt32BE(1); |
| 288 | var minor = chunk.readUInt32BE(5); |
| 289 | var reason = chunk.readUInt32BE(9); |
| 290 | var outBuffer = Buffer.alloc(93); |
| 291 | outBuffer.writeUInt8(192, 0); |
| 292 | outBuffer.writeUInt32BE(1, 1); |
| 293 | outBuffer.writeUInt32BE(0, 5); |
| 294 | outBuffer.writeUInt32BE(reason, 9); |
| 295 | //console.log('Answering PROTOCOL_VERSION'); |
| 296 | this.write(outBuffer); |
| 297 | break; |
| 298 | case APF_CHANNEL_WINDOW_ADJUST: |
| 299 | var rChannelId = chunk.readUInt32BE(1); |
| 300 | var bytesToAdd = chunk.readUInt32BE(5); |
| 301 | if (this.sockets[rChannelId] != undefined) { |
| 302 | this.sockets[rChannelId].lme.txWindow += bytesToAdd; |
| 303 | if (!this.sockets[rChannelId].bufferedStream.isEmpty() && this.sockets[rChannelId].bufferedStream.isWaiting()) { |
| 304 | this.sockets[rChannelId].bufferedStream.emit('readable'); |
| 305 | } |
| 306 | } else { |
| 307 | console.log('Unknown Recipient ID/' + rChannelId + ' for APF_CHANNEL_WINDOW_ADJUST'); |
| 308 | } |
| 309 | break; |
| 310 | case APF_CHANNEL_DATA: |
| 311 | var rChannelId = chunk.readUInt32BE(1); |
| 312 | var dataLen = chunk.readUInt32BE(5); |
| 313 | var data = chunk.slice(9, 9 + dataLen); |
| 314 | if ((this.sockets != null) && (this.sockets[rChannelId] != undefined)) { |
| 315 | this.sockets[rChannelId].pendingBytes.push(data.length); |
| 316 | this.sockets[rChannelId].write(data, function () { |
| 317 | var written = this.pendingBytes.shift(); |
| 318 | //console.log('adjust', this.lme.amtId, written); |
| 319 | var outBuffer = Buffer.alloc(9); |
| 320 | outBuffer.writeUInt8(APF_CHANNEL_WINDOW_ADJUST, 0); |
| 321 | outBuffer.writeUInt32BE(this.lme.amtId, 1); |
| 322 | outBuffer.writeUInt32BE(written, 5); |
| 323 | this.HECI.write(outBuffer); |
| 324 | }); |
| 325 | } else if ((this.insockets != null) && (this.insockets[rChannelId] != undefined)) { |
| 326 | var channel = this.insockets[rChannelId]; |
| 327 | if (channel.data == null) { channel.data = data.toString(); } else { channel.data += data.toString(); } |
| 328 | channel.rxWindow += dataLen; |
| 329 | //console.log('IN DATA', channel.rxWindow, channel.data.length, dataLen, channel.amtId, data.toString()); |
| 330 | var httpData = parseHttp(channel.data); |
| 331 | if ((httpData != null) || (channel.data.length >= 8000)) { |
| 332 | // Parse the WSMAN |
| 333 | var notify = null; |
| 334 | if (xmlParser != null) { try { notify = xmlParser.ParseWsman(httpData); } catch (e) { } } |
| 335 | |
| 336 | // Event the http data |
| 337 | if (notify != null) { this.LMS.emit('notify', notify, channel.options, _lmsNotifyToCode(notify)); } |
| 338 | |
| 339 | // Send channel close |
| 340 | var buffer = Buffer.alloc(5); |
| 341 | buffer.writeUInt8(APF_CHANNEL_CLOSE, 0); |
| 342 | buffer.writeUInt32BE(amtId, 1); |
| 343 | this.write(buffer); |
| 344 | } else { |
| 345 | if (channel.rxWindow > 6000) { |
| 346 | // Send window adjust |
| 347 | var buffer = Buffer.alloc(9); |
| 348 | buffer.writeUInt8(APF_CHANNEL_WINDOW_ADJUST, 0); |
| 349 | buffer.writeUInt32BE(channel.amtId, 1); |
| 350 | buffer.writeUInt32BE(channel.rxWindow, 5); |
| 351 | this.write(buffer); |
| 352 | channel.rxWindow = 0; |
| 353 | } |
| 354 | } |
| 355 | } else { |
| 356 | console.log('Unknown Recipient ID/' + rChannelId + ' for APF_CHANNEL_DATA'); |
| 357 | } |
| 358 | break; |
| 359 | case APF_CHANNEL_OPEN_FAILURE: |
| 360 | var rChannelId = chunk.readUInt32BE(1); |
| 361 | var reasonCode = chunk.readUInt32BE(5); |
| 362 | if ((this.sockets != null) && (this.sockets[rChannelId] != undefined)) { |
| 363 | this.sockets[rChannelId].end(); |
| 364 | delete this.sockets[rChannelId]; |
| 365 | } else if ((this.insockets != null) && (this.insockets[rChannelId] != undefined)) { |
| 366 | delete this.insockets[rChannelId]; |
| 367 | } else { |
| 368 | console.log('Unknown Recipient ID/' + rChannelId + ' for APF_CHANNEL_OPEN_FAILURE'); |
| 369 | } |
| 370 | break; |
| 371 | case APF_CHANNEL_CLOSE: |
| 372 | var rChannelId = chunk.readUInt32BE(1); |
| 373 | if ((this.sockets != null) && (this.sockets[rChannelId] != undefined)) { |
| 374 | this.sockets[rChannelId].end(); |
| 375 | var amtId = this.sockets[rChannelId].lme.amtId; |
| 376 | var buffer = Buffer.alloc(5); |
| 377 | delete this.sockets[rChannelId]; |
| 378 | |
| 379 | buffer.writeUInt8(APF_CHANNEL_CLOSE, 0); // ???????????????????????????? |
| 380 | buffer.writeUInt32BE(amtId, 1); |
| 381 | this.write(buffer); |
| 382 | } else if ((this.insockets != null) && (this.insockets[rChannelId] != undefined)) { |
| 383 | delete this.insockets[rChannelId]; |
| 384 | // Should I send a close back???? |
| 385 | } else { |
| 386 | console.log('Unknown Recipient ID/' + rChannelId + ' for APF_CHANNEL_CLOSE'); |
| 387 | } |
| 388 | break; |
| 389 | case APF_CHANNEL_OPEN: |
| 390 | var nameLen = chunk.readUInt32BE(1); |
| 391 | var name = chunk.slice(5, nameLen + 5).toString(); |
| 392 | var channelSender = chunk.readUInt32BE(nameLen + 5); |
| 393 | var initialWindowSize = chunk.readUInt32BE(nameLen + 9); |
| 394 | var hostToConnectLen = chunk.readUInt32BE(nameLen + 17); |
| 395 | var hostToConnect = chunk.slice(nameLen + 21, nameLen + 21 + hostToConnectLen).toString(); |
| 396 | var portToConnect = chunk.readUInt32BE(nameLen + 21 + hostToConnectLen); |
| 397 | var originatorIpLen = chunk.readUInt32BE(nameLen + 25 + hostToConnectLen); |
| 398 | var originatorIp = chunk.slice(nameLen + 29 + hostToConnectLen, nameLen + 29 + hostToConnectLen + originatorIpLen).toString(); |
| 399 | var originatorPort = chunk.readUInt32BE(nameLen + 29 + hostToConnectLen + originatorIpLen); |
| 400 | //console.log('APF_CHANNEL_OPEN', name, channelSender, initialWindowSize, 'From: ' + originatorIp + ':' + originatorPort, 'To: ' + hostToConnect + ':' + portToConnect); |
| 401 | |
| 402 | if (this.insockets == null) { this.insockets = {}; } |
| 403 | var ourId = ++lme_id; |
| 404 | var insocket = new lme_object(); |
| 405 | insocket.ourId = ourId; |
| 406 | insocket.amtId = channelSender; |
| 407 | insocket.txWindow = initialWindowSize; |
| 408 | insocket.rxWindow = 0; |
| 409 | insocket.options = { target: hostToConnect, targetPort: portToConnect, source: originatorIp, sourcePort: originatorPort }; |
| 410 | this.insockets[ourId] = insocket; |
| 411 | |
| 412 | var buffer = Buffer.alloc(17); |
| 413 | buffer.writeUInt8(APF_CHANNEL_OPEN_CONFIRMATION, 0); |
| 414 | buffer.writeUInt32BE(channelSender, 1); // Intel AMT sender channel |
| 415 | buffer.writeUInt32BE(ourId, 5); // Our receiver channel id |
| 416 | buffer.writeUInt32BE(4000, 9); // Initial Window Size |
| 417 | buffer.writeUInt32BE(0xFFFFFFFF, 13); // Reserved |
| 418 | this.write(buffer); |
| 419 | |
| 420 | /* |
| 421 | var buffer = Buffer.alloc(17); |
| 422 | buffer.writeUInt8(APF_CHANNEL_OPEN_FAILURE, 0); |
| 423 | buffer.writeUInt32BE(channelSender, 1); // Intel AMT sender channel |
| 424 | buffer.writeUInt32BE(2, 5); // Reason code |
| 425 | buffer.writeUInt32BE(0, 9); // Reserved |
| 426 | buffer.writeUInt32BE(0, 13); // Reserved |
| 427 | this.write(buffer); |
| 428 | console.log('Sent APF_CHANNEL_OPEN_FAILURE', channelSender); |
| 429 | */ |
| 430 | |
| 431 | break; |
| 432 | } |
| 433 | }); |
| 434 | // |
| 435 | // Due to a change in behavior with AMT/11 (and possibly earlier), we are not going to emit 'connect' here, until |
| 436 | // we can verify that the first APF/Channel can be bound. Older AMT, like AMT/7 only allowed a single LME connection, so we |
| 437 | // used to emit connect here. However, newer AMT's will allow more than 1 LME connection, which will result in APF/Bind failure |
| 438 | // |
| 439 | //this.LMS.emit('connect'); |
| 440 | this.resume(); |
| 441 | |
| 442 | }); |
| 443 | |
| 444 | this.bindDuplexStream = function (duplexStream, remoteFamily, localPort) { |
| 445 | var socket = duplexStream; |
| 446 | //console.log('New [' + remoteFamily + '] Virtual Connection/' + socket.localPort); |
| 447 | socket.pendingBytes = []; |
| 448 | socket.HECI = this._LME; |
| 449 | socket.LMS = this; |
| 450 | socket.lme = new lme_object(); |
| 451 | socket.lme.Socket = socket; |
| 452 | socket.localPort = localPort; |
| 453 | var buffer = new MemoryStream(); |
| 454 | buffer.writeUInt8(0x5A); |
| 455 | buffer.writeUInt32BE(15); |
| 456 | buffer.write('forwarded-tcpip'); |
| 457 | buffer.writeUInt32BE(socket.lme.ourId); |
| 458 | buffer.writeUInt32BE(this.INITIAL_RXWINDOW_SIZE); |
| 459 | buffer.writeUInt32BE(0xFFFFFFFF); |
| 460 | for (var i = 0; i < 2; ++i) { |
| 461 | if (remoteFamily == 'IPv6') { |
| 462 | buffer.writeUInt32BE(3); |
| 463 | buffer.write('::1'); |
| 464 | } else { |
| 465 | buffer.writeUInt32BE(9); |
| 466 | buffer.write('127.0.0.1'); |
| 467 | } |
| 468 | buffer.writeUInt32BE(localPort); |
| 469 | } |
| 470 | this._LME.write(buffer.buffer); |
| 471 | if (this._LME.sockets == undefined) { this._LME.sockets = {}; } |
| 472 | this._LME.sockets[socket.lme.ourId] = socket; |
| 473 | socket.pause(); |
| 474 | }; |
| 475 | |
| 476 | this._LME.connect(heci.GUIDS.LME, { noPipeline: 0 }); |
| 477 | } |
| 478 | |
| 479 | function parseHttp(httpData) { |
| 480 | var i = httpData.indexOf('\r\n\r\n'); |
| 481 | if ((i == -1) || (httpData.length < (i + 2))) { return null; } |
| 482 | var headers = require('http-headers')(httpData.substring(0, i), true); |
| 483 | var contentLength = parseInt(headers['content-length']); |
| 484 | if (httpData.length >= contentLength + i + 4) { return httpData.substring(i + 4, i + 4 + contentLength); } |
| 485 | return null; |
| 486 | } |
| 487 | |
| 488 | function _lmsNotifyToCode(notify) { |
| 489 | if ((notify == null) || (notify.Body == null) || (notify.Body.MessageID == null)) return null; |
| 490 | var msgid = notify.Body.MessageID; |
| 491 | try { msgid += '-' + notify.Body.MessageArguments[0]; } catch (e) { } |
| 492 | return msgid; |
| 493 | } |
| 494 | |
| 495 | module.exports = lme_heci; |