mirror of
https://github.com/Ylianst/MeshCentral.git
synced 2024-12-25 14:45:52 -05:00
325 lines
14 KiB
JavaScript
325 lines
14 KiB
JavaScript
/*
|
|
Copyright 2018 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;
|
|
var lme_port_offset = 0; // Debug: Set this to "-100" to bind to 16892 & 16893 and IN_ADDRANY. This is for LMS debugging.
|
|
|
|
|
|
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;
|
|
|
|
// 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');
|
|
|
|
if (options.debug == true) { lme_port_offset = -100; } // LMS debug mode
|
|
|
|
var heci = require('heci');
|
|
this.INITIAL_RXWINDOW_SIZE = 4096;
|
|
|
|
this._LME = heci.create();
|
|
this._LME.LMS = this;
|
|
this._LME.on('error', function (e) { this.LMS.emit('error', e); });
|
|
this._LME.on('connect', function () {
|
|
this.LMS.emit('connect');
|
|
this.on('data', function (chunk) {
|
|
// this = HECI
|
|
var cmd = chunk.readUInt8(0);
|
|
|
|
switch (cmd) {
|
|
default:
|
|
//console.log('Received ' + chunk.length + ' bytes of data for LMS');
|
|
//console.log('Command = ' + cmd);
|
|
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 == 'pfwd@amt.intel.com' || name == 'auth@amt.intel.com') {
|
|
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] = {};
|
|
}
|
|
this[name][port] = require('net').createServer();
|
|
this[name][port].HECI = this;
|
|
if (lme_port_offset == 0) {
|
|
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);
|
|
});
|
|
var outBuffer = Buffer.alloc(5);
|
|
outBuffer.writeUInt8(81, 0);
|
|
outBuffer.writeUInt32BE(port, 1);
|
|
this.write(outBuffer);
|
|
break;
|
|
case 'cancel-tcpip-forward':
|
|
break;
|
|
case 'udp-send-to@amt.intel.com':
|
|
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[rChannelId] != undefined) {
|
|
this.sockets[rChannelId].pendingBytes.push(data.length);
|
|
this.sockets[rChannelId].write(data, function () {
|
|
var written = this.pendingBytes.shift();
|
|
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 {
|
|
//console.log('Unknown Recipient ID/' + rChannelId + ' for APF_CHANNEL_DATA');
|
|
}
|
|
break;
|
|
case APF_CHANNEL_CLOSE:
|
|
var rChannelId = chunk.readUInt32BE(1);
|
|
if (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 {
|
|
//console.log('Unknown Recipient ID/' + rChannelId + ' for APF_CHANNEL_CLOSE');
|
|
}
|
|
break;
|
|
}
|
|
});
|
|
});
|
|
|
|
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;
|
|
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 });
|
|
}
|
|
|
|
module.exports = lme_heci;
|