Revision 512c571e vncauthproxy/proxy.py
b/vncauthproxy/proxy.py | ||
---|---|---|
47 | 47 |
from signal import SIGINT, SIGTERM |
48 | 48 |
from gevent import signal |
49 | 49 |
from gevent.select import select |
50 |
from time import sleep |
|
50 | 51 |
|
51 | 52 |
logger = None |
52 | 53 |
|
... | ... | |
80 | 81 |
""" |
81 | 82 |
id = 1 |
82 | 83 |
|
83 |
def __init__(self, logger, listeners, pool, daddr, dport, password, connect_timeout): |
|
84 |
def __init__(self, logger, listeners, pool, daddr, dport, server, password, connect_timeout):
|
|
84 | 85 |
""" |
85 | 86 |
@type logger: logging.Logger |
86 | 87 |
@param logger: the logger to use |
... | ... | |
92 | 93 |
@param daddr: destination address (IPv4, IPv6 or hostname) |
93 | 94 |
@type dport: int |
94 | 95 |
@param dport: destination port |
96 |
@type server: socket |
|
97 |
@param server: VNC server socket |
|
95 | 98 |
@type password: str |
96 | 99 |
@param password: password to request from the client |
97 | 100 |
@type connect_timeout: int |
... | ... | |
109 | 112 |
self.pool = pool |
110 | 113 |
self.daddr = daddr |
111 | 114 |
self.dport = dport |
115 |
self.server = server |
|
112 | 116 |
self.password = password |
113 |
self.server = None |
|
114 | 117 |
self.client = None |
115 | 118 |
self.timeout = connect_timeout |
116 | 119 |
|
... | ... | |
173 | 176 |
# No need to close the source and dest sockets here. |
174 | 177 |
# They are owned by and will be closed by the original greenlet. |
175 | 178 |
|
176 |
def _handshake(self): |
|
179 |
def _client_handshake(self):
|
|
177 | 180 |
""" |
178 | 181 |
Perform handshake/authentication with a connecting client |
179 | 182 |
|
... | ... | |
184 | 187 |
4. We send an authentication challenge |
185 | 188 |
5. Client sends the authentication response |
186 | 189 |
6. We check the authentication |
187 |
7. We initiate a connection with the backend server and perform basic |
|
188 |
RFB 3.8 handshake with it. |
|
189 | 190 |
|
190 |
Upon return, self.client and self.server are sockets |
|
191 |
connected to the client and the backend server, respectively. |
|
191 |
Upon return, self.client socket is connected to the client. |
|
192 | 192 |
|
193 | 193 |
""" |
194 | 194 |
self.client.send(rfb.RFB_VERSION_3_8 + "\n") |
... | ... | |
235 | 235 |
|
236 | 236 |
# Accept the authentication |
237 | 237 |
self.client.send(rfb.to_u32(rfb.RFB_AUTH_SUCCESS)) |
238 |
|
|
239 |
# Try to connect to the server |
|
240 |
tries = 50 |
|
241 |
|
|
242 |
while tries: |
|
243 |
tries -= 1 |
|
244 |
|
|
245 |
# Initiate server connection |
|
246 |
for res in socket.getaddrinfo(self.daddr, self.dport, socket.AF_UNSPEC, |
|
247 |
socket.SOCK_STREAM, 0, socket.AI_PASSIVE): |
|
248 |
af, socktype, proto, canonname, sa = res |
|
249 |
try: |
|
250 |
self.server = socket.socket(af, socktype, proto) |
|
251 |
except socket.error, msg: |
|
252 |
self.server = None |
|
253 |
continue |
|
254 |
|
|
255 |
try: |
|
256 |
self.debug("Connecting to %s:%s" % sa[:2]) |
|
257 |
self.server.connect(sa) |
|
258 |
self.debug("Connection to %s:%s successful" % sa[:2]) |
|
259 |
except socket.error, msg: |
|
260 |
self.server.close() |
|
261 |
self.server = None |
|
262 |
continue |
|
263 |
|
|
264 |
# We succesfully connected to the server |
|
265 |
tries = 0 |
|
266 |
break |
|
267 |
|
|
268 |
# Wait and retry |
|
269 |
gevent.sleep(0.2) |
|
270 |
|
|
271 |
if self.server is None: |
|
272 |
self.error("Failed to connect to server") |
|
273 |
raise gevent.GreenletExit |
|
274 |
|
|
275 |
version = self.server.recv(1024) |
|
276 |
if not rfb.check_version(version): |
|
277 |
self.error("Unsupported RFB version: %s" % version.strip()) |
|
278 |
raise gevent.GreenletExit |
|
279 |
|
|
280 |
self.server.send(rfb.RFB_VERSION_3_8 + "\n") |
|
281 |
|
|
282 |
res = self.server.recv(1024) |
|
283 |
types = rfb.parse_auth_request(res) |
|
284 |
if not types: |
|
285 |
self.error("Error handshaking with the server") |
|
286 |
raise gevent.GreenletExit |
|
287 |
|
|
288 |
else: |
|
289 |
self.debug("Supported authentication types: %s" % |
|
290 |
" ".join([str(x) for x in types])) |
|
291 |
|
|
292 |
if rfb.RFB_AUTHTYPE_NONE not in types: |
|
293 |
self.error("Error, server demands authentication") |
|
294 |
raise gevent.GreenletExit |
|
295 |
|
|
296 |
self.server.send(rfb.to_u8(rfb.RFB_AUTHTYPE_NONE)) |
|
297 |
|
|
298 |
# Check authentication response |
|
299 |
res = self.server.recv(4) |
|
300 |
res = rfb.from_u32(res) |
|
301 |
|
|
302 |
if res != 0: |
|
303 |
self.error("Authentication error") |
|
304 |
raise gevent.GreenletExit |
|
305 | 238 |
|
306 | 239 |
def _run(self): |
307 | 240 |
try: |
... | ... | |
322 | 255 |
self.listeners.pop().close() |
323 | 256 |
break |
324 | 257 |
|
325 |
# Perform RFB handshake with the client and the backend server. |
|
326 |
# If all goes as planned, we have two connected sockets, |
|
327 |
# self.client and self.server. |
|
328 |
self._handshake() |
|
258 |
# Perform RFB handshake with the client. |
|
259 |
self._client_handshake() |
|
329 | 260 |
|
330 | 261 |
# Bridge both connections through two "forwarder" greenlets. |
331 | 262 |
self.workers = [gevent.spawn(self._forward, self.client, self.server), |
... | ... | |
384 | 315 |
|
385 | 316 |
return sockets |
386 | 317 |
|
318 |
def perform_server_handshake(daddr, dport): |
|
319 |
""" |
|
320 |
Initiate a connection with the backend server and perform basic |
|
321 |
RFB 3.8 handshake with it. |
|
322 |
|
|
323 |
Returns a socket connected to the backend server. |
|
324 |
|
|
325 |
""" |
|
326 |
server = None |
|
327 |
# Try to connect to the server |
|
328 |
tries = 50 |
|
329 |
|
|
330 |
while tries: |
|
331 |
tries -= 1 |
|
332 |
|
|
333 |
# Initiate server connection |
|
334 |
for res in socket.getaddrinfo(daddr, dport, socket.AF_UNSPEC, |
|
335 |
socket.SOCK_STREAM, 0, socket.AI_PASSIVE): |
|
336 |
af, socktype, proto, canonname, sa = res |
|
337 |
try: |
|
338 |
server = socket.socket(af, socktype, proto) |
|
339 |
except socket.error, msg: |
|
340 |
server = None |
|
341 |
continue |
|
342 |
|
|
343 |
try: |
|
344 |
logger.debug("Connecting to %s:%s" % sa[:2]) |
|
345 |
server.connect(sa) |
|
346 |
logger.debug("Connection to %s:%s successful" % sa[:2]) |
|
347 |
except socket.error, msg: |
|
348 |
server.close() |
|
349 |
server = None |
|
350 |
continue |
|
351 |
|
|
352 |
# We succesfully connected to the server |
|
353 |
tries = 0 |
|
354 |
break |
|
355 |
|
|
356 |
# Wait and retry |
|
357 |
sleep(0.2) |
|
358 |
|
|
359 |
if server is None: |
|
360 |
raise Exception("Failed to connect to server") |
|
361 |
|
|
362 |
version = server.recv(1024) |
|
363 |
if not rfb.check_version(version): |
|
364 |
raise Exception("Unsupported RFB version: %s" % version.strip()) |
|
365 |
|
|
366 |
server.send(rfb.RFB_VERSION_3_8 + "\n") |
|
367 |
|
|
368 |
res = server.recv(1024) |
|
369 |
types = rfb.parse_auth_request(res) |
|
370 |
if not types: |
|
371 |
raise Exception("Error handshaking with the server") |
|
372 |
|
|
373 |
else: |
|
374 |
logger.debug("Supported authentication types: %s" % |
|
375 |
" ".join([str(x) for x in types])) |
|
376 |
|
|
377 |
if rfb.RFB_AUTHTYPE_NONE not in types: |
|
378 |
raise Exception("Error, server demands authentication") |
|
379 |
|
|
380 |
server.send(rfb.to_u8(rfb.RFB_AUTHTYPE_NONE)) |
|
381 |
|
|
382 |
# Check authentication response |
|
383 |
res = server.recv(4) |
|
384 |
res = rfb.from_u32(res) |
|
385 |
|
|
386 |
if res != 0: |
|
387 |
raise Exception("Authentication error") |
|
388 |
|
|
389 |
return server |
|
390 |
|
|
387 | 391 |
def parse_arguments(args): |
388 | 392 |
from optparse import OptionParser |
389 | 393 |
|
... | ... | |
527 | 531 |
continue |
528 | 532 |
|
529 | 533 |
# Spawn a new Greenlet to service the request. |
534 |
server = None |
|
530 | 535 |
try: |
531 | 536 |
# If the client has so indicated, pick an ephemeral source port |
532 | 537 |
# randomly, and remove it from the port pool. |
... | ... | |
539 | 544 |
else: |
540 | 545 |
sport = sport_orig |
541 | 546 |
pool = None |
547 |
|
|
542 | 548 |
listeners = get_listening_sockets(sport) |
549 |
server = perform_server_handshake(daddr, dport) |
|
550 |
|
|
543 | 551 |
VncAuthProxy.spawn(logger, listeners, pool, daddr, dport, |
544 |
password, opts.connect_timeout) |
|
552 |
server, password, opts.connect_timeout) |
|
553 |
|
|
545 | 554 |
logger.info("New forwarding [%d (req'd by client: %d) -> %s:%d]" % |
546 | 555 |
(sport, sport_orig, daddr, dport)) |
547 | 556 |
response = { |
... | ... | |
551 | 560 |
except IndexError: |
552 | 561 |
logger.error("FAILED forwarding, out of ports for [req'd by " |
553 | 562 |
"client: %d -> %s:%d]" % (sport_orig, daddr, dport)) |
554 |
except socket.error, msg: |
|
563 |
except Exception, msg: |
|
564 |
logger.error(msg) |
|
555 | 565 |
logger.error("FAILED forwarding [%d (req'd by client: %d) -> %s:%d]" % |
556 | 566 |
(sport, sport_orig, daddr, dport)) |
557 | 567 |
if not pool is None: |
558 | 568 |
pool.append(sport) |
559 | 569 |
logger.debug("Returned port %d to port pool, contains %d ports", |
560 | 570 |
sport, len(pool)) |
571 |
if not server is None: |
|
572 |
server.close() |
|
561 | 573 |
finally: |
562 | 574 |
client.send(json.dumps(response)) |
563 | 575 |
client.close() |
Also available in: Unified diff