Add ATEN iKVM support

I (@kelleyk) have rebased the original patch by @jimdigriz (Alexander Clouter)
on the current upstream version of noVNC.  This required the following changes:

  - Rename keysym symbols.
  - Convert use of _rfb_state to _rfb_init_state.
  - Update state transition logic; fixes "old-style" ATEN auth detection.
  - Fix conditional ordering so that we do not skip new-type ATEN iKVM detection.
  - Fix assertion broken in test case broken by upstream changes.
  - Convert arr.push(x) function calls to push(arr, x).
  - Restore keyEvent and pointerEvent handlers before relevant test cases.
This commit is contained in:
Kevin Kelley 2017-01-12 02:16:06 -08:00
parent 7541906688
commit 6499a8c8f7
4 changed files with 522 additions and 1 deletions

View File

@ -141,6 +141,7 @@ WebSockets to TCP socket proxy. There is a python proxy included
* Original Logo : Michael Sersen * Original Logo : Michael Sersen
* tight encoding : Michael Tinglof (Mercuri.ca) * tight encoding : Michael Tinglof (Mercuri.ca)
* pixel format conversion : [Alexander Clouter](http://www.digriz.org.uk/) * pixel format conversion : [Alexander Clouter](http://www.digriz.org.uk/)
* ATEN iKVM support : [Alexander Clouter](http://www.digriz.org.uk/)
* Included libraries: * Included libraries:
* as3crypto : Henri Torgemane (code.google.com/p/as3crypto) * as3crypto : Henri Torgemane (code.google.com/p/as3crypto)

View File

@ -379,4 +379,82 @@ var KeyTable = {
XK_ydiaeresis: 0x00ff, /* U+00FF LATIN SMALL LETTER Y WITH DIAERESIS */ XK_ydiaeresis: 0x00ff, /* U+00FF LATIN SMALL LETTER Y WITH DIAERESIS */
}; };
var XK2HID = {};
// F{1..12}
for (var i = KeyTable.XK_F1; i <= KeyTable.XK_F12; i++) {
XK2HID[i] = 0x3a + (i - KeyTable.XK_F1);
}
// A-Za-z
for (var i = KeyTable.XK_A; i <= KeyTable.XK_Z; i++) {
XK2HID[i] = 0x04 + (i - KeyTable.XK_A);
XK2HID[i + (KeyTable.XK_a - KeyTable.XK_A)] = 0x04 + (i - KeyTable.XK_A);
}
// 1-9
for (var i = KeyTable.XK_1; i <= KeyTable.XK_9; i++) {
XK2HID[i] = 0x1e + (i - KeyTable.XK_1);
}
XK2HID[KeyTable.XK_0] = 0x27;
XK2HID[KeyTable.XK_Return] = 0x28;
XK2HID[KeyTable.XK_Escape] = 0x29;
XK2HID[KeyTable.XK_BackSpace] = 0x2a;
XK2HID[KeyTable.XK_Tab] = 0x2b;
XK2HID[KeyTable.XK_space] = 0x2c;
XK2HID[KeyTable.XK_minus] = 0x2d;
XK2HID[KeyTable.XK_equal] = 0x2e;
XK2HID[KeyTable.XK_bracketleft] = 0x2f;
XK2HID[KeyTable.XK_bracketright] = 0x30;
XK2HID[KeyTable.XK_backslash] = 0x31;
XK2HID[KeyTable.XK_semicolon] = 0x33;
XK2HID[KeyTable.XK_apostrophe] = 0x34;
XK2HID[KeyTable.XK_grave] = 0x35;
XK2HID[KeyTable.XK_comma] = 0x36;
XK2HID[KeyTable.XK_period] = 0x37;
XK2HID[KeyTable.XK_slash] = 0x38;
XK2HID[KeyTable.XK_Print] = 0x46;
XK2HID[KeyTable.XK_Scroll_Lock] = 0x47;
XK2HID[KeyTable.XK_Pause] = 0x48;
XK2HID[KeyTable.XK_Insert] = 0x49;
XK2HID[KeyTable.XK_Home] = 0x4a;
XK2HID[KeyTable.XK_Page_Up] = 0x4b;
XK2HID[KeyTable.XK_Delete] = 0x4c;
XK2HID[KeyTable.XK_End] = 0x4d;
XK2HID[KeyTable.XK_Page_Down] = 0x4e;
XK2HID[KeyTable.XK_Right] = 0x4f;
XK2HID[KeyTable.XK_Left] = 0x50;
XK2HID[KeyTable.XK_Down] = 0x51;
XK2HID[KeyTable.XK_Up] = 0x52;
XK2HID[KeyTable.XK_Control_L] = 0xe0;
XK2HID[KeyTable.XK_Control_R] = XK2HID[KeyTable.XK_Control_L];
XK2HID[KeyTable.XK_Shift_L] = 0xe1;
XK2HID[KeyTable.XK_Shift_R] = XK2HID[KeyTable.XK_Shift_L];
XK2HID[KeyTable.XK_Alt_L] = 0xe2;
XK2HID[KeyTable.XK_Alt_R] = XK2HID[KeyTable.XK_Alt_L];
XK2HID[KeyTable.XK_Super_L] = 0xe3;
XK2HID[KeyTable.XK_Super_R] = XK2HID[KeyTable.XK_Super_L];
XK2HID[KeyTable.XK_Caps_Lock] = 0x39;
// locale hardcoded hack
XK2HID[KeyTable.XK_less] = XK2HID[KeyTable.XK_comma];
XK2HID[KeyTable.XK_greater] = XK2HID[KeyTable.XK_period];
XK2HID[KeyTable.XK_exclam] = XK2HID[KeyTable.XK_1];
XK2HID[KeyTable.XK_at] = XK2HID[KeyTable.XK_2];
XK2HID[KeyTable.XK_numbersign] = XK2HID[KeyTable.XK_3];
XK2HID[KeyTable.XK_dollar] = XK2HID[KeyTable.XK_4];
XK2HID[KeyTable.XK_percent] = XK2HID[KeyTable.XK_5];
XK2HID[KeyTable.XK_asciicircum] = XK2HID[KeyTable.XK_6];
XK2HID[KeyTable.XK_ampersand] = XK2HID[KeyTable.XK_7];
XK2HID[KeyTable.XK_asterisk] = XK2HID[KeyTable.XK_8];
XK2HID[KeyTable.XK_parenleft] = XK2HID[KeyTable.XK_9];
XK2HID[KeyTable.XK_parenright] = XK2HID[KeyTable.XK_0];
XK2HID[KeyTable.XK_underscore] = XK2HID[KeyTable.XK_minus];
XK2HID[KeyTable.XK_bar] = XK2HID[KeyTable.XK_backslash];
XK2HID[KeyTable.XK_quotedbl] = XK2HID[KeyTable.XK_apostrophe];
XK2HID[KeyTable.XK_asciitilde] = XK2HID[KeyTable.XK_grave];
/* [module] export default KeyTable; */ /* [module] export default KeyTable; */

View File

@ -18,6 +18,7 @@
* import Base64 from "./base64"; * import Base64 from "./base64";
* import DES from "./des"; * import DES from "./des";
* import KeyTable from "./input/keysym"; * import KeyTable from "./input/keysym";
* import XK2HID from "./input/keysym";
* import XtScancode from "./input/xtscancodes"; * import XtScancode from "./input/xtscancodes";
* import Inflator from "./inflator.mod"; * import Inflator from "./inflator.mod";
*/ */
@ -53,6 +54,7 @@
['HEXTILE', 0x05 ], ['HEXTILE', 0x05 ],
['RRE', 0x02 ], ['RRE', 0x02 ],
['RAW', 0x00 ], ['RAW', 0x00 ],
['ATEN', 0x59 ],
// Psuedo-encoding settings // Psuedo-encoding settings
@ -153,6 +155,7 @@
'local_cursor': false, // Request locally rendered cursor 'local_cursor': false, // Request locally rendered cursor
'shared': true, // Request shared mode 'shared': true, // Request shared mode
'view_only': false, // Disable client mouse/keyboard 'view_only': false, // Disable client mouse/keyboard
'aten_password_sep': ':', // Separator for ATEN iKVM password fields
'xvp_password_sep': '@', // Separator for XVP password fields 'xvp_password_sep': '@', // Separator for XVP password fields
'disconnectTimeout': 3, // Time (s) to wait for disconnection 'disconnectTimeout': 3, // Time (s) to wait for disconnection
'wsProtocols': ['binary'], // Protocols to use in the WebSocket connection 'wsProtocols': ['binary'], // Protocols to use in the WebSocket connection
@ -412,9 +415,12 @@
this._FBU.lines = 0; // RAW this._FBU.lines = 0; // RAW
this._FBU.tiles = 0; // HEXTILE this._FBU.tiles = 0; // HEXTILE
this._FBU.zlibs = []; // TIGHT zlib encoders this._FBU.zlibs = []; // TIGHT zlib encoders
this._FBU.aten_len = -1; // ATEN
this._FBU.aten_type = -1; // ATEN
this._mouse_buttonMask = 0; this._mouse_buttonMask = 0;
this._mouse_arr = []; this._mouse_arr = [];
this._rfb_tightvnc = false; this._rfb_tightvnc = false;
this._rfb_atenikvm = false;
this._convert_color = false; this._convert_color = false;
// Clear the per connection encoding stats // Clear the per connection encoding stats
@ -831,6 +837,36 @@
}, },
// authentication // authentication
_negotiate_aten_auth: function () {
var aten_sep = this._aten_password_sep;
var aten_auth = this._rfb_password.split(aten_sep);
if (aten_auth.length < 2) {
this._onPasswordRequired(this, 'ATEN iKVM credentials required (user' + aten_sep +
'password) -- got only ' + this._rfb_password);
return false;
}
this._rfb_atenikvm = true;
this._convert_color = true;
if (this._rfb_tightvnc) {
this._rfb_tightvnc = false;
} else {
this._sock.rQskipBytes(4);
}
this._sock.rQskipBytes(16);
var username = aten_auth[0];
username += new Array(24 - username.length+1).join("\x00");
var password = aten_auth.slice(1).join(aten_sep);
password += new Array(24 - password.length+1).join("\x00");
this._sock.send_string(username + password);
this._rfb_init_state = 'SecurityResult';
return true;
},
_negotiate_xvp_auth: function () { _negotiate_xvp_auth: function () {
var xvp_sep = this._xvp_password_sep; var xvp_sep = this._xvp_password_sep;
var xvp_auth = this._rfb_password.split(xvp_sep); var xvp_auth = this._rfb_password.split(xvp_sep);
@ -898,9 +934,12 @@
}, },
_negotiate_tight_auth: function () { _negotiate_tight_auth: function () {
var numTunnels; // NB(directxman12): this is only in scope within the following block,
// or if equal to zero (necessary for ATEN iKVM support)
if (!this._rfb_tightvnc) { // first pass, do the tunnel negotiation if (!this._rfb_tightvnc) { // first pass, do the tunnel negotiation
if (this._sock.rQwait("num tunnels", 4)) { return false; } if (this._sock.rQwait("num tunnels", 4)) { return false; }
var numTunnels = this._sock.rQshift32(); numTunnels = this._sock.rQshift32();
if (this._rfb_version === 3.8 && (numTunnels & 0xffff0ff0) >>> 0 === 0xaff90fb0) { return this._negotiate_aten_auth(); }
if (numTunnels > 0 && this._sock.rQwait("tunnel capabilities", 16 * numTunnels, 4)) { return false; } if (numTunnels > 0 && this._sock.rQwait("tunnel capabilities", 16 * numTunnels, 4)) { return false; }
this._rfb_tightvnc = true; this._rfb_tightvnc = true;
@ -921,6 +960,12 @@
if (this._sock.rQwait("sub auth capabilities", 16 * subAuthCount, 4)) { return false; } if (this._sock.rQwait("sub auth capabilities", 16 * subAuthCount, 4)) { return false; }
// Newer X10 Supermicro motherboards get here
if (this._rfb_version === 3.8 && numTunnels === 0 && subAuthCount === 0) {
Util.Warn("Newer ATEN iKVM detected, you may get an 'unsupported encoding 87'");
return this._negotiate_aten_auth();
}
var clientSupportedTypes = { var clientSupportedTypes = {
'STDVNOAUTH__': 1, 'STDVNOAUTH__': 1,
'STDVVNCAUTH_': 2 'STDVVNCAUTH_': 2
@ -1016,6 +1061,7 @@
_negotiate_server_init: function () { _negotiate_server_init: function () {
if (this._sock.rQwait("server initialization", 24)) { return false; } if (this._sock.rQwait("server initialization", 24)) { return false; }
if (this._rfb_atenikvm && this._sock.rQwait("ATEN server initialization", 36)) { return false; }
/* Screen size */ /* Screen size */
this._fb_width = this._sock.rQshift16(); this._fb_width = this._sock.rQshift16();
@ -1044,6 +1090,14 @@
if (this._sock.rQwait('server init name', name_length, 24)) { return false; } if (this._sock.rQwait('server init name', name_length, 24)) { return false; }
this._fb_name = Util.decodeUTF8(this._sock.rQshiftStr(name_length)); this._fb_name = Util.decodeUTF8(this._sock.rQshiftStr(name_length));
if (this._rfb_atenikvm) {
this._sock.rQskipBytes(8); // unknown
this._sock.rQskip8(); // IKVMVideoEnable
this._sock.rQskip8(); // IKVMKMEnable
this._sock.rQskip8(); // IKVMKickEnable
this._sock.rQskip8(); // VUSBEnable
}
if (this._rfb_tightvnc) { if (this._rfb_tightvnc) {
if (this._sock.rQwait('TightVNC extended server init header', 8, 24 + name_length)) { return false; } if (this._sock.rQwait('TightVNC extended server init header', 8, 24 + name_length)) { return false; }
// In TightVNC mode, ServerInit message is extended // In TightVNC mode, ServerInit message is extended
@ -1089,6 +1143,60 @@
this._convert_color = true; this._convert_color = true;
} }
// ATEN 'wisdom' from chicken-aten-ikvm:lens/lens.rb
// tested against the following Supermicro motherboards
// (use 'dmidecode -s baseboard-product-name' for model):
// - X7SPA-F
// - X8DTL
// - X8SIE-F
// - X9SCL/X9SCM
// - X9SCM-F
// - X9DRD-iF
// - X9SRE/X9SRE-3F/X9SRi/X9SRi-3F
// - X9DRL-3F/X9DRL-6F
// - X10SLD
//
// Not supported (uses encoding 87):
// - X10SL7-F
// - X10SLD-F
// - X10SLM-F
// - X10SLE
//
// Simply does not work:
// Hermon (WPMC450) [hangs at login]:
// - X7SB3-F
// - X8DTU-F
// - X8STi-3F
// Peppercon (Raritan/Kira100) [connection refused]:
// - X7SBi
//
// Thanks to Brian Rak and Erik Smit for testing
if (this._rfb_atenikvm) {
// we do not know the resolution till the first fbupdate so go large
// although, not necessary, saves a pointless full screen refresh
this._fb_width = 10000;
this._fb_height = 10000;
// lies about what it supports
Util.Warn("ATEN iKVM lies and only does 15 bit depth with RGB555");
this._convert_color = true;
this._pixelFormat.bpp = 16;
this._pixelFormat.depth = 15;
this._pixelFormat.red_max = (1 << 5) - 1;
this._pixelFormat.green_max = (1 << 5) - 1;
this._pixelFormat.blue_max = (1 << 5) - 1;
this._pixelFormat.red_shift = 10;
this._pixelFormat.green_shift = 5;
this._pixelFormat.blue_shift = 0;
// XXX(kelleyk): Doing this will break interaction with non-ATEN
// servers until the page is reloaded; it also breaks the mouse/
// keyboard portions of the test suite!
//
RFB.messages.keyEvent = RFB.messages.atenKeyEvent;
RFB.messages.pointerEvent = RFB.messages.atenPointerEvent;
}
if (this._convert_color) if (this._convert_color)
this._display.set_true_color(this._pixelFormat.true_color); this._display.set_true_color(this._pixelFormat.true_color);
this._display.resize(this._fb_width, this._fb_height); this._display.resize(this._fb_width, this._fb_height);
@ -1304,6 +1412,46 @@
msg_type = this._sock.rQshift8(); msg_type = this._sock.rQshift8();
} }
if (this._rfb_atenikvm) {
// ATEN iKVM servers use a variety of proprietary messages that
// can and do conflict with standard message types. For
// example, 4 woudl normally be a "ResizeFrameBuffer" message.
switch (msg_type) {
case 4: // Front Ground Event
Util.Debug("ATEN iKVM Front Ground Event");
this._sock.rQskipBytes(20);
return true;
case 22: // Keep Alive Event
Util.Debug("ATEN iKVM Keep Alive Event");
this._sock.rQskipBytes(1);
return true;
case 51: // Video Get Info
Util.Debug("ATEN iKVM Video Get Info");
this._sock.rQskipBytes(4);
return true;
case 55: // Mouse Get Info
Util.Debug("ATEN iKVM Mouse Get Info");
this._sock.rQskipBytes(2);
return true;
case 57: // Session Message
Util.Debug("ATEN iKVM Session Message");
this._sock.rQskipBytes(4); // u32
this._sock.rQskipBytes(4); // u32
this._sock.rQskipBytes(256);
return true;
case 60: // Get Viewer Lang
Util.Debug("ATEN iKVM Get Viewer Lang");
this._sock.rQskipBytes(8);
return true;
}
}
switch (msg_type) { switch (msg_type) {
case 0: // FramebufferUpdate case 0: // FramebufferUpdate
var ret = this._framebufferUpdate(); var ret = this._framebufferUpdate();
@ -1411,6 +1559,11 @@
this._FBU.encoding); this._FBU.encoding);
return false; return false;
} }
// ATEN uses 0x00 even when it is meant to be 0x59
if (this._rfb_atenikvm && this._FBU.encoding === 0x00) {
this._FBU.encoding = 0x59;
}
} }
this._timing.last_fbu = (new Date()).getTime(); this._timing.last_fbu = (new Date()).getTime();
@ -1564,6 +1717,7 @@
['local_cursor', 'rw', 'bool'], // Request locally rendered cursor ['local_cursor', 'rw', 'bool'], // Request locally rendered cursor
['shared', 'rw', 'bool'], // Request shared mode ['shared', 'rw', 'bool'], // Request shared mode
['view_only', 'rw', 'bool'], // Disable client mouse/keyboard ['view_only', 'rw', 'bool'], // Disable client mouse/keyboard
['aten_password_sep', 'rw', 'str'], // Separator for ATEN iKVM password fields
['xvp_password_sep', 'rw', 'str'], // Separator for XVP password fields ['xvp_password_sep', 'rw', 'str'], // Separator for XVP password fields
['disconnectTimeout', 'rw', 'int'], // Time (s) to wait for disconnection ['disconnectTimeout', 'rw', 'int'], // Time (s) to wait for disconnection
['wsProtocols', 'rw', 'arr'], // Protocols to use in the WebSocket connection ['wsProtocols', 'rw', 'arr'], // Protocols to use in the WebSocket connection
@ -1659,6 +1813,70 @@
sock.flush(); sock.flush();
}, },
atenKeyEvent: function (sock, keysym, down) {
var ks = XK2HID[keysym];
var buff = sock._sQ;
var offset = sock._sQlen;
buff[offset] = 4;
buff[offset + 1] = 0;
buff[offset + 2] = down;
buff[offset + 3] = 0;
buff[offset + 4] = 0;
buff[offset + 5] = (ks >> 24);
buff[offset + 6] = (ks >> 16);
buff[offset + 7] = (ks >> 8);
buff[offset + 8] = ks;
buff[offset + 9] = 0;
buff[offset + 10] = 0;
buff[offset + 11] = 0;
buff[offset + 12] = 0;
buff[offset + 13] = 0;
buff[offset + 14] = 0;
buff[offset + 15] = 0;
buff[offset + 16] = 0;
buff[offset + 17] = 0;
sock._sQlen += 18;
},
atenPointerEvent: function (sock, x, y, mask) {
var buff = sock._sQ;
var offset = sock._sQlen;
buff[offset] = 5;
buff[offset + 1] = 0;
buff[offset + 2] = mask;
buff[offset + 3] = x >> 8;
buff[offset + 4] = x;
buff[offset + 5] = y >> 8;
buff[offset + 6] = y;
buff[offset + 7] = 0;
buff[offset + 8] = 0;
buff[offset + 9] = 0;
buff[offset + 10] = 0;
buff[offset + 11] = 0;
buff[offset + 12] = 0;
buff[offset + 13] = 0;
buff[offset + 14] = 0;
buff[offset + 15] = 0;
buff[offset + 16] = 0;
buff[offset + 17] = 0;
sock._sQlen += 18;
},
pointerEvent: function (sock, x, y, mask) { pointerEvent: function (sock, x, y, mask) {
var buff = sock._sQ; var buff = sock._sQ;
var offset = sock._sQlen; var offset = sock._sQlen;
@ -2546,6 +2764,86 @@
compress_lo: function () { compress_lo: function () {
Util.Error("Server sent compress level pseudo-encoding"); Util.Error("Server sent compress level pseudo-encoding");
},
ATEN: function () {
if (this._FBU.aten_len === -1) {
this._FBU.bytes = 8;
if (this._sock.rQwait("ATEN", this._FBU.bytes)) { return false; }
this._FBU.bytes = 0;
this._sock.rQskipBytes(4);
this._FBU.aten_len = this._sock.rQshift32();
if (this._FBU.width === 64896 && this._FBU.height === 65056) {
Util.Info("ATEN iKVM screen is probably off");
if (this._FBU.aten_len !== 10 && this._FBU.aten_len !== 0) {
Util.Debug(">> ATEN iKVM screen off (aten_len="+this._FBU.aten_len+")");
this._fail('expected aten_len to be 10 when screen is off');
}
this._FBU.aten_len = 0;
return true;
}
if (this._fb_width !== this._FBU.width && this._fb_height !== this._FBU.height) {
Util.Debug(">> ATEN resize desktop");
this._fb_width = this._FBU.width;
this._fb_height = this._FBU.height;
this._onFBResize(this, this._fb_width, this._fb_height);
this._display.resize(this._fb_width, this._fb_height);
Util.Debug("<< ATEN resize desktop");
}
}
if (this._FBU.aten_type === -1) {
this._FBU.bytes = 10;
if (this._sock.rQwait("ATEN", this._FBU.bytes)) { return false; }
this._FBU.bytes = 0;
this._FBU.aten_type = this._sock.rQshift8();
this._sock.rQskip8();
this._sock.rQskipBytes(4); // number of subrects
if (this._FBU.aten_len !== this._sock.rQshift32()) {
return this._fail('ATEN RAW len mis-match');
}
this._FBU.aten_len -= 10;
}
while (this._FBU.aten_len > 0) {
switch (this._FBU.aten_type) {
case 0: // Subrects
this._FBU.bytes = 6 + (16 * 16 * this._pixelFormat.Bpp); // at least a subrect
if (this._sock.rQwait("ATEN", this._FBU.bytes)) { return false; }
var a = this._sock.rQshift16();
var b = this._sock.rQshift16();
var y = this._sock.rQshift8();
var x = this._sock.rQshift8();
this._convert_color_and_copy(this._destBuff, this._sock.rQshiftBytes(this._FBU.bytes - 6));
this._display.blitImage(x * 16, y * 16, 16, 16, this._destBuff, 0, true, false);
this._FBU.aten_len -= this._FBU.bytes;
this._FBU.bytes = 0;
break;
case 1: // RAW
var olines = (this._FBU.lines === 0) ? this._FBU.height : this._FBU.lines;
this._encHandlers.RAW();
this._FBU.aten_len -= (olines - this._FBU.lines) * this._FBU.width * this._pixelFormat.Bpp;
if (this._FBU.bytes > 0) return false;
break;
default:
return this._fail('unknown ATEN type: '+this._FBU.aten_type);
}
}
if (this._FBU.aten_len < 0) {
this._fail('aten_len dropped below zero');
}
if (this._FBU.aten_type === 0) {
this._FBU.rects--;
}
this._FBU.aten_len = -1;
this._FBU.aten_type = -1;
return true;
} }
}; };
})(); })();

View File

@ -985,6 +985,40 @@ describe('Remote Frame Buffer Protocol Client', function() {
expect(client._fail).to.have.been.calledOnce; expect(client._fail).to.have.been.calledOnce;
}); });
}); });
describe('ATEN iKVM Authentication Handler', function () {
var client;
beforeEach(function () {
client = make_rfb();
client.connect('host', 8675);
client._sock._websocket._open();
client._rfb_init_state = 'Security';
client._rfb_version = 3.8;
send_security(16, client);
client._sock._websocket._get_sent_data(); // skip the security reply
client._rfb_password = 'test1:test2';
});
var auth = [
116, 101, 115, 116, 49, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
116, 101, 115, 116, 50, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0];
it('via old style method', function () {
client._sock._websocket._receive_data(new Uint8Array([
0xaf, 0xf9, 0x0f, 0xb0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0]));
expect(client._rfb_tightvnc).to.be.false;
expect(client._rfb_atenikvm).to.be.true;
expect(client._sock).to.have.sent(auth);
});
});
}); });
describe('SecurityResult', function () { describe('SecurityResult', function () {
@ -1132,6 +1166,25 @@ describe('Remote Frame Buffer Protocol Client', function() {
expect(client._rfb_connection_state).to.equal('connected'); expect(client._rfb_connection_state).to.equal('connected');
}); });
it('should handle an ATEN iKVM server initialization', function () {
RFB.ATEN_INIT_WIDTH = 1;
RFB.ATEN_INIT_HEIGHT = 1;
client._rfb_atenikvm = true;
send_server_init({ true_color: 1, bpp: 32 }, client);
client._sock._websocket._receive_data(new Uint8Array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]));
expect(client._pixelFormat.bpp).to.equal(16);
expect(client._pixelFormat.depth).to.equal(15);
expect(client._pixelFormat.red_max).to.equal(31);
expect(client._pixelFormat.green_max).to.equal(31);
expect(client._pixelFormat.blue_max).to.equal(31);
expect(client._pixelFormat.red_shift).to.equal(10);
expect(client._pixelFormat.green_shift).to.equal(5);
expect(client._pixelFormat.blue_shift).to.equal(0);
expect(client._pixelFormat.Bpp).to.equal(2);
expect(client._pixelFormat.Bdepth).to.equal(2);
expect(client._rfb_connection_state).to.equal('connected');
});
it('should call the resize callback and resize the display', function () { it('should call the resize callback and resize the display', function () {
client.set_onFBResize(sinon.spy()); client.set_onFBResize(sinon.spy());
sinon.spy(client._display, 'resize'); sinon.spy(client._display, 'resize');
@ -1667,6 +1720,97 @@ describe('Remote Frame Buffer Protocol Client', function() {
// TODO(directxman12): test this // TODO(directxman12): test this
}); });
describe('the ATEN encoding handler', function () {
var client;
beforeEach(function () {
client = make_rfb();
client.connect('host', 8675);
client._sock._websocket._open();
client._rfb_connection_state = 'connected';
client._fb_name = 'some device';
client._rfb_atenikvm = true;
// start large, then go small
client._fb_width = 10000;
client._fb_height = 10000;
client._display._fb_width = 10000;
client._display._fb_height = 10000;
client._display._viewportLoc.w = 10000;
client._display._viewportLoc.h = 10000;
client._convert_color = true;
client._pixelFormat.Bpp = 2;
client._pixelFormat.big_endian = false;
client._pixelFormat.red_shift = 10;
client._pixelFormat.red_max = 31;
client._pixelFormat.green_shift = 5;
client._pixelFormat.green_max = 31;
client._pixelFormat.blue_shift = 0;
client._pixelFormat.blue_max = 31;
client._destBuff = new Uint8Array(client._fb_width * client._fb_height * 4);
});
var aten_target_data_arr = [0xa8, 0xe8, 0xf8, 0xff];
var aten_target_data;
before(function () {
for (var i = 0; i < 10; i++) {
aten_target_data_arr = aten_target_data_arr.concat(aten_target_data_arr);
}
aten_target_data = new Uint8Array(aten_target_data_arr);
});
it('should handle subtype subrect encoding', function () {
var info = [{ x: 0, y: 0, width: 32, height: 32, encoding: 0x59 }];
var rect = [];
push32(rect, 0); // padding
push32(rect, 2082); // 10 + 32x32x2Bpp + 6*(num of subrects)
push8(rect, 0); // type
push8(rect, 0); // padding
push32(rect, 4); // num of subrects (32/16)*(32/16)
push32(rect, 2082); // length (again)
for (var y = 0; y < 2; y++) {
for (var x = 0; x < 2; x++) {
push16(rect, 0); // a
push16(rect, 0); // b
push8(rect, y);
push8(rect, x);
for (var i = 0; i < 16*16; i++) {
push16(rect, 0xbf57);
}
}
}
send_fbu_msg(info, [rect], client);
expect(client._display).to.have.displayed(aten_target_data);
});
it('should handle subtype RAW encoding', function () {
// do not use encoding=0x59 here, as rfb.js should override it
var info = [{ x: 0, y: 0, width: 32, height: 32, encoding: 0x00 }];
var rect = [];
push32(rect, 0); // padding
push32(rect, 2058); // 10 + 32x32x2Bpp
push8(rect, 1); // type
push8(rect, 0); // padding
push32(rect, 0); // padding
push32(rect, 2058); // length (again)
for (var i = 0; i < 32*32; i++) {
push16(rect, 0xbf57);
}
send_fbu_msg(info, [rect], client);
expect(client._display).to.have.displayed(aten_target_data);
});
});
it('should handle the DesktopSize pseduo-encoding', function () { it('should handle the DesktopSize pseduo-encoding', function () {
client.set_onFBResize(sinon.spy()); client.set_onFBResize(sinon.spy());
sinon.spy(client._display, 'resize'); sinon.spy(client._display, 'resize');