Index: freeDiameter/CMakeLists.txt
===================================================================
--- freeDiameter/CMakeLists.txt	(revision 1)
+++ freeDiameter/CMakeLists.txt	(revision 2)
@@ -18,4 +18,7 @@
 TARGET_LINK_LIBRARIES(freeDiameterd libfreeDiameter ${FD_LIBS})
 
+# Save the list of files, if needed
+SET(FD_COMMON_SRC ${FD_COMMON_SRC} PARENT_SCOPE)
+
 # The unary tests directory
 OPTION(SKIP_TESTS "Skip compilation of the tests?" OFF)
Index: freeDiameter/main.c
===================================================================
--- freeDiameter/main.c	(revision 1)
+++ freeDiameter/main.c	(revision 3)
@@ -51,7 +51,4 @@
 	CHECK_FCT( fd_dict_base_protocol(fd_g_dict) );
 	
-	/* For debug */
-	fd_dict_dump(fd_g_dict);
-	
 	TRACE_DEBUG(INFO, "freeDiameter daemon initialized.");
 	
Index: freeDiameter/tests/CMakeLists.txt
===================================================================
--- freeDiameter/tests/CMakeLists.txt	(revision 1)
+++ freeDiameter/tests/CMakeLists.txt	(revision 3)
@@ -12,7 +12,9 @@
 # List the test cases
 SET(TEST_LIST
+	testlist
 	testdict
 	testmesg
 	testmq
+	testsess
 )
 
@@ -20,8 +22,9 @@
 # Some parameters for the tests
 
-# Add this flag to add some debug information in the tests themselves
 ADD_DEFINITIONS(-DTEST_DEBUG)
+ADD_DEFINITIONS(-DTRACE_LEVEL=NONE)
 
 INCLUDE_DIRECTORIES( ".." )
+INCLUDE_DIRECTORIES( "../../libfreeDiameter" )
 
 SET(TEST_COMMON_SRC "")
@@ -35,4 +38,8 @@
 # ENDFOREACH(SRC_FILE)
 
+FOREACH( SRC_FILE ${LFD_SRC})
+   SET(TEST_COMMON_SRC ${TEST_COMMON_SRC} "../../libfreeDiameter/${SRC_FILE}")
+ENDFOREACH(SRC_FILE)
+
 # Create an archive with the daemon common files (all but main)
 ADD_LIBRARY(fDcore STATIC ${TEST_COMMON_SRC})
@@ -43,5 +50,5 @@
 FOREACH( TEST ${TEST_LIST} )
    ADD_EXECUTABLE(${TEST} ${TEST}.c tests.h)
-   TARGET_LINK_LIBRARIES(${TEST} libfreeDiameter fDcore ${FD_LIBS})
+   TARGET_LINK_LIBRARIES(${TEST} fDcore ${FD_LIBS})
    ADD_TEST(${TEST} ${EXECUTABLE_OUTPUT_PATH}/${TEST})
 ENDFOREACH( TEST )
Index: freeDiameter/tests/testlist.c
===================================================================
--- freeDiameter/tests/testlist.c	(revision 3)
+++ freeDiameter/tests/testlist.c	(revision 3)
@@ -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();
+} 
+	
Index: freeDiameter/tests/testmesg.c
===================================================================
--- freeDiameter/tests/testmesg.c	(revision 1)
+++ freeDiameter/tests/testmesg.c	(revision 2)
@@ -436,5 +436,5 @@
 				memset(&buf, 0, sizeof(buf)); /* Test that the OS value is really copied */
 				CHECK( 0, fd_msg_update_length ( avpi ) );
-				#if 1
+				#if 0
 				fd_log_debug("AVP octet string, 'This\\0 is a b...'\n");
 				fd_msg_dump_one(0, avpi);
Index: freeDiameter/tests/tests.h
===================================================================
--- freeDiameter/tests/tests.h	(revision 1)
+++ freeDiameter/tests/tests.h	(revision 3)
@@ -52,6 +52,4 @@
 #endif /* TEST_TIMEOUT */
 
-static int test_verbosity = 0;
-
 /* Standard includes */
 #include <getopt.h>
@@ -76,7 +74,9 @@
 }
 
+static int test_verbo = 0;
+
 /* Define the standard check routines */
 #define CHECK( _val, _assert ){				\
-	if (test_verbosity > 0) {			\
+	if (test_verbo > 0) {				\
 		fprintf(stderr,				\
 			"%s:%-4d: CHECK( " #_assert " == "\
@@ -98,5 +98,5 @@
 /* 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) );		\
@@ -111,9 +111,9 @@
 		switch (c) {
 			case 'd':	/* Increase verbosity of debug messages.  */
-				test_verbosity++;
+				test_verbo++;
 				break;
 				
-			case 'q':	/* Decrease verbosity then remove debug messages.  */
-				test_verbosity--;
+			case 'q':	/* Decrease verbosity.  */
+				test_verbo--;
 				break;
 			
@@ -126,4 +126,5 @@
 		}
 	}
+	fd_g_debug_lvl = (test_verbo > 0) ? (test_verbo - 1) : 0;
 	if (!no_timeout)
 		alarm(TEST_TIMEOUT);
Index: freeDiameter/tests/testsess.c
===================================================================
--- freeDiameter/tests/testsess.c	(revision 3)
+++ freeDiameter/tests/testsess.c	(revision 3)
@@ -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();
+} 
+	
Index: include/freeDiameter/libfreeDiameter.h
===================================================================
--- include/freeDiameter/libfreeDiameter.h	(revision 1)
+++ include/freeDiameter/libfreeDiameter.h	(revision 3)
@@ -122,5 +122,4 @@
 char * fd_log_time ( char * buf, size_t len );
 
-/************************** DEBUG MACROS ************************************/
 
 /* levels definitions */
@@ -137,14 +136,11 @@
 #endif /* TRACE_LEVEL */
 
-/* The level of the file being compiled */
+/* The level of the file being compiled. */
 static int local_debug_level = TRACE_LEVEL;
 
-/* helper macros (pre-processor hacks) */
-#define __str( arg )  #arg
-#define _stringize( arg ) __str( arg )
-#define __agr( arg1, arg2 ) arg1 ## arg2
-#define _aggregate( arg1, arg2 ) __agr( arg1, arg2 )
-
-/* Some portability tricks to get nice function name in __PRETTY_FUNCTION__ */
+/* A global level, changed by configuration or cmd line for example. default is 0. */
+extern int fd_g_debug_lvl;
+
+/* Some portability code to get nice function name in __PRETTY_FUNCTION__ */
 #if __STDC_VERSION__ < 199901L
 # if __GNUC__ >= 2
@@ -159,7 +155,7 @@
 
 /* Boolean for tracing at a certain level */
-#define TRACE_BOOL(_level_) ( (_level_) <= local_debug_level )
-
-/* The general debug macro, each call results in two lines of debug messages */
+#define TRACE_BOOL(_level_) ( (_level_) <= local_debug_level + fd_g_debug_lvl )
+
+/* The general debug macro, each call results in two lines of debug messages (change the macro for more compact output) */
 #define TRACE_DEBUG(level,format,args... ) {											\
 	if ( TRACE_BOOL(level) ) {												\
@@ -173,13 +169,13 @@
 }
 
-/* Helper for function entry */
+/* Helper for function entry -- for very detailed trace of the execution */
 #define TRACE_ENTRY(_format,_args... ) \
 	TRACE_DEBUG(FCTS, "->%s (" #_args ") = (" _format ") >", __PRETTY_FUNCTION__, ##_args );
 
-/* Helper for debugging by adding traces */
+/* Helper for debugging by adding traces -- for debuging a specific location of the code */
 #define TRACE_HERE()	\
-	TRACE_DEBUG(INFO, " -- debug checkpoint -- ");
-
-/* Helper for tracing the CHECK_* macros bellow */
+	TRACE_DEBUG(NONE, " -- debug checkpoint -- ");
+
+/* Helper for tracing the CHECK_* macros bellow -- very very verbose code execution! */
 #define TRACE_DEBUG_ALL( str ) 	\
 	TRACE_DEBUG(CALL, str );
@@ -284,5 +280,14 @@
 }
 
-/****************************** 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 */
@@ -337,7 +342,4 @@
 	(((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. */
@@ -354,8 +356,8 @@
 #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 )
 
@@ -365,9 +367,4 @@
 	  || ((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':'-'
 
 /*============================================================*/
@@ -403,5 +400,5 @@
 }
 
-/* Cleanups for cancellation (all threads should be safely cancelable!) */
+/* Cleanups for cancellation (all threads should be safely cancelable...) */
 static __inline__ void fd_cleanup_mutex( void * mutex )
 {
@@ -591,5 +588,5 @@
 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:
@@ -598,8 +595,8 @@
 	 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:
  {
@@ -607,10 +604,10 @@
 	 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:
  {
@@ -618,6 +615,6 @@
 	 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 );
  }
@@ -660,5 +657,5 @@
 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:
  {
@@ -675,9 +672,9 @@
 	 };
 	
-	 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
  {
@@ -685,9 +682,9 @@
 	 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:
  {
@@ -695,6 +692,6 @@
 	 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 );
  }
@@ -797,5 +794,5 @@
  *  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. 
  
@@ -811,13 +808,13 @@
 		 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);
  }
  
@@ -858,5 +855,5 @@
  *  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":
@@ -881,11 +878,11 @@
 		 .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:
  {
@@ -899,8 +896,8 @@
 	 	};
 	 
-	 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:
  {
@@ -915,6 +912,6 @@
 	 	};
 	 
-	 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 );
  }
@@ -944,4 +941,7 @@
 #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 */
@@ -981,5 +981,5 @@
 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:
  {
@@ -1006,13 +1006,13 @@
 	
  	 -- 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
  {
@@ -1026,11 +1026,11 @@
 	 	};
 	 
-	 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:
  {
@@ -1038,6 +1038,6 @@
 	 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 );
  }
@@ -1068,4 +1068,8 @@
 #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 {
@@ -1095,5 +1099,5 @@
 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:
  {
@@ -1108,13 +1112,13 @@
 	 };
 	
-	 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
  {
@@ -1122,9 +1126,9 @@
 	 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:
  {
@@ -1133,7 +1137,7 @@
 	 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 );
  }
@@ -1186,5 +1190,5 @@
 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.
  {
@@ -1201,12 +1205,12 @@
 	
 	-- 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.
@@ -1216,11 +1220,11 @@
 	
 	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.
 
 */
@@ -1274,6 +1278,258 @@
 #define ER_DIAMETER_REDIRECT_INDICATION		3006
 
-/* 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 *) );
+
+/*============================================================*/
+/*                         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 );
+
+
+
+/*
+ * 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.
+ *
+ * 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.
+ *
+ * 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!
+ *
+ * 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.
+ *
+ * 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.
+ *
+ *   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.
+ *
+ * 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
+ *
+ * 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
+ *
+ * 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.
+ *
+ * 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                           */
+/*============================================================*/
+
+/* The dispatch process consists in passing a message to some handlers
+ (typically provided by extensions) based on its content (app id, cmd code...) */
+
+/* The dispatch module has two main roles:
+ *  - help determine if a message can be handled locally (during the routing step)
+ *  - pass the message to the callback(s) that will handle it (during the dispatch step)
+ *
+ * These are the possibilities for registering a callback:
+ *
+ * -> For All messages.
+ *  This callback is called for all messages that are handled locally. This should be used only
+ *  internally by the daemon, or for debug purpose.
+ *
+ * -> by AVP value (constants).
+ *  This callback will be called when a message is received and contains a certain AVP with a specified value.
+ *
+ * -> by AVP.
+ *  This callback will be called when the received message contains a certain AVP.
+ *
+ * -> by command-code.
+ *  This callback will be called when the message is a specific command.
+ *
+ * -> by application.
+ *  This callback will be called when the message has a specific application-id.
+ *
+ * ( by vendor: would this be useful? it may be added later)
+ *
+ * Note that several criteria may be selected at the same time, for example command-code AND application id.
+ *
+ * When a callback is called, it receives the message as parameter, and eventually a pointer to 
+ * the AVP in the message when this is appropriate.
+ *
+ * The callback must process the message, and eventually create an answer to it. See the definition
+ * bellow for more information.
+ *
+ * If no callback has handled the message, a default handler will be called with the effect of
+ * requeuing the message for forwarding on the network to another peer (for requests, if possible), or 
+ * discarding the message (for answers).
+ */
+
+
 
 
Index: libfreeDiameter/CMakeLists.txt
===================================================================
--- libfreeDiameter/CMakeLists.txt	(revision 1)
+++ libfreeDiameter/CMakeLists.txt	(revision 3)
@@ -13,4 +13,5 @@
 	messages.c
 	mqueues.c
+	sessions.c
 	)
 
@@ -24,2 +25,5 @@
 TARGET_LINK_LIBRARIES(libfreeDiameter ${FD_LIBS})
 
+# Save the list of files for testcases in the daemon's directory
+SET(LFD_SRC ${LFD_SRC} PARENT_SCOPE)
+
Index: libfreeDiameter/init.c
===================================================================
--- libfreeDiameter/init.c	(revision 1)
+++ libfreeDiameter/init.c	(revision 3)
@@ -47,6 +47,7 @@
 	}
 	
-	/* 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;
Index: libfreeDiameter/libfD.h
===================================================================
--- libfreeDiameter/libfD.h	(revision 1)
+++ libfreeDiameter/libfD.h	(revision 3)
@@ -45,4 +45,9 @@
 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 *) );
+
 
 
Index: libfreeDiameter/log.c
===================================================================
--- libfreeDiameter/log.c	(revision 1)
+++ libfreeDiameter/log.c	(revision 2)
@@ -40,4 +40,5 @@
 pthread_mutex_t fd_log_lock = PTHREAD_MUTEX_INITIALIZER;
 pthread_key_t	fd_log_thname;
+int fd_g_debug_lvl = 0;
 
 /* Log a debug message */
Index: libfreeDiameter/sessions.c
===================================================================
--- libfreeDiameter/sessions.c	(revision 3)
+++ libfreeDiameter/sessions.c	(revision 3)
@@ -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);
+}	
