Added Web-RDP mouse cursor support.
Ylian Saint-Hilaire committed
Jun 23, 2022 at 22:10 UTC
6dbae08c4031461838e927cd1f4dd7d32be71799
6 files changed
+109
-17
apprelays.js
+9
-3
@@ -202,10 +202,16 @@ module.exports.CreateMstscRelay = function (parent, db, ws, req, args, domain) {
202
delete bitmap.data;
203
send(['rdp-bitmap', bitmap]); // Send the bitmap metadata seperately, without bitmap data.
204
}).on('clipboard', function (content) {
205
- // Clipboard data changed
206
- send(['rdp-clipboard', content]);
205
+ send(['rdp-clipboard', content]); // The clipboard data has changed
206
+ }).on('pointer', function (cursorId, cursorStr) {
207
+ if (cursorStr == null) { cursorStr = 'default'; }
208
+ if (obj.lastCursorStrSent != cursorStr) {
209
+ obj.lastCursorStrSent = cursorStr;
210
+ //console.log('pointer', cursorStr);
211
+ send(['rdp-pointer', cursorStr]); // The mouse pointer has changed
212
+ }
213
}).on('close', function () {
208
- send(['rdp-close']);
214
+ send(['rdp-close']); // This RDP session has closed
215
}).on('error', function (err) {
216
if (typeof err == 'string') { send(['rdp-error', err]); }
217
if ((typeof err == 'object') && (err.err) && (err.code)) { send(['rdp-error', err.err, err.code]); }
public/scripts/agent-rdp-0.0.1.js
+10
@@ -18,6 +18,10 @@ var CreateRDPDesktop = function (canvasid) {
18
obj.m.onClipboardChanged = null;
19
obj.onConsoleMessageChange = null;
20
21
+ var xMouseCursorActive = true;
22
+ var xMouseCursorCurrent = 'default';
23
+ obj.mouseCursorActive = function (x) { if (xMouseCursorActive == x) return; xMouseCursorActive = x; obj.CanvasId.style.cursor = ((x == true) ? xMouseCursorCurrent : 'default'); }
24
+
25
function mouseButtonMap(button) {
26
// Swap mouse buttons if needed
27
if (obj.m.SwapMouse === true) return [2, 0, 1, 0, 0][button];
@@ -75,6 +79,12 @@ var CreateRDPDesktop = function (canvasid) {
79
obj.render.update(bitmap);
80
break;
81
}
82
+ case 'rdp-pointer': {
83
+ var pointer = msg[1];
84
+ xMouseCursorCurrent = pointer;
85
+ if (xMouseCursorActive) { obj.CanvasId.style.cursor = pointer; }
86
+ break;
87
+ }
88
case 'rdp-close': {
89
obj.Stop();
90
break;
rdp/core/layer.js
+1
-1
@@ -22,7 +22,7 @@ var fs = require('fs');
22
var type = require('./type');
23
var log = require('./log');
24
var tls = require('tls');
25
-var crypto = require('crypto');
25
+//var crypto = require('crypto');
26
var events = require('events');
27
28
/**
rdp/protocol/pdu/data.js
+78
-9
@@ -186,7 +186,7 @@ var FastPathUpdateType = {
186
FASTPATH_UPDATETYPE_PTR_NULL : 0x5,
187
FASTPATH_UPDATETYPE_PTR_DEFAULT : 0x6,
188
FASTPATH_UPDATETYPE_PTR_POSITION : 0x8,
189
- FASTPATH_UPDATETYPE_COLOR : 0x9,
189
+ FASTPATH_UPDATETYPE_COLOR : 0x9, // Mouse cursor
190
FASTPATH_UPDATETYPE_CACHED : 0xA,
191
FASTPATH_UPDATETYPE_POINTER : 0xB
192
};
@@ -1075,7 +1075,7 @@ function clipPDU() {
1075
* @param opt {object} type option
1076
* @returns {type.Component}
1077
*/
1078
-function fastPathBitmapUpdateDataPDU (opt) {
1078
+function fastPathBitmapUpdateDataPDU(opt) {
1079
var self = {
1080
__FASTPATH_UPDATE_TYPE__ : FastPathUpdateType.FASTPATH_UPDATETYPE_BITMAP,
1081
header : new type.UInt16Le(FastPathUpdateType.FASTPATH_UPDATETYPE_BITMAP, { constant : true }),
@@ -1093,13 +1093,67 @@ function fastPathBitmapUpdateDataPDU (opt) {
1093
return new type.Component(self, opt);
1094
}
1095
1096
+// This is a table of cursorid to cursor name.
1097
+// Created by movering the mouse over this page: https://www.w3schools.com/csSref/tryit.asp?filename=trycss_cursor
1098
+const cursorIdTable = {
1099
+ // Normal style mouse cursor
1100
+ 903013897: 'alias',
1101
+ 370524792: 'all-scroll',
1102
+ 853046751: 'cell',
1103
+ 2101250798: 'col-resize',
1104
+ 703681364: 'copy',
1105
+ 992638936: 'crosshair',
1106
+ 1539083673: 'ew-resize',
1107
+ 1919796298: 'grab',
1108
+ 1010243511: 'grabbing',
1109
+ 1247283057: 'help',
1110
+ 1390892051: 'none',
1111
+ 885751489: 'not-allowed',
1112
+ 1732952247: 'row-resize',
1113
+ 747144997: 'url',
1114
+ 2018345610: 'zoom-in',
1115
+ 347367048: 'zoom-out',
1116
+ 1872942890: 'default',
1117
+ 1737852989: 'text',
1118
+ 1932827019: 'ns-resize',
1119
+ 1884471290: 'nesw-resize',
1120
+ 1204065391: 'nwse-resize',
1121
+ 2030531519: 'progress',
1122
+ 1050842114: 'pointer',
1123
+
1124
+ // Black style cursors
1125
+ 1258195498: 'default',
1126
+ 219484254: 'all-scroll',
1127
+ 399295089: 'text',
1128
+ 1912613597: 'wait',
1129
+ 864127801: 'ew-resize',
1130
+ 23245044: 'nesw-resize',
1131
+ 1966995494: 'not-allowed',
1132
+ 1873216615: 'help',
1133
+ 255126408: 'nesw-resize',
1134
+ 157191894: 'ns-resize',
1135
+ 1768446509: 'pointer',
1136
+ 1032011501: 'crosshair'
1137
+}
1138
+
1139
+function fastPathPointerUpdateDataPDU(opt, cursorId, cursorStr) {
1140
+ var self = {
1141
+ __FASTPATH_UPDATE_TYPE__: FastPathUpdateType.FASTPATH_UPDATETYPE_COLOR,
1142
+ header: new type.UInt16Le(FastPathUpdateType.FASTPATH_UPDATETYPE_COLOR, { constant: true }),
1143
+ cursorId: cursorId,
1144
+ cursorStr: cursorStr
1145
+ };
1146
+
1147
+ return new type.Component(self, opt);
1148
+}
1149
+
1150
/**
1151
* @see http://msdn.microsoft.com/en-us/library/cc240622.aspx
1152
* @param updateData {type.Component}
1153
* @param opt {object} type option
1154
* @returns {type.Component}
1155
*/
1102
-function fastPathUpdatePDU (updateData, opt) {
1156
+function fastPathUpdatePDU(updateData, opt) {
1157
var self = {
1158
updateHeader : new type.UInt8( function () {
1159
return self.updateData.obj.__FASTPATH_UPDATE_TYPE__;
@@ -1116,12 +1170,27 @@ function fastPathUpdatePDU (updateData, opt) {
1170
}) };
1171
1172
switch (self.updateHeader.value & 0xf) {
1119
- case FastPathUpdateType.FASTPATH_UPDATETYPE_BITMAP:
1120
- self.updateData = fastPathBitmapUpdateDataPDU(options).read(s);
1121
- break;
1122
- default:
1123
- self.updateData = new type.BinaryString(null, options).read(s);
1124
- log.debug('unknown fast path pdu type ' + (self.updateHeader.value & 0xf));
1173
+ case FastPathUpdateType.FASTPATH_UPDATETYPE_BITMAP: {
1174
+ self.updateData = fastPathBitmapUpdateDataPDU(options).read(s);
1175
+ break;
1176
+ }
1177
+ case FastPathUpdateType.FASTPATH_UPDATETYPE_COLOR: {
1178
+ var data = new type.BinaryString(null, options).read(s);
1179
+
1180
+ // Hash the data to get a cursor id.
1181
+ // This is a hack since the cursor bitmap is sent but we can't use that, we has hash the bitmap and use that as a hint as to what cursor we need to display
1182
+ const hasher = require('crypto').createHash('sha384');
1183
+ hasher.update(data.value);
1184
+ const cursorid = Math.abs(hasher.digest().readInt32BE(0));
1185
+ const cursorStr = cursorIdTable[cursorid];
1186
+ //if (cursorStr == null) { console.log('Unknown cursorId: ' + cursorid); }
1187
+ self.updateData = fastPathPointerUpdateDataPDU(options, cursorid, cursorStr);
1188
+ break;
1189
+ }
1190
+ default: {
1191
+ self.updateData = new type.BinaryString(null, options).read(s);
1192
+ log.debug('unknown fast path pdu type ' + (self.updateHeader.value & 0xf));
1193
+ }
1194
}
1195
})
1196
};
rdp/protocol/pdu/global.js
+9
-4
@@ -268,11 +268,16 @@ Client.prototype.recvServerFontMapPDU = function(s) {
268
*/
269
Client.prototype.recvFastPath = function (secFlag, s) {
270
while (s.availableLength() > 0) {
271
- var pdu = data.fastPathUpdatePDU().read(s);
271
+ var pdu = data.fastPathUpdatePDU().read(s);
272
switch (pdu.obj.updateHeader.value & 0xf) {
273
- case data.FastPathUpdateType.FASTPATH_UPDATETYPE_BITMAP:
274
- this.emit('bitmap', pdu.obj.updateData.obj.rectangles.obj);
275
- break;
273
+ case data.FastPathUpdateType.FASTPATH_UPDATETYPE_BITMAP: {
274
+ this.emit('bitmap', pdu.obj.updateData.obj.rectangles.obj);
275
+ break;
276
+ }
277
+ case data.FastPathUpdateType.FASTPATH_UPDATETYPE_COLOR: {
278
+ this.emit('pointer', pdu.obj.updateData.obj.cursorId, pdu.obj.updateData.obj.cursorStr);
279
+ break;
280
+ }
281
default:
282
}
283
}
rdp/protocol/rdp.js
+2
@@ -160,6 +160,8 @@ function RdpClient(config) {
160
}).on('close', function () {
161
self.connected = false;
162
self.emit('close');
163
+ }).on('pointer', function (cursorId, cursorStr) {
164
+ self.emit('pointer', cursorId, cursorStr);
165
}).on('bitmap', function (bitmaps) {
166
for (var bitmap in bitmaps) {
167
var bitmapData = bitmaps[bitmap].obj.bitmapDataStream.value;