echo "$*"
echo
fi
- echo "Usage: ${NAME} [--web WEB_PORT] [--proxy PROXY_PORT] [--vnc VNC_HOST:PORT]"
+ echo "Usage: ${NAME} [--listen PORT] [--vnc VNC_HOST:PORT] [--cert CERT]"
echo
echo "Starts a mini-webserver and the WebSockets proxy and"
echo "provides a cut and paste URL to go to."
echo
- echo " --web WEB_PORT Port to serve web pages at"
- echo " Default: 8080"
- echo " --proxy PROXY_PORT Port for proxy to listen on"
- echo " Default: 8081"
+ echo " --listen PORT Port for webserver/proxy to listen on"
+ echo " Default: 6080"
echo " --vnc VNC_HOST:PORT VNC server host:port proxy target"
echo " Default: localhost:5900"
+ echo " --cert CERT Path to combined cert/key file"
+ echo " Default: self.pem"
exit 2
}
NAME="$(basename $0)"
HERE="$(cd "$(dirname "$0")" && pwd)"
-WEB_PORT="6080"
-PROXY_PORT="6081"
+PORT="6080"
VNC_DEST="localhost:5900"
-web_pid=""
+CERT=""
proxy_pid=""
die() {
trap - TERM QUIT INT EXIT
trap "true" CHLD # Ignore cleanup messages
echo
- if [ -n "${web_pid}" ]; then
- echo "Terminating webserver (${web_pid})"
- kill ${web_pid}
- fi
if [ -n "${proxy_pid}" ]; then
echo "Terminating WebSockets proxy (${proxy_pid})"
kill ${proxy_pid}
while [ "$*" ]; do
param=$1; shift; OPTARG=$1
case $param in
- --web) WEB_PORT="${OPTARG}"; shift ;;
- --proxy) PROXY_PORT="${OPTARG}"; shift ;;
- --vnc) VNC_DEST="${OPTARG}"; shift ;;
- -h|--help) usage ;;
+ --listen) PORT="${OPTARG}"; shift ;;
+ --vnc) VNC_DEST="${OPTARG}"; shift ;;
+ --cert) CERT="${OPTARG}"; shift ;;
+ -h|--help) usage ;;
-*) usage "Unknown chrooter option: ${param}" ;;
- *) break ;;
+ *) break ;;
esac
done
which netstat >/dev/null 2>&1 \
|| die "Must have netstat installed"
-netstat -ltn | grep -qs "${WEB_PORT}.*LISTEN" \
- && die "Port ${WEB_PORT} in use. Try --web WEB_PORT"
-
-netstat -ltn | grep -qs "${PROXY_PORT}.*LISTEN" \
- && die "Port ${PROXY_PORT} in use. Try --proxy PROXY_PORT"
+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
- TOP=$(pwd)
+ WEB=$(pwd)
elif [ -e "${HERE}/../vnc.html" ]; then
- TOP=${HERE}/../
+ WEB=${HERE}/../
elif [ -e "${HERE}/vnc.html" ]; then
- TOP=${HERE}
+ WEB=${HERE}
else
die "Could not find vnc.html"
fi
-cd ${TOP}
-echo "Starting webserver on port ${WEB_PORT}"
-${HERE}/web.py ${WEB_PORT} >/dev/null &
-web_pid="$!"
-sleep 1
-if ps -p ${web_pid} >/dev/null; then
- echo "Started webserver (pid: ${web_pid})"
+# Find self.pem
+if [ -n "${CERT}" ]; then
+ if [ ! -e "${CERT}" ]; then
+ die "Could not find ${CERT}"
+ fi
+elif [ -e "$(pwd)/self.pem" ]; then
+ CERT="$(pwd)/self.pem"
+elif [ -e "${HERE}/../self.pem" ]; then
+ CERT="${HERE}/../self.pem"
+elif [ -e "${HERE}/self.pem" ]; then
+ CERT="${HERE}/self.pem"
else
- web_pid=
- echo "Failed to start webserver"
- exit 1
+ echo "Warning: could not find self.pem"
fi
-echo "Starting WebSockets proxy on port ${PROXY_PORT}"
-${HERE}/wsproxy.py -f ${PROXY_PORT} ${VNC_DEST} &
+echo "Starting webserver and WebSockets proxy on port ${PORT}"
+${HERE}/wsproxy.py -f --web ${WEB} ${CERT:+--cert ${CERT}} ${PORT} ${VNC_DEST} &
proxy_pid="$!"
sleep 1
if ps -p ${proxy_pid} >/dev/null; then
fi
echo -e "\n\nNavigate to to this URL:\n"
-echo -e " http://$(hostname):${WEB_PORT}/vnc.html?host=$(hostname)&port=${PROXY_PORT}\n"
+echo -e " http://$(hostname):${PORT}/vnc.html?host=$(hostname)&port=${PORT}\n"
echo -e "Press Ctrl-C to exit\n\n"
-wait ${web_pid}
-
+wait ${proxy_pid}
import sys, socket, ssl, struct, traceback
import os, resource, errno, signal # daemonizing
+from SimpleHTTPServer import SimpleHTTPRequestHandler
+from cStringIO import StringIO
from base64 import b64encode, b64decode
try:
from hashlib import md5
'key' : None,
'ssl_only' : False,
'daemon' : True,
- 'record' : None, }
+ 'record' : None,
+ 'web' : False, }
server_handshake = """HTTP/1.1 101 Web Socket Protocol Handshake\r
Upgrade: WebSocket\r
class EClose(Exception):
pass
+# HTTP handler with request from a string and response to a socket
+class SplitHTTPHandler(SimpleHTTPRequestHandler):
+ def __init__(self, req, resp, addr):
+ # Save the response socket
+ self.response = resp
+ SimpleHTTPRequestHandler.__init__(self, req, addr, object())
+
+ def setup(self):
+ self.connection = self.response
+ # Duck type request string to file object
+ self.rfile = StringIO(self.request)
+ self.wfile = self.connection.makefile('wb', self.wbufsize)
+
+ def send_response(self, code, message=None):
+ # Save the status code
+ self.last_code = code
+ SimpleHTTPRequestHandler.send_response(self, code, message)
+
+ def log_message(self, f, *args):
+ # Save instead of printing
+ self.last_message = f % args
+
+
def traffic(token="."):
if settings['verbose'] and not settings['daemon']:
sys.stdout.write(token)
def handler_msg(msg):
if not settings['daemon']:
- print " %d: %s" % (settings['handler_id'], msg)
+ print "% 3d: %s" % (settings['handler_id'], msg)
+
+def handler_vmsg(msg):
+ if settings['verbose']: handler_msg(msg)
def encode(buf):
buf = b64encode(buf)
return md5(struct.pack('>II8s', num1, num2, key3)).digest()
-def do_handshake(sock):
+def do_handshake(sock, address):
+ stype = ""
# Peek, but don't read the data
handshake = sock.recv(1024, socket.MSG_PEEK)
#handler_msg("Handshake [%s]" % repr(handshake))
if handshake == "":
- handler_msg("ignoring empty handshake")
- sock.close()
- return False
+ raise EClose("ignoring empty handshake")
elif handshake.startswith("<policy-file-request/>"):
handshake = sock.recv(1024)
- handler_msg("Sending flash policy response")
sock.send(policy_response)
- sock.close()
- return False
+ raise EClose("Sending flash policy response")
elif handshake[0] in ("\x16", "\x80"):
if not os.path.exists(settings['cert']):
- handler_msg("SSL connection but '%s' not found"
- % settings['cert'])
- sock.close()
- return False
- retsock = ssl.wrap_socket(
- sock,
- server_side=True,
- certfile=settings['cert'],
- keyfile=settings['key'])
+ raise EClose("SSL connection but '%s' not found"
+ % settings['cert'])
+ try:
+ retsock = ssl.wrap_socket(
+ sock,
+ server_side=True,
+ certfile=settings['cert'],
+ keyfile=settings['key'])
+ except ssl.SSLError, x:
+ if x.args[0] == ssl.SSL_ERROR_EOF:
+ raise EClose("")
+ else:
+ raise
+
scheme = "wss"
- handler_msg("using SSL/TLS")
+ stype = "SSL/TLS (wss://)"
elif settings['ssl_only']:
- handler_msg("non-SSL connection disallowed")
- sock.close()
- return False
+ raise EClose("non-SSL connection received but disallowed")
else:
retsock = sock
scheme = "ws"
- handler_msg("using plain (not SSL) socket")
+ stype = "Plain non-SSL (ws://)"
+
+ # Now get the data from the socket
handshake = retsock.recv(4096)
#handler_msg("handshake: " + repr(handshake))
+
if len(handshake) == 0:
raise EClose("Client closed during handshake")
+
+ # Handle normal web requests
+ if handshake.startswith('GET ') and \
+ handshake.find('Upgrade: WebSocket\r\n') == -1:
+ if not settings['web']:
+ raise EClose("Normal web request received but disallowed")
+ sh = SplitHTTPHandler(handshake, retsock, address)
+ if sh.last_code < 200 or sh.last_code >= 300:
+ raise EClose(sh.last_message)
+ elif settings['verbose']:
+ raise EClose(sh.last_message)
+ else:
+ raise EClose("")
+
+ # Do WebSockets handshake and return the socket
h = parse_handshake(handshake)
if h.get('key3'):
trailer = gen_md5(h)
pre = "Sec-"
- handler_msg("using protocol version 76")
+ ver = 76
else:
trailer = ""
pre = ""
- handler_msg("using protocol version 75")
+ ver = 75
+
+ handler_msg("%s WebSocket connection (version %s) from %s"
+ % (stype, ver, address[0]))
response = server_handshake % (pre, h['Origin'], pre, scheme,
h['Host'], h['path'], pre, trailer)
try:
if fd != keepfd:
os.close(fd)
- elif settings['verbose']:
- print "Keeping fd: %d" % fd
+ else:
+ handler_vmsg("Keeping fd: %d" % fd)
except OSError, exc:
if exc.errno != errno.EBADF: raise
csock = startsock = None
pid = 0
startsock, address = lsock.accept()
- handler_msg('got client connection from %s' % address[0])
-
- handler_msg("forking handler process")
+ handler_vmsg('%s: forking handler' % address[0])
pid = os.fork()
if pid == 0: # handler process
- csock = do_handshake(startsock)
- if not csock:
- handler_msg("No connection after handshake");
- break
+ csock = do_handshake(startsock, address)
settings['handler'](csock)
else: # parent process
settings['handler_id'] += 1
except EClose, exc:
- handler_msg("handler exit: %s" % exc.args)
+ if csock and csock != startsock:
+ csock.close()
+ startsock.close()
+ if exc.args[0]:
+ handler_msg("%s: %s" % (address[0], exc.args[0]))
except Exception, exc:
handler_msg("handler exception: %s" % str(exc))
- #handler_msg(traceback.format_exc())
+ if settings['verbose']:
+ handler_msg(traceback.format_exc())
if pid == 0:
if csock: csock.close()