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