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