diff --git a/README.md b/README.md index 4d1b1e51..ead2abd6 100644 --- a/README.md +++ b/README.md @@ -137,7 +137,7 @@ There a few reasons why a proxy is required: `./utils/wsproxy.py -f 8787 localhost:5901` -* To run the mini python web server without the launch script: +* To run a mini python web server without the launch script: `./utils/web.py PORT` diff --git a/utils/launch.sh b/utils/launch.sh index 1ee7d15b..4ed9f92d 100755 --- a/utils/launch.sh +++ b/utils/launch.sh @@ -5,26 +5,25 @@ usage() { 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() { @@ -36,10 +35,6 @@ cleanup() { 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} @@ -52,12 +47,12 @@ cleanup() { 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 @@ -65,40 +60,39 @@ 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 @@ -110,8 +104,7 @@ else 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} diff --git a/utils/websocket.py b/utils/websocket.py index 70748c1d..7efd01a3 100755 --- a/utils/websocket.py +++ b/utils/websocket.py @@ -13,6 +13,8 @@ as taken from http://docs.python.org/dev/library/ssl.html#certificates 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 @@ -31,7 +33,8 @@ settings = { '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 @@ -47,6 +50,29 @@ policy_response = """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(""): 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) @@ -177,8 +227,8 @@ def daemonize(keepfd=None): 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 @@ -209,25 +259,25 @@ def start_server(): 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() diff --git a/utils/wsproxy.py b/utils/wsproxy.py index 378e4743..32a4021e 100755 --- a/utils/wsproxy.py +++ b/utils/wsproxy.py @@ -143,6 +143,8 @@ if __name__ == '__main__': help="SSL key file (if separate from cert)") parser.add_option("--ssl-only", action="store_true", help="disallow non-encrypted connections") + parser.add_option("--web", default=None, metavar="DIR", + help="run webserver on same port. Serve files from DIR.") (options, args) = parser.parse_args() if len(args) > 2: parser.error("Too many arguments") @@ -176,4 +178,7 @@ if __name__ == '__main__': settings['daemon'] = options.daemon if options.record: settings['record'] = os.path.abspath(options.record) + if options.web: + os.chdir = options.web + settings['web'] = options.web start_server()