1 /*
2 * Copyright (C) 2012 Smile Communications, jason.penton@smilecoms.com
3 * Copyright (C) 2012 Smile Communications, richard.good@smilecoms.com
4 *
5 * The initial version of this code was written by Dragos Vingarzan
6 * (dragos(dot)vingarzan(at)fokus(dot)fraunhofer(dot)de and the
7 * Fruanhofer Institute. It was and still is maintained in a separate
8 * branch of the original SER. We are therefore migrating it to
9 * Kamailio/SR and look forward to maintaining it from here on out.
10 * 2011/2012 Smile Communications, Pty. Ltd.
11 * ported/maintained/improved by
12 * Jason Penton (jason(dot)penton(at)smilecoms.com and
13 * Richard Good (richard(dot)good(at)smilecoms.com) as part of an
14 * effort to add full IMS support to Kamailio/SR using a new and
15 * improved architecture
16 *
17 * NB: Alot of this code was originally part of OpenIMSCore,
18 * FhG Fokus.
19 * Copyright (C) 2004-2006 FhG Fokus
20 * Thanks for great work! This is an effort to
21 * break apart the various CSCF functions into logically separate
22 * components. We hope this will drive wider use. We also feel
23 * that in this way the architecture is more complete and thereby easier
24 * to manage in the Kamailio/SR environment
25 *
26 * This file is part of Kamailio, a free SIP server.
27 *
28 * Kamailio is free software; you can redistribute it and/or modify
29 * it under the terms of the GNU General Public License as published by
30 * the Free Software Foundation; either version 2 of the License, or
31 * (at your option) any later version
32 *
33 * Kamailio is distributed in the hope that it will be useful,
34 * but WITHOUT ANY WARRANTY; without even the implied warranty of
35 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
36 * GNU General Public License for more details.
37 *
38 * You should have received a copy of the GNU General Public License
39 * along with this program; if not, write to the Free Software
40 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
41 *
42 */
43
44 #include <time.h>
45 #include <stdio.h>
46 #include <stdlib.h>
47
48 #include "../../core/sr_module.h"
49 #include "session.h"
50 #include "diameter.h"
51 #include "config.h"
52 #include "authstatemachine.h"
53 #include "acctstatemachine.h"
54 #include "timer.h"
55 #include "globals.h"
56
57 extern dp_config *config; /**< Configuration for this diameter peer */
58
59 gen_lock_t *session_lock; /**< lock for session operation */
60
61 int sessions_hash_size = 1024; /**< the size of the session hash table */
62 cdp_session_list_t *sessions; /**< the session hash table */
63
64 unsigned int *session_id1; /**< counter for first part of the session id */
65 unsigned int *session_id2; /**< counter for second part of the session id */
66
67 #define GRACE_DISCON_TIMEOUT 60 /**< 60 seconds for a DISCON acct session to hang around for before being cleaned up */
68
69
70 /**
71 * Lock a hash table row
72 */
AAASessionsLock(unsigned int hash)73 inline void AAASessionsLock(unsigned int hash)
74 {
75 if(destroy_modules_phase()) return;
76 if ( hash < sessions_hash_size ){
77 lock_get(sessions[hash].lock);
78 }
79 else {
80 LM_ERR("AAASessionsLock: hash :%d out of range of sessions_hash_size: %d !\n", hash, sessions_hash_size);
81 }
82 }
83
84 /**
85 * Unlock a hash table row
86 */
AAASessionsUnlock(unsigned int hash)87 inline void AAASessionsUnlock(unsigned int hash)
88 {
89 if(destroy_modules_phase()) return;
90
91 if ( hash < sessions_hash_size ){
92 lock_release(sessions[hash].lock);
93 }
94 else {
95 LM_ERR("AAASessionsLock: hash :%d out of range of sessions_hash_size: %d !\n", hash, sessions_hash_size);
96 }
97 }
98
99
100
101 /**
102 * Free a session structure
103 */
free_session(cdp_session_t * x)104 void free_session(cdp_session_t *x)
105 {
106 if (x){
107 if (x->id.s) shm_free(x->id.s);
108 switch(x->type){
109 case UNKNOWN_SESSION:
110 if (x->u.generic_data){
111 LM_ERR("free_session(): The session->u.generic_data should be freed and reset before dropping the session!"
112 "Possible memory leak!\n");
113 }
114 break;
115 case AUTH_CLIENT_STATEFULL:
116 break;
117 case AUTH_SERVER_STATEFULL:
118 break;
119 case ACCT_CC_CLIENT:
120 break;
121 default:
122 LM_ERR("free_session(): Unknown session type %d!\n",x->type);
123 }
124
125 if(x->dest_host.s) shm_free(x->dest_host.s);
126 if(x->dest_realm.s) shm_free(x->dest_realm.s);
127 if (x->sticky_peer_fqdn_buflen && x->sticky_peer_fqdn.s) {
128 shm_free(x->sticky_peer_fqdn.s);
129 }
130 shm_free(x);
131 }
132 }
133
134
135 /**
136 * Initializes the session related structures.
137 */
cdp_sessions_init(int hash_size)138 int cdp_sessions_init(int hash_size)
139 {
140 int i;
141 session_lock = lock_alloc();
142 if (!session_lock){
143 LOG_NO_MEM("lock",sizeof(gen_lock_t));
144 goto error;
145 }
146 session_lock = lock_init(session_lock);
147 sessions_hash_size=hash_size;
148
149 sessions = shm_malloc(sizeof(cdp_session_list_t)*hash_size);
150 if (!sessions){
151 LOG_NO_MEM("shm",sizeof(cdp_session_list_t)*hash_size);
152 goto error;
153 }
154 memset(sessions,0,sizeof(cdp_session_list_t)*hash_size);
155
156 for(i=0;i<hash_size;i++){
157 sessions[i].lock = lock_alloc();
158 if (!sessions[i].lock){
159 LOG_NO_MEM("lock",sizeof(gen_lock_t));
160 goto error;
161 }
162 sessions[i].lock = lock_init(sessions[i].lock);
163 }
164
165 session_id1 = shm_malloc(sizeof(unsigned int));
166 if (!session_id1){
167 LOG_NO_MEM("shm",sizeof(unsigned int));
168 goto error;
169 }
170 session_id2 = shm_malloc(sizeof(unsigned int));
171 if (!session_id2){
172 LOG_NO_MEM("shm",sizeof(unsigned int));
173 goto error;
174 }
175 *session_id1 = kam_rand();
176 *session_id1 <<= 16;
177 *session_id1 += time(0)&0xFFFF;
178 *session_id2 = 0;
179
180 add_timer(1,0,cdp_sessions_timer,0);
181 return 1;
182 error:
183 return 0;
184 }
185
186 /**
187 * Destroys the session related structures.
188 */
cdp_sessions_destroy()189 int cdp_sessions_destroy()
190 {
191 int i;
192 cdp_session_t *n,*x;
193
194 if (session_lock){
195 lock_get(session_lock);
196 lock_destroy(session_lock);
197 lock_dealloc((void*)session_lock);
198 session_lock=0;
199 }
200 for(i=0;i<sessions_hash_size;i++){
201 AAASessionsLock(i);
202 for(x = sessions[i].head; x; x = n){
203 n = x->next;
204 free_session(x);
205 }
206 lock_destroy(sessions[i].lock);
207 lock_dealloc((void*)sessions[i].lock);
208 }
209 shm_free(sessions);
210
211 shm_free(session_id1);
212 shm_free(session_id2);
213 return 1;
214 }
215
216
217
218
219 /**
220 * Computes the hash for a string.
221 * @param aor - the aor to compute the hash on
222 * @param hash_size - value to % with
223 * @returns the hash % hash_size
224 */
get_str_hash(str x,int hash_size)225 unsigned int get_str_hash(str x,int hash_size)
226 {
227 #define h_inc h+=v^(v>>3)
228 char* p;
229 register unsigned v;
230 register unsigned h;
231
232 h=0;
233 for (p=x.s; p<=(x.s+x.len-4); p+=4){
234 v=(*p<<24)+(p[1]<<16)+(p[2]<<8)+p[3];
235 h_inc;
236 }
237 v=0;
238 for (;p<(x.s+x.len); p++) {
239 v<<=8;
240 v+=*p;
241 }
242 h_inc;
243
244 h=((h)+(h>>11))+((h>>13)+(h>>23));
245 return (h)%hash_size;
246 #undef h_inc
247 }
248
249 /**
250 * Create a new session structure
251 * @param id - the session id string, already allocated in shm
252 * @param type - the session type
253 * @returns the new cdp_session_t on success or 0 on failure
254 */
cdp_new_session(str id,cdp_session_type_t type)255 cdp_session_t* cdp_new_session(str id,cdp_session_type_t type)
256 {
257 cdp_session_t *x=0;
258
259 x = shm_malloc(sizeof(cdp_session_t));
260 if (!x){
261 LOG_NO_MEM("shm",sizeof(cdp_session_t));
262 goto error;
263 }
264 memset(x,0,sizeof(cdp_session_t));
265 x->id = id;
266 x->type = type;
267 x->hash = get_str_hash(x->id,sessions_hash_size);
268 return x;
269 error:
270 return 0;
271 }
272
273 /**
274 * Adds the session to the session list.
275 * \note This returns with a lock, so unlock when done
276 * @param x - the session to add
277 */
cdp_add_session(cdp_session_t * x)278 void cdp_add_session(cdp_session_t *x)
279 {
280 // unsigned int hash;
281 if (!x) return;
282 // hash = get_str_hash(x->id,sessions_hash_size);
283 // x->hash = hash;
284 LM_DBG("adding a session with id %.*s\n",x->id.len,x->id.s);
285 AAASessionsLock(x->hash);
286 x->next = 0;
287 x->prev = sessions[x->hash].tail;
288 if (sessions[x->hash].tail) sessions[x->hash].tail->next = x;
289 sessions[x->hash].tail = x;
290 if (!sessions[x->hash].head) sessions[x->hash].head = x;
291 }
292
293 /**
294 * Finds a session in the session hash table.
295 * \note Returns with a lock on the sessions[x->hash].lock!!!
296 * @param id - the id of the session
297 * @returns the session if found or 0 if not
298 */
cdp_get_session(str id)299 cdp_session_t* cdp_get_session(str id)
300 {
301 unsigned int hash;
302 cdp_session_t *x;
303 if (!id.len) return 0;
304 hash = get_str_hash(id,sessions_hash_size);
305 LM_DBG("called get session with id %.*s and hash %u\n",id.len,id.s,hash);
306 AAASessionsLock(hash);
307 for(x = sessions[hash].head;x;x=x->next){
308 LM_DBG("looking for |%.*s| in |%.*s|\n",id.len,id.s,x->id.len,x->id.s);
309 if (x->id.len == id.len &&
310 strncasecmp(x->id.s,id.s,id.len)==0)
311 return x;
312 }
313 AAASessionsUnlock(hash);
314 LM_DBG("no session found\n");
315 return 0;
316 }
317
318
319 /**
320 * Removes and frees a session.
321 * \note must be called with a lock on the x->hash and it will unlock on exit. Do not use x after calling this
322 *
323 * @param x - the session to remove
324 */
del_session(cdp_session_t * x)325 void del_session(cdp_session_t *x)
326 {
327 unsigned int hash;
328
329 if (!x) return;
330
331 hash = x->hash;
332 if (hash >= sessions_hash_size) {
333 LM_ERR("del_session: x->hash :%d out of range of sessions_hash_size: %d !\n",hash, sessions_hash_size);
334 return;
335 }
336
337 if (sessions[x->hash].head == x) sessions[x->hash].head = x->next;
338 else if (x->prev) x->prev->next = x->next;
339 if (sessions[x->hash].tail == x) sessions[x->hash].tail = x->prev;
340 else if (x->next) x->next->prev = x->prev;
341
342 AAASessionsUnlock(hash);
343
344 free_session(x);
345 }
346
347
348 /**
349 * Generates a new session_ID (conforming with draft-ietf-aaa-diameter-17).
350 * This function is thread safe.
351 * @returns an 1 if success or -1 if error.
352 */
generate_session_id(str * id,unsigned int end_pad_len)353 static int generate_session_id(str *id, unsigned int end_pad_len)
354 {
355 unsigned int s2;
356
357 /* some checks */
358 if (!id)
359 goto error;
360
361 /* compute id's len */
362 id->len = config->identity.len +
363 1/*;*/ + 10/*high 32 bits*/ +
364 1/*;*/ + 10/*low 32 bits*/ +
365 // 1/*;*/ + 8/*optional value*/ +
366 1 /* terminating \0 */ +
367 end_pad_len;
368
369 /* get some memory for it */
370 id->s = (char*)shm_malloc( id->len );
371 if (id->s==0) {
372 LM_ERR("generate_session_id: no more free memory!\n");
373 goto error;
374 }
375
376 lock_get(session_lock);
377 s2 = *session_id2 +1;
378 *session_id2 = s2;
379 lock_release(session_lock);
380
381 /* build the sessionID */
382 sprintf(id->s,"%.*s;%u;%u",config->identity.len,config->identity.s,*session_id1,s2);
383 id->len = strlen(id->s);
384 return 1;
385 error:
386 return -1;
387 }
388
cdp_sessions_log()389 void cdp_sessions_log()
390 {
391 if (debug_heavy) {
392 int hash;
393 cdp_session_t *x;
394
395 LM_DBG("------- CDP Sessions ----------------\n");
396 for(hash=0;hash<sessions_hash_size;hash++){
397 AAASessionsLock(hash);
398 for(x = sessions[hash].head;x;x=x->next) {
399 LM_DBG(" %3u. [%.*s] AppId [%d] Type [%d]\n",
400 hash,
401 x->id.len,x->id.s,
402 x->application_id,
403 x->type);
404 switch (x->type){
405 case AUTH_CLIENT_STATEFULL:
406 case AUTH_SERVER_STATEFULL:
407 LM_DBG("Auth State [%d] Timeout [%d] Lifetime [%d] Grace [%d] Generic [%p] Class [%d]\n",
408 x->u.auth.state,
409 (int)(x->u.auth.timeout-time(0)),
410 x->u.auth.lifetime?(int)(x->u.auth.lifetime-time(0)):-1,
411 (int)(x->u.auth.grace_period),
412 x->u.auth.generic_data,
413 x->u.auth.class);
414 break;
415 case ACCT_CC_CLIENT:
416 LM_DBG("CCAcct State [%d] Charging Active [%c (%d)s] Reserved Units(valid=%ds) [%d] Generic [%p]\n",
417 x->u.cc_acc.state,
418 (x->u.cc_acc.charging_start_time&&x->u.cc_acc.state!=ACC_CC_ST_DISCON)?'Y':'N',
419 x->u.cc_acc.charging_start_time?(int)((int)time(0) - (int)x->u.cc_acc.charging_start_time):-1,
420 x->u.cc_acc.reserved_units?(int)((int)x->u.cc_acc.last_reservation_request_time + x->u.cc_acc.reserved_units_validity_time) - (int)time(0):-1,
421 x->u.cc_acc.reserved_units,
422 x->u.cc_acc.generic_data);
423 break;
424 default:
425 break;
426 }
427 }
428 AAASessionsUnlock(hash);
429 }
430 LM_DBG("-------------------------------------\n");
431 }
432 }
433
cdp_sessions_timer(time_t now,void * ptr)434 int cdp_sessions_timer(time_t now, void* ptr)
435 {
436 int hash;
437 cdp_session_t *x,*n;
438 for(hash=0;hash<sessions_hash_size;hash++){
439 AAASessionsLock(hash);
440 for(x = sessions[hash].head;x;x=n) {
441 n = x->next;
442 switch (x->type){
443 case ACCT_CC_CLIENT:
444 if (x->u.cc_acc.type == ACC_CC_TYPE_SESSION) {
445 //check for old, stale sessions, we need to do something more elegant
446 //here to ensure that if a CCR start record is sent and the client never sends anything
447 //else that we catch it and clean up the session from within CDP, calling all callbacks, etc
448 if ((time(0) > (x->u.cc_acc.discon_time + GRACE_DISCON_TIMEOUT)) && (x->u.cc_acc.state==ACC_CC_ST_DISCON)) {
449 cc_acc_client_stateful_sm_process(x, ACC_CC_EV_SESSION_STALE, 0);
450 }
451 //check reservation timers - again here we are assuming CC-Time applications
452 int last_res_timestamp = x->u.cc_acc.last_reservation_request_time;
453 int res_valid_for = x->u.cc_acc.reserved_units_validity_time;
454 int last_reservation = x->u.cc_acc.reserved_units;
455 int buffer_time = 15; //15 seconds - TODO: add as config parameter
456 //we should check for reservation expiries if the state is open
457 if(x->u.cc_acc.state==ACC_CC_ST_OPEN){
458 if (last_res_timestamp) {
459 //we have obv already started reservations
460 if ((last_res_timestamp + res_valid_for) < (time(0) + last_reservation + buffer_time)) {
461 LM_DBG("reservation about to expire, sending callback\n");
462 cc_acc_client_stateful_sm_process(x, ACC_CC_EV_RSVN_WARNING, 0);
463 }
464
465 }
466 }
467 /* TODO: if reservation has expired we need to tear down the session. Ideally
468 * the client application (module) should do this but for completeness we should
469 * put a failsafe here too.
470 */
471 }
472 break;
473 case AUTH_CLIENT_STATEFULL:
474 if (x->u.auth.timeout>=0 && x->u.auth.timeout<=now){
475 //Session timeout
476 LM_CRIT("session TIMEOUT\n");
477 auth_client_statefull_sm_process(x,AUTH_EV_SESSION_TIMEOUT,0);
478 } else if (x->u.auth.lifetime>0 && x->u.auth.lifetime+x->u.auth.grace_period<=now){
479 //lifetime + grace timeout
480 LM_CRIT("lifetime+grace TIMEOUT\n");
481 auth_client_statefull_sm_process(x,AUTH_EV_SESSION_GRACE_TIMEOUT,0);
482 }else if (x->u.auth.lifetime>0 && x->u.auth.lifetime<=now){
483 //lifetime timeout
484 LM_CRIT("lifetime+grace TIMEOUT\n");
485 auth_client_statefull_sm_process(x,AUTH_EV_SESSION_LIFETIME_TIMEOUT,0);
486 }
487 break;
488 case AUTH_SERVER_STATEFULL:
489 if (x->u.auth.timeout>=0 && x->u.auth.timeout<=now){
490 //Session timeout
491 LM_CRIT("session TIMEOUT\n");
492 auth_server_statefull_sm_process(x,AUTH_EV_SESSION_TIMEOUT,0);
493 }else if (x->u.auth.lifetime>0 && x->u.auth.lifetime+x->u.auth.grace_period<=now){
494 //lifetime + grace timeout
495 LM_CRIT("lifetime+grace TIMEOUT\n");
496 auth_server_statefull_sm_process(x,AUTH_EV_SESSION_GRACE_TIMEOUT,0);
497 }else if (x->u.auth.lifetime>0 && x->u.auth.lifetime<=now){
498 //lifetime timeout
499 LM_CRIT("lifetime+grace TIMEOUT\n");
500 auth_server_statefull_sm_process(x,AUTH_EV_SESSION_LIFETIME_TIMEOUT,0);
501 }
502 break;
503 default:
504 break;
505
506 }
507 }
508 AAASessionsUnlock(hash);
509 }
510 if (now%5==0)cdp_sessions_log();
511 return 1;
512 }
513
514
515
516 /****************************** API FUNCTIONS ********************************/
517
518 /**
519 * Creates a Generic Session.
520 */
AAACreateSession(void * generic_data)521 AAASession* AAACreateSession(void *generic_data)
522 {
523 AAASession *s;
524 str id;
525
526 generate_session_id(&id,0);
527 s = cdp_new_session(id,UNKNOWN_SESSION);
528 if (s) {
529 s->u.generic_data = generic_data;
530 cdp_add_session(s);
531 }
532 return s;
533 }
534
535 /**
536 * Make a session based on already known parameters.
537 * \Note This should be used to recover saved sessions after a restart.
538 * @param app_id
539 * @param type
540 * @param session_id - will be duplicated to shm
541 * @return
542 */
AAAMakeSession(int app_id,int type,str session_id)543 AAASession* AAAMakeSession(int app_id,int type,str session_id)
544 {
545 AAASession *s;
546 str id;
547
548 id.s = shm_malloc(session_id.len);
549 if (!id.s){
550 LM_ERR("Error allocating %d bytes!\n",session_id.len);
551 return 0;
552 }
553 memcpy(id.s,session_id.s,session_id.len);
554 id.len = session_id.len;
555 s = cdp_new_session(id,type);
556 s->application_id = app_id;
557 if (s) {
558 cdp_add_session(s);
559 }
560 return s;
561 }
562 /**
563 * Deallocates the memory taken by a Generic Session
564 */
AAADropSession(AAASession * s)565 void AAADropSession(AAASession *s)
566 {
567 // first give a chance to the cb to free the generic param
568 if (s&&s->cb)
569 (s->cb)(AUTH_EV_SESSION_DROP,s);
570 del_session(s);
571 }
572
573
cdp_new_auth_session(str id,int is_client,int is_statefull)574 AAASession* cdp_new_auth_session(str id,int is_client,int is_statefull)
575 {
576 AAASession *s;
577 cdp_session_type_t type;
578
579 if (is_client){
580 if (is_statefull) type = AUTH_CLIENT_STATEFULL;
581 else type = AUTH_CLIENT_STATELESS;
582 }else{
583 if (is_statefull) type = AUTH_SERVER_STATEFULL;
584 else type = AUTH_SERVER_STATELESS;
585 }
586 s = cdp_new_session(id,type);
587 if (s) {
588 s->u.auth.timeout=time(0)+config->default_auth_session_timeout;
589 s->u.auth.lifetime=0;
590 s->u.auth.grace_period=0;
591 s->u.auth.class = AUTH_CLASS_UNKNOWN;
592 s->u.auth.last_requested_grace = s->u.auth.last_requested_lifetime = s->u.auth.last_requested_timeout = 0;
593 cdp_add_session(s);
594 }
595 return s;
596 }
597
598 /**
599 * Creates a Credit Control Accounting session for Client.
600 * It generates a new id and adds the session to the cdp list of sessions
601 * \note Returns with a lock on AAASession->hash. Unlock when done working with the result
602 * @returns the new AAASession or null on error
603 */
cdp_new_cc_acc_session(str id,int is_statefull)604 AAASession* cdp_new_cc_acc_session(str id, int is_statefull)
605 {
606 AAASession *s;
607 cdp_session_type_t type;
608
609 //for now everything will be supported through this SM (until we add IEC)
610 type = ACCT_CC_CLIENT;
611
612 s = cdp_new_session(id,type);
613 if (s) {
614 if (is_statefull)
615 s->u.cc_acc.type = ACC_CC_TYPE_SESSION;
616 else
617 s->u.cc_acc.type = ACC_CC_TYPE_EVENT;
618
619 // s->u.cc_acc.timeout=time(0)+config->default_cc_acct_session_timeout;
620 cdp_add_session(s);
621 }
622 return s;
623 }
624
625 /**
626 * Creates a Authorization Session for the Client.
627 * It generates a new id and adds the session to the cdp list of sessions
628 * \note Returns with a lock on AAASession->hash. Unlock when done working with the result
629 * @returns the new AAASession or null on error
630 */
AAACreateClientAuthSession(int is_statefull,AAASessionCallback_f * cb,void * generic_data)631 AAASession* AAACreateClientAuthSession(int is_statefull,AAASessionCallback_f *cb,void *generic_data)
632 {
633 AAASession *s;
634 str id;
635
636 generate_session_id(&id,0);
637
638 s = cdp_new_auth_session(id,1,is_statefull);
639 if (s) {
640 s->u.auth.generic_data = generic_data;
641 s->cb = cb;
642 if (s->cb)
643 (s->cb)(AUTH_EV_SESSION_CREATED,s);
644 }
645 return s;
646 }
647 /**
648 * Creates a Authorization Session for the Server, from the application specific Session starting request
649 * It generates a new id and adds the session to the cdp list of sessions
650 * \note Returns with a lock on AAASession->hash. Unlock when done working with the result
651 * @returns the new AAASession or null on error
652 */
AAACreateServerAuthSession(AAAMessage * msg,int is_statefull,AAASessionCallback_f * cb,void * generic_data)653 AAASession* AAACreateServerAuthSession(AAAMessage *msg,int is_statefull,AAASessionCallback_f *cb,void *generic_data)
654 {
655 AAASession *s;
656 str id;
657
658 if (!msg||!msg->sessionId||!msg->sessionId->data.len){
659 LM_ERR("Error retrieving the Session-Id from the message.\n");
660 return 0;
661 }
662 id.s = shm_malloc(msg->sessionId->data.len);
663 if (!id.s){
664 LM_ERR("Error allocating %d bytes of shm!\n",msg->sessionId->data.len);
665 return 0;
666 }else{
667 id.len = msg->sessionId->data.len;
668 memcpy(id.s,msg->sessionId->data.s,id.len);
669 s=cdp_new_auth_session(id,0,is_statefull);
670 if (s) {
671 s->u.auth.generic_data = generic_data;
672 s->cb = cb;
673 if (s->cb)
674 (s->cb)(AUTH_EV_SESSION_CREATED,s);
675 update_auth_session_timers(&(s->u.auth),msg);
676 auth_server_statefull_sm_process(s,AUTH_EV_RECV_REQ,msg);
677 // this is a special exception where the session lock is not released
678 //s=0;
679 }
680 }
681 return s;
682 }
683
684 /**
685 * Looks for a session with a given id and returns it if found
686 * \note Returns with a lock on AAASession->hash. Unlock when done working with the result
687 * @returns the new AAASession or null on error
688 */
AAAGetSession(str id)689 AAASession* AAAGetSession(str id)
690 {
691 return cdp_get_session(id);
692 }
693
694 /**
695 * Looks for an Auth session with a given id and returns it if found
696 * \note Returns with a lock on AAASession->hash. Unlock when done working with the result
697 * @returns the new AAASession or null on error
698 */
AAAGetAuthSession(str id)699 AAASession* AAAGetAuthSession(str id)
700 {
701 AAASession *x=cdp_get_session(id);
702 if (x){
703 switch (x->type){
704 case AUTH_CLIENT_STATEFULL:
705 case AUTH_CLIENT_STATELESS:
706 case AUTH_SERVER_STATEFULL:
707 case AUTH_SERVER_STATELESS:
708 return x;
709 default:
710 AAASessionsUnlock(x->hash);
711 return 0;
712 }
713 }
714 return 0;
715 }
716
717 /**
718 * Sends a Service terminated event to the session
719 */
AAATerminateAuthSession(AAASession * s)720 void AAATerminateAuthSession(AAASession *s)
721 {
722 if (s->type==AUTH_CLIENT_STATEFULL) {
723 auth_client_statefull_sm_process(s,AUTH_EV_SERVICE_TERMINATED,0);
724 }
725 }
726
727 /**
728 * Deallocates the memory taken by a Authorization Session
729 * \note Must be called with a lock on the s->hash - will unlock it, so don't use the session after this
730 */
AAADropAuthSession(AAASession * s)731 void AAADropAuthSession(AAASession *s)
732 {
733 AAADropSession(s);
734 }
735
736 /**
737 * Creates an Accounting Session.
738 */
AAACreateAccSession(void * generic_data)739 AAASession* AAACreateAccSession(void *generic_data)
740 {
741 return 0;
742 }
743
744 /**
745 * Deallocates the memory taken by a Accounting Session
746 */
AAADropAccSession(AAASession * s)747 void AAADropAccSession(AAASession *s)
748 {
749 AAADropSession(s);
750 }
751
752 /**
753 * Creates an Accounting Session (Credit control - RFC 4006) for the client
754 * It generates a new id and adds the session to the cdp list of sessions
755 * \note Returns with a lock on AAASession->hash. Unlock when done working with the result
756 * @returns the new AAASession or null on error
757 */
AAACreateCCAccSession(AAASessionCallback_f * cb,int is_session,void * generic_data)758 AAASession* AAACreateCCAccSession(AAASessionCallback_f *cb, int is_session, void *generic_data)
759 {
760 AAASession *s;
761 str id;
762
763 generate_session_id(&id, 0);
764
765 s = cdp_new_cc_acc_session(id, is_session);
766 if (s) {
767 if (generic_data)
768 s->u.auth.generic_data = generic_data;
769 s->cb = cb;
770 if (s->cb)
771 (s->cb)(ACC_CC_EV_SESSION_CREATED, s);
772 }
773 return s;
774 }
775
776 /**
777 * Starts accounting on time-based CC App session (Credit control - RFC 4006) for the client
778 * @returns 0 on success, anything else on failure
779 */
AAAStartChargingCCAccSession(AAASession * s)780 int AAAStartChargingCCAccSession(AAASession *s)
781 {
782 if (s->type != ACCT_CC_CLIENT && s->u.cc_acc.type != ACC_CC_TYPE_SESSION) {
783 LM_ERR("Can't start charging on a credit-control session that is not session based\n");
784 return -1;
785 }
786
787 s->u.cc_acc.charging_start_time = time(0);
788 return 0;
789 }
790 /**
791 * Deallocates the memory taken by a Accounting Session (Credit Control - RFC 4006)
792 */
AAADropCCAccSession(AAASession * s)793 void AAADropCCAccSession(AAASession *s)
794 {
795 AAADropSession(s);
796 }
797
798 /**
799 * Looks for a CC Acc dession with a given id and returns it if found
800 * \note Returns with a lock on AAASession->hash. Unlock when done working with the result
801 * @returns the new AAASession or null on error
802 */
AAAGetCCAccSession(str id)803 AAASession* AAAGetCCAccSession(str id)
804 {
805 AAASession *x=cdp_get_session(id);
806 if (x){
807 switch (x->type){
808 case ACCT_CC_CLIENT:
809 return x;
810 default:
811 AAASessionsUnlock(x->hash);
812 return 0;
813 }
814 }
815 return 0;
816 }
817
818 /**
819 * Sends a Service terminated event to the session
820 */
AAATerminateCCAccSession(AAASession * s)821 void AAATerminateCCAccSession(AAASession *s)
822 {
823 if (s->type==ACCT_CC_CLIENT) {
824 //TODO: run state machine for terminate event
825 }
826 }
827
cdp_session_cleanup(cdp_session_t * s,AAAMessage * msg)828 void cdp_session_cleanup(cdp_session_t* s, AAAMessage* msg) {
829 // Here we should drop the session ! and free everything related to it
830 // but the generic_data thing should be freed by the callback function registered
831 // when the auth session was created
832 AAASessionCallback_f *cb;
833
834 LM_DBG("cleaning up session %.*s\n", s->id.len, s->id.s);
835 switch (s->type) {
836 case ACCT_CC_CLIENT:
837 if (s->cb) {
838 cb = s->cb;
839 (cb)(ACC_CC_EV_SESSION_TERMINATED, s);
840 }
841 AAADropCCAccSession(s);
842 break;
843 case AUTH_CLIENT_STATEFULL:
844 case AUTH_CLIENT_STATELESS:
845 if (s->cb) {
846 cb = s->cb;
847 (cb)(AUTH_EV_SERVICE_TERMINATED, s);
848 }
849 AAADropAuthSession(s);
850 break;
851 default:
852 LM_WARN("asked to cleanup unknown/unhandled session type [%d]\n", s->type);
853 break;
854 }
855
856 }
857
858