diff --git a/include/web-socket-js/flash-src/WebSocket.as b/include/web-socket-js/flash-src/WebSocket.as index df254b52..03c5d8b0 100644 --- a/include/web-socket-js/flash-src/WebSocket.as +++ b/include/web-socket-js/flash-src/WebSocket.as @@ -219,10 +219,32 @@ public class WebSocket extends EventDispatcher { } } else { if (buffer[pos] == 0xff) { + //if (buffer.bytesAvailable > 1) { if (buffer.readByte() != 0x00) { close(); main.fatal("data must start with \\x00"); } + /* + var data:String = "", byte:uint; + while (buffer.bytesAvailable > 1) { + byte = buffer[buffer.position]; + if (byte === 0x00) { + // readUTFBytes mishandles 0x00 + data = data + "\x00"; + buffer.position++; + } else if (byte === 0xff) { + // End of WebSocket frame + //ExternalInterface.call("console.log", "[WebSocket] early 0xff found"); + break; + } else if ((byte & 0x80) === 0x00) { + // One UTF-8 input byte to one output byte + data = data + buffer.readUTFBytes(1); + } else { + // Assume two UTF-8 input bytes to one output byte + data = data + buffer.readUTFBytes(2); + } + } + */ var data:String = buffer.readUTFBytes(pos - 1); main.log("received: " + data); dispatchEvent(new WebSocketMessageEvent("message", encodeURIComponent(data))); diff --git a/include/web-socket-js/flash-src/WebSocketMain.swf b/include/web-socket-js/flash-src/WebSocketMain.swf index 91da2f8c..c3a0ebd1 100644 Binary files a/include/web-socket-js/flash-src/WebSocketMain.swf and b/include/web-socket-js/flash-src/WebSocketMain.swf differ diff --git a/tests/wsencoding.html b/tests/wsencoding.html new file mode 100644 index 00000000..a783467a --- /dev/null +++ b/tests/wsencoding.html @@ -0,0 +1,155 @@ + + + WebSockets Test + + + + Host:   + Port:   + Encrypt:   +   + +
+ Messages:
+ + + + + + + + + + + + + + diff --git a/tests/wsencoding.py b/tests/wsencoding.py new file mode 100755 index 00000000..f29c2e9e --- /dev/null +++ b/tests/wsencoding.py @@ -0,0 +1,84 @@ +#!/usr/bin/python + +''' +WebSocket server-side load test program. Sends and receives traffic +that has a random payload (length and content) that is checksummed and +given a sequence number. Any errors are reported and counted. +''' + +import sys, os, socket, ssl, time, traceback +import random, time +from base64 import b64encode, b64decode +from codecs import utf_8_encode, utf_8_decode +from select import select + +sys.path.insert(0,os.path.dirname(__file__) + "/../") +from websocket import * + +buffer_size = 65536 +recv_cnt = send_cnt = 0 + + +def check(buf): + + if buf[0] != '\x00' or buf[-1] != '\xff': + raise Exception("Invalid WS packet") + + for decoded in decode(buf): + nums = [ord(c) for c in decoded] + print "Received nums: ", nums + + return + + +def responder(client): + cpartial = "" + socks = [client] + sent = False + received = False + + while True: + ins, outs, excepts = select(socks, socks, socks, 1) + if excepts: raise Exception("Socket exception") + + if client in ins: + buf = client.recv(buffer_size) + if len(buf) == 0: raise Exception("Client closed") + received = True + #print "Client recv: %s (%d)" % (repr(buf[1:-1]), len(buf)) + if buf[-1] == '\xff': + if cpartial: + err = check(cpartial + buf) + cpartial = "" + else: + err = check(buf) + if err: + print err + else: + print "received partitial" + cpartial = cpartial + buf + + if received and not sent and client in outs: + sent = True + #nums = "".join([unichr(c) for c in range(0,256)]) + #nums = "".join([chr(c) for c in range(1,128)]) + #nums = nums + chr(194) + chr(128) + chr(194) + chr(129) + #nums = "".join([chr(c) for c in range(0,256)]) + nums = "\x81\xff" + nums = nums + "".join([chr(c) for c in range(0,256,4)]) + nums = nums + "\x00\x40\x41\xff\x81" +# print nums + client.send(encode(nums)) +# client.send("\x00" + nums + "\xff") +# print "Sent characters 0-255" +# #print "Client send: %s (%d)" % (repr(nums), len(nums)) + +if __name__ == '__main__': + try: + if len(sys.argv) < 2: raise + listen_port = int(sys.argv[1]) + except: + print "Usage: " + sys.exit(1) + + start_server(listen_port, responder) diff --git a/vnc.js b/vnc.js index c8a02ff4..31044f47 100644 --- a/vnc.js +++ b/vnc.js @@ -53,6 +53,7 @@ RFB = { ws : null, // Web Socket object sendID : null, use_seq : false, +b64encode : true, // Receive and send queues RQ : [], // Receive Queue @@ -789,6 +790,27 @@ clientCutText: function (text) { * Utility routines */ +encode_message: function(arr) { + if (RFB.b64encode) { + RFB.SQ = RFB.SQ + Base64.encode(arr); + } else { + RFB.SQ = RFB.SQ + arr.map(function (num) { + return String.fromCharCode(num); } ).join(''); + } +}, + +decode_message: function(data, offset) { + //console.log(">> decode_message: " + data); + if (RFB.b64encode) { + RFB.RQ = RFB.RQ.concat(Base64.decode(data, offset)); + } else { + RFB.RQ = RFB.RQ.concat(data.split('').slice(offset). + map(function (chr) { + return (chr.charCodeAt(0) % 256); })); + } + //console.log(">> decode_message, RQ: " + RFB.RQ); +}, + recv_message: function(e) { //console.log(">> recv_message"); @@ -796,7 +818,7 @@ recv_message: function(e) { if (RFB.use_seq) { RFB.recv_message_reorder(e); } else { - RFB.RQ = RFB.RQ.concat(Base64.decode(e.data, 0)); + RFB.decode_message(e.data, 0); RFB.handle_message(); } @@ -819,7 +841,7 @@ recv_message_reorder: function(e) { offset = e.data.indexOf(":") + 1; seq_num = parseInt(e.data.substr(0, offset-1), 10); if (RFB.RQ_seq_num === seq_num) { - RFB.RQ = RFB.RQ.concat(Base64.decode(e.data, offset)); + RFB.decode_message(e.data, offset); RFB.RQ_seq_num++; } else { console.warn("sequence number mismatch: expected " + @@ -838,9 +860,7 @@ recv_message_reorder: function(e) { /* Remove it from reorder queue, decode it and * add it to the receive queue */ console.log("Found re-ordered packet seq_num " + seq_num); - RFB.RQ = RFB.RQ.concat( - Base64.decode(RFB.RQ_reorder.splice(i, 1)[0], - offset)); + RFB.decode_message(RFB.RQ_reorder.splice(i, 1)[0], offset); RFB.RQ_seq_num++; i = 0; // Start search again for next one } else { @@ -892,8 +912,7 @@ send_string: function (str) { send_array: function (arr) { //console.log(">> send_array: " + arr); - //console.log(">> send_array: " + Base64.encode(arr)); - RFB.SQ = RFB.SQ + Base64.encode(arr); + RFB.encode_message(arr); if (RFB.ws.bufferedAmount === 0) { RFB.ws.send(RFB.SQ); RFB.SQ = ""; @@ -1097,15 +1116,21 @@ updateState: function(state, statusMsg) { init_ws: function () { console.log(">> init_ws"); - var uri = ""; + var uri = "", vars = []; if (RFB.encrypt) { uri = "wss://"; } else { uri = "ws://"; } - uri += RFB.host + ":" + RFB.port + "/?b64encode"; + uri += RFB.host + ":" + RFB.port + "/"; + if (RFB.b64encode) { + vars.push("b64encode"); + } if (RFB.use_seq) { - uri += "&seq_num"; + vars.push("seq_num"); + } + if (vars.length > 0) { + uri += "?" + vars.join("&"); } console.log("connecting to " + uri); RFB.ws = new WebSocket(uri); diff --git a/websocket.py b/websocket.py index c7f68b44..c9b33c54 100755 --- a/websocket.py +++ b/websocket.py @@ -33,16 +33,26 @@ def traffic(token="."): def decode(buf): """ Parse out WebSocket packets. """ if buf.count('\xff') > 1: - return [b64decode(d[1:]) for d in buf.split('\xff')] + if client_settings["b64encode"]: + return [b64decode(d[1:]) for d in buf.split('\xff')] + else: + # Modified UTF-8 decode + return [d[1:].replace("\xc4\x80", "\x00").decode('utf-8').encode('latin-1') for d in buf.split('\xff')] else: - return [b64decode(buf[1:-1])] + if client_settings["b64encode"]: + return [b64decode(buf[1:-1])] + else: + return [buf[1:-1].replace("\xc4\x80", "\x00").decode('utf-8').encode('latin-1')] def encode(buf): global send_seq - if client_settings.get("b64encode"): + if client_settings["b64encode"]: buf = b64encode(buf) + else: + # Modified UTF-8 encode + buf = buf.decode('latin-1').encode('utf-8').replace("\x00", "\xc4\x80") - if client_settings.get("seq_num"): + if client_settings["seq_num"]: send_seq += 1 return "\x00%d:%s\xff" % (send_seq-1, buf) else: @@ -81,7 +91,7 @@ def do_handshake(sock): # Parse settings from the path cvars = path.partition('?')[2].partition('#')[0].split('&') - client_settings = {} + client_settings = {'b64encode': None, 'seq_num': None} for cvar in [c for c in cvars if c]: name, _, value = cvar.partition('=') client_settings[name] = value and value or True