23
72
self.password = open(passfile).readall()
25
print "No Password or Passfile in client config file"
26
# raise RuntimeError XXX
27
self.password = "gazonk"
74
raise RuntimeError(u"No Password or Passfile for client %s"
76
self.fqdn = fqdn # string
77
self.created = datetime.datetime.now()
30
78
self.last_seen = None
31
79
if timeout is None:
32
timeout = self.server.options.timeout
80
timeout = options.timeout
33
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
35
interval = self.server.options.interval
88
interval = options.interval
90
interval = string_to_delta(interval)
36
91
self.interval = interval
38
def server_bind(self):
39
if self.options.interface:
40
if not hasattr(socket, "SO_BINDTODEVICE"):
41
# From /usr/include/asm-i486/socket.h
42
socket.SO_BINDTODEVICE = 25
44
self.socket.setsockopt(socket.SOL_SOCKET,
45
socket.SO_BINDTODEVICE,
46
self.options.interface)
47
except socket.error, error:
48
if error[0] == errno.EPERM:
49
print "Warning: Denied permission to bind to interface", \
50
self.options.interface
53
return super(type(self), self).server_bind()
56
def init_with_options(self, *args, **kwargs):
57
if "options" in kwargs:
58
self.options = kwargs["options"]
60
if "clients" in kwargs:
61
self.clients = kwargs["clients"]
63
if "credentials" in kwargs:
64
self.credentials = kwargs["credentials"]
65
del kwargs["credentials"]
66
return super(type(self), self).__init__(*args, **kwargs)
69
class udp_handler(SocketServer.DatagramRequestHandler, object):
71
self.wfile.write("Polo")
72
print "UDP request answered"
75
class IPv6_UDPServer(SocketServer.UDPServer, object):
76
__init__ = init_with_options
77
address_family = socket.AF_INET6
78
allow_reuse_address = True
79
server_bind = server_bind
80
def verify_request(self, request, client_address):
81
print "UDP request came"
82
return request[0] == "Marco"
92
self.interval_milliseconds = ((self.interval.days
93
* 24 * 60 * 60 * 1000)
94
+ (self.interval.seconds * 1000)
95
+ (self.interval.microseconds
97
self.stop_hook = stop_hook
99
self.checker_initiator_tag = None
100
self.stop_initiator_tag = None
101
self.checker_callback_tag = None
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,
109
# Also start a new checker *right now*.
111
# Schedule a stop() when 'timeout' has passed
112
self.stop_initiator_tag = gobject.\
113
timeout_add(self.timeout_milliseconds,
117
The possibility that this client might be restarted is left
118
open, but not currently used."""
119
# print "Stopping client", self.name
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
130
# Do not run this again if called by a gobject.timeout_add
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
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)
150
gobject.source_remove(self.stop_initiator_tag)
151
self.stop_initiator_tag = gobject.\
152
timeout_add(self.timeout_milliseconds,
155
# if not os.WIFEXITED(condition):
156
# print "Checker for %(name)s crashed?" % vars(self)
158
# print "Checker for %(name)s failed" % vars(self)
160
self.checker_callback_tag = None
161
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
165
if self.checker is None:
166
#print "Starting checker for", self.name
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,
174
self.checker_callback_tag = gobject.\
175
child_watch_add(self.checker.pid,
178
except subprocess.OSError, error:
179
sys.stderr.write(u"Failed to start subprocess: %s\n"
181
# Re-run this periodically if run by gobject.timeout_add
183
def stop_checker(self):
184
"""Force the checker process, if any, to stop."""
185
if not hasattr(self, "checker") or self.checker is None:
187
gobject.source_remove(self.checker_callback_tag)
188
self.checker_callback_tag = None
189
os.kill(self.checker.pid, signal.SIGTERM)
190
if self.checker.poll() is None:
191
os.kill(self.checker.pid, signal.SIGKILL)
193
def still_valid(self, now=None):
194
"""Has the timeout not yet passed for this client?"""
196
now = datetime.datetime.now()
197
if self.last_seen is None:
198
return now < (self.created + self.timeout)
200
return now < (self.last_seen + self.timeout)
85
203
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."""
87
print "TCP request came"
88
print "Request:", self.request
89
print "Client Address:", self.client_address
90
print "Server:", self.server
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
91
212
session = gnutls.connection.ServerSession(self.request,
92
self.server.credentials)
94
if session.peer_certificate:
95
print "DN:", session.peer_certificate.subject
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.
222
#if session.peer_certificate:
223
# print "DN:", session.peer_certificate.subject
97
225
session.verify_peer()
98
226
except gnutls.errors.CertificateError, error:
99
print "Verify failed", error
227
#sys.stderr.write(u"Verify failed: %s\n" % error)
103
session.send(dict((client.dn, client.password)
104
for client in self.server.clients)
105
[session.peer_certificate.subject])
107
session.send("gazonk")
232
if c.dn == session.peer_certificate.subject:
235
# Have to check if client.still_valid(), since it is possible
236
# that the client timed out while establishing the GnuTLS
238
if client and client.still_valid():
239
session.send(client.password)
242
# sys.stderr.write(u"Client %(name)s is invalid\n"
245
# sys.stderr.write(u"Client not found for DN: %s\n"
246
# % session.peer_certificate.subject)
247
#session.send("gazonk")
111
252
class IPv6_TCPServer(SocketServer.ForkingTCPServer, object):
112
__init__ = init_with_options
253
"""IPv6 TCP server. Accepts 'None' as address and/or port.
255
options: Command line options
256
clients: Set() of Client objects
257
credentials: GnuTLS X.509 credentials
113
259
address_family = socket.AF_INET6
114
allow_reuse_address = True
115
request_queue_size = 1024
116
server_bind = server_bind
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
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)
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]:
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],
298
return super(type(self), self).server_bind()
301
def string_to_delta(interval):
302
"""Parse a string and return a datetime.timedelta
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)
316
suffix=unicode(interval[-1])
317
value=int(interval[:-1])
319
delta = datetime.timedelta(value)
321
delta = datetime.timedelta(0, value)
323
delta = datetime.timedelta(0, 0, 0, 0, value)
325
delta = datetime.timedelta(0, 0, 0, 0, 0, value)
327
delta = datetime.timedelta(0, 0, 0, 0, 0, 0, value)
330
except (ValueError, IndexError):
336
"""From the Avahi server example code"""
337
global group, serviceName, serviceType, servicePort, serviceTXT, \
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)
347
# print "Adding service '%s' of type '%s' ..." % (serviceName,
351
serviceInterface, # interface
352
avahi.PROTO_INET6, # protocol
353
dbus.UInt32(0), # flags
354
serviceName, serviceType,
356
dbus.UInt16(servicePort),
357
avahi.string_array_to_txt_array(serviceTXT))
361
def remove_service():
362
"""From the Avahi server example code"""
365
if not group is None:
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"
374
elif state == avahi.SERVER_RUNNING:
378
def entry_group_state_changed(state, error):
379
"""From the Avahi server example code"""
380
global serviceName, server, rename_count
382
# print "state change: %i" % state
384
if state == avahi.ENTRY_GROUP_ESTABLISHED:
386
# print "Service established."
387
elif state == avahi.ENTRY_GROUP_COLLISION:
389
rename_count = rename_count - 1
391
name = server.GetAlternativeServiceName(name)
392
print "WARNING: Service name collision, changing name to '%s' ..." % name
397
print "ERROR: No suitable service name found after %i retries, exiting." % n_rename
399
elif state == avahi.ENTRY_GROUP_FAILURE:
400
print "Error in group state changed", error
405
def if_nametoindex(interface):
406
"""Call the C function if_nametoindex()"""
408
if "ctypes" not in sys.modules:
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:
415
if "fcntl" not in sys.modules:
417
SIOCGIFINDEX = 0x8933 # From /usr/include/linux/sockios.h
419
ifreq = fcntl.ioctl(s, SIOCGIFINDEX,
420
struct.pack("16s16x", interface))
422
interface_index = struct.unpack("I", ifreq[16:20])[0]
423
return interface_index
426
if __name__ == '__main__':
124
427
parser = OptionParser()
125
428
parser.add_option("-i", "--interface", type="string",
126
default="eth0", metavar="IF",
127
help="Interface to bind to")
429
default=None, metavar="IF",
430
help="Bind to interface IF")
128
431
parser.add_option("--cert", type="string", default="cert.pem",
130
help="Public key certificate to use")
433
help="Public key certificate PEM file to use")
131
434
parser.add_option("--key", type="string", default="key.pem",
133
help="Private key to use")
436
help="Private key PEM file to use")
134
437
parser.add_option("--ca", type="string", default="ca.pem",
136
help="Certificate Authority certificate to use")
439
help="Certificate Authority certificate PEM file to use")
137
440
parser.add_option("--crl", type="string", default="crl.pem",
139
help="Certificate Revokation List to use")
140
parser.add_option("-p", "--port", type="int", default=49001,
442
help="Certificate Revokation List PEM file to use")
443
parser.add_option("-p", "--port", type="int", default=None,
141
444
help="Port number to receive requests on")
142
parser.add_option("--dh", type="int", metavar="BITS",
143
help="DH group to use")
144
parser.add_option("-t", "--timeout", type="string", # Parsed later
445
parser.add_option("--timeout", type="string", # Parsed later
146
447
help="Amount of downtime allowed for clients")
448
parser.add_option("--interval", type="string", # Parsed later
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")
147
453
(options, args) = parser.parse_args()
149
# Parse the time argument
460
# Parse the time arguments
151
suffix=options.timeout[-1]
152
value=int(options.timeout[:-1])
154
options.timeout = datetime.timedelta(value)
156
options.timeout = datetime.timedelta(0, value)
158
options.timeout = datetime.timedelta(0, 0, 0, 0, value)
160
options.timeout = datetime.timedelta(0, 0, 0, 0, 0, value)
162
options.timeout = datetime.timedelta(0, 0, 0, 0, 0, 0,
166
except (ValueError, IndexError):
462
options.timeout = string_to_delta(options.timeout)
167
464
parser.error("option --timeout: Unparseable time")
466
options.interval = string_to_delta(options.interval)
468
parser.error("option --interval: Unparseable time")
169
470
cert = gnutls.crypto.X509Certificate(open(options.cert).read())
170
471
key = gnutls.crypto.X509PrivateKey(open(options.key).read())