/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 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
17
 
22
18
class Client(object):
23
19
    def __init__(self, name=None, options=None, dn=None,
41
37
        self.timeout = timeout          # datetime.timedelta()
42
38
        if interval == -1:
43
39
            interval = options.interval
44
 
        else:
45
 
            interval = string_to_delta(interval)
46
40
        self.interval = interval        # datetime.timedelta()
47
41
        self.next_check = datetime.datetime.now() # datetime.datetime()
48
 
        # Note: next_check may be in the past if checker is not None
49
42
        self.checker = None             # or a subprocess.Popen()
50
 
    def check_action(self):
 
43
    def check_action(self, now=None):
51
44
        """The checker said something and might have completed.
52
45
        Check if is has, and take appropriate actions."""
53
46
        if self.checker.poll() is None:
54
47
            # False alarm, no result yet
55
48
            #self.checker.read()
56
 
            #print "Checker for %(name)s said nothing?" % vars(self)
57
49
            return
58
 
        now = datetime.datetime.now()
 
50
        if now is None:
 
51
            now = datetime.datetime.now()
59
52
        if self.checker.returncode == 0:
60
 
            print "Checker for %(name)s succeeded" % vars(self)
61
53
            self.last_seen = now
62
 
        else:
63
 
            print "Checker for %(name)s failed" % vars(self)
64
54
        while self.next_check <= now:
65
55
            self.next_check += self.interval
66
 
        self.checker = None
67
56
    handle_request = check_action
68
57
    def start_checker(self):
69
58
        self.stop_checker()
70
59
        try:
71
 
            self.checker = subprocess.Popen("sleep 10; fping -q -- %s"
 
60
            self.checker = subprocess.Popen("sleep 1; fping -q -- %s"
72
61
                                            % re.escape(self.fqdn),
73
62
                                            stdout=subprocess.PIPE,
74
63
                                            close_fds=True,
88
77
            return None
89
78
        return self.checker.stdout.fileno()
90
79
    def next_stop(self):
91
 
        """The time when something must be done about this client
92
 
        May be in the past."""
93
 
        if self.last_seen is None:
94
 
            # This client has never been seen
95
 
            next_timeout = self.created + self.timeout
96
 
        else:
97
 
            next_timeout = self.last_seen + self.timeout
98
 
        if self.checker is None:
99
 
            return min(next_timeout, self.next_check)
100
 
        else:
101
 
            return next_timeout
 
80
        """The time when something must be done about this client"""
 
81
        return min(self.last_seen + self.timeout, self.next_check)
102
82
    def still_valid(self, now=None):
103
83
        """Has this client's timeout not passed?"""
104
84
        if now is None:
105
85
            now = datetime.datetime.now()
106
 
        if self.last_seen is None:
107
 
            return now < (self.created + self.timeout)
108
 
        else:
109
 
            return now < (self.last_seen + self.timeout)
 
86
        return now < (self.last_seen + timeout)
110
87
    def it_is_time_to_check(self, now=None):
111
88
        if now is None:
112
89
            now = datetime.datetime.now()
181
158
            session.bye()
182
159
            return
183
160
        try:
184
 
            session.send([client.password
185
 
                          for client in self.server.clients
186
 
                          if (client.dn ==
187
 
                              session.peer_certificate.subject)][0])
188
 
        except IndexError:
 
161
            session.send(dict((client.dn, client.password)
 
162
                              for client in self.server.clients)
 
163
                         [session.peer_certificate.subject])
 
164
        except KeyError:
189
165
            session.send("gazonk")
190
166
            # Log maybe? XXX
191
167
        session.bye()
193
169
 
194
170
class IPv6_TCPServer(SocketServer.ForkingTCPServer, object):
195
171
    __metaclass__ = server_metaclass
196
 
 
 
172
    request_queue_size = 1024
 
173
 
 
174
 
 
175
in6addr_any = "::"
197
176
 
198
177
def string_to_delta(interval):
199
178
    """Parse a string and return a datetime.timedelta
286
265
    defaults = {}
287
266
    client_config_object = ConfigParser.SafeConfigParser(defaults)
288
267
    client_config_object.read("mandos-clients.conf")
289
 
    clients = Set(Client(name=section, options=options,
290
 
                         **(dict(client_config_object\
291
 
                                 .items(section))))
292
 
                  for section in client_config_object.sections())
 
268
    clients = [Client(name=section, options=options,
 
269
                      **(dict(client_config_object.items(section))))
 
270
               for section in client_config_object.sections()]
293
271
    
294
 
    in6addr_any = "::"
295
272
    udp_server = IPv6_UDPServer((in6addr_any, options.port),
296
273
                                udp_handler,
297
274
                                options=options)
303
280
                                credentials=cred)
304
281
    
305
282
    while True:
306
 
        if not clients:
307
 
            break
308
283
        try:
309
 
            next_stop = min(client.next_stop() for client in clients)
310
 
            now = datetime.datetime.now()
311
 
            if next_stop > now:
312
 
                delay = next_stop - now
313
 
                delay_seconds = (delay.days * 24 * 60 * 60
314
 
                                 + delay.seconds
315
 
                                 + delay.microseconds / 1000000)
316
 
                clients_with_checkers = tuple(client for client in
317
 
                                              clients
318
 
                                              if client.checker
319
 
                                              is not None)
320
 
                input_checks = (udp_server, tcp_server) \
321
 
                               + clients_with_checkers
322
 
                print "Waiting for network",
323
 
                if clients_with_checkers:
324
 
                    print "and checkers for:",
325
 
                    for client in clients_with_checkers:
326
 
                        print client.name,
327
 
                print
328
 
                input, out, err = select.select(input_checks, (), (),
329
 
                                                delay_seconds)
 
284
            input, out, err = select.select((udp_server,
 
285
                                             tcp_server), (), ())
 
286
            if not input:
 
287
                pass
 
288
            else:
330
289
                for obj in input:
331
290
                    obj.handle_request()
332
 
            # start new checkers
333
 
            for client in clients:
334
 
                if client.it_is_time_to_check(now=now) and \
335
 
                       client.checker is None:
336
 
                    print "Starting checker for client %(name)s" \
337
 
                          % vars(client)
338
 
                    client.start_checker()
339
 
            # delete timed-out clients
340
 
            for client in clients.copy():
341
 
                if not client.still_valid(now=now):
342
 
                    # log xxx
343
 
                    print "Removing client %(name)s" % vars(client)
344
 
                    clients.remove(client)
345
291
        except KeyboardInterrupt:
346
292
            break
347
293