/mandos/release

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

« back to all changes in this revision

Viewing changes to server.py

  • Committer: Teddy Hogeborn
  • Date: 2008-01-30 17:28:18 UTC
  • Revision ID: teddy@fukt.bsnet.se-20080130172818-fnrq2xmtg1wa8wo2
* client.cpp (main): Get t_old early since it is used on error exits.

* server.py (Client.checker): New attribute.
  (Client.check_action, Client.start_checker, Client.stop_checker,
  Client.fileno, Client.next_stop, Client.still_valid,
  Client.it_is_time_to_check): New methods.
  (Client.handle_request): Alias to "check_action".
  (main): Stop all started checkers on exit.  Exit cleanly on keyboard
  interrupt.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
#!/usr/bin/python
2
2
 
3
 
from __future__ import division
4
 
 
5
3
import SocketServer
6
4
import socket
7
5
import select
16
14
import re
17
15
import os
18
16
import signal
19
 
from sets import Set
20
 
import subprocess
21
 
 
22
 
import dbus
23
 
import gobject
24
 
import avahi
25
 
from dbus.mainloop.glib import DBusGMainLoop
26
 
 
27
 
# This variable is used to optionally bind to a specified
28
 
# interface.
29
 
serviceInterface = avahi.IF_UNSPEC
30
 
# It is a global variable to fit in with the rest of the
31
 
# variables from the Avahi server example code:
32
 
serviceName = "Mandos"
33
 
serviceType = "_mandos._tcp" # http://www.dns-sd.org/ServiceTypes.html
34
 
servicePort = None                      # Not known at startup
35
 
serviceTXT = []                         # TXT record for the service
36
 
domain = ""                  # Domain to publish on, default to .local
37
 
host = ""          # Host to publish records for, default to localhost
38
 
group = None #our entry group
39
 
rename_count = 12       # Counter so we only rename after collisions a
40
 
                        # sensible number of times
41
 
# End of Avahi example code
42
 
 
43
17
 
44
18
class Client(object):
45
 
    """A representation of a client host served by this server.
46
 
    Attributes:
47
 
    password:  string
48
 
    fqdn:      string, FQDN (used by the checker)
49
 
    created:   datetime.datetime()
50
 
    last_seen: datetime.datetime() or None if not yet seen
51
 
    timeout:   datetime.timedelta(); How long from last_seen until
52
 
                                     this client is invalid
53
 
    interval:  datetime.timedelta(); How often to start a new checker
54
 
    timeout_milliseconds: Used by gobject.timeout_add()
55
 
    interval_milliseconds: - '' -
56
 
    stop_hook: If set, called by stop() as stop_hook(self)
57
 
    checker:   subprocess.Popen(); a running checker process used
58
 
                                   to see if the client lives.
59
 
                                   Is None if no process is running.
60
 
    checker_initiator_tag: a gobject event source tag, or None
61
 
    stop_initiator_tag:    - '' -
62
 
    checker_callback_tag:  - '' -
63
 
    """
64
 
    def __init__(self, name=None, options=None, stop_hook=None,
65
 
                 dn=None, password=None, passfile=None, fqdn=None,
 
19
    def __init__(self, name=None, options=None, dn=None,
 
20
                 password=None, passfile=None, fqdn=None,
66
21
                 timeout=None, interval=-1):
67
22
        self.name = name
68
23
        self.dn = dn
71
26
        elif passfile:
72
27
            self.password = open(passfile).readall()
73
28
        else:
74
 
            raise RuntimeError(u"No Password or Passfile for client %s"
75
 
                               % self.name)
 
29
            print "No Password or Passfile in client config file"
 
30
            # raise RuntimeError XXX
 
31
            self.password = "gazonk"
76
32
        self.fqdn = fqdn                # string
77
33
        self.created = datetime.datetime.now()
78
 
        self.last_seen = None
 
34
        self.last_seen = None           # datetime.datetime()
79
35
        if timeout is None:
80
36
            timeout = options.timeout
81
 
        self.timeout = timeout
82
 
        self.timeout_milliseconds = ((self.timeout.days
83
 
                                      * 24 * 60 * 60 * 1000)
84
 
                                     + (self.timeout.seconds * 1000)
85
 
                                     + (self.timeout.microseconds
86
 
                                        // 1000))
 
37
        self.timeout = timeout          # datetime.timedelta()
87
38
        if interval == -1:
88
39
            interval = options.interval
89
 
        else:
90
 
            interval = string_to_delta(interval)
91
 
        self.interval = interval
92
 
        self.interval_milliseconds = ((self.interval.days
93
 
                                       * 24 * 60 * 60 * 1000)
94
 
                                      + (self.interval.seconds * 1000)
95
 
                                      + (self.interval.microseconds
96
 
                                         // 1000))
97
 
        self.stop_hook = stop_hook
98
 
        self.checker = None
99
 
        self.checker_initiator_tag = None
100
 
        self.stop_initiator_tag = None
101
 
        self.checker_callback_tag = None
102
 
    def start(self):
103
 
        """Start this clients checker and timeout hooks"""
104
 
        # Schedule a new checker to be started an 'interval' from now,
105
 
        # and every interval from then on.
106
 
        self.checker_initiator_tag = gobject.\
107
 
                                     timeout_add(self.interval_milliseconds,
108
 
                                                 self.start_checker)
109
 
        # Also start a new checker *right now*.
110
 
        self.start_checker()
111
 
        # Schedule a stop() when 'timeout' has passed
112
 
        self.stop_initiator_tag = gobject.\
113
 
                                     timeout_add(self.timeout_milliseconds,
114
 
                                                 self.stop)
115
 
    def stop(self):
116
 
        """Stop this client.
117
 
        The possibility that this client might be restarted is left
118
 
        open, but not currently used."""
119
 
        # print "Stopping client", self.name
120
 
        self.password = None
121
 
        if self.stop_initiator_tag:
122
 
            gobject.source_remove(self.stop_initiator_tag)
123
 
            self.stop_initiator_tag = None
124
 
        if self.checker_initiator_tag:
125
 
            gobject.source_remove(self.checker_initiator_tag)
126
 
            self.checker_initiator_tag = None
127
 
        self.stop_checker()
128
 
        if self.stop_hook:
129
 
            self.stop_hook(self)
130
 
        # Do not run this again if called by a gobject.timeout_add
131
 
        return False
132
 
    def __del__(self):
133
 
        # Some code duplication here and in stop()
134
 
        if hasattr(self, "stop_initiator_tag") \
135
 
               and self.stop_initiator_tag:
136
 
            gobject.source_remove(self.stop_initiator_tag)
137
 
            self.stop_initiator_tag = None
138
 
        if hasattr(self, "checker_initiator_tag") \
139
 
               and self.checker_initiator_tag:
140
 
            gobject.source_remove(self.checker_initiator_tag)
141
 
            self.checker_initiator_tag = None
142
 
        self.stop_checker()
143
 
    def checker_callback(self, pid, condition):
144
 
        """The checker has completed, so take appropriate actions."""
145
 
        now = datetime.datetime.now()
146
 
        if os.WIFEXITED(condition) \
147
 
               and (os.WEXITSTATUS(condition) == 0):
148
 
            #print "Checker for %(name)s succeeded" % vars(self)
 
40
        self.interval = interval        # datetime.timedelta()
 
41
        self.next_check = datetime.datetime.now() # datetime.datetime()
 
42
        self.checker = None             # or a subprocess.Popen()
 
43
    def check_action(self, now=None):
 
44
        """The checker said something and might have completed.
 
45
        Check if is has, and take appropriate actions."""
 
46
        if self.checker.poll() is None:
 
47
            # False alarm, no result yet
 
48
            #self.checker.read()
 
49
            return
 
50
        if now is None:
 
51
            now = datetime.datetime.now()
 
52
        if self.checker.returncode == 0:
149
53
            self.last_seen = now
150
 
            gobject.source_remove(self.stop_initiator_tag)
151
 
            self.stop_initiator_tag = gobject.\
152
 
                                      timeout_add(self.timeout_milliseconds,
153
 
                                                  self.stop)
154
 
        #else:
155
 
        #    if not os.WIFEXITED(condition):
156
 
        #        print "Checker for %(name)s crashed?" % vars(self)
157
 
        #    else:
158
 
        #        print "Checker for %(name)s failed" % vars(self)
159
 
        self.checker = None
160
 
        self.checker_callback_tag = None
 
54
        while self.next_check <= now:
 
55
            self.next_check += self.interval
 
56
    handle_request = check_action
161
57
    def start_checker(self):
162
 
        """Start a new checker subprocess if one is not running.
163
 
        If a checker already exists, leave it running and do
164
 
        nothing."""
165
 
        if self.checker is None:
166
 
            #print "Starting checker for", self.name
167
 
            try:
168
 
                self.checker = subprocess.\
169
 
                               Popen("sleep 1; fping -q -- %s"
170
 
                                     % re.escape(self.fqdn),
171
 
                                     stdout=subprocess.PIPE,
172
 
                                     close_fds=True, shell=True,
173
 
                                     cwd="/")
174
 
                self.checker_callback_tag = gobject.\
175
 
                                            child_watch_add(self.checker.pid,
176
 
                                                            self.\
177
 
                                                            checker_callback)
178
 
            except subprocess.OSError, error:
179
 
                sys.stderr.write(u"Failed to start subprocess: %s\n"
180
 
                                 % error)
181
 
        # Re-run this periodically if run by gobject.timeout_add
182
 
        return True
 
58
        self.stop_checker()
 
59
        try:
 
60
            self.checker = subprocess.Popen("sleep 1; fping -q -- %s"
 
61
                                            % re.escape(self.fqdn),
 
62
                                            stdout=subprocess.PIPE,
 
63
                                            close_fds=True,
 
64
                                            shell=True, cwd="/")
 
65
        except subprocess.OSError, e:
 
66
            print "Failed to start subprocess:", e
183
67
    def stop_checker(self):
184
 
        """Force the checker process, if any, to stop."""
185
 
        if not hasattr(self, "checker") or self.checker is None:
 
68
        if self.checker is None:
186
69
            return
187
 
        gobject.source_remove(self.checker_callback_tag)
188
 
        self.checker_callback_tag = None
189
70
        os.kill(self.checker.pid, signal.SIGTERM)
190
71
        if self.checker.poll() is None:
191
72
            os.kill(self.checker.pid, signal.SIGKILL)
192
73
        self.checker = None
 
74
    __del__ = stop_checker
 
75
    def fileno(self):
 
76
        if self.checker is None:
 
77
            return None
 
78
        return self.checker.stdout.fileno()
 
79
    def next_stop(self):
 
80
        """The time when something must be done about this client"""
 
81
        return min(self.last_seen + self.timeout, self.next_check)
193
82
    def still_valid(self, now=None):
194
 
        """Has the timeout not yet passed for this client?"""
195
 
        if now is None:
196
 
            now = datetime.datetime.now()
197
 
        if self.last_seen is None:
198
 
            return now < (self.created + self.timeout)
199
 
        else:
200
 
            return now < (self.last_seen + self.timeout)
 
83
        """Has this client's timeout not passed?"""
 
84
        if now is None:
 
85
            now = datetime.datetime.now()
 
86
        return now < (self.last_seen + timeout)
 
87
    def it_is_time_to_check(self, now=None):
 
88
        if now is None:
 
89
            now = datetime.datetime.now()
 
90
        return self.next_check <= now
 
91
 
 
92
 
 
93
class server_metaclass(type):
 
94
    "Common behavior for the UDP and TCP server classes"
 
95
    def __new__(cls, name, bases, attrs):
 
96
        attrs["address_family"] = socket.AF_INET6
 
97
        attrs["allow_reuse_address"] = True
 
98
        def server_bind(self):
 
99
            if self.options.interface:
 
100
                if not hasattr(socket, "SO_BINDTODEVICE"):
 
101
                    # From /usr/include/asm-i486/socket.h
 
102
                    socket.SO_BINDTODEVICE = 25
 
103
                try:
 
104
                    self.socket.setsockopt(socket.SOL_SOCKET,
 
105
                                           socket.SO_BINDTODEVICE,
 
106
                                           self.options.interface)
 
107
                except socket.error, error:
 
108
                    if error[0] == errno.EPERM:
 
109
                        print "Warning: No permission to bind to interface", \
 
110
                              self.options.interface
 
111
                    else:
 
112
                        raise error
 
113
            return super(type(self), self).server_bind()
 
114
        attrs["server_bind"] = server_bind
 
115
        def init(self, *args, **kwargs):
 
116
            if "options" in kwargs:
 
117
                self.options = kwargs["options"]
 
118
                del kwargs["options"]
 
119
            if "clients" in kwargs:
 
120
                self.clients = kwargs["clients"]
 
121
                del kwargs["clients"]
 
122
            if "credentials" in kwargs:
 
123
                self.credentials = kwargs["credentials"]
 
124
                del kwargs["credentials"]
 
125
            return super(type(self), self).__init__(*args, **kwargs)
 
126
        attrs["__init__"] = init
 
127
        return type.__new__(cls, name, bases, attrs)
 
128
 
 
129
 
 
130
class udp_handler(SocketServer.DatagramRequestHandler, object):
 
131
    def handle(self):
 
132
        self.wfile.write("Polo")
 
133
        print "UDP request answered"
 
134
 
 
135
 
 
136
class IPv6_UDPServer(SocketServer.UDPServer, object):
 
137
    __metaclass__ = server_metaclass
 
138
    def verify_request(self, request, client_address):
 
139
        print "UDP request came"
 
140
        return request[0] == "Marco"
201
141
 
202
142
 
203
143
class tcp_handler(SocketServer.BaseRequestHandler, object):
204
 
    """A TCP request handler class.
205
 
    Instantiated by IPv6_TCPServer for each request to handle it.
206
 
    Note: This will run in its own forked process."""
207
144
    def handle(self):
208
 
        #print u"TCP request came"
209
 
        #print u"Request:", self.request
210
 
        #print u"Client Address:", self.client_address
211
 
        #print u"Server:", self.server
 
145
        print "TCP request came"
 
146
        print "Request:", self.request
 
147
        print "Client Address:", self.client_address
 
148
        print "Server:", self.server
212
149
        session = gnutls.connection.ServerSession(self.request,
213
 
                                                  self.server\
214
 
                                                  .credentials)
215
 
        try:
216
 
            session.handshake()
217
 
        except gnutls.errors.GNUTLSError, error:
218
 
            #sys.stderr.write(u"Handshake failed: %s\n" % error)
219
 
            # Do not run session.bye() here: the session is not
220
 
            # established.  Just abandon the request.
221
 
            return
222
 
        #if session.peer_certificate:
223
 
        #    print "DN:", session.peer_certificate.subject
 
150
                                                  self.server.credentials)
 
151
        session.handshake()
 
152
        if session.peer_certificate:
 
153
            print "DN:", session.peer_certificate.subject
224
154
        try:
225
155
            session.verify_peer()
226
156
        except gnutls.errors.CertificateError, error:
227
 
            #sys.stderr.write(u"Verify failed: %s\n" % error)
 
157
            print "Verify failed", error
228
158
            session.bye()
229
159
            return
230
 
        client = None
231
 
        for c in clients:
232
 
            if c.dn == session.peer_certificate.subject:
233
 
                client = c
234
 
                break
235
 
        # Have to check if client.still_valid(), since it is possible
236
 
        # that the client timed out while establishing the GnuTLS
237
 
        # session.
238
 
        if client and client.still_valid():
239
 
            session.send(client.password)
240
 
        else:
241
 
            #if client:
242
 
            #    sys.stderr.write(u"Client %(name)s is invalid\n"
243
 
            #                     % vars(client))
244
 
            #else:
245
 
            #    sys.stderr.write(u"Client not found for DN: %s\n"
246
 
            #                     % session.peer_certificate.subject)
247
 
            #session.send("gazonk")
248
 
            pass
 
160
        try:
 
161
            session.send(dict((client.dn, client.password)
 
162
                              for client in self.server.clients)
 
163
                         [session.peer_certificate.subject])
 
164
        except KeyError:
 
165
            session.send("gazonk")
 
166
            # Log maybe? XXX
249
167
        session.bye()
250
168
 
251
169
 
252
170
class IPv6_TCPServer(SocketServer.ForkingTCPServer, object):
253
 
    """IPv6 TCP server.  Accepts 'None' as address and/or port.
254
 
    Attributes:
255
 
        options:        Command line options
256
 
        clients:        Set() of Client objects
257
 
        credentials:    GnuTLS X.509 credentials
258
 
    """
259
 
    address_family = socket.AF_INET6
260
 
    def __init__(self, *args, **kwargs):
261
 
        if "options" in kwargs:
262
 
            self.options = kwargs["options"]
263
 
            del kwargs["options"]
264
 
        if "clients" in kwargs:
265
 
            self.clients = kwargs["clients"]
266
 
            del kwargs["clients"]
267
 
        if "credentials" in kwargs:
268
 
            self.credentials = kwargs["credentials"]
269
 
            del kwargs["credentials"]
270
 
        return super(type(self), self).__init__(*args, **kwargs)
271
 
    def server_bind(self):
272
 
        """This overrides the normal server_bind() function
273
 
        to bind to an interface if one was specified, and also NOT to
274
 
        bind to an address or port if they were not specified."""
275
 
        if self.options.interface:
276
 
            if not hasattr(socket, "SO_BINDTODEVICE"):
277
 
                # From /usr/include/asm-i486/socket.h
278
 
                socket.SO_BINDTODEVICE = 25
279
 
            try:
280
 
                self.socket.setsockopt(socket.SOL_SOCKET,
281
 
                                       socket.SO_BINDTODEVICE,
282
 
                                       self.options.interface)
283
 
            except socket.error, error:
284
 
                if error[0] == errno.EPERM:
285
 
                    sys.stderr.write(u"Warning: No permission to bind to interface %s\n"
286
 
                                     % self.options.interface)
287
 
                else:
288
 
                    raise error
289
 
        # Only bind(2) the socket if we really need to.
290
 
        if self.server_address[0] or self.server_address[1]:
291
 
            if not self.server_address[0]:
292
 
                in6addr_any = "::"
293
 
                self.server_address = (in6addr_any,
294
 
                                       self.server_address[1])
295
 
            elif self.server_address[1] is None:
296
 
                self.server_address = (self.server_address[0],
297
 
                                       0)
298
 
            return super(type(self), self).server_bind()
299
 
 
 
171
    __metaclass__ = server_metaclass
 
172
    request_queue_size = 1024
 
173
 
 
174
 
 
175
in6addr_any = "::"
300
176
 
301
177
def string_to_delta(interval):
302
178
    """Parse a string and return a datetime.timedelta
332
208
    return delta
333
209
 
334
210
 
335
 
def add_service():
336
 
    """From the Avahi server example code"""
337
 
    global group, serviceName, serviceType, servicePort, serviceTXT, \
338
 
           domain, host
339
 
    if group is None:
340
 
        group = dbus.Interface(
341
 
                bus.get_object( avahi.DBUS_NAME,
342
 
                                server.EntryGroupNew()),
343
 
                avahi.DBUS_INTERFACE_ENTRY_GROUP)
344
 
        group.connect_to_signal('StateChanged',
345
 
                                entry_group_state_changed)
346
 
    
347
 
    # print "Adding service '%s' of type '%s' ..." % (serviceName,
348
 
    #                                                 serviceType)
349
 
    
350
 
    group.AddService(
351
 
            serviceInterface,           # interface
352
 
            avahi.PROTO_INET6,          # protocol
353
 
            dbus.UInt32(0),             # flags
354
 
            serviceName, serviceType,
355
 
            domain, host,
356
 
            dbus.UInt16(servicePort),
357
 
            avahi.string_array_to_txt_array(serviceTXT))
358
 
    group.Commit()
359
 
 
360
 
 
361
 
def remove_service():
362
 
    """From the Avahi server example code"""
363
 
    global group
364
 
    
365
 
    if not group is None:
366
 
        group.Reset()
367
 
 
368
 
 
369
 
def server_state_changed(state):
370
 
    """From the Avahi server example code"""
371
 
    if state == avahi.SERVER_COLLISION:
372
 
        print "WARNING: Server name collision"
373
 
        remove_service()
374
 
    elif state == avahi.SERVER_RUNNING:
375
 
        add_service()
376
 
 
377
 
 
378
 
def entry_group_state_changed(state, error):
379
 
    """From the Avahi server example code"""
380
 
    global serviceName, server, rename_count
381
 
    
382
 
    # print "state change: %i" % state
383
 
    
384
 
    if state == avahi.ENTRY_GROUP_ESTABLISHED:
385
 
        pass
386
 
        # print "Service established."
387
 
    elif state == avahi.ENTRY_GROUP_COLLISION:
388
 
        
389
 
        rename_count = rename_count - 1
390
 
        if rename_count > 0:
391
 
            name = server.GetAlternativeServiceName(name)
392
 
            print "WARNING: Service name collision, changing name to '%s' ..." % name
393
 
            remove_service()
394
 
            add_service()
395
 
            
396
 
        else:
397
 
            print "ERROR: No suitable service name found after %i retries, exiting." % n_rename
398
 
            main_loop.quit()
399
 
    elif state == avahi.ENTRY_GROUP_FAILURE:
400
 
        print "Error in group state changed", error
401
 
        main_loop.quit()
402
 
        return
403
 
 
404
 
 
405
 
def if_nametoindex(interface):
406
 
    """Call the C function if_nametoindex()"""
407
 
    try:
408
 
        if "ctypes" not in sys.modules:
409
 
            import ctypes
410
 
        libc = ctypes.cdll.LoadLibrary("libc.so.6")
411
 
        return libc.if_nametoindex(interface)
412
 
    except (ImportError, OSError, AttributeError):
413
 
        if "struct" not in sys.modules:
414
 
            import struct
415
 
        if "fcntl" not in sys.modules:
416
 
            import fcntl
417
 
        SIOCGIFINDEX = 0x8933      # From /usr/include/linux/sockios.h
418
 
        s = socket.socket()
419
 
        ifreq = fcntl.ioctl(s, SIOCGIFINDEX,
420
 
                            struct.pack("16s16x", interface))
421
 
        s.close()
422
 
        interface_index = struct.unpack("I", ifreq[16:20])[0]
423
 
        return interface_index
424
 
 
425
 
 
426
 
if __name__ == '__main__':
 
211
def main():
427
212
    parser = OptionParser()
428
213
    parser.add_option("-i", "--interface", type="string",
429
 
                      default=None, metavar="IF",
430
 
                      help="Bind to interface IF")
 
214
                      default="eth0", metavar="IF",
 
215
                      help="Interface to bind to")
431
216
    parser.add_option("--cert", type="string", default="cert.pem",
432
217
                      metavar="FILE",
433
 
                      help="Public key certificate PEM file to use")
 
218
                      help="Public key certificate to use")
434
219
    parser.add_option("--key", type="string", default="key.pem",
435
220
                      metavar="FILE",
436
 
                      help="Private key PEM file to use")
 
221
                      help="Private key to use")
437
222
    parser.add_option("--ca", type="string", default="ca.pem",
438
223
                      metavar="FILE",
439
 
                      help="Certificate Authority certificate PEM file to use")
 
224
                      help="Certificate Authority certificate to use")
440
225
    parser.add_option("--crl", type="string", default="crl.pem",
441
226
                      metavar="FILE",
442
 
                      help="Certificate Revokation List PEM file to use")
443
 
    parser.add_option("-p", "--port", type="int", default=None,
 
227
                      help="Certificate Revokation List to use")
 
228
    parser.add_option("-p", "--port", type="int", default=49001,
444
229
                      help="Port number to receive requests on")
445
 
    parser.add_option("--timeout", type="string", # Parsed later
446
 
                      default="1h",
 
230
    parser.add_option("--dh", type="int", metavar="BITS",
 
231
                      help="DH group to use")
 
232
    parser.add_option("-t", "--timeout", type="string", # Parsed later
 
233
                      default="15m",
447
234
                      help="Amount of downtime allowed for clients")
448
235
    parser.add_option("--interval", type="string", # Parsed later
449
236
                      default="5m",
451
238
    parser.add_option("--check", action="store_true", default=False,
452
239
                      help="Run self-test")
453
240
    (options, args) = parser.parse_args()
454
 
    
 
241
 
455
242
    if options.check:
456
243
        import doctest
457
244
        doctest.testmod()
462
249
        options.timeout = string_to_delta(options.timeout)
463
250
    except ValueError:
464
251
        parser.error("option --timeout: Unparseable time")
 
252
    
465
253
    try:
466
254
        options.interval = string_to_delta(options.interval)
467
255
    except ValueError:
475
263
    
476
264
    # Parse config file
477
265
    defaults = {}
478
 
    client_config = ConfigParser.SafeConfigParser(defaults)
479
 
    #client_config.readfp(open("secrets.conf"), "secrets.conf")
480
 
    client_config.read("mandos-clients.conf")
481
 
    
482
 
    # From the Avahi server example code
483
 
    DBusGMainLoop(set_as_default=True )
484
 
    main_loop = gobject.MainLoop()
485
 
    bus = dbus.SystemBus()
486
 
    server = dbus.Interface(
487
 
            bus.get_object( avahi.DBUS_NAME, avahi.DBUS_PATH_SERVER ),
488
 
            avahi.DBUS_INTERFACE_SERVER )
489
 
    # End of Avahi example code
490
 
    
491
 
    clients = Set()
492
 
    def remove_from_clients(client):
493
 
        clients.remove(client)
494
 
        if not clients:
495
 
            print "No clients left, exiting"
496
 
            main_loop.quit()
497
 
    
498
 
    clients.update(Set(Client(name=section, options=options,
499
 
                              stop_hook = remove_from_clients,
500
 
                              **(dict(client_config\
501
 
                                      .items(section))))
502
 
                       for section in client_config.sections()))
503
 
    for client in clients:
504
 
        client.start()
505
 
    
506
 
    tcp_server = IPv6_TCPServer((None, options.port),
 
266
    client_config_object = ConfigParser.SafeConfigParser(defaults)
 
267
    client_config_object.read("mandos-clients.conf")
 
268
    clients = [Client(name=section, options=options,
 
269
                      **(dict(client_config_object.items(section))))
 
270
               for section in client_config_object.sections()]
 
271
    
 
272
    udp_server = IPv6_UDPServer((in6addr_any, options.port),
 
273
                                udp_handler,
 
274
                                options=options)
 
275
    
 
276
    tcp_server = IPv6_TCPServer((in6addr_any, options.port),
507
277
                                tcp_handler,
508
278
                                options=options,
509
279
                                clients=clients,
510
280
                                credentials=cred)
511
 
    # Find out what random port we got
512
 
    servicePort = tcp_server.socket.getsockname()[1]
513
 
    #sys.stderr.write("Now listening on port %d\n" % servicePort)
514
 
    
515
 
    if options.interface is not None:
516
 
        serviceInterface = if_nametoindex(options.interface)
517
 
    
518
 
    # From the Avahi server example code
519
 
    server.connect_to_signal("StateChanged", server_state_changed)
520
 
    server_state_changed(server.GetState())
521
 
    # End of Avahi example code
522
 
    
523
 
    gobject.io_add_watch(tcp_server.fileno(), gobject.IO_IN,
524
 
                         lambda *args, **kwargs:
525
 
                         tcp_server.handle_request(*args[2:],
526
 
                                                   **kwargs) or True)
527
 
    try:
528
 
        main_loop.run()
529
 
    except KeyboardInterrupt:
530
 
        print
 
281
    
 
282
    while True:
 
283
        try:
 
284
            input, out, err = select.select((udp_server,
 
285
                                             tcp_server), (), ())
 
286
            if not input:
 
287
                pass
 
288
            else:
 
289
                for obj in input:
 
290
                    obj.handle_request()
 
291
        except KeyboardInterrupt:
 
292
            break
531
293
    
532
294
    # Cleanup here
533
 
 
534
 
    # From the Avahi server example code
535
 
    if not group is None:
536
 
        group.Free()
537
 
    # End of Avahi example code
538
 
    
539
295
    for client in clients:
540
 
        client.stop_hook = None
541
 
        client.stop()
 
296
        client.stop_checker()
 
297
 
 
298
 
 
299
if __name__ == "__main__":
 
300
    main()
 
301