]>
git.proxmox.com Git - mirror_novnc.git/blob - utils/websocket.py
4 Python WebSocket library with support for "wss://" encryption.
6 You can make a cert/key with openssl using:
7 openssl req -new -x509 -days 365 -nodes -out self.pem -keyout self.pem
8 as taken from http://docs.python.org/dev/library/ssl.html#certificates
12 import sys
, socket
, ssl
, struct
, traceback
13 import os
, resource
, errno
, signal
# daemonizing
14 from base64
import b64encode
, b64decode
15 from hashlib
import md5
31 server_handshake
= """HTTP/1.1 101 Web Socket Protocol Handshake\r
34 %sWebSocket-Origin: %s\r
35 %sWebSocket-Location: %s://%s%s\r
36 %sWebSocket-Protocol: sample\r
40 policy_response
= """<cross-domain-policy><allow-access-from domain="*" to-ports="*" /></cross-domain-policy>\n"""
42 def traffic(token
="."):
43 sys
.stdout
.write(token
)
47 """ Parse out WebSocket packets. """
48 if buf
.count('\xff') > 1:
49 if client_settings
['b64encode']:
50 return [b64decode(d
[1:]) for d
in buf
.split('\xff')]
52 # Modified UTF-8 decode
53 return [d
[1:].replace("\xc4\x80", "\x00").decode('utf-8').encode('latin-1') for d
in buf
.split('\xff')]
55 if client_settings
['b64encode']:
56 return [b64decode(buf
[1:-1])]
58 return [buf
[1:-1].replace("\xc4\x80", "\x00").decode('utf-8').encode('latin-1')]
62 if client_settings
['b64encode']:
65 # Modified UTF-8 encode
66 buf
= buf
.decode('latin-1').encode('utf-8').replace("\x00", "\xc4\x80")
68 if client_settings
['seq_num']:
70 return "\x00%d:%s\xff" % (send_seq
-1, buf
)
72 return "\x00%s\xff" % buf
75 def do_handshake(sock
):
76 global client_settings
, send_seq
78 client_settings
['b64encode'] = False
79 client_settings
['seq_num'] = False
82 # Peek, but don't read the data
83 handshake
= sock
.recv(1024, socket
.MSG_PEEK
)
84 #print "Handshake [%s]" % repr(handshake)
85 if handshake
.startswith("<policy-file-request/>"):
86 handshake
= sock
.recv(1024)
87 print "Sending flash policy response"
88 sock
.send(policy_response
)
91 elif handshake
.startswith("\x16"):
92 retsock
= ssl
.wrap_socket(
95 certfile
=settings
['cert'],
96 ssl_version
=ssl
.PROTOCOL_TLSv1
)
98 print " using SSL/TLS"
99 elif settings
['ssl_only']:
100 print "Non-SSL connection disallowed"
106 print " using plain (not SSL) socket"
107 handshake
= retsock
.recv(4096)
108 #print "handshake: " + repr(handshake)
109 h
= parse_handshake(handshake
)
111 # Parse client settings from the GET path
112 cvars
= h
['path'].partition('?')[2].partition('#')[0].split('&')
113 for cvar
in [c
for c
in cvars
if c
]:
114 name
, _
, val
= cvar
.partition('=')
115 if name
not in ['b64encode', 'seq_num']: continue
116 value
= val
and val
or True
117 client_settings
[name
] = value
118 print " %s=%s" % (name
, value
)
127 response
= server_handshake
% (pre
, h
['Origin'], pre
, scheme
,
128 h
['Host'], h
['path'], pre
, trailer
)
130 #print "sending response:", repr(response)
131 retsock
.send(response
)
134 def parse_handshake(handshake
):
136 req_lines
= handshake
.split("\r\n")
137 if not req_lines
[0].startswith("GET "):
138 raise "Invalid handshake: no GET request line"
139 ret
['path'] = req_lines
[0].split(" ")[1]
140 for line
in req_lines
[1:]:
142 var
, delim
, val
= line
.partition(": ")
145 if req_lines
[-2] == "":
146 ret
['key3'] = req_lines
[-1]
151 key1
= keys
['Sec-WebSocket-Key1']
152 key2
= keys
['Sec-WebSocket-Key2']
154 spaces1
= key1
.count(" ")
155 spaces2
= key2
.count(" ")
156 num1
= int("".join([c
for c
in key1
if c
.isdigit()])) / spaces1
157 num2
= int("".join([c
for c
in key2
if c
.isdigit()])) / spaces2
159 return md5(struct
.pack('>II8s', num1
, num2
, key3
)).digest()
164 os
.setgid(os
.getgid()) # relinquish elevations
165 os
.setuid(os
.getuid()) # relinquish elevations
167 # Double fork to daemonize
168 if os
.fork() > 0: os
._exit
(0) # Parent exits
169 os
.setsid() # Obtain new process group
170 if os
.fork() > 0: os
._exit
(0) # Parent exits
173 def terminate(a
,b
): os
._exit
(0)
174 signal
.signal(signal
.SIGTERM
, terminate
)
175 signal
.signal(signal
.SIGINT
, signal
.SIG_IGN
)
178 maxfd
= resource
.getrlimit(resource
.RLIMIT_NOFILE
)[1]
179 if maxfd
== resource
.RLIM_INFINITY
: maxfd
= 256
180 for fd
in reversed(range(maxfd
)):
184 if exc
.errno
!= errno
.EBADF
: raise
186 # Redirect I/O to /dev/null
187 os
.dup2(os
.open(os
.devnull
, os
.O_RDWR
), sys
.stdin
.fileno())
188 os
.dup2(os
.open(os
.devnull
, os
.O_RDWR
), sys
.stdout
.fileno())
189 os
.dup2(os
.open(os
.devnull
, os
.O_RDWR
), sys
.stderr
.fileno())
194 if settings
['daemon']: daemonize()
196 lsock
= socket
.socket(socket
.AF_INET
, socket
.SOCK_STREAM
)
197 lsock
.setsockopt(socket
.SOL_SOCKET
, socket
.SO_REUSEADDR
, 1)
198 lsock
.bind((settings
['listen_host'], settings
['listen_port']))
202 csock
= startsock
= None
203 print 'waiting for connection on port %s' % settings
['listen_port']
204 startsock
, address
= lsock
.accept()
205 print 'Got client connection from %s' % address
[0]
206 csock
= do_handshake(startsock
)
207 if not csock
: continue
209 settings
['handler'](csock
)
212 print "Ignoring exception:"
213 print traceback
.format_exc()
214 if csock
: csock
.close()
215 if startsock
and startsock
!= csock
: startsock
.close()