changeset 3:ef303f1078ab

Progress; added session module; testsess to be completed
author Sebastien Decugis <sdecugis@nict.go.jp>
date Wed, 02 Sep 2009 18:22:00 +0900
parents d8ce06172629
children 883311bf7df3
files freeDiameter/main.c freeDiameter/tests/CMakeLists.txt freeDiameter/tests/testlist.c freeDiameter/tests/tests.h freeDiameter/tests/testsess.c include/freeDiameter/libfreeDiameter.h libfreeDiameter/CMakeLists.txt libfreeDiameter/init.c libfreeDiameter/libfD.h libfreeDiameter/sessions.c
diffstat 10 files changed, 1175 insertions(+), 108 deletions(-) [+]
line wrap: on
line diff
--- a/freeDiameter/main.c	Mon Aug 31 17:32:22 2009 +0900
+++ b/freeDiameter/main.c	Wed Sep 02 18:22:00 2009 +0900
@@ -50,9 +50,6 @@
 	/* Add definitions of the base protocol */
 	CHECK_FCT( fd_dict_base_protocol(fd_g_dict) );
 	
-	/* For debug */
-	fd_dict_dump(fd_g_dict);
-	
 	TRACE_DEBUG(INFO, "freeDiameter daemon initialized.");
 	
 	return 0;
--- a/freeDiameter/tests/CMakeLists.txt	Mon Aug 31 17:32:22 2009 +0900
+++ b/freeDiameter/tests/CMakeLists.txt	Wed Sep 02 18:22:00 2009 +0900
@@ -11,9 +11,11 @@
 #############################
 # List the test cases
 SET(TEST_LIST
+	testlist
 	testdict
 	testmesg
 	testmq
+	testsess
 )
 
 #############################
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/freeDiameter/tests/testlist.c	Wed Sep 02 18:22:00 2009 +0900
@@ -0,0 +1,74 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@nict.go.jp>							 *
+*													 *
+* Copyright (c) 2009, WIDE Project and NICT								 *
+* All rights reserved.											 *
+* 													 *
+* Redistribution and use of this software in source and binary forms, with or without modification, are  *
+* permitted provided that the following conditions are met:						 *
+* 													 *
+* * Redistributions of source code must retain the above 						 *
+*   copyright notice, this list of conditions and the 							 *
+*   following disclaimer.										 *
+*    													 *
+* * Redistributions in binary form must reproduce the above 						 *
+*   copyright notice, this list of conditions and the 							 *
+*   following disclaimer in the documentation and/or other						 *
+*   materials provided with the distribution.								 *
+* 													 *
+* * Neither the name of the WIDE Project or NICT nor the 						 *
+*   names of its contributors may be used to endorse or 						 *
+*   promote products derived from this software without 						 *
+*   specific prior written permission of WIDE Project and 						 *
+*   NICT.												 *
+* 													 *
+* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED *
+* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
+* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR *
+* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 	 *
+* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 	 *
+* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR *
+* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF   *
+* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.								 *
+*********************************************************************************************************/
+
+#include "tests.h"
+
+#define TEST_STR "This is my test string (with extra unused data)"
+#define TEST_STRLEN 22
+
+/* Main test routine */
+int main(int argc, char *argv[])
+{
+	/* First, initialize the daemon modules */
+	INIT_FD();
+	
+	/* Check the hash function */
+	{
+		char buf[30];
+		
+		uint32_t hash = fd_hash(TEST_STR, TEST_STRLEN); /* reference value */
+		
+		/* Check that a hash of a substring / surstring is different */
+		CHECK( 1, hash != fd_hash(TEST_STR, TEST_STRLEN - 1) ? 1 : 0 );
+		CHECK( 1, hash != fd_hash(TEST_STR, TEST_STRLEN + 1) ? 1 : 0 );
+		
+		/* Check alignment of the string is not important */
+		memcpy(buf + 4, TEST_STR, TEST_STRLEN);
+		CHECK( hash, fd_hash(buf + 4, TEST_STRLEN) );
+		
+		memcpy(buf + 3, TEST_STR, TEST_STRLEN);
+		CHECK( hash, fd_hash(buf + 3, TEST_STRLEN) );
+		
+		memcpy(buf + 2, TEST_STR, TEST_STRLEN);
+		CHECK( hash, fd_hash(buf + 2, TEST_STRLEN) );
+		
+		memcpy(buf + 1, TEST_STR, TEST_STRLEN);
+		CHECK( hash, fd_hash(buf + 1, TEST_STRLEN) );
+	}
+
+	/* That's all for the tests yet */
+	PASSTEST();
+} 
+	
--- a/freeDiameter/tests/tests.h	Mon Aug 31 17:32:22 2009 +0900
+++ b/freeDiameter/tests/tests.h	Wed Sep 02 18:22:00 2009 +0900
@@ -97,7 +97,7 @@
 
 /* Minimum inits */
 #define INIT_FD() {					\
-	pthread_key_create(&fd_log_thname, free);	\
+	CHECK( 0, fd_lib_init() );			\
 	fd_log_threadname(basename(__FILE__));		\
 	CHECK( 0, fd_dict_init(&fd_g_dict) );		\
 	CHECK( 0, fd_dict_base_protocol(fd_g_dict) );	\
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/freeDiameter/tests/testsess.c	Wed Sep 02 18:22:00 2009 +0900
@@ -0,0 +1,157 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@nict.go.jp>							 *
+*													 *
+* Copyright (c) 2009, WIDE Project and NICT								 *
+* All rights reserved.											 *
+* 													 *
+* Redistribution and use of this software in source and binary forms, with or without modification, are  *
+* permitted provided that the following conditions are met:						 *
+* 													 *
+* * Redistributions of source code must retain the above 						 *
+*   copyright notice, this list of conditions and the 							 *
+*   following disclaimer.										 *
+*    													 *
+* * Redistributions in binary form must reproduce the above 						 *
+*   copyright notice, this list of conditions and the 							 *
+*   following disclaimer in the documentation and/or other						 *
+*   materials provided with the distribution.								 *
+* 													 *
+* * Neither the name of the WIDE Project or NICT nor the 						 *
+*   names of its contributors may be used to endorse or 						 *
+*   promote products derived from this software without 						 *
+*   specific prior written permission of WIDE Project and 						 *
+*   NICT.												 *
+* 													 *
+* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED *
+* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
+* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR *
+* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 	 *
+* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 	 *
+* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR *
+* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF   *
+* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.								 *
+*********************************************************************************************************/
+
+#include "tests.h"
+
+#define TEST_DIAM_ID 	"testsess.myid"
+#define TEST_OPT	"suffix"
+#define TEST_SID	TEST_DIAM_ID ";1234;5678;" TEST_OPT
+
+#define TEST_EYEC	0x7e57e1ec
+struct mystate {
+	int	eyec;	/* TEST_EYEC */
+	char *  sid; 	/* the session with which the data was registered */
+	int  *  freed;	/* location where to write the freed status */
+};
+
+void mycleanup( char * sid, struct mystate * data )
+{
+	/* sanity */
+	CHECK( 1, sid ? 1 : 0 );
+	CHECK( 1, data? 1 : 0 );
+	CHECK( TEST_EYEC, data->eyec );
+	CHECK( 0, strcmp(sid, data->sid) );
+	if (data->freed)
+		*(data->freed) += 1;
+	/* Now, free the data */
+	free(data->sid);
+	free(data);
+}
+
+/* Main test routine */
+int main(int argc, char *argv[])
+{
+	struct session_handler * hdl1, *hdl2;
+	struct session *sess1, *sess2, *sess3;
+	char *str1, *str2;
+	int new;
+	
+	/* First, initialize the daemon modules */
+	INIT_FD();
+	
+	/* Test functions related to handlers (simple situation) */
+	{
+		CHECK( 0, fd_sess_handler_create ( &hdl1, mycleanup ) );
+		CHECK( 0, fd_sess_handler_create ( &hdl2, mycleanup ) );
+		CHECK( 0, fd_sess_handler_destroy( &hdl2 ) );
+		CHECK( 0, fd_sess_handler_create ( &hdl2, mycleanup ) );
+		#if 1
+		fd_sess_dump_hdl(0, hdl1);
+		fd_sess_dump_hdl(0, hdl2);
+		#endif
+	}
+	
+	/* Test Session Id generation (fd_sess_new) */
+	{
+		/* DiamId is provided, not opt */
+		CHECK( 0, fd_sess_new( &sess1, TEST_DIAM_ID, NULL, 0 ) );
+		CHECK( 0, fd_sess_new( &sess2, TEST_DIAM_ID, NULL, 0 ) );
+		#if 1
+		fd_sess_dump(0, sess1);
+		fd_sess_dump(0, sess2);
+		#endif
+		
+		/* Check both string start with the diameter Id, but are different */
+		CHECK( 0, fd_sess_getsid(sess1, &str1) );
+		CHECK( 0, strncmp(str1, TEST_DIAM_ID ";", strlen(TEST_DIAM_ID) + 1) );
+		CHECK( 0, fd_sess_getsid(sess2, &str2) );
+		CHECK( 0, strncmp(str2, TEST_DIAM_ID ";", strlen(TEST_DIAM_ID) + 1) );
+		CHECK( 1, strcmp(str1, str2) ? 1 : 0 );
+		CHECK( 0, fd_sess_destroy( &sess1 ) );
+		CHECK( 0, fd_sess_destroy( &sess2 ) );
+		
+		/* diamId and opt */
+		CHECK( 0, fd_sess_new( &sess1, TEST_DIAM_ID, TEST_OPT, 0 ) );
+		CHECK( 0, fd_sess_new( &sess2, TEST_DIAM_ID, TEST_OPT, strlen(TEST_OPT) - 1 ) );
+		#if 1
+		fd_sess_dump(0, sess1);
+		fd_sess_dump(0, sess2);
+		#endif
+		
+		CHECK( 0, fd_sess_getsid(sess1, &str1) );
+		CHECK( 0, strncmp(str1, TEST_DIAM_ID ";", strlen(TEST_DIAM_ID) + 1) );
+		CHECK( 0, strcmp(str1 + strlen(str1) - strlen(TEST_OPT) - 1, ";" TEST_OPT) );
+		
+		CHECK( 0, fd_sess_getsid(sess2, &str2) );
+		CHECK( 0, strncmp(str2, TEST_DIAM_ID ";", strlen(TEST_DIAM_ID) + 1) );
+		CHECK( 0, strncmp(str2 + strlen(str2) - strlen(TEST_OPT), ";" TEST_OPT, strlen(TEST_OPT)) );
+		
+		CHECK( 1, strcmp(str1, str2) ? 1 : 0 );
+		CHECK( 0, fd_sess_destroy( &sess1 ) );
+		CHECK( 0, fd_sess_destroy( &sess2 ) );
+		
+		/* Now, only opt is provided */
+		CHECK( 0, fd_sess_new( &sess1, NULL, TEST_SID, 0 ) );
+		CHECK( EALREADY, fd_sess_new( &sess2, NULL, TEST_SID, 0 ) );
+		CHECK( sess2, sess1 );
+		CHECK( EALREADY, fd_sess_new( &sess3, NULL, TEST_SID, strlen(TEST_SID) ) );
+		CHECK( sess3, sess1 );
+		CHECK( 0, fd_sess_new( &sess2, NULL, TEST_SID, strlen(TEST_SID) - 1 ) );
+		#if 1
+		fd_sess_dump(0, sess1);
+		fd_sess_dump(0, sess2);
+		#endif
+		CHECK( 0, fd_sess_getsid(sess1, &str1) );
+		CHECK( 0, fd_sess_getsid(sess2, &str2) );
+		CHECK( 0, strncmp( str1, str2, strlen(TEST_SID) - 1 ) );
+		CHECK( 0, strcmp( str1, TEST_SID ) );
+		
+		CHECK( 0, fd_sess_destroy( &sess2 ) );
+	}
+		
+		
+	
+/*	
+int fd_sess_new ( struct session ** session, char * diamId, char * opt, size_t optlen );
+int fd_sess_fromsid ( char * sid, size_t len, struct session ** session, int * new);
+int fd_sess_getsid ( struct session * session, char ** sid );
+int fd_sess_settimeout( struct session * session, const struct timespec * timeout );
+int fd_sess_destroy ( struct session ** session );
+*/
+
+	/* That's all for the tests yet */
+	PASSTEST();
+} 
+	
--- a/include/freeDiameter/libfreeDiameter.h	Mon Aug 31 17:32:22 2009 +0900
+++ b/include/freeDiameter/libfreeDiameter.h	Wed Sep 02 18:22:00 2009 +0900
@@ -141,12 +141,6 @@
 /* A global level, changed by configuration or cmd line for example. default is 0. */
 extern int fd_g_debug_lvl;
 
-/* helper macros (pre-processor hacks to allow macro arguments) */
-#define __str( arg )  #arg
-#define _stringize( arg ) __str( arg )
-#define __agr( arg1, arg2 ) arg1 ## arg2
-#define _aggregate( arg1, arg2 ) __agr( arg1, arg2 )
-
 /* Some portability code to get nice function name in __PRETTY_FUNCTION__ */
 #if __STDC_VERSION__ < 199901L
 # if __GNUC__ >= 2
@@ -285,7 +279,16 @@
 	CHECK_FCT_DO( __v__ = (__call__), return __v__ );				\
 }
 
-/****************************** Socket helpers ************************************/
+
+/*============================================================*/
+/*                          MACROS                            */
+/*============================================================*/
+
+/* helper macros (pre-processor hacks to allow macro arguments) */
+#define __str( arg )  #arg
+#define _stringize( arg ) __str( arg )
+#define __agr( arg1, arg2 ) arg1 ## arg2
+#define _aggregate( arg1, arg2 ) __agr( arg1, arg2 )
 
 /* Some aliases to socket addresses structures */
 #define sSS	struct sockaddr_storage
@@ -338,9 +341,6 @@
 #define IN6_ADDR_V4UNMAP( a6 ) 				\
 	(((in_addr_t *)(a6))[3])
 
-/*
- * Other macros 
- */
 
 /* We provide macros to convert 64 bit values to and from network byte-order, on systems where it is not already provided. */
 #ifndef HAVE_NTOHLL	/* Defined in config.h, if the ntohll symbol is defined on the system */
@@ -355,10 +355,10 @@
 # endif /* HOST_BIG_ENDIAN */
 #endif /* HAVE_NTOHLL */
 
-/* This macro will pad a size to the next multiple of 4. */
+/* This macro will give the next multiple of 4 for an integer (used for padding sizes of AVP). */
 #define PAD4(_x) ((_x) + ( (4 - (_x)) & 3 ) )
 
-/* Useful to display as ASCII some bytes values */
+/* Useful to display as safe ASCII a value (will garbage UTF-8 output...) */
 #define ASCII(_c) ( ((_c < 32) || (_c > 127)) ? ( _c ? '?' : ' ' ) : _c )
 
 /* Compare timespec structures */
@@ -366,11 +366,6 @@
 	(    ((ts1)->tv_sec  < (ts2)->tv_sec ) 	\
 	  || ((ts1)->tv_nsec < (ts2)->tv_nsec) )
 
-/* Some constants for dumping flags and values */
-#define DUMP_AVPFL_str	"%c%c"
-#define DUMP_AVPFL_val(_val) (_val & AVP_FLAG_VENDOR)?'V':'-' , (_val & AVP_FLAG_MANDATORY)?'M':'-'
-#define DUMP_CMDFL_str	"%c%c%c%c"
-#define DUMP_CMDFL_val(_val) (_val & CMD_FLAG_REQUEST)?'R':'-' , (_val & CMD_FLAG_PROXIABLE)?'P':'-' , (_val & CMD_FLAG_ERROR)?'E':'-' , (_val & CMD_FLAG_RETRANSMIT)?'T':'-'
 
 /*============================================================*/
 /*                          THREADS                           */
@@ -592,35 +587,35 @@
 On the other side, when value is retrieved with dict_getval, the string is not copied and MUST NOT be freed. It will
 be freed automatically along with the object itself with call to dict_fini later.
  
-- dict_new:
+- fd_dict_new:
  The "parent" parameter is not used for vendors. 
  Sample code to create a vendor:
  {
 	 int ret;
 	 struct dict_object * myvendor;
 	 struct dict_vendor_data myvendordata = { 23455, "my vendor name" };  -- just an example...
-	 ret = dict_new ( DICT_VENDOR, &myvendordata, NULL, &myvendor );
+	 ret = fd_dict_new ( dict, DICT_VENDOR, &myvendordata, NULL, &myvendor );
  }
 
-- dict_search:
+- fd_dict_search:
  Sample codes to look for a vendor object, by its id or name:
  {
 	 int ret;
 	 struct dict_object * vendor_found;
 	 vendor_id_t vendorid = 23455;
-	 ret = dict_search ( DICT_VENDOR, VENDOR_BY_ID, &vendorid, &vendor_found, ENOENT);
+	 ret = fd_dict_search ( dict, DICT_VENDOR, VENDOR_BY_ID, &vendorid, &vendor_found, ENOENT);
 	 - or -
-	 ret = dict_search ( DICT_VENDOR, VENDOR_BY_NAME, "my vendor name", &vendor_found, ENOENT);
+	 ret = fd_dict_search ( dict, DICT_VENDOR, VENDOR_BY_NAME, "my vendor name", &vendor_found, ENOENT);
  }
  
- - dict_getval:
+ - fd_dict_getval:
  Sample code to retrieve the data from a vendor object:
  {
 	 int ret;
 	 struct dict_object * myvendor;
 	 struct dict_vendor_data myvendordata;
-	 ret = dict_search ( DICT_VENDOR, VENDOR_BY_NAME, "my vendor name", &myvendor, ENOENT);
-	 ret = dict_getval ( myvendor, &myvendordata );
+	 ret = fd_dict_search ( dict, DICT_VENDOR, VENDOR_BY_NAME, "my vendor name", &myvendor, ENOENT);
+	 ret = fd_dict_getval ( myvendor, &myvendordata );
 	 printf("my vendor id: %d\n", myvendordata.vendor_id );
  }
 		 
@@ -661,7 +656,7 @@
 for standard-track applications, the "parent" parameter should be NULL.
 The vendor associated to an application is retrieved with VENDOR_OF_APPLICATION search criteria on vendors.
 
-- dict_new:
+- fd_dict_new:
  Sample code for application creation:
  {
 	 int ret;
@@ -676,28 +671,28 @@
 		 "my vendor's application"
 	 };
 	
-	 ret = dict_new ( DICT_VENDOR, &vendor_data, NULL, &vendor );
-	 ret = dict_new ( DICT_APPLICATION, &app_data, vendor, &appl );
+	 ret = fd_dict_new ( dict, DICT_VENDOR, &vendor_data, NULL, &vendor );
+	 ret = fd_dict_new ( dict, DICT_APPLICATION, &app_data, vendor, &appl );
  }
 
-- dict_search:
+- fd_dict_search:
  Sample code to retrieve the vendor of an application
  {
 	 int ret;
 	 struct dict_object * vendor, * appli;
 	 
-	 ret = dict_search ( DICT_APPLICATION, APPLICATION_BY_NAME, "my vendor's application", &appli, ENOENT);
-	 ret = dict_search ( DICT_VENDOR, VENDOR_OF_APPLICATION, appli, &vendor, ENOENT);
+	 ret = fd_dict_search ( dict, DICT_APPLICATION, APPLICATION_BY_NAME, "my vendor's application", &appli, ENOENT);
+	 ret = fd_dict_search ( dict, DICT_VENDOR, VENDOR_OF_APPLICATION, appli, &vendor, ENOENT);
  }
  
- - dict_getval:
+ - fd_dict_getval:
  Sample code to retrieve the data from an application object:
  {
 	 int ret;
 	 struct dict_object * appli;
 	 struct dict_application_data appl_data;
-	 ret = dict_search ( DICT_APPLICATION, APPLICATION_BY_NAME, "my vendor's application", &appli, ENOENT);
-	 ret = dict_getval ( appli, &appl_data );
+	 ret = fd_dict_search ( dict, DICT_APPLICATION, APPLICATION_BY_NAME, "my vendor's application", &appli, ENOENT);
+	 ret = fd_dict_getval ( appli, &appl_data );
 	 printf("my application id: %s\n", appl_data.application_id );
  }
 
@@ -798,7 +793,7 @@
 /***
  *  API usage :
 
-- dict_new:
+- fd_dict_new:
  The "parent" parameter may point to an application object, when a type is defined by a Diameter application. 
  
  Sample code:
@@ -812,15 +807,15 @@
 		 NULL,
 		 NULL
 		};
-	 ret = dict_new ( DICT_TYPE, &mytypedata, NULL, &mytype );
+	 ret = fd_dict_new ( dict, DICT_TYPE, &mytypedata, NULL, &mytype );
  }
 
-- dict_search:
+- fd_dict_search:
  Sample code:
  {
 	 int ret;
 	 struct dict_object * address_type;
-	 ret = dict_search ( DICT_TYPE, TYPE_BY_NAME, "Address", &address_type, ENOENT);
+	 ret = fd_dict_search ( dict, DICT_TYPE, TYPE_BY_NAME, "Address", &address_type, ENOENT);
  }
  
 */
@@ -859,7 +854,7 @@
 /***
  *  API usage :
 
-- dict_new:
+- fd_dict_new:
  The "parent" parameter must point to a derived type object. 
  Sample code to create a type "Boolean" with two constants "True" and "False":
  {
@@ -882,13 +877,13 @@
 		 .enum_name="True",
 		 .enum_value.i32 = -1
 	 	};
-	 ret = dict_new ( DICT_TYPE, &type_boolean_data, NULL, &type_boolean );
-	 ret = dict_new ( DICT_ENUMVAL, &boolean_false, type_boolean, NULL );
-	 ret = dict_new ( DICT_ENUMVAL, &boolean_true , type_boolean, NULL );
+	 ret = fd_dict_new ( dict, DICT_TYPE, &type_boolean_data, NULL, &type_boolean );
+	 ret = fd_dict_new ( dict, DICT_ENUMVAL, &boolean_false, type_boolean, NULL );
+	 ret = fd_dict_new ( dict, DICT_ENUMVAL, &boolean_true , type_boolean, NULL );
 	 
  }
 
-- dict_search:
+- fd_dict_search:
  Sample code to look for a constant name, by its value:
  {
 	 int ret;
@@ -900,10 +895,10 @@
 		 .search.enum_value.i32 = -1
 	 	};
 	 
-	 ret = dict_search ( DICT_ENUMVAL, ENUMVAL_BY_STRUCT, &boolean_by_value, &value_found, ENOENT);
+	 ret = fd_dict_search ( dict, DICT_ENUMVAL, ENUMVAL_BY_STRUCT, &boolean_by_value, &value_found, ENOENT);
  }
  
- - dict_getval:
+ - fd_dict_getval:
  Sample code to retrieve the data from a constant object:
  {
 	 int ret;
@@ -916,8 +911,8 @@
 		 .search.enum_value.i32 = 0
 	 	};
 	 
-	 ret = dict_search ( DICT_ENUMVAL, ENUMVAL_BY_STRUCT, &boolean_by_value, &value_found, ENOENT);
-	 ret = dict_getval ( value_found, &boolean_data );
+	 ret = fd_dict_search ( dict, DICT_ENUMVAL, ENUMVAL_BY_STRUCT, &boolean_by_value, &value_found, ENOENT);
+	 ret = fd_dict_getval ( value_found, &boolean_data );
 	 printf(" Boolean with value 0: %s", boolean_data.enum_name );
  }
 */
@@ -945,6 +940,9 @@
 #define	AVP_FLAG_RESERVED7	0x02
 #define	AVP_FLAG_RESERVED8	0x01
 
+/* For dumping flags and values */
+#define DUMP_AVPFL_str	"%c%c"
+#define DUMP_AVPFL_val(_val) (_val & AVP_FLAG_VENDOR)?'V':'-' , (_val & AVP_FLAG_MANDATORY)?'M':'-'
 
 /* Type to hold data associated to an avp */
 struct dict_avp_data {
@@ -982,7 +980,7 @@
 
 To create the rules (ABNF) for children of Grouped AVP, see the DICT_RULE related part.
 
-- dict_new:
+- fd_dict_new:
  Sample code for AVP creation:
  {
 	 int ret;
@@ -1007,15 +1005,15 @@
 	 };
 	
  	 -- Create an AVP with a base type --
-	 ret = dict_new ( DICT_AVP, &user_name_data, NULL, &user_name_avp );
+	 ret = fd_dict_new ( dict, DICT_AVP, &user_name_data, NULL, &user_name_avp );
 	 
 	 -- Create an AVP with a derived type --
-	 ret = dict_search ( DICT_TYPE, TYPE_BY_NAME, "Boolean", &boolean_type, ENOENT);
-	 ret = dict_new ( DICT_AVP, &sample_boolean_data , boolean_type, &sample_boolean_avp );
+	 ret = fd_dict_search ( dict, DICT_TYPE, TYPE_BY_NAME, "Boolean", &boolean_type, ENOENT);
+	 ret = fd_dict_new ( dict, DICT_AVP, &sample_boolean_data , boolean_type, &sample_boolean_avp );
 	 
  }
 
-- dict_search:
+- fd_dict_search:
  Sample code to look for an AVP
  {
 	 int ret;
@@ -1027,20 +1025,20 @@
 		 .avp_name   = "Sample-Boolean"
 	 	};
 	 
-	 ret = dict_search ( DICT_AVP, AVP_BY_NAME, "User-Name", &avp_username, ENOENT);
+	 ret = fd_dict_search ( dict, DICT_AVP, AVP_BY_NAME, "User-Name", &avp_username, ENOENT);
 	 
-	 ret = dict_search ( DICT_AVP, AVP_BY_NAME_AND_VENDOR, &avpvendorboolean, &avp_sampleboolean, ENOENT);
+	 ret = fd_dict_search ( dict, DICT_AVP, AVP_BY_NAME_AND_VENDOR, &avpvendorboolean, &avp_sampleboolean, ENOENT);
 	 
  }
  
- - dict_getval:
+ - fd_dict_getval:
  Sample code to retrieve the data from an AVP object:
  {
 	 int ret;
 	 struct dict_object * avp_username;
 	 struct dict_avp_data user_name_data;
-	 ret = dict_search ( DICT_AVP, AVP_BY_NAME, "User-Name", &avp_username, ENOENT);
-	 ret = dict_getval ( avp_username, &user_name_data );
+	 ret = fd_dict_search ( dict, DICT_AVP, AVP_BY_NAME, "User-Name", &avp_username, ENOENT);
+	 ret = fd_dict_getval ( avp_username, &user_name_data );
 	 printf("User-Name code: %d\n", user_name_data.avp_code );
  }
 
@@ -1069,6 +1067,10 @@
 #define CMD_FLAG_RESERVED7	0x02
 #define CMD_FLAG_RESERVED8	0x01
 
+/* For dumping flags and values */
+#define DUMP_CMDFL_str	"%c%c%c%c"
+#define DUMP_CMDFL_val(_val) (_val & CMD_FLAG_REQUEST)?'R':'-' , (_val & CMD_FLAG_PROXIABLE)?'P':'-' , (_val & CMD_FLAG_ERROR)?'E':'-' , (_val & CMD_FLAG_RETRANSMIT)?'T':'-'
+
 /* Type to hold data associated to a command */
 struct dict_cmd_data {
 	command_code_t	 cmd_code;	/* code of the command */
@@ -1096,7 +1098,7 @@
 
 Note that the "Request" and "Answer" commands are two independant objects. This allows to have different rules for each.
 
-- dict_new:
+- fd_dict_new:
  Sample code for command creation:
  {
 	 int ret;
@@ -1109,34 +1111,34 @@
 		 CMD_FLAG_REQUEST			// value. Only the "R" flag is constrained here, set.
 	 };
 	
-	 ret = dict_new ( DICT_COMMAND, &ce_data, NULL, &cer );
+	 ret = fd_dict_new (dict,  DICT_COMMAND, &ce_data, NULL, &cer );
 	 
 	 ce_data.cmd_name = "Capabilities-Exchange-Answer";
 	 ce_data.cmd_flag_val = 0;			// Same constraint on "R" flag, but this time it must be cleared.
 
-	 ret = dict_new ( DICT_COMMAND, &ce_data, NULL, &cea );
+	 ret = fd_dict_new ( dict, DICT_COMMAND, &ce_data, NULL, &cea );
  }
 
-- dict_search:
+- fd_dict_search:
  Sample code to look for a command
  {
 	 int ret;
 	 struct dict_object * cer, * cea;
 	 command_code_t	code = 257;
-	 ret = dict_search ( DICT_COMMAND, CMD_BY_NAME, "Capabilities-Exchange-Request", &cer, ENOENT);
-	 ret = dict_search ( DICT_COMMAND, CMD_BY_CODE_R, &code, &cer, ENOENT);
+	 ret = fd_dict_search ( dict, DICT_COMMAND, CMD_BY_NAME, "Capabilities-Exchange-Request", &cer, ENOENT);
+	 ret = fd_dict_search ( dict, DICT_COMMAND, CMD_BY_CODE_R, &code, &cer, ENOENT);
  }
  
- - dict_getval:
+ - fd_dict_getval:
  Sample code to retrieve the data from a command object:
  {
 	 int ret;
 	 struct dict_object * cer;
 	 struct dict_object * cea;
 	 struct dict_cmd_data cea_data;
-	 ret = dict_search ( DICT_COMMAND, CMD_BY_NAME, "Capabilities-Exchange-Request", &cer, ENOENT);
-	 ret = dict_search ( DICT_COMMAND, CMD_ANSWER, cer, &cea, ENOENT);
-	 ret = dict_getval ( cea, &cea_data );
+	 ret = fd_dict_search ( dict, DICT_COMMAND, CMD_BY_NAME, "Capabilities-Exchange-Request", &cer, ENOENT);
+	 ret = fd_dict_search ( dict, DICT_COMMAND, CMD_ANSWER, cer, &cea, ENOENT);
+	 ret = fd_dict_getval ( cea, &cea_data );
 	 printf("Answer to CER: %s\n", cea_data.cmd_name );
  }
 
@@ -1187,7 +1189,7 @@
 
 The "parent" parameter can not be NULL. It points to the object (grouped avp or command) to which this rule apply (i.e. for which the ABNF is defined).
 
-- dict_new:
+- fd_dict_new:
  Sample code for rule creation. Let's create the Proxy-Info grouped AVP for example.
  {
 	int ret;
@@ -1202,14 +1204,14 @@
 	struct dict_avp_data proxy_state_data = { 33, 0, "Proxy-State",AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, AVP_FLAG_MANDATORY, AVP_TYPE_OCTETSTRING };
 	
 	-- Create the parent AVP
-	ret = dict_new ( DICT_AVP, &proxy_info_data, NULL, &proxy_info_avp );
+	ret = fd_dict_new ( dict, DICT_AVP, &proxy_info_data, NULL, &proxy_info_avp );
 	
 	-- Create the first child AVP.
-	ret = dict_new ( DICT_TYPE, &di_type_data, NULL, &diameteridentity_type );
-	ret = dict_new ( DICT_AVP, &proxy_host_data, diameteridentity_type, &proxy_host_avp );
+	ret = fd_dict_new ( dict, DICT_TYPE, &di_type_data, NULL, &diameteridentity_type );
+	ret = fd_dict_new ( dict, DICT_AVP, &proxy_host_data, diameteridentity_type, &proxy_host_avp );
 	
 	-- Create the other child AVP
-	ret = dict_new ( DICT_AVP, &proxy_state_data, NULL, &proxy_state_avp );
+	ret = fd_dict_new ( dict, DICT_AVP, &proxy_state_data, NULL, &proxy_state_avp );
 	
 	-- Now we can create the rules. Both children AVP are mandatory.
 	rule_data.rule_position = RULE_REQUIRED;
@@ -1217,13 +1219,13 @@
 	rule_data.rule_max = -1;
 	
 	rule_data.rule_avp = proxy_host_avp;
-	ret = dict_new ( DICT_RULE, &rule_data, proxy_info_avp, NULL );
+	ret = fd_dict_new ( dict, DICT_RULE, &rule_data, proxy_info_avp, NULL );
 	
 	rule_data.rule_avp = proxy_state_avp;
-	ret = dict_new ( DICT_RULE, &rule_data, proxy_info_avp, NULL );
+	ret = fd_dict_new ( dict, DICT_RULE, &rule_data, proxy_info_avp, NULL );
 }
 
-- dict_search and dict_getval are similar to previous examples.
+- fd_dict_search and fd_dict_getval are similar to previous examples.
 
 */
 		
@@ -1280,48 +1282,209 @@
 /*                         SESSIONS                           */
 /*============================================================*/
 
+/* Modules that want to associate a state with a Session-Id must first register a handler of this type */
+struct session_handler;
+
+/* This opaque structure represents a session associated with a Session-Id */
+struct session;
+
+/* The state information that a module associate with a session -- each module define its own data format */
+typedef void session_state;
+
+/*
+ * FUNCTION:	fd_sess_handler_create
+ *
+ * PARAMETERS:
+ *  handler	: location where the new handler must be stored.
+ *  cleanup	: a callback function that must be called when the session with associated data is destroyed.
+ *
+ * DESCRIPTION: 
+ *  Create a new session handler. This is needed by a module to associate a state with a session object.
+ * The cleanup handler is called when the session timeout expires, or fd_sess_destroy is called. It must free
+ * the state associated with the session, and eventually trig other actions (send a STR, ...).
+ *
+ * RETURN VALUE:
+ *  0      	: The new handler has been created.
+ *  EINVAL 	: A parameter is invalid.
+ *  ENOMEM	: Not enough memory to complete the operation
+ */
+int fd_sess_handler_create_int ( struct session_handler ** handler, void (*cleanup)(char * sid, session_state * state) );
+/* Macro to avoid casting everywhere */
+#define fd_sess_handler_create( _handler, _cleanup ) \
+	fd_sess_handler_create_int( (_handler), (void (*)(char *, session_state *))(_cleanup) )
+
+/*
+ * FUNCTION:	fd_sess_handler_destroy
+ *
+ * PARAMETERS:
+ *  handler	: location of an handler created by fd_sess_handler_create.
+ *
+ * DESCRIPTION: 
+ *  This destroys a session handler (typically called when an application is shutting down).
+ * If sessions states are registered with this handler, the cleanup callback is called on them.
+ *
+ * RETURN VALUE:
+ *  0      	: The handler was destroyed.
+ *  EINVAL 	: A parameter is invalid.
+ *  ENOMEM	: Not enough memory to complete the operation
+ */
+int fd_sess_handler_destroy ( struct session_handler ** handler );
+
 
 
 /*
- * The libfreeDiameter does not provide a full support of the sessions state machines as described in the RFC3588.
- * It only provides a basic support allowing an extension to associate some state with a session identifier, and retrieve 
- * this data later.
+ * FUNCTION:	fd_sess_new
+ *
+ * PARAMETERS:
+ *  session	  : The location where the session object will be created upon success.
+ *  diamId	  : \0-terminated string containing a Diameter Identity.
+ *  opt           : Additional string. Usage is described bellow.
+ *  optlen	  : if opt is \0-terminated, this can be 0. Otherwise, the length of opt.
+ *
+ * DESCRIPTION: 
+ *   Create a new session object. The Session-Id string associated with this session is generated as follow:
+ *  If diamId parameter is provided, the string is created according to the RFC: <diamId>;<high32>;<low32>[;opt] where
+ *    diamId is a Diameter Identity.
+ *    high32 and low32 are the parts of a monotonic 64 bits counter initialized to (time, 0) at startup.
+ *    opt is an optional string that can be concatenated to the identifier.
+ *  If diamId is NULL, the string is exactly the content of opt.
+ *
+ * RETURN VALUE:
+ *  0      	: The session is created.
+ *  EINVAL 	: A parameter is invalid.
+ *  EALREADY	: A session with the same name already exists (returned in *session)
+ *  ENOMEM	: Not enough memory to complete the operation
+ */
+int fd_sess_new ( struct session ** session, char * diamId, char * opt, size_t optlen );
+
+/*
+ * FUNCTION:	fd_sess_fromsid
+ *
+ * PARAMETERS:
+ *  sid	  	: pointer to a string containing a Session-Id (UTF-8).
+ *  len		: length of the sid string (which does not need to be '\0'-terminated)
+ *  session	: On success, pointer to the session object created / retrieved.
+ *  new		: if not NULL, set to 1 on return if the session object has been created, 0 if it was simply retrieved.
  *
- * A session is an opaque object, associated with a value of a Session-Id AVP.
- * An extension that wants to associate data with the session must first register as session module client 
- * with the sess_regext function to get an identifier object (sess_reg_t).
- * 
- * The module manages tuplets ( sess_id_t *, sess_reg_t *, void *). The following functions are used to manage these tuplets:
- * sess_data_reg  : associate a pointer with a given session for a given module client.
- * sess_data_dereg: removes an association.
- * sess_data_get  : get the pointer associated with an association without changing it.
+ * DESCRIPTION: 
+ *   Retrieve a session object from a Session-Id string. Calling this function makes an implicit call to the
+ *  fd_sess_link function on the returned session. In case no session object was previously existing with this 
+ *  id, a new object is silently created (equivalent to fd_sess_new with flag SESSION_NEW_FULL).
+ *
+ * RETURN VALUE:
+ *  0      	: The session parameter has been updated.
+ *  EINVAL 	: A parameter is invalid.
+ *  ENOMEM	: Not enough memory to complete the operation
+ */
+int fd_sess_fromsid ( char * sid, size_t len, struct session ** session, int * new);
+
+/*
+ * FUNCTION:	fd_sess_getsid
+ *
+ * PARAMETERS:
+ *  session	: Pointer to a session object.
+ *  sid	  	: On success, the location of a (\0-terminated) string is stored here.
  *
- * Note that creating an association calls sess_link as a side effect, and removing the association calls sess_unlink.
+ * DESCRIPTION: 
+ *   Retrieve the session identifier (Session-Id) corresponding to a session object.
+ *  The returned sid is an UTF-8 string terminated by \0, suitable for calls to strlen and strcpy.
+ *  It may be used for example to set the value of an AVP.
+ *  Note that the sid string is not copied, just its reference... do not free it!
  *
- * QUICK TUTORIAL:
- *  For an extension that wants to implement a session state machine, here is a quick guide.
+ * RETURN VALUE:
+ *  0      	: The sid parameter has been updated.
+ *  EINVAL 	: A parameter is invalid.
+ */
+int fd_sess_getsid ( struct session * session, char ** sid );
+
+/*
+ * FUNCTION:	fd_sess_settimeout
+ *
+ * PARAMETERS:
+ *  session	: The session for which to set the timeout.
+ *  timeout	: The date when the session times out.
  *
- * First, the extension must define a structure to save the session state, for example appstate_t.
+ * DESCRIPTION: 
+ *   Set the lifetime for a given session object. This function may be 
+ * called several times on the same object to update the timeout value.
+ *   When the timeout date is reached, the cleanup handler of each 
+ * module that registered data with this session is called, then the 
+ * session is cleared.
  *
- * Since the extension will use the session module, it creates a sess_reg_t by calling sess_regext.
+ *   There is a possible race condition between cleanup of the session
+ * and use of its data; applications should ensure that they are not 
+ * using data from a session that is about to expire / expired.
  *
- * If the extension behaves as a client, it receives external events that trig the start of a new sessions.
- * When such event occurs, the extension calls sess_new with the appropriate parameters to create a new session.
- * It initializes an appstate_t structure with the data of this session and creates an association with sess_data_reg (%).
- * Then it creates a message (application-specific) to request authentication and/or authorization for the service
- * and the message is sent.
+ * RETURN VALUE:
+ *  0      	: The session timeout has been updated.
+ *  EINVAL 	: A parameter is invalid.
+ */
+int fd_sess_settimeout( struct session * session, const struct timespec * timeout );
+
+/*
+ * FUNCTION:	fd_sess_destroy
+ *
+ * PARAMETERS:
+ *  session	: Pointer to a session object.
+ *
+ * DESCRIPTION: 
+ *   Destroys a session an all associated data, if any.
+ * Equivalent to a session timeout expired, but the effect is immediate.
+ *
+ * RETURN VALUE:
+ *  0      	: The session no longer exists.
+ *  EINVAL 	: A parameter is invalid.
+ */
+int fd_sess_destroy ( struct session ** session );
+
+
+
+/*
+ * FUNCTION:	fd_sess_state_store
  *
- * Later, assuming that the extension has registered appropriate callbacks in the dispatcher module, when a message
- * is received, the extension can retrieve the state of the session with the sess_data_get function.
+ * PARAMETERS:
+ *  handler	: The handler with which the state is registered.
+ *  session	: The session object with which the state is registered.
+ *  state	: An application state (opaque data) to store with the session.
+ *
+ * DESCRIPTION: 
+ *  Stores an application state with a session. This state can later be retrieved
+ * with fd_sess_state_retrieve, or implicitly in the cleanup handler when the session
+ * is destroyed.
+ *
+ * RETURN VALUE:
+ *  0      	: The state has been stored.
+ *  EINVAL 	: A parameter is invalid.
+ *  EALREADY	: Data was already associated with this session and client.
+ *  ENOMEM	: Not enough memory to complete the operation
+ */
+int fd_sess_state_store ( struct session_handler * handler, struct session * session, session_state ** state ); 
+
+/*
+ * FUNCTION:	fd_sess_state_retrieve
  *
- * Finaly, when the extension decides to terminate the session (timer, or as result of a message exchange), it
- * calls sess_data_dereg in order to destroy the binding in the daemon. When last message refering this session is freed,
- * the session data is freed.
+ * PARAMETERS:
+ *  handler	: The handler with which the state was registered.
+ *  session	: The session object with which the state was registered.
+ *  state	: Location where the state must be saved if it is found.
+ *
+ * DESCRIPTION: 
+ *  Retrieves a state saved by fd_sess_state_store.
+ * After this function has been called, the state is no longer associated with 
+ * the session. A new call to fd_sess_state_store must be performed in order to
+ * store again the data with the session.
  *
- * (%) A this time, the extension must call sess_unlink in order to counter the effects of the sess_new function.
- * This allows to have the session destroyed when no more data is associated to it.
+ * RETURN VALUE:
+ *  0      	: *state is updated (NULL or points to the state if it was found).
+ *  EINVAL 	: A parameter is invalid.
+ */
+int fd_sess_state_retrieve ( struct session_handler * handler, struct session * session, session_state ** state ); 
 
 
+/* For debug */
+void fd_sess_dump(int level, struct session * session);
+void fd_sess_dump_hdl(int level, struct session_handler * handler);
+
 
 /*============================================================*/
 /*                         DISPATCH                           */
--- a/libfreeDiameter/CMakeLists.txt	Mon Aug 31 17:32:22 2009 +0900
+++ b/libfreeDiameter/CMakeLists.txt	Wed Sep 02 18:22:00 2009 +0900
@@ -12,6 +12,7 @@
 	dictionary.c
 	messages.c
 	mqueues.c
+	sessions.c
 	)
 
 # Build as a shared library
--- a/libfreeDiameter/init.c	Mon Aug 31 17:32:22 2009 +0900
+++ b/libfreeDiameter/init.c	Wed Sep 02 18:22:00 2009 +0900
@@ -46,8 +46,9 @@
 		return ret;
 	}
 	
-	/* Initialize the end-to-end id counter with random value as described in RFC3588 */
+	/* Initialize the modules that need it */
 	fd_msg_eteid_init();
+	CHECK_FCT( fd_sess_init() );
 	
 	return 0;
 }
--- a/libfreeDiameter/libfD.h	Mon Aug 31 17:32:22 2009 +0900
+++ b/libfreeDiameter/libfD.h	Wed Sep 02 18:22:00 2009 +0900
@@ -44,6 +44,7 @@
 /* Internal to the library */
 extern const char * type_base_name[];
 void fd_msg_eteid_init(void);
+int fd_sess_init(void);
 
 /* Iterator on the rules of a parent object */
 int fd_dict_iterate_rules ( struct dict_object *parent, void * data, int (*cb)(void *, struct dict_rule_data *) );
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libfreeDiameter/sessions.c	Wed Sep 02 18:22:00 2009 +0900
@@ -0,0 +1,671 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@nict.go.jp>							 *
+*													 *
+* Copyright (c) 2009, WIDE Project and NICT								 *
+* All rights reserved.											 *
+* 													 *
+* Redistribution and use of this software in source and binary forms, with or without modification, are  *
+* permitted provided that the following conditions are met:						 *
+* 													 *
+* * Redistributions of source code must retain the above 						 *
+*   copyright notice, this list of conditions and the 							 *
+*   following disclaimer.										 *
+*    													 *
+* * Redistributions in binary form must reproduce the above 						 *
+*   copyright notice, this list of conditions and the 							 *
+*   following disclaimer in the documentation and/or other						 *
+*   materials provided with the distribution.								 *
+* 													 *
+* * Neither the name of the WIDE Project or NICT nor the 						 *
+*   names of its contributors may be used to endorse or 						 *
+*   promote products derived from this software without 						 *
+*   specific prior written permission of WIDE Project and 						 *
+*   NICT.												 *
+* 													 *
+* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED *
+* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
+* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR *
+* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 	 *
+* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 	 *
+* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR *
+* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF   *
+* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.								 *
+*********************************************************************************************************/
+
+/* Sessions module.
+ * 
+ * Basic functionalities to help implementing User sessions state machines from RFC3588.
+ */
+
+#include "libfD.h"
+
+/*********************** Parameters **********************/
+
+/* Size of the hash table containing the session objects (pow of 2. ex: 6 => 2^6 = 64). must be between 0 and 31. */
+#ifndef SESS_HASH_SIZE
+#define SESS_HASH_SIZE	6
+#endif /* SESS_HASH_SIZE */
+
+/* Default lifetime of a session, in seconds. (31 days = 2678400 seconds) */
+#ifndef SESS_DEFAULT_LIFETIME
+#define SESS_DEFAULT_LIFETIME	2678400
+#endif /* SESS_DEFAULT_LIFETIME */
+
+/********************** /Parameters **********************/
+
+/* Eyescatchers definitions */
+#define SH_EYEC 0x53554AD1
+#define SD_EYEC 0x5355D474
+#define SI_EYEC 0x53551D
+
+/* Macro to check an object is valid */
+#define VALIDATE_SH( _obj ) ( ((_obj) != NULL) && ( ((struct session_handler *)(_obj))->eyec == SH_EYEC) )
+#define VALIDATE_SI( _obj ) ( ((_obj) != NULL) && ( ((struct session         *)(_obj))->eyec == SI_EYEC) )
+
+
+/* Handlers registered by users of the session module */
+struct session_handler {
+	int		  eyec;	/* An eye catcher also used to ensure the object is valid, must be SH_EYEC */
+	int		  id;	/* A unique integer to identify this handler */
+	void 		(*cleanup)(char *, session_state *); /* The cleanup function to be called for cleaning a state */
+};
+
+static int 		hdl_id = 0;				/* A global counter to initialize the id field */
+static pthread_mutex_t	hdl_lock = PTHREAD_MUTEX_INITIALIZER;	/* lock to protect hdl_id; we could use atomic operations otherwise (less portable) */
+
+
+/* Data structures linked from the sessions, containing the applications states */
+struct state {
+	int			 eyec;	/* Must be SD_EYEC */
+	session_state		*state;	/* The state registered by the application, never NULL (or the whole object is deleted) */
+	struct fd_list		 chain;	/* Chaining in the list of session's states ordered by hdl->id */
+	union {
+		struct session_handler	*hdl;	/* The handler for which this state was registered */
+		char 			*sid;	/* For deleted state, the sid of the session it belong to */
+	};
+};
+
+/* Session object, one for each value of Session-Id AVP */
+struct session {
+	int 		eyec;	/* Eyecatcher, SI_EYEC */
+	
+	char *		sid;	/* The \0-terminated Session-Id */
+	uint32_t	hash;	/* computed hash of sid */
+	struct fd_list	chain_h;/* chaining in the hash table of sessions. */
+	
+	struct timespec	timeout;/* Timeout date for the session */
+	struct fd_list	expire;	/* List of expiring sessions, ordered by timeouts. */
+	
+	pthread_mutex_t stlock;	/* A lock to protect the list of states associated with this session */
+	struct fd_list	states;	/* Sentinel for the list of states of this session. */
+};
+
+/* Sessions hash table, to allow fast sid to session retrieval */
+static struct {
+	struct fd_list	sentinel;	/* sentinel element for this sublist */
+	pthread_mutex_t lock;		/* the mutex for this sublist */
+} sess_hash [ 1 << SESS_HASH_SIZE ] ;
+#define H_MASK( __hash ) ((__hash) & (( 1 << SESS_HASH_SIZE ) - 1))
+#define H_LIST( _hash ) (&(sess_hash[H_MASK(_hash)].sentinel))
+#define H_LOCK( _hash ) (&(sess_hash[H_MASK(_hash)].lock    ))
+
+/* The following are used to generate sid values that are eternaly unique */
+static uint32_t   	sid_h;	/* initialized to the current time in fd_sess_init */
+static uint32_t   	sid_l;	/* incremented each time a session id is created -- could use atomic operation probably */
+static pthread_mutex_t 	sid_lock = PTHREAD_MUTEX_INITIALIZER;
+
+/* Expiring sessions management */
+static struct fd_list	exp_sentinel;	/* list of sessions ordered by their timeout date */
+static pthread_mutex_t	exp_lock = PTHREAD_MUTEX_INITIALIZER;	/* lock protecting the list. */
+static pthread_cond_t	exp_cond = PTHREAD_COND_INITIALIZER;	/* condvar used by the expiry mecahinsm. */
+static pthread_t	exp_thr; 	/* The expiry thread that handles cleanup of expired sessions */
+
+/* Hierarchy of the locks, to avoid deadlocks:
+ *  hash lock > state lock > expiry lock
+ * i.e. state lock can be taken while holding the hash lock, but not while holding the expiry lock.
+ * As well, the hash lock cannot be taken while holding a state lock.
+ */
+
+/********************************************************************************************************/
+
+/* Initialize a session object. It is not linked now. sid must be already alloc'ed. */
+static struct session * new_session(char * sid, size_t sidlen)
+{
+	struct session * sess;
+	
+	TRACE_ENTRY("%p %d", sid, sidlen);
+	CHECK_PARAMS_DO( sid && sidlen, return NULL );
+	
+	CHECK_MALLOC_DO( sess = malloc(sizeof(struct session)), return NULL );
+	memset(sess, 0, sizeof(struct session));
+	
+	sess->eyec = SI_EYEC;
+	
+	sess->sid  = sid;
+	sess->hash = fd_hash(sid, sidlen);
+	fd_list_init(&sess->chain_h, sess);
+	
+	CHECK_SYS_DO( clock_gettime(CLOCK_REALTIME, &sess->timeout), return NULL );
+	sess->timeout.tv_sec += SESS_DEFAULT_LIFETIME;
+	fd_list_init(&sess->expire, sess);
+	
+	CHECK_POSIX_DO( pthread_mutex_init(&sess->stlock, NULL), return NULL );
+	fd_list_init(&sess->states, sess);
+	
+	return sess;
+}
+	
+
+
+
+/* The expiry thread */
+static void * exp_fct(void * arg)
+{
+	fd_log_threadname ( "Session/expire" );
+	TRACE_ENTRY( "" );
+	
+	CHECK_POSIX_DO( pthread_mutex_lock(&exp_lock),  goto error );
+	pthread_cleanup_push( fd_cleanup_mutex, &exp_lock );
+	
+	do {
+		struct timespec	now;
+		struct session * first;
+		
+		/* Check if there are expiring sessions available */
+		if (FD_IS_LIST_EMPTY(&exp_sentinel)) {
+			/* Just wait for a change or cancelation */
+			CHECK_POSIX_DO( pthread_cond_wait( &exp_cond, &exp_lock ), goto error );
+			/* Restart the loop on wakeup */
+			continue;
+		}
+		
+		/* Get the pointer to the session that expires first */
+		first = (struct session *)(exp_sentinel.next->o);
+		ASSERT( VALIDATE_SI(first) );
+		
+		/* Get the current time */
+		CHECK_SYS_DO(  clock_gettime(CLOCK_REALTIME, &now),  goto error  );
+
+		/* If first session is not expired, we just wait until it happens */
+		if ( TS_IS_INFERIOR( &now, &first->timeout ) ) {
+			
+			CHECK_POSIX_DO2(  pthread_cond_timedwait( &exp_cond, &exp_lock, &first->timeout ),  
+					ETIMEDOUT, /* ETIMEDOUT is a normal error, continue */,
+					/* on other error, */ goto error );
+	
+			/* on wakeup, loop */
+			continue;
+		}
+		
+		/* Now, the first session in the list is expired; destroy it */
+		CHECK_POSIX_DO( pthread_mutex_unlock(&exp_lock),  goto error );
+		CHECK_FCT_DO( fd_sess_destroy( &first ), goto error );
+		CHECK_POSIX_DO( pthread_mutex_lock(&exp_lock),  goto error );
+		
+	} while (1);
+	
+	pthread_cleanup_pop( 1 );
+error:
+	TRACE_DEBUG(INFO, "An error occurred in session module! Expiry thread is terminating...");
+	ASSERT(0);
+	return NULL;
+}
+	
+	
+
+/********************************************************************************************************/
+
+/* Initialize the session module */
+int fd_sess_init(void)
+{
+	int i;
+	
+	TRACE_ENTRY( "" );
+	
+	/* Initialize the global counters */
+	sid_h = (uint32_t) time(NULL);
+	sid_l = 0;
+	
+	/* Initialize the hash table */
+	for (i = 0; i < sizeof(sess_hash) / sizeof(sess_hash[0]); i++) {
+		fd_list_init( &sess_hash[i].sentinel, NULL );
+		CHECK_POSIX(  pthread_mutex_init(&sess_hash[i].lock, NULL)  );
+	}
+	
+	/* Initialize expiry management */
+	fd_list_init( &exp_sentinel, NULL );
+	CHECK_POSIX(  pthread_create(&exp_thr, NULL, exp_fct, NULL)  );
+	
+	return 0;
+}
+
+/* Create a new handler */
+int fd_sess_handler_create_int ( struct session_handler ** handler, void (*cleanup)(char * sid, session_state * state) )
+{
+	struct session_handler *new;
+	
+	TRACE_ENTRY("%p %p", handler, cleanup);
+	
+	CHECK_PARAMS( handler && cleanup );
+	
+	CHECK_MALLOC( new = malloc(sizeof(struct session_handler)) );
+	memset(new, 0, sizeof(struct session_handler));
+	
+	CHECK_POSIX( pthread_mutex_lock(&hdl_lock) );
+	new->id = ++hdl_id;
+	CHECK_POSIX( pthread_mutex_unlock(&hdl_lock) );
+	
+	new->eyec = SH_EYEC;
+	new->cleanup = cleanup;
+	
+	*handler = new;
+	return 0;
+}
+
+/* Destroy a handler, and all states attached to this handler. This operation is very slow but we don't care since it's rarely used. 
+ * Note that it's better to call this function after all sessions have been deleted... */
+int fd_sess_handler_destroy ( struct session_handler ** handler )
+{
+	struct session_handler * del;
+	struct fd_list deleted_states; /* Save the list of states to be cleaned up. We do it after finding them to avoid deadlocks. the "o" field becomes a copy of the sid. */
+	int i;
+	
+	TRACE_ENTRY("%p", handler);
+	CHECK_PARAMS( handler && VALIDATE_SH(*handler) );
+	
+	del = *handler;
+	*handler = NULL;
+	fd_list_init(&deleted_states, NULL);
+	
+	del->eyec = 0xdead; /* The handler is not valid anymore for any other operation */
+	
+	/* Now find all sessions with data registered for this handler, and move this data to the deleted_states list. */
+	for (i = 0; i < sizeof(sess_hash) / sizeof(sess_hash[0]); i++) {
+		struct fd_list * li_si;
+		CHECK_POSIX(  pthread_mutex_lock(&sess_hash[i].lock)  );
+		
+		for (li_si = sess_hash[i].sentinel.next; li_si != &sess_hash[i].sentinel; li_si = li_si->next) {
+			struct fd_list * li_st;
+			struct session * sess = (struct session *)(li_si->o);
+			CHECK_POSIX(  pthread_mutex_lock(&sess->stlock)  );
+			for (li_st = sess->states.next; li_st != &sess->states; li_st = li_st->next) {
+				struct state * st = (struct state *)(li_st->o);
+				char * sid_cpy;
+				/* The list is ordered */
+				if (st->hdl->id < del->id)
+					continue;
+				if (st->hdl->id > del->id)
+					break;
+				/* This state belongs to the handler we are deleting, move the item to the deleted_states list */
+				fd_list_unlink(&st->chain);
+				CHECK_MALLOC( st->sid = strdup(sess->sid) );
+				fd_list_insert_before(&deleted_states, &st->chain);
+			}
+			CHECK_POSIX(  pthread_mutex_unlock(&sess->stlock)  );
+		}
+		CHECK_POSIX(  pthread_mutex_unlock(&sess_hash[i].lock)  );
+	}
+	
+	/* Now, delete all states after calling their cleanup handler */
+	while (!FD_IS_LIST_EMPTY(&deleted_states)) {
+		struct state * st = (struct state *)(deleted_states.next->o);
+		TRACE_DEBUG(FULL, "Calling cleanup handler for session '%s' and data %p", st->sid, st->state);
+		(*del->cleanup)(st->sid, st->state);
+		free(st->sid);
+		fd_list_unlink(&st->chain);
+		free(st);
+	}
+	
+	return 0;
+}
+
+
+
+/* Create a new session object with the default timeout value, and link it */
+int fd_sess_new ( struct session ** session, char * diamId, char * opt, size_t optlen )
+{
+	char * sid = NULL;
+	size_t sidlen;
+	uint32_t hash;
+	struct session * sess;
+	struct fd_list * li;
+	int found = 0;
+	
+	TRACE_ENTRY("%p %p %p %d", session, diamId, opt, optlen);
+	CHECK_PARAMS( session && (diamId || opt) );
+	
+	/* Ok, first create the identifier for the string */
+	if (diamId == NULL) {
+		/* opt is the full string */
+		if (optlen) {
+			CHECK_MALLOC( sid = malloc(optlen + 1) );
+			strncpy(sid, opt, optlen);
+			sid[optlen] = '\0';
+			sidlen = optlen;
+		} else {
+			CHECK_MALLOC( sid = strdup(opt) );
+			sidlen = strlen(sid);
+		}
+	} else {
+		/* "<diamId>;<high32>;<low32>[;opt]" */
+		sidlen = strlen(diamId);
+		sidlen += 22; /* max size of ';<high32>;<low32>' */
+		if (opt)
+			sidlen += 1 + (optlen ?: strlen(opt)) ;
+		sidlen++; /* space for the final \0 also */
+		CHECK_MALLOC( sid = malloc(sidlen) );
+		CHECK_POSIX( pthread_mutex_lock(&sid_lock) );
+		if ( ++sid_l == 0 ) /* overflow */
+			++sid_h;
+		
+		if (opt) {
+			if (optlen)
+				sidlen = snprintf(sid, sidlen, "%s;%u;%u;%*.*s", diamId, sid_h, sid_l, optlen, optlen, opt);
+			else
+				sidlen = snprintf(sid, sidlen, "%s;%u;%u;%s", diamId, sid_h, sid_l, opt);
+		} else {
+			sidlen = snprintf(sid, sidlen, "%s;%u;%u", diamId, sid_h, sid_l);
+		}
+	
+		CHECK_POSIX( pthread_mutex_unlock(&sid_lock) );
+	}
+	
+	/* Initialize the session object now, to spend less time inside locked section later. 
+	 * Cons: we malloc then free if there is already a session with same SID; we could malloc later to avoid this. */
+	CHECK_MALLOC( sess = new_session(sid, sidlen) );
+	
+	/* Now find the place to add this object in the hash table. */
+	CHECK_POSIX( pthread_mutex_lock( H_LOCK(sess->hash) ) );
+	for (li = H_LIST(sess->hash)->next; li != H_LIST(sess->hash); li = li->next) {
+		int cmp;
+		struct session * s = (struct session *)(li->o);
+		
+		/* The list is ordered by hash and sid (in case of collisions) */
+		if (s->hash < sess->hash)
+			continue;
+		if (s->hash > sess->hash)
+			break;
+		
+		cmp = strcasecmp(s->sid, sess->sid);
+		if (cmp < 0)
+			continue;
+		if (cmp > 0)
+			break;
+		
+		/* A session with the same sid was already in the hash table */
+		found = 1;
+		*session = s;
+		break;
+	}
+	
+	/* If the session did not exist, we can add it into the hash table */
+	if (!found) {
+		fd_list_insert_before(li, &sess->chain_h);
+		
+		/* We must also insert in the expiry list */
+		CHECK_POSIX( pthread_mutex_lock( &exp_lock ) );
+		
+		/* Find the position in that list. We take it in reverse order */
+		for (li = exp_sentinel.prev; li != &exp_sentinel; li = li->prev) {
+			struct session * s = (struct session *)(li->o);
+			
+			if (TS_IS_INFERIOR( &s->timeout, &sess->timeout ) )
+				break;
+			
+			continue;
+		}
+		fd_list_insert_after( li, &sess->expire );
+		
+		/* We added a new expiring element, we must signal */
+		CHECK_POSIX( pthread_cond_signal(&exp_cond) );
+		
+		/* We're done */
+		CHECK_POSIX( pthread_mutex_unlock( &exp_lock ) );
+	}
+	
+	CHECK_POSIX( pthread_mutex_unlock( H_LOCK(sess->hash) ) );
+	
+	/* If a session already existed, we must destroy the new element */
+	if (found) {
+		CHECK_FCT( fd_sess_destroy( &sess ) ); /* we could avoid locking this time for optimization */
+		return EALREADY;
+	}
+	
+	*session = sess;
+	return 0;
+}
+
+/* Find or create a session */
+int fd_sess_fromsid ( char * sid, size_t len, struct session ** session, int * new)
+{
+	int ret;
+	
+	TRACE_ENTRY("%p %d %p %p", sid, len, session, new);
+	CHECK_PARAMS( sid && session );
+	
+	/* All the work is done in sess_new */
+	ret = fd_sess_new ( session, NULL, sid, len );
+	switch (ret) {
+		case 0:
+		case EALREADY:
+			break;
+		
+		default:
+			CHECK_FCT(ret);
+	}
+	
+	if (new)
+		*new = ret ? 0 : 1;
+	
+	return 0;
+}
+
+/* Get the sid of a session */
+int fd_sess_getsid ( struct session * session, char ** sid )
+{
+	TRACE_ENTRY("%p %p", session, sid);
+	
+	CHECK_PARAMS( VALIDATE_SI(session) && sid );
+	
+	*sid = session->sid;
+	
+	return 0;
+}
+
+/* Change the timeout value of a session */
+int fd_sess_settimeout( struct session * session, const struct timespec * timeout )
+{
+	struct fd_list * li;
+	
+	TRACE_ENTRY("%p %p", session, timeout);
+	CHECK_PARAMS( VALIDATE_SI(session) && timeout );
+	
+	/* Lock -- do we need to lock the hash table as well? I don't think so... */
+	CHECK_POSIX( pthread_mutex_lock( &exp_lock ) );
+	
+	/* Update the timeout */
+	fd_list_unlink(&session->expire);
+	memcpy(&session->timeout, timeout, sizeof(struct timespec));
+	
+	/* Find the new position in expire list. We take it in normal order */
+	for (li = exp_sentinel.next; li != &exp_sentinel; li = li->next) {
+		struct session * s = (struct session *)(li->o);
+
+		if (TS_IS_INFERIOR( &s->timeout, &session->timeout ) )
+			continue;
+
+		break;
+	}
+	fd_list_insert_before( li, &session->expire );
+
+	/* We added a new expiring element, we must signal */
+	CHECK_POSIX( pthread_cond_signal(&exp_cond) );
+
+	/* We're done */
+	CHECK_POSIX( pthread_mutex_unlock( &exp_lock ) );
+	
+	return 0;
+}
+
+/* Destroy a session immediatly */
+int fd_sess_destroy ( struct session ** session )
+{
+	struct session * sess;
+	
+	TRACE_ENTRY("%p", session);
+	CHECK_PARAMS( session && VALIDATE_SI(*session) );
+	
+	sess = *session;
+	*session = NULL;
+	
+	/* Unlink and invalidate */
+	CHECK_FCT( pthread_mutex_lock( H_LOCK(sess->hash) ) );
+	CHECK_FCT( pthread_mutex_lock( &exp_lock ) );
+	fd_list_unlink( &sess->chain_h );
+	fd_list_unlink( &sess->expire ); /* no need to signal the condition here */
+	sess->eyec = 0xdead;
+	CHECK_FCT( pthread_mutex_unlock( &exp_lock ) );
+	CHECK_FCT( pthread_mutex_unlock( H_LOCK(sess->hash) ) );
+	
+	/* Now destroy all states associated -- we don't take the lock since nobody can access this session anymore (in theory) */
+	while (!FD_IS_LIST_EMPTY(&sess->states)) {
+		struct state * st = (struct state *)(sess->states.next->o);
+		fd_list_unlink(&st->chain);
+		TRACE_DEBUG(FULL, "Calling handler %p cleanup for state registered with session '%s'", st->hdl, sess->sid);
+		(*st->hdl->cleanup)(sess->sid, st->state);
+		free(st);
+	}
+	
+	/* Finally, destroy the session itself */
+	free(sess->sid);
+	free(sess);
+	
+	return 0;
+}
+
+
+
+/* Save a state information with a session */
+int fd_sess_state_store ( struct session_handler * handler, struct session * session, session_state ** state )
+{
+	struct state *new;
+	struct fd_list * li;
+	int already = 0;
+	
+	TRACE_ENTRY("%p %p %p", handler, session, state);
+	CHECK_PARAMS( handler && VALIDATE_SH(handler) && session && VALIDATE_SI(session) && state );
+	
+	/* Lock the session state list */
+	CHECK_POSIX( pthread_mutex_lock(&session->stlock) );
+			
+	/* Create the new state object */
+	CHECK_MALLOC(new = malloc(sizeof(struct state)) );
+	memset(new, 0, sizeof(struct state));
+	
+	new->eyec = SD_EYEC;
+	new->state= *state;
+	fd_list_init(&new->chain, new);
+	new->hdl = handler;
+	
+	/* find place for this state in the list */
+	for (li = session->states.next; li != &session->states; li = li->next) {
+		struct state * st = (struct state *)(li->o);
+		/* The list is ordered by handler's id */
+		if (st->hdl->id < handler->id)
+			continue;
+		
+		if (st->hdl->id == handler->id) {
+			TRACE_DEBUG(INFO, "A state was already stored for session '%s' and handler '%p', at location %p", session->sid, st->hdl, st->state);
+			already = 1;
+		}
+		
+		break;
+	}
+	
+	if (!already) {
+		fd_list_insert_before(li, &new->chain);
+		*state = NULL;
+	} else {
+		free(new);
+	}
+	
+	CHECK_POSIX( pthread_mutex_unlock(&session->stlock) );
+	
+	return already ? EALREADY : 0;
+}
+
+/* Get the data back */
+int fd_sess_state_retrieve ( struct session_handler * handler, struct session * session, session_state ** state )
+{
+	struct fd_list * li;
+	struct state * st = NULL;
+	
+	TRACE_ENTRY("%p %p %p", handler, session, state);
+	CHECK_PARAMS( handler && VALIDATE_SH(handler) && session && VALIDATE_SI(session) && state );
+	
+	*state = NULL;
+	
+	/* Lock the session state list */
+	CHECK_POSIX( pthread_mutex_lock(&session->stlock) );
+	
+	/* find the state in the list */
+	for (li = session->states.next; li != &session->states; li = li->next) {
+		st = (struct state *)(li->o);
+		
+		/* The list is ordered by handler's id */
+		if (st->hdl->id > handler->id)
+			break;
+	}
+	
+	/* If we found the state */
+	if (st && (st->hdl == handler)) {
+		fd_list_unlink(&st->chain);
+		*state = st->state;
+		free(st);
+	}
+	
+	CHECK_POSIX( pthread_mutex_unlock(&session->stlock) );
+	
+	return 0;
+}
+
+
+
+/* Dump functions */
+void fd_sess_dump(int level, struct session * session)
+{
+	struct fd_list * li;
+	if (!TRACE_BOOL(level))
+		return;
+	
+	fd_log_debug("Session @%p:\n", session);
+	if (!VALIDATE_SI(session)) {
+		fd_log_debug("  Invalid session object\n");
+		return;
+	}
+		
+	fd_log_debug("  sid '%s', hash %x\n", session->sid, session->hash);
+	fd_log_debug("  timeout %d.%09d\n", session->timeout.tv_sec, session->timeout.tv_nsec);
+	
+	CHECK_POSIX_DO( pthread_mutex_lock(&session->stlock), /* ignore */ );
+	for (li = session->states.next; li != &session->states; li = li->next) {
+		struct state * st = (struct state *)(li->o);
+		fd_log_debug("    handler %d registered data %p\n", st->hdl->id, st->state);
+	}
+	CHECK_POSIX_DO( pthread_mutex_unlock(&session->stlock), /* ignore */ );
+}
+
+void fd_sess_dump_hdl(int level, struct session_handler * handler)
+{
+	if (!TRACE_BOOL(level))
+		return;
+	
+	fd_log_debug("Handler @%p:\n", handler);
+	if (!VALIDATE_SH(handler)) {
+		fd_log_debug("  Invalid session handler object\n");
+		return;
+	}
+		
+	fd_log_debug("  id %d, cleanup %p\n", handler->id, handler->cleanup);
+}	
"Welcome to our mercurial repository"