diff options
author | Pierre Ossman <ossman@cendio.se> | 2022-08-19 10:10:10 +0200 |
---|---|---|
committer | Pierre Ossman <ossman@cendio.se> | 2022-08-19 10:10:10 +0200 |
commit | 1d148a8478f54084c9be433b2f35aee3e0a906a9 (patch) | |
tree | 063a081f67677576f830675fd9aabbae61ed1cee | |
parent | cdfb33665195eb9a73fb00feb6ebaccd1068cd50 (diff) | |
parent | df8d005de960e2133ded0610656af591e92fc6b7 (diff) | |
download | novnc-1d148a8478f54084c9be433b2f35aee3e0a906a9.tar.gz |
Merge branch 'authprio' of https://github.com/CendioOssman/noVNC
-rw-r--r-- | core/rfb.js | 223 | ||||
-rw-r--r-- | tests/test.rfb.js | 231 |
2 files changed, 260 insertions, 194 deletions
diff --git a/core/rfb.js b/core/rfb.js index 578a898..4b3526f 100644 --- a/core/rfb.js +++ b/core/rfb.js @@ -54,6 +54,21 @@ const GESTURE_SCRLSENS = 50; const DOUBLE_TAP_TIMEOUT = 1000; const DOUBLE_TAP_THRESHOLD = 50; +// Security types +const securityTypeNone = 1; +const securityTypeVNCAuth = 2; +const securityTypeRA2ne = 6; +const securityTypeTight = 16; +const securityTypeVeNCrypt = 19; +const securityTypeXVP = 22; +const securityTypeARD = 30; + +// Special Tight security types +const securityTypeUnixLogon = 129; + +// VeNCrypt security types +const securityTypePlain = 256; + // Extended clipboard pseudo-encoding formats const extendedClipboardFormatText = 1; /*eslint-disable no-unused-vars */ @@ -402,7 +417,7 @@ export default class RFB extends EventTargetMixin { sendCredentials(creds) { this._rfbCredentials = creds; - setTimeout(this._initMsg.bind(this), 0); + this._resumeAuthentication(); } sendCtrlAltDel() { @@ -922,8 +937,15 @@ export default class RFB extends EventTargetMixin { } } break; + case 'connecting': + while (this._rfbConnectionState === 'connecting') { + if (!this._initMsg()) { + break; + } + } + break; default: - this._initMsg(); + Log.Error("Got data while in an invalid state"); break; } } @@ -1332,6 +1354,21 @@ export default class RFB extends EventTargetMixin { this._rfbInitState = 'Security'; } + _isSupportedSecurityType(type) { + const clientTypes = [ + securityTypeNone, + securityTypeVNCAuth, + securityTypeRA2ne, + securityTypeTight, + securityTypeVeNCrypt, + securityTypeXVP, + securityTypeARD, + securityTypePlain, + ]; + + return clientTypes.includes(type); + } + _negotiateSecurity() { if (this._rfbVersion >= 3.7) { // Server sends supported list, client decides @@ -1342,28 +1379,23 @@ export default class RFB extends EventTargetMixin { this._rfbInitState = "SecurityReason"; this._securityContext = "no security types"; this._securityStatus = 1; - return this._initMsg(); + return true; } const types = this._sock.rQshiftBytes(numTypes); Log.Debug("Server security types: " + types); - // Look for each auth in preferred order - if (types.includes(1)) { - this._rfbAuthScheme = 1; // None - } else if (types.includes(22)) { - this._rfbAuthScheme = 22; // XVP - } else if (types.includes(16)) { - this._rfbAuthScheme = 16; // Tight - } else if (types.includes(6)) { - this._rfbAuthScheme = 6; // RA2ne Auth - } else if (types.includes(2)) { - this._rfbAuthScheme = 2; // VNC Auth - } else if (types.includes(30)) { - this._rfbAuthScheme = 30; // ARD Auth - } else if (types.includes(19)) { - this._rfbAuthScheme = 19; // VeNCrypt Auth - } else { + // Look for a matching security type in the order that the + // server prefers + this._rfbAuthScheme = -1; + for (let type of types) { + if (this._isSupportedSecurityType(type)) { + this._rfbAuthScheme = type; + break; + } + } + + if (this._rfbAuthScheme === -1) { return this._fail("Unsupported security types (types: " + types + ")"); } @@ -1377,14 +1409,14 @@ export default class RFB extends EventTargetMixin { this._rfbInitState = "SecurityReason"; this._securityContext = "authentication scheme"; this._securityStatus = 1; - return this._initMsg(); + return true; } } this._rfbInitState = 'Authentication'; Log.Debug('Authenticating using scheme: ' + this._rfbAuthScheme); - return this._initMsg(); // jump to authentication + return true; } _handleSecurityReason() { @@ -1434,7 +1466,7 @@ export default class RFB extends EventTargetMixin { this._rfbCredentials.username + this._rfbCredentials.target; this._sock.sendString(xvpAuthStr); - this._rfbAuthScheme = 2; + this._rfbAuthScheme = securityTypeVNCAuth; return this._negotiateAuthentication(); } @@ -1492,49 +1524,66 @@ export default class RFB extends EventTargetMixin { subtypes.push(this._sock.rQshift32()); } - // 256 = Plain subtype - if (subtypes.indexOf(256) != -1) { - // 0x100 = 256 - this._sock.send([0, 0, 1, 0]); - this._rfbVeNCryptState = 4; - } else { - return this._fail("VeNCrypt Plain subtype not offered by server"); + // Look for a matching security type in the order that the + // server prefers + this._rfbAuthScheme = -1; + for (let type of subtypes) { + // Avoid getting in to a loop + if (type === securityTypeVeNCrypt) { + continue; + } + + if (this._isSupportedSecurityType(type)) { + this._rfbAuthScheme = type; + break; + } } - } - // negotiated Plain subtype, server waits for password - if (this._rfbVeNCryptState == 4) { - if (this._rfbCredentials.username === undefined || - this._rfbCredentials.password === undefined) { - this.dispatchEvent(new CustomEvent( - "credentialsrequired", - { detail: { types: ["username", "password"] } })); - return false; + if (this._rfbAuthScheme === -1) { + return this._fail("Unsupported security types (types: " + subtypes + ")"); } - const user = encodeUTF8(this._rfbCredentials.username); - const pass = encodeUTF8(this._rfbCredentials.password); - - this._sock.send([ - (user.length >> 24) & 0xFF, - (user.length >> 16) & 0xFF, - (user.length >> 8) & 0xFF, - user.length & 0xFF - ]); - this._sock.send([ - (pass.length >> 24) & 0xFF, - (pass.length >> 16) & 0xFF, - (pass.length >> 8) & 0xFF, - pass.length & 0xFF - ]); - this._sock.sendString(user); - this._sock.sendString(pass); + this._sock.send([this._rfbAuthScheme >> 24, + this._rfbAuthScheme >> 16, + this._rfbAuthScheme >> 8, + this._rfbAuthScheme]); - this._rfbInitState = "SecurityResult"; + this._rfbVeNCryptState == 4; return true; } } + _negotiatePlainAuth() { + if (this._rfbCredentials.username === undefined || + this._rfbCredentials.password === undefined) { + this.dispatchEvent(new CustomEvent( + "credentialsrequired", + { detail: { types: ["username", "password"] } })); + return false; + } + + const user = encodeUTF8(this._rfbCredentials.username); + const pass = encodeUTF8(this._rfbCredentials.password); + + this._sock.send([ + (user.length >> 24) & 0xFF, + (user.length >> 16) & 0xFF, + (user.length >> 8) & 0xFF, + user.length & 0xFF + ]); + this._sock.send([ + (pass.length >> 24) & 0xFF, + (pass.length >> 16) & 0xFF, + (pass.length >> 8) & 0xFF, + pass.length & 0xFF + ]); + this._sock.sendString(user); + this._sock.sendString(pass); + + this._rfbInitState = "SecurityResult"; + return true; + } + _negotiateStdVNCAuth() { if (this._sock.rQwait("auth challenge", 16)) { return false; } @@ -1661,7 +1710,7 @@ export default class RFB extends EventTargetMixin { this._rfbCredentials.ardCredentials = encrypted; this._rfbCredentials.ardPublicKey = clientPublicKey; - setTimeout(this._initMsg.bind(this), 0); + this._resumeAuthentication(); } _negotiateTightUnixAuth() { @@ -1771,12 +1820,12 @@ export default class RFB extends EventTargetMixin { case 'STDVNOAUTH__': // no auth this._rfbInitState = 'SecurityResult'; return true; - case 'STDVVNCAUTH_': // VNC auth - this._rfbAuthScheme = 2; - return this._initMsg(); - case 'TGHTULGNAUTH': // UNIX auth - this._rfbAuthScheme = 129; - return this._initMsg(); + case 'STDVVNCAUTH_': + this._rfbAuthScheme = securityTypeVNCAuth; + return true; + case 'TGHTULGNAUTH': + this._rfbAuthScheme = securityTypeUnixLogon; + return true; default: return this._fail("Unsupported tiny auth scheme " + "(scheme: " + authType + ")"); @@ -1813,7 +1862,7 @@ export default class RFB extends EventTargetMixin { }).then(() => { this.dispatchEvent(new CustomEvent('securityresult')); this._rfbInitState = "SecurityResult"; - this._initMsg(); + return true; }).finally(() => { this._rfbRSAAESAuthenticationState.removeEventListener( "serververification", this._eventHandlers.handleRSAAESServerVerification); @@ -1827,33 +1876,32 @@ export default class RFB extends EventTargetMixin { _negotiateAuthentication() { switch (this._rfbAuthScheme) { - case 1: // no auth - if (this._rfbVersion >= 3.8) { - this._rfbInitState = 'SecurityResult'; - return true; - } - this._rfbInitState = 'ClientInitialisation'; - return this._initMsg(); + case securityTypeNone: + this._rfbInitState = 'SecurityResult'; + return true; - case 22: // XVP auth + case securityTypeXVP: return this._negotiateXvpAuth(); - case 30: // ARD auth + case securityTypeARD: return this._negotiateARDAuth(); - case 2: // VNC authentication + case securityTypeVNCAuth: return this._negotiateStdVNCAuth(); - case 16: // TightVNC Security Type + case securityTypeTight: return this._negotiateTightAuth(); - case 19: // VeNCrypt Security Type + case securityTypeVeNCrypt: return this._negotiateVeNCryptAuth(); - case 129: // TightVNC UNIX Security Type + case securityTypePlain: + return this._negotiatePlainAuth(); + + case securityTypeUnixLogon: return this._negotiateTightUnixAuth(); - case 6: // RA2ne Security Type + case securityTypeRA2ne: return this._negotiateRA2neAuth(); default: @@ -1863,6 +1911,13 @@ export default class RFB extends EventTargetMixin { } _handleSecurityResult() { + // There is no security choice, and hence no security result + // until RFB 3.7 + if (this._rfbVersion < 3.7) { + this._rfbInitState = 'ClientInitialisation'; + return true; + } + if (this._sock.rQwait('VNC auth response ', 4)) { return false; } const status = this._sock.rQshift32(); @@ -1870,13 +1925,13 @@ export default class RFB extends EventTargetMixin { if (status === 0) { // OK this._rfbInitState = 'ClientInitialisation'; Log.Debug('Authentication OK'); - return this._initMsg(); + return true; } else { if (this._rfbVersion >= 3.8) { this._rfbInitState = "SecurityReason"; this._securityContext = "security result"; this._securityStatus = status; - return this._initMsg(); + return true; } else { this.dispatchEvent(new CustomEvent( "securityfailure", @@ -2052,6 +2107,14 @@ export default class RFB extends EventTargetMixin { } } + // Resume authentication handshake after it was paused for some + // reason, e.g. waiting for a password from the user + _resumeAuthentication() { + // We use setTimeout() so it's run in its own context, just like + // it originally did via the WebSocket's event handler + setTimeout(this._initMsg.bind(this), 0); + } + _handleSetColourMapMsg() { Log.Debug("SetColorMapEntries"); diff --git a/tests/test.rfb.js b/tests/test.rfb.js index 3f79bc0..75d1e11 100644 --- a/tests/test.rfb.js +++ b/tests/test.rfb.js @@ -1026,17 +1026,21 @@ describe('Remote Frame Buffer Protocol Client', function () { client._rfbConnectionState = 'connecting'; }); - describe('ProtocolVersion', function () { - function sendVer(ver, client) { - const arr = new Uint8Array(12); - for (let i = 0; i < ver.length; i++) { - arr[i+4] = ver.charCodeAt(i); - } - arr[0] = 'R'; arr[1] = 'F'; arr[2] = 'B'; arr[3] = ' '; - arr[11] = '\n'; - client._sock._websocket._receiveData(arr); + function sendVer(ver, client) { + const arr = new Uint8Array(12); + for (let i = 0; i < ver.length; i++) { + arr[i+4] = ver.charCodeAt(i); } + arr[0] = 'R'; arr[1] = 'F'; arr[2] = 'B'; arr[3] = ' '; + arr[11] = '\n'; + client._sock._websocket._receiveData(arr); + } + function sendSecurity(type, cl) { + cl._sock._websocket._receiveData(new Uint8Array([1, type])); + } + + describe('ProtocolVersion', function () { describe('version parsing', function () { it('should interpret version 003.003 as version 3.3', function () { sendVer('003.003', client); @@ -1127,44 +1131,24 @@ describe('Remote Frame Buffer Protocol Client', function () { describe('Security', function () { beforeEach(function () { - client._rfbInitState = 'Security'; - }); - - it('should simply receive the auth scheme when for versions < 3.7', function () { - client._rfbVersion = 3.6; - const authSchemeRaw = [1, 2, 3, 4]; - const authScheme = (authSchemeRaw[0] << 24) + (authSchemeRaw[1] << 16) + - (authSchemeRaw[2] << 8) + authSchemeRaw[3]; - client._sock._websocket._receiveData(new Uint8Array(authSchemeRaw)); - expect(client._rfbAuthScheme).to.equal(authScheme); - }); - - it('should prefer no authentication is possible', function () { - client._rfbVersion = 3.7; - const authSchemes = [2, 1, 3]; - client._sock._websocket._receiveData(new Uint8Array(authSchemes)); - expect(client._rfbAuthScheme).to.equal(1); - expect(client._sock).to.have.sent(new Uint8Array([1, 1])); + sendVer('003.008\n', client); + client._sock._websocket._getSentData(); }); - it('should choose for the most prefered scheme possible for versions >= 3.7', function () { - client._rfbVersion = 3.7; - const authSchemes = [2, 22, 16]; + it('should respect server preference order', function () { + const authSchemes = [ 6, 79, 30, 188, 16, 6, 1 ]; client._sock._websocket._receiveData(new Uint8Array(authSchemes)); - expect(client._rfbAuthScheme).to.equal(22); - expect(client._sock).to.have.sent(new Uint8Array([22])); + expect(client._sock).to.have.sent(new Uint8Array([30])); }); - it('should fail if there are no supported schemes for versions >= 3.7', function () { + it('should fail if there are no supported schemes', function () { sinon.spy(client, "_fail"); - client._rfbVersion = 3.7; const authSchemes = [1, 32]; client._sock._websocket._receiveData(new Uint8Array(authSchemes)); expect(client._fail).to.have.been.calledOnce; }); - it('should fail with the appropriate message if no types are sent for versions >= 3.7', function () { - client._rfbVersion = 3.7; + it('should fail with the appropriate message if no types are sent', function () { const failureData = [0, 0, 0, 0, 6, 119, 104, 111, 111, 112, 115]; sinon.spy(client, '_fail'); client._sock._websocket._receiveData(new Uint8Array(failureData)); @@ -1175,7 +1159,6 @@ describe('Remote Frame Buffer Protocol Client', function () { }); it('should transition to the Authentication state and continue on successful negotiation', function () { - client._rfbVersion = 3.7; const authSchemes = [1, 1]; client._negotiateAuthentication = sinon.spy(); client._sock._websocket._receiveData(new Uint8Array(authSchemes)); @@ -1184,17 +1167,8 @@ describe('Remote Frame Buffer Protocol Client', function () { }); }); - describe('Authentication', function () { - beforeEach(function () { - client._rfbInitState = 'Security'; - }); - - function sendSecurity(type, cl) { - cl._sock._websocket._receiveData(new Uint8Array([1, type])); - } - + describe('Legacy Authentication', function () { it('should fail on auth scheme 0 (pre 3.7) with the given message', function () { - client._rfbVersion = 3.6; const errMsg = "Whoopsies"; const data = [0, 0, 0, 0]; const errLen = errMsg.length; @@ -1203,37 +1177,42 @@ describe('Remote Frame Buffer Protocol Client', function () { data.push(errMsg.charCodeAt(i)); } + sendVer('003.006\n', client); + client._sock._websocket._getSentData(); + sinon.spy(client, '_fail'); client._sock._websocket._receiveData(new Uint8Array(data)); expect(client._fail).to.have.been.calledWith( 'Security negotiation failed on authentication scheme (reason: Whoopsies)'); }); - it('should transition straight to SecurityResult on "no auth" (1) for versions >= 3.8', function () { - client._rfbVersion = 3.8; - sendSecurity(1, client); - expect(client._rfbInitState).to.equal('SecurityResult'); + it('should transition straight to ServerInitialisation on "no auth" for versions < 3.7', function () { + sendVer('003.006\n', client); + client._sock._websocket._getSentData(); + + client._sock._websocket._receiveData(new Uint8Array([0, 0, 0, 1])); + expect(client._rfbInitState).to.equal('ServerInitialisation'); + }); + }); + + describe('Authentication', function () { + beforeEach(function () { + sendVer('003.008\n', client); + client._sock._websocket._getSentData(); }); - it('should transition straight to ServerInitialisation on "no auth" for versions < 3.8', function () { - client._rfbVersion = 3.7; + it('should transition straight to SecurityResult on "no auth" (1)', function () { sendSecurity(1, client); - expect(client._rfbInitState).to.equal('ServerInitialisation'); + expect(client._rfbInitState).to.equal('SecurityResult'); }); it('should fail on an unknown auth scheme', function () { sinon.spy(client, "_fail"); - client._rfbVersion = 3.8; sendSecurity(57, client); expect(client._fail).to.have.been.calledOnce; }); describe('VNC Authentication (type 2) Handler', function () { - beforeEach(function () { - client._rfbInitState = 'Security'; - client._rfbVersion = 3.8; - }); - it('should fire the credentialsrequired event if missing a password', function () { const spy = sinon.spy(); client.addEventListener("credentialsrequired", spy); @@ -1274,12 +1253,6 @@ describe('Remote Frame Buffer Protocol Client', function () { }); describe('ARD Authentication (type 30) Handler', function () { - - beforeEach(function () { - client._rfbInitState = 'Security'; - client._rfbVersion = 3.8; - }); - it('should fire the credentialsrequired event if all credentials are missing', function () { const spy = sinon.spy(); client.addEventListener("credentialsrequired", spy); @@ -1347,11 +1320,6 @@ describe('Remote Frame Buffer Protocol Client', function () { }); describe('XVP Authentication (type 22) Handler', function () { - beforeEach(function () { - client._rfbInitState = 'Security'; - client._rfbVersion = 3.8; - }); - it('should fall through to standard VNC authentication upon completion', function () { client._rfbCredentials = { username: 'user', target: 'target', @@ -1400,8 +1368,6 @@ describe('Remote Frame Buffer Protocol Client', function () { describe('TightVNC Authentication (type 16) Handler', function () { beforeEach(function () { - client._rfbInitState = 'Security'; - client._rfbVersion = 3.8; sendSecurity(16, client); client._sock._websocket._getSentData(); // skip the security reply }); @@ -1487,8 +1453,6 @@ describe('Remote Frame Buffer Protocol Client', function () { describe('VeNCrypt Authentication (type 19) Handler', function () { beforeEach(function () { - client._rfbInitState = 'Security'; - client._rfbVersion = 3.8; sendSecurity(19, client); expect(client._sock).to.have.sent(new Uint8Array([19])); }); @@ -1499,18 +1463,70 @@ describe('Remote Frame Buffer Protocol Client', function () { expect(client._fail).to.have.been.calledOnce; }); - it('should fail if the Plain authentication is not present', function () { + it('should fail if there are no supported subtypes', function () { // VeNCrypt version client._sock._websocket._receiveData(new Uint8Array([0, 2])); expect(client._sock).to.have.sent(new Uint8Array([0, 2])); // Server ACK. client._sock._websocket._receiveData(new Uint8Array([0])); - // Subtype list, only list subtype 1. + // Subtype list sinon.spy(client, "_fail"); - client._sock._websocket._receiveData(new Uint8Array([1, 0, 0, 0, 1])); + client._sock._websocket._receiveData(new Uint8Array([2, 0, 0, 0, 9, 0, 0, 1, 4])); expect(client._fail).to.have.been.calledOnce; }); + it('should support standard types', function () { + // VeNCrypt version + client._sock._websocket._receiveData(new Uint8Array([0, 2])); + expect(client._sock).to.have.sent(new Uint8Array([0, 2])); + // Server ACK. + client._sock._websocket._receiveData(new Uint8Array([0])); + // Subtype list + client._sock._websocket._receiveData(new Uint8Array([2, 0, 0, 0, 2, 0, 0, 1, 4])); + + let expectedResponse = []; + push32(expectedResponse, 2); // Chosen subtype. + + expect(client._sock).to.have.sent(new Uint8Array(expectedResponse)); + }); + + it('should respect server preference order', function () { + // VeNCrypt version + client._sock._websocket._receiveData(new Uint8Array([0, 2])); + expect(client._sock).to.have.sent(new Uint8Array([0, 2])); + // Server ACK. + client._sock._websocket._receiveData(new Uint8Array([0])); + // Subtype list + let subtypes = [ 6 ]; + push32(subtypes, 79); + push32(subtypes, 30); + push32(subtypes, 188); + push32(subtypes, 256); + push32(subtypes, 6); + push32(subtypes, 1); + client._sock._websocket._receiveData(new Uint8Array(subtypes)); + + let expectedResponse = []; + push32(expectedResponse, 30); // Chosen subtype. + + expect(client._sock).to.have.sent(new Uint8Array(expectedResponse)); + }); + + it('should ignore redundant VeNCrypt subtype', function () { + // VeNCrypt version + client._sock._websocket._receiveData(new Uint8Array([0, 2])); + expect(client._sock).to.have.sent(new Uint8Array([0, 2])); + // Server ACK. + client._sock._websocket._receiveData(new Uint8Array([0])); + // Subtype list + client._sock._websocket._receiveData(new Uint8Array([2, 0, 0, 0, 19, 0, 0, 0, 2])); + + let expectedResponse = []; + push32(expectedResponse, 2); // Chosen subtype. + + expect(client._sock).to.have.sent(new Uint8Array(expectedResponse)); + }); + it('should support Plain authentication', function () { client._rfbCredentials = { username: 'username', password: 'password' }; // VeNCrypt version @@ -1582,70 +1598,57 @@ describe('Remote Frame Buffer Protocol Client', function () { }); }); - describe('SecurityResult', function () { + describe('Legacy SecurityResult', function () { beforeEach(function () { - client._rfbInitState = 'SecurityResult'; - }); - - it('should fall through to ServerInitialisation on a response code of 0', function () { - client._sock._websocket._receiveData(new Uint8Array([0, 0, 0, 0])); - expect(client._rfbInitState).to.equal('ServerInitialisation'); - }); - - it('should fail on an error code of 1 with the given message for versions >= 3.8', function () { - client._rfbVersion = 3.8; - sinon.spy(client, '_fail'); - const failureData = [0, 0, 0, 1, 0, 0, 0, 6, 119, 104, 111, 111, 112, 115]; - client._sock._websocket._receiveData(new Uint8Array(failureData)); - expect(client._fail).to.have.been.calledWith( - 'Security negotiation failed on security result (reason: whoops)'); - }); - - it('should fail on an error code of 1 with a standard message for version < 3.8', function () { - sinon.spy(client, '_fail'); - client._rfbVersion = 3.7; - client._sock._websocket._receiveData(new Uint8Array([0, 0, 0, 1])); - expect(client._fail).to.have.been.calledWith( - 'Security handshake failed'); + sendVer('003.007\n', client); + client._sock._websocket._getSentData(); + sendSecurity(1, client); + client._sock._websocket._getSentData(); }); - it('should result in securityfailure event when receiving a non zero status', function () { + it('should not include reason in securityfailure event', function () { const spy = sinon.spy(); client.addEventListener("securityfailure", spy); client._sock._websocket._receiveData(new Uint8Array([0, 0, 0, 2])); expect(spy).to.have.been.calledOnce; expect(spy.args[0][0].detail.status).to.equal(2); + expect('reason' in spy.args[0][0].detail).to.be.false; + }); + }); + + describe('SecurityResult', function () { + beforeEach(function () { + sendVer('003.008\n', client); + client._sock._websocket._getSentData(); + sendSecurity(1, client); + client._sock._websocket._getSentData(); + }); + + it('should fall through to ServerInitialisation on a response code of 0', function () { + client._sock._websocket._receiveData(new Uint8Array([0, 0, 0, 0])); + expect(client._rfbInitState).to.equal('ServerInitialisation'); }); it('should include reason when provided in securityfailure event', function () { - client._rfbVersion = 3.8; const spy = sinon.spy(); client.addEventListener("securityfailure", spy); const failureData = [0, 0, 0, 1, 0, 0, 0, 12, 115, 117, 99, 104, 32, 102, 97, 105, 108, 117, 114, 101]; client._sock._websocket._receiveData(new Uint8Array(failureData)); + expect(spy).to.have.been.calledOnce; expect(spy.args[0][0].detail.status).to.equal(1); expect(spy.args[0][0].detail.reason).to.equal('such failure'); }); it('should not include reason when length is zero in securityfailure event', function () { - client._rfbVersion = 3.9; const spy = sinon.spy(); client.addEventListener("securityfailure", spy); const failureData = [0, 0, 0, 1, 0, 0, 0, 0]; client._sock._websocket._receiveData(new Uint8Array(failureData)); + expect(spy).to.have.been.calledOnce; expect(spy.args[0][0].detail.status).to.equal(1); expect('reason' in spy.args[0][0].detail).to.be.false; }); - - it('should not include reason in securityfailure event for version < 3.8', function () { - client._rfbVersion = 3.6; - const spy = sinon.spy(); - client.addEventListener("securityfailure", spy); - client._sock._websocket._receiveData(new Uint8Array([0, 0, 0, 2])); - expect(spy.args[0][0].detail.status).to.equal(2); - expect('reason' in spy.args[0][0].detail).to.be.false; - }); }); describe('ClientInitialisation', function () { |