First RFB protocol stub implementation.

- Requires wsproxy to proxy to the VNC server.
This commit is contained in:
Joel Martin 2010-04-01 11:36:22 -05:00
parent f9d4566547
commit 65e27ddd4c
4 changed files with 537 additions and 4 deletions

147
rfb_notes Normal file
View File

@ -0,0 +1,147 @@
5.1.1 ProtocolVersion: 12, 12 bytes
- Sent by server, max supported
12 ascii - "RFB 003.008\n"
- Response by client, version to use
12 ascii - "RFB 003.003\n"
5.1.2 Authentication: >=4, [16, 4] bytes
- Sent by server
CARD32 - authentication-scheme
0 - connection failed
CARD32 - length
length - reason
1 - no authentication
2 - VNC authentication
16 CARD8 - challenge (random bytes)
- Response by client (if VNC authentication)
16 CARD8 - client encrypts the challenge with DES, using user
password as key, sends resulting 16 byte response
- Response by server (if VNC authentication)
CARD32 - 0 - OK
1 - failed
2 - too-many
5.1.3 ClientInitialisation: 1 byte
- Sent by client
CARD8 - shared-flag, 0 exclusive, non-zero shared
5.1.4 ServerInitialisation: >=24 bytes
- Sent by server
CARD16 - framebuffer-width
CARD16 - framebuffer-height
16 byte PIXEL_FORMAT - server-pixel-format
CARD8 - bits-per-pixel
CARD8 - depth
CARD8 - big-endian-flag, non-zero is big endian
CARD8 - true-color-flag, non-zero then next 6 apply
CARD16 - red-max
CARD16 - green-max
CARD16 - blue-max
CARD8 - red-shift
CARD8 - green-shift
CARD8 - blue-shift
3 bytes - padding
CARD32 - name-length
CARD8[length] - name-string
Client to Server Messages:
5.2.1 SetPixelFormat: 20 bytes
CARD8: 0 - message-type
...
5.2.2 FixColourMapEntries: >=6 bytes
CARD8: 1 - message-type
...
5.2.3 SetEncodings: >=8 bytes
CARD8: 2 - message-type
CARD8 - padding
CARD16 - numer-of-encodings
CARD32 - encoding-type in preference order
0 - raw
1 - copy-rectangle
2 - RRE
4 - CoRRE
5 - hextile
5.2.4 FramebufferUpdateRequest (10 bytes)
CARD8: 3 - message-type
CARD8 - incremental (0 for full-update, non-zero for incremental)
CARD16 - x-position
CARD16 - y-position
CARD16 - width
CARD16 - height
5.2.5 KeyEvent: 8 bytes
CARD8: 4 - message-type
CARD8 - down-flag
2 bytes - padding
CARD32 - key (X-Windows keysym values)
5.2.6 PointerEvent: 6 bytes
CARD8: 5 - message-type
CARD8 - button-mask
CARD16 - x-position
CARD16 - y-position
5.2.7 ClientCutText: >=9 bytes
CARD8: 6 - message-type
...
Server to Client Messages:
5.3.1 FramebufferUpdate
CARD8: 0 - message-type
1 byte - padding
CARD16 - number-of-rectangles
CARD16 - x-position
CARD16 - y-position
CARD16 - width
CARD16 - height
CARD16 - encoding-type:
0 - raw
1 - copy rectangle
2 - RRE
4 - CoRRE
5 - hextile
raw:
- width x height pixel values
copy rectangle:
CARD16 - src-x-position
CARD16 - src-y-position
RRE:
CARD32 - N number-of-subrectangles
Nxd bytes - background-pixel-value (d bits-per-pixel)
...
5.3.2 SetColourMapEntries (no support)
CARD8: 1 - message-type
...
5.3.3 Bell
CARD8: 2 - message-type
5.3.4 ServerCutText
CARD8: 3 - message-type

38
vnc.html Normal file
View File

@ -0,0 +1,38 @@
<html>
<body onload="draw();">
VNC Window:<br>
<canvas id="vnc" width="800" height="600">
Canvas not supported.
</canvas>
<br><br>
Debug:
<div id="debug"></div>
</body>
<script src="include/mootools.js"></script>
<script src="include/mootools-more.js"></script>
<script src="vnc.js"></script>
<script type="text/javascript">
function connect() {
debug(">> connect");
var uri = new URI(window.location);
var host = uri.getData("host");
var port = uri.getData("port");
if ((!host) || (!port)) {
debug("must set host and port");
return;
}
init_ws(host, port);
debug("<< connect");
}
window.onload = connect();
</script>
</html>

339
vnc.js Normal file
View File

@ -0,0 +1,339 @@
debug("here0");
var ws = null;
var vnc_host = '';
var vnc_port = 5900;
var rfb_state = 'ProtocolVersion';
var rfb_shared = 1;
var fb_width = 0;
var fb_height = 0;
var fb_name = "";
function card8(num) {
return String.fromCharCode(num);
}
String.prototype.card8 = function (pos) {
return this.charCodeAt(pos);
}
function card16(num) {
return String.fromCharCode(num >> 8) +
String.fromCharCode(num & 0xFF);
}
String.prototype.card16 = function (pos) {
debug("card16 0: " + this.charCodeAt(pos));
debug("card16 1: " + this.charCodeAt(pos+1));
return (this.charCodeAt(pos) << 8) +
(this.charCodeAt(pos+1) );
}
function card32(num) {
return String.fromCharCode( num >> 24) +
String.fromCharCode((num >> 16) & 0xFF) +
String.fromCharCode((num >> 8) & 0xFF) +
String.fromCharCode( num & 0xFF);
}
String.prototype.card32 = function (pos) {
debug("card32 0: " + this.charCodeAt(pos));
debug("card32 1: " + this.charCodeAt(pos+1));
debug("card32 2: " + this.charCodeAt(pos+2));
debug("card32 3: " + this.charCodeAt(pos+3));
return (this.charCodeAt(pos) << 24) +
(this.charCodeAt(pos+1) << 16) +
(this.charCodeAt(pos+2) << 8) +
(this.charCodeAt(pos+3) );
}
function debug(str) {
cell = $('debug');
cell.innerHTML += str + "<br/>";
}
/*
* Server message handlers
*/
/* RFB/VNC initialisation */
function rfb_init_msg(data) {
debug(">> rfb_init_msg");
switch (rfb_state) {
case 'ProtocolVersion' :
debug("ProtocolVersion: " + data)
if (data.length != 12) {
debug("Invalid protocol version from server");
rfb_state = 'reset';
return;
}
ws.send("RFB 003.003\n");
rfb_state = 'Authentication';
break;
case 'Authentication' :
debug("Authentication")
if (data.length != 4) {
debug("Invalid auth scheme");
rfb_state = 'reset';
return;
}
var scheme = data.card32(0);
debug("Auth scheme: " + scheme);
switch (scheme) {
case 0: // connection failed
var strlen = data.card32(4);
var reason = data.substr(8, strlen);
debug("auth failed: " + reason);
rfb_state = "reset";
return;
case 1: // no authentication
ws.send(card8(rfb_shared)); // ClientInitialisation
rfb_state = "ServerInitialisation";
break;
case 2: // VNC authentication
var challenge = data.substr(4, 16);
// TODO:
//var crypt = des(challenge, password);
//ws.send(crypt);
rfb_state = "Authentication-VNC";
break;
}
break;
case 'Authentication-VNC' :
debug("Authentication-VNC")
if (data.length != 4) {
debug("Invalid server auth response");
rfb_state = 'reset';
return;
}
var resp = data.card32(0);
switch (resp) {
case 0: // OK
debug("Authentication OK");
break;
case 1: // failed
debug("Authentication failed");
rfb_state = "reset";
return;
case 2: // too-many
debug("Too many authentication attempts");
rfb_state = "reset";
return;
}
ws.send(card8(rfb_shared)); // ClientInitialisation
rfb_state = "ServerInitialisation";
break;
case 'ServerInitialisation' :
debug("ServerInitialisation")
if (data.length < 24) {
debug("Invalid server initialisation");
rfb_state = 'reset';
return;
}
fb_width = data.card16(0);
fb_height = data.card16(2);
var name_length = data.card32(20);
fb_name = data.substr(24, name_length);
debug("Screen size: " + fb_width + "x" + fb_height);
debug("Name: " + fb_name);
rfb_state = 'normal';
break;
}
debug("<< rfb_init_msg");
}
/* Normal RFB/VNC messages */
function rfb_msg(data) {
debug(">> rfb_msg");
var msg_type = data.card8(0);
switch (msg_type) {
case 0: // FramebufferUpdate
debug("FramebufferUpdate");
break;
case 1: // SetColourMapEntries
debug("SetColourMapEntries");
break;
case 2: // Bell
debug("Bell");
break;
case 3: // ServerCutText
debug("ServerCutText");
break;
default:
debug("Unknown server message type: " + msg_type);
break;
}
debug("<< rfb_msg");
}
/*
* Client message routines
*/
function setPixelFormat() {
}
function fixColourMapEntries() {
}
function setEncodings() {
}
function fbUpdateRequest() {
}
function keyEvent() {
}
function pointerEvent() {
}
function clientCutText() {
}
/*
* Setup routines
*/
function _init_ws() {
debug(">> _init_ws");
var uri = "ws://" + vnc_host + ":" + vnc_port;
debug("connecting to " + uri);
ws = new WebSocket(uri);
ws.onmessage = function(e) {
debug(">> onmessage");
if (rfb_state != 'normal') {
rfb_init_msg(e.data);
} else {
rfb_msg(e.data);
}
if (rfb_state == 'reset') {
/* close and reset connection */
ws.close();
_init_ws();
}
debug("<< onmessage");
};
ws.onopen = function(e) {
debug(">> onopen");
rfb_state = "ProtocolVersion";
debug("<< onopen");
};
ws.onclose = function(e) {
debug(">> onclose");
rfb_state = "closed";
debug("<< onclose");
}
debug("<< _init_ws");
}
function init_ws(host, port) {
debug(">> init_ws");
vnc_host = host;
vnc_port = port;
if (ws) {
ws.close();
}
_init_ws();
debug("<< init_ws");
}
/*
function draw() {
var canvas = document.getElementById('vnc');
if (! canvas.getContext) return;
var ctx = canvas.getContext('2d');
ctx.fillStyle = "rgb(50,50,50)";
ctx.fillRect(0, 0, 800, 600);
var img = new Image();
img.src = "head_ani2.gif"
ctx.drawImage(img, 10, 10);
ctx.drawImage(canvas, 20, 20, 30, 30, 70, 70, 30, 30);
}
function draw2() {
var canvas = document.getElementById('tutorial');
if (! canvas.getContext) return;
var ctx = canvas.getContext('2d');
roundedRect(ctx,12,12,150,150,15);
roundedRect(ctx,19,19,150,150,9);
roundedRect(ctx,53,53,49,33,10);
roundedRect(ctx,53,119,49,16,6);
roundedRect(ctx,135,53,49,33,10);
roundedRect(ctx,135,119,25,49,10);
ctx.beginPath();
ctx.arc(37,37,13,Math.PI/7,-Math.PI/7,true);
ctx.lineTo(31,37);
ctx.fill();
for(var i=0;i<8;i++){
ctx.fillRect(51+i*16,35,4,4);
}
for(i=0;i<6;i++){
ctx.fillRect(115,51+i*16,4,4);
}
for(i=0;i<8;i++){
ctx.fillRect(51+i*16,99,4,4);
}
ctx.beginPath();
ctx.moveTo(83,116);
ctx.lineTo(83,102);
ctx.bezierCurveTo(83,94,89,88,97,88);
ctx.bezierCurveTo(105,88,111,94,111,102);
ctx.lineTo(111,116);
ctx.lineTo(106.333,111.333);
ctx.lineTo(101.666,116);
ctx.lineTo(97,111.333);
ctx.lineTo(92.333,116);
ctx.lineTo(87.666,111.333);
ctx.lineTo(83,116);
ctx.fill();
ctx.fillStyle = "white";
ctx.beginPath();
ctx.moveTo(91,96);
ctx.bezierCurveTo(88,96,87,99,87,101);
ctx.bezierCurveTo(87,103,88,106,91,106);
ctx.bezierCurveTo(94,106,95,103,95,101);
ctx.bezierCurveTo(95,99,94,96,91,96);
ctx.moveTo(103,96);
ctx.bezierCurveTo(100,96,99,99,99,101);
ctx.bezierCurveTo(99,103,100,106,103,106);
ctx.bezierCurveTo(106,106,107,103,107,101);
ctx.bezierCurveTo(107,99,106,96,103,96);
ctx.fill();
ctx.fillStyle = "black";
ctx.beginPath();
ctx.arc(101,102,2,0,Math.PI*2,true);
ctx.fill();
ctx.beginPath();
ctx.arc(89,102,2,0,Math.PI*2,true);
ctx.fill();
}
function roundedRect(ctx,x,y,width,height,radius){
ctx.beginPath();
ctx.moveTo(x,y+radius);
ctx.lineTo(x,y+height-radius);
ctx.quadraticCurveTo(x,y+height,x+radius,y+height);
ctx.lineTo(x+width-radius,y+height);
ctx.quadraticCurveTo(x+width,y+height,x+width,y+height-radius);
ctx.lineTo(x+width,y+radius);
ctx.quadraticCurveTo(x+width,y,x+width-radius,y);
ctx.lineTo(x+radius,y);
ctx.quadraticCurveTo(x,y,x,y+radius);
ctx.stroke();
}
*/
debug("here10");

View File

@ -20,6 +20,10 @@ def handshake(client):
_, host = req_lines[3].split(" ")
client.send(server_handshake % (origin, host, path))
def traffic(token="."):
sys.stdout.write(token)
sys.stdout.flush()
def proxy(client, target):
cqueue = []
tqueue = []
@ -33,23 +37,28 @@ def proxy(client, target):
buf = client.recv(1024)
if len(buf) == 0: raise Exception("Client closed")
tqueue.append(buf[1:-1])
print "Client recv: %s (%d)" % (buf[1:-1], len(buf))
#print "Client recv: %s (%d)" % (buf[1:-1], len(buf))
traffic("}")
if target in ins:
buf = target.recv(1024)
if len(buf) == 0: raise Exception("Target closed")
cqueue.append("\x00" + buf + "\xff")
print "Target recv: %s (%d)" % (buf, len(buf))
#print "Target recv: %s (%d)" % (buf, len(buf))
traffic("{")
if cqueue and client in outs:
while cqueue:
print "Client send: %s" % cqueue[0]
#print "Client send: %s" % cqueue[0]
client.send(cqueue.pop(0))
traffic("<")
if tqueue and target in outs:
while tqueue:
print "Target send: %s" % tqueue[0]
#print "Target send: %s" % tqueue[0]
sys.stdout.flush()
target.send(tqueue.pop(0))
traffic(">")
def start_server(listen_port, target_host, target_port):
lsock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)