aboutsummaryrefslogtreecommitdiff
path: root/third_party/websockify
diff options
context:
space:
mode:
authorAlon Zakai <alonzakai@gmail.com>2012-10-30 10:52:24 -0700
committerAlon Zakai <alonzakai@gmail.com>2012-10-30 10:52:24 -0700
commitfcb6c33bac763d7a8246674eff33b599c28c1b6e (patch)
tree15fc927f186a0de2476729a1c1e1906478f82e7e /third_party/websockify
parent5ba8486b1cd87d31f3d911cbfeb126717e0865b1 (diff)
update websockify to latest trunk
Diffstat (limited to 'third_party/websockify')
-rw-r--r--third_party/websockify/CHANGES.txt5
-rw-r--r--third_party/websockify/LICENSE.txt11
-rw-r--r--third_party/websockify/README.md21
-rw-r--r--third_party/websockify/include/base64.js99
-rw-r--r--third_party/websockify/include/util.js62
-rw-r--r--third_party/websockify/include/websock.js25
-rw-r--r--third_party/websockify/include/webutil.js88
-rwxr-xr-x[-rw-r--r--]third_party/websockify/other/websockify.js93
-rw-r--r--third_party/websockify/setup.py2
-rw-r--r--third_party/websockify/websockify/websocket.py56
-rwxr-xr-xthird_party/websockify/websockify/websocketproxy.py45
11 files changed, 343 insertions, 164 deletions
diff --git a/third_party/websockify/CHANGES.txt b/third_party/websockify/CHANGES.txt
index cbd3f73f..0ed52b80 100644
--- a/third_party/websockify/CHANGES.txt
+++ b/third_party/websockify/CHANGES.txt
@@ -1,6 +1,11 @@
Changes
=======
+0.2.1 - Oct 15, 2012
+--------------------
+
+ * re-released with updated version number
+
0.2.0 - Sep 17, 2012
--------------------
diff --git a/third_party/websockify/LICENSE.txt b/third_party/websockify/LICENSE.txt
index 916a77a0..f1648dec 100644
--- a/third_party/websockify/LICENSE.txt
+++ b/third_party/websockify/LICENSE.txt
@@ -1,11 +1,16 @@
websockify is licensed under the LGPL version 3 (see docs/LICENSE.GPL-3 and
docs/LICENSE.LGPL-3) with the following exceptions:
- include/base64.js : Choice of MIT 1.1, GPL-2 or LGPL-2.1
-
- include/web-socket-js/ : New BSD license. Source code at
+ include/websock.js : MPL 2.0
+
+ include/base64.js : MPL 2.0
+
+ include/des.js : Various BSD style licenses
+
+ include/web-socket-js/ : New BSD license (3-clause). Source code at
https://github.com/gimite/web-socket-js
other/kumina.c : Simplified BSD license (2 clause).
Original source at
https://github.com/kumina/wsproxy
+
diff --git a/third_party/websockify/README.md b/third_party/websockify/README.md
index 48c790af..2cfac0f5 100644
--- a/third_party/websockify/README.md
+++ b/third_party/websockify/README.md
@@ -11,11 +11,24 @@ the target in both directions.
### WebSockets binary data
Websockify supports all versions of the WebSockets protocol (Hixie and
-HyBI). The older Hixie versions of the protocol only support UTF-8
+HyBi). The older Hixie versions of the protocol only support UTF-8
text payloads. In order to transport binary data over UTF-8 an
-encoding must used to encapsulate the data within UTF-8. Websockify
-uses base64 to encode all traffic to and from the client. This does
-not affect the data between websockify and the server.
+encoding must used to encapsulate the data within UTF-8.
+
+With Hixie clients, Websockify uses base64 to encode all traffic to
+and from the client. This does not affect the data between websockify
+and the server.
+
+With HyBi clients, websockify negotiates whether to base64 encode
+traffic to and from the client via the subprotocol header
+(Sec-WebSocket-Protocol). The valid subprotocol values are 'binary'
+and 'base64' and if the client sends both then the server (the python
+implementation) will prefer 'binary'. The 'binary' subprotocol
+indicates that the data will be sent raw using binary WebSocket
+frames. Some HyBi clients (such as the Flash fallback and older Chrome
+and iOS versions) do not support binary data which is why the
+negotiation is necessary.
+
### Encrypted WebSocket connections (wss://)
diff --git a/third_party/websockify/include/base64.js b/third_party/websockify/include/base64.js
index e9b3c522..87336127 100644
--- a/third_party/websockify/include/base64.js
+++ b/third_party/websockify/include/base64.js
@@ -1,45 +1,8 @@
-/*
- * Modified from:
- * http://lxr.mozilla.org/mozilla/source/extensions/xml-rpc/src/nsXmlRpcClient.js#956
- */
-
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is Mozilla XML-RPC Client component.
- *
- * The Initial Developer of the Original Code is
- * Digital Creations 2, Inc.
- * Portions created by the Initial Developer are Copyright (C) 2000
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- * Martijn Pieters <mj@digicool.com> (original author)
- * Samuel Sieb <samuel@sieb.net>
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// From: http://hg.mozilla.org/mozilla-central/raw-file/ec10630b1a54/js/src/devtools/jint/sunspider/string-base64.js
/*jslint white: false, bitwise: false, plusplus: false */
/*global console */
@@ -47,35 +10,37 @@
var Base64 = {
/* Convert data (an array of integers) to a Base64 string. */
-toBase64Table : 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/',
+toBase64Table : 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'.split(''),
base64Pad : '=',
encode: function (data) {
"use strict";
- var result = '',
- chrTable = Base64.toBase64Table.split(''),
- pad = Base64.base64Pad,
- length = data.length,
- i;
+ var result = '';
+ var toBase64Table = Base64.toBase64Table;
+ var base64Pad = Base64.base64Pad;
+ var length = data.length;
+ var i;
// Convert every three bytes to 4 ascii characters.
+ /* BEGIN LOOP */
for (i = 0; i < (length - 2); i += 3) {
- result += chrTable[data[i] >> 2];
- result += chrTable[((data[i] & 0x03) << 4) + (data[i+1] >> 4)];
- result += chrTable[((data[i+1] & 0x0f) << 2) + (data[i+2] >> 6)];
- result += chrTable[data[i+2] & 0x3f];
+ result += toBase64Table[data[i] >> 2];
+ result += toBase64Table[((data[i] & 0x03) << 4) + (data[i+1] >> 4)];
+ result += toBase64Table[((data[i+1] & 0x0f) << 2) + (data[i+2] >> 6)];
+ result += toBase64Table[data[i+2] & 0x3f];
}
+ /* END LOOP */
// Convert the remaining 1 or 2 bytes, pad out to 4 characters.
if (length%3) {
i = length - (length%3);
- result += chrTable[data[i] >> 2];
+ result += toBase64Table[data[i] >> 2];
if ((length%3) === 2) {
- result += chrTable[((data[i] & 0x03) << 4) + (data[i+1] >> 4)];
- result += chrTable[(data[i+1] & 0x0f) << 2];
- result += pad;
+ result += toBase64Table[((data[i] & 0x03) << 4) + (data[i+1] >> 4)];
+ result += toBase64Table[(data[i+1] & 0x0f) << 2];
+ result += base64Pad;
} else {
- result += chrTable[(data[i] & 0x03) << 4];
- result += pad + pad;
+ result += toBase64Table[(data[i] & 0x03) << 4];
+ result += base64Pad + base64Pad;
}
}
@@ -97,12 +62,12 @@ toBinaryTable : [
decode: function (data, offset) {
"use strict";
offset = typeof(offset) !== 'undefined' ? offset : 0;
- var binTable = Base64.toBinaryTable,
- pad = Base64.base64Pad,
- result, result_length, idx, i, c, padding,
- leftbits = 0, // number of bits decoded, but yet to be appended
- leftdata = 0, // bits decoded, but yet to be appended
- data_length = data.indexOf('=') - offset;
+ var toBinaryTable = Base64.toBinaryTable;
+ var base64Pad = Base64.base64Pad;
+ var result, result_length, idx, i, c, padding;
+ var leftbits = 0; // number of bits decoded, but yet to be appended
+ var leftdata = 0; // bits decoded, but yet to be appended
+ var data_length = data.indexOf('=') - offset;
if (data_length < 0) { data_length = data.length - offset; }
@@ -111,9 +76,10 @@ decode: function (data, offset) {
result = new Array(result_length);
// Convert one by one.
+ /* BEGIN LOOP */
for (idx = 0, i = offset; i < data.length; i++) {
- c = binTable[data.charCodeAt(i) & 0x7f];
- padding = (data.charAt(i) === pad);
+ c = toBinaryTable[data.charCodeAt(i) & 0x7f];
+ padding = (data.charAt(i) === base64Pad);
// Skip illegal characters and whitespace
if (c === -1) {
console.error("Illegal character code " + data.charCodeAt(i) + " at position " + i);
@@ -134,6 +100,7 @@ decode: function (data, offset) {
leftdata &= (1 << leftbits) - 1;
}
}
+ /* END LOOP */
// If there are any bits left, the base64 string was corrupted
if (leftbits) {
diff --git a/third_party/websockify/include/util.js b/third_party/websockify/include/util.js
index ddc1914c..ab2e1c1e 100644
--- a/third_party/websockify/include/util.js
+++ b/third_party/websockify/include/util.js
@@ -1,7 +1,7 @@
/*
- * noVNC: HTML5 VNC client
- * Copyright (C) 2011 Joel Martin
- * Licensed under LGPL-3 (see LICENSE.txt)
+ * from noVNC: HTML5 VNC client
+ * Copyright (C) 2012 Joel Martin
+ * Licensed under MPL 2.0 (see LICENSE.txt)
*
* See README.md for usage and integration instructions.
*/
@@ -57,6 +57,21 @@ if (!Array.prototype.map)
};
}
+//
+// requestAnimationFrame shim with setTimeout fallback
+//
+
+window.requestAnimFrame = (function(){
+ return window.requestAnimationFrame ||
+ window.webkitRequestAnimationFrame ||
+ window.mozRequestAnimationFrame ||
+ window.oRequestAnimationFrame ||
+ window.msRequestAnimationFrame ||
+ function(callback){
+ window.setTimeout(callback, 1000 / 60);
+ };
+})();
+
/*
* ------------------------------------------------------
* Namespaced in Util
@@ -131,6 +146,8 @@ Util.conf_default = function(cfg, api, defaults, v, mode, type, defval, desc) {
}
} else if (type in {'integer':1, 'int':1}) {
val = parseInt(val, 10);
+ } else if (type === 'str') {
+ val = String(val);
} else if (type === 'func') {
if (!val) {
val = function () {};
@@ -190,6 +207,45 @@ Util.conf_defaults = function(cfg, api, defaults, arr) {
* Cross-browser routines
*/
+
+// Dynamically load scripts without using document.write()
+// Reference: http://unixpapa.com/js/dyna.html
+//
+// Handles the case where load_scripts is invoked from a script that
+// itself is loaded via load_scripts. Once all scripts are loaded the
+// window.onscriptsloaded handler is called (if set).
+Util.get_include_uri = function() {
+ return (typeof INCLUDE_URI !== "undefined") ? INCLUDE_URI : "include/";
+}
+Util._pending_scripts = [];
+Util.load_scripts = function(files) {
+ var head = document.getElementsByTagName('head')[0],
+ ps = Util._pending_scripts;
+ for (var f=0; f<files.length; f++) {
+ var script = document.createElement('script');
+ script.type = 'text/javascript';
+ script.src = Util.get_include_uri() + files[f];
+ //console.log("loading script: " + Util.get_include_uri() + files[f]);
+ head.appendChild(script);
+ ps.push(script);
+ script.onload = script.onreadystatechange = function (e) {
+ if (!this.readyState ||
+ this.readyState == 'complete' ||
+ this.readyState == 'loaded') {
+ this.onload = this.onreadystatechange = null;
+ if (ps.indexOf(this) >= 0) {
+ //console.log("loaded script: " + this.src);
+ ps.splice(ps.indexOf(this), 1);
+ }
+ // Call window.onscriptsload after last script loads
+ if (ps.length === 0 && window.onscriptsload) {
+ window.onscriptsload();
+ }
+ }
+ }
+ }
+}
+
// Get DOM element position on page
Util.getPosition = function (obj) {
var x = 0, y = 0;
diff --git a/third_party/websockify/include/websock.js b/third_party/websockify/include/websock.js
index ccb7d4c1..7d12644e 100644
--- a/third_party/websockify/include/websock.js
+++ b/third_party/websockify/include/websock.js
@@ -1,7 +1,7 @@
/*
* Websock: high-performance binary WebSockets
* Copyright (C) 2012 Joel Martin
- * Licensed under LGPL-3 (see LICENSE.txt)
+ * Licensed under MPL 2.0 (see LICENSE.txt)
*
* Websock is similar to the standard WebSocket object but Websock
* enables communication with raw TCP sockets (i.e. the binary stream)
@@ -35,23 +35,14 @@ if (window.WebSocket && !window.WEB_SOCKET_FORCE_FLASH) {
Websock_native = false;
(function () {
- function get_INCLUDE_URI() {
- return (typeof INCLUDE_URI !== "undefined") ?
- INCLUDE_URI : "include/";
- }
-
- var start = "<script src='" + get_INCLUDE_URI(),
- end = "'><\/script>", extra = "";
-
- window.WEB_SOCKET_SWF_LOCATION = get_INCLUDE_URI() +
+ window.WEB_SOCKET_SWF_LOCATION = Util.get_include_uri() +
"web-socket-js/WebSocketMain.swf";
if (Util.Engine.trident) {
Util.Debug("Forcing uncached load of WebSocketMain.swf");
window.WEB_SOCKET_SWF_LOCATION += "?" + Math.random();
}
- extra += start + "web-socket-js/swfobject.js" + end;
- extra += start + "web-socket-js/web_socket.js" + end;
- document.write(extra);
+ Util.load_scripts(["web-socket-js/swfobject.js",
+ "web-socket-js/web_socket.js"]);
}());
}
@@ -181,7 +172,10 @@ function decode_message(data) {
//Util.Debug(">> decode_message: " + data);
if (mode === 'binary') {
// push arraybuffer values onto the end
- rQ.push.apply(rQ, (new Uint8Array(data)));
+ var u8 = new Uint8Array(data);
+ for (var i = 0; i < u8.length; i++) {
+ rQ.push(u8[i]);
+ }
} else {
// base64 decode and concat to the end
rQ = rQ.concat(Base64.decode(data, 0));
@@ -380,8 +374,9 @@ function close() {
// Override internal functions for testing
// Takes a send function, returns reference to recv function
-function testMode(override_send) {
+function testMode(override_send, data_mode) {
test_mode = true;
+ mode = data_mode;
api.send = override_send;
api.close = function () {};
return recv_message;
diff --git a/third_party/websockify/include/webutil.js b/third_party/websockify/include/webutil.js
index 2966cd79..2c82c73c 100644
--- a/third_party/websockify/include/webutil.js
+++ b/third_party/websockify/include/webutil.js
@@ -1,7 +1,7 @@
/*
* from noVNC: HTML5 VNC client
- * Copyright (C) 2010 Joel Martin
- * Licensed under LGPL-3 (see LICENSE.txt)
+ * Copyright (C) 2012 Joel Martin
+ * Licensed under MPL 2.0 (see LICENSE.txt)
*
* See README.md for usage and integration instructions.
*/
@@ -37,14 +37,16 @@ if (!window.$D) {
*/
// init log level reading the logging HTTP param
-WebUtil.init_logging = function() {
- Util._log_level = (document.location.href.match(
- /logging=([A-Za-z0-9\._\-]*)/) ||
- ['', Util._log_level])[1];
-
+WebUtil.init_logging = function(level) {
+ if (typeof level !== "undefined") {
+ Util._log_level = level;
+ } else {
+ Util._log_level = (document.location.href.match(
+ /logging=([A-Za-z0-9\._\-]*)/) ||
+ ['', Util._log_level])[1];
+ }
Util.init_logging();
};
-WebUtil.init_logging();
WebUtil.dirObj = function (obj, depth, parent) {
@@ -75,9 +77,14 @@ WebUtil.dirObj = function (obj, depth, parent) {
// Read a query string variable
WebUtil.getQueryVar = function(name, defVal) {
- var re = new RegExp('[?][^#]*' + name + '=([^&#]*)');
+ var re = new RegExp('[?][^#]*' + name + '=([^&#]*)'),
+ match = document.location.href.match(re);
if (typeof defVal === 'undefined') { defVal = null; }
- return (document.location.href.match(re) || ['',defVal])[1];
+ if (match) {
+ return decodeURIComponent(match[1]);
+ } else {
+ return defVal;
+ }
};
@@ -114,6 +121,67 @@ WebUtil.eraseCookie = function(name) {
};
/*
+ * Setting handling.
+ */
+
+WebUtil.initSettings = function(callback) {
+ var callbackArgs = Array.prototype.slice.call(arguments, 1);
+ if (window.chrome && window.chrome.storage) {
+ window.chrome.storage.sync.get(function (cfg) {
+ WebUtil.settings = cfg;
+ console.log(WebUtil.settings);
+ if (callback) {
+ callback.apply(this, callbackArgs);
+ }
+ });
+ } else {
+ // No-op
+ if (callback) {
+ callback.apply(this, callbackArgs);
+ }
+ }
+};
+
+// No days means only for this browser session
+WebUtil.writeSetting = function(name, value) {
+ if (window.chrome && window.chrome.storage) {
+ //console.log("writeSetting:", name, value);
+ if (WebUtil.settings[name] !== value) {
+ WebUtil.settings[name] = value;
+ window.chrome.storage.sync.set(WebUtil.settings);
+ }
+ } else {
+ localStorage.setItem(name, value);
+ }
+};
+
+WebUtil.readSetting = function(name, defaultValue) {
+ var value;
+ if (window.chrome && window.chrome.storage) {
+ value = WebUtil.settings[name];
+ } else {
+ value = localStorage.getItem(name);
+ }
+ if (typeof value === "undefined") {
+ value = null;
+ }
+ if (value === null && typeof defaultValue !== undefined) {
+ return defaultValue;
+ } else {
+ return value;
+ }
+};
+
+WebUtil.eraseSetting = function(name) {
+ if (window.chrome && window.chrome.storage) {
+ window.chrome.storage.sync.remove(name);
+ delete WebUtil.settings[name];
+ } else {
+ localStorage.removeItem(name);
+ }
+};
+
+/*
* Alternate stylesheet selection
*/
WebUtil.getStylesheets = function() { var i, links, sheets = [];
diff --git a/third_party/websockify/other/websockify.js b/third_party/websockify/other/websockify.js
index cda1aaf9..10447587 100644..100755
--- a/third_party/websockify/other/websockify.js
+++ b/third_party/websockify/other/websockify.js
@@ -1,14 +1,26 @@
+#!/usr/bin/env node
+
// A WebSocket to TCP socket proxy
// Copyright 2012 Joel Martin
// Licensed under LGPL version 3 (see docs/LICENSE.LGPL-3)
-// Known to work with node 0.6
+// Known to work with node 0.8.9
// Requires node modules: ws, base64, optimist and policyfile
// npm install ws base64 optimist policyfile
+//
+// NOTE:
+// This version requires a patched version of einaros/ws that supports
+// subprotocol negotiation. You can use the patched version like this:
+//
+// cd websockify/other
+// git clone https://github.com/kanaka/ws
+// npm link ./ws
+
var argv = require('optimist').argv,
net = require('net'),
http = require('http'),
+ https = require('https'),
url = require('url'),
path = require('path'),
fs = require('fs'),
@@ -18,36 +30,55 @@ var argv = require('optimist').argv,
Buffer = require('buffer').Buffer,
WebSocketServer = require('ws').Server,
- httpServer, wsServer,
+ webServer, wsServer,
source_host, source_port, target_host, target_port,
web_path = null;
// Handle new WebSocket client
new_client = function(client) {
- console.log('WebSocket client connected');
- //console.log('protocol: ' + client.protocol);
-
- var target = net.createConnection(target_port,target_host);
- target.on('begin', function() {
- console.log('connected to target');
+ var clientAddr = client._socket.remoteAddress, log;
+ log = function (msg) {
+ console.log(' ' + clientAddr + ': '+ msg);
+ };
+ log('WebSocket connection');
+ log('Version ' + client.protocolVersion + ', subprotocol: ' + client.protocol);
+
+ var target = net.createConnection(target_port,target_host, function() {
+ log('connected to target');
});
target.on('data', function(data) {
- client.send(base64.encode(new Buffer(data)));
+ //log("sending message: " + data);
+ try {
+ if (client.protocol === 'base64') {
+ client.send(base64.encode(new Buffer(data)));
+ } else {
+ client.send(data,{binary: true});
+ }
+ } catch(e) {
+ log("Client closed, cleaning up target");
+ target.end();
+ }
});
target.on('end', function() {
- console.log('target disconnected');
+ log('target disconnected');
});
client.on('message', function(msg) {
- //console.log('got some message');
- target.write(base64.decode(msg),'binary');
+ //log('got message: ' + msg);
+ if (client.protocol === 'base64') {
+ target.write(base64.decode(msg),'binary');
+ } else {
+ target.write(msg,'binary');
+ }
});
client.on('close', function(code, reason) {
- console.log('WebSocket client disconnected: ' + code + ' [' + reason + ']');
+ log('WebSocket client disconnected: ' + code + ' [' + reason + ']');
+ target.end();
});
client.on('error', function(a) {
- console.log('WebSocket client error: ' + a);
+ log('WebSocket client error: ' + a);
+ target.end();
});
};
@@ -94,6 +125,20 @@ http_request = function (request, response) {
});
};
+// Select 'binary' or 'base64' subprotocol, preferring 'binary'
+selectProtocol = function(protocols, callback) {
+ var plist = protocols ? protocols.split(',') : "";
+ var plist = protocols.split(',');
+ if (plist.indexOf('binary') >= 0) {
+ callback(true, 'binary');
+ } else if (plist.indexOf('base64') >= 0) {
+ callback(true, 'base64');
+ } else {
+ console.log("Client must support 'binary' or 'base64' protocol");
+ callback(false);
+ }
+}
+
// parse source and target arguments into parts
try {
source_arg = argv._[0].toString();
@@ -120,7 +165,7 @@ try {
throw("illegal port");
}
} catch(e) {
- console.error("wsproxy.py [--web web_dir] [source_addr:]source_port target_addr:target_port");
+ console.error("websockify.js [--web web_dir] [--cert cert.pem [--key key.pem]] [source_addr:]source_port target_addr:target_port");
process.exit(2);
}
@@ -131,11 +176,21 @@ if (argv.web) {
console.log(" - Web server active. Serving: " + argv.web);
}
-httpServer = http.createServer(http_request);
-httpServer.listen(source_port, function() {
- wsServer = new WebSocketServer({server: httpServer});
+if (argv.cert) {
+ argv.key = argv.key || argv.cert;
+ var cert = fs.readFileSync(argv.cert),
+ key = fs.readFileSync(argv.key);
+ console.log(" - Running in encrypted HTTPS (wss://) mode using: " + argv.cert + ", " + argv.key);
+ webServer = https.createServer({cert: cert, key: key}, http_request);
+} else {
+ console.log(" - Running in unencrypted HTTP (ws://) mode");
+ webServer = http.createServer(http_request);
+}
+webServer.listen(source_port, function() {
+ wsServer = new WebSocketServer({server: webServer,
+ handleProtocols: selectProtocol});
wsServer.on('connection', new_client);
});
// Attach Flash policyfile answer service
-policyfile.createServer().listen(-1, httpServer);
+policyfile.createServer().listen(-1, webServer);
diff --git a/third_party/websockify/setup.py b/third_party/websockify/setup.py
index 947f0a52..ebf5e610 100644
--- a/third_party/websockify/setup.py
+++ b/third_party/websockify/setup.py
@@ -1,6 +1,6 @@
from setuptools import setup, find_packages
-version = '0.1.0'
+version = '0.2.1'
name = 'websockify'
long_description = open("README.md").read() + "\n" + \
open("CHANGES.txt").read() + "\n"
diff --git a/third_party/websockify/websockify/websocket.py b/third_party/websockify/websockify/websocket.py
index 4c0cacc9..1a5b9ffa 100644
--- a/third_party/websockify/websockify/websocket.py
+++ b/third_party/websockify/websockify/websocket.py
@@ -240,32 +240,33 @@ Sec-WebSocket-Accept: %s\r
os.dup2(os.open(os.devnull, os.O_RDWR), sys.stderr.fileno())
@staticmethod
- def unmask(buf, f):
- pstart = f['hlen'] + 4
- pend = pstart + f['length']
+ def unmask(buf, hlen, plen):
+ pstart = hlen + 4
+ pend = pstart + plen
if numpy:
b = c = s2b('')
- if f['length'] >= 4:
+ if plen >= 4:
mask = numpy.frombuffer(buf, dtype=numpy.dtype('<u4'),
- offset=f['hlen'], count=1)
+ offset=hlen, count=1)
data = numpy.frombuffer(buf, dtype=numpy.dtype('<u4'),
- offset=pstart, count=int(f['length'] / 4))
+ offset=pstart, count=int(plen / 4))
#b = numpy.bitwise_xor(data, mask).data
b = numpy.bitwise_xor(data, mask).tostring()
- if f['length'] % 4:
+ if plen % 4:
#print("Partial unmask")
mask = numpy.frombuffer(buf, dtype=numpy.dtype('B'),
- offset=f['hlen'], count=(f['length'] % 4))
+ offset=hlen, count=(plen % 4))
data = numpy.frombuffer(buf, dtype=numpy.dtype('B'),
- offset=pend - (f['length'] % 4),
- count=(f['length'] % 4))
+ offset=pend - (plen % 4),
+ count=(plen % 4))
c = numpy.bitwise_xor(data, mask).tostring()
return b + c
else:
# Slower fallback
+ mask = buf[hlen:hlen+4]
data = array.array('B')
- mask = s2a(f['mask'])
+ mask = s2a(mask)
data.fromstring(buf[pstart:pend])
for i in range(len(data)):
data[i] ^= mask[i % 4]
@@ -304,7 +305,7 @@ Sec-WebSocket-Accept: %s\r
Returns:
{'fin' : 0_or_1,
'opcode' : number,
- 'mask' : 32_bit_number,
+ 'masked' : boolean,
'hlen' : header_bytes_number,
'length' : payload_bytes_number,
'payload' : decoded_buffer,
@@ -315,7 +316,7 @@ Sec-WebSocket-Accept: %s\r
f = {'fin' : 0,
'opcode' : 0,
- 'mask' : 0,
+ 'masked' : False,
'hlen' : 2,
'length' : 0,
'payload' : None,
@@ -332,7 +333,7 @@ Sec-WebSocket-Accept: %s\r
b1, b2 = unpack_from(">BB", buf)
f['opcode'] = b1 & 0x0f
f['fin'] = (b1 & 0x80) >> 7
- has_mask = (b2 & 0x80) >> 7
+ f['masked'] = (b2 & 0x80) >> 7
f['length'] = b2 & 0x7f
@@ -347,7 +348,7 @@ Sec-WebSocket-Accept: %s\r
return f # Incomplete frame header
(f['length'],) = unpack_from('>xxQ', buf)
- full_len = f['hlen'] + has_mask * 4 + f['length']
+ full_len = f['hlen'] + f['masked'] * 4 + f['length']
if blen < full_len: # Incomplete frame
return f # Incomplete frame header
@@ -356,13 +357,13 @@ Sec-WebSocket-Accept: %s\r
f['left'] = blen - full_len
# Process 1 frame
- if has_mask:
+ if f['masked']:
# unmask payload
- f['mask'] = buf[f['hlen']:f['hlen']+4]
- f['payload'] = WebSocketServer.unmask(buf, f)
+ f['payload'] = WebSocketServer.unmask(buf, f['hlen'],
+ f['length'])
else:
print("Unmasked frame: %s" % repr(buf))
- f['payload'] = buf[(f['hlen'] + has_mask * 4):full_len]
+ f['payload'] = buf[(f['hlen'] + f['masked'] * 4):full_len]
if base64 and f['opcode'] in [1, 2]:
try:
@@ -389,6 +390,7 @@ Sec-WebSocket-Accept: %s\r
end = buf.find(s2b('\xff'))
return {'payload': b64decode(buf[1:end]),
'hlen': 1,
+ 'masked': False,
'length': end - 1,
'left': len(buf) - (end + 1)}
@@ -456,7 +458,7 @@ Sec-WebSocket-Accept: %s\r
if self.rec:
self.rec.write("%s,\n" %
repr("{%s{" % tdelta
- + encbuf[lenhead:-lentail]))
+ + encbuf[lenhead:len(encbuf)-lentail]))
self.send_parts.append(encbuf)
@@ -536,8 +538,14 @@ Sec-WebSocket-Accept: %s\r
if self.rec:
start = frame['hlen']
end = frame['hlen'] + frame['length']
+ if frame['masked']:
+ recbuf = WebSocketServer.unmask(buf, frame['hlen'],
+ frame['length'])
+ else:
+ recbuf = buf[frame['hlen']:frame['hlen'] +
+ frame['length']]
self.rec.write("%s,\n" %
- repr("}%s}" % tdelta + buf[start:end]))
+ repr("}%s}" % tdelta + recbuf))
bufs.append(frame['payload'])
@@ -779,6 +787,10 @@ Sec-WebSocket-Accept: %s\r
self.handler_id)
self.msg("opening record file: %s" % fname)
self.rec = open(fname, 'w+')
+ encoding = "binary"
+ if self.base64: encoding = "base64"
+ self.rec.write("var VNC_frame_encoding = '%s';\n"
+ % encoding)
self.rec.write("var VNC_frame_data = [\n")
self.ws_connection = True
@@ -800,7 +812,7 @@ Sec-WebSocket-Accept: %s\r
self.msg(traceback.format_exc())
finally:
if self.rec:
- self.rec.write("'EOF']\n")
+ self.rec.write("'EOF'];\n")
self.rec.close()
if self.client and self.client != startsock:
diff --git a/third_party/websockify/websockify/websocketproxy.py b/third_party/websockify/websockify/websocketproxy.py
index 8bd67ec4..1154d925 100755
--- a/third_party/websockify/websockify/websocketproxy.py
+++ b/third_party/websockify/websockify/websocketproxy.py
@@ -14,8 +14,11 @@ as taken from http://docs.python.org/dev/library/ssl.html#certificates
import signal, socket, optparse, time, os, sys, subprocess
from select import select
import websocket
-try: from urllib.parse import parse_qs, urlparse
-except: from urlparse import parse_qs, urlparse
+try:
+ from urllib.parse import parse_qs, urlparse
+except:
+ from cgi import parse_qs
+ from urlparse import urlparse
class WebSocketProxy(websocket.WebSocketServer):
"""
@@ -205,7 +208,7 @@ Traffic Legend:
# Extract the token parameter from url
args = parse_qs(urlparse(path)[4]) # 4 is the query from url
- if not len(args['token']):
+ if not args.has_key('token') or not len(args['token']):
raise self.EClose("Token not present")
token = args['token'][0].rstrip('\n')
@@ -249,6 +252,24 @@ Traffic Legend:
ins, outs, excepts = select(rlist, wlist, [], 1)
if excepts: raise Exception("Socket exception")
+ if self.client in outs:
+ # Send queued target data to the client
+ c_pend = self.send_frames(cqueue)
+
+ cqueue = []
+
+ if self.client in ins:
+ # Receive client data, decode it, and queue for target
+ bufs, closed = self.recv_frames()
+ tqueue.extend(bufs)
+
+ if closed:
+ # TODO: What about blocking on client socket?
+ self.vmsg("%s:%s: Client closed connection" %(
+ self.target_host, self.target_port))
+ raise self.CClose(closed['code'], closed['reason'])
+
+
if target in outs:
# Send queued client data to the target
dat = tqueue.pop(0)
@@ -273,24 +294,6 @@ Traffic Legend:
self.traffic("{")
- if self.client in outs:
- # Send queued target data to the client
- c_pend = self.send_frames(cqueue)
-
- cqueue = []
-
-
- if self.client in ins:
- # Receive client data, decode it, and queue for target
- bufs, closed = self.recv_frames()
- tqueue.extend(bufs)
-
- if closed:
- # TODO: What about blocking on client socket?
- self.vmsg("%s:%s: Client closed connection" %(
- self.target_host, self.target_port))
- raise self.CClose(closed['code'], closed['reason'])
-
def _subprocess_setup():
# Python installs a SIGPIPE handler by default. This is usually not what