/mandos/trunk

To get this branch, use:
bzr branch http://bzr.recompile.se/loggerhead/mandos/trunk
3 by Björn Påhlsson
Python based server
1
#!/usr/bin/python
2
10 by Teddy Hogeborn
* server.py: Bug fix: Do "from __future__ import division".
3
from __future__ import division
4
3 by Björn Påhlsson
Python based server
5
import SocketServer
6
import socket
7
import select
8
from optparse import OptionParser
9
import datetime
10
import errno
11
import gnutls.crypto
12
import gnutls.connection
13
import gnutls.errors
14
import ConfigParser
4 by Teddy Hogeborn
* server.py (Client.created, Client.next_check): New.
15
import sys
9 by Teddy Hogeborn
* client.cpp (main): Get t_old early since it is used on error exits.
16
import re
17
import os
18
import signal
10 by Teddy Hogeborn
* server.py: Bug fix: Do "from __future__ import division".
19
from sets import Set
20
import subprocess
5 by Teddy Hogeborn
* server.py (server_metaclass): New.
21
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
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
3 by Björn Påhlsson
Python based server
44
class Client(object):
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
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,
5 by Teddy Hogeborn
* server.py (server_metaclass): New.
66
                 timeout=None, interval=-1):
3 by Björn Påhlsson
Python based server
67
        self.name = name
68
        self.dn = dn
69
        if password:
70
            self.password = password
71
        elif passfile:
72
            self.password = open(passfile).readall()
73
        else:
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
74
            raise RuntimeError(u"No Password or Passfile for client %s"
75
                               % self.name)
9 by Teddy Hogeborn
* client.cpp (main): Get t_old early since it is used on error exits.
76
        self.fqdn = fqdn                # string
4 by Teddy Hogeborn
* server.py (Client.created, Client.next_check): New.
77
        self.created = datetime.datetime.now()
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
78
        self.last_seen = None
3 by Björn Påhlsson
Python based server
79
        if timeout is None:
5 by Teddy Hogeborn
* server.py (server_metaclass): New.
80
            timeout = options.timeout
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
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))
3 by Björn Påhlsson
Python based server
87
        if interval == -1:
5 by Teddy Hogeborn
* server.py (server_metaclass): New.
88
            interval = options.interval
10 by Teddy Hogeborn
* server.py: Bug fix: Do "from __future__ import division".
89
        else:
90
            interval = string_to_delta(interval)
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
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."""
10 by Teddy Hogeborn
* server.py: Bug fix: Do "from __future__ import division".
145
        now = datetime.datetime.now()
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
146
        if os.WIFEXITED(condition) \
147
               and (os.WEXITSTATUS(condition) == 0):
148
            #print "Checker for %(name)s succeeded" % vars(self)
9 by Teddy Hogeborn
* client.cpp (main): Get t_old early since it is used on error exits.
149
            self.last_seen = now
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
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)
10 by Teddy Hogeborn
* server.py: Bug fix: Do "from __future__ import division".
159
        self.checker = None
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
160
        self.checker_callback_tag = None
9 by Teddy Hogeborn
* client.cpp (main): Get t_old early since it is used on error exits.
161
    def start_checker(self):
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
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
9 by Teddy Hogeborn
* client.cpp (main): Get t_old early since it is used on error exits.
183
    def stop_checker(self):
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
184
        """Force the checker process, if any, to stop."""
185
        if not hasattr(self, "checker") or self.checker is None:
9 by Teddy Hogeborn
* client.cpp (main): Get t_old early since it is used on error exits.
186
            return
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
187
        gobject.source_remove(self.checker_callback_tag)
188
        self.checker_callback_tag = None
9 by Teddy Hogeborn
* client.cpp (main): Get t_old early since it is used on error exits.
189
        os.kill(self.checker.pid, signal.SIGTERM)
190
        if self.checker.poll() is None:
191
            os.kill(self.checker.pid, signal.SIGKILL)
192
        self.checker = None
193
    def still_valid(self, now=None):
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
194
        """Has the timeout not yet passed for this client?"""
9 by Teddy Hogeborn
* client.cpp (main): Get t_old early since it is used on error exits.
195
        if now is None:
196
            now = datetime.datetime.now()
10 by Teddy Hogeborn
* server.py: Bug fix: Do "from __future__ import division".
197
        if self.last_seen is None:
198
            return now < (self.created + self.timeout)
199
        else:
200
            return now < (self.last_seen + self.timeout)
3 by Björn Påhlsson
Python based server
201
202
203
class tcp_handler(SocketServer.BaseRequestHandler, object):
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
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."""
3 by Björn Påhlsson
Python based server
207
    def handle(self):
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
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
3 by Björn Påhlsson
Python based server
212
        session = gnutls.connection.ServerSession(self.request,
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
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
3 by Björn Påhlsson
Python based server
224
        try:
225
            session.verify_peer()
226
        except gnutls.errors.CertificateError, error:
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
227
            #sys.stderr.write(u"Verify failed: %s\n" % error)
3 by Björn Påhlsson
Python based server
228
            session.bye()
229
            return
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
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
3 by Björn Påhlsson
Python based server
249
        session.bye()
250
5 by Teddy Hogeborn
* server.py (server_metaclass): New.
251
3 by Björn Påhlsson
Python based server
252
class IPv6_TCPServer(SocketServer.ForkingTCPServer, object):
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
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()
10 by Teddy Hogeborn
* server.py: Bug fix: Do "from __future__ import division".
299
3 by Björn Påhlsson
Python based server
300
4 by Teddy Hogeborn
* server.py (Client.created, Client.next_check): New.
301
def string_to_delta(interval):
302
    """Parse a string and return a datetime.timedelta
303
304
    >>> string_to_delta('7d')
305
    datetime.timedelta(7)
306
    >>> string_to_delta('60s')
307
    datetime.timedelta(0, 60)
308
    >>> string_to_delta('60m')
309
    datetime.timedelta(0, 3600)
310
    >>> string_to_delta('24h')
311
    datetime.timedelta(1)
312
    >>> string_to_delta(u'1w')
313
    datetime.timedelta(7)
314
    """
315
    try:
316
        suffix=unicode(interval[-1])
317
        value=int(interval[:-1])
318
        if suffix == u"d":
319
            delta = datetime.timedelta(value)
320
        elif suffix == u"s":
321
            delta = datetime.timedelta(0, value)
322
        elif suffix == u"m":
323
            delta = datetime.timedelta(0, 0, 0, 0, value)
324
        elif suffix == u"h":
325
            delta = datetime.timedelta(0, 0, 0, 0, 0, value)
326
        elif suffix == u"w":
327
            delta = datetime.timedelta(0, 0, 0, 0, 0, 0, value)
328
        else:
329
            raise ValueError
330
    except (ValueError, IndexError):
331
        raise ValueError
332
    return delta
333
8 by Teddy Hogeborn
* Makefile (client_debug): Bug fix; add quotes and / to CERT_ROOT.
334
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
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__':
3 by Björn Påhlsson
Python based server
427
    parser = OptionParser()
428
    parser.add_option("-i", "--interface", type="string",
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
429
                      default=None, metavar="IF",
430
                      help="Bind to interface IF")
3 by Björn Påhlsson
Python based server
431
    parser.add_option("--cert", type="string", default="cert.pem",
432
                      metavar="FILE",
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
433
                      help="Public key certificate PEM file to use")
3 by Björn Påhlsson
Python based server
434
    parser.add_option("--key", type="string", default="key.pem",
435
                      metavar="FILE",
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
436
                      help="Private key PEM file to use")
3 by Björn Påhlsson
Python based server
437
    parser.add_option("--ca", type="string", default="ca.pem",
438
                      metavar="FILE",
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
439
                      help="Certificate Authority certificate PEM file to use")
3 by Björn Påhlsson
Python based server
440
    parser.add_option("--crl", type="string", default="crl.pem",
441
                      metavar="FILE",
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
442
                      help="Certificate Revokation List PEM file to use")
443
    parser.add_option("-p", "--port", type="int", default=None,
3 by Björn Påhlsson
Python based server
444
                      help="Port number to receive requests on")
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
445
    parser.add_option("--timeout", type="string", # Parsed later
446
                      default="1h",
3 by Björn Påhlsson
Python based server
447
                      help="Amount of downtime allowed for clients")
4 by Teddy Hogeborn
* server.py (Client.created, Client.next_check): New.
448
    parser.add_option("--interval", type="string", # Parsed later
449
                      default="5m",
450
                      help="How often to check that a client is up")
451
    parser.add_option("--check", action="store_true", default=False,
452
                      help="Run self-test")
3 by Björn Påhlsson
Python based server
453
    (options, args) = parser.parse_args()
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
454
    
4 by Teddy Hogeborn
* server.py (Client.created, Client.next_check): New.
455
    if options.check:
456
        import doctest
457
        doctest.testmod()
458
        sys.exit()
3 by Björn Påhlsson
Python based server
459
    
4 by Teddy Hogeborn
* server.py (Client.created, Client.next_check): New.
460
    # Parse the time arguments
3 by Björn Påhlsson
Python based server
461
    try:
4 by Teddy Hogeborn
* server.py (Client.created, Client.next_check): New.
462
        options.timeout = string_to_delta(options.timeout)
463
    except ValueError:
3 by Björn Påhlsson
Python based server
464
        parser.error("option --timeout: Unparseable time")
4 by Teddy Hogeborn
* server.py (Client.created, Client.next_check): New.
465
    try:
466
        options.interval = string_to_delta(options.interval)
467
    except ValueError:
468
        parser.error("option --interval: Unparseable time")
469
    
3 by Björn Påhlsson
Python based server
470
    cert = gnutls.crypto.X509Certificate(open(options.cert).read())
471
    key = gnutls.crypto.X509PrivateKey(open(options.key).read())
472
    ca = gnutls.crypto.X509Certificate(open(options.ca).read())
473
    crl = gnutls.crypto.X509CRL(open(options.crl).read())
474
    cred = gnutls.connection.X509Credentials(cert, key, [ca], [crl])
475
    
476
    # Parse config file
477
    defaults = {}
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
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),
3 by Björn Påhlsson
Python based server
507
                                tcp_handler,
508
                                options=options,
509
                                clients=clients,
510
                                credentials=cred)
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
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
9 by Teddy Hogeborn
* client.cpp (main): Get t_old early since it is used on error exits.
531
    
532
    # Cleanup here
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
533
534
    # From the Avahi server example code
535
    if not group is None:
536
        group.Free()
537
    # End of Avahi example code
538
    
9 by Teddy Hogeborn
* client.cpp (main): Get t_old early since it is used on error exits.
539
    for client in clients:
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
540
        client.stop_hook = None
541
        client.stop()