]> git.proxmox.com Git - mirror_novnc.git/blame - utils/wsproxy.py
Add daemonization support to wsproxy.*.
[mirror_novnc.git] / utils / wsproxy.py
CommitLineData
3292c4a9
JM
1#!/usr/bin/python
2
adfe6ac1
JM
3'''
4A WebSocket to TCP socket proxy with support for "wss://" encryption.
af6b17ce
JM
5Copyright 2010 Joel Martin
6Licensed under LGPL version 3 (see LICENSE.LGPL-3)
adfe6ac1
JM
7
8You can make a cert/key with openssl using:
9openssl req -new -x509 -days 365 -nodes -out self.pem -keyout self.pem
10as taken from http://docs.python.org/dev/library/ssl.html#certificates
11
12'''
13
325d9eb7 14import sys, socket, ssl, optparse
3292c4a9 15from select import select
95ef30a1 16from websocket import *
3292c4a9 17
64ab5c4d 18buffer_size = 65536
325d9eb7 19rec = None
5d2c3864 20
5d8e7ec0
JM
21traffic_legend = """
22Traffic Legend:
23 } - Client receive
24 }. - Client receive partial
25 { - Target receive
26
27 > - Target send
28 >. - Target send partial
29 < - Client send
30 <. - Client send partial
31"""
32
95ef30a1 33def do_proxy(client, target):
5d2c3864 34 """ Proxy WebSocket to normal socket. """
325d9eb7 35 global rec
ce0e28c7 36 cqueue = []
5d2c3864 37 cpartial = ""
ce0e28c7 38 tqueue = []
92f572a2 39 rlist = [client, target]
3292c4a9 40
ce0e28c7 41 while True:
92f572a2
JM
42 wlist = []
43 if tqueue: wlist.append(target)
44 if cqueue: wlist.append(client)
45 ins, outs, excepts = select(rlist, wlist, [], 1)
ce0e28c7
JM
46 if excepts: raise Exception("Socket exception")
47
92f572a2 48 if target in outs:
5d8e7ec0
JM
49 dat = tqueue.pop(0)
50 sent = target.send(dat)
51 if sent == len(dat):
52 traffic(">")
53 else:
54 tqueue.insert(0, dat[sent:])
95ef30a1 55 traffic(".>")
325d9eb7 56 ##if rec: rec.write("Target send: %s\n" % map(ord, dat))
5d8e7ec0 57
92f572a2 58 if client in outs:
5d8e7ec0
JM
59 dat = cqueue.pop(0)
60 sent = client.send(dat)
61 if sent == len(dat):
62 traffic("<")
325d9eb7
JM
63 ##if rec: rec.write("Client send: %s ...\n" % repr(dat[0:80]))
64 if rec: rec.write("%s,\n" % repr(">" + dat[1:-1]))
5d8e7ec0
JM
65 else:
66 cqueue.insert(0, dat[sent:])
67 traffic("<.")
325d9eb7 68 ##if rec: rec.write("Client send partial: %s\n" % repr(dat[0:send]))
5d8e7ec0
JM
69
70
71 if target in ins:
72 buf = target.recv(buffer_size)
73 if len(buf) == 0: raise Exception("Target closed")
74
95ef30a1 75 cqueue.append(encode(buf))
5d8e7ec0 76 traffic("{")
325d9eb7 77 ##if rec: rec.write("Target recv (%d): %s\n" % (len(buf), map(ord, buf)))
5d8e7ec0 78
ce0e28c7 79 if client in ins:
64ab5c4d 80 buf = client.recv(buffer_size)
ce0e28c7 81 if len(buf) == 0: raise Exception("Client closed")
5d2c3864 82
95ef30a1
JM
83 if buf[-1] == '\xff':
84 if buf.count('\xff') > 1:
85 traffic(str(buf.count('\xff')))
5d8e7ec0 86 traffic("}")
325d9eb7
JM
87 ##if rec: rec.write("Client recv (%d): %s\n" % (len(buf), repr(buf)))
88 if rec: rec.write("%s,\n" % repr(buf[1:-1]))
5d2c3864
JM
89 if cpartial:
90 tqueue.extend(decode(cpartial + buf))
91 cpartial = ""
92 else:
93 tqueue.extend(decode(buf))
5d2c3864 94 else:
95ef30a1 95 traffic(".}")
325d9eb7 96 ##if rec: rec.write("Client recv partial (%d): %s\n" % (len(buf), repr(buf)))
5d2c3864
JM
97 cpartial = cpartial + buf
98
95ef30a1 99def proxy_handler(client):
325d9eb7 100 global target_host, target_port, options, rec
95ef30a1 101
6ee61a4c
JM
102 if settings['record']:
103 print "Opening record file: %s" % settings['record']
104 rec = open(settings['record'], 'w')
105
95ef30a1
JM
106 print "Connecting to: %s:%s" % (target_host, target_port)
107 tsock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
108 tsock.connect((target_host, target_port))
ce0e28c7 109
5d8e7ec0 110 print traffic_legend
95ef30a1
JM
111
112 try:
113 do_proxy(client, tsock)
114 except:
115 if tsock: tsock.close()
325d9eb7 116 if rec: rec.close()
95ef30a1 117 raise
3292c4a9
JM
118
119if __name__ == '__main__':
f2898eab
JM
120 usage = "%prog [--record FILE]"
121 usage += " [source_addr:]source_port target_addr:target_port"
122 parser = optparse.OptionParser(usage=usage)
459b2578 123 parser.add_option("--record",
325d9eb7 124 help="record session to a file", metavar="FILE")
6ee61a4c
JM
125 parser.add_option("--foreground", "-f",
126 dest="daemon", default=True, action="store_false",
127 help="stay in foreground, do not daemonize")
459b2578
JM
128 parser.add_option("--ssl-only", action="store_true",
129 help="disallow non-encrypted connections")
6ee61a4c
JM
130 parser.add_option("--cert", default="self.pem",
131 help="SSL certificate")
325d9eb7
JM
132 (options, args) = parser.parse_args()
133
f2898eab
JM
134 if len(args) > 2: parser.error("Too many arguments")
135 if len(args) < 2: parser.error("Too few arguments")
136 if args[0].count(':') > 0:
6ee61a4c 137 host,port = args[0].split(':')
f2898eab 138 else:
6ee61a4c 139 host,port = '',args[0]
f2898eab
JM
140 if args[1].count(':') > 0:
141 target_host,target_port = args[1].split(':')
142 else:
143 parser.error("Error parsing target")
6ee61a4c 144 try: port = int(port)
325d9eb7 145 except: parser.error("Error parsing listen port")
f2898eab 146 try: target_port = int(target_port)
325d9eb7
JM
147 except: parser.error("Error parsing target port")
148
6ee61a4c
JM
149 settings['listen_host'] = host
150 settings['listen_port'] = port
151 settings['handler'] = proxy_handler
152 settings['cert'] = os.path.abspath(options.cert)
153 settings['ssl_only'] = options.ssl_only
154 settings['daemon'] = options.daemon
155 settings['record'] = os.path.abspath(options.record)
156 start_server()