16 Commits

Author SHA1 Message Date
Joel Martin 264f8fdd7f Update to version 0.5.1
*** NOTE ***

This version of websockify no longer supports the old Hixie protocol.
2013-06-27 16:35:54 -05:00
Joel Martin 36cb8f4676 Move javascript websockify files to other/js 2013-06-27 16:29:17 -05:00
Joel Martin 36fcd5784f Add package file for websockify.js 2013-06-27 16:29:17 -05:00
Joel Martin 46450577c2 Use upstream einaros/ws 0.4.27 with sub-protocol support.
Also, some connection error handling.
2013-06-27 16:29:17 -05:00
Joel Martin d3ba23fa64 Update to c0855c6cae of web-socket-js
Update both the submodule and the swf build. The submodule now
contains the unobfuscated source for swfobject.js which should make
websockify more DFSG compliant.
2013-06-27 16:29:17 -05:00
Joel Martin 6d27b5d321 Merge pull request #84 from nttdatainc-openstackers/master
Fix for issue #83
2013-06-27 14:01:07 -07:00
NTT Data OpenStackers 888e75a8fb Add 2 arguments to websockify.WSRequestHandler
This patch adds 2 arguments to websockify.WSRequestHandler for security:

* file_only: returns 404 response if non-file contents are requested.
             Required to disable directory listing.

* no_parent: returns 403 response if contents out of the web root are
             requested. Required to disable directory traversal.
2013-06-24 06:09:01 -07:00
Joel Martin b7f255ce0b Clarify messages when optional modules are not found. 2013-04-19 17:14:20 -05:00
Joel Martin 477947ba96 Remove wsproxy references. Sync launch.sh from noVNC. 2013-04-15 12:22:08 -05:00
Joel Martin 33e9a21ce4 Add gimite/web-socket-js submodule for DFSG compliance. 2013-04-12 08:24:07 -05:00
Joel Martin 2d078e8cd8 Merge pull request #76 from SlapOS/include
correctly include include directory in egg.
2013-04-03 09:44:37 -07:00
Cédric de Saint Martin d6d960dbe5 correctly include include directory in egg. 2013-04-03 12:12:37 +02:00
Joel Martin 903198a724 tests: use new module path and remove Hixie code. 2013-03-14 12:24:58 -05:00
Joel Martin d3865688c8 README: no longer supporting Hixie.
NOTE: Hixie support is in version 0.4.X. If necessary, I will branch
and apply critical fixes release a new 0.4.X tagged version.
2013-03-12 13:54:11 -05:00
Joel Martin 5a726c2e4d Merge pull request #70 from astrand/master
Remove support for old Hixie protocols
2013-03-12 11:52:30 -07:00
Peter Åstrand (astrand) b2fe57c950 Remove support for old Hixie protocols.
Hybi protocols 7 and 8 are still supported, in addition to
protocol 13 -  RFC 6455.
2013-03-04 09:38:29 +01:00
18 changed files with 135 additions and 171 deletions
+3
View File
@@ -0,0 +1,3 @@
[submodule "include/web-socket-js-project"]
path = include/web-socket-js-project
url = https://github.com/gimite/web-socket-js.git
+10
View File
@@ -1,6 +1,16 @@
Changes
=======
0.5.1 - Jun 27, 2013
--------------------
* use upstream einaros/ws (>=0.4.27) with websockify.js
* file_only and no_parent security options for WSRequestHandler
* Update build of web-socket-js (c0855c6cae)
* add include/web-socket-js-project submodule to gimite/web-socket-js
for DSFG compliance.
* drop Hixie protocol support
0.4.1 - Mar 12, 2013
--------------------
+2 -1
View File
@@ -1 +1,2 @@
include CHANGES.txt websockify include README.md LICENSE.txt
include CHANGES.txt README.md LICENSE.txt
graft include
+9 -17
View File
@@ -10,25 +10,17 @@ the target in both directions.
### WebSockets binary data
Websockify 0.4.X supports all versions of the WebSockets protocol
(Hixie and HyBi). Starting with websockify 0.5.0, only the HyBi / IETF
Starting with websockify 0.5.0, only the HyBi / IETF
6455 WebSocket protocol is supported.
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. 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.
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://)
+4 -1
View File
@@ -1,4 +1,4 @@
- Update setup.py and CHANGES.txt and commit
- Update setup.py, CHANGES.txt and other/package.json and commit
- Create version tag and tarball from tag
WVER=0.1.0
git tag v${WVER}
@@ -8,3 +8,6 @@
python setup.py register
- Upload the source distribution to pypi
python setup.py sdist upload
- Register with npmjs.org (once)
- Upload websockify.js npmjs.org package
npm publish other/js
Binary file not shown.
+7
View File
@@ -0,0 +1,7 @@
A JavaScript implementation of the websockify WebSocket-to-TCP bridge/proxy.
Copyright (C) 2013 - Joel Martin (github.com/kanaka)
Licensed under LGPL-3.
See http://github.com/kanaka/websockify for more info.
+24
View File
@@ -0,0 +1,24 @@
{
"author": "Joel Martin <github@martintribe.org> (http://github.com/kanaka)",
"name": "websockify",
"description": "websockify is a WebSocket-to-TCP proxy/bridge",
"license": "LGPL-3",
"version": "0.5.1",
"repository": {
"type": "git",
"url": "git://github.com/kanaka/websockify.git"
},
"files": ["../../docs/LICENSE.LGPL-3"],
"bin": {
"websockify": "./websockify.js"
},
"engines": {
"node": ">=0.8.9"
},
"dependencies": {
"ws": ">=0.4.27",
"base64": "latest",
"optimist": "latest",
"policyfile": "latest"
}
}
+9 -12
View File
@@ -7,14 +7,6 @@
// Known to work with node 0.8.9
// Requires node modules: ws, optimist and policyfile
// npm install ws 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,
@@ -37,6 +29,7 @@ var argv = require('optimist').argv,
// Handle new WebSocket client
new_client = function(client) {
var clientAddr = client._socket.remoteAddress, log;
console.log(client.upgradeReq.url);
log = function (msg) {
console.log(' ' + clientAddr + ': '+ msg);
};
@@ -61,6 +54,12 @@ new_client = function(client) {
});
target.on('end', function() {
log('target disconnected');
client.close();
});
target.on('error', function() {
log('target connection error');
target.end();
client.close();
});
client.on('message', function(msg) {
@@ -126,11 +125,9 @@ 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) {
if (protocols.indexOf('binary') >= 0) {
callback(true, 'binary');
} else if (plist.indexOf('base64') >= 0) {
} else if (protocols.indexOf('base64') >= 0) {
callback(true, 'base64');
} else {
console.log("Client must support 'binary' or 'base64' protocol");
+14 -4
View File
@@ -16,6 +16,8 @@ usage() {
echo " Default: localhost:5900"
echo " --cert CERT Path to combined cert/key file"
echo " Default: self.pem"
echo " --web WEB Path to web files (e.g. vnc.html)"
echo " Default: ./"
exit 2
}
@@ -24,6 +26,7 @@ HERE="$(cd "$(dirname "$0")" && pwd)"
PORT="6080"
VNC_DEST="localhost:5900"
CERT=""
WEB=""
proxy_pid=""
die() {
@@ -50,6 +53,7 @@ while [ "$*" ]; do
--listen) PORT="${OPTARG}"; shift ;;
--vnc) VNC_DEST="${OPTARG}"; shift ;;
--cert) CERT="${OPTARG}"; shift ;;
--web) WEB="${OPTARG}"; shift ;;
-h|--help) usage ;;
-*) usage "Unknown chrooter option: ${param}" ;;
*) break ;;
@@ -60,18 +64,24 @@ done
which netstat >/dev/null 2>&1 \
|| die "Must have netstat installed"
netstat -ltn | grep -qs "${PORT}.*LISTEN" \
netstat -ltn | grep -qs "${PORT} .*LISTEN" \
&& die "Port ${PORT} in use. Try --listen PORT"
trap "cleanup" TERM QUIT INT EXIT
# Find vnc.html
if [ -e "$(pwd)/vnc.html" ]; then
if [ -n "${WEB}" ]; then
if [ ! -e "${WEB}/vnc.html" ]; then
die "Could not find ${WEB}/vnc.html"
fi
elif [ -e "$(pwd)/vnc.html" ]; then
WEB=$(pwd)
elif [ -e "${HERE}/../vnc.html" ]; then
WEB=${HERE}/../
elif [ -e "${HERE}/vnc.html" ]; then
WEB=${HERE}
elif [ -e "${HERE}/../share/novnc/vnc.html" ]; then
WEB=${HERE}/../share/novnc/
else
die "Could not find vnc.html"
fi
@@ -92,7 +102,7 @@ else
fi
echo "Starting webserver and WebSockets proxy on port ${PORT}"
${HERE}/wsproxy.py --web ${WEB} ${CERT:+--cert ${CERT}} ${PORT} ${VNC_DEST} &
${HERE}/websockify --web ${WEB} ${CERT:+--cert ${CERT}} ${PORT} ${VNC_DEST} &
proxy_pid="$!"
sleep 1
if ! ps -p ${proxy_pid} >/dev/null; then
@@ -101,7 +111,7 @@ if ! ps -p ${proxy_pid} >/dev/null; then
exit 1
fi
echo -e "\n\nNavigate to to this URL:\n"
echo -e "\n\nNavigate to this URL:\n"
echo -e " http://$(hostname):${PORT}/vnc.html?host=$(hostname)&port=${PORT}\n"
echo -e "Press Ctrl-C to exit\n\n"
+1 -1
View File
@@ -795,7 +795,7 @@ void start_server() {
}
handler_msg("handler exit\n");
} else {
handler_msg("wsproxy exit\n");
handler_msg("websockify exit\n");
}
}
+2 -2
View File
@@ -7,8 +7,8 @@
* REBIND_PORT_NEW environment variables are set then bind on the new
* port (of localhost) instead of the old port.
*
* This allows a proxy (such as wsproxy) to run on the old port and translate
* traffic to/from the new port.
* This allows a bridge/proxy (such as websockify) to run on the old port and
* translate traffic to/from the new port.
*
* Usage:
* LD_PRELOAD=./rebind.so \
+1 -1
View File
@@ -1,6 +1,6 @@
from setuptools import setup, find_packages
version = '0.4.1'
version = '0.5.1'
name = 'websockify'
long_description = open("README.md").read() + "\n" + \
open("CHANGES.txt").read() + "\n"
+1 -1
View File
@@ -11,7 +11,7 @@ as taken from http://docs.python.org/dev/library/ssl.html#certificates
'''
import os, sys, select, optparse
sys.path.insert(0,os.path.dirname(__file__) + "/../")
sys.path.insert(0,os.path.dirname(__file__) + "/../websockify")
from websocket import WebSocketServer
class WebSocketEcho(WebSocketServer):
+1 -1
View File
@@ -7,7 +7,7 @@ given a sequence number. Any errors are reported and counted.
'''
import sys, os, select, random, time, optparse
sys.path.insert(0,os.path.dirname(__file__) + "/../")
sys.path.insert(0,os.path.dirname(__file__) + "/../websockify")
from websocket import WebSocketServer
class WebSocketLoad(WebSocketServer):
+3 -5
View File
@@ -6,17 +6,15 @@ Display UTF-8 encoding for 0-255.'''
import sys, os, socket, ssl, time, traceback
from select import select
sys.path.insert(0,os.path.dirname(__file__) + "/../")
sys.path.insert(0,os.path.dirname(__file__) + "/../websockify")
from websocket import WebSocketServer
if __name__ == '__main__':
print "val: hixie | hybi_base64 | hybi_binary"
print "val: hybi_base64 | hybi_binary"
for c in range(0, 256):
hixie = WebSocketServer.encode_hixie(chr(c))
hybi_base64 = WebSocketServer.encode_hybi(chr(c), opcode=1,
base64=True)
hybi_binary = WebSocketServer.encode_hybi(chr(c), opcode=2,
base64=False)
print "%d: %s | %s | %s" % (c, repr(hixie), repr(hybi_base64),
repr(hybi_binary))
print "%d: %s | %s" % (c, repr(hybi_base64), repr(hybi_binary))
+43 -125
View File
@@ -6,9 +6,9 @@ Copyright 2011 Joel Martin
Licensed under LGPL version 3 (see docs/LICENSE.LGPL-3)
Supports following protocol versions:
- http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-75
- http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-76
- http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-07
- http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-10
- http://tools.ietf.org/html/rfc6455
You can make a cert/key with openssl using:
openssl req -new -x509 -days 365 -nodes -out self.pem -keyout self.pem
@@ -37,8 +37,8 @@ try: from http.server import SimpleHTTPRequestHandler
except: from SimpleHTTPServer import SimpleHTTPRequestHandler
# python 2.6 differences
try: from hashlib import md5, sha1
except: from md5 import md5; from sha import sha as sha1
try: from hashlib import sha1
except: from sha import sha as sha1
# python 2.5 differences
try:
@@ -50,15 +50,15 @@ except:
return struct.unpack(fmt, slice)
# Degraded functionality if these imports are missing
for mod, sup in [('numpy', 'HyBi protocol'), ('ssl', 'TLS/SSL/wss'),
('multiprocessing', 'Multi-Processing'),
('resource', 'daemonizing')]:
for mod, msg in [('numpy', 'HyBi protocol will be slower'),
('ssl', 'TLS/SSL/wss is disabled'),
('multiprocessing', 'Multi-Processing is disabled'),
('resource', 'daemonizing is disabled')]:
try:
globals()[mod] = __import__(mod)
except ImportError:
globals()[mod] = None
print("WARNING: no '%s' module, %s is slower or disabled" % (
mod, sup))
print("WARNING: no '%s' module, %s" % (mod, msg))
if multiprocessing and sys.platform == 'win32':
# make sockets pickle-able/inheritable
import multiprocessing.reduction
@@ -72,14 +72,6 @@ class WebSocketServer(object):
buffer_size = 65536
server_handshake_hixie = """HTTP/1.1 101 Web Socket Protocol Handshake\r
Upgrade: WebSocket\r
Connection: Upgrade\r
%sWebSocket-Origin: %s\r
%sWebSocket-Location: %s://%s%s\r
"""
server_handshake_hybi = """HTTP/1.1 101 Switching Protocols\r
Upgrade: websocket\r
Connection: Upgrade\r
@@ -100,7 +92,7 @@ Sec-WebSocket-Accept: %s\r
def __init__(self, listen_host='', listen_port=None, source_is_ipv6=False,
verbose=False, cert='', key='', ssl_only=None,
daemon=False, record='', web='',
daemon=False, record='', web='', file_only=False, no_parent=False,
run_once=False, timeout=0, idle_timeout=0):
# settings
@@ -118,6 +110,9 @@ Sec-WebSocket-Accept: %s\r
self.ws_connection = False
self.handler_id = 1
self.file_only = file_only
self.no_parent = no_parent
# Make paths settings absolute
self.cert = os.path.abspath(cert)
self.key = self.web = self.record = ''
@@ -381,33 +376,6 @@ Sec-WebSocket-Accept: %s\r
return f
@staticmethod
def encode_hixie(buf):
return s2b("\x00" + b2s(b64encode(buf)) + "\xff"), 1, 1
@staticmethod
def decode_hixie(buf):
end = buf.find(s2b('\xff'))
return {'payload': b64decode(buf[1:end]),
'hlen': 1,
'masked': False,
'length': end - 1,
'left': len(buf) - (end + 1)}
@staticmethod
def gen_md5(keys):
""" Generate hash value for WebSockets hixie-76. """
key1 = keys['Sec-WebSocket-Key1']
key2 = keys['Sec-WebSocket-Key2']
key3 = keys['key3']
spaces1 = key1.count(" ")
spaces2 = key2.count(" ")
num1 = int("".join([c for c in key1 if c.isdigit()])) / spaces1
num2 = int("".join([c for c in key2 if c.isdigit()])) / spaces2
return b2s(md5(pack('>II8s',
int(num1), int(num2), key3)).digest())
#
# WebSocketServer logging/output functions
@@ -444,16 +412,10 @@ Sec-WebSocket-Accept: %s\r
if bufs:
for buf in bufs:
if self.version.startswith("hybi"):
if self.base64:
encbuf, lenhead, lentail = self.encode_hybi(
buf, opcode=1, base64=True)
else:
encbuf, lenhead, lentail = self.encode_hybi(
buf, opcode=2, base64=False)
if self.base64:
encbuf, lenhead, lentail = self.encode_hybi(buf, opcode=1, base64=True)
else:
encbuf, lenhead, lentail = self.encode_hixie(buf)
encbuf, lenhead, lentail = self.encode_hybi(buf, opcode=2, base64=False)
if self.rec:
self.rec.write("%s,\n" %
@@ -498,41 +460,21 @@ Sec-WebSocket-Accept: %s\r
self.recv_part = None
while buf:
if self.version.startswith("hybi"):
frame = self.decode_hybi(buf, base64=self.base64)
#print("Received buf: %s, frame: %s" % (repr(buf), frame))
if frame['payload'] == None:
# Incomplete/partial frame
self.traffic("}.")
if frame['left'] > 0:
self.recv_part = buf[-frame['left']:]
break
else:
if frame['opcode'] == 0x8: # connection close
closed = {'code': frame['close_code'],
'reason': frame['close_reason']}
break
frame = self.decode_hybi(buf, base64=self.base64)
#print("Received buf: %s, frame: %s" % (repr(buf), frame))
if frame['payload'] == None:
# Incomplete/partial frame
self.traffic("}.")
if frame['left'] > 0:
self.recv_part = buf[-frame['left']:]
break
else:
if buf[0:2] == s2b('\xff\x00'):
closed = {'code': 1000,
'reason': "Client sent orderly close frame"}
if frame['opcode'] == 0x8: # connection close
closed = {'code': frame['close_code'],
'reason': frame['close_reason']}
break
elif buf[0:2] == s2b('\x00\xff'):
buf = buf[2:]
continue # No-op
elif buf.count(s2b('\xff')) == 0:
# Partial frame
self.traffic("}.")
self.recv_part = buf
break
frame = self.decode_hixie(buf)
self.traffic("}")
if self.rec:
@@ -560,17 +502,9 @@ Sec-WebSocket-Accept: %s\r
def send_close(self, code=1000, reason=''):
""" Send a WebSocket orderly close frame. """
if self.version.startswith("hybi"):
msg = pack(">H%ds" % len(reason), code, reason)
buf, h, t = self.encode_hybi(msg, opcode=0x08, base64=False)
self.client.send(buf)
elif self.version == "hixie-76":
buf = s2b('\xff\x00')
self.client.send(buf)
# No orderly close for 75
msg = pack(">H%ds" % len(reason), code, reason)
buf, h, t = self.encode_hybi(msg, opcode=0x08, base64=False)
self.client.send(buf)
def do_websocket_handshake(self, headers, path):
h = self.headers = headers
@@ -612,28 +546,7 @@ Sec-WebSocket-Accept: %s\r
response += "\r\n"
else:
# Hixie version of the protocol (75 or 76)
if h.get('key3'):
trailer = self.gen_md5(h)
pre = "Sec-"
self.version = "hixie-76"
else:
trailer = ""
pre = ""
self.version = "hixie-75"
# We only support base64 in Hixie era
self.base64 = True
response = self.server_handshake_hixie % (pre,
h['Origin'], pre, self.scheme, h['Host'], path)
if 'base64' in protocols:
response += "%sWebSocket-Protocol: base64\r\n" % pre
else:
self.msg("Warning: client does not report 'base64' protocol support")
response += "\r\n" + trailer
raise self.EClose("Missing Sec-WebSocket-Version header. Hixie protocols not supported.")
return response
@@ -710,7 +623,8 @@ Sec-WebSocket-Accept: %s\r
self.scheme = "ws"
stype = "Plain non-SSL (ws://)"
wsh = WSRequestHandler(retsock, address, not self.web)
wsh = WSRequestHandler(retsock, address, not self.web,
self.file_only, self.no_parent)
if wsh.last_code == 101:
# Continue on to handle WebSocket upgrade
pass
@@ -949,19 +863,19 @@ Sec-WebSocket-Accept: %s\r
# HTTP handler with WebSocket upgrade support
class WSRequestHandler(SimpleHTTPRequestHandler):
def __init__(self, req, addr, only_upgrade=False):
def __init__(self, req, addr, only_upgrade=False, file_only=False,
no_parent=False):
self.only_upgrade = only_upgrade # only allow upgrades
self.webroot = os.path.realpath(".")
self.file_only = file_only
self.no_parent = no_parent
SimpleHTTPRequestHandler.__init__(self, req, addr, object())
def do_GET(self):
abspath = os.path.realpath("." + (self.path.split('?')[0]))
if (self.headers.get('upgrade') and
self.headers.get('upgrade').lower() == 'websocket'):
if (self.headers.get('sec-websocket-key1') or
self.headers.get('websocket-key1')):
# For Hixie-76 read out the key hash
self.headers.__setitem__('key3', self.rfile.read(8))
# Just indicate that an WebSocket upgrade is needed
self.last_code = 101
self.last_message = "101 Switching Protocols"
@@ -969,6 +883,10 @@ class WSRequestHandler(SimpleHTTPRequestHandler):
# Normal web request responses are disabled
self.last_code = 405
self.last_message = "405 Method Not Allowed"
elif self.file_only and not os.path.isfile(abspath):
self.send_response(404, "No such file")
elif self.no_parent and not abspath.startswith(self.webroot):
self.send_response(403, "Hidden resources")
else:
SimpleHTTPRequestHandler.do_GET(self)