@novnc/novnc 1.2.0 → 1.3.0-g0ef7582
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.txt +0 -6
- package/README.md +16 -6
- package/core/decoders/copyrect.js +5 -0
- package/core/decoders/hextile.js +57 -3
- package/core/decoders/jpeg.js +141 -0
- package/core/decoders/raw.js +12 -2
- package/core/decoders/tight.js +24 -8
- package/core/decoders/zrle.js +185 -0
- package/core/display.js +21 -151
- package/core/encodings.js +4 -0
- package/core/input/domkeytable.js +25 -21
- package/core/input/keyboard.js +22 -127
- package/core/input/util.js +18 -35
- package/core/input/vkeys.js +0 -1
- package/core/input/xtscancodes.js +5 -3
- package/core/ra2.js +567 -0
- package/core/rfb.js +487 -171
- package/core/util/browser.js +0 -17
- package/core/util/cursor.js +1 -11
- package/core/util/events.js +0 -4
- package/core/util/md5.js +79 -0
- package/core/websock.js +76 -17
- package/docs/API.md +107 -6
- package/docs/LIBRARY.md +3 -7
- package/lib/base64.js +24 -38
- package/lib/decoders/copyrect.js +6 -11
- package/lib/decoders/hextile.js +68 -44
- package/lib/decoders/jpeg.js +146 -0
- package/lib/decoders/raw.js +14 -21
- package/lib/decoders/rre.js +3 -17
- package/lib/decoders/tight.js +43 -93
- package/lib/decoders/tightpng.js +11 -33
- package/lib/decoders/zrle.js +185 -0
- package/lib/deflator.js +9 -26
- package/lib/des.js +22 -38
- package/lib/display.js +100 -315
- package/lib/encodings.js +7 -8
- package/lib/inflator.js +6 -22
- package/lib/input/domkeytable.js +240 -208
- package/lib/input/fixedkeys.js +10 -5
- package/lib/input/gesturehandler.js +84 -154
- package/lib/input/keyboard.js +87 -238
- package/lib/input/keysym.js +16 -272
- package/lib/input/keysymdef.js +7 -9
- package/lib/input/util.js +69 -156
- package/lib/input/vkeys.js +2 -7
- package/lib/input/xtscancodes.js +10 -171
- package/lib/ra2.js +1033 -0
- package/lib/rfb.js +947 -1149
- package/lib/util/browser.js +25 -52
- package/lib/util/cursor.js +25 -81
- package/lib/util/element.js +3 -5
- package/lib/util/events.js +26 -35
- package/lib/util/eventtarget.js +4 -16
- package/lib/util/int.js +2 -3
- package/lib/util/logging.js +3 -21
- package/lib/util/md5.js +83 -0
- package/lib/util/strings.js +3 -5
- package/lib/vendor/pako/lib/utils/common.js +10 -19
- package/lib/vendor/pako/lib/zlib/adler32.js +4 -8
- package/lib/vendor/pako/lib/zlib/constants.js +4 -7
- package/lib/vendor/pako/lib/zlib/crc32.js +6 -13
- package/lib/vendor/pako/lib/zlib/deflate.js +304 -708
- package/lib/vendor/pako/lib/zlib/gzheader.js +2 -14
- package/lib/vendor/pako/lib/zlib/inffast.js +61 -177
- package/lib/vendor/pako/lib/zlib/inflate.js +421 -909
- package/lib/vendor/pako/lib/zlib/inftrees.js +66 -172
- package/lib/vendor/pako/lib/zlib/messages.js +3 -13
- package/lib/vendor/pako/lib/zlib/trees.js +250 -592
- package/lib/vendor/pako/lib/zlib/zstream.js +3 -19
- package/lib/websock.js +119 -111
- package/package.json +2 -10
- package/core/util/polyfill.js +0 -61
- package/lib/util/polyfill.js +0 -72
- package/lib/vendor/promise.js +0 -255
package/core/rfb.js
CHANGED
|
@@ -25,7 +25,8 @@ import DES from "./des.js";
|
|
|
25
25
|
import KeyTable from "./input/keysym.js";
|
|
26
26
|
import XtScancode from "./input/xtscancodes.js";
|
|
27
27
|
import { encodings } from "./encodings.js";
|
|
28
|
-
import "./
|
|
28
|
+
import RSAAESAuthenticationState from "./ra2.js";
|
|
29
|
+
import { MD5 } from "./util/md5.js";
|
|
29
30
|
|
|
30
31
|
import RawDecoder from "./decoders/raw.js";
|
|
31
32
|
import CopyRectDecoder from "./decoders/copyrect.js";
|
|
@@ -33,6 +34,8 @@ import RREDecoder from "./decoders/rre.js";
|
|
|
33
34
|
import HextileDecoder from "./decoders/hextile.js";
|
|
34
35
|
import TightDecoder from "./decoders/tight.js";
|
|
35
36
|
import TightPNGDecoder from "./decoders/tightpng.js";
|
|
37
|
+
import ZRLEDecoder from "./decoders/zrle.js";
|
|
38
|
+
import JPEGDecoder from "./decoders/jpeg.js";
|
|
36
39
|
|
|
37
40
|
// How many seconds to wait for a disconnect to finish
|
|
38
41
|
const DISCONNECT_TIMEOUT = 3;
|
|
@@ -51,6 +54,21 @@ const GESTURE_SCRLSENS = 50;
|
|
|
51
54
|
const DOUBLE_TAP_TIMEOUT = 1000;
|
|
52
55
|
const DOUBLE_TAP_THRESHOLD = 50;
|
|
53
56
|
|
|
57
|
+
// Security types
|
|
58
|
+
const securityTypeNone = 1;
|
|
59
|
+
const securityTypeVNCAuth = 2;
|
|
60
|
+
const securityTypeRA2ne = 6;
|
|
61
|
+
const securityTypeTight = 16;
|
|
62
|
+
const securityTypeVeNCrypt = 19;
|
|
63
|
+
const securityTypeXVP = 22;
|
|
64
|
+
const securityTypeARD = 30;
|
|
65
|
+
|
|
66
|
+
// Special Tight security types
|
|
67
|
+
const securityTypeUnixLogon = 129;
|
|
68
|
+
|
|
69
|
+
// VeNCrypt security types
|
|
70
|
+
const securityTypePlain = 256;
|
|
71
|
+
|
|
54
72
|
// Extended clipboard pseudo-encoding formats
|
|
55
73
|
const extendedClipboardFormatText = 1;
|
|
56
74
|
/*eslint-disable no-unused-vars */
|
|
@@ -67,20 +85,31 @@ const extendedClipboardActionPeek = 1 << 26;
|
|
|
67
85
|
const extendedClipboardActionNotify = 1 << 27;
|
|
68
86
|
const extendedClipboardActionProvide = 1 << 28;
|
|
69
87
|
|
|
70
|
-
|
|
71
88
|
export default class RFB extends EventTargetMixin {
|
|
72
|
-
constructor(target,
|
|
89
|
+
constructor(target, urlOrChannel, options) {
|
|
73
90
|
if (!target) {
|
|
74
91
|
throw new Error("Must specify target");
|
|
75
92
|
}
|
|
76
|
-
if (!
|
|
77
|
-
throw new Error("Must specify URL");
|
|
93
|
+
if (!urlOrChannel) {
|
|
94
|
+
throw new Error("Must specify URL, WebSocket or RTCDataChannel");
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// We rely on modern APIs which might not be available in an
|
|
98
|
+
// insecure context
|
|
99
|
+
if (!window.isSecureContext) {
|
|
100
|
+
Log.Error("noVNC requires a secure context (TLS). Expect crashes!");
|
|
78
101
|
}
|
|
79
102
|
|
|
80
103
|
super();
|
|
81
104
|
|
|
82
105
|
this._target = target;
|
|
83
|
-
|
|
106
|
+
|
|
107
|
+
if (typeof urlOrChannel === "string") {
|
|
108
|
+
this._url = urlOrChannel;
|
|
109
|
+
} else {
|
|
110
|
+
this._url = null;
|
|
111
|
+
this._rawChannel = urlOrChannel;
|
|
112
|
+
}
|
|
84
113
|
|
|
85
114
|
// Connection details
|
|
86
115
|
options = options || {};
|
|
@@ -94,6 +123,7 @@ export default class RFB extends EventTargetMixin {
|
|
|
94
123
|
this._rfbInitState = '';
|
|
95
124
|
this._rfbAuthScheme = -1;
|
|
96
125
|
this._rfbCleanDisconnect = true;
|
|
126
|
+
this._rfbRSAAESAuthenticationState = null;
|
|
97
127
|
|
|
98
128
|
// Server capabilities
|
|
99
129
|
this._rfbVersion = 0;
|
|
@@ -130,6 +160,7 @@ export default class RFB extends EventTargetMixin {
|
|
|
130
160
|
this._flushing = false; // Display flushing state
|
|
131
161
|
this._keyboard = null; // Keyboard input handler object
|
|
132
162
|
this._gestures = null; // Gesture input handler object
|
|
163
|
+
this._resizeObserver = null; // Resize observer object
|
|
133
164
|
|
|
134
165
|
// Timers
|
|
135
166
|
this._disconnTimer = null; // disconnection timer
|
|
@@ -167,10 +198,12 @@ export default class RFB extends EventTargetMixin {
|
|
|
167
198
|
// Bound event handlers
|
|
168
199
|
this._eventHandlers = {
|
|
169
200
|
focusCanvas: this._focusCanvas.bind(this),
|
|
170
|
-
|
|
201
|
+
handleResize: this._handleResize.bind(this),
|
|
171
202
|
handleMouse: this._handleMouse.bind(this),
|
|
172
203
|
handleWheel: this._handleWheel.bind(this),
|
|
173
204
|
handleGesture: this._handleGesture.bind(this),
|
|
205
|
+
handleRSAAESCredentialsRequired: this._handleRSAAESCredentialsRequired.bind(this),
|
|
206
|
+
handleRSAAESServerVerification: this._handleRSAAESServerVerification.bind(this),
|
|
174
207
|
};
|
|
175
208
|
|
|
176
209
|
// main setup
|
|
@@ -187,8 +220,6 @@ export default class RFB extends EventTargetMixin {
|
|
|
187
220
|
this._canvas.style.margin = 'auto';
|
|
188
221
|
// Some browsers add an outline on focus
|
|
189
222
|
this._canvas.style.outline = 'none';
|
|
190
|
-
// IE miscalculates width without this :(
|
|
191
|
-
this._canvas.style.flexShrink = '0';
|
|
192
223
|
this._canvas.width = 0;
|
|
193
224
|
this._canvas.height = 0;
|
|
194
225
|
this._canvas.tabIndex = -1;
|
|
@@ -215,6 +246,8 @@ export default class RFB extends EventTargetMixin {
|
|
|
215
246
|
this._decoders[encodings.encodingHextile] = new HextileDecoder();
|
|
216
247
|
this._decoders[encodings.encodingTight] = new TightDecoder();
|
|
217
248
|
this._decoders[encodings.encodingTightPNG] = new TightPNGDecoder();
|
|
249
|
+
this._decoders[encodings.encodingZRLE] = new ZRLEDecoder();
|
|
250
|
+
this._decoders[encodings.encodingJPEG] = new JPEGDecoder();
|
|
218
251
|
|
|
219
252
|
// NB: nothing that needs explicit teardown should be done
|
|
220
253
|
// before this point, since this can throw an exception
|
|
@@ -232,58 +265,17 @@ export default class RFB extends EventTargetMixin {
|
|
|
232
265
|
this._gestures = new GestureHandler();
|
|
233
266
|
|
|
234
267
|
this._sock = new Websock();
|
|
235
|
-
this._sock.on('
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
this._sock.on('
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
} else {
|
|
244
|
-
this._fail("Unexpected server connection while " +
|
|
245
|
-
this._rfbConnectionState);
|
|
246
|
-
}
|
|
247
|
-
});
|
|
248
|
-
this._sock.on('close', (e) => {
|
|
249
|
-
Log.Debug("WebSocket on-close event");
|
|
250
|
-
let msg = "";
|
|
251
|
-
if (e.code) {
|
|
252
|
-
msg = "(code: " + e.code;
|
|
253
|
-
if (e.reason) {
|
|
254
|
-
msg += ", reason: " + e.reason;
|
|
255
|
-
}
|
|
256
|
-
msg += ")";
|
|
257
|
-
}
|
|
258
|
-
switch (this._rfbConnectionState) {
|
|
259
|
-
case 'connecting':
|
|
260
|
-
this._fail("Connection closed " + msg);
|
|
261
|
-
break;
|
|
262
|
-
case 'connected':
|
|
263
|
-
// Handle disconnects that were initiated server-side
|
|
264
|
-
this._updateConnectionState('disconnecting');
|
|
265
|
-
this._updateConnectionState('disconnected');
|
|
266
|
-
break;
|
|
267
|
-
case 'disconnecting':
|
|
268
|
-
// Normal disconnection path
|
|
269
|
-
this._updateConnectionState('disconnected');
|
|
270
|
-
break;
|
|
271
|
-
case 'disconnected':
|
|
272
|
-
this._fail("Unexpected server disconnect " +
|
|
273
|
-
"when already disconnected " + msg);
|
|
274
|
-
break;
|
|
275
|
-
default:
|
|
276
|
-
this._fail("Unexpected server disconnect before connecting " +
|
|
277
|
-
msg);
|
|
278
|
-
break;
|
|
279
|
-
}
|
|
280
|
-
this._sock.off('close');
|
|
281
|
-
});
|
|
282
|
-
this._sock.on('error', e => Log.Warn("WebSocket on-error event"));
|
|
268
|
+
this._sock.on('open', this._socketOpen.bind(this));
|
|
269
|
+
this._sock.on('close', this._socketClose.bind(this));
|
|
270
|
+
this._sock.on('message', this._handleMessage.bind(this));
|
|
271
|
+
this._sock.on('error', this._socketError.bind(this));
|
|
272
|
+
|
|
273
|
+
this._expectedClientWidth = null;
|
|
274
|
+
this._expectedClientHeight = null;
|
|
275
|
+
this._resizeObserver = new ResizeObserver(this._eventHandlers.handleResize);
|
|
283
276
|
|
|
284
|
-
//
|
|
285
|
-
|
|
286
|
-
setTimeout(this._updateConnectionState.bind(this, 'connecting'));
|
|
277
|
+
// All prepared, kick off the connection
|
|
278
|
+
this._updateConnectionState('connecting');
|
|
287
279
|
|
|
288
280
|
Log.Debug("<< RFB.constructor");
|
|
289
281
|
|
|
@@ -412,11 +404,20 @@ export default class RFB extends EventTargetMixin {
|
|
|
412
404
|
this._sock.off('error');
|
|
413
405
|
this._sock.off('message');
|
|
414
406
|
this._sock.off('open');
|
|
407
|
+
if (this._rfbRSAAESAuthenticationState !== null) {
|
|
408
|
+
this._rfbRSAAESAuthenticationState.disconnect();
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
approveServer() {
|
|
413
|
+
if (this._rfbRSAAESAuthenticationState !== null) {
|
|
414
|
+
this._rfbRSAAESAuthenticationState.approveServer();
|
|
415
|
+
}
|
|
415
416
|
}
|
|
416
417
|
|
|
417
418
|
sendCredentials(creds) {
|
|
418
419
|
this._rfbCredentials = creds;
|
|
419
|
-
|
|
420
|
+
this._resumeAuthentication();
|
|
420
421
|
}
|
|
421
422
|
|
|
422
423
|
sendCtrlAltDel() {
|
|
@@ -472,8 +473,8 @@ export default class RFB extends EventTargetMixin {
|
|
|
472
473
|
}
|
|
473
474
|
}
|
|
474
475
|
|
|
475
|
-
focus() {
|
|
476
|
-
this._canvas.focus();
|
|
476
|
+
focus(options) {
|
|
477
|
+
this._canvas.focus(options);
|
|
477
478
|
}
|
|
478
479
|
|
|
479
480
|
blur() {
|
|
@@ -489,31 +490,66 @@ export default class RFB extends EventTargetMixin {
|
|
|
489
490
|
this._clipboardText = text;
|
|
490
491
|
RFB.messages.extendedClipboardNotify(this._sock, [extendedClipboardFormatText]);
|
|
491
492
|
} else {
|
|
492
|
-
let
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
493
|
+
let length, i;
|
|
494
|
+
let data;
|
|
495
|
+
|
|
496
|
+
length = 0;
|
|
497
|
+
// eslint-disable-next-line no-unused-vars
|
|
498
|
+
for (let codePoint of text) {
|
|
499
|
+
length++;
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
data = new Uint8Array(length);
|
|
503
|
+
|
|
504
|
+
i = 0;
|
|
505
|
+
for (let codePoint of text) {
|
|
506
|
+
let code = codePoint.codePointAt(0);
|
|
507
|
+
|
|
508
|
+
/* Only ISO 8859-1 is supported */
|
|
509
|
+
if (code > 0xff) {
|
|
510
|
+
code = 0x3f; // '?'
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
data[i++] = code;
|
|
496
514
|
}
|
|
497
515
|
|
|
498
516
|
RFB.messages.clientCutText(this._sock, data);
|
|
499
517
|
}
|
|
500
518
|
}
|
|
501
519
|
|
|
520
|
+
getImageData() {
|
|
521
|
+
return this._display.getImageData();
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
toDataURL(type, encoderOptions) {
|
|
525
|
+
return this._display.toDataURL(type, encoderOptions);
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
toBlob(callback, type, quality) {
|
|
529
|
+
return this._display.toBlob(callback, type, quality);
|
|
530
|
+
}
|
|
531
|
+
|
|
502
532
|
// ===== PRIVATE METHODS =====
|
|
503
533
|
|
|
504
534
|
_connect() {
|
|
505
535
|
Log.Debug(">> RFB.connect");
|
|
506
536
|
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
try {
|
|
510
|
-
// WebSocket.onopen transitions to the RFB init states
|
|
537
|
+
if (this._url) {
|
|
538
|
+
Log.Info(`connecting to ${this._url}`);
|
|
511
539
|
this._sock.open(this._url, this._wsProtocols);
|
|
512
|
-
}
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
540
|
+
} else {
|
|
541
|
+
Log.Info(`attaching ${this._rawChannel} to Websock`);
|
|
542
|
+
this._sock.attach(this._rawChannel);
|
|
543
|
+
|
|
544
|
+
if (this._sock.readyState === 'closed') {
|
|
545
|
+
throw Error("Cannot use already closed WebSocket/RTCDataChannel");
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
if (this._sock.readyState === 'open') {
|
|
549
|
+
// FIXME: _socketOpen() can in theory call _fail(), which
|
|
550
|
+
// isn't allowed this early, but I'm not sure that can
|
|
551
|
+
// happen without a bug messing up our state variables
|
|
552
|
+
this._socketOpen();
|
|
517
553
|
}
|
|
518
554
|
}
|
|
519
555
|
|
|
@@ -525,9 +561,8 @@ export default class RFB extends EventTargetMixin {
|
|
|
525
561
|
this._cursor.attach(this._canvas);
|
|
526
562
|
this._refreshCursor();
|
|
527
563
|
|
|
528
|
-
// Monitor size changes of the screen
|
|
529
|
-
|
|
530
|
-
window.addEventListener('resize', this._eventHandlers.windowResize);
|
|
564
|
+
// Monitor size changes of the screen element
|
|
565
|
+
this._resizeObserver.observe(this._screen);
|
|
531
566
|
|
|
532
567
|
// Always grab focus on some kind of click event
|
|
533
568
|
this._canvas.addEventListener("mousedown", this._eventHandlers.focusCanvas);
|
|
@@ -568,7 +603,7 @@ export default class RFB extends EventTargetMixin {
|
|
|
568
603
|
this._canvas.removeEventListener('contextmenu', this._eventHandlers.handleMouse);
|
|
569
604
|
this._canvas.removeEventListener("mousedown", this._eventHandlers.focusCanvas);
|
|
570
605
|
this._canvas.removeEventListener("touchstart", this._eventHandlers.focusCanvas);
|
|
571
|
-
|
|
606
|
+
this._resizeObserver.disconnect();
|
|
572
607
|
this._keyboard.ungrab();
|
|
573
608
|
this._gestures.detach();
|
|
574
609
|
this._sock.close();
|
|
@@ -587,12 +622,64 @@ export default class RFB extends EventTargetMixin {
|
|
|
587
622
|
Log.Debug("<< RFB.disconnect");
|
|
588
623
|
}
|
|
589
624
|
|
|
625
|
+
_socketOpen() {
|
|
626
|
+
if ((this._rfbConnectionState === 'connecting') &&
|
|
627
|
+
(this._rfbInitState === '')) {
|
|
628
|
+
this._rfbInitState = 'ProtocolVersion';
|
|
629
|
+
Log.Debug("Starting VNC handshake");
|
|
630
|
+
} else {
|
|
631
|
+
this._fail("Unexpected server connection while " +
|
|
632
|
+
this._rfbConnectionState);
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
_socketClose(e) {
|
|
637
|
+
Log.Debug("WebSocket on-close event");
|
|
638
|
+
let msg = "";
|
|
639
|
+
if (e.code) {
|
|
640
|
+
msg = "(code: " + e.code;
|
|
641
|
+
if (e.reason) {
|
|
642
|
+
msg += ", reason: " + e.reason;
|
|
643
|
+
}
|
|
644
|
+
msg += ")";
|
|
645
|
+
}
|
|
646
|
+
switch (this._rfbConnectionState) {
|
|
647
|
+
case 'connecting':
|
|
648
|
+
this._fail("Connection closed " + msg);
|
|
649
|
+
break;
|
|
650
|
+
case 'connected':
|
|
651
|
+
// Handle disconnects that were initiated server-side
|
|
652
|
+
this._updateConnectionState('disconnecting');
|
|
653
|
+
this._updateConnectionState('disconnected');
|
|
654
|
+
break;
|
|
655
|
+
case 'disconnecting':
|
|
656
|
+
// Normal disconnection path
|
|
657
|
+
this._updateConnectionState('disconnected');
|
|
658
|
+
break;
|
|
659
|
+
case 'disconnected':
|
|
660
|
+
this._fail("Unexpected server disconnect " +
|
|
661
|
+
"when already disconnected " + msg);
|
|
662
|
+
break;
|
|
663
|
+
default:
|
|
664
|
+
this._fail("Unexpected server disconnect before connecting " +
|
|
665
|
+
msg);
|
|
666
|
+
break;
|
|
667
|
+
}
|
|
668
|
+
this._sock.off('close');
|
|
669
|
+
// Delete reference to raw channel to allow cleanup.
|
|
670
|
+
this._rawChannel = null;
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
_socketError(e) {
|
|
674
|
+
Log.Warn("WebSocket on-error event");
|
|
675
|
+
}
|
|
676
|
+
|
|
590
677
|
_focusCanvas(event) {
|
|
591
678
|
if (!this.focusOnClick) {
|
|
592
679
|
return;
|
|
593
680
|
}
|
|
594
681
|
|
|
595
|
-
this.focus();
|
|
682
|
+
this.focus({ preventScroll: true });
|
|
596
683
|
}
|
|
597
684
|
|
|
598
685
|
_setDesktopName(name) {
|
|
@@ -602,7 +689,26 @@ export default class RFB extends EventTargetMixin {
|
|
|
602
689
|
{ detail: { name: this._fbName } }));
|
|
603
690
|
}
|
|
604
691
|
|
|
605
|
-
|
|
692
|
+
_saveExpectedClientSize() {
|
|
693
|
+
this._expectedClientWidth = this._screen.clientWidth;
|
|
694
|
+
this._expectedClientHeight = this._screen.clientHeight;
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
_currentClientSize() {
|
|
698
|
+
return [this._screen.clientWidth, this._screen.clientHeight];
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
_clientHasExpectedSize() {
|
|
702
|
+
const [currentWidth, currentHeight] = this._currentClientSize();
|
|
703
|
+
return currentWidth == this._expectedClientWidth &&
|
|
704
|
+
currentHeight == this._expectedClientHeight;
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
_handleResize() {
|
|
708
|
+
// Don't change anything if the client size is already as expected
|
|
709
|
+
if (this._clientHasExpectedSize()) {
|
|
710
|
+
return;
|
|
711
|
+
}
|
|
606
712
|
// If the window resized then our screen element might have
|
|
607
713
|
// as well. Update the viewport dimensions.
|
|
608
714
|
window.requestAnimationFrame(() => {
|
|
@@ -643,6 +749,12 @@ export default class RFB extends EventTargetMixin {
|
|
|
643
749
|
this._display.viewportChangeSize(size.w, size.h);
|
|
644
750
|
this._fixScrollbars();
|
|
645
751
|
}
|
|
752
|
+
|
|
753
|
+
// When changing clipping we might show or hide scrollbars.
|
|
754
|
+
// This causes the expected client dimensions to change.
|
|
755
|
+
if (curClip !== newClip) {
|
|
756
|
+
this._saveExpectedClientSize();
|
|
757
|
+
}
|
|
646
758
|
}
|
|
647
759
|
|
|
648
760
|
_updateScale() {
|
|
@@ -667,6 +779,7 @@ export default class RFB extends EventTargetMixin {
|
|
|
667
779
|
}
|
|
668
780
|
|
|
669
781
|
const size = this._screenSize();
|
|
782
|
+
|
|
670
783
|
RFB.messages.setDesktopSize(this._sock,
|
|
671
784
|
Math.floor(size.w), Math.floor(size.h),
|
|
672
785
|
this._screenID, this._screenFlags);
|
|
@@ -682,12 +795,13 @@ export default class RFB extends EventTargetMixin {
|
|
|
682
795
|
}
|
|
683
796
|
|
|
684
797
|
_fixScrollbars() {
|
|
685
|
-
// This is a hack because
|
|
686
|
-
// for when scrollbars are needed.
|
|
687
|
-
//
|
|
798
|
+
// This is a hack because Safari on macOS screws up the calculation
|
|
799
|
+
// for when scrollbars are needed. We get scrollbars when making the
|
|
800
|
+
// browser smaller, despite remote resize being enabled. So to fix it
|
|
801
|
+
// we temporarily toggle them off and on.
|
|
688
802
|
const orig = this._screen.style.overflow;
|
|
689
803
|
this._screen.style.overflow = 'hidden';
|
|
690
|
-
// Force
|
|
804
|
+
// Force Safari to recalculate the layout by asking for
|
|
691
805
|
// an element's dimensions
|
|
692
806
|
this._screen.getBoundingClientRect();
|
|
693
807
|
this._screen.style.overflow = orig;
|
|
@@ -852,8 +966,15 @@ export default class RFB extends EventTargetMixin {
|
|
|
852
966
|
}
|
|
853
967
|
}
|
|
854
968
|
break;
|
|
969
|
+
case 'connecting':
|
|
970
|
+
while (this._rfbConnectionState === 'connecting') {
|
|
971
|
+
if (!this._initMsg()) {
|
|
972
|
+
break;
|
|
973
|
+
}
|
|
974
|
+
}
|
|
975
|
+
break;
|
|
855
976
|
default:
|
|
856
|
-
|
|
977
|
+
Log.Error("Got data while in an invalid state");
|
|
857
978
|
break;
|
|
858
979
|
}
|
|
859
980
|
}
|
|
@@ -1225,13 +1346,13 @@ export default class RFB extends EventTargetMixin {
|
|
|
1225
1346
|
break;
|
|
1226
1347
|
case "003.003":
|
|
1227
1348
|
case "003.006": // UltraVNC
|
|
1228
|
-
case "003.889": // Apple Remote Desktop
|
|
1229
1349
|
this._rfbVersion = 3.3;
|
|
1230
1350
|
break;
|
|
1231
1351
|
case "003.007":
|
|
1232
1352
|
this._rfbVersion = 3.7;
|
|
1233
1353
|
break;
|
|
1234
1354
|
case "003.008":
|
|
1355
|
+
case "003.889": // Apple Remote Desktop
|
|
1235
1356
|
case "004.000": // Intel AMT KVM
|
|
1236
1357
|
case "004.001": // RealVNC 4.6
|
|
1237
1358
|
case "005.000": // RealVNC 5.3
|
|
@@ -1262,18 +1383,22 @@ export default class RFB extends EventTargetMixin {
|
|
|
1262
1383
|
this._rfbInitState = 'Security';
|
|
1263
1384
|
}
|
|
1264
1385
|
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1386
|
+
_isSupportedSecurityType(type) {
|
|
1387
|
+
const clientTypes = [
|
|
1388
|
+
securityTypeNone,
|
|
1389
|
+
securityTypeVNCAuth,
|
|
1390
|
+
securityTypeRA2ne,
|
|
1391
|
+
securityTypeTight,
|
|
1392
|
+
securityTypeVeNCrypt,
|
|
1393
|
+
securityTypeXVP,
|
|
1394
|
+
securityTypeARD,
|
|
1395
|
+
securityTypePlain,
|
|
1396
|
+
];
|
|
1397
|
+
|
|
1398
|
+
return clientTypes.includes(type);
|
|
1399
|
+
}
|
|
1276
1400
|
|
|
1401
|
+
_negotiateSecurity() {
|
|
1277
1402
|
if (this._rfbVersion >= 3.7) {
|
|
1278
1403
|
// Server sends supported list, client decides
|
|
1279
1404
|
const numTypes = this._sock.rQshift8();
|
|
@@ -1283,24 +1408,23 @@ export default class RFB extends EventTargetMixin {
|
|
|
1283
1408
|
this._rfbInitState = "SecurityReason";
|
|
1284
1409
|
this._securityContext = "no security types";
|
|
1285
1410
|
this._securityStatus = 1;
|
|
1286
|
-
return
|
|
1411
|
+
return true;
|
|
1287
1412
|
}
|
|
1288
1413
|
|
|
1289
1414
|
const types = this._sock.rQshiftBytes(numTypes);
|
|
1290
1415
|
Log.Debug("Server security types: " + types);
|
|
1291
1416
|
|
|
1292
|
-
// Look for
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
this.
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
} else {
|
|
1417
|
+
// Look for a matching security type in the order that the
|
|
1418
|
+
// server prefers
|
|
1419
|
+
this._rfbAuthScheme = -1;
|
|
1420
|
+
for (let type of types) {
|
|
1421
|
+
if (this._isSupportedSecurityType(type)) {
|
|
1422
|
+
this._rfbAuthScheme = type;
|
|
1423
|
+
break;
|
|
1424
|
+
}
|
|
1425
|
+
}
|
|
1426
|
+
|
|
1427
|
+
if (this._rfbAuthScheme === -1) {
|
|
1304
1428
|
return this._fail("Unsupported security types (types: " + types + ")");
|
|
1305
1429
|
}
|
|
1306
1430
|
|
|
@@ -1314,14 +1438,14 @@ export default class RFB extends EventTargetMixin {
|
|
|
1314
1438
|
this._rfbInitState = "SecurityReason";
|
|
1315
1439
|
this._securityContext = "authentication scheme";
|
|
1316
1440
|
this._securityStatus = 1;
|
|
1317
|
-
return
|
|
1441
|
+
return true;
|
|
1318
1442
|
}
|
|
1319
1443
|
}
|
|
1320
1444
|
|
|
1321
1445
|
this._rfbInitState = 'Authentication';
|
|
1322
1446
|
Log.Debug('Authenticating using scheme: ' + this._rfbAuthScheme);
|
|
1323
1447
|
|
|
1324
|
-
return
|
|
1448
|
+
return true;
|
|
1325
1449
|
}
|
|
1326
1450
|
|
|
1327
1451
|
_handleSecurityReason() {
|
|
@@ -1371,7 +1495,7 @@ export default class RFB extends EventTargetMixin {
|
|
|
1371
1495
|
this._rfbCredentials.username +
|
|
1372
1496
|
this._rfbCredentials.target;
|
|
1373
1497
|
this._sock.sendString(xvpAuthStr);
|
|
1374
|
-
this._rfbAuthScheme =
|
|
1498
|
+
this._rfbAuthScheme = securityTypeVNCAuth;
|
|
1375
1499
|
return this._negotiateAuthentication();
|
|
1376
1500
|
}
|
|
1377
1501
|
|
|
@@ -1429,40 +1553,66 @@ export default class RFB extends EventTargetMixin {
|
|
|
1429
1553
|
subtypes.push(this._sock.rQshift32());
|
|
1430
1554
|
}
|
|
1431
1555
|
|
|
1432
|
-
//
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
}
|
|
1556
|
+
// Look for a matching security type in the order that the
|
|
1557
|
+
// server prefers
|
|
1558
|
+
this._rfbAuthScheme = -1;
|
|
1559
|
+
for (let type of subtypes) {
|
|
1560
|
+
// Avoid getting in to a loop
|
|
1561
|
+
if (type === securityTypeVeNCrypt) {
|
|
1562
|
+
continue;
|
|
1563
|
+
}
|
|
1441
1564
|
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
this.dispatchEvent(new CustomEvent(
|
|
1447
|
-
"credentialsrequired",
|
|
1448
|
-
{ detail: { types: ["username", "password"] } }));
|
|
1449
|
-
return false;
|
|
1565
|
+
if (this._isSupportedSecurityType(type)) {
|
|
1566
|
+
this._rfbAuthScheme = type;
|
|
1567
|
+
break;
|
|
1568
|
+
}
|
|
1450
1569
|
}
|
|
1451
1570
|
|
|
1452
|
-
|
|
1453
|
-
|
|
1571
|
+
if (this._rfbAuthScheme === -1) {
|
|
1572
|
+
return this._fail("Unsupported security types (types: " + subtypes + ")");
|
|
1573
|
+
}
|
|
1454
1574
|
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
this._sock.sendString(pass);
|
|
1575
|
+
this._sock.send([this._rfbAuthScheme >> 24,
|
|
1576
|
+
this._rfbAuthScheme >> 16,
|
|
1577
|
+
this._rfbAuthScheme >> 8,
|
|
1578
|
+
this._rfbAuthScheme]);
|
|
1460
1579
|
|
|
1461
|
-
this.
|
|
1580
|
+
this._rfbVeNCryptState == 4;
|
|
1462
1581
|
return true;
|
|
1463
1582
|
}
|
|
1464
1583
|
}
|
|
1465
1584
|
|
|
1585
|
+
_negotiatePlainAuth() {
|
|
1586
|
+
if (this._rfbCredentials.username === undefined ||
|
|
1587
|
+
this._rfbCredentials.password === undefined) {
|
|
1588
|
+
this.dispatchEvent(new CustomEvent(
|
|
1589
|
+
"credentialsrequired",
|
|
1590
|
+
{ detail: { types: ["username", "password"] } }));
|
|
1591
|
+
return false;
|
|
1592
|
+
}
|
|
1593
|
+
|
|
1594
|
+
const user = encodeUTF8(this._rfbCredentials.username);
|
|
1595
|
+
const pass = encodeUTF8(this._rfbCredentials.password);
|
|
1596
|
+
|
|
1597
|
+
this._sock.send([
|
|
1598
|
+
(user.length >> 24) & 0xFF,
|
|
1599
|
+
(user.length >> 16) & 0xFF,
|
|
1600
|
+
(user.length >> 8) & 0xFF,
|
|
1601
|
+
user.length & 0xFF
|
|
1602
|
+
]);
|
|
1603
|
+
this._sock.send([
|
|
1604
|
+
(pass.length >> 24) & 0xFF,
|
|
1605
|
+
(pass.length >> 16) & 0xFF,
|
|
1606
|
+
(pass.length >> 8) & 0xFF,
|
|
1607
|
+
pass.length & 0xFF
|
|
1608
|
+
]);
|
|
1609
|
+
this._sock.sendString(user);
|
|
1610
|
+
this._sock.sendString(pass);
|
|
1611
|
+
|
|
1612
|
+
this._rfbInitState = "SecurityResult";
|
|
1613
|
+
return true;
|
|
1614
|
+
}
|
|
1615
|
+
|
|
1466
1616
|
_negotiateStdVNCAuth() {
|
|
1467
1617
|
if (this._sock.rQwait("auth challenge", 16)) { return false; }
|
|
1468
1618
|
|
|
@@ -1481,6 +1631,117 @@ export default class RFB extends EventTargetMixin {
|
|
|
1481
1631
|
return true;
|
|
1482
1632
|
}
|
|
1483
1633
|
|
|
1634
|
+
_negotiateARDAuth() {
|
|
1635
|
+
|
|
1636
|
+
if (this._rfbCredentials.username === undefined ||
|
|
1637
|
+
this._rfbCredentials.password === undefined) {
|
|
1638
|
+
this.dispatchEvent(new CustomEvent(
|
|
1639
|
+
"credentialsrequired",
|
|
1640
|
+
{ detail: { types: ["username", "password"] } }));
|
|
1641
|
+
return false;
|
|
1642
|
+
}
|
|
1643
|
+
|
|
1644
|
+
if (this._rfbCredentials.ardPublicKey != undefined &&
|
|
1645
|
+
this._rfbCredentials.ardCredentials != undefined) {
|
|
1646
|
+
// if the async web crypto is done return the results
|
|
1647
|
+
this._sock.send(this._rfbCredentials.ardCredentials);
|
|
1648
|
+
this._sock.send(this._rfbCredentials.ardPublicKey);
|
|
1649
|
+
this._rfbCredentials.ardCredentials = null;
|
|
1650
|
+
this._rfbCredentials.ardPublicKey = null;
|
|
1651
|
+
this._rfbInitState = "SecurityResult";
|
|
1652
|
+
return true;
|
|
1653
|
+
}
|
|
1654
|
+
|
|
1655
|
+
if (this._sock.rQwait("read ard", 4)) { return false; }
|
|
1656
|
+
|
|
1657
|
+
let generator = this._sock.rQshiftBytes(2); // DH base generator value
|
|
1658
|
+
|
|
1659
|
+
let keyLength = this._sock.rQshift16();
|
|
1660
|
+
|
|
1661
|
+
if (this._sock.rQwait("read ard keylength", keyLength*2, 4)) { return false; }
|
|
1662
|
+
|
|
1663
|
+
// read the server values
|
|
1664
|
+
let prime = this._sock.rQshiftBytes(keyLength); // predetermined prime modulus
|
|
1665
|
+
let serverPublicKey = this._sock.rQshiftBytes(keyLength); // other party's public key
|
|
1666
|
+
|
|
1667
|
+
let clientPrivateKey = window.crypto.getRandomValues(new Uint8Array(keyLength));
|
|
1668
|
+
let padding = Array.from(window.crypto.getRandomValues(new Uint8Array(64)), byte => String.fromCharCode(65+byte%26)).join('');
|
|
1669
|
+
|
|
1670
|
+
this._negotiateARDAuthAsync(generator, keyLength, prime, serverPublicKey, clientPrivateKey, padding);
|
|
1671
|
+
|
|
1672
|
+
return false;
|
|
1673
|
+
}
|
|
1674
|
+
|
|
1675
|
+
_modPow(base, exponent, modulus) {
|
|
1676
|
+
|
|
1677
|
+
let baseHex = "0x"+Array.from(base, byte => ('0' + (byte & 0xFF).toString(16)).slice(-2)).join('');
|
|
1678
|
+
let exponentHex = "0x"+Array.from(exponent, byte => ('0' + (byte & 0xFF).toString(16)).slice(-2)).join('');
|
|
1679
|
+
let modulusHex = "0x"+Array.from(modulus, byte => ('0' + (byte & 0xFF).toString(16)).slice(-2)).join('');
|
|
1680
|
+
|
|
1681
|
+
let b = BigInt(baseHex);
|
|
1682
|
+
let e = BigInt(exponentHex);
|
|
1683
|
+
let m = BigInt(modulusHex);
|
|
1684
|
+
let r = 1n;
|
|
1685
|
+
b = b % m;
|
|
1686
|
+
while (e > 0) {
|
|
1687
|
+
if (e % 2n === 1n) {
|
|
1688
|
+
r = (r * b) % m;
|
|
1689
|
+
}
|
|
1690
|
+
e = e / 2n;
|
|
1691
|
+
b = (b * b) % m;
|
|
1692
|
+
}
|
|
1693
|
+
let hexResult = r.toString(16);
|
|
1694
|
+
|
|
1695
|
+
while (hexResult.length/2<exponent.length || (hexResult.length%2 != 0)) {
|
|
1696
|
+
hexResult = "0"+hexResult;
|
|
1697
|
+
}
|
|
1698
|
+
|
|
1699
|
+
let bytesResult = [];
|
|
1700
|
+
for (let c = 0; c < hexResult.length; c += 2) {
|
|
1701
|
+
bytesResult.push(parseInt(hexResult.substr(c, 2), 16));
|
|
1702
|
+
}
|
|
1703
|
+
return bytesResult;
|
|
1704
|
+
}
|
|
1705
|
+
|
|
1706
|
+
async _aesEcbEncrypt(string, key) {
|
|
1707
|
+
// perform AES-ECB blocks
|
|
1708
|
+
let keyString = Array.from(key, byte => String.fromCharCode(byte)).join('');
|
|
1709
|
+
let aesKey = await window.crypto.subtle.importKey("raw", MD5(keyString), {name: "AES-CBC"}, false, ["encrypt"]);
|
|
1710
|
+
let data = new Uint8Array(string.length);
|
|
1711
|
+
for (let i = 0; i < string.length; ++i) {
|
|
1712
|
+
data[i] = string.charCodeAt(i);
|
|
1713
|
+
}
|
|
1714
|
+
let encrypted = new Uint8Array(data.length);
|
|
1715
|
+
for (let i=0;i<data.length;i+=16) {
|
|
1716
|
+
let block = data.slice(i, i+16);
|
|
1717
|
+
let encryptedBlock = await window.crypto.subtle.encrypt({name: "AES-CBC", iv: block},
|
|
1718
|
+
aesKey, new Uint8Array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0])
|
|
1719
|
+
);
|
|
1720
|
+
encrypted.set((new Uint8Array(encryptedBlock)).slice(0, 16), i);
|
|
1721
|
+
}
|
|
1722
|
+
return encrypted;
|
|
1723
|
+
}
|
|
1724
|
+
|
|
1725
|
+
async _negotiateARDAuthAsync(generator, keyLength, prime, serverPublicKey, clientPrivateKey, padding) {
|
|
1726
|
+
// calculate the DH keys
|
|
1727
|
+
let clientPublicKey = this._modPow(generator, clientPrivateKey, prime);
|
|
1728
|
+
let sharedKey = this._modPow(serverPublicKey, clientPrivateKey, prime);
|
|
1729
|
+
|
|
1730
|
+
let username = encodeUTF8(this._rfbCredentials.username).substring(0, 63);
|
|
1731
|
+
let password = encodeUTF8(this._rfbCredentials.password).substring(0, 63);
|
|
1732
|
+
|
|
1733
|
+
let paddedUsername = username + '\0' + padding.substring(0, 63);
|
|
1734
|
+
let paddedPassword = password + '\0' + padding.substring(0, 63);
|
|
1735
|
+
let credentials = paddedUsername.substring(0, 64) + paddedPassword.substring(0, 64);
|
|
1736
|
+
|
|
1737
|
+
let encrypted = await this._aesEcbEncrypt(credentials, sharedKey);
|
|
1738
|
+
|
|
1739
|
+
this._rfbCredentials.ardCredentials = encrypted;
|
|
1740
|
+
this._rfbCredentials.ardPublicKey = clientPublicKey;
|
|
1741
|
+
|
|
1742
|
+
this._resumeAuthentication();
|
|
1743
|
+
}
|
|
1744
|
+
|
|
1484
1745
|
_negotiateTightUnixAuth() {
|
|
1485
1746
|
if (this._rfbCredentials.username === undefined ||
|
|
1486
1747
|
this._rfbCredentials.password === undefined) {
|
|
@@ -1588,12 +1849,12 @@ export default class RFB extends EventTargetMixin {
|
|
|
1588
1849
|
case 'STDVNOAUTH__': // no auth
|
|
1589
1850
|
this._rfbInitState = 'SecurityResult';
|
|
1590
1851
|
return true;
|
|
1591
|
-
case 'STDVVNCAUTH_':
|
|
1592
|
-
this._rfbAuthScheme =
|
|
1593
|
-
return
|
|
1594
|
-
case 'TGHTULGNAUTH':
|
|
1595
|
-
this._rfbAuthScheme =
|
|
1596
|
-
return
|
|
1852
|
+
case 'STDVVNCAUTH_':
|
|
1853
|
+
this._rfbAuthScheme = securityTypeVNCAuth;
|
|
1854
|
+
return true;
|
|
1855
|
+
case 'TGHTULGNAUTH':
|
|
1856
|
+
this._rfbAuthScheme = securityTypeUnixLogon;
|
|
1857
|
+
return true;
|
|
1597
1858
|
default:
|
|
1598
1859
|
return this._fail("Unsupported tiny auth scheme " +
|
|
1599
1860
|
"(scheme: " + authType + ")");
|
|
@@ -1604,31 +1865,74 @@ export default class RFB extends EventTargetMixin {
|
|
|
1604
1865
|
return this._fail("No supported sub-auth types!");
|
|
1605
1866
|
}
|
|
1606
1867
|
|
|
1868
|
+
_handleRSAAESCredentialsRequired(event) {
|
|
1869
|
+
this.dispatchEvent(event);
|
|
1870
|
+
}
|
|
1871
|
+
|
|
1872
|
+
_handleRSAAESServerVerification(event) {
|
|
1873
|
+
this.dispatchEvent(event);
|
|
1874
|
+
}
|
|
1875
|
+
|
|
1876
|
+
_negotiateRA2neAuth() {
|
|
1877
|
+
if (this._rfbRSAAESAuthenticationState === null) {
|
|
1878
|
+
this._rfbRSAAESAuthenticationState = new RSAAESAuthenticationState(this._sock, () => this._rfbCredentials);
|
|
1879
|
+
this._rfbRSAAESAuthenticationState.addEventListener(
|
|
1880
|
+
"serververification", this._eventHandlers.handleRSAAESServerVerification);
|
|
1881
|
+
this._rfbRSAAESAuthenticationState.addEventListener(
|
|
1882
|
+
"credentialsrequired", this._eventHandlers.handleRSAAESCredentialsRequired);
|
|
1883
|
+
}
|
|
1884
|
+
this._rfbRSAAESAuthenticationState.checkInternalEvents();
|
|
1885
|
+
if (!this._rfbRSAAESAuthenticationState.hasStarted) {
|
|
1886
|
+
this._rfbRSAAESAuthenticationState.negotiateRA2neAuthAsync()
|
|
1887
|
+
.catch((e) => {
|
|
1888
|
+
if (e.message !== "disconnect normally") {
|
|
1889
|
+
this._fail(e.message);
|
|
1890
|
+
}
|
|
1891
|
+
}).then(() => {
|
|
1892
|
+
this.dispatchEvent(new CustomEvent('securityresult'));
|
|
1893
|
+
this._rfbInitState = "SecurityResult";
|
|
1894
|
+
return true;
|
|
1895
|
+
}).finally(() => {
|
|
1896
|
+
this._rfbRSAAESAuthenticationState.removeEventListener(
|
|
1897
|
+
"serververification", this._eventHandlers.handleRSAAESServerVerification);
|
|
1898
|
+
this._rfbRSAAESAuthenticationState.removeEventListener(
|
|
1899
|
+
"credentialsrequired", this._eventHandlers.handleRSAAESCredentialsRequired);
|
|
1900
|
+
this._rfbRSAAESAuthenticationState = null;
|
|
1901
|
+
});
|
|
1902
|
+
}
|
|
1903
|
+
return false;
|
|
1904
|
+
}
|
|
1905
|
+
|
|
1607
1906
|
_negotiateAuthentication() {
|
|
1608
1907
|
switch (this._rfbAuthScheme) {
|
|
1609
|
-
case
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
return true;
|
|
1613
|
-
}
|
|
1614
|
-
this._rfbInitState = 'ClientInitialisation';
|
|
1615
|
-
return this._initMsg();
|
|
1908
|
+
case securityTypeNone:
|
|
1909
|
+
this._rfbInitState = 'SecurityResult';
|
|
1910
|
+
return true;
|
|
1616
1911
|
|
|
1617
|
-
case
|
|
1912
|
+
case securityTypeXVP:
|
|
1618
1913
|
return this._negotiateXvpAuth();
|
|
1619
1914
|
|
|
1620
|
-
case
|
|
1915
|
+
case securityTypeARD:
|
|
1916
|
+
return this._negotiateARDAuth();
|
|
1917
|
+
|
|
1918
|
+
case securityTypeVNCAuth:
|
|
1621
1919
|
return this._negotiateStdVNCAuth();
|
|
1622
1920
|
|
|
1623
|
-
case
|
|
1921
|
+
case securityTypeTight:
|
|
1624
1922
|
return this._negotiateTightAuth();
|
|
1625
1923
|
|
|
1626
|
-
case
|
|
1924
|
+
case securityTypeVeNCrypt:
|
|
1627
1925
|
return this._negotiateVeNCryptAuth();
|
|
1628
1926
|
|
|
1629
|
-
case
|
|
1927
|
+
case securityTypePlain:
|
|
1928
|
+
return this._negotiatePlainAuth();
|
|
1929
|
+
|
|
1930
|
+
case securityTypeUnixLogon:
|
|
1630
1931
|
return this._negotiateTightUnixAuth();
|
|
1631
1932
|
|
|
1933
|
+
case securityTypeRA2ne:
|
|
1934
|
+
return this._negotiateRA2neAuth();
|
|
1935
|
+
|
|
1632
1936
|
default:
|
|
1633
1937
|
return this._fail("Unsupported auth scheme (scheme: " +
|
|
1634
1938
|
this._rfbAuthScheme + ")");
|
|
@@ -1636,6 +1940,13 @@ export default class RFB extends EventTargetMixin {
|
|
|
1636
1940
|
}
|
|
1637
1941
|
|
|
1638
1942
|
_handleSecurityResult() {
|
|
1943
|
+
// There is no security choice, and hence no security result
|
|
1944
|
+
// until RFB 3.7
|
|
1945
|
+
if (this._rfbVersion < 3.7) {
|
|
1946
|
+
this._rfbInitState = 'ClientInitialisation';
|
|
1947
|
+
return true;
|
|
1948
|
+
}
|
|
1949
|
+
|
|
1639
1950
|
if (this._sock.rQwait('VNC auth response ', 4)) { return false; }
|
|
1640
1951
|
|
|
1641
1952
|
const status = this._sock.rQshift32();
|
|
@@ -1643,13 +1954,13 @@ export default class RFB extends EventTargetMixin {
|
|
|
1643
1954
|
if (status === 0) { // OK
|
|
1644
1955
|
this._rfbInitState = 'ClientInitialisation';
|
|
1645
1956
|
Log.Debug('Authentication OK');
|
|
1646
|
-
return
|
|
1957
|
+
return true;
|
|
1647
1958
|
} else {
|
|
1648
1959
|
if (this._rfbVersion >= 3.8) {
|
|
1649
1960
|
this._rfbInitState = "SecurityReason";
|
|
1650
1961
|
this._securityContext = "security result";
|
|
1651
1962
|
this._securityStatus = status;
|
|
1652
|
-
return
|
|
1963
|
+
return true;
|
|
1653
1964
|
} else {
|
|
1654
1965
|
this.dispatchEvent(new CustomEvent(
|
|
1655
1966
|
"securityfailure",
|
|
@@ -1757,6 +2068,8 @@ export default class RFB extends EventTargetMixin {
|
|
|
1757
2068
|
if (this._fbDepth == 24) {
|
|
1758
2069
|
encs.push(encodings.encodingTight);
|
|
1759
2070
|
encs.push(encodings.encodingTightPNG);
|
|
2071
|
+
encs.push(encodings.encodingZRLE);
|
|
2072
|
+
encs.push(encodings.encodingJPEG);
|
|
1760
2073
|
encs.push(encodings.encodingHextile);
|
|
1761
2074
|
encs.push(encodings.encodingRRE);
|
|
1762
2075
|
}
|
|
@@ -1823,6 +2136,14 @@ export default class RFB extends EventTargetMixin {
|
|
|
1823
2136
|
}
|
|
1824
2137
|
}
|
|
1825
2138
|
|
|
2139
|
+
// Resume authentication handshake after it was paused for some
|
|
2140
|
+
// reason, e.g. waiting for a password from the user
|
|
2141
|
+
_resumeAuthentication() {
|
|
2142
|
+
// We use setTimeout() so it's run in its own context, just like
|
|
2143
|
+
// it originally did via the WebSocket's event handler
|
|
2144
|
+
setTimeout(this._initMsg.bind(this), 0);
|
|
2145
|
+
}
|
|
2146
|
+
|
|
1826
2147
|
_handleSetColourMapMsg() {
|
|
1827
2148
|
Log.Debug("SetColorMapEntries");
|
|
1828
2149
|
|
|
@@ -2183,15 +2504,7 @@ export default class RFB extends EventTargetMixin {
|
|
|
2183
2504
|
return this._handleCursor();
|
|
2184
2505
|
|
|
2185
2506
|
case encodings.pseudoEncodingQEMUExtendedKeyEvent:
|
|
2186
|
-
|
|
2187
|
-
try {
|
|
2188
|
-
const keyboardEvent = document.createEvent("keyboardEvent");
|
|
2189
|
-
if (keyboardEvent.code !== undefined) {
|
|
2190
|
-
this._qemuExtKeyEventSupported = true;
|
|
2191
|
-
}
|
|
2192
|
-
} catch (err) {
|
|
2193
|
-
// Do nothing
|
|
2194
|
-
}
|
|
2507
|
+
this._qemuExtKeyEventSupported = true;
|
|
2195
2508
|
return true;
|
|
2196
2509
|
|
|
2197
2510
|
case encodings.pseudoEncodingDesktopName:
|
|
@@ -2493,6 +2806,9 @@ export default class RFB extends EventTargetMixin {
|
|
|
2493
2806
|
this._updateScale();
|
|
2494
2807
|
|
|
2495
2808
|
this._updateContinuousUpdates();
|
|
2809
|
+
|
|
2810
|
+
// Keep this size until browser client size changes
|
|
2811
|
+
this._saveExpectedClientSize();
|
|
2496
2812
|
}
|
|
2497
2813
|
|
|
2498
2814
|
_xvpOp(ver, op) {
|
|
@@ -2882,9 +3198,9 @@ RFB.messages = {
|
|
|
2882
3198
|
buff[offset + 12] = 0; // blue-max
|
|
2883
3199
|
buff[offset + 13] = (1 << bits) - 1; // blue-max
|
|
2884
3200
|
|
|
2885
|
-
buff[offset + 14] = bits *
|
|
3201
|
+
buff[offset + 14] = bits * 0; // red-shift
|
|
2886
3202
|
buff[offset + 15] = bits * 1; // green-shift
|
|
2887
|
-
buff[offset + 16] = bits *
|
|
3203
|
+
buff[offset + 16] = bits * 2; // blue-shift
|
|
2888
3204
|
|
|
2889
3205
|
buff[offset + 17] = 0; // padding
|
|
2890
3206
|
buff[offset + 18] = 0; // padding
|