/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

  • Committer: Teddy Hogeborn
  • Date: 2009-04-16 06:47:28 UTC
  • Revision ID: teddy@fukt.bsnet.se-20090416064728-c3d36mvgxo5q9aoh
* mandos: Import "SocketServer" as "socketserver" and "ConfigParser"
          as "configparser".  All users changed.

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", "remove", "server_state_changed",
10
 
# "entry_group_state_changed", "cleanup", and "activate" in the
11
 
# "AvahiService" class, and some lines in "main".
 
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".
12
12
13
13
# Everything else is
14
14
# Copyright © 2008,2009 Teddy Hogeborn
58
58
from contextlib import closing
59
59
import struct
60
60
import fcntl
61
 
import functools
62
61
 
63
62
import dbus
64
63
import dbus.service
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
128
 
    group: D-Bus Entry Group
129
 
    server: D-Bus Server
130
127
    """
131
128
    def __init__(self, interface = avahi.IF_UNSPEC, name = None,
132
129
                 servicetype = None, port = None, TXT = None,
133
130
                 domain = u"", host = u"", max_renames = 32768,
134
 
                 protocol = avahi.PROTO_UNSPEC, bus = None):
 
131
                 protocol = avahi.PROTO_UNSPEC):
135
132
        self.interface = interface
136
133
        self.name = name
137
134
        self.type = servicetype
142
139
        self.rename_count = 0
143
140
        self.max_renames = max_renames
144
141
        self.protocol = protocol
145
 
        self.group = None       # our entry group
146
 
        self.server = None
147
 
        self.bus = bus
148
142
    def rename(self):
149
143
        """Derived from the Avahi example code"""
150
144
        if self.rename_count >= self.max_renames:
152
146
                            u" after %i retries, exiting.",
153
147
                            self.rename_count)
154
148
            raise AvahiServiceError(u"Too many renames")
155
 
        self.name = self.server.GetAlternativeServiceName(self.name)
 
149
        self.name = server.GetAlternativeServiceName(self.name)
156
150
        logger.info(u"Changing Zeroconf service name to %r ...",
157
 
                    unicode(self.name))
 
151
                    self.name)
158
152
        syslogger.setFormatter(logging.Formatter
159
153
                               (u'Mandos (%s) [%%(process)d]:'
160
154
                                u' %%(levelname)s: %%(message)s'
164
158
        self.rename_count += 1
165
159
    def remove(self):
166
160
        """Derived from the Avahi example code"""
167
 
        if self.group is not None:
168
 
            self.group.Reset()
 
161
        if group is not None:
 
162
            group.Reset()
169
163
    def add(self):
170
164
        """Derived from the Avahi example code"""
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)
 
165
        global group
 
166
        if group is None:
 
167
            group = dbus.Interface(bus.get_object
 
168
                                   (avahi.DBUS_NAME,
 
169
                                    server.EntryGroupNew()),
 
170
                                   avahi.DBUS_INTERFACE_ENTRY_GROUP)
 
171
            group.connect_to_signal('StateChanged',
 
172
                                    entry_group_state_changed)
178
173
        logger.debug(u"Adding Zeroconf service '%s' of type '%s' ...",
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())
 
174
                     service.name, service.type)
 
175
        group.AddService(
 
176
                self.interface,         # interface
 
177
                self.protocol,          # protocol
 
178
                dbus.UInt32(0),         # flags
 
179
                self.name, self.type,
 
180
                self.domain, self.host,
 
181
                dbus.UInt16(self.port),
 
182
                avahi.string_array_to_txt_array(self.TXT))
 
183
        group.Commit()
 
184
 
 
185
# From the Avahi example code:
 
186
group = None                            # our entry group
 
187
# End of Avahi example code
 
188
 
 
189
 
 
190
def _datetime_to_dbus(dt, variant_level=0):
 
191
    """Convert a UTC datetime.datetime() to a D-Bus type."""
 
192
    return dbus.String(dt.isoformat(), variant_level=variant_level)
225
193
 
226
194
 
227
195
class Client(object):
482
450
    """
483
451
    # dbus.service.Object doesn't use super(), so we can't either.
484
452
    
485
 
    def __init__(self, bus = None, *args, **kwargs):
486
 
        self.bus = bus
 
453
    def __init__(self, *args, **kwargs):
487
454
        Client.__init__(self, *args, **kwargs)
488
455
        # Only now, when this client is initialized, can it show up on
489
456
        # the D-Bus
490
457
        self.dbus_object_path = (dbus.ObjectPath
491
458
                                 (u"/clients/"
492
459
                                  + self.name.replace(u".", u"_")))
493
 
        dbus.service.Object.__init__(self, self.bus,
 
460
        dbus.service.Object.__init__(self, bus,
494
461
                                     self.dbus_object_path)
495
 
    
496
 
    @staticmethod
497
 
    def _datetime_to_dbus(dt, variant_level=0):
498
 
        """Convert a UTC datetime.datetime() to a D-Bus type."""
499
 
        return dbus.String(dt.isoformat(),
500
 
                           variant_level=variant_level)
501
 
    
502
462
    def enable(self):
503
463
        oldstate = getattr(self, u"enabled", False)
504
464
        r = Client.enable(self)
506
466
            # Emit D-Bus signals
507
467
            self.PropertyChanged(dbus.String(u"enabled"),
508
468
                                 dbus.Boolean(True, variant_level=1))
509
 
            self.PropertyChanged(
510
 
                dbus.String(u"last_enabled"),
511
 
                self._datetime_to_dbus(self.last_enabled,
512
 
                                       variant_level=1))
 
469
            self.PropertyChanged(dbus.String(u"last_enabled"),
 
470
                                 (_datetime_to_dbus(self.last_enabled,
 
471
                                                    variant_level=1)))
513
472
        return r
514
473
    
515
474
    def disable(self, signal = True):
557
516
        # Emit D-Bus signal
558
517
        self.PropertyChanged(
559
518
            dbus.String(u"last_checked_ok"),
560
 
            (self._datetime_to_dbus(self.last_checked_ok,
561
 
                                    variant_level=1)))
 
519
            (_datetime_to_dbus(self.last_checked_ok,
 
520
                               variant_level=1)))
562
521
        return r
563
522
    
564
523
    def start_checker(self, *args, **kwargs):
619
578
                dbus.String(u"host"):
620
579
                    dbus.String(self.host, variant_level=1),
621
580
                dbus.String(u"created"):
622
 
                    self._datetime_to_dbus(self.created,
623
 
                                           variant_level=1),
 
581
                    _datetime_to_dbus(self.created, variant_level=1),
624
582
                dbus.String(u"last_enabled"):
625
 
                    (self._datetime_to_dbus(self.last_enabled,
626
 
                                            variant_level=1)
 
583
                    (_datetime_to_dbus(self.last_enabled,
 
584
                                       variant_level=1)
627
585
                     if self.last_enabled is not None
628
586
                     else dbus.Boolean(False, variant_level=1)),
629
587
                dbus.String(u"enabled"):
630
588
                    dbus.Boolean(self.enabled, variant_level=1),
631
589
                dbus.String(u"last_checked_ok"):
632
 
                    (self._datetime_to_dbus(self.last_checked_ok,
633
 
                                            variant_level=1)
 
590
                    (_datetime_to_dbus(self.last_checked_ok,
 
591
                                       variant_level=1)
634
592
                     if self.last_checked_ok is not None
635
593
                     else dbus.Boolean (False, variant_level=1)),
636
594
                dbus.String(u"timeout"):
1091
1049
    return timevalue
1092
1050
 
1093
1051
 
 
1052
def server_state_changed(state):
 
1053
    """Derived from the Avahi example code"""
 
1054
    if state == avahi.SERVER_COLLISION:
 
1055
        logger.error(u"Zeroconf server name collision")
 
1056
        service.remove()
 
1057
    elif state == avahi.SERVER_RUNNING:
 
1058
        service.add()
 
1059
 
 
1060
 
 
1061
def entry_group_state_changed(state, error):
 
1062
    """Derived from the Avahi example code"""
 
1063
    logger.debug(u"Avahi state change: %i", state)
 
1064
    
 
1065
    if state == avahi.ENTRY_GROUP_ESTABLISHED:
 
1066
        logger.debug(u"Zeroconf service established.")
 
1067
    elif state == avahi.ENTRY_GROUP_COLLISION:
 
1068
        logger.warning(u"Zeroconf service name collision.")
 
1069
        service.rename()
 
1070
    elif state == avahi.ENTRY_GROUP_FAILURE:
 
1071
        logger.critical(u"Avahi: Error in group state changed %s",
 
1072
                        unicode(error))
 
1073
        raise AvahiGroupError(u"State changed: %s" % unicode(error))
 
1074
 
1094
1075
def if_nametoindex(interface):
1095
1076
    """Call the C function if_nametoindex(), or equivalent
1096
1077
    
1300
1281
        (gnutls.library.functions
1301
1282
         .gnutls_global_set_log_function(debug_gnutls))
1302
1283
    
 
1284
    global service
 
1285
    protocol = avahi.PROTO_INET6 if use_ipv6 else avahi.PROTO_INET
 
1286
    service = AvahiService(name = server_settings[u"servicename"],
 
1287
                           servicetype = u"_mandos._tcp",
 
1288
                           protocol = protocol)
 
1289
    if server_settings["interface"]:
 
1290
        service.interface = (if_nametoindex
 
1291
                             (str(server_settings[u"interface"])))
 
1292
    
1303
1293
    global main_loop
 
1294
    global bus
 
1295
    global server
1304
1296
    # From the Avahi example code
1305
1297
    DBusGMainLoop(set_as_default=True )
1306
1298
    main_loop = gobject.MainLoop()
1307
1299
    bus = dbus.SystemBus()
 
1300
    server = dbus.Interface(bus.get_object(avahi.DBUS_NAME,
 
1301
                                           avahi.DBUS_PATH_SERVER),
 
1302
                            avahi.DBUS_INTERFACE_SERVER)
1308
1303
    # End of Avahi example code
1309
1304
    if use_dbus:
1310
1305
        bus_name = dbus.service.BusName(u"se.bsnet.fukt.Mandos", bus)
1311
 
    protocol = avahi.PROTO_INET6 if use_ipv6 else avahi.PROTO_INET
1312
 
    service = AvahiService(name = server_settings[u"servicename"],
1313
 
                           servicetype = u"_mandos._tcp",
1314
 
                           protocol = protocol, bus = bus)
1315
 
    if server_settings["interface"]:
1316
 
        service.interface = (if_nametoindex
1317
 
                             (str(server_settings[u"interface"])))
1318
1306
    
1319
1307
    client_class = Client
1320
1308
    if use_dbus:
1321
 
        client_class = functools.partial(ClientDBus, bus = bus)
 
1309
        client_class = ClientDBus
1322
1310
    clients.update(set(
1323
1311
            client_class(name = section,
1324
1312
                         config= dict(client_config.items(section)))
1353
1341
    
1354
1342
    def cleanup():
1355
1343
        "Cleanup function; run on exit"
1356
 
        service.cleanup()
 
1344
        global group
 
1345
        # From the Avahi example code
 
1346
        if not group is None:
 
1347
            group.Free()
 
1348
            group = None
 
1349
        # End of Avahi example code
1357
1350
        
1358
1351
        while clients:
1359
1352
            client = clients.pop()
1445
1438
    
1446
1439
    try:
1447
1440
        # From the Avahi example code
 
1441
        server.connect_to_signal(u"StateChanged", server_state_changed)
1448
1442
        try:
1449
 
            service.activate()
 
1443
            server_state_changed(server.GetState())
1450
1444
        except dbus.exceptions.DBusException, error:
1451
1445
            logger.critical(u"DBusException: %s", error)
1452
1446
            sys.exit(1)