/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 10:41:41 UTC
  • Revision ID: teddy@fukt.bsnet.se-20090416104141-19m1kqbwr5v1q416
Code cleanup.

* mandos: Move some global stuff into classes.
  (server, group): Moved into "AvahiService".  All users changed.
  (AvahiService.group, AvahiService.server): New attributes.
  (entry_group_state_changed, server_state_changed): Moved into
                                                     "AvahiService".
                                                     All callers
                                                     changed.
  (AvahiService.cleanup, AvahiService.activate): New.
  (_datetime_to_dbus): Moved into "ClientDBus".  All callers changed.
  (ClientDBus._datetime_to_dbus): New.

Show diffs side-by-side

added added

removed removed

Lines of Context:
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
74
73
    try:
75
74
        from IN import SO_BINDTODEVICE
76
75
    except ImportError:
77
 
        SO_BINDTODEVICE = None
 
76
        # From /usr/include/asm/socket.h
 
77
        SO_BINDTODEVICE = 25
78
78
 
79
79
 
80
80
version = "1.0.8"
126
126
                  a sensible number of times
127
127
    group: D-Bus Entry Group
128
128
    server: D-Bus Server
129
 
    bus: dbus.SystemBus()
130
129
    """
131
130
    def __init__(self, interface = avahi.IF_UNSPEC, name = None,
132
131
                 servicetype = None, port = None, TXT = None,
133
132
                 domain = u"", host = u"", max_renames = 32768,
134
 
                 protocol = avahi.PROTO_UNSPEC, bus = None):
 
133
                 protocol = avahi.PROTO_UNSPEC):
135
134
        self.interface = interface
136
135
        self.name = name
137
136
        self.type = servicetype
144
143
        self.protocol = protocol
145
144
        self.group = None       # our entry group
146
145
        self.server = None
147
 
        self.bus = bus
148
146
    def rename(self):
149
147
        """Derived from the Avahi example code"""
150
148
        if self.rename_count >= self.max_renames:
170
168
        """Derived from the Avahi example code"""
171
169
        if self.group is None:
172
170
            self.group = dbus.Interface(
173
 
                self.bus.get_object(avahi.DBUS_NAME,
174
 
                                    self.server.EntryGroupNew()),
 
171
                bus.get_object(avahi.DBUS_NAME,
 
172
                               self.server.EntryGroupNew()),
175
173
                avahi.DBUS_INTERFACE_ENTRY_GROUP)
176
174
            self.group.connect_to_signal('StateChanged',
177
175
                                         self.entry_group_state_changed)
216
214
        """Derived from the Avahi example code"""
217
215
        if self.server is None:
218
216
            self.server = dbus.Interface(
219
 
                self.bus.get_object(avahi.DBUS_NAME,
220
 
                                    avahi.DBUS_PATH_SERVER),
 
217
                bus.get_object(avahi.DBUS_NAME,
 
218
                               avahi.DBUS_PATH_SERVER),
221
219
                avahi.DBUS_INTERFACE_SERVER)
222
220
        self.server.connect_to_signal(u"StateChanged",
223
221
                                 self.server_state_changed)
312
310
    
313
311
    def enable(self):
314
312
        """Start this client's checker and timeout hooks"""
315
 
        if getattr(self, u"enabled", False):
316
 
            # Already enabled
317
 
            return
318
313
        self.last_enabled = datetime.datetime.utcnow()
319
314
        # Schedule a new checker to be started an 'interval' from now,
320
315
        # and every interval from then on.
481
476
    """A Client class using D-Bus
482
477
    
483
478
    Attributes:
484
 
    dbus_object_path: dbus.ObjectPath
485
 
    bus: dbus.SystemBus()
 
479
    dbus_object_path: dbus.ObjectPath ; only set if self.use_dbus
486
480
    """
487
481
    # dbus.service.Object doesn't use super(), so we can't either.
488
482
    
489
 
    def __init__(self, bus = None, *args, **kwargs):
490
 
        self.bus = bus
 
483
    def __init__(self, *args, **kwargs):
491
484
        Client.__init__(self, *args, **kwargs)
492
485
        # Only now, when this client is initialized, can it show up on
493
486
        # the D-Bus
494
487
        self.dbus_object_path = (dbus.ObjectPath
495
488
                                 (u"/clients/"
496
489
                                  + self.name.replace(u".", u"_")))
497
 
        dbus.service.Object.__init__(self, self.bus,
 
490
        dbus.service.Object.__init__(self, bus,
498
491
                                     self.dbus_object_path)
499
492
    
500
493
    @staticmethod
896
889
 
897
890
 
898
891
class ForkingMixInWithPipe(socketserver.ForkingMixIn, object):
899
 
    """Like socketserver.ForkingMixIn, but also pass a pipe."""
 
892
    """Like socketserver.ForkingMixIn, but also pass a pipe.
 
893
    
 
894
    Assumes a gobject.MainLoop event loop.
 
895
    """
900
896
    def process_request(self, request, client_address):
901
897
        """Overrides and wraps the original process_request().
902
898
        
906
902
        super(ForkingMixInWithPipe,
907
903
              self).process_request(request, client_address)
908
904
        os.close(self.pipe[1])  # close write end
909
 
        self.add_pipe(self.pipe[0])
910
 
    def add_pipe(self, pipe):
 
905
        # Call "handle_ipc" for both data and EOF events
 
906
        gobject.io_add_watch(self.pipe[0],
 
907
                             gobject.IO_IN | gobject.IO_HUP,
 
908
                             self.handle_ipc)
 
909
    def handle_ipc(source, condition):
911
910
        """Dummy function; override as necessary"""
912
 
        os.close(pipe)
 
911
        os.close(source)
 
912
        return False
913
913
 
914
914
 
915
915
class IPv6_TCPServer(ForkingMixInWithPipe,
920
920
        enabled:        Boolean; whether this server is activated yet
921
921
        interface:      None or a network interface name (string)
922
922
        use_ipv6:       Boolean; to use IPv6 or not
 
923
        ----
 
924
        clients:        set of Client objects
 
925
        gnutls_priority GnuTLS priority string
 
926
        use_dbus:       Boolean; to emit D-Bus signals or not
923
927
    """
924
928
    def __init__(self, server_address, RequestHandlerClass,
925
 
                 interface=None, use_ipv6=True):
 
929
                 interface=None, use_ipv6=True, clients=None,
 
930
                 gnutls_priority=None, use_dbus=True):
 
931
        self.enabled = False
926
932
        self.interface = interface
927
933
        if use_ipv6:
928
934
            self.address_family = socket.AF_INET6
 
935
        self.clients = clients
 
936
        self.use_dbus = use_dbus
 
937
        self.gnutls_priority = gnutls_priority
929
938
        socketserver.TCPServer.__init__(self, server_address,
930
939
                                        RequestHandlerClass)
931
940
    def server_bind(self):
933
942
        to bind to an interface if one was specified, and also NOT to
934
943
        bind to an address or port if they were not specified."""
935
944
        if self.interface is not None:
936
 
            if SO_BINDTODEVICE is None:
937
 
                logger.error(u"SO_BINDTODEVICE does not exist;"
938
 
                             u" cannot bind to interface %s",
939
 
                             self.interface)
940
 
            else:
941
 
                try:
942
 
                    self.socket.setsockopt(socket.SOL_SOCKET,
943
 
                                           SO_BINDTODEVICE,
944
 
                                           str(self.interface
945
 
                                               + u'\0'))
946
 
                except socket.error, error:
947
 
                    if error[0] == errno.EPERM:
948
 
                        logger.error(u"No permission to"
949
 
                                     u" bind to interface %s",
950
 
                                     self.interface)
951
 
                    elif error[0] == errno.ENOPROTOOPT:
952
 
                        logger.error(u"SO_BINDTODEVICE not available;"
953
 
                                     u" cannot bind to interface %s",
954
 
                                     self.interface)
955
 
                    else:
956
 
                        raise
 
945
            try:
 
946
                self.socket.setsockopt(socket.SOL_SOCKET,
 
947
                                       SO_BINDTODEVICE,
 
948
                                       str(self.interface + u'\0'))
 
949
            except socket.error, error:
 
950
                if error[0] == errno.EPERM:
 
951
                    logger.error(u"No permission to"
 
952
                                 u" bind to interface %s",
 
953
                                 self.interface)
 
954
                else:
 
955
                    raise
957
956
        # Only bind(2) the socket if we really need to.
958
957
        if self.server_address[0] or self.server_address[1]:
959
958
            if not self.server_address[0]:
973
972
#                                            if_nametoindex
974
973
#                                            (self.interface))
975
974
            return socketserver.TCPServer.server_bind(self)
976
 
 
977
 
 
978
 
class MandosServer(IPv6_TCPServer):
979
 
    """Mandos server.
980
 
    
981
 
    Attributes:
982
 
        clients:        set of Client objects
983
 
        gnutls_priority GnuTLS priority string
984
 
        use_dbus:       Boolean; to emit D-Bus signals or not
985
 
        clients:        set of Client objects
986
 
        gnutls_priority GnuTLS priority string
987
 
        use_dbus:       Boolean; to emit D-Bus signals or not
988
 
    
989
 
    Assumes a gobject.MainLoop event loop.
990
 
    """
991
 
    def __init__(self, server_address, RequestHandlerClass,
992
 
                 interface=None, use_ipv6=True, clients=None,
993
 
                 gnutls_priority=None, use_dbus=True):
994
 
        self.enabled = False
995
 
        self.clients = clients
996
 
        if self.clients is None:
997
 
            self.clients = set()
998
 
        self.use_dbus = use_dbus
999
 
        self.gnutls_priority = gnutls_priority
1000
 
        IPv6_TCPServer.__init__(self, server_address,
1001
 
                                RequestHandlerClass,
1002
 
                                interface = interface,
1003
 
                                use_ipv6 = use_ipv6)
1004
975
    def server_activate(self):
1005
976
        if self.enabled:
1006
977
            return socketserver.TCPServer.server_activate(self)
1007
978
    def enable(self):
1008
979
        self.enabled = True
1009
 
    def add_pipe(self, pipe):
1010
 
        # Call "handle_ipc" for both data and EOF events
1011
 
        gobject.io_add_watch(pipe, gobject.IO_IN | gobject.IO_HUP,
1012
 
                             self.handle_ipc)
1013
980
    def handle_ipc(self, source, condition, file_objects={}):
1014
981
        condition_names = {
1015
982
            gobject.IO_IN: u"IN",   # There is data to read.
1279
1246
    global mandos_dbus_service
1280
1247
    mandos_dbus_service = None
1281
1248
    
1282
 
    tcp_server = MandosServer((server_settings[u"address"],
1283
 
                               server_settings[u"port"]),
1284
 
                              ClientHandler,
1285
 
                              interface=server_settings[u"interface"],
1286
 
                              use_ipv6=use_ipv6,
1287
 
                              gnutls_priority=
1288
 
                              server_settings[u"priority"],
1289
 
                              use_dbus=use_dbus)
 
1249
    clients = set()
 
1250
    tcp_server = IPv6_TCPServer((server_settings[u"address"],
 
1251
                                 server_settings[u"port"]),
 
1252
                                ClientHandler,
 
1253
                                interface=
 
1254
                                server_settings[u"interface"],
 
1255
                                use_ipv6=use_ipv6,
 
1256
                                clients=clients,
 
1257
                                gnutls_priority=
 
1258
                                server_settings[u"priority"],
 
1259
                                use_dbus=use_dbus)
1290
1260
    pidfilename = u"/var/run/mandos.pid"
1291
1261
    try:
1292
1262
        pidfile = open(pidfilename, u"w")
1327
1297
        (gnutls.library.functions
1328
1298
         .gnutls_global_set_log_function(debug_gnutls))
1329
1299
    
 
1300
    global service
 
1301
    protocol = avahi.PROTO_INET6 if use_ipv6 else avahi.PROTO_INET
 
1302
    service = AvahiService(name = server_settings[u"servicename"],
 
1303
                           servicetype = u"_mandos._tcp",
 
1304
                           protocol = protocol)
 
1305
    if server_settings["interface"]:
 
1306
        service.interface = (if_nametoindex
 
1307
                             (str(server_settings[u"interface"])))
 
1308
    
1330
1309
    global main_loop
 
1310
    global bus
1331
1311
    # From the Avahi example code
1332
1312
    DBusGMainLoop(set_as_default=True )
1333
1313
    main_loop = gobject.MainLoop()
1335
1315
    # End of Avahi example code
1336
1316
    if use_dbus:
1337
1317
        bus_name = dbus.service.BusName(u"se.bsnet.fukt.Mandos", bus)
1338
 
    protocol = avahi.PROTO_INET6 if use_ipv6 else avahi.PROTO_INET
1339
 
    service = AvahiService(name = server_settings[u"servicename"],
1340
 
                           servicetype = u"_mandos._tcp",
1341
 
                           protocol = protocol, bus = bus)
1342
 
    if server_settings["interface"]:
1343
 
        service.interface = (if_nametoindex
1344
 
                             (str(server_settings[u"interface"])))
1345
1318
    
1346
1319
    client_class = Client
1347
1320
    if use_dbus:
1348
 
        client_class = functools.partial(ClientDBus, bus = bus)
1349
 
    tcp_server.clients.update(set(
 
1321
        client_class = ClientDBus
 
1322
    clients.update(set(
1350
1323
            client_class(name = section,
1351
1324
                         config= dict(client_config.items(section)))
1352
1325
            for section in client_config.sections()))
1353
 
    if not tcp_server.clients:
 
1326
    if not clients:
1354
1327
        logger.warning(u"No clients defined")
1355
1328
    
1356
1329
    if debug:
1382
1355
        "Cleanup function; run on exit"
1383
1356
        service.cleanup()
1384
1357
        
1385
 
        while tcp_server.clients:
1386
 
            client = tcp_server.clients.pop()
 
1358
        while clients:
 
1359
            client = clients.pop()
1387
1360
            client.disable_hook = None
1388
1361
            client.disable()
1389
1362
    
1419
1392
            @dbus.service.method(_interface, out_signature=u"ao")
1420
1393
            def GetAllClients(self):
1421
1394
                "D-Bus method"
1422
 
                return dbus.Array(c.dbus_object_path
1423
 
                                  for c in tcp_server.clients)
 
1395
                return dbus.Array(c.dbus_object_path for c in clients)
1424
1396
            
1425
1397
            @dbus.service.method(_interface,
1426
1398
                                 out_signature=u"a{oa{sv}}")
1428
1400
                "D-Bus method"
1429
1401
                return dbus.Dictionary(
1430
1402
                    ((c.dbus_object_path, c.GetAllProperties())
1431
 
                     for c in tcp_server.clients),
 
1403
                     for c in clients),
1432
1404
                    signature=u"oa{sv}")
1433
1405
            
1434
1406
            @dbus.service.method(_interface, in_signature=u"o")
1435
1407
            def RemoveClient(self, object_path):
1436
1408
                "D-Bus method"
1437
 
                for c in tcp_server.clients:
 
1409
                for c in clients:
1438
1410
                    if c.dbus_object_path == object_path:
1439
 
                        tcp_server.clients.remove(c)
 
1411
                        clients.remove(c)
1440
1412
                        c.remove_from_connection()
1441
1413
                        # Don't signal anything except ClientRemoved
1442
1414
                        c.disable(signal=False)
1449
1421
        
1450
1422
        mandos_dbus_service = MandosDBusService()
1451
1423
    
1452
 
    for client in tcp_server.clients:
 
1424
    for client in clients:
1453
1425
        if use_dbus:
1454
1426
            # Emit D-Bus signal
1455
1427
            mandos_dbus_service.ClientAdded(client.dbus_object_path,