| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374 |
- /*
- * Copyright (c) 2014-2015 Sylvain Peyrefitte
- *
- * This file is part of node-rdpjs.
- *
- * node-rdpjs is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
- var net = require('net');
- var inherits = require('util').inherits;
- var events = require('events');
- var layer = require('../core').layer;
- var error = require('../core').error;
- var rle = require('../core').rle;
- var log = require('../core').log;
- var TPKT = require('./tpkt');
- var x224 = require('./x224');
- var t125 = require('./t125');
- var pdu = require('./pdu');
- /**
- * decompress bitmap from RLE algorithm
- * @param bitmap {object} bitmap object of bitmap event of node-rdpjs
- */
- function decompress(bitmap) {
- var fName = null;
- switch (bitmap.bitsPerPixel.value) {
- case 15:
- fName = 'bitmap_decompress_15';
- break;
- case 16:
- fName = 'bitmap_decompress_16';
- break;
- case 24:
- fName = 'bitmap_decompress_24';
- break;
- case 32:
- fName = 'bitmap_decompress_32';
- break;
- default:
- throw 'invalid bitmap data format';
- }
- var input = new Uint8Array(bitmap.bitmapDataStream.value);
- var inputPtr = rle._malloc(input.length);
- var inputHeap = new Uint8Array(rle.HEAPU8.buffer, inputPtr, input.length);
- inputHeap.set(input);
- var ouputSize = bitmap.width.value * bitmap.height.value * 4;
- var outputPtr = rle._malloc(ouputSize);
- var outputHeap = new Uint8Array(rle.HEAPU8.buffer, outputPtr, ouputSize);
- var res = rle.ccall(fName,
- 'number',
- ['number', 'number', 'number', 'number', 'number', 'number', 'number', 'number'],
- [outputHeap.byteOffset, bitmap.width.value, bitmap.height.value, bitmap.width.value, bitmap.height.value, inputHeap.byteOffset, input.length]
- );
- var output = new Uint8ClampedArray(outputHeap.buffer, outputHeap.byteOffset, ouputSize);
- rle._free(inputPtr);
- rle._free(outputPtr);
- return output;
- }
- /**
- * Main RDP module
- */
- function RdpClient(config) {
- config = config || {};
- this.connected = false;
- this.bufferLayer = new layer.BufferLayer(new net.Socket());
- this.tpkt = new TPKT(this.bufferLayer);
- this.x224 = new x224.Client(this.tpkt, config);
- this.mcs = new t125.mcs.Client(this.x224);
- this.sec = new pdu.sec.Client(this.mcs, this.tpkt);
- this.cliprdr = new pdu.cliprdr.Client(this.mcs);
- this.global = new pdu.global.Client(this.sec, this.sec);
- // config log level
- log.level = log.Levels[config.logLevel || 'INFO'] || log.Levels.INFO;
- // credentials
- if (config.domain) {
- this.sec.infos.obj.domain.value = Buffer.from(config.domain + '\x00', 'ucs2');
- }
- if (config.userName) {
- this.sec.infos.obj.userName.value = Buffer.from(config.userName + '\x00', 'ucs2');
- }
- if (config.password) {
- this.sec.infos.obj.password.value = Buffer.from(config.password + '\x00', 'ucs2');
- }
- if (config.workingDir) {
- this.sec.infos.obj.workingDir.value = Buffer.from(config.workingDir + '\x00', 'ucs2');
- }
- if (config.alternateShell) {
- this.sec.infos.obj.alternateShell.value = Buffer.from(config.alternateShell + '\x00', 'ucs2');
- }
- if (config.perfFlags != null) {
- this.sec.infos.obj.extendedInfo.obj.performanceFlags.value = config.perfFlags;
- } else {
- if (config.enablePerf) {
- this.sec.infos.obj.extendedInfo.obj.performanceFlags.value =
- pdu.sec.PerfFlag.PERF_DISABLE_WALLPAPER
- | pdu.sec.PerfFlag.PERF_DISABLE_MENUANIMATIONS
- | pdu.sec.PerfFlag.PERF_DISABLE_CURSOR_SHADOW
- | pdu.sec.PerfFlag.PERF_DISABLE_THEMING
- | pdu.sec.PerfFlag.PERF_DISABLE_FULLWINDOWDRAG;
- }
- }
- if (config.autoLogin) {
- this.sec.infos.obj.flag.value |= pdu.sec.InfoFlag.INFO_AUTOLOGON;
- }
- if (config.screen && config.screen.width && config.screen.height) {
- this.mcs.clientCoreData.obj.desktopWidth.value = config.screen.width;
- this.mcs.clientCoreData.obj.desktopHeight.value = config.screen.height;
- }
- log.debug('screen ' + this.mcs.clientCoreData.obj.desktopWidth.value + 'x' + this.mcs.clientCoreData.obj.desktopHeight.value);
- // config keyboard layout
- switch (config.locale) {
- case 'fr':
- log.debug('french keyboard layout');
- this.mcs.clientCoreData.obj.kbdLayout.value = t125.gcc.KeyboardLayout.FRENCH;
- break;
- case 'en':
- default:
- log.debug('english keyboard layout');
- this.mcs.clientCoreData.obj.kbdLayout.value = t125.gcc.KeyboardLayout.US;
- }
- this.cliprdr.on('clipboard', (content) => {
- this.emit('clipboard', content)
- });
- //bind all events
- var self = this;
- this.global.on('connect', function () {
- self.connected = true;
- self.emit('connect');
- }).on('session', function () {
- self.emit('session');
- }).on('close', function () {
- self.connected = false;
- self.emit('close');
- }).on('pointer', function (cursorId, cursorStr) {
- self.emit('pointer', cursorId, cursorStr);
- }).on('bitmap', function (bitmaps) {
- for (var bitmap in bitmaps) {
- var bitmapData = bitmaps[bitmap].obj.bitmapDataStream.value;
- var isCompress = bitmaps[bitmap].obj.flags.value & pdu.data.BitmapFlag.BITMAP_COMPRESSION;
- if (isCompress && config.decompress) {
- bitmapData = decompress(bitmaps[bitmap].obj);
- isCompress = false;
- }
- self.emit('bitmap', {
- destTop: bitmaps[bitmap].obj.destTop.value,
- destLeft: bitmaps[bitmap].obj.destLeft.value,
- destBottom: bitmaps[bitmap].obj.destBottom.value,
- destRight: bitmaps[bitmap].obj.destRight.value,
- width: bitmaps[bitmap].obj.width.value,
- height: bitmaps[bitmap].obj.height.value,
- bitsPerPixel: bitmaps[bitmap].obj.bitsPerPixel.value,
- isCompress: isCompress,
- data: bitmapData
- });
- }
- }).on('error', function (err) {
- log.warn(err.code + '(' + err.message + ')\n' + err.stack);
- if (err instanceof error.FatalError) { throw err; } else { self.emit('error', err); }
- });
- }
- inherits(RdpClient, events.EventEmitter);
- /**
- * Connect RDP client
- * @param host {string} destination host
- * @param port {integer} destination port
- */
- RdpClient.prototype.connect = function (host, port) {
- log.debug('connect to ' + host + ':' + port);
- var self = this;
- this.bufferLayer.socket.connect(port, host, function () {
- // in client mode connection start from x224 layer
- self.x224.connect();
- });
- return this;
- };
- /**
- * Close RDP client
- */
- RdpClient.prototype.close = function () {
- if (this.connected) {
- this.global.close();
- }
- this.connected = false;
- return this;
- };
- /**
- * Send pointer event to server
- * @param x {integer} mouse x position
- * @param y {integer} mouse y position
- * @param button {integer} button number of mouse
- * @param isPressed {boolean} state of button
- */
- RdpClient.prototype.sendPointerEvent = function (x, y, button, isPressed) {
- if (!this.connected)
- return;
- var event = pdu.data.pointerEvent();
- if (isPressed) {
- event.obj.pointerFlags.value |= pdu.data.PointerFlag.PTRFLAGS_DOWN;
- }
- switch (button) {
- case 1:
- event.obj.pointerFlags.value |= pdu.data.PointerFlag.PTRFLAGS_BUTTON1;
- break;
- case 2:
- event.obj.pointerFlags.value |= pdu.data.PointerFlag.PTRFLAGS_BUTTON2;
- break;
- case 3:
- event.obj.pointerFlags.value |= pdu.data.PointerFlag.PTRFLAGS_BUTTON3
- break;
- default:
- event.obj.pointerFlags.value |= pdu.data.PointerFlag.PTRFLAGS_MOVE;
- }
- event.obj.xPos.value = x;
- event.obj.yPos.value = y;
- this.global.sendInputEvents([event]);
- };
- /**
- * send scancode event
- * @param code {integer}
- * @param isPressed {boolean}
- * @param extended {boolenan} extended keys
- */
- RdpClient.prototype.sendKeyEventScancode = function (code, isPressed, extended) {
- if (!this.connected)
- return;
- extended = extended || false;
- var event = pdu.data.scancodeKeyEvent();
- event.obj.keyCode.value = code;
- if (!isPressed) {
- event.obj.keyboardFlags.value |= pdu.data.KeyboardFlag.KBDFLAGS_RELEASE;
- }
- if (extended) {
- event.obj.keyboardFlags.value |= pdu.data.KeyboardFlag.KBDFLAGS_EXTENDED;
- }
- this.global.sendInputEvents([event]);
- };
- /**
- * Send key event as unicode
- * @param code {integer}
- * @param isPressed {boolean}
- */
- RdpClient.prototype.sendKeyEventUnicode = function (code, isPressed) {
- if (!this.connected)
- return;
- var event = pdu.data.unicodeKeyEvent();
- event.obj.unicode.value = code;
- if (!isPressed) {
- event.obj.keyboardFlags.value |= pdu.data.KeyboardFlag.KBDFLAGS_RELEASE;
- }
- this.global.sendInputEvents([event]);
- }
- /**
- * Wheel mouse event
- * @param x {integer} mouse x position
- * @param y {integer} mouse y position
- * @param step {integer} wheel step
- * @param isNegative {boolean}
- * @param isHorizontal {boolean}
- */
- RdpClient.prototype.sendWheelEvent = function (x, y, step, isNegative, isHorizontal) {
- if (!this.connected)
- return;
- var event = pdu.data.pointerEvent();
- if (isHorizontal) {
- event.obj.pointerFlags.value |= pdu.data.PointerFlag.PTRFLAGS_HWHEEL;
- }
- else {
- event.obj.pointerFlags.value |= pdu.data.PointerFlag.PTRFLAGS_WHEEL;
- }
- if (isNegative) {
- event.obj.pointerFlags.value |= pdu.data.PointerFlag.PTRFLAGS_WHEEL_NEGATIVE;
- }
- event.obj.pointerFlags.value |= (step & pdu.data.PointerFlag.WheelRotationMask)
- event.obj.xPos.value = x;
- event.obj.yPos.value = y;
- this.global.sendInputEvents([event]);
- }
- /**
- * Clipboard event
- * @param data {String} content for clipboard
- */
- RdpClient.prototype.setClipboardData = function (content) {
- this.cliprdr.setClipboardData(content);
- }
- function createClient(config) {
- return new RdpClient(config);
- };
- /**
- * RDP server side protocol
- * @param config {object} configuration
- * @param socket {net.Socket}
- */
- function RdpServer(config, socket) {
- if (!(config.key && config.cert)) {
- throw new error.FatalError('NODE_RDP_PROTOCOL_RDP_SERVER_CONFIG_MISSING', 'missing cryptographic tools')
- }
- this.connected = false;
- this.bufferLayer = new layer.BufferLayer(socket);
- this.tpkt = new TPKT(this.bufferLayer);
- this.x224 = new x224.Server(this.tpkt, config.key, config.cert);
- this.mcs = new t125.mcs.Server(this.x224);
- };
- inherits(RdpServer, events.EventEmitter);
- function createServer(config, next) {
- return net.createServer(function (socket) {
- next(new RdpServer(config, socket));
- });
- };
- /**
- * Module exports
- */
- module.exports = {
- createClient: createClient,
- createServer: createServer
- };
|