265
235
                       .replace(b"\n", b"\\n")
 
266
236
                       .replace(b"\0", b"\\x00"))
 
269
239
    def encrypt(self, data, password):
 
270
240
        passphrase = self.password_encode(password)
 
271
241
        with tempfile.NamedTemporaryFile(
 
272
242
                dir=self.tempdir) as passfile:
 
273
243
            passfile.write(passphrase)
 
275
 
            proc = subprocess.Popen([self.gpg, "--symmetric",
 
 
245
            proc = subprocess.Popen([self.gpg, '--symmetric',
 
278
248
                                    + self.gnupgargs,
 
279
 
                                    stdin=subprocess.PIPE,
 
280
 
                                    stdout=subprocess.PIPE,
 
281
 
                                    stderr=subprocess.PIPE)
 
282
 
            ciphertext, err = proc.communicate(input=data)
 
 
249
                                    stdin = subprocess.PIPE,
 
 
250
                                    stdout = subprocess.PIPE,
 
 
251
                                    stderr = subprocess.PIPE)
 
 
252
            ciphertext, err = proc.communicate(input = data)
 
283
253
        if proc.returncode != 0:
 
284
254
            raise PGPError(err)
 
285
255
        return ciphertext
 
287
257
    def decrypt(self, data, password):
 
288
258
        passphrase = self.password_encode(password)
 
289
259
        with tempfile.NamedTemporaryFile(
 
290
 
                dir=self.tempdir) as passfile:
 
 
260
                dir = self.tempdir) as passfile:
 
291
261
            passfile.write(passphrase)
 
293
 
            proc = subprocess.Popen([self.gpg, "--decrypt",
 
 
263
            proc = subprocess.Popen([self.gpg, '--decrypt',
 
296
266
                                    + self.gnupgargs,
 
297
 
                                    stdin=subprocess.PIPE,
 
298
 
                                    stdout=subprocess.PIPE,
 
299
 
                                    stderr=subprocess.PIPE)
 
300
 
            decrypted_plaintext, err = proc.communicate(input=data)
 
 
267
                                    stdin = subprocess.PIPE,
 
 
268
                                    stdout = subprocess.PIPE,
 
 
269
                                    stderr = subprocess.PIPE)
 
 
270
            decrypted_plaintext, err = proc.communicate(input = data)
 
301
271
        if proc.returncode != 0:
 
302
272
            raise PGPError(err)
 
303
273
        return decrypted_plaintext
 
306
275
# Pretend that we have an Avahi module
 
308
 
    """This isn't so much a class as it is a module-like namespace."""
 
309
 
    IF_UNSPEC = -1               # avahi-common/address.h
 
310
 
    PROTO_UNSPEC = -1            # avahi-common/address.h
 
311
 
    PROTO_INET = 0               # avahi-common/address.h
 
312
 
    PROTO_INET6 = 1              # avahi-common/address.h
 
 
277
    """This isn't so much a class as it is a module-like namespace.
 
 
278
    It is instantiated once, and simulates having an Avahi module."""
 
 
279
    IF_UNSPEC = -1              # avahi-common/address.h
 
 
280
    PROTO_UNSPEC = -1           # avahi-common/address.h
 
 
281
    PROTO_INET = 0              # avahi-common/address.h
 
 
282
    PROTO_INET6 = 1             # avahi-common/address.h
 
313
283
    DBUS_NAME = "org.freedesktop.Avahi"
 
314
284
    DBUS_INTERFACE_ENTRY_GROUP = DBUS_NAME + ".EntryGroup"
 
315
285
    DBUS_INTERFACE_SERVER = DBUS_NAME + ".Server"
 
316
286
    DBUS_PATH_SERVER = "/"
 
319
 
    def string_array_to_txt_array(t):
 
 
287
    def string_array_to_txt_array(self, t):
 
320
288
        return dbus.Array((dbus.ByteArray(s.encode("utf-8"))
 
321
289
                           for s in t), signature="ay")
 
322
 
    ENTRY_GROUP_ESTABLISHED = 2  # avahi-common/defs.h
 
323
 
    ENTRY_GROUP_COLLISION = 3    # avahi-common/defs.h
 
324
 
    ENTRY_GROUP_FAILURE = 4      # avahi-common/defs.h
 
325
 
    SERVER_INVALID = 0           # avahi-common/defs.h
 
326
 
    SERVER_REGISTERING = 1       # avahi-common/defs.h
 
327
 
    SERVER_RUNNING = 2           # avahi-common/defs.h
 
328
 
    SERVER_COLLISION = 3         # avahi-common/defs.h
 
329
 
    SERVER_FAILURE = 4           # avahi-common/defs.h
 
 
290
    ENTRY_GROUP_ESTABLISHED = 2 # avahi-common/defs.h
 
 
291
    ENTRY_GROUP_COLLISION = 3   # avahi-common/defs.h
 
 
292
    ENTRY_GROUP_FAILURE = 4     # avahi-common/defs.h
 
 
293
    SERVER_INVALID = 0          # avahi-common/defs.h
 
 
294
    SERVER_REGISTERING = 1      # avahi-common/defs.h
 
 
295
    SERVER_RUNNING = 2          # avahi-common/defs.h
 
 
296
    SERVER_COLLISION = 3        # avahi-common/defs.h
 
 
297
    SERVER_FAILURE = 4          # avahi-common/defs.h
 
332
300
class AvahiError(Exception):
 
333
301
    def __init__(self, value, *args, **kwargs):
 
 
523
492
class AvahiServiceToSyslog(AvahiService):
 
524
493
    def rename(self, *args, **kwargs):
 
525
494
        """Add the new name to the syslog messages"""
 
526
 
        ret = super(AvahiServiceToSyslog, self).rename(*args,
 
 
495
        ret = AvahiService.rename(self, *args, **kwargs)
 
528
496
        syslogger.setFormatter(logging.Formatter(
 
529
 
            "Mandos ({}) [%(process)d]: %(levelname)s: %(message)s"
 
 
497
            'Mandos ({}) [%(process)d]: %(levelname)s: %(message)s'
 
530
498
            .format(self.name)))
 
534
501
# Pretend that we have a GnuTLS module
 
536
 
    """This isn't so much a class as it is a module-like namespace."""
 
538
 
    library = ctypes.util.find_library("gnutls")
 
540
 
        library = ctypes.util.find_library("gnutls-deb0")
 
541
 
    _library = ctypes.cdll.LoadLibrary(library)
 
 
502
class GnuTLS(object):
 
 
503
    """This isn't so much a class as it is a module-like namespace.
 
 
504
    It is instantiated once, and simulates having a GnuTLS module."""
 
 
506
    _library = ctypes.cdll.LoadLibrary(
 
 
507
        ctypes.util.find_library("gnutls"))
 
 
508
    _need_version = b"3.3.0"
 
 
510
        # Need to use class name "GnuTLS" here, since this method is
 
 
511
        # called before the assignment to the "gnutls" global variable
 
 
513
        if GnuTLS.check_version(self._need_version) is None:
 
 
514
            raise GnuTLS.Error("Needs GnuTLS {} or later"
 
 
515
                               .format(self._need_version))
 
544
517
    # Unless otherwise indicated, the constants and types below are
 
545
518
    # all from the gnutls/gnutls.h C header file.
 
549
522
    E_INTERRUPTED = -52
 
555
527
    CRD_CERTIFICATE = 1
 
556
528
    E_NO_CERTIFICATE_FOUND = -49
 
561
 
    KEYID_USE_SHA256 = 1        # gnutls/x509.h
 
562
529
    OPENPGP_FMT_RAW = 0         # gnutls/openpgp.h
 
565
 
    class _session_int(ctypes.Structure):
 
 
532
    class session_int(ctypes.Structure):
 
567
 
    session_t = ctypes.POINTER(_session_int)
 
 
534
    session_t = ctypes.POINTER(session_int)
 
569
535
    class certificate_credentials_st(ctypes.Structure):
 
571
537
    certificate_credentials_t = ctypes.POINTER(
 
572
538
        certificate_credentials_st)
 
573
539
    certificate_type_t = ctypes.c_int
 
575
540
    class datum_t(ctypes.Structure):
 
576
 
        _fields_ = [("data", ctypes.POINTER(ctypes.c_ubyte)),
 
577
 
                    ("size", ctypes.c_uint)]
 
579
 
    class _openpgp_crt_int(ctypes.Structure):
 
 
541
        _fields_ = [('data', ctypes.POINTER(ctypes.c_ubyte)),
 
 
542
                    ('size', ctypes.c_uint)]
 
 
543
    class openpgp_crt_int(ctypes.Structure):
 
581
 
    openpgp_crt_t = ctypes.POINTER(_openpgp_crt_int)
 
582
 
    openpgp_crt_fmt_t = ctypes.c_int  # gnutls/openpgp.h
 
 
545
    openpgp_crt_t = ctypes.POINTER(openpgp_crt_int)
 
 
546
    openpgp_crt_fmt_t = ctypes.c_int # gnutls/openpgp.h
 
583
547
    log_func = ctypes.CFUNCTYPE(None, ctypes.c_int, ctypes.c_char_p)
 
584
548
    credentials_type_t = ctypes.c_int
 
585
549
    transport_ptr_t = ctypes.c_void_p
 
586
550
    close_request_t = ctypes.c_int
 
589
553
    class Error(Exception):
 
590
 
        def __init__(self, message=None, code=None, args=()):
 
 
554
        # We need to use the class name "GnuTLS" here, since this
 
 
555
        # exception might be raised from within GnuTLS.__init__,
 
 
556
        # which is called before the assignment to the "gnutls"
 
 
557
        # global variable has happened.
 
 
558
        def __init__(self, message = None, code = None, args=()):
 
591
559
            # Default usage is by a message string, but if a return
 
592
560
            # code is passed, convert it to a string with
 
593
561
            # gnutls.strerror()
 
595
563
            if message is None and code is not None:
 
596
 
                message = gnutls.strerror(code).decode(
 
597
 
                    "utf-8", errors="replace")
 
598
 
            return super(gnutls.Error, self).__init__(
 
 
564
                message = GnuTLS.strerror(code)
 
 
565
            return super(GnuTLS.Error, self).__init__(
 
601
568
    class CertificateSecurityError(Error):
 
605
 
        def __init__(self, cls):
 
608
 
        def from_param(self, obj):
 
609
 
            if not isinstance(obj, self.cls):
 
610
 
                raise TypeError("Not of type {}: {!r}"
 
611
 
                                .format(self.cls.__name__, obj))
 
612
 
            return ctypes.byref(obj.from_param(obj))
 
614
 
    class CastToVoidPointer:
 
615
 
        def __init__(self, cls):
 
618
 
        def from_param(self, obj):
 
619
 
            if not isinstance(obj, self.cls):
 
620
 
                raise TypeError("Not of type {}: {!r}"
 
621
 
                                .format(self.cls.__name__, obj))
 
622
 
            return ctypes.cast(obj.from_param(obj), ctypes.c_void_p)
 
624
 
    class With_from_param:
 
626
 
        def from_param(cls, obj):
 
627
 
            return obj._as_parameter_
 
630
 
    class Credentials(With_from_param):
 
 
572
    class Credentials(object):
 
631
573
        def __init__(self):
 
632
 
            self._as_parameter_ = gnutls.certificate_credentials_t()
 
633
 
            gnutls.certificate_allocate_credentials(self)
 
 
574
            self._c_object = gnutls.certificate_credentials_t()
 
 
575
            gnutls.certificate_allocate_credentials(
 
 
576
                ctypes.byref(self._c_object))
 
634
577
            self.type = gnutls.CRD_CERTIFICATE
 
636
579
        def __del__(self):
 
637
 
            gnutls.certificate_free_credentials(self)
 
639
 
    class ClientSession(With_from_param):
 
640
 
        def __init__(self, socket, credentials=None):
 
641
 
            self._as_parameter_ = gnutls.session_t()
 
642
 
            gnutls_flags = gnutls.CLIENT
 
643
 
            if gnutls.check_version(b"3.5.6"):
 
644
 
                gnutls_flags |= gnutls.NO_TICKETS
 
646
 
                gnutls_flags |= gnutls.ENABLE_RAWPK
 
647
 
            gnutls.init(self, gnutls_flags)
 
649
 
            gnutls.set_default_priority(self)
 
650
 
            gnutls.transport_set_ptr(self, socket.fileno())
 
651
 
            gnutls.handshake_set_private_extensions(self, True)
 
 
580
            gnutls.certificate_free_credentials(self._c_object)
 
 
582
    class ClientSession(object):
 
 
583
        def __init__(self, socket, credentials = None):
 
 
584
            self._c_object = gnutls.session_t()
 
 
585
            gnutls.init(ctypes.byref(self._c_object), gnutls.CLIENT)
 
 
586
            gnutls.set_default_priority(self._c_object)
 
 
587
            gnutls.transport_set_ptr(self._c_object, socket.fileno())
 
 
588
            gnutls.handshake_set_private_extensions(self._c_object,
 
652
590
            self.socket = socket
 
653
591
            if credentials is None:
 
654
592
                credentials = gnutls.Credentials()
 
655
 
            gnutls.credentials_set(self, credentials.type,
 
 
593
            gnutls.credentials_set(self._c_object, credentials.type,
 
 
594
                                   ctypes.cast(credentials._c_object,
 
657
596
            self.credentials = credentials
 
659
598
        def __del__(self):
 
 
599
            gnutls.deinit(self._c_object)
 
662
601
        def handshake(self):
 
663
 
            return gnutls.handshake(self)
 
 
602
            return gnutls.handshake(self._c_object)
 
665
604
        def send(self, data):
 
666
605
            data = bytes(data)
 
667
606
            data_len = len(data)
 
668
607
            while data_len > 0:
 
669
 
                data_len -= gnutls.record_send(self, data[-data_len:],
 
 
608
                data_len -= gnutls.record_send(self._c_object,
 
673
 
            return gnutls.bye(self, gnutls.SHUT_RDWR)
 
 
613
            return gnutls.bye(self._c_object, gnutls.SHUT_RDWR)
 
675
615
    # Error handling functions
 
676
616
    def _error_code(result):
 
677
617
        """A function to raise exceptions on errors, suitable
 
678
 
        for the "restype" attribute on ctypes functions"""
 
679
 
        if result >= gnutls.E_SUCCESS:
 
 
618
        for the 'restype' attribute on ctypes functions"""
 
681
621
        if result == gnutls.E_NO_CERTIFICATE_FOUND:
 
682
 
            raise gnutls.CertificateSecurityError(code=result)
 
683
 
        raise gnutls.Error(code=result)
 
685
 
    def _retry_on_error(result, func, arguments,
 
686
 
                        _error_code=_error_code):
 
 
622
            raise gnutls.CertificateSecurityError(code = result)
 
 
623
        raise gnutls.Error(code = result)
 
 
625
    def _retry_on_error(result, func, arguments):
 
687
626
        """A function to retry on some errors, suitable
 
688
 
        for the "errcheck" attribute on ctypes functions"""
 
689
 
        while result < gnutls.E_SUCCESS:
 
 
627
        for the 'errcheck' attribute on ctypes functions"""
 
690
629
            if result not in (gnutls.E_INTERRUPTED, gnutls.E_AGAIN):
 
691
630
                return _error_code(result)
 
692
631
            result = func(*arguments)
 
695
634
    # Unless otherwise indicated, the function declarations below are
 
696
635
    # all from the gnutls/gnutls.h C header file.
 
699
638
    priority_set_direct = _library.gnutls_priority_set_direct
 
700
 
    priority_set_direct.argtypes = [ClientSession, ctypes.c_char_p,
 
 
639
    priority_set_direct.argtypes = [session_t, ctypes.c_char_p,
 
701
640
                                    ctypes.POINTER(ctypes.c_char_p)]
 
702
641
    priority_set_direct.restype = _error_code
 
704
643
    init = _library.gnutls_init
 
705
 
    init.argtypes = [PointerTo(ClientSession), ctypes.c_int]
 
 
644
    init.argtypes = [ctypes.POINTER(session_t), ctypes.c_int]
 
706
645
    init.restype = _error_code
 
708
647
    set_default_priority = _library.gnutls_set_default_priority
 
709
 
    set_default_priority.argtypes = [ClientSession]
 
 
648
    set_default_priority.argtypes = [session_t]
 
710
649
    set_default_priority.restype = _error_code
 
712
651
    record_send = _library.gnutls_record_send
 
713
 
    record_send.argtypes = [ClientSession, ctypes.c_void_p,
 
 
652
    record_send.argtypes = [session_t, ctypes.c_void_p,
 
715
654
    record_send.restype = ctypes.c_ssize_t
 
716
655
    record_send.errcheck = _retry_on_error
 
718
657
    certificate_allocate_credentials = (
 
719
658
        _library.gnutls_certificate_allocate_credentials)
 
720
659
    certificate_allocate_credentials.argtypes = [
 
721
 
        PointerTo(Credentials)]
 
 
660
        ctypes.POINTER(certificate_credentials_t)]
 
722
661
    certificate_allocate_credentials.restype = _error_code
 
724
663
    certificate_free_credentials = (
 
725
664
        _library.gnutls_certificate_free_credentials)
 
726
 
    certificate_free_credentials.argtypes = [Credentials]
 
 
665
    certificate_free_credentials.argtypes = [certificate_credentials_t]
 
727
666
    certificate_free_credentials.restype = None
 
729
668
    handshake_set_private_extensions = (
 
730
669
        _library.gnutls_handshake_set_private_extensions)
 
731
 
    handshake_set_private_extensions.argtypes = [ClientSession,
 
 
670
    handshake_set_private_extensions.argtypes = [session_t,
 
733
672
    handshake_set_private_extensions.restype = None
 
735
674
    credentials_set = _library.gnutls_credentials_set
 
736
 
    credentials_set.argtypes = [ClientSession, credentials_type_t,
 
737
 
                                CastToVoidPointer(Credentials)]
 
 
675
    credentials_set.argtypes = [session_t, credentials_type_t,
 
738
677
    credentials_set.restype = _error_code
 
740
679
    strerror = _library.gnutls_strerror
 
741
680
    strerror.argtypes = [ctypes.c_int]
 
742
681
    strerror.restype = ctypes.c_char_p
 
744
683
    certificate_type_get = _library.gnutls_certificate_type_get
 
745
 
    certificate_type_get.argtypes = [ClientSession]
 
 
684
    certificate_type_get.argtypes = [session_t]
 
746
685
    certificate_type_get.restype = _error_code
 
748
687
    certificate_get_peers = _library.gnutls_certificate_get_peers
 
749
 
    certificate_get_peers.argtypes = [ClientSession,
 
 
688
    certificate_get_peers.argtypes = [session_t,
 
750
689
                                      ctypes.POINTER(ctypes.c_uint)]
 
751
690
    certificate_get_peers.restype = ctypes.POINTER(datum_t)
 
753
692
    global_set_log_level = _library.gnutls_global_set_log_level
 
754
693
    global_set_log_level.argtypes = [ctypes.c_int]
 
755
694
    global_set_log_level.restype = None
 
757
696
    global_set_log_function = _library.gnutls_global_set_log_function
 
758
697
    global_set_log_function.argtypes = [log_func]
 
759
698
    global_set_log_function.restype = None
 
761
700
    deinit = _library.gnutls_deinit
 
762
 
    deinit.argtypes = [ClientSession]
 
 
701
    deinit.argtypes = [session_t]
 
763
702
    deinit.restype = None
 
765
704
    handshake = _library.gnutls_handshake
 
766
 
    handshake.argtypes = [ClientSession]
 
767
 
    handshake.restype = ctypes.c_int
 
 
705
    handshake.argtypes = [session_t]
 
 
706
    handshake.restype = _error_code
 
768
707
    handshake.errcheck = _retry_on_error
 
770
709
    transport_set_ptr = _library.gnutls_transport_set_ptr
 
771
 
    transport_set_ptr.argtypes = [ClientSession, transport_ptr_t]
 
 
710
    transport_set_ptr.argtypes = [session_t, transport_ptr_t]
 
772
711
    transport_set_ptr.restype = None
 
774
713
    bye = _library.gnutls_bye
 
775
 
    bye.argtypes = [ClientSession, close_request_t]
 
776
 
    bye.restype = ctypes.c_int
 
 
714
    bye.argtypes = [session_t, close_request_t]
 
 
715
    bye.restype = _error_code
 
777
716
    bye.errcheck = _retry_on_error
 
779
718
    check_version = _library.gnutls_check_version
 
780
719
    check_version.argtypes = [ctypes.c_char_p]
 
781
720
    check_version.restype = ctypes.c_char_p
 
783
 
    _need_version = b"3.3.0"
 
784
 
    if check_version(_need_version) is None:
 
785
 
        raise self.Error("Needs GnuTLS {} or later"
 
786
 
                         .format(_need_version))
 
788
 
    _tls_rawpk_version = b"3.6.6"
 
789
 
    has_rawpk = bool(check_version(_tls_rawpk_version))
 
793
 
        class pubkey_st(ctypes.Structure):
 
795
 
        pubkey_t = ctypes.POINTER(pubkey_st)
 
797
 
        x509_crt_fmt_t = ctypes.c_int
 
799
 
        # All the function declarations below are from
 
801
 
        pubkey_init = _library.gnutls_pubkey_init
 
802
 
        pubkey_init.argtypes = [ctypes.POINTER(pubkey_t)]
 
803
 
        pubkey_init.restype = _error_code
 
805
 
        pubkey_import = _library.gnutls_pubkey_import
 
806
 
        pubkey_import.argtypes = [pubkey_t, ctypes.POINTER(datum_t),
 
808
 
        pubkey_import.restype = _error_code
 
810
 
        pubkey_get_key_id = _library.gnutls_pubkey_get_key_id
 
811
 
        pubkey_get_key_id.argtypes = [pubkey_t, ctypes.c_int,
 
812
 
                                      ctypes.POINTER(ctypes.c_ubyte),
 
813
 
                                      ctypes.POINTER(ctypes.c_size_t)]
 
814
 
        pubkey_get_key_id.restype = _error_code
 
816
 
        pubkey_deinit = _library.gnutls_pubkey_deinit
 
817
 
        pubkey_deinit.argtypes = [pubkey_t]
 
818
 
        pubkey_deinit.restype = None
 
820
 
        # All the function declarations below are from
 
823
 
        openpgp_crt_init = _library.gnutls_openpgp_crt_init
 
824
 
        openpgp_crt_init.argtypes = [ctypes.POINTER(openpgp_crt_t)]
 
825
 
        openpgp_crt_init.restype = _error_code
 
827
 
        openpgp_crt_import = _library.gnutls_openpgp_crt_import
 
828
 
        openpgp_crt_import.argtypes = [openpgp_crt_t,
 
829
 
                                       ctypes.POINTER(datum_t),
 
831
 
        openpgp_crt_import.restype = _error_code
 
833
 
        openpgp_crt_verify_self = \
 
834
 
            _library.gnutls_openpgp_crt_verify_self
 
835
 
        openpgp_crt_verify_self.argtypes = [
 
838
 
            ctypes.POINTER(ctypes.c_uint),
 
840
 
        openpgp_crt_verify_self.restype = _error_code
 
842
 
        openpgp_crt_deinit = _library.gnutls_openpgp_crt_deinit
 
843
 
        openpgp_crt_deinit.argtypes = [openpgp_crt_t]
 
844
 
        openpgp_crt_deinit.restype = None
 
846
 
        openpgp_crt_get_fingerprint = (
 
847
 
            _library.gnutls_openpgp_crt_get_fingerprint)
 
848
 
        openpgp_crt_get_fingerprint.argtypes = [openpgp_crt_t,
 
852
 
        openpgp_crt_get_fingerprint.restype = _error_code
 
854
 
    if check_version(b"3.6.4"):
 
855
 
        certificate_type_get2 = _library.gnutls_certificate_type_get2
 
856
 
        certificate_type_get2.argtypes = [ClientSession, ctypes.c_int]
 
857
 
        certificate_type_get2.restype = _error_code
 
 
722
    # All the function declarations below are from gnutls/openpgp.h
 
 
724
    openpgp_crt_init = _library.gnutls_openpgp_crt_init
 
 
725
    openpgp_crt_init.argtypes = [ctypes.POINTER(openpgp_crt_t)]
 
 
726
    openpgp_crt_init.restype = _error_code
 
 
728
    openpgp_crt_import = _library.gnutls_openpgp_crt_import
 
 
729
    openpgp_crt_import.argtypes = [openpgp_crt_t,
 
 
730
                                   ctypes.POINTER(datum_t),
 
 
732
    openpgp_crt_import.restype = _error_code
 
 
734
    openpgp_crt_verify_self = _library.gnutls_openpgp_crt_verify_self
 
 
735
    openpgp_crt_verify_self.argtypes = [openpgp_crt_t, ctypes.c_uint,
 
 
736
                                        ctypes.POINTER(ctypes.c_uint)]
 
 
737
    openpgp_crt_verify_self.restype = _error_code
 
 
739
    openpgp_crt_deinit = _library.gnutls_openpgp_crt_deinit
 
 
740
    openpgp_crt_deinit.argtypes = [openpgp_crt_t]
 
 
741
    openpgp_crt_deinit.restype = None
 
 
743
    openpgp_crt_get_fingerprint = (
 
 
744
        _library.gnutls_openpgp_crt_get_fingerprint)
 
 
745
    openpgp_crt_get_fingerprint.argtypes = [openpgp_crt_t,
 
 
749
    openpgp_crt_get_fingerprint.restype = _error_code
 
859
751
    # Remove non-public functions
 
860
752
    del _error_code, _retry_on_error
 
 
753
# Create the global "gnutls" object, simulating a module
 
863
756
def call_pipe(connection,       # : multiprocessing.Connection
 
864
757
              func, *args, **kwargs):
 
865
758
    """This function is meant to be called by multiprocessing.Process
 
867
760
    This function runs func(*args, **kwargs), and writes the resulting
 
868
761
    return value on the provided multiprocessing.Connection.
 
870
763
    connection.send(func(*args, **kwargs))
 
871
764
    connection.close()
 
 
766
class Client(object):
 
875
767
    """A representation of a client host served by this server.
 
878
 
    approved:   bool(); None if not yet approved/disapproved
 
 
770
    approved:   bool(); 'None' if not yet approved/disapproved
 
879
771
    approval_delay: datetime.timedelta(); Time to wait for approval
 
880
772
    approval_duration: datetime.timedelta(); Duration of one approval
 
881
 
    checker: multiprocessing.Process(); a running checker process used
 
882
 
             to see if the client lives. None if no process is
 
 
773
    checker:    subprocess.Popen(); a running checker process used
 
 
774
                                    to see if the client lives.
 
 
775
                                    'None' if no process is running.
 
884
776
    checker_callback_tag: a GLib event source tag, or None
 
885
777
    checker_command: string; External command which is run to check
 
886
778
                     if client lives.  %() expansions are done at
 
 
2260
2136
                           byte_arrays=True)
 
2261
2137
    def Secret_dbus_property(self, value):
 
2262
2138
        self.secret = bytes(value)
 
2268
 
    def __init__(self, child_pipe, key_id, fpr, address):
 
 
2143
class ProxyClient(object):
 
 
2144
    def __init__(self, child_pipe, fpr, address):
 
2269
2145
        self._pipe = child_pipe
 
2270
 
        self._pipe.send(("init", key_id, fpr, address))
 
 
2146
        self._pipe.send(('init', fpr, address))
 
2271
2147
        if not self._pipe.recv():
 
2272
 
            raise KeyError(key_id or fpr)
 
2274
2150
    def __getattribute__(self, name):
 
2276
2152
            return super(ProxyClient, self).__getattribute__(name)
 
2277
 
        self._pipe.send(("getattr", name))
 
 
2153
        self._pipe.send(('getattr', name))
 
2278
2154
        data = self._pipe.recv()
 
2279
 
        if data[0] == "data":
 
 
2155
        if data[0] == 'data':
 
2281
 
        if data[0] == "function":
 
 
2157
        if data[0] == 'function':
 
2283
2159
            def func(*args, **kwargs):
 
2284
 
                self._pipe.send(("funcall", name, args, kwargs))
 
 
2160
                self._pipe.send(('funcall', name, args, kwargs))
 
2285
2161
                return self._pipe.recv()[1]
 
2289
2165
    def __setattr__(self, name, value):
 
2291
2167
            return super(ProxyClient, self).__setattr__(name, value)
 
2292
 
        self._pipe.send(("setattr", name, value))
 
 
2168
        self._pipe.send(('setattr', name, value))
 
2295
2171
class ClientHandler(socketserver.BaseRequestHandler, object):
 
2296
2172
    """A class to handle client connections.
 
2298
2174
    Instantiated once for each connection to handle it.
 
2299
2175
    Note: This will run in its own forked process."""
 
2301
2177
    def handle(self):
 
2302
2178
        with contextlib.closing(self.server.child_pipe) as child_pipe:
 
2303
 
            log.info("TCP connection from: %s",
 
2304
 
                     str(self.client_address))
 
2305
 
            log.debug("Pipe FD: %d", self.server.child_pipe.fileno())
 
 
2179
            logger.info("TCP connection from: %s",
 
 
2180
                        str(self.client_address))
 
 
2181
            logger.debug("Pipe FD: %d",
 
 
2182
                         self.server.child_pipe.fileno())
 
2307
2184
            session = gnutls.ClientSession(self.request)
 
2309
 
            # priority = ":".join(("NONE", "+VERS-TLS1.1",
 
2310
 
            #                       "+AES-256-CBC", "+SHA1",
 
2311
 
            #                       "+COMP-NULL", "+CTYPE-OPENPGP",
 
 
2186
            #priority = ':'.join(("NONE", "+VERS-TLS1.1",
 
 
2187
            #                      "+AES-256-CBC", "+SHA1",
 
 
2188
            #                      "+COMP-NULL", "+CTYPE-OPENPGP",
 
2313
2190
            # Use a fallback default, since this MUST be set.
 
2314
2191
            priority = self.server.gnutls_priority
 
2315
2192
            if priority is None:
 
2316
2193
                priority = "NORMAL"
 
2317
 
            gnutls.priority_set_direct(session,
 
2318
 
                                       priority.encode("utf-8"), None)
 
 
2194
            gnutls.priority_set_direct(session._c_object,
 
 
2195
                                       priority.encode("utf-8"),
 
2320
2198
            # Start communication using the Mandos protocol
 
2321
2199
            # Get protocol number
 
2322
2200
            line = self.request.makefile().readline()
 
2323
 
            log.debug("Protocol version: %r", line)
 
 
2201
            logger.debug("Protocol version: %r", line)
 
2325
2203
                if int(line.strip().split()[0]) > 1:
 
2326
2204
                    raise RuntimeError(line)
 
2327
2205
            except (ValueError, IndexError, RuntimeError) as error:
 
2328
 
                log.error("Unknown protocol version: %s", error)
 
 
2206
                logger.error("Unknown protocol version: %s", error)
 
2331
2209
            # Start GnuTLS connection
 
2333
2211
                session.handshake()
 
2334
2212
            except gnutls.Error as error:
 
2335
 
                log.warning("Handshake failed: %s", error)
 
 
2213
                logger.warning("Handshake failed: %s", error)
 
2336
2214
                # Do not run session.bye() here: the session is not
 
2337
2215
                # established.  Just abandon the request.
 
2339
 
            log.debug("Handshake succeeded")
 
 
2217
            logger.debug("Handshake succeeded")
 
2341
2219
            approval_required = False
 
2343
 
                if gnutls.has_rawpk:
 
2346
 
                        key_id = self.key_id(
 
2347
 
                            self.peer_certificate(session))
 
2348
 
                    except (TypeError, gnutls.Error) as error:
 
2349
 
                        log.warning("Bad certificate: %s", error)
 
2351
 
                    log.debug("Key ID: %s",
 
2352
 
                              key_id.decode("utf-8",
 
2358
 
                        fpr = self.fingerprint(
 
2359
 
                            self.peer_certificate(session))
 
2360
 
                    except (TypeError, gnutls.Error) as error:
 
2361
 
                        log.warning("Bad certificate: %s", error)
 
2363
 
                    log.debug("Fingerprint: %s", fpr)
 
2366
 
                    client = ProxyClient(child_pipe, key_id, fpr,
 
 
2222
                    fpr = self.fingerprint(
 
 
2223
                        self.peer_certificate(session))
 
 
2224
                except (TypeError, gnutls.Error) as error:
 
 
2225
                    logger.warning("Bad certificate: %s", error)
 
 
2227
                logger.debug("Fingerprint: %s", fpr)
 
 
2230
                    client = ProxyClient(child_pipe, fpr,
 
2367
2231
                                         self.client_address)
 
2368
2232
                except KeyError:
 
2371
2235
                if client.approval_delay:
 
2372
2236
                    delay = client.approval_delay
 
2373
2237
                    client.approvals_pending += 1
 
2374
2238
                    approval_required = True
 
2377
2241
                    if not client.enabled:
 
2378
 
                        log.info("Client %s is disabled", client.name)
 
 
2242
                        logger.info("Client %s is disabled",
 
2379
2244
                        if self.server.use_dbus:
 
2380
2245
                            # Emit D-Bus signal
 
2381
2246
                            client.Rejected("Disabled")
 
2384
2249
                    if client.approved or not client.approval_delay:
 
2385
 
                        # We are approved or approval is disabled
 
 
2250
                        #We are approved or approval is disabled
 
2387
2252
                    elif client.approved is None:
 
2388
 
                        log.info("Client %s needs approval",
 
 
2253
                        logger.info("Client %s needs approval",
 
2390
2255
                        if self.server.use_dbus:
 
2391
2256
                            # Emit D-Bus signal
 
2392
2257
                            client.NeedApproval(
 
2393
2258
                                client.approval_delay.total_seconds()
 
2394
2259
                                * 1000, client.approved_by_default)
 
2396
 
                        log.warning("Client %s was not approved",
 
 
2261
                        logger.warning("Client %s was not approved",
 
2398
2263
                        if self.server.use_dbus:
 
2399
2264
                            # Emit D-Bus signal
 
2400
2265
                            client.Rejected("Denied")
 
2403
 
                    # wait until timeout or approved
 
 
2268
                    #wait until timeout or approved
 
2404
2269
                    time = datetime.datetime.now()
 
2405
2270
                    client.changedstate.acquire()
 
2406
2271
                    client.changedstate.wait(delay.total_seconds())
 
 
2704
2523
        self.gnutls_priority = gnutls_priority
 
2705
2524
        IPv6_TCPServer.__init__(self, server_address,
 
2706
2525
                                RequestHandlerClass,
 
2707
 
                                interface=interface,
 
 
2526
                                interface = interface,
 
 
2527
                                use_ipv6 = use_ipv6,
 
 
2528
                                socketfd = socketfd)
 
2711
2530
    def server_activate(self):
 
2712
2531
        if self.enabled:
 
2713
2532
            return socketserver.TCPServer.server_activate(self)
 
2715
2534
    def enable(self):
 
2716
2535
        self.enabled = True
 
2718
2537
    def add_pipe(self, parent_pipe, proc):
 
2719
2538
        # Call "handle_ipc" for both data and EOF events
 
2720
2539
        GLib.io_add_watch(
 
2721
 
            GLib.IOChannel.unix_new(parent_pipe.fileno()),
 
2722
 
            GLib.PRIORITY_DEFAULT, GLib.IO_IN | GLib.IO_HUP,
 
 
2540
            parent_pipe.fileno(),
 
 
2541
            GLib.IO_IN | GLib.IO_HUP,
 
2723
2542
            functools.partial(self.handle_ipc,
 
2724
 
                              parent_pipe=parent_pipe,
 
 
2543
                              parent_pipe = parent_pipe,
 
2727
2546
    def handle_ipc(self, source, condition,
 
2728
2547
                   parent_pipe=None,
 
2730
2549
                   client_object=None):
 
2731
2550
        # error, or the other end of multiprocessing.Pipe has closed
 
2732
2551
        if condition & (GLib.IO_ERR | GLib.IO_HUP):
 
2733
2552
            # Wait for other process to exit
 
2737
2556
        # Read a request from the child
 
2738
2557
        request = parent_pipe.recv()
 
2739
2558
        command = request[0]
 
2741
 
        if command == "init":
 
2742
 
            key_id = request[1].decode("ascii")
 
2743
 
            fpr = request[2].decode("ascii")
 
2744
 
            address = request[3]
 
 
2560
        if command == 'init':
 
 
2562
            address = request[2]
 
2746
2564
            for c in self.clients.values():
 
2747
 
                if key_id == ("E3B0C44298FC1C149AFBF4C8996FB924"
 
2748
 
                              "27AE41E4649B934CA495991B7852B855"):
 
2750
 
                if key_id and c.key_id == key_id:
 
2753
 
                if fpr and c.fingerprint == fpr:
 
 
2565
                if c.fingerprint == fpr:
 
2757
 
                log.info("Client not found for key ID: %s, address:"
 
2758
 
                         " %s", key_id or fpr, address)
 
 
2569
                logger.info("Client not found for fingerprint: %s, ad"
 
 
2570
                            "dress: %s", fpr, address)
 
2759
2571
                if self.use_dbus:
 
2760
2572
                    # Emit D-Bus signal
 
2761
 
                    mandos_dbus_service.ClientNotFound(key_id or fpr,
 
 
2573
                    mandos_dbus_service.ClientNotFound(fpr,
 
2763
2575
                parent_pipe.send(False)
 
2766
2578
            GLib.io_add_watch(
 
2767
 
                GLib.IOChannel.unix_new(parent_pipe.fileno()),
 
2768
 
                GLib.PRIORITY_DEFAULT, GLib.IO_IN | GLib.IO_HUP,
 
 
2579
                parent_pipe.fileno(),
 
 
2580
                GLib.IO_IN | GLib.IO_HUP,
 
2769
2581
                functools.partial(self.handle_ipc,
 
2770
 
                                  parent_pipe=parent_pipe,
 
2772
 
                                  client_object=client))
 
 
2582
                                  parent_pipe = parent_pipe,
 
 
2584
                                  client_object = client))
 
2773
2585
            parent_pipe.send(True)
 
2774
2586
            # remove the old hook in favor of the new above hook on
 
2777
 
        if command == "funcall":
 
 
2589
        if command == 'funcall':
 
2778
2590
            funcname = request[1]
 
2779
2591
            args = request[2]
 
2780
2592
            kwargs = request[3]
 
2782
 
            parent_pipe.send(("data", getattr(client_object,
 
 
2594
            parent_pipe.send(('data', getattr(client_object,
 
2783
2595
                                              funcname)(*args,
 
2786
 
        if command == "getattr":
 
 
2598
        if command == 'getattr':
 
2787
2599
            attrname = request[1]
 
2788
2600
            if isinstance(client_object.__getattribute__(attrname),
 
2789
 
                          collections.abc.Callable):
 
2790
 
                parent_pipe.send(("function", ))
 
 
2601
                          collections.Callable):
 
 
2602
                parent_pipe.send(('function', ))
 
2792
2604
                parent_pipe.send((
 
2793
 
                    "data", client_object.__getattribute__(attrname)))
 
2795
 
        if command == "setattr":
 
 
2605
                    'data', client_object.__getattribute__(attrname)))
 
 
2607
        if command == 'setattr':
 
2796
2608
            attrname = request[1]
 
2797
2609
            value = request[2]
 
2798
2610
            setattr(client_object, attrname, value)
 
2803
2615
def rfc3339_duration_to_delta(duration):
 
2804
2616
    """Parse an RFC 3339 "duration" and return a datetime.timedelta
 
2806
 
    >>> timedelta = datetime.timedelta
 
2807
 
    >>> rfc3339_duration_to_delta("P7D") == timedelta(7)
 
2809
 
    >>> rfc3339_duration_to_delta("PT60S") == timedelta(0, 60)
 
2811
 
    >>> rfc3339_duration_to_delta("PT60M") == timedelta(0, 3600)
 
2813
 
    >>> rfc3339_duration_to_delta("PT24H") == timedelta(1)
 
2815
 
    >>> rfc3339_duration_to_delta("P1W") == timedelta(7)
 
2817
 
    >>> rfc3339_duration_to_delta("PT5M30S") == timedelta(0, 330)
 
2819
 
    >>> rfc3339_duration_to_delta("P1DT3M20S") == timedelta(1, 200)
 
 
2618
    >>> rfc3339_duration_to_delta("P7D")
 
 
2619
    datetime.timedelta(7)
 
 
2620
    >>> rfc3339_duration_to_delta("PT60S")
 
 
2621
    datetime.timedelta(0, 60)
 
 
2622
    >>> rfc3339_duration_to_delta("PT60M")
 
 
2623
    datetime.timedelta(0, 3600)
 
 
2624
    >>> rfc3339_duration_to_delta("PT24H")
 
 
2625
    datetime.timedelta(1)
 
 
2626
    >>> rfc3339_duration_to_delta("P1W")
 
 
2627
    datetime.timedelta(7)
 
 
2628
    >>> rfc3339_duration_to_delta("PT5M30S")
 
 
2629
    datetime.timedelta(0, 330)
 
 
2630
    >>> rfc3339_duration_to_delta("P1DT3M20S")
 
 
2631
    datetime.timedelta(1, 200)
 
2824
2634
    # Parsing an RFC 3339 duration with regular expressions is not
 
2825
2635
    # possible - there would have to be multiple places for the same
 
2826
2636
    # values, like seconds.  The current code, while more esoteric, is
 
2827
2637
    # cleaner without depending on a parsing library.  If Python had a
 
2828
2638
    # built-in library for parsing we would use it, but we'd like to
 
2829
2639
    # avoid excessive use of external libraries.
 
2831
2641
    # New type for defining tokens, syntax, and semantics all-in-one
 
2832
2642
    Token = collections.namedtuple("Token", (
 
2833
2643
        "regexp",  # To match token; if "value" is not None, must have
 
 
3225
3032
                "se.bsnet.fukt.Mandos", bus,
 
3226
3033
                do_not_queue=True)
 
3227
3034
        except dbus.exceptions.DBusException as e:
 
3228
 
            log.error("Disabling D-Bus:", exc_info=e)
 
 
3035
            logger.error("Disabling D-Bus:", exc_info=e)
 
3229
3036
            use_dbus = False
 
3230
3037
            server_settings["use_dbus"] = False
 
3231
3038
            tcp_server.use_dbus = False
 
3233
3040
        protocol = avahi.PROTO_INET6 if use_ipv6 else avahi.PROTO_INET
 
3234
3041
        service = AvahiServiceToSyslog(
 
3235
 
            name=server_settings["servicename"],
 
3236
 
            servicetype="_mandos._tcp",
 
 
3042
            name = server_settings["servicename"],
 
 
3043
            servicetype = "_mandos._tcp",
 
 
3044
            protocol = protocol,
 
3239
3046
        if server_settings["interface"]:
 
3240
3047
            service.interface = if_nametoindex(
 
3241
3048
                server_settings["interface"].encode("utf-8"))
 
3243
3050
    global multiprocessing_manager
 
3244
3051
    multiprocessing_manager = multiprocessing.Manager()
 
3246
3053
    client_class = Client
 
3248
 
        client_class = functools.partial(ClientDBus, bus=bus)
 
 
3055
        client_class = functools.partial(ClientDBus, bus = bus)
 
3250
3057
    client_settings = Client.config_parser(client_config)
 
3251
3058
    old_client_settings = {}
 
3252
3059
    clients_data = {}
 
3254
3061
    # This is used to redirect stdout and stderr for checker processes
 
3256
 
    wnull = open(os.devnull, "w")  # A writable /dev/null
 
 
3063
    wnull = open(os.devnull, "w") # A writable /dev/null
 
3257
3064
    # Only used if server is running in foreground but not in debug
 
3259
3066
    if debug or not foreground:
 
3262
3069
    # Get client data and settings from last running state.
 
3263
3070
    if server_settings["restore"]:
 
3265
3072
            with open(stored_state_path, "rb") as stored_state:
 
3266
 
                if sys.version_info.major == 2:
 
 
3073
                if sys.version_info.major == 2:                
 
3267
3074
                    clients_data, old_client_settings = pickle.load(
 
3270
3077
                    bytes_clients_data, bytes_old_client_settings = (
 
3271
 
                        pickle.load(stored_state, encoding="bytes"))
 
3272
 
                    #   Fix bytes to strings
 
 
3078
                        pickle.load(stored_state, encoding = "bytes"))
 
 
3079
                    ### Fix bytes to strings
 
3275
 
                    clients_data = {(key.decode("utf-8")
 
3276
 
                                     if isinstance(key, bytes)
 
3279
 
                                    bytes_clients_data.items()}
 
 
3082
                    clients_data = { (key.decode("utf-8")
 
 
3083
                                      if isinstance(key, bytes)
 
 
3086
                                     bytes_clients_data.items() }
 
3280
3087
                    del bytes_clients_data
 
3281
3088
                    for key in clients_data:
 
3282
 
                        value = {(k.decode("utf-8")
 
3283
 
                                  if isinstance(k, bytes) else k): v
 
3285
 
                                 clients_data[key].items()}
 
 
3089
                        value = { (k.decode("utf-8")
 
 
3090
                                   if isinstance(k, bytes) else k): v
 
 
3092
                                  clients_data[key].items() }
 
3286
3093
                        clients_data[key] = value
 
3287
3094
                        # .client_structure
 
3288
3095
                        value["client_structure"] = [
 
3289
3096
                            (s.decode("utf-8")
 
3290
3097
                             if isinstance(s, bytes)
 
3291
3098
                             else s) for s in
 
3292
 
                            value["client_structure"]]
 
3293
 
                        # .name, .host, and .checker_command
 
3294
 
                        for k in ("name", "host", "checker_command"):
 
 
3099
                            value["client_structure"] ]
 
 
3101
                        for k in ("name", "host"):
 
3295
3102
                            if isinstance(value[k], bytes):
 
3296
3103
                                value[k] = value[k].decode("utf-8")
 
3297
 
                        if "key_id" not in value:
 
3298
 
                            value["key_id"] = ""
 
3299
 
                        elif "fingerprint" not in value:
 
3300
 
                            value["fingerprint"] = ""
 
3301
 
                    #  old_client_settings
 
 
3104
                    ## old_client_settings
 
3303
3106
                    old_client_settings = {
 
3304
3107
                        (key.decode("utf-8")
 
3305
3108
                         if isinstance(key, bytes)
 
3306
3109
                         else key): value
 
3307
3110
                        for key, value in
 
3308
 
                        bytes_old_client_settings.items()}
 
 
3111
                        bytes_old_client_settings.items() }
 
3309
3112
                    del bytes_old_client_settings
 
3310
 
                    # .host and .checker_command
 
3311
3114
                    for value in old_client_settings.values():
 
3312
 
                        for attribute in ("host", "checker_command"):
 
3313
 
                            if isinstance(value[attribute], bytes):
 
3314
 
                                value[attribute] = (value[attribute]
 
 
3115
                        if isinstance(value["host"], bytes):
 
 
3116
                            value["host"] = (value["host"]
 
3316
3118
            os.remove(stored_state_path)
 
3317
3119
        except IOError as e:
 
3318
3120
            if e.errno == errno.ENOENT:
 
3319
 
                log.warning("Could not load persistent state:"
 
3320
 
                            " %s", os.strerror(e.errno))
 
 
3121
                logger.warning("Could not load persistent state:"
 
 
3122
                               " {}".format(os.strerror(e.errno)))
 
3322
 
                log.critical("Could not load persistent state:",
 
 
3124
                logger.critical("Could not load persistent state:",
 
3325
3127
        except EOFError as e:
 
3326
 
            log.warning("Could not load persistent state: EOFError:",
 
 
3128
            logger.warning("Could not load persistent state: "
 
3329
3132
    with PGPEngine() as pgp:
 
3330
3133
        for client_name, client in clients_data.items():
 
3331
3134
            # Skip removed clients
 
3332
3135
            if client_name not in client_settings:
 
3335
3138
            # Decide which value to use after restoring saved state.
 
3336
3139
            # We have three different values: Old config file,
 
3337
3140
            # new config file, and saved state.
 
 
3610
3416
        # Need to initiate checking of clients
 
3611
3417
        if client.enabled:
 
3612
3418
            client.init_checker()
 
3614
3420
    tcp_server.enable()
 
3615
3421
    tcp_server.server_activate()
 
3617
3423
    # Find out what port we got
 
3619
3425
        service.port = tcp_server.socket.getsockname()[1]
 
3621
 
        log.info("Now listening on address %r, port %d, flowinfo %d,"
 
3622
 
                 " scope_id %d", *tcp_server.socket.getsockname())
 
 
3427
        logger.info("Now listening on address %r, port %d,"
 
 
3428
                    " flowinfo %d, scope_id %d",
 
 
3429
                    *tcp_server.socket.getsockname())
 
3624
 
        log.info("Now listening on address %r, port %d",
 
3625
 
                 *tcp_server.socket.getsockname())
 
3627
 
    # service.interface = tcp_server.socket.getsockname()[3]
 
 
3431
        logger.info("Now listening on address %r, port %d",
 
 
3432
                    *tcp_server.socket.getsockname())
 
 
3434
    #service.interface = tcp_server.socket.getsockname()[3]
 
3631
3438
            # From the Avahi example code
 
3633
3440
                service.activate()
 
3634
3441
            except dbus.exceptions.DBusException as error:
 
3635
 
                log.critical("D-Bus Exception", exc_info=error)
 
 
3442
                logger.critical("D-Bus Exception", exc_info=error)
 
3638
3445
            # End of Avahi example code
 
3641
 
            GLib.IOChannel.unix_new(tcp_server.fileno()),
 
3642
 
            GLib.PRIORITY_DEFAULT, GLib.IO_IN,
 
3643
 
            lambda *args, **kwargs: (tcp_server.handle_request
 
3644
 
                                     (*args[2:], **kwargs) or True))
 
3646
 
        log.debug("Starting main loop")
 
 
3447
        GLib.io_add_watch(tcp_server.fileno(), GLib.IO_IN,
 
 
3448
                          lambda *args, **kwargs:
 
 
3449
                          (tcp_server.handle_request
 
 
3450
                           (*args[2:], **kwargs) or True))
 
 
3452
        logger.debug("Starting main loop")
 
3647
3453
        main_loop.run()
 
3648
3454
    except AvahiError as error:
 
3649
 
        log.critical("Avahi Error", exc_info=error)
 
 
3455
        logger.critical("Avahi Error", exc_info=error)
 
3652
3458
    except KeyboardInterrupt:
 
3654
3460
            print("", file=sys.stderr)
 
3655
 
        log.debug("Server received KeyboardInterrupt")
 
3656
 
    log.debug("Server exiting")
 
 
3461
        logger.debug("Server received KeyboardInterrupt")
 
 
3462
    logger.debug("Server exiting")
 
3657
3463
    # Must run before the D-Bus bus name gets deregistered
 
3661
 
def parse_test_args():
 
3662
 
    # type: () -> argparse.Namespace
 
3663
 
    parser = argparse.ArgumentParser(add_help=False)
 
3664
 
    parser.add_argument("--check", action="store_true")
 
3665
 
    parser.add_argument("--prefix", )
 
3666
 
    args, unknown_args = parser.parse_known_args()
 
3668
 
        # Remove test options from sys.argv
 
3669
 
        sys.argv[1:] = unknown_args
 
3672
 
# Add all tests from doctest strings
 
3673
 
def load_tests(loader, tests, none):
 
3675
 
    tests.addTests(doctest.DocTestSuite())
 
3678
 
if __name__ == "__main__":
 
3679
 
    options = parse_test_args()
 
3682
 
            extra_test_prefix = options.prefix
 
3683
 
            if extra_test_prefix is not None:
 
3684
 
                if not (unittest.main(argv=[""], exit=False)
 
3685
 
                        .result.wasSuccessful()):
 
3687
 
                class ExtraTestLoader(unittest.TestLoader):
 
3688
 
                    testMethodPrefix = extra_test_prefix
 
3689
 
                # Call using ./scriptname --test [--verbose]
 
3690
 
                unittest.main(argv=[""], testLoader=ExtraTestLoader())
 
3692
 
                unittest.main(argv=[""])
 
3700
 
# (lambda (&optional extra)
 
3701
 
#   (if (not (funcall run-tests-in-test-buffer default-directory
 
3703
 
#       (funcall show-test-buffer-in-test-window)
 
3704
 
#     (funcall remove-test-window)
 
3705
 
#     (if extra (message "Extra tests run successfully!"))))
 
3706
 
# run-tests-in-test-buffer:
 
3707
 
# (lambda (dir &optional extra)
 
3708
 
#   (with-current-buffer (get-buffer-create "*Test*")
 
3709
 
#     (setq buffer-read-only nil
 
3710
 
#           default-directory dir)
 
3712
 
#     (compilation-mode))
 
3713
 
#   (let ((process-result
 
3714
 
#          (let ((inhibit-read-only t))
 
3715
 
#            (process-file-shell-command
 
3716
 
#             (funcall get-command-line extra) nil "*Test*"))))
 
3717
 
#     (and (numberp process-result)
 
3718
 
#          (= process-result 0))))
 
3720
 
# (lambda (&optional extra)
 
3721
 
#   (let ((quoted-script
 
3722
 
#          (shell-quote-argument (funcall get-script-name))))
 
3724
 
#      (concat "%s --check" (if extra " --prefix=atest" ""))
 
3728
 
#   (if (fboundp 'file-local-name)
 
3729
 
#       (file-local-name (buffer-file-name))
 
3730
 
#     (or (file-remote-p (buffer-file-name) 'localname)
 
3731
 
#         (buffer-file-name))))
 
3732
 
# remove-test-window:
 
3734
 
#   (let ((test-window (get-buffer-window "*Test*")))
 
3735
 
#     (if test-window (delete-window test-window))))
 
3736
 
# show-test-buffer-in-test-window:
 
3738
 
#   (when (not (get-buffer-window-list "*Test*"))
 
3739
 
#     (setq next-error-last-buffer (get-buffer "*Test*"))
 
3740
 
#     (let* ((side (if (>= (window-width) 146) 'right 'bottom))
 
3741
 
#            (display-buffer-overriding-action
 
3742
 
#             `((display-buffer-in-side-window) (side . ,side)
 
3743
 
#               (window-height . fit-window-to-buffer)
 
3744
 
#               (window-width . fit-window-to-buffer))))
 
3745
 
#       (display-buffer "*Test*"))))
 
3748
 
#   (let* ((run-extra-tests (lambda () (interactive)
 
3749
 
#                             (funcall run-tests t)))
 
3750
 
#          (inner-keymap `(keymap (116 . ,run-extra-tests))) ; t
 
3751
 
#          (outer-keymap `(keymap (3 . ,inner-keymap))))     ; C-c
 
3752
 
#     (setq minor-mode-overriding-map-alist
 
3753
 
#           (cons `(run-tests . ,outer-keymap)
 
3754
 
#                 minor-mode-overriding-map-alist)))
 
3755
 
#   (add-hook 'after-save-hook run-tests 90 t))
 
 
3467
if __name__ == '__main__':