/mandos/trunk

To get this branch, use:
bzr branch http://bzr.recompile.se/loggerhead/mandos/trunk

« back to all changes in this revision

Viewing changes to mandos

Merge from trunk.  Lots of bug fixes, including Debian bug #546928.

Show diffs side-by-side

added added

removed removed

Lines of Context:
6
6
# This program is partly derived from an example program for an Avahi
7
7
# service publisher, downloaded from
8
8
# <http://avahi.org/wiki/PythonPublishExample>.  This includes the
9
 
# methods "add" and "remove" in the "AvahiService" class, the
10
 
# "server_state_changed" and "entry_group_state_changed" functions,
11
 
# and some lines in "main".
 
9
# methods "add", "remove", "server_state_changed",
 
10
# "entry_group_state_changed", "cleanup", and "activate" in the
 
11
# "AvahiService" class, and some lines in "main".
12
12
13
13
# Everything else is
14
14
# Copyright © 2008,2009 Teddy Hogeborn
33
33
 
34
34
from __future__ import division, with_statement, absolute_import
35
35
 
36
 
import SocketServer
 
36
import SocketServer as socketserver
37
37
import socket
38
38
import optparse
39
39
import datetime
44
44
import gnutls.library.functions
45
45
import gnutls.library.constants
46
46
import gnutls.library.types
47
 
import ConfigParser
 
47
import ConfigParser as configparser
48
48
import sys
49
49
import re
50
50
import os
51
51
import signal
52
 
from sets import Set
53
52
import subprocess
54
53
import atexit
55
54
import stat
59
58
from contextlib import closing
60
59
import struct
61
60
import fcntl
 
61
import functools
62
62
 
63
63
import dbus
64
64
import dbus.service
74
74
    try:
75
75
        from IN import SO_BINDTODEVICE
76
76
    except ImportError:
77
 
        # From /usr/include/asm/socket.h
78
 
        SO_BINDTODEVICE = 25
79
 
 
80
 
 
81
 
version = "1.0.8"
 
77
        SO_BINDTODEVICE = None
 
78
 
 
79
 
 
80
version = "1.0.11"
82
81
 
83
82
logger = logging.Logger(u'mandos')
84
83
syslogger = (logging.handlers.SysLogHandler
125
124
    max_renames: integer; maximum number of renames
126
125
    rename_count: integer; counter so we only rename after collisions
127
126
                  a sensible number of times
 
127
    group: D-Bus Entry Group
 
128
    server: D-Bus Server
 
129
    bus: dbus.SystemBus()
128
130
    """
129
131
    def __init__(self, interface = avahi.IF_UNSPEC, name = None,
130
132
                 servicetype = None, port = None, TXT = None,
131
133
                 domain = u"", host = u"", max_renames = 32768,
132
 
                 protocol = avahi.PROTO_UNSPEC):
 
134
                 protocol = avahi.PROTO_UNSPEC, bus = None):
133
135
        self.interface = interface
134
136
        self.name = name
135
137
        self.type = servicetype
140
142
        self.rename_count = 0
141
143
        self.max_renames = max_renames
142
144
        self.protocol = protocol
 
145
        self.group = None       # our entry group
 
146
        self.server = None
 
147
        self.bus = bus
143
148
    def rename(self):
144
149
        """Derived from the Avahi example code"""
145
150
        if self.rename_count >= self.max_renames:
147
152
                            u" after %i retries, exiting.",
148
153
                            self.rename_count)
149
154
            raise AvahiServiceError(u"Too many renames")
150
 
        self.name = server.GetAlternativeServiceName(self.name)
 
155
        self.name = self.server.GetAlternativeServiceName(self.name)
151
156
        logger.info(u"Changing Zeroconf service name to %r ...",
152
 
                    self.name)
 
157
                    unicode(self.name))
153
158
        syslogger.setFormatter(logging.Formatter
154
159
                               (u'Mandos (%s) [%%(process)d]:'
155
160
                                u' %%(levelname)s: %%(message)s'
159
164
        self.rename_count += 1
160
165
    def remove(self):
161
166
        """Derived from the Avahi example code"""
162
 
        if group is not None:
163
 
            group.Reset()
 
167
        if self.group is not None:
 
168
            self.group.Reset()
164
169
    def add(self):
165
170
        """Derived from the Avahi example code"""
166
 
        global group
167
 
        if group is None:
168
 
            group = dbus.Interface(bus.get_object
169
 
                                   (avahi.DBUS_NAME,
170
 
                                    server.EntryGroupNew()),
171
 
                                   avahi.DBUS_INTERFACE_ENTRY_GROUP)
172
 
            group.connect_to_signal('StateChanged',
173
 
                                    entry_group_state_changed)
 
171
        if self.group is None:
 
172
            self.group = dbus.Interface(
 
173
                self.bus.get_object(avahi.DBUS_NAME,
 
174
                                    self.server.EntryGroupNew()),
 
175
                avahi.DBUS_INTERFACE_ENTRY_GROUP)
 
176
            self.group.connect_to_signal('StateChanged',
 
177
                                         self.entry_group_state_changed)
174
178
        logger.debug(u"Adding Zeroconf service '%s' of type '%s' ...",
175
 
                     service.name, service.type)
176
 
        group.AddService(
177
 
                self.interface,         # interface
178
 
                self.protocol,          # protocol
179
 
                dbus.UInt32(0),         # flags
180
 
                self.name, self.type,
181
 
                self.domain, self.host,
182
 
                dbus.UInt16(self.port),
183
 
                avahi.string_array_to_txt_array(self.TXT))
184
 
        group.Commit()
185
 
 
186
 
# From the Avahi example code:
187
 
group = None                            # our entry group
188
 
# End of Avahi example code
189
 
 
190
 
 
191
 
def _datetime_to_dbus(dt, variant_level=0):
192
 
    """Convert a UTC datetime.datetime() to a D-Bus type."""
193
 
    return dbus.String(dt.isoformat(), variant_level=variant_level)
 
179
                     self.name, self.type)
 
180
        self.group.AddService(
 
181
            self.interface,
 
182
            self.protocol,
 
183
            dbus.UInt32(0),     # flags
 
184
            self.name, self.type,
 
185
            self.domain, self.host,
 
186
            dbus.UInt16(self.port),
 
187
            avahi.string_array_to_txt_array(self.TXT))
 
188
        self.group.Commit()
 
189
    def entry_group_state_changed(self, state, error):
 
190
        """Derived from the Avahi example code"""
 
191
        logger.debug(u"Avahi state change: %i", state)
 
192
        
 
193
        if state == avahi.ENTRY_GROUP_ESTABLISHED:
 
194
            logger.debug(u"Zeroconf service established.")
 
195
        elif state == avahi.ENTRY_GROUP_COLLISION:
 
196
            logger.warning(u"Zeroconf service name collision.")
 
197
            self.rename()
 
198
        elif state == avahi.ENTRY_GROUP_FAILURE:
 
199
            logger.critical(u"Avahi: Error in group state changed %s",
 
200
                            unicode(error))
 
201
            raise AvahiGroupError(u"State changed: %s"
 
202
                                  % unicode(error))
 
203
    def cleanup(self):
 
204
        """Derived from the Avahi example code"""
 
205
        if self.group is not None:
 
206
            self.group.Free()
 
207
            self.group = None
 
208
    def server_state_changed(self, state):
 
209
        """Derived from the Avahi example code"""
 
210
        if state == avahi.SERVER_COLLISION:
 
211
            logger.error(u"Zeroconf server name collision")
 
212
            self.remove()
 
213
        elif state == avahi.SERVER_RUNNING:
 
214
            self.add()
 
215
    def activate(self):
 
216
        """Derived from the Avahi example code"""
 
217
        if self.server is None:
 
218
            self.server = dbus.Interface(
 
219
                self.bus.get_object(avahi.DBUS_NAME,
 
220
                                    avahi.DBUS_PATH_SERVER),
 
221
                avahi.DBUS_INTERFACE_SERVER)
 
222
        self.server.connect_to_signal(u"StateChanged",
 
223
                                 self.server_state_changed)
 
224
        self.server_state_changed(self.server.GetState())
194
225
 
195
226
 
196
227
class Client(object):
281
312
    
282
313
    def enable(self):
283
314
        """Start this client's checker and timeout hooks"""
 
315
        if getattr(self, u"enabled", False):
 
316
            # Already enabled
 
317
            return
284
318
        self.last_enabled = datetime.datetime.utcnow()
285
319
        # Schedule a new checker to be started an 'interval' from now,
286
320
        # and every interval from then on.
447
481
    """A Client class using D-Bus
448
482
    
449
483
    Attributes:
450
 
    dbus_object_path: dbus.ObjectPath ; only set if self.use_dbus
 
484
    dbus_object_path: dbus.ObjectPath
 
485
    bus: dbus.SystemBus()
451
486
    """
452
487
    # dbus.service.Object doesn't use super(), so we can't either.
453
488
    
454
 
    def __init__(self, *args, **kwargs):
 
489
    def __init__(self, bus = None, *args, **kwargs):
 
490
        self.bus = bus
455
491
        Client.__init__(self, *args, **kwargs)
456
492
        # Only now, when this client is initialized, can it show up on
457
493
        # the D-Bus
458
494
        self.dbus_object_path = (dbus.ObjectPath
459
495
                                 (u"/clients/"
460
496
                                  + self.name.replace(u".", u"_")))
461
 
        dbus.service.Object.__init__(self, bus,
 
497
        dbus.service.Object.__init__(self, self.bus,
462
498
                                     self.dbus_object_path)
 
499
    
 
500
    @staticmethod
 
501
    def _datetime_to_dbus(dt, variant_level=0):
 
502
        """Convert a UTC datetime.datetime() to a D-Bus type."""
 
503
        return dbus.String(dt.isoformat(),
 
504
                           variant_level=variant_level)
 
505
    
463
506
    def enable(self):
464
507
        oldstate = getattr(self, u"enabled", False)
465
508
        r = Client.enable(self)
467
510
            # Emit D-Bus signals
468
511
            self.PropertyChanged(dbus.String(u"enabled"),
469
512
                                 dbus.Boolean(True, variant_level=1))
470
 
            self.PropertyChanged(dbus.String(u"last_enabled"),
471
 
                                 (_datetime_to_dbus(self.last_enabled,
472
 
                                                    variant_level=1)))
 
513
            self.PropertyChanged(
 
514
                dbus.String(u"last_enabled"),
 
515
                self._datetime_to_dbus(self.last_enabled,
 
516
                                       variant_level=1))
473
517
        return r
474
518
    
475
519
    def disable(self, signal = True):
517
561
        # Emit D-Bus signal
518
562
        self.PropertyChanged(
519
563
            dbus.String(u"last_checked_ok"),
520
 
            (_datetime_to_dbus(self.last_checked_ok,
521
 
                               variant_level=1)))
 
564
            (self._datetime_to_dbus(self.last_checked_ok,
 
565
                                    variant_level=1)))
522
566
        return r
523
567
    
524
568
    def start_checker(self, *args, **kwargs):
579
623
                dbus.String(u"host"):
580
624
                    dbus.String(self.host, variant_level=1),
581
625
                dbus.String(u"created"):
582
 
                    _datetime_to_dbus(self.created, variant_level=1),
 
626
                    self._datetime_to_dbus(self.created,
 
627
                                           variant_level=1),
583
628
                dbus.String(u"last_enabled"):
584
 
                    (_datetime_to_dbus(self.last_enabled,
585
 
                                       variant_level=1)
 
629
                    (self._datetime_to_dbus(self.last_enabled,
 
630
                                            variant_level=1)
586
631
                     if self.last_enabled is not None
587
632
                     else dbus.Boolean(False, variant_level=1)),
588
633
                dbus.String(u"enabled"):
589
634
                    dbus.Boolean(self.enabled, variant_level=1),
590
635
                dbus.String(u"last_checked_ok"):
591
 
                    (_datetime_to_dbus(self.last_checked_ok,
592
 
                                       variant_level=1)
 
636
                    (self._datetime_to_dbus(self.last_checked_ok,
 
637
                                            variant_level=1)
593
638
                     if self.last_checked_ok is not None
594
639
                     else dbus.Boolean (False, variant_level=1)),
595
640
                dbus.String(u"timeout"):
702
747
    del _interface
703
748
 
704
749
 
705
 
class ClientHandler(SocketServer.BaseRequestHandler, object):
 
750
class ClientHandler(socketserver.BaseRequestHandler, object):
706
751
    """A class to handle client connections.
707
752
    
708
753
    Instantiated once for each connection to handle it.
766
811
                    client = c
767
812
                    break
768
813
            else:
769
 
                ipc.write(u"NOTFOUND %s\n" % fpr)
 
814
                ipc.write(u"NOTFOUND %s %s\n"
 
815
                          % (fpr, unicode(self.client_address)))
770
816
                session.bye()
771
817
                return
772
818
            # Have to check if client.still_valid(), since it is
850
896
        return hex_fpr
851
897
 
852
898
 
853
 
class ForkingMixInWithPipe(SocketServer.ForkingMixIn, object):
854
 
    """Like SocketServer.ForkingMixIn, but also pass a pipe.
855
 
    
856
 
    Assumes a gobject.MainLoop event loop.
857
 
    """
 
899
class ForkingMixInWithPipe(socketserver.ForkingMixIn, object):
 
900
    """Like socketserver.ForkingMixIn, but also pass a pipe."""
858
901
    def process_request(self, request, client_address):
859
902
        """Overrides and wraps the original process_request().
860
903
        
861
 
        This function creates a new pipe in self.pipe 
 
904
        This function creates a new pipe in self.pipe
862
905
        """
863
906
        self.pipe = os.pipe()
864
907
        super(ForkingMixInWithPipe,
865
908
              self).process_request(request, client_address)
866
909
        os.close(self.pipe[1])  # close write end
867
 
        # Call "handle_ipc" for both data and EOF events
868
 
        gobject.io_add_watch(self.pipe[0],
869
 
                             gobject.IO_IN | gobject.IO_HUP,
870
 
                             self.handle_ipc)
871
 
    def handle_ipc(source, condition):
 
910
        self.add_pipe(self.pipe[0])
 
911
    def add_pipe(self, pipe):
872
912
        """Dummy function; override as necessary"""
873
 
        os.close(source)
874
 
        return False
 
913
        os.close(pipe)
875
914
 
876
915
 
877
916
class IPv6_TCPServer(ForkingMixInWithPipe,
878
 
                     SocketServer.TCPServer, object):
 
917
                     socketserver.TCPServer, object):
879
918
    """IPv6-capable TCP server.  Accepts 'None' as address and/or port
880
919
    
881
920
    Attributes:
882
921
        enabled:        Boolean; whether this server is activated yet
883
922
        interface:      None or a network interface name (string)
884
923
        use_ipv6:       Boolean; to use IPv6 or not
885
 
        ----
886
 
        clients:        Set() of Client objects
887
 
        gnutls_priority GnuTLS priority string
888
 
        use_dbus:       Boolean; to emit D-Bus signals or not
889
924
    """
890
925
    def __init__(self, server_address, RequestHandlerClass,
891
 
                 interface=None, use_ipv6=True, clients=None,
892
 
                 gnutls_priority=None, use_dbus=True):
893
 
        self.enabled = False
 
926
                 interface=None, use_ipv6=True):
894
927
        self.interface = interface
895
928
        if use_ipv6:
896
929
            self.address_family = socket.AF_INET6
897
 
        self.clients = clients
898
 
        self.use_dbus = use_dbus
899
 
        self.gnutls_priority = gnutls_priority
900
 
        SocketServer.TCPServer.__init__(self, server_address,
 
930
        socketserver.TCPServer.__init__(self, server_address,
901
931
                                        RequestHandlerClass)
902
932
    def server_bind(self):
903
933
        """This overrides the normal server_bind() function
904
934
        to bind to an interface if one was specified, and also NOT to
905
935
        bind to an address or port if they were not specified."""
906
936
        if self.interface is not None:
907
 
            try:
908
 
                self.socket.setsockopt(socket.SOL_SOCKET,
909
 
                                       SO_BINDTODEVICE,
910
 
                                       str(self.interface + u'\0'))
911
 
            except socket.error, error:
912
 
                if error[0] == errno.EPERM:
913
 
                    logger.error(u"No permission to"
914
 
                                 u" bind to interface %s",
915
 
                                 self.interface)
916
 
                else:
917
 
                    raise
 
937
            if SO_BINDTODEVICE is None:
 
938
                logger.error(u"SO_BINDTODEVICE does not exist;"
 
939
                             u" cannot bind to interface %s",
 
940
                             self.interface)
 
941
            else:
 
942
                try:
 
943
                    self.socket.setsockopt(socket.SOL_SOCKET,
 
944
                                           SO_BINDTODEVICE,
 
945
                                           str(self.interface
 
946
                                               + u'\0'))
 
947
                except socket.error, error:
 
948
                    if error[0] == errno.EPERM:
 
949
                        logger.error(u"No permission to"
 
950
                                     u" bind to interface %s",
 
951
                                     self.interface)
 
952
                    elif error[0] == errno.ENOPROTOOPT:
 
953
                        logger.error(u"SO_BINDTODEVICE not available;"
 
954
                                     u" cannot bind to interface %s",
 
955
                                     self.interface)
 
956
                    else:
 
957
                        raise
918
958
        # Only bind(2) the socket if we really need to.
919
959
        if self.server_address[0] or self.server_address[1]:
920
960
            if not self.server_address[0]:
933
973
#                                            0, # flowinfo
934
974
#                                            if_nametoindex
935
975
#                                            (self.interface))
936
 
            return SocketServer.TCPServer.server_bind(self)
 
976
            return socketserver.TCPServer.server_bind(self)
 
977
 
 
978
 
 
979
class MandosServer(IPv6_TCPServer):
 
980
    """Mandos server.
 
981
    
 
982
    Attributes:
 
983
        clients:        set of Client objects
 
984
        gnutls_priority GnuTLS priority string
 
985
        use_dbus:       Boolean; to emit D-Bus signals or not
 
986
        clients:        set of Client objects
 
987
        gnutls_priority GnuTLS priority string
 
988
        use_dbus:       Boolean; to emit D-Bus signals or not
 
989
    
 
990
    Assumes a gobject.MainLoop event loop.
 
991
    """
 
992
    def __init__(self, server_address, RequestHandlerClass,
 
993
                 interface=None, use_ipv6=True, clients=None,
 
994
                 gnutls_priority=None, use_dbus=True):
 
995
        self.enabled = False
 
996
        self.clients = clients
 
997
        if self.clients is None:
 
998
            self.clients = set()
 
999
        self.use_dbus = use_dbus
 
1000
        self.gnutls_priority = gnutls_priority
 
1001
        IPv6_TCPServer.__init__(self, server_address,
 
1002
                                RequestHandlerClass,
 
1003
                                interface = interface,
 
1004
                                use_ipv6 = use_ipv6)
937
1005
    def server_activate(self):
938
1006
        if self.enabled:
939
 
            return SocketServer.TCPServer.server_activate(self)
 
1007
            return socketserver.TCPServer.server_activate(self)
940
1008
    def enable(self):
941
1009
        self.enabled = True
 
1010
    def add_pipe(self, pipe):
 
1011
        # Call "handle_ipc" for both data and EOF events
 
1012
        gobject.io_add_watch(pipe, gobject.IO_IN | gobject.IO_HUP,
 
1013
                             self.handle_ipc)
942
1014
    def handle_ipc(self, source, condition, file_objects={}):
943
1015
        condition_names = {
944
1016
            gobject.IO_IN: u"IN",   # There is data to read.
1050
1122
    return timevalue
1051
1123
 
1052
1124
 
1053
 
def server_state_changed(state):
1054
 
    """Derived from the Avahi example code"""
1055
 
    if state == avahi.SERVER_COLLISION:
1056
 
        logger.error(u"Zeroconf server name collision")
1057
 
        service.remove()
1058
 
    elif state == avahi.SERVER_RUNNING:
1059
 
        service.add()
1060
 
 
1061
 
 
1062
 
def entry_group_state_changed(state, error):
1063
 
    """Derived from the Avahi example code"""
1064
 
    logger.debug(u"Avahi state change: %i", state)
1065
 
    
1066
 
    if state == avahi.ENTRY_GROUP_ESTABLISHED:
1067
 
        logger.debug(u"Zeroconf service established.")
1068
 
    elif state == avahi.ENTRY_GROUP_COLLISION:
1069
 
        logger.warning(u"Zeroconf service name collision.")
1070
 
        service.rename()
1071
 
    elif state == avahi.ENTRY_GROUP_FAILURE:
1072
 
        logger.critical(u"Avahi: Error in group state changed %s",
1073
 
                        unicode(error))
1074
 
        raise AvahiGroupError(u"State changed: %s" % unicode(error))
1075
 
 
1076
1125
def if_nametoindex(interface):
1077
1126
    """Call the C function if_nametoindex(), or equivalent
1078
1127
    
1147
1196
                      help=u"Directory to search for configuration"
1148
1197
                      u" files")
1149
1198
    parser.add_option("--no-dbus", action=u"store_false",
1150
 
                      dest=u"use_dbus", help=u"Do not provide D-Bus"
1151
 
                      u" system bus interface")
 
1199
                      dest=u"use_dbus",
 
1200
                      help=optparse.SUPPRESS_HELP) # XXX: Not done yet
1152
1201
    parser.add_option("--no-ipv6", action=u"store_false",
1153
1202
                      dest=u"use_ipv6", help=u"Do not use IPv6")
1154
1203
    options = parser.parse_args()[0]
1171
1220
                        }
1172
1221
    
1173
1222
    # Parse config file for server-global settings
1174
 
    server_config = ConfigParser.SafeConfigParser(server_defaults)
 
1223
    server_config = configparser.SafeConfigParser(server_defaults)
1175
1224
    del server_defaults
1176
1225
    server_config.read(os.path.join(options.configdir,
1177
1226
                                    u"mandos.conf"))
1206
1255
    # For convenience
1207
1256
    debug = server_settings[u"debug"]
1208
1257
    use_dbus = server_settings[u"use_dbus"]
 
1258
    use_dbus = False            # XXX: Not done yet
1209
1259
    use_ipv6 = server_settings[u"use_ipv6"]
1210
1260
    
1211
1261
    if not debug:
1224
1274
                        u"checker": u"fping -q -- %%(host)s",
1225
1275
                        u"host": u"",
1226
1276
                        }
1227
 
    client_config = ConfigParser.SafeConfigParser(client_defaults)
 
1277
    client_config = configparser.SafeConfigParser(client_defaults)
1228
1278
    client_config.read(os.path.join(server_settings[u"configdir"],
1229
1279
                                    u"clients.conf"))
1230
1280
    
1231
1281
    global mandos_dbus_service
1232
1282
    mandos_dbus_service = None
1233
1283
    
1234
 
    clients = Set()
1235
 
    tcp_server = IPv6_TCPServer((server_settings[u"address"],
1236
 
                                 server_settings[u"port"]),
1237
 
                                ClientHandler,
1238
 
                                interface=
1239
 
                                server_settings[u"interface"],
1240
 
                                use_ipv6=use_ipv6,
1241
 
                                clients=clients,
1242
 
                                gnutls_priority=
1243
 
                                server_settings[u"priority"],
1244
 
                                use_dbus=use_dbus)
 
1284
    tcp_server = MandosServer((server_settings[u"address"],
 
1285
                               server_settings[u"port"]),
 
1286
                              ClientHandler,
 
1287
                              interface=server_settings[u"interface"],
 
1288
                              use_ipv6=use_ipv6,
 
1289
                              gnutls_priority=
 
1290
                              server_settings[u"priority"],
 
1291
                              use_dbus=use_dbus)
1245
1292
    pidfilename = u"/var/run/mandos.pid"
1246
1293
    try:
1247
1294
        pidfile = open(pidfilename, u"w")
1282
1329
        (gnutls.library.functions
1283
1330
         .gnutls_global_set_log_function(debug_gnutls))
1284
1331
    
1285
 
    global service
1286
 
    protocol = avahi.PROTO_INET6 if use_ipv6 else avahi.PROTO_INET
1287
 
    service = AvahiService(name = server_settings[u"servicename"],
1288
 
                           servicetype = u"_mandos._tcp",
1289
 
                           protocol = protocol)
1290
 
    if server_settings["interface"]:
1291
 
        service.interface = (if_nametoindex
1292
 
                             (str(server_settings[u"interface"])))
1293
 
    
1294
1332
    global main_loop
1295
 
    global bus
1296
 
    global server
1297
1333
    # From the Avahi example code
1298
1334
    DBusGMainLoop(set_as_default=True )
1299
1335
    main_loop = gobject.MainLoop()
1300
1336
    bus = dbus.SystemBus()
1301
 
    server = dbus.Interface(bus.get_object(avahi.DBUS_NAME,
1302
 
                                           avahi.DBUS_PATH_SERVER),
1303
 
                            avahi.DBUS_INTERFACE_SERVER)
1304
1337
    # End of Avahi example code
1305
1338
    if use_dbus:
1306
1339
        bus_name = dbus.service.BusName(u"se.bsnet.fukt.Mandos", bus)
 
1340
    protocol = avahi.PROTO_INET6 if use_ipv6 else avahi.PROTO_INET
 
1341
    service = AvahiService(name = server_settings[u"servicename"],
 
1342
                           servicetype = u"_mandos._tcp",
 
1343
                           protocol = protocol, bus = bus)
 
1344
    if server_settings["interface"]:
 
1345
        service.interface = (if_nametoindex
 
1346
                             (str(server_settings[u"interface"])))
1307
1347
    
1308
1348
    client_class = Client
1309
1349
    if use_dbus:
1310
 
        client_class = ClientDBus
1311
 
    clients.update(Set(
 
1350
        client_class = functools.partial(ClientDBus, bus = bus)
 
1351
    tcp_server.clients.update(set(
1312
1352
            client_class(name = section,
1313
1353
                         config= dict(client_config.items(section)))
1314
1354
            for section in client_config.sections()))
1315
 
    if not clients:
 
1355
    if not tcp_server.clients:
1316
1356
        logger.warning(u"No clients defined")
1317
1357
    
1318
1358
    if debug:
1342
1382
    
1343
1383
    def cleanup():
1344
1384
        "Cleanup function; run on exit"
1345
 
        global group
1346
 
        # From the Avahi example code
1347
 
        if not group is None:
1348
 
            group.Free()
1349
 
            group = None
1350
 
        # End of Avahi example code
 
1385
        service.cleanup()
1351
1386
        
1352
 
        while clients:
1353
 
            client = clients.pop()
 
1387
        while tcp_server.clients:
 
1388
            client = tcp_server.clients.pop()
1354
1389
            client.disable_hook = None
1355
1390
            client.disable()
1356
1391
    
1386
1421
            @dbus.service.method(_interface, out_signature=u"ao")
1387
1422
            def GetAllClients(self):
1388
1423
                "D-Bus method"
1389
 
                return dbus.Array(c.dbus_object_path for c in clients)
 
1424
                return dbus.Array(c.dbus_object_path
 
1425
                                  for c in tcp_server.clients)
1390
1426
            
1391
1427
            @dbus.service.method(_interface,
1392
1428
                                 out_signature=u"a{oa{sv}}")
1394
1430
                "D-Bus method"
1395
1431
                return dbus.Dictionary(
1396
1432
                    ((c.dbus_object_path, c.GetAllProperties())
1397
 
                     for c in clients),
 
1433
                     for c in tcp_server.clients),
1398
1434
                    signature=u"oa{sv}")
1399
1435
            
1400
1436
            @dbus.service.method(_interface, in_signature=u"o")
1401
1437
            def RemoveClient(self, object_path):
1402
1438
                "D-Bus method"
1403
 
                for c in clients:
 
1439
                for c in tcp_server.clients:
1404
1440
                    if c.dbus_object_path == object_path:
1405
 
                        clients.remove(c)
 
1441
                        tcp_server.clients.remove(c)
1406
1442
                        c.remove_from_connection()
1407
1443
                        # Don't signal anything except ClientRemoved
1408
1444
                        c.disable(signal=False)
1415
1451
        
1416
1452
        mandos_dbus_service = MandosDBusService()
1417
1453
    
1418
 
    for client in clients:
 
1454
    for client in tcp_server.clients:
1419
1455
        if use_dbus:
1420
1456
            # Emit D-Bus signal
1421
1457
            mandos_dbus_service.ClientAdded(client.dbus_object_path,
1439
1475
    
1440
1476
    try:
1441
1477
        # From the Avahi example code
1442
 
        server.connect_to_signal(u"StateChanged", server_state_changed)
1443
1478
        try:
1444
 
            server_state_changed(server.GetState())
 
1479
            service.activate()
1445
1480
        except dbus.exceptions.DBusException, error:
1446
1481
            logger.critical(u"DBusException: %s", error)
1447
1482
            sys.exit(1)