Show contents of the CP payload in message_t stringification
[strongswan.git] / src / libfast / dispatcher.c
1 /*
2 * Copyright (C) 2007 Martin Willi
3 * Hochschule fuer Technik Rapperswil
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 * for more details.
14 */
15
16 #include "dispatcher.h"
17
18 #include "request.h"
19 #include "session.h"
20
21 #include <fcgiapp.h>
22 #include <signal.h>
23 #include <unistd.h>
24
25 #include <debug.h>
26 #include <threading/thread.h>
27 #include <threading/condvar.h>
28 #include <threading/mutex.h>
29 #include <utils/linked_list.h>
30 #include <utils/hashtable.h>
31
32 /** Intervall to check for expired sessions, in seconds */
33 #define CLEANUP_INTERVAL 30
34
35 typedef struct private_dispatcher_t private_dispatcher_t;
36
37 /**
38 * private data of the task manager
39 */
40 struct private_dispatcher_t {
41
42 /**
43 * public functions
44 */
45 dispatcher_t public;
46
47 /**
48 * fcgi socket fd
49 */
50 int fd;
51
52 /**
53 * thread list
54 */
55 thread_t **threads;
56
57 /**
58 * number of threads in "threads"
59 */
60 int thread_count;
61
62 /**
63 * session locking mutex
64 */
65 mutex_t *mutex;
66
67 /**
68 * Hahstable with active sessions
69 */
70 hashtable_t *sessions;
71
72 /**
73 * session timeout
74 */
75 time_t timeout;
76
77 /**
78 * timestamp of last session cleanup round
79 */
80 time_t last_cleanup;
81
82 /**
83 * running in debug mode?
84 */
85 bool debug;
86
87 /**
88 * List of controllers controller_constructor_t
89 */
90 linked_list_t *controllers;
91
92 /**
93 * List of filters filter_constructor_t
94 */
95 linked_list_t *filters;
96
97 /**
98 * constructor function to create session context (in controller_entry_t)
99 */
100 context_constructor_t context_constructor;
101
102 /**
103 * user param to context constructor
104 */
105 void *param;
106 };
107
108 typedef struct {
109 /** constructor function */
110 controller_constructor_t constructor;
111 /** parameter to constructor */
112 void *param;
113 } controller_entry_t;
114
115 typedef struct {
116 /** constructor function */
117 filter_constructor_t constructor;
118 /** parameter to constructor */
119 void *param;
120 } filter_entry_t;
121
122 typedef struct {
123 /** session instance */
124 session_t *session;
125 /** condvar to wait for session */
126 condvar_t *cond;
127 /** client host address, to prevent session hijacking */
128 char *host;
129 /** TRUE if session is in use */
130 bool in_use;
131 /** last use of the session */
132 time_t used;
133 /** has the session been closed by the handler? */
134 bool closed;
135 } session_entry_t;
136
137 /**
138 * create a session and instanciate controllers
139 */
140 static session_t* load_session(private_dispatcher_t *this)
141 {
142 enumerator_t *enumerator;
143 controller_entry_t *centry;
144 filter_entry_t *fentry;
145 session_t *session;
146 context_t *context = NULL;
147 controller_t *controller;
148 filter_t *filter;
149
150 if (this->context_constructor)
151 {
152 context = this->context_constructor(this->param);
153 }
154 session = session_create(context);
155
156 enumerator = this->controllers->create_enumerator(this->controllers);
157 while (enumerator->enumerate(enumerator, &centry))
158 {
159 controller = centry->constructor(context, centry->param);
160 session->add_controller(session, controller);
161 }
162 enumerator->destroy(enumerator);
163
164 enumerator = this->filters->create_enumerator(this->filters);
165 while (enumerator->enumerate(enumerator, &fentry))
166 {
167 filter = fentry->constructor(context, fentry->param);
168 session->add_filter(session, filter);
169 }
170 enumerator->destroy(enumerator);
171
172 return session;
173 }
174
175 /**
176 * create a new session entry
177 */
178 static session_entry_t *session_entry_create(private_dispatcher_t *this,
179 char *host)
180 {
181 session_entry_t *entry;
182
183 entry = malloc_thing(session_entry_t);
184 entry->in_use = FALSE;
185 entry->closed = FALSE;
186 entry->cond = condvar_create(CONDVAR_TYPE_DEFAULT);
187 entry->session = load_session(this);
188 entry->used = time_monotonic(NULL);
189 entry->host = strdup(host);
190
191 return entry;
192 }
193
194 /**
195 * destroy a session
196 */
197 static void session_entry_destroy(session_entry_t *entry)
198 {
199 entry->session->destroy(entry->session);
200 entry->cond->destroy(entry->cond);
201 free(entry->host);
202 free(entry);
203 }
204
205 /**
206 * Implementation of dispatcher_t.add_controller.
207 */
208 static void add_controller(private_dispatcher_t *this,
209 controller_constructor_t constructor, void *param)
210 {
211 controller_entry_t *entry = malloc_thing(controller_entry_t);
212
213 entry->constructor = constructor;
214 entry->param = param;
215 this->controllers->insert_last(this->controllers, entry);
216 }
217
218 /**
219 * Implementation of dispatcher_t.add_filter.
220 */
221 static void add_filter(private_dispatcher_t *this,
222 filter_constructor_t constructor, void *param)
223 {
224 filter_entry_t *entry = malloc_thing(filter_entry_t);
225
226 entry->constructor = constructor;
227 entry->param = param;
228 this->filters->insert_last(this->filters, entry);
229 }
230
231 /**
232 * Hashtable hash function
233 */
234 static u_int session_hash(char *sid)
235 {
236 return chunk_hash(chunk_create(sid, strlen(sid)));
237 }
238
239 /**
240 * Hashtable equals function
241 */
242 static bool session_equals(char *sid1, char *sid2)
243 {
244 return streq(sid1, sid2);
245 }
246
247 /**
248 * Cleanup unused sessions
249 */
250 static void cleanup_sessions(private_dispatcher_t *this, time_t now)
251 {
252 if (this->last_cleanup < now - CLEANUP_INTERVAL)
253 {
254 char *sid;
255 session_entry_t *entry;
256 enumerator_t *enumerator;
257 linked_list_t *remove;
258
259 this->last_cleanup = now;
260 remove = linked_list_create();
261 enumerator = this->sessions->create_enumerator(this->sessions);
262 while (enumerator->enumerate(enumerator, &sid, &entry))
263 {
264 /* check all sessions for timeout or close flag */
265 if (!entry->in_use &&
266 (entry->used < now - this->timeout || entry->closed))
267 {
268 remove->insert_last(remove, sid);
269 }
270 }
271 enumerator->destroy(enumerator);
272
273 while (remove->remove_last(remove, (void**)&sid) == SUCCESS)
274 {
275 entry = this->sessions->remove(this->sessions, sid);
276 if (entry)
277 {
278 session_entry_destroy(entry);
279 }
280 }
281 remove->destroy(remove);
282 }
283 }
284
285 /**
286 * Actual dispatching code
287 */
288 static void dispatch(private_dispatcher_t *this)
289 {
290 thread_cancelability(FALSE);
291
292 while (TRUE)
293 {
294 request_t *request;
295 session_entry_t *found = NULL;
296 time_t now;
297 char *sid;
298
299 thread_cancelability(TRUE);
300 request = request_create(this->fd, this->debug);
301 thread_cancelability(FALSE);
302
303 if (request == NULL)
304 {
305 continue;
306 }
307 now = time_monotonic(NULL);
308 sid = request->get_cookie(request, "SID");
309
310 this->mutex->lock(this->mutex);
311 if (sid)
312 {
313 found = this->sessions->get(this->sessions, sid);
314 }
315 if (found && !streq(found->host, request->get_host(request)))
316 {
317 found = NULL;
318 }
319 if (found)
320 {
321 /* wait until session is unused */
322 while (found->in_use)
323 {
324 found->cond->wait(found->cond, this->mutex);
325 }
326 }
327 else
328 { /* create a new session if not found */
329 found = session_entry_create(this, request->get_host(request));
330 sid = found->session->get_sid(found->session);
331 this->sessions->put(this->sessions, sid, found);
332 }
333 found->in_use = TRUE;
334 this->mutex->unlock(this->mutex);
335
336 /* start processing */
337 found->session->process(found->session, request);
338 found->used = time_monotonic(NULL);
339
340 /* release session */
341 this->mutex->lock(this->mutex);
342 found->in_use = FALSE;
343 found->closed = request->session_closed(request);
344 found->cond->signal(found->cond);
345 cleanup_sessions(this, now);
346 this->mutex->unlock(this->mutex);
347
348 request->destroy(request);
349 }
350 }
351
352 /**
353 * Implementation of dispatcher_t.run.
354 */
355 static void run(private_dispatcher_t *this, int threads)
356 {
357 this->thread_count = threads;
358 this->threads = malloc(sizeof(thread_t*) * threads);
359 while (threads)
360 {
361 this->threads[threads - 1] = thread_create((thread_main_t)dispatch,
362 this);
363 if (this->threads[threads - 1])
364 {
365 threads--;
366 }
367 }
368 }
369
370 /**
371 * Implementation of dispatcher_t.waitsignal.
372 */
373 static void waitsignal(private_dispatcher_t *this)
374 {
375 sigset_t set;
376 int sig;
377
378 sigemptyset(&set);
379 sigaddset(&set, SIGINT);
380 sigaddset(&set, SIGTERM);
381 sigaddset(&set, SIGHUP);
382 sigprocmask(SIG_BLOCK, &set, NULL);
383 sigwait(&set, &sig);
384 }
385
386 /**
387 * Implementation of dispatcher_t.destroy
388 */
389 static void destroy(private_dispatcher_t *this)
390 {
391 char *sid;
392 session_entry_t *entry;
393 enumerator_t *enumerator;
394
395 FCGX_ShutdownPending();
396 while (this->thread_count--)
397 {
398 thread_t *thread = this->threads[this->thread_count];
399 thread->cancel(thread);
400 thread->join(thread);
401 }
402 enumerator = this->sessions->create_enumerator(this->sessions);
403 while (enumerator->enumerate(enumerator, &sid, &entry))
404 {
405 session_entry_destroy(entry);
406 }
407 enumerator->destroy(enumerator);
408 this->sessions->destroy(this->sessions);
409 this->controllers->destroy_function(this->controllers, free);
410 this->filters->destroy_function(this->filters, free);
411 this->mutex->destroy(this->mutex);
412 free(this->threads);
413 free(this);
414 }
415
416 /*
417 * see header file
418 */
419 dispatcher_t *dispatcher_create(char *socket, bool debug, int timeout,
420 context_constructor_t constructor, void *param)
421 {
422 private_dispatcher_t *this = malloc_thing(private_dispatcher_t);
423
424 this->public.add_controller = (void(*)(dispatcher_t*, controller_constructor_t, void*))add_controller;
425 this->public.add_filter = (void(*)(dispatcher_t*,filter_constructor_t constructor, void *param))add_filter;
426 this->public.run = (void(*)(dispatcher_t*, int threads))run;
427 this->public.waitsignal = (void(*)(dispatcher_t*))waitsignal;
428 this->public.destroy = (void(*)(dispatcher_t*))destroy;
429
430 this->sessions = hashtable_create((void*)session_hash,
431 (void*)session_equals, 4096);
432 this->controllers = linked_list_create();
433 this->filters = linked_list_create();
434 this->context_constructor = constructor;
435 this->mutex = mutex_create(MUTEX_TYPE_DEFAULT);
436 this->param = param;
437 this->fd = 0;
438 this->timeout = timeout;
439 this->last_cleanup = time_monotonic(NULL);
440 this->debug = debug;
441 this->threads = NULL;
442
443 FCGX_Init();
444
445 if (socket)
446 {
447 unlink(socket);
448 this->fd = FCGX_OpenSocket(socket, 10);
449 }
450 return &this->public;
451 }
452