Revision 4331e4d8 vncauthproxy/proxy.py

b/vncauthproxy/proxy.py
88 88
# Take care of differences between python-daemon versions.
89 89
try:
90 90
    from daemon import pidfile as pidlockfile
91
except:
91
except ImportError:
92 92
    from daemon import pidlockfile
93 93

  
94 94

  
95 95
logger = None
96 96

  
97 97

  
98
class InternalError(Exception):
99
    """Exception for internal vncauthproxy errors"""
100
    pass
101

  
102

  
98 103
# Currently, gevent uses libevent-dns for asynchronous DNS resolution,
99 104
# which opens a socket upon initialization time. Since we can't get the fd
100 105
# reliably, We have to maintain all file descriptors open (which won't harm
......
241 246
                    self.debug("Connecting to %s:%s", *sa[:2])
242 247
                    server.connect(sa)
243 248
                    self.debug("Connection to %s:%s successful", *sa[:2])
244
                except socket.error:
249
                except socket.error as err:
250
                    self.debug("Failed to perform sever hanshake, retrying...")
245 251
                    server.close()
246 252
                    server = None
247 253
                    continue
......
254 260
            gevent.sleep(VncAuthProxy.retry_wait)
255 261

  
256 262
        if server is None:
257
            raise Exception("Failed to connect to server")
263
            raise InternalError("Failed to connect to server")
258 264

  
259 265
        version = server.recv(1024)
260 266
        if not rfb.check_version(version):
261
            raise Exception("Unsupported RFB version: %s" % version.strip())
267
            raise InternalError("Unsupported RFB version: %s"
268
                                % version.strip())
262 269

  
263 270
        server.send(rfb.RFB_VERSION_3_8 + "\n")
264 271

  
265 272
        res = server.recv(1024)
266 273
        types = rfb.parse_auth_request(res)
267 274
        if not types:
268
            raise Exception("Error handshaking with the server")
275
            raise InternalError("Error handshaking with the server")
269 276

  
270 277
        else:
271 278
            self.debug("Supported authentication types: %s",
272 279
                         " ".join([str(x) for x in types]))
273 280

  
274 281
        if rfb.RFB_AUTHTYPE_NONE not in types:
275
            raise Exception("Error, server demands authentication")
282
            raise InternalError("Error, server demands authentication")
276 283

  
277 284
        server.send(rfb.to_u8(rfb.RFB_AUTHTYPE_NONE))
278 285

  
......
281 288
        res = rfb.from_u32(res)
282 289

  
283 290
        if res != 0:
284
            raise Exception("Authentication error")
291
            raise InternalError("Authentication error")
285 292

  
286 293
        # Reset the timeout for the rest of the session
287 294
        server.settimeout(None)
......
298 305
            "status": "FAILED",
299 306
        }
300 307
        try:
301
            # TODO: support multiple forwardings in the same message?
302
            #
303 308
            # Control request, in JSON:
304 309
            #
305 310
            # {
......
340 345

  
341 346
            if auth_user not in VncAuthProxy.authdb:
342 347
                msg = "Authentication failure: user not found"
343
                raise Exception(msg)
348
                raise InternalError(msg)
344 349

  
345 350
            (cipher, authdb_password) = VncAuthProxy.authdb[auth_user]
346 351
            if cipher == 'HA1':
......
349 354

  
350 355
            if auth_password != authdb_password:
351 356
                msg = "Authentication failure: wrong password"
352
                raise Exception(msg)
357
                raise InternalError(msg)
353 358
        except KeyError:
354 359
            msg = "Malformed request: %s" % buf
355
            raise Exception(msg)
360
            raise InternalError(msg)
361
        except InternalError as err:
362
            self.warn(err)
363
            response['reason'] = str(err)
364
            client.send(json.dumps(response))
365
            client.close()
366
            raise gevent.GreenletExit
356 367
        except Exception as err:
357
            logger.exception(err)
358
            msg = err.args
359
            self.warn(msg)
360
            response['reason'] = msg
368
            self.exception(err)
369
            self.error("Unexpected error")
361 370
            client.send(json.dumps(response))
362 371
            client.close()
363 372
            raise gevent.GreenletExit
364 373

  
365 374
        server = None
375
        pool = None
376
        sport = sport_orig
366 377
        try:
367 378
            # If the client has so indicated, pick an ephemeral source port
368 379
            # randomly, and remove it from the port pool.
......
376 387
                        self.debug("Port %d already taken", sport)
377 388

  
378 389
                self.debug("Got port %d from pool, %d remaining",
379
                             sport, len(ports))
390
                           sport, len(ports))
380 391
                pool = ports
381
            else:
382
                sport = sport_orig
383
                pool = None
384 392

  
385 393
            self.sport = sport
386 394
            self.pool = pool
387 395

  
388
            self.listeners = get_listening_sockets(self, sport)
396
            self.listeners = get_listening_sockets(sport)
389 397
            self._perform_server_handshake()
390 398

  
391 399
            self.info("New forwarding: %d (client req'd: %d) -> %s:%d",
......
397 405
                          "client: %d -> %s:%d]"),
398 406
                         sport_orig, self.daddr, self.dport)
399 407
            raise gevent.GreenletExit
400
        except Exception, msg:
401
            self.error(msg)
408
        except InternalError as err:
409
            self.error(err)
410
            self.error(("FAILED forwarding: %d (client req'd: %d) -> "
411
                          "%s:%d"), sport, sport_orig, self.daddr, self.dport)
412
            if pool:
413
                pool.append(sport)
414
                self.debug("Returned port %d to pool, %d remanining",
415
                             sport, len(pool))
416
            if server:
417
                server.close()
418
            raise gevent.GreenletExit
419
        except Exception as err:
420
            self.exception(err)
421
            self.error("Unexpected error")
402 422
            self.error(("FAILED forwarding: %d (client req'd: %d) -> "
403 423
                          "%s:%d"), sport, sport_orig, self.daddr, self.dport)
404
            if not pool is None:
424
            if pool:
405 425
                pool.append(sport)
406 426
                self.debug("Returned port %d to pool, %d remanining",
407 427
                             sport, len(pool))
408
            if not server is None:
428
            if server:
409 429
                server.close()
410 430
            raise gevent.GreenletExit
411 431
        finally:
......
526 546
            # all worker, socket and port cleanup
527 547
            self.debug("A forwarder died, our work here is done")
528 548
            raise gevent.GreenletExit
529
        except Exception, e:
549
        except Exception as err:
530 550
            # Any unhandled exception in the previous block
531 551
            # is an error and must be logged accordingly
532 552
            if not isinstance(e, gevent.GreenletExit):
533
                self.exception(e)
534
            raise e
553
                self.exception(err)
554
                self.error("Unexpected error")
555
            raise err
535 556
        finally:
536 557
            self._cleanup()
537 558

  
......
557 578
    raise SystemExit
558 579

  
559 580

  
560
def get_listening_sockets(logger, sport, saddr=None, reuse_addr=False):
581
def get_listening_sockets(sport, saddr=None, reuse_addr=False):
561 582
    sockets = []
562 583

  
563 584
    # Use two sockets, one for IPv4, one for IPv6. IPv4-to-IPv6 mapped
......
582 603
            s.listen(1)
583 604
            sockets.append(s)
584 605
            logger.debug("Listening on %s:%d", *sa[:2])
585
        except socket.error, msg:
586
            logger.error("Error binding to %s:%d: %s", sa[0], sa[1], msg[1])
606
        except socket.error as err:
607
            logger.error("Error binding to %s:%d: %s", sa[0], sa[1], err[1])
587 608
            if s:
588 609
                s.close()
589 610
            while sockets:
590 611
                sock = sockets.pop().close()
591 612

  
592 613
            # Make sure we fail immediately if we cannot get a socket
593
            raise msg
614
            raise InernalError(err)
594 615

  
595 616
    return sockets
596 617

  
......
599 620
    supported_ciphers = ('cleartext', 'HA1')
600 621

  
601 622
    users = {}
602
    with open(auth_file) as f:
603
        lines = [l.strip().split() for l in f.readlines()]
623
    try:
624
        with open(auth_file) as f:
625
            lines = [l.strip().split() for l in f.readlines()]
604 626

  
605
        for line in lines:
606
            if not line or line[0][0] == '#':
607
                continue
627
            for line in lines:
628
                if not line or line[0][0] == '#':
629
                    continue
608 630

  
609
            if len(line) != 2:
610
                raise Exception("Invaild user entry in auth file")
631
                if len(line) != 2:
632
                    raise InternalError("Invaild user entry in auth file")
611 633

  
612
            user = line[0]
613
            password = line[1]
634
                user = line[0]
635
                password = line[1]
614 636

  
615
            split_password = ('{cleartext}', password)
616
            if password[0] == '{':
617
                split_password = password[1:].split('}')
618
                if len(split_password) != 2 or not split_password[1] \
619
                        or split_password[0] not in supported_ciphers:
620
                    raise Exception("Invalid password format in auth file")
637
                split_password = ('{cleartext}', password)
638
                if password[0] == '{':
639
                    split_password = password[1:].split('}')
640
                    if len(split_password) != 2 or not split_password[1] \
641
                            or split_password[0] not in supported_ciphers:
642
                        raise InternalError("Invalid password format "
643
                                            "in auth file")
621 644

  
622
            if user in users:
623
                raise Exception("Duplicate user entry in auth file")
645
                if user in users:
646
                    raise InternalError("Duplicate user entry in auth file")
624 647

  
625
            users[user] = split_password
648
                users[user] = split_password
649
    except IOError as err:
650
        logger.error("Couldn't read auth file")
651
        raise InternalError(err)
626 652

  
627 653
    if not users:
628 654
        logger.warn("No users specified.")
......
786 812

  
787 813
    try:
788 814
        VncAuthProxy.authdb = parse_auth_file(opts.auth_file)
789
        sockets = get_listening_sockets(logger, opts.listen_port,
790
                                        opts.listen_address, reuse_addr=True)
791
    except socket.error as err:
815
    except InternalError as err:
816
        logger.critical(err)
817
        sys.exit(1)
818
    except Exception as err:
792 819
        logger.exception(err)
820
        logger.error("Unexpected error")
821
        sys.exit(1)
822

  
823
    try:
824
        sockets = get_listening_sockets(opts.listen_port, opts.listen_address,
825
                                        reuse_addr=True)
826
    except InternalError as err:
793 827
        logger.critical("Error binding control socket")
794 828
        sys.exit(1)
795 829
    except Exception as err:
796 830
        logger.exception(err)
797
        logger.critical("Unexpected error: %s", err.args)
831
        logger.error("Unexpected error")
798 832
        sys.exit(1)
799 833

  
800 834
    while True:
......
815 849
            continue
816 850
        except Exception, e:
817 851
            logger.exception(e)
852
            logger.error("Unexpected error")
818 853
            if client:
819 854
                client.close()
820 855
            continue

Also available in: Unified diff