Initial commit

Change-Id: I6a4444e3c193dae437cd7929f4c39aba7b749efa
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
new file mode 100644
index 0000000..eaaaa94
--- /dev/null
+++ b/tests/CMakeLists.txt
@@ -0,0 +1,117 @@
+# Test directory
+PROJECT("freeDiameter tests" C)
+
+# give the possibility to configure the timeout duration for the tests
+OPTION(TEST_TIMEOUT "Timeout for the tests, in seconds (default: 120)?")
+IF(TEST_TIMEOUT)
+	ADD_DEFINITIONS(-DTEST_TIMEOUT=${TEST_TIMEOUT})
+ENDIF(TEST_TIMEOUT)
+
+# default command line for the tests
+OPTION(OPT_TEST_ARGUMENTS "Specify the default arguments for the tests" OFF)
+IF(OPT_TEST_ARGUMENTS)
+	SET(TEST_ARGUMENTS ${OPT_TEST_ARGUMENTS})
+ELSE(OPT_TEST_ARGUMENTS)
+	SET(TEST_ARGUMENTS "")
+ENDIF(OPT_TEST_ARGUMENTS)
+
+
+#############################
+# List the test cases
+SET(TEST_LIST
+	testsctp
+	testostr
+	testfifo
+	testpeers
+	testdict
+	testmesg
+	testmesg_stress
+	testsess
+	testdisp
+	testcnx
+	testloadext
+)
+
+#############################
+# Some parameters for the tests
+
+ADD_DEFINITIONS(-DTEST_DEBUG)
+ADD_DEFINITIONS(-DBUILD_DIR="${CMAKE_BINARY_DIR}")
+
+INCLUDE_DIRECTORIES( "../libfdproto" )
+INCLUDE_DIRECTORIES( "../libfdcore" )
+INCLUDE_DIRECTORIES(${LFDCORE_INCLUDES})
+
+
+SET(testcnx_ADDITIONAL_LIB  ${CLOCK_GETTIME_LIBS})
+SET(testfifo_ADDITIONAL_LIB ${CLOCK_GETTIME_LIBS})
+SET(testsess_ADDITIONAL_LIB ${CLOCK_GETTIME_LIBS})
+SET(testloadext_ADDITIONAL_LIB ${CMAKE_DL_LIBS})
+SET(testmesg_stress_ADDITIONAL_LIB ${CLOCK_GETTIME_LIBS} ${CMAKE_DL_LIBS})
+
+##############################
+# App_acct test
+
+IF(BUILD_APP_ACCT OR ALL_EXTENSIONS)
+	OPTION(TEST_APP_ACCT "Test app_acct extension? (Requires a configured database, see testappacct.c for details)" OFF)
+	IF(TEST_APP_ACCT)
+	
+		OPTION(TEST_APP_ACCT_CONNINFO "The connection string to the database")
+		IF(TEST_APP_ACCT_CONNINFO)
+			ADD_DEFINITIONS(-DTEST_CONNINFO="${TEST_APP_ACCT_CONNINFO}")
+		ENDIF(TEST_APP_ACCT_CONNINFO)
+	
+		SET(TEST_LIST ${TEST_LIST} testappacct)
+
+		# Extension dependencies
+		FIND_PACKAGE(PostgreSQL REQUIRED)
+		INCLUDE_DIRECTORIES(${POSTGRESQL_INCLUDE_DIR})
+		SET(testappacct_ADDITIONAL_LIB ${POSTGRESQL_LIBRARIES})
+
+		# List of source files, copied from the extension CMakeLists.
+		BISON_FILE(../extensions/app_acct/acct_conf.y)
+		FLEX_FILE(../extensions/app_acct/acct_conf.l)
+		#SET_SOURCE_FILES_PROPERTIES(lex.acct_conf.c acct_conf.tab.c PROPERTIES COMPILE_FLAGS "-I ${CMAKE_CURRENT_SOURCE_DIR}")
+		
+		SET( APP_ACCT_SRC
+			app_acct.h
+			app_acct.c
+			acct_db.c
+			acct_records.c
+		)
+		SET( APP_ACCT_SRC_GEN
+			lex.acct_conf.c
+			acct_conf.tab.c
+			acct_conf.tab.h
+		)
+
+		# The extension headers
+		INCLUDE_DIRECTORIES( "../extensions/app_acct" )
+
+		SET(testappacct_ADDITIONAL "")
+
+		FOREACH( SRC_FILE ${APP_ACCT_SRC})
+		   SET(testappacct_ADDITIONAL ${testappacct_ADDITIONAL} "../extensions/app_acct/${SRC_FILE}")
+		ENDFOREACH(SRC_FILE)
+
+		FOREACH( SRC_FILE ${APP_ACCT_SRC_GEN})
+		   SET(testappacct_ADDITIONAL ${testappacct_ADDITIONAL} "${CMAKE_CURRENT_BINARY_DIR}/../extensions/app_acct/${SRC_FILE}")
+		ENDFOREACH(SRC_FILE)
+
+	ENDIF(TEST_APP_ACCT)
+ENDIF(BUILD_APP_ACCT OR ALL_EXTENSIONS)
+
+
+#############################
+# Compile each test
+FOREACH( TEST ${TEST_LIST} )
+   ADD_EXECUTABLE(${TEST} ${TEST}.c tests.h ${${TEST}_ADDITIONAL})
+   TARGET_LINK_LIBRARIES(${TEST} libfdproto libfdcore ${GNUTLS_LIBRARIES} ${GCRYPT_LIBRARY} ${${TEST}_ADDITIONAL_LIB})
+   ADD_TEST(${TEST} ${EXECUTABLE_OUTPUT_PATH}/${TEST} ${TEST_ARGUMENTS})
+ENDFOREACH( TEST )
+
+
+####
+## INSTALL section ##
+
+# we do not install the tests
diff --git a/tests/testappacct.c b/tests/testappacct.c
new file mode 100644
index 0000000..2ec94f3
--- /dev/null
+++ b/tests/testappacct.c
@@ -0,0 +1,278 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@freediameter.net>							 *
+*													 *
+* Copyright (c) 2013, 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"
+
+/* The connection string to the database */
+#ifndef TEST_CONNINFO
+#error "Please specify the conninfo information"
+#endif /* TEST_CONNINFO */
+
+/* The table used for tests. This table will receive the following instructions:
+DROP TABLE <table>;
+CREATE TABLE <table>
+(
+  recorded_on timestamp with time zone NOT NULL,
+  "Accounting-Record-Type" integer,
+  "Session-Id" bytea,
+  "Accounting-Record-Number" integer,
+  "Route-Record1" bytea,
+  "Route-Record2" bytea,
+  "Route-Record3" bytea,
+  "Route-Record4" bytea
+);
+*/
+#define TABLE "incoming_test"
+
+#include "app_acct.h"
+#include <libpq-fe.h>
+
+static int add_avp_in_conf(char * avpname, int multi) 
+{
+	struct acct_conf_avp *new;
+	struct dict_object * dict;
+	struct dict_avp_data dictdata;
+
+	/* Validate the avp name first */
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, avpname, &dict, ENOENT) );
+	CHECK_FCT( fd_dict_getval( dict, &dictdata ));
+
+	/* Create a new entry */
+	CHECK_MALLOC( new = malloc(sizeof(struct acct_conf_avp)) );
+	memset(new, 0, sizeof(struct acct_conf_avp));
+	fd_list_init(&new->chain, NULL);
+	new->avpname = avpname;
+	new->avpobj = dict;
+	new->avptype = dictdata.avp_basetype;
+	new->multi = multi;
+
+	/* Add this new entry at the end of the list */
+	fd_list_insert_before( &acct_config->avps, &new->chain );
+	
+	return 0;
+}
+
+#define LOCAL_ID	"test.app.acct"
+#define LOCAL_REALM	"app.acct"
+
+/* Main test routine */
+int main(int argc, char *argv[])
+{
+	extern pthread_key_t connk; /* in acct_db.c */
+	PGconn *conn;
+	extern int fd_ext_init(int major, int minor, char * conffile); /* defined in include's extension.h */
+	extern void fd_ext_fini(void); /* defined in the extension itself */
+	struct msg * msg;
+	os0_t sess_bkp;
+	size_t sess_bkp_len;
+	
+	/* First, initialize the daemon modules */
+	INIT_FD();
+	fd_g_config->cnf_diamid = strdup(LOCAL_ID);
+	fd_g_config->cnf_diamid_len = CONSTSTRLEN(LOCAL_ID);
+	fd_g_config->cnf_diamrlm = strdup(LOCAL_REALM);
+	fd_g_config->cnf_diamrlm_len = CONSTSTRLEN(LOCAL_REALM);
+	
+	CHECK( 0, fd_queues_init()  );
+	CHECK( 0, fd_msg_init()  );
+	CHECK( 0, fd_rtdisp_init()  );
+	
+	/* Initialize the extension configuration for the test */
+	{
+		CHECK( 0, acct_conf_init() );
+		acct_config->conninfo = strdup(TEST_CONNINFO);
+		acct_config->tablename = strdup(TABLE);
+		acct_config->tsfield = strdup("recorded_on");
+		CHECK( 0, add_avp_in_conf(strdup("Session-Id"), 0) );
+		CHECK( 0, add_avp_in_conf(strdup("Accounting-Record-Type"), 0) );
+		CHECK( 0, add_avp_in_conf(strdup("Accounting-Record-Number"), 0) );
+		CHECK( 0, add_avp_in_conf(strdup("Route-Record"), 4) );
+		
+		/* Now, call the one of the extension */
+		CHECK( 0, fd_ext_init(FD_PROJECT_VERSION_MAJOR, FD_PROJECT_VERSION_MINOR,NULL) );
+		conn = pthread_getspecific(connk);
+	}
+	
+	/* Drop and recreate the table for the test */
+	{
+		PGresult * res;
+		CHECK( CONNECTION_OK, PQstatus(conn) );
+		
+		res = PQexec(conn, "DROP TABLE " TABLE ";");
+		CHECK( PGRES_COMMAND_OK, PQresultStatus(res) );
+		PQclear(res);
+		
+		res = PQexec(conn, "CREATE TABLE " TABLE " ( "
+					"  recorded_on timestamp with time zone NOT NULL, "
+					"  \"Accounting-Record-Type\" integer, "
+					"  \"Session-Id\" bytea, "
+					"  \"Accounting-Record-Number\" integer, "
+					"  \"Route-Record1\" bytea, "
+					"  \"Route-Record2\" bytea, "
+					"  \"Route-Record3\" bytea, "
+					"  \"Route-Record4\" bytea "
+					");"
+				);
+		CHECK( PGRES_COMMAND_OK, PQresultStatus(res) );
+		PQclear(res);
+	}
+	
+	/* OK, we are ready to test now. Create an ACR message that will pass the ABNF check */
+	{
+		struct dict_object * d = NULL;
+		struct avp *avp = NULL;
+		union avp_value avp_val;
+
+		/* Now find the ACR dictionary object */
+		CHECK( 0, fd_dict_search ( fd_g_config->cnf_dict, DICT_COMMAND, CMD_BY_NAME, "Accounting-Request", &d, ENOENT ) );
+
+		/* Create the instance */
+		CHECK( 0, fd_msg_new ( d, MSGFL_ALLOC_ETEID, &msg ) );
+		
+		/* App id */
+		{
+			struct msg_hdr * h;
+			CHECK( 0, fd_msg_hdr( msg, &h ) );
+			h->msg_appl = 3;
+		}
+		
+		/* sid */
+		{
+			struct session * sess = NULL;
+			os0_t s;
+			CHECK( 0, fd_sess_new( &sess, fd_g_config->cnf_diamid, fd_g_config->cnf_diamid_len, NULL, 0) );
+			CHECK( 0, fd_sess_getsid(sess, &s, &sess_bkp_len) );
+			CHECK( 1, (sess_bkp = os0dup(s, sess_bkp_len)) ? 1 : 0);
+
+			CHECK( 0, fd_dict_search ( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Session-Id", &d, ENOENT ) );
+			CHECK( 0, fd_msg_avp_new ( d, 0, &avp ) );
+			memset(&avp_val, 0, sizeof(avp_val));
+			avp_val.os.data = sess_bkp;
+			avp_val.os.len = sess_bkp_len;
+			CHECK( 0, fd_msg_avp_setvalue ( avp, &avp_val ) );
+			CHECK( 0, fd_msg_avp_add ( msg, MSG_BRW_FIRST_CHILD, avp) );
+		}
+		
+		/* Origin-* */
+		CHECK( 0, fd_msg_add_origin(msg, 1) );
+		
+		/* Destination-Realm */
+		{
+			CHECK( 0, fd_dict_search ( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Destination-Realm", &d, ENOENT ) );
+			CHECK( 0, fd_msg_avp_new ( d, 0, &avp ) );
+			memset(&avp_val, 0, sizeof(avp_val));
+			avp_val.os.data = (unsigned char *)fd_g_config->cnf_diamrlm;
+			avp_val.os.len = fd_g_config->cnf_diamrlm_len;
+			CHECK( 0, fd_msg_avp_setvalue ( avp, &avp_val ) );
+			CHECK( 0, fd_msg_avp_add ( msg, MSG_BRW_LAST_CHILD, avp) );
+		}
+		
+		/* Accounting-Record-Type */
+		{
+			CHECK( 0, fd_dict_search ( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Accounting-Record-Type", &d, ENOENT ) );
+			CHECK( 0, fd_msg_avp_new ( d, 0, &avp ) );
+			memset(&avp_val, 0, sizeof(avp_val));
+			avp_val.u32 = 2;
+			CHECK( 0, fd_msg_avp_setvalue ( avp, &avp_val ) );
+			CHECK( 0, fd_msg_avp_add ( msg, MSG_BRW_LAST_CHILD, avp) );
+		}
+		
+		/* Accounting-Record-Number */
+		{
+			CHECK( 0, fd_dict_search ( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Accounting-Record-Number", &d, ENOENT ) );
+			CHECK( 0, fd_msg_avp_new ( d, 0, &avp ) );
+			memset(&avp_val, 0, sizeof(avp_val));
+			avp_val.u32 = 2;
+			CHECK( 0, fd_msg_avp_setvalue ( avp, &avp_val ) );
+			CHECK( 0, fd_msg_avp_add ( msg, MSG_BRW_LAST_CHILD, avp) );
+		}
+		
+		/* Route-Record */
+		{
+			CHECK( 0, fd_dict_search ( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Route-Record", &d, ENOENT ) );
+			CHECK( 0, fd_msg_avp_new ( d, 0, &avp ) );
+			memset(&avp_val, 0, sizeof(avp_val));
+			avp_val.os.data = (unsigned char *)"peer1";
+			avp_val.os.len = strlen((char *)avp_val.os.data);
+			CHECK( 0, fd_msg_avp_setvalue ( avp, &avp_val ) );
+			CHECK( 0, fd_msg_avp_add ( msg, MSG_BRW_LAST_CHILD, avp) );
+			
+			CHECK( 0, fd_msg_avp_new ( d, 0, &avp ) );
+			memset(&avp_val, 0, sizeof(avp_val));
+			avp_val.os.data = (unsigned char *)"peer2";
+			avp_val.os.len = strlen((char *)avp_val.os.data);
+			CHECK( 0, fd_msg_avp_setvalue ( avp, &avp_val ) );
+			CHECK( 0, fd_msg_avp_add ( msg, MSG_BRW_LAST_CHILD, avp) );
+		}
+		
+		/* Source */
+		CHECK( 0, fd_msg_source_set( msg, "peer3", CONSTSTRLEN("peer3") ) );
+		CHECK( 0, fd_msg_source_setrr( msg, "peer3", CONSTSTRLEN("peer3"), fd_g_config->cnf_dict ) );
+	}
+	
+	/* Now, have the daemon handle this */
+	CHECK( 0, fd_fifo_post(fd_g_incoming, &msg) );
+	
+	/* It is picked by the dispatch module, the extension handles the query, inserts the records in the DB, send creates the answer.
+	   Once the answer is ready, it is sent to "peer3" which is not available of course; then the message is simply destroyed.
+	   We wait 1 second for this to happen... */
+	sleep(1);
+	
+	/* Now, check the record was actually registered properly */
+	{
+		PGresult * res;
+		uint8_t * bs;
+		char * es;
+		size_t l;
+		
+		res = PQexec(conn, "SELECT \"Session-Id\" from " TABLE ";");
+		CHECK( PGRES_TUPLES_OK, PQresultStatus(res) );
+		
+		/* We also check that the Session-Id we retrieve is the same as what we generated earlier (not trashed in the process) */
+		es = PQgetvalue(res, 0, 0);
+		bs = PQunescapeBytea((uint8_t *)es, &l);
+		
+		CHECK( 0, fd_os_cmp(bs, l, sess_bkp, sess_bkp_len) );
+		
+		PQclear(res);
+		PQfreemem(bs);
+	}  
+
+	/* That's all for the tests yet */
+	free(sess_bkp);
+	
+	PASSTEST();
+} 
+	
diff --git a/tests/testcnx.c b/tests/testcnx.c
new file mode 100644
index 0000000..e1826c9
--- /dev/null
+++ b/tests/testcnx.c
@@ -0,0 +1,2009 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@freediameter.net>							 *
+*													 *
+* Copyright (c) 2013, 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"
+
+#ifndef TEST_PORT
+#define TEST_PORT	3868
+#endif /* TEST_PORT */
+
+#ifndef NB_STREAMS
+#define NB_STREAMS	10
+#endif /* NB_STREAMS */
+
+#ifndef GNUTLS_DEFAULT_PRIORITY
+# define GNUTLS_DEFAULT_PRIORITY "NORMAL"
+#endif /* GNUTLS_DEFAULT_PRIORITY */
+
+#ifndef GNUTLS_DEFAULT_DHBITS
+# define GNUTLS_DEFAULT_DHBITS 1024
+#endif /* GNUTLS_DEFAULT_DHBITS */
+
+
+/* The cryptographic data */
+static char ca_data[] =		"-----BEGIN CERTIFICATE-----\n"
+				"MIIEqjCCA5KgAwIBAgIJANKgDwdlDYQDMA0GCSqGSIb3DQEBBQUAMIGUMQswCQYD\n"
+				"VQQGEwJKUDEOMAwGA1UECAwFVG9reW8xEDAOBgNVBAcMB0tvZ2FuZWkxDTALBgNV\n"
+				"BAoMBFdJREUxDzANBgNVBAsMBkFBQSBXRzEfMB0GA1UEAwwWY2hhdnJvdXguY293\n"
+				"YWRkaWN0Lm9yZzEiMCAGCSqGSIb3DQEJARYTc2RlY3VnaXNAbmljdC5nby5qcDAe\n"
+				"Fw0wOTEwMDUwODUxNDRaFw0xOTEwMDMwODUxNDRaMIGUMQswCQYDVQQGEwJKUDEO\n"
+				"MAwGA1UECAwFVG9reW8xEDAOBgNVBAcMB0tvZ2FuZWkxDTALBgNVBAoMBFdJREUx\n"
+				"DzANBgNVBAsMBkFBQSBXRzEfMB0GA1UEAwwWY2hhdnJvdXguY293YWRkaWN0Lm9y\n"
+				"ZzEiMCAGCSqGSIb3DQEJARYTc2RlY3VnaXNAbmljdC5nby5qcDCCASIwDQYJKoZI\n"
+				"hvcNAQEBBQADggEPADCCAQoCggEBAM5c6w4NnngTvGNWcJzbo0Kklp+kvUNQNgGu\n"
+				"myvz826qPp07HTSyJrIcgFnuYDR0Nd130Ot9u5osqpQhHTvolxDE87Tii8i3hJSj\n"
+				"TTY9K0ZwGb4AZ6QkuyMXS1jtOY657HqjpGZqT/2Syh0i7dM/hqSXFw0SPbyq+W1H\n"
+				"SVFWa1CTkPywFWAzwdr5WKah77uZ1dxWqgPgUdcZOiIQtLRp5n3fg40Nwso5YdwS\n"
+				"64+ebBX1pkhrCQ8AGc8O61Ep1JTXcO7jqQmPgzjiN+FeostI1Dp73S3MqleTAHjR\n"
+				"hqZ77VF7nkroMM9btMHJBaxnfwc2ewULUJwnuOiGWrvMq/9Z4J8CAwEAAaOB/DCB\n"
+				"+TAdBgNVHQ4EFgQUkqpVn7N3gmiJ7X5zQ2bki+7qv4UwgckGA1UdIwSBwTCBvoAU\n"
+				"kqpVn7N3gmiJ7X5zQ2bki+7qv4WhgZqkgZcwgZQxCzAJBgNVBAYTAkpQMQ4wDAYD\n"
+				"VQQIDAVUb2t5bzEQMA4GA1UEBwwHS29nYW5laTENMAsGA1UECgwEV0lERTEPMA0G\n"
+				"A1UECwwGQUFBIFdHMR8wHQYDVQQDDBZjaGF2cm91eC5jb3dhZGRpY3Qub3JnMSIw\n"
+				"IAYJKoZIhvcNAQkBFhNzZGVjdWdpc0BuaWN0LmdvLmpwggkA0qAPB2UNhAMwDAYD\n"
+				"VR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEAJy0XLk8j8YLSTt2/VMy9TAUx\n"
+				"esXUiZj0Ung+gkr7A1K0NnwYxDzG2adMhf13upHoydu2ErLMmD6F77x+QuY/q7nc\n"
+				"ZvO0tvcoAP6ToSDwiypU5dnTmnfkgwVwzFkNCi1sGRosEm8c/c/8MfK0I0nVdj1/\n"
+				"BIkIG7tTDVi9JvkWYl0UlSKWTZKrntVwCmscfC02DGb+GoLbO9+QmiNM5Y3yOYZ4\n"
+				"Pc7SSoKLL0rwJBmpPNs7boYsweeSuCAVu0shRfgC90odXcej2EN5ETfCuU1evXNW\n"
+				"5cA+zZsDK/nWJwxBaW0CxAHX579FElFWlK4+BnzhZRdDhmJDnN5dh4ekJGM6Lg==\n"
+				"-----END CERTIFICATE-----\n";
+				
+/* Client:
+				Certificate:
+				    Data:
+        				Version: 3 (0x2)
+        				Serial Number: 5 (0x5)
+        				Signature Algorithm: sha1WithRSAEncryption
+        				Issuer: C=JP, ST=Tokyo, L=Koganei, O=WIDE, OU=AAA WG, CN=chavroux.cowaddict.org/emailAddress=sdecugis@nict.go.jp
+        				Validity
+        				    Not Before: Oct 27 04:04:05 2009 GMT
+        				    Not After : Oct 25 04:04:05 2019 GMT
+        				Subject: C=JP, ST=Tokyo, L=Koganei, O=WIDE, OU=AAA WG, CN=client.test/emailAddress=client@test
+        				Subject Public Key Info:
+        				    Public Key Algorithm: rsaEncryption
+        				    RSA Public Key: (1024 bit)
+                				Modulus (1024 bit):
+                				    00:bd:eb:50:1e:9d:7a:cd:9d:bb:e7:bc:4e:38:4a:
+                				    b2:cc:9e:b4:89:77:01:ef:d1:c6:19:29:00:fe:ce:
+                				    3c:62:05:13:b1:8c:ff:31:7a:0f:c1:2e:4b:3c:0c:
+                				    40:1e:36:4e:76:da:0a:64:43:fc:1e:ea:0c:97:b2:
+                				    57:9c:9c:8c:90:bd:eb:23:7b:b8:b7:5c:03:ed:6f:
+                				    48:55:8a:88:08:38:c5:cd:33:b7:ab:a8:3a:6f:7f:
+                				    13:10:65:a5:50:b9:f4:8b:cc:2e:e9:79:58:a6:11:
+                				    f0:58:45:41:ef:36:b3:35:cb:14:ec:82:0c:ad:11:
+                				    6a:ea:64:ef:28:a2:6e:47:45
+                				Exponent: 65537 (0x10001)
+        				X509v3 extensions:
+        				    X509v3 Basic Constraints: 
+                				CA:FALSE
+        				    Netscape Comment: 
+                				OpenSSL Generated Certificate
+        				    X509v3 Subject Key Identifier: 
+                				BE:B3:89:4F:9D:8F:6C:20:C4:D0:3E:6A:05:11:82:50:54:49:70:A2
+        				    X509v3 Authority Key Identifier: 
+                				keyid:92:AA:55:9F:B3:77:82:68:89:ED:7E:73:43:66:E4:8B:EE:EA:BF:85
+
+				    Signature Algorithm: sha1WithRSAEncryption
+        				a3:88:f5:15:b5:ad:20:60:a1:85:19:3f:b9:5e:1e:be:31:7f:
+        				84:7a:c2:18:3a:63:6a:67:1f:46:86:4d:10:d6:1d:ad:a2:c8:
+        				0b:95:33:fa:e4:05:f4:b8:70:34:77:f7:85:6e:70:46:ac:39:
+        				54:a9:5f:ea:5e:d1:33:bb:c9:a3:42:81:41:90:25:b5:92:8b:
+        				e8:6e:3e:97:06:dd:9a:cc:29:61:34:5a:d3:1c:5d:ad:d1:a3:
+        				eb:6a:47:b4:d0:c2:17:89:e1:e2:2d:36:18:50:1a:e7:d4:fc:
+        				38:2e:47:0b:39:50:87:2f:aa:07:64:f8:9a:4d:47:01:da:10:
+        				d8:97:c7:a6:13:bc:0e:ca:63:c1:f2:09:fb:f8:6a:a4:5f:08:
+        				b5:ad:ed:4f:71:b9:89:7f:43:27:85:72:e7:8d:a8:4a:cc:f6:
+        				36:ca:8a:ae:82:b5:a8:42:41:99:87:84:7c:f0:90:fd:ca:96:
+        				37:a2:e0:d9:fa:dd:a4:c9:f1:50:b7:e5:e6:8f:af:83:8c:23:
+        				b6:20:cc:66:e3:08:60:13:02:8f:42:3a:07:91:a7:38:b2:72:
+        				16:fd:bd:a9:60:f0:e2:9f:23:f3:c0:99:e3:17:bc:00:7c:b3:
+        				89:9c:ea:fa:3e:f6:69:a1:98:c2:ec:46:da:70:b6:f9:c3:93:
+        				a7:fc:36:dd
+*/
+static char client_cert_data[] ="-----BEGIN CERTIFICATE-----\n"
+				"MIIDiTCCAnGgAwIBAgIBBTANBgkqhkiG9w0BAQUFADCBlDELMAkGA1UEBhMCSlAx\n"
+				"DjAMBgNVBAgMBVRva3lvMRAwDgYDVQQHDAdLb2dhbmVpMQ0wCwYDVQQKDARXSURF\n"
+				"MQ8wDQYDVQQLDAZBQUEgV0cxHzAdBgNVBAMMFmNoYXZyb3V4LmNvd2FkZGljdC5v\n"
+				"cmcxIjAgBgkqhkiG9w0BCQEWE3NkZWN1Z2lzQG5pY3QuZ28uanAwHhcNMDkxMDI3\n"
+				"MDQwNDA1WhcNMTkxMDI1MDQwNDA1WjCBgTELMAkGA1UEBhMCSlAxDjAMBgNVBAgM\n"
+				"BVRva3lvMRAwDgYDVQQHDAdLb2dhbmVpMQ0wCwYDVQQKDARXSURFMQ8wDQYDVQQL\n"
+				"DAZBQUEgV0cxFDASBgNVBAMMC2NsaWVudC50ZXN0MRowGAYJKoZIhvcNAQkBFgtj\n"
+				"bGllbnRAdGVzdDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAvetQHp16zZ27\n"
+				"57xOOEqyzJ60iXcB79HGGSkA/s48YgUTsYz/MXoPwS5LPAxAHjZOdtoKZEP8HuoM\n"
+				"l7JXnJyMkL3rI3u4t1wD7W9IVYqICDjFzTO3q6g6b38TEGWlULn0i8wu6XlYphHw\n"
+				"WEVB7zazNcsU7IIMrRFq6mTvKKJuR0UCAwEAAaN7MHkwCQYDVR0TBAIwADAsBglg\n"
+				"hkgBhvhCAQ0EHxYdT3BlblNTTCBHZW5lcmF0ZWQgQ2VydGlmaWNhdGUwHQYDVR0O\n"
+				"BBYEFL6ziU+dj2wgxNA+agURglBUSXCiMB8GA1UdIwQYMBaAFJKqVZ+zd4Joie1+\n"
+				"c0Nm5Ivu6r+FMA0GCSqGSIb3DQEBBQUAA4IBAQCjiPUVta0gYKGFGT+5Xh6+MX+E\n"
+				"esIYOmNqZx9Ghk0Q1h2tosgLlTP65AX0uHA0d/eFbnBGrDlUqV/qXtEzu8mjQoFB\n"
+				"kCW1kovobj6XBt2azClhNFrTHF2t0aPrake00MIXieHiLTYYUBrn1Pw4LkcLOVCH\n"
+				"L6oHZPiaTUcB2hDYl8emE7wOymPB8gn7+GqkXwi1re1PcbmJf0MnhXLnjahKzPY2\n"
+				"yoqugrWoQkGZh4R88JD9ypY3ouDZ+t2kyfFQt+Xmj6+DjCO2IMxm4whgEwKPQjoH\n"
+				"kac4snIW/b2pYPDinyPzwJnjF7wAfLOJnOr6PvZpoZjC7EbacLb5w5On/Dbd\n"
+				"-----END CERTIFICATE-----\n";
+static char client_priv_data[] ="-----BEGIN RSA PRIVATE KEY-----\n"
+				"MIICXgIBAAKBgQC961AenXrNnbvnvE44SrLMnrSJdwHv0cYZKQD+zjxiBROxjP8x\n"
+				"eg/BLks8DEAeNk522gpkQ/we6gyXslecnIyQvesje7i3XAPtb0hViogIOMXNM7er\n"
+				"qDpvfxMQZaVQufSLzC7peVimEfBYRUHvNrM1yxTsggytEWrqZO8oom5HRQIDAQAB\n"
+				"AoGBAIYnsOLPby3LnC5n8AEHkyHDgdgQvsd/MSYYtuFHIZRD7dNfu+xhQru9TdvO\n"
+				"84Pj7K07/FczRuc3gUmu6wBv/UIP9To15RHZh+/n537nybGus5S4IYKVvap477To\n"
+				"0rQDf9ec27iw77gxb7moQ9Otuxwbv0h0Z+1EVLI8d8jHOq0BAkEA9YNr0R+7KXBS\n"
+				"48yT43g5HpOFkTZzNXWVdpSvYGneb56wslk5Eatp235I4uz/a7Rej5v99W0M3nSe\n"
+				"/AgHfYn75QJBAMYH/pBx/WkrLj+pPaARlNwInCIC5zUhr6B0IKCt2tvy5eyuc5sd\n"
+				"AoTFaU+cSI+ZqsRzY8jMKkonktxBg48oJ+ECQQCt4AtlqcFVkbVCm8pJGQXq/7Ni\n"
+				"qlthiwr1Vkv2TkQ4bPza8pGWT/3Cc2ePPyWN08n8jw+G11p72cAW4mDbqfN5AkEA\n"
+				"mNYKrkiLn+NnqlJf8W4gSUGL3uQGtYbuGRQHKnuDckWhFm39YzWcgAQsJvkjN1EN\n"
+				"7thvpsWLzfeE7ODTPGVtgQJATObxYJOt6rms3fAStwuXW3ET77TA1ja4XsUEe5Yu\n"
+				"JpcQOruJb9XwndqzNbL0dSUePb9gFiBCGKYOyreNTTRTmw==\n"
+				"-----END RSA PRIVATE KEY-----\n";
+				
+/* Server:
+				Certificate:
+				    Data:
+        				Version: 3 (0x2)
+        				Serial Number: 4 (0x4)
+        				Signature Algorithm: sha1WithRSAEncryption
+        				Issuer: C=JP, ST=Tokyo, L=Koganei, O=WIDE, OU=AAA WG, CN=chavroux.cowaddict.org/emailAddress=sdecugis@nict.go.jp
+        				Validity
+        				    Not Before: Oct 27 04:03:39 2009 GMT
+        				    Not After : Oct 25 04:03:39 2019 GMT
+        				Subject: C=JP, ST=Tokyo, L=Koganei, O=WIDE, OU=AAA WG, CN=serv.test/emailAddress=serv@test
+        				Subject Public Key Info:
+        				    Public Key Algorithm: rsaEncryption
+        				    RSA Public Key: (1024 bit)
+                				Modulus (1024 bit):
+                				    00:a6:f7:1c:a9:90:5b:fa:c8:f6:a3:04:0c:d0:8b:
+                				    45:c3:90:f7:2d:c2:c9:d7:bd:66:8a:7c:1c:51:89:
+                				    40:9e:cd:70:57:cb:00:47:a3:e8:76:8b:00:b3:c9:
+                				    c3:0d:b1:b9:2a:08:9f:52:92:82:d3:18:c1:d8:d1:
+                				    b8:1e:fd:71:fe:23:ec:19:e9:6d:9d:fd:ae:88:bc:
+                				    39:44:7a:37:ad:c6:88:d1:64:7c:b1:d4:3c:a9:30:
+                				    c4:de:51:02:c4:48:4f:25:3e:2f:93:ae:25:32:66:
+                				    9a:dc:f4:44:45:ff:7f:12:49:97:0d:01:8d:13:9a:
+                				    d3:8f:9e:2d:62:95:02:0a:c7
+                				Exponent: 65537 (0x10001)
+        				X509v3 extensions:
+        				    X509v3 Basic Constraints: 
+                				CA:FALSE
+        				    Netscape Comment: 
+                				OpenSSL Generated Certificate
+        				    X509v3 Subject Key Identifier: 
+                				0C:33:C4:7F:39:D0:34:FF:F8:61:A1:46:8B:49:1D:A3:57:B3:4D:58
+        				    X509v3 Authority Key Identifier: 
+                				keyid:92:AA:55:9F:B3:77:82:68:89:ED:7E:73:43:66:E4:8B:EE:EA:BF:85
+
+				    Signature Algorithm: sha1WithRSAEncryption
+        				87:f5:49:a6:04:f9:98:9a:f1:1a:68:ce:06:ae:4c:0c:08:eb:
+        				ba:98:e7:3f:df:22:7f:35:88:1d:b7:8a:f3:89:a3:68:0d:53:
+        				45:eb:23:a1:dd:6b:dc:b0:80:58:0c:10:0b:49:74:ea:a8:b6:
+        				8c:2e:c6:73:dc:7a:74:c7:59:3e:79:5a:d2:5c:15:0b:f1:d8:
+        				19:37:2a:c0:22:75:10:3f:4c:e9:a1:e0:eb:b2:9e:09:70:3d:
+        				2a:4c:fe:9c:99:36:4b:aa:6c:e1:8b:9c:aa:e1:29:1f:49:6b:
+        				14:db:12:ae:cf:68:4a:dd:03:e1:3b:ad:79:b4:54:84:1d:bb:
+        				ac:45:c4:85:f1:03:65:65:96:23:ae:e7:97:3c:5c:db:ce:55:
+        				34:5d:c3:73:ec:cd:f6:0f:a5:81:5f:c2:ab:a3:42:fa:36:7f:
+        				83:ef:db:0f:cd:62:0b:ea:d9:4f:73:35:68:5f:23:d5:0a:be:
+        				ff:7f:23:9a:af:0d:a5:f8:3e:3a:f0:63:1c:e1:d2:96:81:cf:
+        				7b:5a:6b:d0:9b:67:56:9e:aa:a9:e8:f1:6c:fb:54:2b:1a:f4:
+        				ef:16:5a:be:1d:a9:c8:d6:cc:f7:42:8c:fe:83:2c:84:8c:80:
+        				fb:1c:88:f6:35:1c:ae:43:72:fa:68:30:9c:25:8b:db:2c:84:
+        				87:76:9d:b9
+*/
+static char server_cert_data[] ="-----BEGIN CERTIFICATE-----\n"
+				"MIIDhDCCAmygAwIBAgIBBDANBgkqhkiG9w0BAQUFADCBlDELMAkGA1UEBhMCSlAx\n"
+				"DjAMBgNVBAgMBVRva3lvMRAwDgYDVQQHDAdLb2dhbmVpMQ0wCwYDVQQKDARXSURF\n"
+				"MQ8wDQYDVQQLDAZBQUEgV0cxHzAdBgNVBAMMFmNoYXZyb3V4LmNvd2FkZGljdC5v\n"
+				"cmcxIjAgBgkqhkiG9w0BCQEWE3NkZWN1Z2lzQG5pY3QuZ28uanAwHhcNMDkxMDI3\n"
+				"MDQwMzM5WhcNMTkxMDI1MDQwMzM5WjB9MQswCQYDVQQGEwJKUDEOMAwGA1UECAwF\n"
+				"VG9reW8xEDAOBgNVBAcMB0tvZ2FuZWkxDTALBgNVBAoMBFdJREUxDzANBgNVBAsM\n"
+				"BkFBQSBXRzESMBAGA1UEAwwJc2Vydi50ZXN0MRgwFgYJKoZIhvcNAQkBFglzZXJ2\n"
+				"QHRlc3QwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAKb3HKmQW/rI9qMEDNCL\n"
+				"RcOQ9y3Cyde9Zop8HFGJQJ7NcFfLAEej6HaLALPJww2xuSoIn1KSgtMYwdjRuB79\n"
+				"cf4j7BnpbZ39roi8OUR6N63GiNFkfLHUPKkwxN5RAsRITyU+L5OuJTJmmtz0REX/\n"
+				"fxJJlw0BjROa04+eLWKVAgrHAgMBAAGjezB5MAkGA1UdEwQCMAAwLAYJYIZIAYb4\n"
+				"QgENBB8WHU9wZW5TU0wgR2VuZXJhdGVkIENlcnRpZmljYXRlMB0GA1UdDgQWBBQM\n"
+				"M8R/OdA0//hhoUaLSR2jV7NNWDAfBgNVHSMEGDAWgBSSqlWfs3eCaIntfnNDZuSL\n"
+				"7uq/hTANBgkqhkiG9w0BAQUFAAOCAQEAh/VJpgT5mJrxGmjOBq5MDAjrupjnP98i\n"
+				"fzWIHbeK84mjaA1TResjod1r3LCAWAwQC0l06qi2jC7Gc9x6dMdZPnla0lwVC/HY\n"
+				"GTcqwCJ1ED9M6aHg67KeCXA9Kkz+nJk2S6ps4YucquEpH0lrFNsSrs9oSt0D4Tut\n"
+				"ebRUhB27rEXEhfEDZWWWI67nlzxc285VNF3Dc+zN9g+lgV/Cq6NC+jZ/g+/bD81i\n"
+				"C+rZT3M1aF8j1Qq+/38jmq8Npfg+OvBjHOHSloHPe1pr0JtnVp6qqejxbPtUKxr0\n"
+				"7xZavh2pyNbM90KM/oMshIyA+xyI9jUcrkNy+mgwnCWL2yyEh3aduQ==\n"
+				"-----END CERTIFICATE-----\n";
+static char server_priv_data[] ="-----BEGIN RSA PRIVATE KEY-----\n"
+				"MIICXQIBAAKBgQCm9xypkFv6yPajBAzQi0XDkPctwsnXvWaKfBxRiUCezXBXywBH\n"
+				"o+h2iwCzycMNsbkqCJ9SkoLTGMHY0bge/XH+I+wZ6W2d/a6IvDlEejetxojRZHyx\n"
+				"1DypMMTeUQLESE8lPi+TriUyZprc9ERF/38SSZcNAY0TmtOPni1ilQIKxwIDAQAB\n"
+				"AoGAZv3Ddm0P79CLIt9asEFY1VvUvSuMqkGwwPfx1/HcJJkBFYapM4fN22G/Gyf3\n"
+				"47ifSWhsLtklTeXVnVMwSh14dJaJQuSEnaFnUUWfjiRbEAXZnMFwAIiaszEZbPap\n"
+				"NUNpcGl06FZrphYAMkjOVUfjCjfOZDAvL4JGpo271Zx4l0ECQQDYoFFQpBCPx0PK\n"
+				"TWUmvatXI/Amo94XkGfofbdeeI8PiAJBO5UI6rmjjIVwsJwO9dQb/IlP1/OnBeJv\n"
+				"p9YW5uixAkEAxVAOKu7mpGu0Q/K2iEUUYDX9YHf253kgkdIDF4iZk4Tcecjoxuru\n"
+				"fIWu9dMtyDVV+HT2X4cNEnO1/oS3kJII9wJBAJkdwDwiqz4lV6o/yFZ4zAoc8dsu\n"
+				"CoZXYMq5SYox5tTQit928OHLn4mVgqBjhPsiEVnyx0+zUZpmE2ZemHm5nxECQHfE\n"
+				"FBVzVYRP6+eil7E3XRrZKqc3qiLunxpkA4RxYebtKnaxwLmdOI1VB9InEQ8JcNmT\n"
+				"BUkOzJx6p+mJ3XJfchkCQQDWmbMYYJajsjlS4YpdUUj7cBSotA6vtkNVHFr0/ak/\n"
+				"S+tLkMNuruaInWizK+BKYTIJLlQDf5u5NTrw41vye5Hv\n"
+				"-----END RSA PRIVATE KEY-----\n";
+
+/* Expired:
+				Certificate:
+				    Data:
+        				Version: 3 (0x2)
+        				Serial Number: 6 (0x6)
+        				Signature Algorithm: sha1WithRSAEncryption
+        				Issuer: C=JP, ST=Tokyo, L=Koganei, O=WIDE, OU=AAA WG, CN=chavroux.cowaddict.org/emailAddress=sdecugis@nict.go.jp
+        				Validity
+        				    Not Before: Oct 27 04:06:35 2009 GMT
+        				    Not After : Oct 28 04:06:35 2009 GMT
+        				Subject: C=JP, ST=Tokyo, L=Koganei, O=WIDE, OU=AAA WG, CN=expired.test/emailAddress=expired@test
+        				Subject Public Key Info:
+        				    Public Key Algorithm: rsaEncryption
+        				    RSA Public Key: (1024 bit)
+                				Modulus (1024 bit):
+                				    00:e3:17:15:54:85:dc:cf:c7:a0:32:4a:49:7d:55:
+                				    75:9b:29:15:db:7e:87:17:d9:0e:65:44:53:d7:19:
+                				    37:27:c7:c6:fe:c6:dc:72:2b:dc:86:1a:ff:24:6c:
+                				    63:3f:75:9c:0a:14:e1:70:06:79:d4:b9:26:d4:68:
+                				    4c:28:38:ba:34:60:56:02:3d:94:55:4a:1f:4e:5a:
+                				    f0:a5:71:4c:3e:71:69:39:ad:bc:aa:55:35:fb:73:
+                				    5b:5f:6c:30:71:8e:8a:b6:a5:06:cc:ee:dd:29:c7:
+                				    52:0d:a7:9c:0f:a1:ba:52:11:e2:1b:b9:74:6b:08:
+                				    87:11:d2:ec:a9:ac:63:63:4f
+                				Exponent: 65537 (0x10001)
+        				X509v3 extensions:
+        				    X509v3 Basic Constraints: 
+                				CA:FALSE
+        				    Netscape Comment: 
+                				OpenSSL Generated Certificate
+        				    X509v3 Subject Key Identifier: 
+                				1C:AF:66:42:5B:AD:AA:A5:9B:D9:AE:3A:C1:5A:AC:2F:CC:CE:22:6C
+        				    X509v3 Authority Key Identifier: 
+                				keyid:92:AA:55:9F:B3:77:82:68:89:ED:7E:73:43:66:E4:8B:EE:EA:BF:85
+
+				    Signature Algorithm: sha1WithRSAEncryption
+        				60:8f:55:55:59:82:0f:64:cb:b8:11:c8:44:ce:bf:69:07:0d:
+        				be:c2:34:be:42:6a:78:15:39:9f:be:8a:17:d6:43:42:c9:7c:
+        				f1:6d:5d:aa:c3:1b:4d:b0:f0:b6:73:46:2a:87:cd:55:56:a3:
+        				6d:cc:de:a8:28:6a:53:85:9e:e5:68:b7:3c:f5:72:13:7b:d0:
+        				21:f2:91:49:35:e0:37:1e:28:19:d5:1b:cc:e1:32:1e:7f:b0:
+        				86:df:43:a4:47:0f:29:0b:eb:51:60:9a:f5:ca:50:f4:2d:59:
+        				cd:fc:50:9d:29:ed:45:98:de:a2:5c:d1:b5:7a:34:ad:7a:73:
+        				48:8b:a2:9b:89:8e:4a:2e:2a:04:19:d6:62:6a:0d:f0:96:f2:
+        				f0:d0:22:77:3b:7f:b1:2a:f4:3b:17:47:5e:38:07:09:65:ad:
+        				1d:ea:46:69:6a:96:b6:6b:3b:5c:cc:6e:30:d7:cb:53:69:59:
+        				c2:63:78:2b:03:d4:d4:f7:17:29:99:9a:43:ff:78:0a:af:42:
+        				c5:b3:8d:09:38:5b:30:70:28:c1:97:ab:fd:7f:87:9a:ec:f2:
+        				97:44:ff:f5:b9:41:30:d1:c6:32:98:69:34:c4:39:30:6f:e2:
+        				d3:b2:70:97:66:ee:41:f5:ae:0f:09:f0:ed:60:96:67:a9:8a:
+        				cd:d6:95:f2
+*/
+static char expired_cert_data[]="-----BEGIN CERTIFICATE-----\n"
+				"MIIDizCCAnOgAwIBAgIBBjANBgkqhkiG9w0BAQUFADCBlDELMAkGA1UEBhMCSlAx\n"
+				"DjAMBgNVBAgMBVRva3lvMRAwDgYDVQQHDAdLb2dhbmVpMQ0wCwYDVQQKDARXSURF\n"
+				"MQ8wDQYDVQQLDAZBQUEgV0cxHzAdBgNVBAMMFmNoYXZyb3V4LmNvd2FkZGljdC5v\n"
+				"cmcxIjAgBgkqhkiG9w0BCQEWE3NkZWN1Z2lzQG5pY3QuZ28uanAwHhcNMDkxMDI3\n"
+				"MDQwNjM1WhcNMDkxMDI4MDQwNjM1WjCBgzELMAkGA1UEBhMCSlAxDjAMBgNVBAgM\n"
+				"BVRva3lvMRAwDgYDVQQHDAdLb2dhbmVpMQ0wCwYDVQQKDARXSURFMQ8wDQYDVQQL\n"
+				"DAZBQUEgV0cxFTATBgNVBAMMDGV4cGlyZWQudGVzdDEbMBkGCSqGSIb3DQEJARYM\n"
+				"ZXhwaXJlZEB0ZXN0MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDjFxVUhdzP\n"
+				"x6AySkl9VXWbKRXbfocX2Q5lRFPXGTcnx8b+xtxyK9yGGv8kbGM/dZwKFOFwBnnU\n"
+				"uSbUaEwoOLo0YFYCPZRVSh9OWvClcUw+cWk5rbyqVTX7c1tfbDBxjoq2pQbM7t0p\n"
+				"x1INp5wPobpSEeIbuXRrCIcR0uyprGNjTwIDAQABo3sweTAJBgNVHRMEAjAAMCwG\n"
+				"CWCGSAGG+EIBDQQfFh1PcGVuU1NMIEdlbmVyYXRlZCBDZXJ0aWZpY2F0ZTAdBgNV\n"
+				"HQ4EFgQUHK9mQlutqqWb2a46wVqsL8zOImwwHwYDVR0jBBgwFoAUkqpVn7N3gmiJ\n"
+				"7X5zQ2bki+7qv4UwDQYJKoZIhvcNAQEFBQADggEBAGCPVVVZgg9ky7gRyETOv2kH\n"
+				"Db7CNL5CangVOZ++ihfWQ0LJfPFtXarDG02w8LZzRiqHzVVWo23M3qgoalOFnuVo\n"
+				"tzz1chN70CHykUk14DceKBnVG8zhMh5/sIbfQ6RHDykL61FgmvXKUPQtWc38UJ0p\n"
+				"7UWY3qJc0bV6NK16c0iLopuJjkouKgQZ1mJqDfCW8vDQInc7f7Eq9DsXR144Bwll\n"
+				"rR3qRmlqlrZrO1zMbjDXy1NpWcJjeCsD1NT3FymZmkP/eAqvQsWzjQk4WzBwKMGX\n"
+				"q/1/h5rs8pdE//W5QTDRxjKYaTTEOTBv4tOycJdm7kH1rg8J8O1glmepis3WlfI=\n"
+				"-----END CERTIFICATE-----\n";
+static char expired_priv_data[]="-----BEGIN RSA PRIVATE KEY-----\n"
+				"MIICXgIBAAKBgQDjFxVUhdzPx6AySkl9VXWbKRXbfocX2Q5lRFPXGTcnx8b+xtxy\n"
+				"K9yGGv8kbGM/dZwKFOFwBnnUuSbUaEwoOLo0YFYCPZRVSh9OWvClcUw+cWk5rbyq\n"
+				"VTX7c1tfbDBxjoq2pQbM7t0px1INp5wPobpSEeIbuXRrCIcR0uyprGNjTwIDAQAB\n"
+				"AoGASwPoDui9XYHTIGm7xwRA+kVjLAOq+qy//aHJlEeHGcP7r1PfpHNqwH4QhGat\n"
+				"jlv6dLYbFld9TVDwS8A8UBkVIPLWnCysd5tF2A4C5akx6ouW6HliW/JheYrgl8AV\n"
+				"PVeR3bm91UbnpC0ABVlw87jp1Ovyr60Suo4jsoJz+CyTa2ECQQD0LJWpnwn1jIlR\n"
+				"DGkLi7F3E70JJcdhTWzBjGFD+Na+/2ZO0MKLhK+O1WUkKa0oi+e5P1JOnGIpTI8c\n"
+				"BJOO415RAkEA7hauapYuqGI/auSPH8/nFB5z1G94RTxo2a5THKcG5MqS/8N3ubFj\n"
+				"i2PPS0lEYVjqoHEsZUsMnDmXp6KDKMAfnwJBAIp+T1UqM8fmsmwaEerOjRXxSCNM\n"
+				"Hk5+T9Vn/jNDjOpAipLhrbbcx4bIWtmsGd8Jm6Fi3RhhcvvhxLorjlZZeEECQQCf\n"
+				"IaPD88sNmlUewdLzhUbCiLQMadCuHflKfRxpyy1tYAQuVFxCTdDlynkzra25ju+K\n"
+				"+vmcXjP4evnk/lbBtt+rAkEAgOr4Apgs3nMppngPV5yFx0NDqH2n8PlEAM1Il4Qs\n"
+				"IuuK18v0KwlUGAfEEmCiNh1e1qkLmD0CnI2QjYAjcLQUhw==\n"
+				"-----END RSA PRIVATE KEY-----\n";
+
+/* Unknown CA certificate :
+				Certificate:
+				    Data:
+        				Version: 3 (0x2)
+        				Serial Number: 1 (0x1)
+        				Signature Algorithm: sha1WithRSAEncryption
+        				Issuer: C=JP, ST=Tokyo, L=Koganei, O=WIDE, OU=AAA WG, CN=chavroux.cowaddict.org/emailAddress=sdecugis@nict.go.jp
+        				Validity
+        				    Not Before: Oct 28 08:04:40 2009 GMT
+        				    Not After : Oct 28 08:04:40 2010 GMT
+        				Subject: C=JP, ST=Tokyo, L=Koganei, O=WIDE, OU=AAA WG, CN=unknown.cs/emailAddress=unknown@ca
+        				Subject Public Key Info:
+        				    Public Key Algorithm: rsaEncryption
+        				    RSA Public Key: (1024 bit)
+                				Modulus (1024 bit):
+                				    00:e6:3a:d5:8a:14:c8:15:d0:f0:5c:03:c3:af:33:
+                				    51:2c:17:b7:65:ac:45:e8:48:2d:ae:70:fd:7c:79:
+                				    3a:c7:80:c8:50:53:d0:19:d8:3a:26:a8:16:4d:4c:
+                				    04:17:09:df:69:9b:59:2b:89:c8:e0:60:bb:1d:37:
+                				    82:d2:3f:17:39:c9:8f:5d:76:e1:0f:6e:08:9a:8f:
+                				    16:4a:ea:83:86:f9:bd:15:14:56:68:87:79:05:f9:
+                				    5f:66:11:bd:22:46:26:64:be:57:16:51:66:41:50:
+                				    ac:f2:b1:ca:d0:38:11:4b:4c:b2:ee:25:36:6e:d3:
+                				    b9:63:72:c4:84:82:1c:2b:27
+                				Exponent: 65537 (0x10001)
+        				X509v3 extensions:
+        				    X509v3 Basic Constraints: 
+                				CA:FALSE
+        				    Netscape Comment: 
+                				OpenSSL Generated Certificate
+        				    X509v3 Subject Key Identifier: 
+                				BA:5A:9D:D2:B0:4B:72:D6:1F:00:11:0B:B5:7B:59:DF:08:38:81:BE
+        				    X509v3 Authority Key Identifier: 
+                				keyid:52:C5:A4:63:B8:DB:AC:F2:92:34:2F:72:56:71:C8:11:8E:76:E6:DF
+
+				    Signature Algorithm: sha1WithRSAEncryption
+        				90:8f:3b:bd:e3:a1:ca:6a:92:a6:fd:f0:64:ae:46:83:32:35:
+        				61:80:57:8b:30:12:70:02:e1:51:d9:87:c8:af:d9:4b:b9:6d:
+        				bf:ab:86:5f:19:1f:dc:af:84:67:bf:3c:bf:33:f3:7c:c6:81:
+        				7b:e4:e9:26:1d:bc:d6:8c:ab:72:94:7f:85:33:95:d9:24:ec:
+        				fd:7b:d2:fd:50:3e:e5:61:4f:75:51:ae:c6:4a:ec:df:cf:aa:
+        				73:a5:08:f7:f3:9a:40:66:48:f0:8e:9b:43:b1:30:f3:e3:c8:
+        				36:3f:68:36:6a:1c:aa:16:40:49:b4:73:9a:71:f1:17:6c:0b:
+        				d3:e1:a7:b7:40:de:2c:3c:36:7c:d4:dd:d6:94:c9:d7:5f:f5:
+        				ae:35:56:e8:cc:65:9c:bb:3d:e8:7a:ca:0e:ed:78:03:41:cb:
+        				fd:80:81:de:f9:de:b2:14:4b:81:24:36:de:29:c1:06:11:86:
+        				8c:a9:b0:0c:c7:57:cf:79:a7:3a:84:0c:27:dc:86:6d:cb:44:
+        				2d:26:dc:7e:fb:17:d6:b2:3d:31:03:d3:f1:ab:5d:91:5d:94:
+        				e4:94:88:70:96:b3:7c:0f:15:fe:c8:c6:4d:99:37:ab:09:0c:
+        				da:ba:b6:0e:fa:5e:bb:4b:ce:04:21:06:09:a9:2c:27:86:76:
+        				cc:ee:73:6f
+*/
+static char notrust_ca_data[] =	"-----BEGIN CERTIFICATE-----\n"
+				"MIIEqjCCA5KgAwIBAgIJAP3UMghSlH9PMA0GCSqGSIb3DQEBBQUAMIGUMQswCQYD\n"
+				"VQQGEwJKUDEOMAwGA1UECAwFVG9reW8xEDAOBgNVBAcMB0tvZ2FuZWkxDTALBgNV\n"
+				"BAoMBFdJREUxDzANBgNVBAsMBkFBQSBXRzEfMB0GA1UEAwwWY2hhdnJvdXguY293\n"
+				"YWRkaWN0Lm9yZzEiMCAGCSqGSIb3DQEJARYTc2RlY3VnaXNAbmljdC5nby5qcDAe\n"
+				"Fw0wOTEwMjgwODAzNDRaFw0xOTEwMjYwODAzNDRaMIGUMQswCQYDVQQGEwJKUDEO\n"
+				"MAwGA1UECAwFVG9reW8xEDAOBgNVBAcMB0tvZ2FuZWkxDTALBgNVBAoMBFdJREUx\n"
+				"DzANBgNVBAsMBkFBQSBXRzEfMB0GA1UEAwwWY2hhdnJvdXguY293YWRkaWN0Lm9y\n"
+				"ZzEiMCAGCSqGSIb3DQEJARYTc2RlY3VnaXNAbmljdC5nby5qcDCCASIwDQYJKoZI\n"
+				"hvcNAQEBBQADggEPADCCAQoCggEBALKW9iSUggF5mbvYe1Xk128Csfiijx+fwH5y\n"
+				"ZqWrHNt0YG/tZSwyCDMWBLXTeuYsntg5y0mcpsrN8v02tvrPiCzDfRPyz3mG68us\n"
+				"DPEEgQ1kqL2Gsti2DUcsdyZcDM+4rgsWRivgOTVyoNimv5f+xgmPYoElkgelLwZK\n"
+				"WxGt1VCebOxP3qZA3hSHWE1hJgL4svful7RD1PbwPzidxJKITyAiJoPKWQA9cjSa\n"
+				"gVzRQ7S4vmYALJn7xe+dMFRcfAK8RMv7/gJF6Rw7zufW0DIZK98KZs6aL0lmMPVk\n"
+				"f31N2uvndf+cjy0n4luwEoXY+TeJZY205lbwHrzR0rH75FSm0RsCAwEAAaOB/DCB\n"
+				"+TAdBgNVHQ4EFgQUUsWkY7jbrPKSNC9yVnHIEY525t8wgckGA1UdIwSBwTCBvoAU\n"
+				"UsWkY7jbrPKSNC9yVnHIEY525t+hgZqkgZcwgZQxCzAJBgNVBAYTAkpQMQ4wDAYD\n"
+				"VQQIDAVUb2t5bzEQMA4GA1UEBwwHS29nYW5laTENMAsGA1UECgwEV0lERTEPMA0G\n"
+				"A1UECwwGQUFBIFdHMR8wHQYDVQQDDBZjaGF2cm91eC5jb3dhZGRpY3Qub3JnMSIw\n"
+				"IAYJKoZIhvcNAQkBFhNzZGVjdWdpc0BuaWN0LmdvLmpwggkA/dQyCFKUf08wDAYD\n"
+				"VR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEACANo6IR3OQlQaXHJaprVVDvl\n"
+				"oMJC0FRbVCK503sbmWTJL98UqxRdsTZNIL07gXlK0oUKyiNijIXiLG8d5IlUrDxF\n"
+				"H/Vsu6s8k3/PpAUVeiO2oygWqvU5NGvt0jg54MrOJKhYYPWrzbmHty+cAXyoNzOR\n"
+				"+W5RX6HRQgxvZWQq2Ok46VX622R1nNjFmCBYT7I7/gWG+hkbIAoH6d9sULLjpC+B\n"
+				"bI+L/N7ac9/Og8pGIgpUI60Gn5zO93+E+Nhg+1BlcDHGnQD6vFNs8LYp5CCX/Zj1\n"
+				"tWFVXZnx58odaU3M4t9/ZQnkZdx9YJIroETbN0PoqlnSagBjgUvbWwn4YCotCA==\n"
+				"-----END CERTIFICATE-----\n";
+				
+static char notrust_cert_data[]="-----BEGIN CERTIFICATE-----\n"
+				"MIIDhjCCAm6gAwIBAgIBATANBgkqhkiG9w0BAQUFADCBlDELMAkGA1UEBhMCSlAx\n"
+				"DjAMBgNVBAgMBVRva3lvMRAwDgYDVQQHDAdLb2dhbmVpMQ0wCwYDVQQKDARXSURF\n"
+				"MQ8wDQYDVQQLDAZBQUEgV0cxHzAdBgNVBAMMFmNoYXZyb3V4LmNvd2FkZGljdC5v\n"
+				"cmcxIjAgBgkqhkiG9w0BCQEWE3NkZWN1Z2lzQG5pY3QuZ28uanAwHhcNMDkxMDI4\n"
+				"MDgwNDQwWhcNMTAxMDI4MDgwNDQwWjB/MQswCQYDVQQGEwJKUDEOMAwGA1UECAwF\n"
+				"VG9reW8xEDAOBgNVBAcMB0tvZ2FuZWkxDTALBgNVBAoMBFdJREUxDzANBgNVBAsM\n"
+				"BkFBQSBXRzETMBEGA1UEAwwKdW5rbm93bi5jczEZMBcGCSqGSIb3DQEJARYKdW5r\n"
+				"bm93bkBjYTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA5jrVihTIFdDwXAPD\n"
+				"rzNRLBe3ZaxF6EgtrnD9fHk6x4DIUFPQGdg6JqgWTUwEFwnfaZtZK4nI4GC7HTeC\n"
+				"0j8XOcmPXXbhD24Imo8WSuqDhvm9FRRWaId5BflfZhG9IkYmZL5XFlFmQVCs8rHK\n"
+				"0DgRS0yy7iU2btO5Y3LEhIIcKycCAwEAAaN7MHkwCQYDVR0TBAIwADAsBglghkgB\n"
+				"hvhCAQ0EHxYdT3BlblNTTCBHZW5lcmF0ZWQgQ2VydGlmaWNhdGUwHQYDVR0OBBYE\n"
+				"FLpandKwS3LWHwARC7V7Wd8IOIG+MB8GA1UdIwQYMBaAFFLFpGO426zykjQvclZx\n"
+				"yBGOdubfMA0GCSqGSIb3DQEBBQUAA4IBAQCQjzu946HKapKm/fBkrkaDMjVhgFeL\n"
+				"MBJwAuFR2YfIr9lLuW2/q4ZfGR/cr4Rnvzy/M/N8xoF75OkmHbzWjKtylH+FM5XZ\n"
+				"JOz9e9L9UD7lYU91Ua7GSuzfz6pzpQj385pAZkjwjptDsTDz48g2P2g2ahyqFkBJ\n"
+				"tHOacfEXbAvT4ae3QN4sPDZ81N3WlMnXX/WuNVbozGWcuz3oesoO7XgDQcv9gIHe\n"
+				"+d6yFEuBJDbeKcEGEYaMqbAMx1fPeac6hAwn3IZty0QtJtx++xfWsj0xA9Pxq12R\n"
+				"XZTklIhwlrN8DxX+yMZNmTerCQzaurYO+l67S84EIQYJqSwnhnbM7nNv\n"
+				"-----END CERTIFICATE-----\n";
+static char notrust_priv_data[]="-----BEGIN RSA PRIVATE KEY-----\n"
+				"MIICXQIBAAKBgQDmOtWKFMgV0PBcA8OvM1EsF7dlrEXoSC2ucP18eTrHgMhQU9AZ\n"
+				"2DomqBZNTAQXCd9pm1kricjgYLsdN4LSPxc5yY9dduEPbgiajxZK6oOG+b0VFFZo\n"
+				"h3kF+V9mEb0iRiZkvlcWUWZBUKzyscrQOBFLTLLuJTZu07ljcsSEghwrJwIDAQAB\n"
+				"AoGAeRec1SGVE5Rvt5XrSK0vFofq2DlCE6hTDpszWFLTDbe4pDdRDybhfw+Nm15O\n"
+				"EGgK8BrbTcEMvKdkAzv9POQeLDE8JImgesHZFxN3jnkK+b762BGRDt57DzvMJsfj\n"
+				"1LBle+UBnZB1CvjrINvu+tNMVPlUpjIstbpMq0D+s01+ijECQQD8MHTv/M+Uc86u\n"
+				"1SFywgs+eQPQ8g0OoTLxzqo6YhW8FtwLjoRCZx2TNQS5gYBuQrixd/yE0Spfv9aS\n"
+				"UtlAaOc1AkEA6bVufggHVHcgiWqS8CHzb6g/GRxQixVshOsoVLMkCSz04zlwIfXF\n"
+				"c03hh5RJVv7jmuBmhHbayujMgvinw75oawJAQb9oXUDt5Wgj1FTgeYi5YbovEoRo\n"
+				"fw3ruDsHCl2UCQt0ptarCJzVixFhf/ORRi3C9RGxFfdqMrhS+qb62N4AmQJBALYU\n"
+				"T1BLiwJoiWXmLTJ/EP0V9Irov2uMtm5cE6DhrJqlduksz8r1gu7RZ3tMsVLg5Iy+\n"
+				"dcCQJOffNa54caQUTZ8CQQDTs/70Nr6F6ktrtmtU/S7lIitpQJCu9u/SPyBYPmFZ\n"
+				"9Axy6Ee66Php+eWDNP4Ln4axrapD0732wD8DcmGDVHij\n"
+				"-----END RSA PRIVATE KEY-----\n";
+
+/* Diffie-Hellman parameters, generated with GNUTLS certtool:
+certtool --generate-dh-params
+				Generator: 06
+
+				Prime: ea:c3:75:0b:32:cf:d9:17:98:5c:da:d1
+					e0:1d:b9:7c:be:29:60:b0:6f:68:a9:f6
+					8d:75:05:59:69:04:ae:39:7c:2b:74:04
+					3c:e2:da:28:8a:9b:93:aa:67:05:a7:3e
+					06:3e:0d:31:63:88:55:ad:5a:bd:41:22
+					b7:58:a7:45:b3:d5:03:ad:de:3c:8d:69
+					42:bf:84:3d:c1:90:e7:39:6a:4b:87:01
+					19:e5:f3:a4:e5:8e:e2:45:d5:0c:6b:17
+					22:2b:2e:50:83:91:0c:5b:82:fc:72:27
+					49:3b:9f:29:11:53:c7:90:b8:8d:87:73
+					1a:7b:05:ab:cb:30:59:16:71:30:60:1b
+					4c:80:15:3a:a2:d3:47:b7:4a:61:de:64
+					7e:79:de:88:53:b7:7a:c6:a2:9a:bb:55
+					40:2d:7a:71:c7:41:b5:29:df:d7:5c:fb
+					42:e4:d8:5e:0b:99:d3:3c:93:0f:33:51
+					8b:f4:60:e4:c5:b5:58:21:c0:51:c4:43
+					25:7c:37:fe:5c:d3:62:6c:2a:af:a7:2a
+					82:d5:d3:e2:bb:5d:ad:84:15:f6:78:d9
+					d5:a8:f7:f0:48:5c:8d:e0:3d:04:ac:cf
+					aa:34:3f:5d:f2:0d:3d:ee:ec:b8:d8:e8
+					ad:dc:d3:40:59:a0:fd:45:62:47:63:c0
+					bd:f5:df:8b
+*/
+static char dh_params_data[] =  "-----BEGIN DH PARAMETERS-----\n"
+				"MIIBCAKCAQEA6sN1CzLP2ReYXNrR4B25fL4pYLBvaKn2jXUFWWkErjl8K3QEPOLa\n"
+				"KIqbk6pnBac+Bj4NMWOIVa1avUEit1inRbPVA63ePI1pQr+EPcGQ5zlqS4cBGeXz\n"
+				"pOWO4kXVDGsXIisuUIORDFuC/HInSTufKRFTx5C4jYdzGnsFq8swWRZxMGAbTIAV\n"
+				"OqLTR7dKYd5kfnneiFO3esaimrtVQC16ccdBtSnf11z7QuTYXguZ0zyTDzNRi/Rg\n"
+				"5MW1WCHAUcRDJXw3/lzTYmwqr6cqgtXT4rtdrYQV9njZ1aj38EhcjeA9BKzPqjQ/\n"
+				"XfINPe7suNjordzTQFmg/UViR2PAvfXfiwIBBg==\n"
+				"-----END DH PARAMETERS-----\n";
+
+
+/* List server endpoints */
+static struct fd_list eps = FD_LIST_INITIALIZER(eps);
+
+/* Pass parameters to the connect thread */
+struct connect_flags {
+	int	proto;
+	int	expect_failure; /* 0 or 1 */
+};
+
+/* Client's side of the connection established from a separate thread */
+static void * connect_thr(void * arg)
+{
+	struct connect_flags * cf = arg;
+	struct cnxctx * cnx = NULL;
+	
+	fd_log_threadname ( "testcnx:connect" );
+	
+	/* Connect to the server */
+	switch (cf->proto) {
+		case IPPROTO_TCP:
+			{
+				struct fd_endpoint * ep = (struct fd_endpoint *)(eps.next);
+				cnx = fd_cnx_cli_connect_tcp( &ep->sa, sSAlen(&ep->ss) );
+				CHECK( 1, (cnx ? 1 : 0) ^ cf->expect_failure );
+			}
+			break;
+#ifndef DISABLE_SCTP
+		case IPPROTO_SCTP:
+			{
+				cnx = fd_cnx_cli_connect_sctp(0, TEST_PORT, &eps);
+				CHECK( 1, (cnx ? 1 : 0) ^ cf->expect_failure );
+			}
+			break;
+#endif /* DISABLE_SCTP */
+		default:
+			CHECK( 0, 1 );
+	}
+	
+	/* exit */
+	return cnx;
+}
+
+/* Parameters to the handshake thread */
+struct handshake_flags {
+	struct cnxctx * cnx;
+	gnutls_certificate_credentials_t	creds;
+	int algo;
+	int ret;
+};
+
+/* Handshake the client's side */
+static void * handshake_thr(void * arg)
+{
+	struct handshake_flags * hf = arg;
+	fd_log_threadname ( "testcnx:handshake" );
+	hf->ret = fd_cnx_handshake(hf->cnx, GNUTLS_CLIENT, hf->algo, NULL, hf->creds);
+	return NULL;
+}
+
+/* Terminate the client's connection side */
+static void * destroy_thr(void * arg)
+{
+	struct cnxctx * cnx = arg;
+	fd_log_threadname ( "testcnx:destroy" );
+	fd_cnx_destroy(cnx);
+	return NULL;
+}
+	
+/* Main test routine */
+int main(int argc, char *argv[])
+{
+	gnutls_datum_t ca 		= { (uint8_t *)ca_data, 		sizeof(ca_data) 	  };
+	gnutls_datum_t server_cert 	= { (uint8_t *)server_cert_data, 	sizeof(server_cert_data)  };
+	gnutls_datum_t server_priv 	= { (uint8_t *)server_priv_data, 	sizeof(server_priv_data)  };
+	gnutls_datum_t client_cert	= { (uint8_t *)client_cert_data, 	sizeof(client_cert_data)  };
+	gnutls_datum_t client_priv 	= { (uint8_t *)client_priv_data, 	sizeof(client_priv_data)  };
+	gnutls_datum_t expired_cert 	= { (uint8_t *)expired_cert_data, 	sizeof(expired_cert_data) };
+	gnutls_datum_t expired_priv 	= { (uint8_t *)expired_priv_data, 	sizeof(expired_priv_data) };
+	gnutls_datum_t notrust_ca 	= { (uint8_t *)notrust_ca_data, 	sizeof(notrust_ca_data)   };
+	gnutls_datum_t notrust_cert 	= { (uint8_t *)notrust_cert_data, 	sizeof(notrust_cert_data) };
+	gnutls_datum_t notrust_priv 	= { (uint8_t *)notrust_priv_data, 	sizeof(notrust_priv_data) };
+	gnutls_datum_t dh_params	= { (uint8_t *)dh_params_data, 		sizeof(dh_params_data) 	  };
+	
+	/* Listening socket, server side */
+	struct cnxctx * listener;
+#ifndef DISABLE_SCTP
+	struct cnxctx * listener_sctp;
+#endif /* DISABLE_SCTP */
+	
+	/* Server & client connected sockets */
+	struct cnxctx * server_side;
+	struct cnxctx * client_side;
+	
+	pthread_t thr;
+	int ret, i;
+	uint8_t * cer_buf;
+	size_t 	  cer_sz;
+	uint8_t * rcv_buf;
+	size_t 	  rcv_sz;
+	
+	/* First, initialize the daemon modules */
+	INIT_FD();
+	
+	/* Restrain the # of streams */
+	fd_g_config->cnf_sctp_str = NB_STREAMS;
+	
+	/* Set the CA parameter in the config */
+	CHECK_GNUTLS_DO( ret = gnutls_certificate_set_x509_trust_mem( fd_g_config->cnf_sec_data.credentials,
+									 &ca,
+									 GNUTLS_X509_FMT_PEM), );
+	CHECK( 1, ret );
+	
+	#ifdef GNUTLS_VERSION_300
+	{
+		/* We import these CA in the trust list */
+		gnutls_x509_crt_t * calist;
+		unsigned int cacount = 0;
+		
+		CHECK_GNUTLS_DO( ret = gnutls_x509_crt_list_import2(&calist, &cacount, &ca, GNUTLS_X509_FMT_PEM, 
+							GNUTLS_X509_CRT_LIST_FAIL_IF_UNSORTED), );
+		CHECK( 1, cacount );
+
+		CHECK_GNUTLS_DO( ret = gnutls_x509_trust_list_add_cas (fd_g_config->cnf_sec_data.trustlist, calist, cacount, 0), );
+		CHECK( 1, ret );
+	}
+		
+	/* Use certificate verification during the handshake */
+	gnutls_certificate_set_verify_function (fd_g_config->cnf_sec_data.credentials, fd_tls_verify_credentials_2);
+	
+	#endif /* GNUTLS_VERSION_300 */
+							
+	
+	/* Set the server credentials (in config) */
+	CHECK_GNUTLS_DO( ret = gnutls_certificate_set_x509_key_mem( fd_g_config->cnf_sec_data.credentials,
+									&server_cert,
+									&server_priv,
+									GNUTLS_X509_FMT_PEM), );
+	CHECK( GNUTLS_E_SUCCESS, ret );
+	
+	/* Set the default priority */
+	CHECK_GNUTLS_DO( ret = gnutls_priority_init( &fd_g_config->cnf_sec_data.prio_cache, GNUTLS_DEFAULT_PRIORITY, NULL), );
+	CHECK( GNUTLS_E_SUCCESS, ret );
+	
+	/* Set default DH params */
+	CHECK_GNUTLS_DO( ret = gnutls_dh_params_import_pkcs3( fd_g_config->cnf_sec_data.dh_cache, &dh_params, GNUTLS_X509_FMT_PEM), );
+	CHECK( GNUTLS_E_SUCCESS, ret );
+	
+	
+	/* Initialize the server address (this should give a safe loopback address + port, even on non-standard configs) */
+	{
+		struct addrinfo hints, *ai, *aip;
+		memset(&hints, 0, sizeof(hints));
+		hints.ai_flags  = AI_NUMERICSERV;
+		hints.ai_family = AF_INET;
+		CHECK( 0, getaddrinfo("localhost", _stringize(TEST_PORT), &hints, &ai) );
+		aip = ai;
+		while (aip) {
+			CHECK( 0, fd_ep_add_merge( &eps, aip->ai_addr, aip->ai_addrlen, EP_FL_DISC | EP_ACCEPTALL ));
+			aip = aip->ai_next;
+		};
+		freeaddrinfo(ai);
+		
+		CHECK( 0, FD_IS_LIST_EMPTY(&eps) ? 1 : 0 );
+	}
+	
+	/* Start the server(s) */
+	{
+		/* TCP server */
+		listener = fd_cnx_serv_tcp(TEST_PORT, 0, (struct fd_endpoint *)(eps.next));
+		CHECK( 1, listener ? 1 : 0 );
+		
+		/* Accept incoming clients */
+		CHECK( 0, fd_cnx_serv_listen(listener));
+
+#ifndef DISABLE_SCTP
+		/* SCTP server */
+		listener_sctp = fd_cnx_serv_sctp(TEST_PORT, &eps);
+		CHECK( 1, listener_sctp ? 1 : 0 );
+		
+		/* Accept incoming clients */
+		CHECK( 0, fd_cnx_serv_listen(listener_sctp));
+#endif /* DISABLE_SCTP */
+
+	}	
+	
+	/* Initialize the CER message */
+	{
+		struct msg * cer;
+		struct dict_object * model = NULL;
+		struct avp * oh;
+		union avp_value value;
+
+		/* Find the CER dictionary object */
+		CHECK( 0, fd_dict_search ( fd_g_config->cnf_dict, DICT_COMMAND, CMD_BY_NAME, "Capabilities-Exchange-Request", &model, ENOENT ) );
+
+		/* Create the instance */
+		CHECK( 0, fd_msg_new ( model, 0, &cer ) );
+		
+		/* Now find the Origin-Host dictionary object */
+		CHECK( 0, fd_dict_search ( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Origin-Host", &model, ENOENT ) );
+
+		/* Create the instance */
+		CHECK( 0, fd_msg_avp_new ( model, 0, &oh ) );
+		value.os.data = (uint8_t *)"Client.side";
+		value.os.len = strlen((char *)value.os.data);
+		CHECK( 0, fd_msg_avp_setvalue ( oh, &value ) );
+		
+		/* Add the AVP */
+		CHECK( 0, fd_msg_avp_add( cer, MSG_BRW_LAST_CHILD, oh) );
+
+		#if 0
+		/* For debug: dump the object */
+		fd_log_debug("Dumping CER");
+		fd_log_debug("%s", fd_msg_dump_treeview(FD_DUMP_TEST_PARAMS, cer, fd_g_config->cnf_dict, 0, 1));
+		#endif
+		
+		CHECK( 0, fd_msg_bufferize( cer, &cer_buf, &cer_sz ) );
+		CHECK( 0, fd_msg_free(cer) );
+	}
+	
+	/* Simple TCP client / server test (no TLS) */
+	{
+		struct connect_flags cf;
+		
+		memset(&cf, 0, sizeof(cf));
+		cf.proto = IPPROTO_TCP;
+		
+		/* Start the client thread */
+		CHECK( 0, pthread_create(&thr, NULL, connect_thr, &cf) );
+
+		/* Accept the connection of the client */
+		server_side = fd_cnx_serv_accept(listener);
+		CHECK( 1, server_side ? 1 : 0 );
+		CHECK( 0, fd_cnx_start_clear(server_side, 0) );
+		
+		/* Retrieve the client connection object */
+		CHECK( 0, pthread_join( thr, (void *)&client_side ) );
+		CHECK( 1, client_side ? 1 : 0 );
+		CHECK( 0, fd_cnx_start_clear(client_side, 0) );
+		
+		/* Send a message and receive it */
+		CHECK( 0, fd_cnx_send(server_side, cer_buf, cer_sz));
+		CHECK( 0, fd_cnx_receive(client_side, NULL, &rcv_buf, &rcv_sz));
+		CHECK( cer_sz, rcv_sz );
+		CHECK( 0, memcmp( rcv_buf, cer_buf, cer_sz ) );
+		free(rcv_buf);
+		
+		/* Do it in the other direction */
+		CHECK( 0, fd_cnx_send(client_side, cer_buf, cer_sz));
+		CHECK( 0, fd_cnx_receive(server_side, NULL, &rcv_buf, &rcv_sz));
+		CHECK( cer_sz, rcv_sz );
+		CHECK( 0, memcmp( rcv_buf, cer_buf, cer_sz ) );
+		free(rcv_buf);
+		
+		/* Now close the connections */
+		fd_cnx_destroy(client_side);
+		fd_cnx_destroy(server_side);
+	}
+		
+#ifndef DISABLE_SCTP
+	/* Simple SCTP client / server test (no TLS) */
+	{
+		struct connect_flags cf;
+		
+		memset(&cf, 0, sizeof(cf));
+		cf.proto = IPPROTO_SCTP;
+		
+		/* Start the client thread */
+		CHECK( 0, pthread_create(&thr, NULL, connect_thr, &cf) );
+
+		/* Accept the connection of the client */
+		server_side = fd_cnx_serv_accept(listener_sctp);
+		CHECK( 1, server_side ? 1 : 0 );
+		
+		/* Retrieve the client connection object */
+		CHECK( 0, pthread_join( thr, (void *)&client_side ) );
+		CHECK( 1, client_side ? 1 : 0 );
+		
+		CHECK( 0, fd_cnx_start_clear(server_side, 1) );
+		
+		/* Send a message and receive it */
+		CHECK( 0, fd_cnx_send(server_side, cer_buf, cer_sz));
+		CHECK( EINVAL, fd_cnx_receive(client_side, NULL, &rcv_buf, &rcv_sz));
+		CHECK( 0, fd_cnx_start_clear(client_side, 0) );
+		CHECK( 0, fd_cnx_receive(client_side, NULL, &rcv_buf, &rcv_sz));
+		CHECK( cer_sz, rcv_sz );
+		CHECK( 0, memcmp( rcv_buf, cer_buf, cer_sz ) );
+		free(rcv_buf);
+		
+		/* Do it in the other direction */
+		CHECK( 0, fd_cnx_send(client_side, cer_buf, cer_sz));
+		CHECK( 0, fd_cnx_receive(server_side, NULL, &rcv_buf, &rcv_sz));
+		CHECK( cer_sz, rcv_sz );
+		CHECK( 0, memcmp( rcv_buf, cer_buf, cer_sz ) );
+		free(rcv_buf);
+		
+		/* Do it one more time to use another stream */
+		CHECK( 0, fd_cnx_send(client_side, cer_buf, cer_sz));
+		CHECK( 0, fd_cnx_receive(server_side, NULL, &rcv_buf, &rcv_sz));
+		CHECK( cer_sz, rcv_sz );
+		CHECK( 0, memcmp( rcv_buf, cer_buf, cer_sz ) );
+		free(rcv_buf);
+		
+		/* Now close the connection */
+		fd_cnx_destroy(client_side);
+		fd_cnx_destroy(server_side);
+	}
+#endif /* DISABLE_SCTP */
+	
+	/* TCP Client / server emulating old Diameter behavior (handshake after 1 message exchange) */
+	{
+		struct connect_flags cf;
+		struct handshake_flags hf;
+		
+		memset(&cf, 0, sizeof(cf));
+		cf.proto = IPPROTO_TCP;
+		
+		memset(&hf, 0, sizeof(hf));
+		
+		/* Initialize remote certificate */
+		CHECK_GNUTLS_DO( ret = gnutls_certificate_allocate_credentials (&hf.creds), );
+		CHECK( GNUTLS_E_SUCCESS, ret );
+		/* Set the CA */
+		CHECK_GNUTLS_DO( ret = gnutls_certificate_set_x509_trust_mem( hf.creds, &ca, GNUTLS_X509_FMT_PEM), );
+		CHECK( 1, ret );
+		/* Set the key */
+		CHECK_GNUTLS_DO( ret = gnutls_certificate_set_x509_key_mem( hf.creds, &client_cert, &client_priv, GNUTLS_X509_FMT_PEM), );
+		CHECK( GNUTLS_E_SUCCESS, ret );
+		
+		/* Start the client thread */
+		CHECK( 0, pthread_create(&thr, NULL, connect_thr, &cf) );
+
+		/* Accept the connection of the client */
+		server_side = fd_cnx_serv_accept(listener);
+		CHECK( 1, server_side ? 1 : 0 );
+		
+		/* Retrieve the client connection object */
+		CHECK( 0, pthread_join( thr, (void *)&client_side ) );
+		CHECK( 1, client_side ? 1 : 0 );
+		hf.cnx = client_side;
+		
+		/* In legacy Diameter, we exchange first one message (CER / CEA) */
+		
+		CHECK( 0, fd_cnx_start_clear(server_side, 0) );
+		CHECK( 0, fd_cnx_start_clear(client_side, 0) );
+		
+		/* Send a message and receive it */
+		CHECK( 0, fd_cnx_send(server_side, cer_buf, cer_sz));
+		CHECK( 0, fd_cnx_receive(client_side, NULL, &rcv_buf, &rcv_sz));
+		CHECK( cer_sz, rcv_sz );
+		CHECK( 0, memcmp( rcv_buf, cer_buf, cer_sz ) );
+		free(rcv_buf);
+		
+		/* And the supposed reply */
+		CHECK( 0, fd_cnx_send(client_side, cer_buf, cer_sz));
+		CHECK( 0, fd_cnx_receive(server_side, NULL, &rcv_buf, &rcv_sz));
+		CHECK( cer_sz, rcv_sz );
+		CHECK( 0, memcmp( rcv_buf, cer_buf, cer_sz ) );
+		free(rcv_buf);
+		
+		/* At this point in legacy Diameter we start the handshake */
+		CHECK( 0, pthread_create(&thr, NULL, handshake_thr, &hf) );
+		CHECK( 0, fd_cnx_handshake(server_side, GNUTLS_SERVER, ALGO_HANDSHAKE_DEFAULT /* No impact on TCP */, NULL, NULL) );
+		CHECK( 0, pthread_join(thr, NULL) );
+		CHECK( 0, hf.ret );
+		
+		/* Send a few TLS protected message, and replies */
+		for (i = 0; i < 2 * NB_STREAMS; i++) {
+			CHECK( 0, fd_cnx_send(server_side, cer_buf, cer_sz));
+			CHECK( 0, fd_cnx_receive(client_side, NULL, &rcv_buf, &rcv_sz));
+			CHECK( cer_sz, rcv_sz );
+			CHECK( 0, memcmp( rcv_buf, cer_buf, cer_sz ) );
+			free(rcv_buf);
+
+			CHECK( 0, fd_cnx_send(client_side, cer_buf, cer_sz));
+			CHECK( 0, fd_cnx_receive(server_side, NULL, &rcv_buf, &rcv_sz));
+			CHECK( cer_sz, rcv_sz );
+			CHECK( 0, memcmp( rcv_buf, cer_buf, cer_sz ) );
+			free(rcv_buf);
+		}
+		
+		
+		/* Now close the connection */
+		CHECK( 0, pthread_create(&thr, NULL, destroy_thr, client_side) );
+		fd_cnx_destroy(server_side);
+		CHECK( 0, pthread_join(thr, NULL) );
+		
+		/* Free the credentials */
+		gnutls_certificate_free_keys(hf.creds);
+		gnutls_certificate_free_cas(hf.creds);
+		gnutls_certificate_free_credentials(hf.creds);
+	}
+		
+#ifndef DISABLE_SCTP
+	/* SCTP Client / server emulating old Diameter behavior (handshake after 1 message exchange) */
+	{
+		struct connect_flags cf;
+		struct handshake_flags hf;
+		
+		memset(&cf, 0, sizeof(cf));
+		cf.proto = IPPROTO_SCTP;
+		
+		memset(&hf, 0, sizeof(hf));
+		hf.algo = ALGO_HANDSHAKE_3436; /* this is mandatory for old TLS mechanism */
+		
+		/* Initialize remote certificate */
+		CHECK_GNUTLS_DO( ret = gnutls_certificate_allocate_credentials (&hf.creds), );
+		CHECK( GNUTLS_E_SUCCESS, ret );
+		/* Set the CA */
+		CHECK_GNUTLS_DO( ret = gnutls_certificate_set_x509_trust_mem( hf.creds, &ca, GNUTLS_X509_FMT_PEM), );
+		CHECK( 1, ret );
+		/* Set the key */
+		CHECK_GNUTLS_DO( ret = gnutls_certificate_set_x509_key_mem( hf.creds, &client_cert, &client_priv, GNUTLS_X509_FMT_PEM), );
+		CHECK( GNUTLS_E_SUCCESS, ret );
+		
+		/* Start the client thread */
+		CHECK( 0, pthread_create(&thr, NULL, connect_thr, &cf) );
+
+		/* Accept the connection of the client */
+		server_side = fd_cnx_serv_accept(listener_sctp);
+		CHECK( 1, server_side ? 1 : 0 );
+		
+		/* Retrieve the client connection object */
+		CHECK( 0, pthread_join( thr, (void *)&client_side ) );
+		CHECK( 1, client_side ? 1 : 0 );
+		hf.cnx = client_side;
+		
+		/* In legacy Diameter, we exchange first one message (CER / CEA) */
+		
+		CHECK( 0, fd_cnx_start_clear(server_side, 0) );
+		CHECK( 0, fd_cnx_start_clear(client_side, 0) );
+		
+		/* Send a message and receive it */
+		CHECK( 0, fd_cnx_send(server_side, cer_buf, cer_sz));
+		CHECK( 0, fd_cnx_receive(client_side, NULL, &rcv_buf, &rcv_sz));
+		CHECK( cer_sz, rcv_sz );
+		CHECK( 0, memcmp( rcv_buf, cer_buf, cer_sz ) );
+		free(rcv_buf);
+		
+		/* And the supposed reply */
+		CHECK( 0, fd_cnx_send(client_side, cer_buf, cer_sz));
+		CHECK( 0, fd_cnx_receive(server_side, NULL, &rcv_buf, &rcv_sz));
+		CHECK( cer_sz, rcv_sz );
+		CHECK( 0, memcmp( rcv_buf, cer_buf, cer_sz ) );
+		free(rcv_buf);
+		
+		/* At this point in legacy Diameter we start the handshake */
+		CHECK( 0, pthread_create(&thr, NULL, handshake_thr, &hf) );
+		CHECK( 0, fd_cnx_handshake(server_side, GNUTLS_SERVER, ALGO_HANDSHAKE_3436, NULL, NULL) );
+		CHECK( 0, pthread_join(thr, NULL) );
+		CHECK( 0, hf.ret );
+		
+		/* Send a few TLS protected message, and replies */
+		for (i = 0; i < 2 * NB_STREAMS; i++) {
+			CHECK( 0, fd_cnx_send(server_side, cer_buf, cer_sz));
+			CHECK( 0, fd_cnx_receive(client_side, NULL, &rcv_buf, &rcv_sz));
+			CHECK( cer_sz, rcv_sz );
+			CHECK( 0, memcmp( rcv_buf, cer_buf, cer_sz ) );
+			free(rcv_buf);
+
+			CHECK( 0, fd_cnx_send(client_side, cer_buf, cer_sz));
+			CHECK( 0, fd_cnx_receive(server_side, NULL, &rcv_buf, &rcv_sz));
+			CHECK( cer_sz, rcv_sz );
+			CHECK( 0, memcmp( rcv_buf, cer_buf, cer_sz ) );
+			free(rcv_buf);
+		}
+		
+		
+		/* Now close the connection */
+		CHECK( 0, pthread_create(&thr, NULL, destroy_thr, client_side) );
+		fd_cnx_destroy(server_side);
+		CHECK( 0, pthread_join(thr, NULL) );
+		
+		/* Free the credentials */
+		gnutls_certificate_free_keys(hf.creds);
+		gnutls_certificate_free_cas(hf.creds);
+		gnutls_certificate_free_credentials(hf.creds);
+	}
+#endif /* DISABLE_SCTP */
+	
+	/* TCP Client / server emulating new Diameter behavior (handshake at connection directly) */
+	{
+		struct connect_flags cf;
+		struct handshake_flags hf;
+		
+		memset(&cf, 0, sizeof(cf));
+		cf.proto = IPPROTO_TCP;
+		
+		memset(&hf, 0, sizeof(hf));
+		
+		/* Initialize remote certificate */
+		CHECK_GNUTLS_DO( ret = gnutls_certificate_allocate_credentials (&hf.creds), );
+		CHECK( GNUTLS_E_SUCCESS, ret );
+		/* Set the CA */
+		CHECK_GNUTLS_DO( ret = gnutls_certificate_set_x509_trust_mem( hf.creds, &ca, GNUTLS_X509_FMT_PEM), );
+		CHECK( 1, ret );
+		/* Set the key */
+		CHECK_GNUTLS_DO( ret = gnutls_certificate_set_x509_key_mem( hf.creds, &client_cert, &client_priv, GNUTLS_X509_FMT_PEM), );
+		CHECK( GNUTLS_E_SUCCESS, ret );
+		
+		/* Start the client thread */
+		CHECK( 0, pthread_create(&thr, NULL, connect_thr, &cf) );
+
+		/* Accept the connection of the client */
+		server_side = fd_cnx_serv_accept(listener);
+		CHECK( 1, server_side ? 1 : 0 );
+		
+		/* Retrieve the client connection object */
+		CHECK( 0, pthread_join( thr, (void *)&client_side ) );
+		CHECK( 1, client_side ? 1 : 0 );
+		hf.cnx = client_side;
+		
+		/* Start the handshake directly */
+		CHECK( 0, pthread_create(&thr, NULL, handshake_thr, &hf) );
+		CHECK( 0, fd_cnx_handshake(server_side, GNUTLS_SERVER, ALGO_HANDSHAKE_DEFAULT, NULL, NULL) );
+		CHECK( 0, pthread_join(thr, NULL) );
+		CHECK( 0, hf.ret );
+		
+		/* Send a few TLS protected message, and replies */
+		for (i = 0; i < 2 * NB_STREAMS; i++) {
+			CHECK( 0, fd_cnx_send(server_side, cer_buf, cer_sz));
+			CHECK( 0, fd_cnx_receive(client_side, NULL, &rcv_buf, &rcv_sz));
+			CHECK( cer_sz, rcv_sz );
+			CHECK( 0, memcmp( rcv_buf, cer_buf, cer_sz ) );
+			free(rcv_buf);
+
+			CHECK( 0, fd_cnx_send(client_side, cer_buf, cer_sz));
+			CHECK( 0, fd_cnx_receive(server_side, NULL, &rcv_buf, &rcv_sz));
+			CHECK( cer_sz, rcv_sz );
+			CHECK( 0, memcmp( rcv_buf, cer_buf, cer_sz ) );
+			free(rcv_buf);
+		}
+		
+		/* Now close the connection */
+		CHECK( 0, pthread_create(&thr, NULL, destroy_thr, client_side) );
+		fd_cnx_destroy(server_side);
+		CHECK( 0, pthread_join(thr, NULL) );
+		
+		/* Free the credentials */
+		gnutls_certificate_free_keys(hf.creds);
+		gnutls_certificate_free_cas(hf.creds);
+		gnutls_certificate_free_credentials(hf.creds);
+	}
+	
+#ifndef DISABLE_SCTP
+	
+	
+	/* SCTP Client / server emulating new Diameter behavior (DTLS handshake at connection directly) */
+	TODO("Enabled after DTLS implementation");
+	if (0)
+	{
+		struct connect_flags cf;
+		struct handshake_flags hf;
+		
+		memset(&cf, 0, sizeof(cf));
+		cf.proto = IPPROTO_SCTP;
+		
+		memset(&hf, 0, sizeof(hf));
+		
+		/* Initialize remote certificate */
+		CHECK_GNUTLS_DO( ret = gnutls_certificate_allocate_credentials (&hf.creds), );
+		CHECK( GNUTLS_E_SUCCESS, ret );
+		/* Set the CA */
+		CHECK_GNUTLS_DO( ret = gnutls_certificate_set_x509_trust_mem( hf.creds, &ca, GNUTLS_X509_FMT_PEM), );
+		CHECK( 1, ret );
+		/* Set the key */
+		CHECK_GNUTLS_DO( ret = gnutls_certificate_set_x509_key_mem( hf.creds, &client_cert, &client_priv, GNUTLS_X509_FMT_PEM), );
+		CHECK( GNUTLS_E_SUCCESS, ret );
+		
+		/* Start the client thread */
+		CHECK( 0, pthread_create(&thr, NULL, connect_thr, &cf) );
+
+		/* Accept the connection of the client */
+		server_side = fd_cnx_serv_accept(listener_sctp);
+		CHECK( 1, server_side ? 1 : 0 );
+		
+		/* Retrieve the client connection object */
+		CHECK( 0, pthread_join( thr, (void *)&client_side ) );
+		CHECK( 1, client_side ? 1 : 0 );
+		hf.cnx = client_side;
+		
+		/* Start the handshake directly */
+		CHECK( 0, pthread_create(&thr, NULL, handshake_thr, &hf) );
+		CHECK( 0, fd_cnx_handshake(server_side, GNUTLS_SERVER, ALGO_HANDSHAKE_DEFAULT, NULL, NULL) );
+		CHECK( 0, pthread_join(thr, NULL) );
+		CHECK( 0, hf.ret );
+		
+		/* Send a few TLS protected messages, and replies */
+		for (i = 0; i < 2 * NB_STREAMS; i++) {
+			CHECK( 0, fd_cnx_send(server_side, cer_buf, cer_sz));
+			CHECK( 0, fd_cnx_receive(client_side, NULL, &rcv_buf, &rcv_sz));
+			CHECK( cer_sz, rcv_sz );
+			CHECK( 0, memcmp( rcv_buf, cer_buf, cer_sz ) );
+			free(rcv_buf);
+
+			CHECK( 0, fd_cnx_send(client_side, cer_buf, cer_sz));
+			CHECK( 0, fd_cnx_receive(server_side, NULL, &rcv_buf, &rcv_sz));
+			CHECK( cer_sz, rcv_sz );
+			CHECK( 0, memcmp( rcv_buf, cer_buf, cer_sz ) );
+			free(rcv_buf);
+		}
+		
+		
+		/* Now close the connection */
+		CHECK( 0, pthread_create(&thr, NULL, destroy_thr, client_side) );
+		fd_cnx_destroy(server_side);
+		CHECK( 0, pthread_join(thr, NULL) );
+		
+		/* Free the credentials */
+		gnutls_certificate_free_keys(hf.creds);
+		gnutls_certificate_free_cas(hf.creds);
+		gnutls_certificate_free_credentials(hf.creds);
+	}
+	
+	/* SCTP Client / server emulating old intermediary Diameter behavior (TLS handshake at connection directly) */
+	{
+		struct connect_flags cf;
+		struct handshake_flags hf;
+		
+		memset(&cf, 0, sizeof(cf));
+		cf.proto = IPPROTO_SCTP;
+		
+		memset(&hf, 0, sizeof(hf));
+		hf.algo = ALGO_HANDSHAKE_3436; /* this is mandatory for old TLS mechanism */
+		
+		/* Initialize remote certificate */
+		CHECK_GNUTLS_DO( ret = gnutls_certificate_allocate_credentials (&hf.creds), );
+		CHECK( GNUTLS_E_SUCCESS, ret );
+		/* Set the CA */
+		CHECK_GNUTLS_DO( ret = gnutls_certificate_set_x509_trust_mem( hf.creds, &ca, GNUTLS_X509_FMT_PEM), );
+		CHECK( 1, ret );
+		/* Set the key */
+		CHECK_GNUTLS_DO( ret = gnutls_certificate_set_x509_key_mem( hf.creds, &client_cert, &client_priv, GNUTLS_X509_FMT_PEM), );
+		CHECK( GNUTLS_E_SUCCESS, ret );
+		
+		/* Start the client thread */
+		CHECK( 0, pthread_create(&thr, NULL, connect_thr, &cf) );
+
+		/* Accept the connection of the client */
+		server_side = fd_cnx_serv_accept(listener_sctp);
+		CHECK( 1, server_side ? 1 : 0 );
+		
+		/* Retrieve the client connection object */
+		CHECK( 0, pthread_join( thr, (void *)&client_side ) );
+		CHECK( 1, client_side ? 1 : 0 );
+		hf.cnx = client_side;
+		
+		/* Start the handshake directly */
+		CHECK( 0, pthread_create(&thr, NULL, handshake_thr, &hf) );
+		CHECK( 0, fd_cnx_handshake(server_side, GNUTLS_SERVER, ALGO_HANDSHAKE_3436, NULL, NULL) );
+		CHECK( 0, pthread_join(thr, NULL) );
+		CHECK( 0, hf.ret );
+		
+		/* Send a few TLS protected messages, and replies */
+		for (i = 0; i < 2 * NB_STREAMS; i++) {
+			CHECK( 0, fd_cnx_send(server_side, cer_buf, cer_sz));
+			CHECK( 0, fd_cnx_receive(client_side, NULL, &rcv_buf, &rcv_sz));
+			CHECK( cer_sz, rcv_sz );
+			CHECK( 0, memcmp( rcv_buf, cer_buf, cer_sz ) );
+			free(rcv_buf);
+
+			CHECK( 0, fd_cnx_send(client_side, cer_buf, cer_sz));
+			CHECK( 0, fd_cnx_receive(server_side, NULL, &rcv_buf, &rcv_sz));
+			CHECK( cer_sz, rcv_sz );
+			CHECK( 0, memcmp( rcv_buf, cer_buf, cer_sz ) );
+			free(rcv_buf);
+		}
+		
+		
+		/* Now close the connection */
+		CHECK( 0, pthread_create(&thr, NULL, destroy_thr, client_side) );
+		fd_cnx_destroy(server_side);
+		CHECK( 0, pthread_join(thr, NULL) );
+		
+		/* Free the credentials */
+		gnutls_certificate_free_keys(hf.creds);
+		gnutls_certificate_free_cas(hf.creds);
+		gnutls_certificate_free_credentials(hf.creds);
+	}
+#endif /* DISABLE_SCTP */
+	
+	/* Test with different number of streams between server and client */
+#ifndef DISABLE_SCTP
+	/* DTLS / SCTP style */
+	TODO("Enabled after DTLS implementation");
+	if (0)
+	{
+		struct connect_flags cf;
+		struct handshake_flags hf;
+		
+		memset(&cf, 0, sizeof(cf));
+		cf.proto = IPPROTO_SCTP;
+		
+		memset(&hf, 0, sizeof(hf));
+		
+		/* Initialize remote certificate */
+		CHECK_GNUTLS_DO( ret = gnutls_certificate_allocate_credentials (&hf.creds), );
+		CHECK( GNUTLS_E_SUCCESS, ret );
+		/* Set the CA */
+		CHECK_GNUTLS_DO( ret = gnutls_certificate_set_x509_trust_mem( hf.creds, &ca, GNUTLS_X509_FMT_PEM), );
+		CHECK( 1, ret );
+		/* Set the key */
+		CHECK_GNUTLS_DO( ret = gnutls_certificate_set_x509_key_mem( hf.creds, &client_cert, &client_priv, GNUTLS_X509_FMT_PEM), );
+		CHECK( GNUTLS_E_SUCCESS, ret );
+		
+		/* Start the client thread with more streams than the server */
+		fd_g_config->cnf_sctp_str = 2 * NB_STREAMS;
+		CHECK( 0, pthread_create(&thr, NULL, connect_thr, &cf) );
+
+		/* Accept the connection of the client */
+		server_side = fd_cnx_serv_accept(listener_sctp);
+		CHECK( 1, server_side ? 1 : 0 );
+		
+		/* Retrieve the client connection object */
+		CHECK( 0, pthread_join( thr, (void *)&client_side ) );
+		CHECK( 1, client_side ? 1 : 0 );
+		hf.cnx = client_side;
+		
+		/* Start the handshake directly */
+		CHECK( 0, pthread_create(&thr, NULL, handshake_thr, &hf) );
+		CHECK( 0, fd_cnx_handshake(server_side, GNUTLS_SERVER, ALGO_HANDSHAKE_DEFAULT, NULL, NULL) );
+		CHECK( 0, pthread_join(thr, NULL) );
+		CHECK( 0, hf.ret );
+		
+		/* Send a few TLS protected message, and replies */
+		for (i = 0; i < 4 * NB_STREAMS; i++) {
+			CHECK( 0, fd_cnx_send(server_side, cer_buf, cer_sz));
+			CHECK( 0, fd_cnx_receive(client_side, NULL, &rcv_buf, &rcv_sz));
+			CHECK( cer_sz, rcv_sz );
+			CHECK( 0, memcmp( rcv_buf, cer_buf, cer_sz ) );
+			free(rcv_buf);
+
+			CHECK( 0, fd_cnx_send(client_side, cer_buf, cer_sz));
+			CHECK( 0, fd_cnx_receive(server_side, NULL, &rcv_buf, &rcv_sz));
+			CHECK( cer_sz, rcv_sz );
+			CHECK( 0, memcmp( rcv_buf, cer_buf, cer_sz ) );
+			free(rcv_buf);
+		}
+		
+		/* Now close the connection */
+		CHECK( 0, pthread_create(&thr, NULL, destroy_thr, client_side) );
+		fd_cnx_destroy(server_side);
+		CHECK( 0, pthread_join(thr, NULL) );
+		
+		/* Do the same test but with more streams on the server this time */
+		fd_g_config->cnf_sctp_str = NB_STREAMS / 2;
+		CHECK( 0, pthread_create(&thr, NULL, connect_thr, &cf) );
+
+		/* Accept the connection of the client */
+		server_side = fd_cnx_serv_accept(listener_sctp);
+		CHECK( 1, server_side ? 1 : 0 );
+		
+		/* Retrieve the client connection object */
+		CHECK( 0, pthread_join( thr, (void *)&client_side ) );
+		CHECK( 1, client_side ? 1 : 0 );
+		hf.cnx = client_side;
+		
+		/* Start the handshake directly */
+		CHECK( 0, pthread_create(&thr, NULL, handshake_thr, &hf) );
+		CHECK( 0, fd_cnx_handshake(server_side, GNUTLS_SERVER, ALGO_HANDSHAKE_DEFAULT, NULL, NULL) );
+		CHECK( 0, pthread_join(thr, NULL) );
+		CHECK( 0, hf.ret );
+		
+		/* Send a few TLS protected message, and replies */
+		for (i = 0; i < 2 * NB_STREAMS; i++) {
+			CHECK( 0, fd_cnx_send(server_side, cer_buf, cer_sz));
+			CHECK( 0, fd_cnx_receive(client_side, NULL, &rcv_buf, &rcv_sz));
+			CHECK( cer_sz, rcv_sz );
+			CHECK( 0, memcmp( rcv_buf, cer_buf, cer_sz ) );
+			free(rcv_buf);
+
+			CHECK( 0, fd_cnx_send(client_side, cer_buf, cer_sz));
+			CHECK( 0, fd_cnx_receive(server_side, NULL, &rcv_buf, &rcv_sz));
+			CHECK( cer_sz, rcv_sz );
+			CHECK( 0, memcmp( rcv_buf, cer_buf, cer_sz ) );
+			free(rcv_buf);
+		}
+		
+		/* Now close the connection */
+		CHECK( 0, pthread_create(&thr, NULL, destroy_thr, client_side) );
+		fd_cnx_destroy(server_side);
+		CHECK( 0, pthread_join(thr, NULL) );
+		
+		
+		/* Free the credentials */
+		gnutls_certificate_free_keys(hf.creds);
+		gnutls_certificate_free_cas(hf.creds);
+		gnutls_certificate_free_credentials(hf.creds);
+	}
+	
+	/* TLS / SCTP style */
+	{
+		struct connect_flags cf;
+		struct handshake_flags hf;
+		
+		memset(&cf, 0, sizeof(cf));
+		cf.proto = IPPROTO_SCTP;
+		
+		memset(&hf, 0, sizeof(hf));
+		hf.algo = ALGO_HANDSHAKE_3436;
+		
+		/* Initialize remote certificate */
+		CHECK_GNUTLS_DO( ret = gnutls_certificate_allocate_credentials (&hf.creds), );
+		CHECK( GNUTLS_E_SUCCESS, ret );
+		/* Set the CA */
+		CHECK_GNUTLS_DO( ret = gnutls_certificate_set_x509_trust_mem( hf.creds, &ca, GNUTLS_X509_FMT_PEM), );
+		CHECK( 1, ret );
+		/* Set the key */
+		CHECK_GNUTLS_DO( ret = gnutls_certificate_set_x509_key_mem( hf.creds, &client_cert, &client_priv, GNUTLS_X509_FMT_PEM), );
+		CHECK( GNUTLS_E_SUCCESS, ret );
+		
+		/* Start the client thread with more streams than the server */
+		fd_g_config->cnf_sctp_str = 2 * NB_STREAMS;
+		CHECK( 0, pthread_create(&thr, NULL, connect_thr, &cf) );
+
+		/* Accept the connection of the client */
+		server_side = fd_cnx_serv_accept(listener_sctp);
+		CHECK( 1, server_side ? 1 : 0 );
+		
+		/* Retrieve the client connection object */
+		CHECK( 0, pthread_join( thr, (void *)&client_side ) );
+		CHECK( 1, client_side ? 1 : 0 );
+		hf.cnx = client_side;
+		
+		/* Start the handshake directly */
+		CHECK( 0, pthread_create(&thr, NULL, handshake_thr, &hf) );
+		CHECK( 0, fd_cnx_handshake(server_side, GNUTLS_SERVER, ALGO_HANDSHAKE_3436, NULL, NULL) );
+		CHECK( 0, pthread_join(thr, NULL) );
+		CHECK( 0, hf.ret );
+		
+		/* Send a few TLS protected message, and replies */
+		for (i = 0; i < 4 * NB_STREAMS; i++) {
+			CHECK( 0, fd_cnx_send(server_side, cer_buf, cer_sz));
+			CHECK( 0, fd_cnx_receive(client_side, NULL, &rcv_buf, &rcv_sz));
+			CHECK( cer_sz, rcv_sz );
+			CHECK( 0, memcmp( rcv_buf, cer_buf, cer_sz ) );
+			free(rcv_buf);
+
+			CHECK( 0, fd_cnx_send(client_side, cer_buf, cer_sz));
+			CHECK( 0, fd_cnx_receive(server_side, NULL, &rcv_buf, &rcv_sz));
+			CHECK( cer_sz, rcv_sz );
+			CHECK( 0, memcmp( rcv_buf, cer_buf, cer_sz ) );
+			free(rcv_buf);
+		}
+		
+		/* Now close the connection */
+		CHECK( 0, pthread_create(&thr, NULL, destroy_thr, client_side) );
+		fd_cnx_destroy(server_side);
+		CHECK( 0, pthread_join(thr, NULL) );
+		
+		/* Do the same test but with more streams on the server this time */
+		fd_g_config->cnf_sctp_str = NB_STREAMS / 2;
+		CHECK( 0, pthread_create(&thr, NULL, connect_thr, &cf) );
+
+		/* Accept the connection of the client */
+		server_side = fd_cnx_serv_accept(listener_sctp);
+		CHECK( 1, server_side ? 1 : 0 );
+		
+		/* Retrieve the client connection object */
+		CHECK( 0, pthread_join( thr, (void *)&client_side ) );
+		CHECK( 1, client_side ? 1 : 0 );
+		hf.cnx = client_side;
+		
+		/* Start the handshake directly */
+		CHECK( 0, pthread_create(&thr, NULL, handshake_thr, &hf) );
+		CHECK( 0, fd_cnx_handshake(server_side, GNUTLS_SERVER, ALGO_HANDSHAKE_3436, NULL, NULL) );
+		CHECK( 0, pthread_join(thr, NULL) );
+		CHECK( 0, hf.ret );
+		
+		/* Send a few TLS protected message, and replies */
+		for (i = 0; i < 2 * NB_STREAMS; i++) {
+			CHECK( 0, fd_cnx_send(server_side, cer_buf, cer_sz));
+			CHECK( 0, fd_cnx_receive(client_side, NULL, &rcv_buf, &rcv_sz));
+			CHECK( cer_sz, rcv_sz );
+			CHECK( 0, memcmp( rcv_buf, cer_buf, cer_sz ) );
+			free(rcv_buf);
+
+			CHECK( 0, fd_cnx_send(client_side, cer_buf, cer_sz));
+			CHECK( 0, fd_cnx_receive(server_side, NULL, &rcv_buf, &rcv_sz));
+			CHECK( cer_sz, rcv_sz );
+			CHECK( 0, memcmp( rcv_buf, cer_buf, cer_sz ) );
+			free(rcv_buf);
+		}
+		
+		/* Now close the connection */
+		CHECK( 0, pthread_create(&thr, NULL, destroy_thr, client_side) );
+		fd_cnx_destroy(server_side);
+		CHECK( 0, pthread_join(thr, NULL) );
+		
+		
+		/* Free the credentials */
+		gnutls_certificate_free_keys(hf.creds);
+		gnutls_certificate_free_cas(hf.creds);
+		gnutls_certificate_free_credentials(hf.creds);
+	}
+	
+#endif /* DISABLE_SCTP */
+	
+	
+	/* Basic operation tested successfully, now test we detect error conditions */
+
+	/* Untrusted certificate, TCP */
+	{
+		struct connect_flags cf;
+		struct handshake_flags hf;
+		
+		memset(&cf, 0, sizeof(cf));
+		cf.proto = IPPROTO_TCP;
+		
+		memset(&hf, 0, sizeof(hf));
+		
+		/* Initialize remote certificate */
+		CHECK_GNUTLS_DO( ret = gnutls_certificate_allocate_credentials (&hf.creds), );
+		CHECK( GNUTLS_E_SUCCESS, ret );
+		/* Set the CA */
+		CHECK_GNUTLS_DO( ret = gnutls_certificate_set_x509_trust_mem( hf.creds, &notrust_ca, GNUTLS_X509_FMT_PEM), );
+		CHECK_GNUTLS_DO( ret = gnutls_certificate_set_x509_trust_mem( hf.creds, &ca, GNUTLS_X509_FMT_PEM), );
+		CHECK( 1, ret );
+		/* Set the key */
+		CHECK_GNUTLS_DO( ret = gnutls_certificate_set_x509_key_mem( hf.creds, &notrust_cert, &notrust_priv, GNUTLS_X509_FMT_PEM), );
+		CHECK( GNUTLS_E_SUCCESS, ret );
+		
+		/* Start the client thread */
+		CHECK( 0, pthread_create(&thr, NULL, connect_thr, &cf) );
+
+		/* Accept the connection of the client */
+		server_side = fd_cnx_serv_accept(listener);
+		CHECK( 1, server_side ? 1 : 0 );
+		
+		/* Retrieve the client connection object */
+		CHECK( 0, pthread_join( thr, (void *)&client_side ) );
+		CHECK( 1, client_side ? 1 : 0 );
+		hf.cnx = client_side;
+		
+		/* Start the handshake directly */
+		CHECK( 0, pthread_create(&thr, NULL, handshake_thr, &hf) );
+		CHECK( EINVAL, fd_cnx_handshake(server_side, GNUTLS_SERVER, ALGO_HANDSHAKE_DEFAULT, NULL, NULL) );
+		fd_cnx_destroy(server_side);
+		
+		CHECK( 0, pthread_join(thr, NULL) );
+		
+		/* Now close the connection */
+		CHECK( 0, pthread_create(&thr, NULL, destroy_thr, client_side) );
+		CHECK( 0, pthread_join(thr, NULL) );
+		
+		/* Free the credentials */
+		gnutls_certificate_free_keys(hf.creds);
+		gnutls_certificate_free_cas(hf.creds);
+		gnutls_certificate_free_credentials(hf.creds);
+	}
+	
+	/* Same in SCTP */
+#ifndef DISABLE_SCTP
+	/* DTLS */
+	TODO("Enabled after DTLS implementation");
+	if (0)
+	{
+		struct connect_flags cf;
+		struct handshake_flags hf;
+		
+		memset(&cf, 0, sizeof(cf));
+		cf.proto = IPPROTO_SCTP;
+		
+		memset(&hf, 0, sizeof(hf));
+		
+		/* Initialize remote certificate */
+		CHECK_GNUTLS_DO( ret = gnutls_certificate_allocate_credentials (&hf.creds), );
+		CHECK( GNUTLS_E_SUCCESS, ret );
+		/* Set the CA */
+		CHECK_GNUTLS_DO( ret = gnutls_certificate_set_x509_trust_mem( hf.creds, &notrust_ca, GNUTLS_X509_FMT_PEM), );
+		CHECK_GNUTLS_DO( ret = gnutls_certificate_set_x509_trust_mem( hf.creds, &ca, GNUTLS_X509_FMT_PEM), );
+		CHECK( 1, ret );
+		/* Set the key */
+		CHECK_GNUTLS_DO( ret = gnutls_certificate_set_x509_key_mem( hf.creds, &notrust_cert, &notrust_priv, GNUTLS_X509_FMT_PEM), );
+		CHECK( GNUTLS_E_SUCCESS, ret );
+		
+		/* Start the client thread */
+		CHECK( 0, pthread_create(&thr, NULL, connect_thr, &cf) );
+
+		/* Accept the connection of the client */
+		server_side = fd_cnx_serv_accept(listener_sctp);
+		CHECK( 1, server_side ? 1 : 0 );
+		
+		/* Retrieve the client connection object */
+		CHECK( 0, pthread_join( thr, (void *)&client_side ) );
+		CHECK( 1, client_side ? 1 : 0 );
+		hf.cnx = client_side;
+		
+		/* Start the handshake directly */
+		CHECK( 0, pthread_create(&thr, NULL, handshake_thr, &hf) );
+		CHECK( EINVAL, fd_cnx_handshake(server_side, GNUTLS_SERVER, ALGO_HANDSHAKE_DEFAULT, NULL, NULL) );
+		fd_cnx_destroy(server_side);
+		CHECK( 0, pthread_join(thr, NULL) );
+		
+		/* Now close the connection */
+		CHECK( 0, pthread_create(&thr, NULL, destroy_thr, client_side) );
+		CHECK( 0, pthread_join(thr, NULL) );
+		
+		/* Free the credentials */
+		gnutls_certificate_free_keys(hf.creds);
+		gnutls_certificate_free_cas(hf.creds);
+		gnutls_certificate_free_credentials(hf.creds);
+	}
+	
+	/* TLS */
+	{
+		struct connect_flags cf;
+		struct handshake_flags hf;
+		
+		memset(&cf, 0, sizeof(cf));
+		cf.proto = IPPROTO_SCTP;
+		
+		memset(&hf, 0, sizeof(hf));
+		hf.algo = ALGO_HANDSHAKE_3436;
+		
+		/* Initialize remote certificate */
+		CHECK_GNUTLS_DO( ret = gnutls_certificate_allocate_credentials (&hf.creds), );
+		CHECK( GNUTLS_E_SUCCESS, ret );
+		/* Set the CA */
+		CHECK_GNUTLS_DO( ret = gnutls_certificate_set_x509_trust_mem( hf.creds, &notrust_ca, GNUTLS_X509_FMT_PEM), );
+		CHECK_GNUTLS_DO( ret = gnutls_certificate_set_x509_trust_mem( hf.creds, &ca, GNUTLS_X509_FMT_PEM), );
+		CHECK( 1, ret );
+		/* Set the key */
+		CHECK_GNUTLS_DO( ret = gnutls_certificate_set_x509_key_mem( hf.creds, &notrust_cert, &notrust_priv, GNUTLS_X509_FMT_PEM), );
+		CHECK( GNUTLS_E_SUCCESS, ret );
+		
+		/* Start the client thread */
+		CHECK( 0, pthread_create(&thr, NULL, connect_thr, &cf) );
+
+		/* Accept the connection of the client */
+		server_side = fd_cnx_serv_accept(listener_sctp);
+		CHECK( 1, server_side ? 1 : 0 );
+		
+		/* Retrieve the client connection object */
+		CHECK( 0, pthread_join( thr, (void *)&client_side ) );
+		CHECK( 1, client_side ? 1 : 0 );
+		hf.cnx = client_side;
+		
+		/* Start the handshake directly */
+		CHECK( 0, pthread_create(&thr, NULL, handshake_thr, &hf) );
+		CHECK( EINVAL, fd_cnx_handshake(server_side, GNUTLS_SERVER, ALGO_HANDSHAKE_3436, NULL, NULL) );
+		fd_cnx_destroy(server_side);
+		CHECK( 0, pthread_join(thr, NULL) );
+		
+		/* Now close the connection */
+		CHECK( 0, pthread_create(&thr, NULL, destroy_thr, client_side) );
+		CHECK( 0, pthread_join(thr, NULL) );
+		
+		/* Free the credentials */
+		gnutls_certificate_free_keys(hf.creds);
+		gnutls_certificate_free_cas(hf.creds);
+		gnutls_certificate_free_credentials(hf.creds);
+	}
+#endif /* DISABLE_SCTP */
+	
+	/* Expired certificate */
+	{
+		struct connect_flags cf;
+		struct handshake_flags hf;
+		
+		memset(&cf, 0, sizeof(cf));
+		cf.proto = IPPROTO_TCP;
+		
+		memset(&hf, 0, sizeof(hf));
+		
+		/* Initialize remote certificate */
+		CHECK_GNUTLS_DO( ret = gnutls_certificate_allocate_credentials (&hf.creds), );
+		CHECK( GNUTLS_E_SUCCESS, ret );
+		/* Set the CA */
+		CHECK_GNUTLS_DO( ret = gnutls_certificate_set_x509_trust_mem( hf.creds, &ca, GNUTLS_X509_FMT_PEM), );
+		CHECK( 1, ret );
+		/* Set the key */
+		CHECK_GNUTLS_DO( ret = gnutls_certificate_set_x509_key_mem( hf.creds, &expired_cert, &expired_priv, GNUTLS_X509_FMT_PEM), );
+		CHECK( GNUTLS_E_SUCCESS, ret );
+		
+		/* Start the client thread */
+		CHECK( 0, pthread_create(&thr, NULL, connect_thr, &cf) );
+
+		/* Accept the connection of the client */
+		server_side = fd_cnx_serv_accept(listener);
+		CHECK( 1, server_side ? 1 : 0 );
+		
+		/* Retrieve the client connection object */
+		CHECK( 0, pthread_join( thr, (void *)&client_side ) );
+		CHECK( 1, client_side ? 1 : 0 );
+		hf.cnx = client_side;
+		
+		/* Start the handshake directly */
+		CHECK( 0, pthread_create(&thr, NULL, handshake_thr, &hf) );
+		CHECK( EINVAL, fd_cnx_handshake(server_side, GNUTLS_SERVER, ALGO_HANDSHAKE_DEFAULT, NULL, NULL) );
+		fd_cnx_destroy(server_side);
+		CHECK( 0, pthread_join(thr, NULL) );
+		
+		/* Now close the connection */
+		CHECK( 0, pthread_create(&thr, NULL, destroy_thr, client_side) );
+		CHECK( 0, pthread_join(thr, NULL) );
+		
+		/* Free the credentials */
+		gnutls_certificate_free_keys(hf.creds);
+		gnutls_certificate_free_cas(hf.creds);
+		gnutls_certificate_free_credentials(hf.creds);
+	}
+	
+	/* Non matching hostname */
+	
+	{
+		struct connect_flags cf;
+		struct handshake_flags hf;
+		
+		memset(&cf, 0, sizeof(cf));
+		cf.proto = IPPROTO_TCP;
+		
+		memset(&hf, 0, sizeof(hf));
+		
+		/* Initialize remote certificate */
+		CHECK_GNUTLS_DO( ret = gnutls_certificate_allocate_credentials (&hf.creds), );
+		CHECK( GNUTLS_E_SUCCESS, ret );
+		/* Set the CA */
+		CHECK_GNUTLS_DO( ret = gnutls_certificate_set_x509_trust_mem( hf.creds, &ca, GNUTLS_X509_FMT_PEM), );
+		CHECK( 1, ret );
+		/* Set the key */
+		CHECK_GNUTLS_DO( ret = gnutls_certificate_set_x509_key_mem( hf.creds, &client_cert, &client_priv, GNUTLS_X509_FMT_PEM), );
+		CHECK( GNUTLS_E_SUCCESS, ret );
+		
+		/* Start the client thread */
+		CHECK( 0, pthread_create(&thr, NULL, connect_thr, &cf) );
+
+		/* Accept the connection of the client */
+		server_side = fd_cnx_serv_accept(listener);
+		CHECK( 1, server_side ? 1 : 0 );
+		
+		/* Retrieve the client connection object */
+		CHECK( 0, pthread_join( thr, (void *)&client_side ) );
+		CHECK( 1, client_side ? 1 : 0 );
+		hf.cnx = client_side;
+		
+		/* Set the correct hostname we expect from the client (in the server) */
+		fd_cnx_sethostname(server_side, "client.test");
+		
+		/* Start the handshake, check it is successful */
+		CHECK( 0, pthread_create(&thr, NULL, handshake_thr, &hf) );
+		CHECK( 0, fd_cnx_handshake(server_side, GNUTLS_SERVER, ALGO_HANDSHAKE_DEFAULT, NULL, NULL) );
+		CHECK( 0, pthread_join(thr, NULL) );
+		CHECK( 0, hf.ret );
+		
+		/* Now close the connection */
+		CHECK( 0, pthread_create(&thr, NULL, destroy_thr, client_side) );
+		fd_cnx_destroy(server_side);
+		CHECK( 0, pthread_join(thr, NULL) );
+		
+		/* Do it again with an invalid hostname */
+		CHECK( 0, pthread_create(&thr, NULL, connect_thr, &cf) );
+
+		/* Accept the connection of the client */
+		server_side = fd_cnx_serv_accept(listener);
+		CHECK( 1, server_side ? 1 : 0 );
+		
+		/* Retrieve the client connection object */
+		CHECK( 0, pthread_join( thr, (void *)&client_side ) );
+		CHECK( 1, client_side ? 1 : 0 );
+		hf.cnx = client_side;
+		
+		/* Set the correct hostname we expect from the client (in the server) */
+		fd_cnx_sethostname(server_side, "nomatch.test");
+		
+		/* Start the handshake, check it is successful */
+		CHECK( 0, pthread_create(&thr, NULL, handshake_thr, &hf) );
+		CHECK( EINVAL, fd_cnx_handshake(server_side, GNUTLS_SERVER, ALGO_HANDSHAKE_DEFAULT, NULL, NULL) );
+		fd_cnx_destroy(server_side);
+		CHECK( 0, pthread_join(thr, NULL) );
+		
+		/* Now close the connection */
+		CHECK( 0, pthread_create(&thr, NULL, destroy_thr, client_side) );
+		CHECK( 0, pthread_join(thr, NULL) );
+		
+		/* Free the credentials */
+		gnutls_certificate_free_keys(hf.creds);
+		gnutls_certificate_free_cas(hf.creds);
+		gnutls_certificate_free_credentials(hf.creds);
+	}
+	
+	/* Test the other functions of the module */
+	{
+		struct connect_flags cf;
+		struct handshake_flags hf;
+		char * str;
+		const gnutls_datum_t *cert_list;
+		unsigned int cert_list_size;
+		struct fifo * myfifo = NULL;
+		struct timespec now;
+		int ev_code;
+		
+		memset(&cf, 0, sizeof(cf));
+		cf.proto = IPPROTO_TCP;
+		
+		memset(&hf, 0, sizeof(hf));
+		
+		/* Initialize remote certificate */
+		CHECK_GNUTLS_DO( ret = gnutls_certificate_allocate_credentials (&hf.creds), );
+		CHECK( GNUTLS_E_SUCCESS, ret );
+		/* Set the CA */
+		CHECK_GNUTLS_DO( ret = gnutls_certificate_set_x509_trust_mem( hf.creds, &ca, GNUTLS_X509_FMT_PEM), );
+		CHECK( 1, ret );
+		/* Set the key */
+		CHECK_GNUTLS_DO( ret = gnutls_certificate_set_x509_key_mem( hf.creds, &client_cert, &client_priv, GNUTLS_X509_FMT_PEM), );
+		CHECK( GNUTLS_E_SUCCESS, ret );
+		
+		/* Start the client thread */
+		CHECK( 0, pthread_create(&thr, NULL, connect_thr, &cf) );
+
+		/* Accept the connection of the client */
+		server_side = fd_cnx_serv_accept(listener);
+		CHECK( 1, server_side ? 1 : 0 );
+		
+		/* Retrieve the client connection object */
+		CHECK( 0, pthread_join( thr, (void *)&client_side ) );
+		CHECK( 1, client_side ? 1 : 0 );
+		hf.cnx = client_side;
+		
+		/* Start the handshake */
+		CHECK( 0, pthread_create(&thr, NULL, handshake_thr, &hf) );
+		CHECK( 0, fd_cnx_handshake(server_side, GNUTLS_SERVER, ALGO_HANDSHAKE_DEFAULT, NULL, NULL) );
+		CHECK( 0, pthread_join(thr, NULL) );
+		CHECK( 0, hf.ret );
+		
+		/* Test some simple functions */
+		
+		/* fd_cnx_getid */
+		str = fd_cnx_getid(server_side);
+		CHECK( 1, str ? 1 : 0 );
+		CHECK( 1, (str[0] != '\0') ? 1 : 0 );
+		
+		/* fd_cnx_getproto */
+		i = fd_cnx_getproto(server_side);
+		CHECK( IPPROTO_TCP, i);
+		
+		/* fd_cnx_getTLS */
+		i = fd_cnx_getTLS(server_side);
+		CHECK( 1, i ? 1 : 0 );
+		
+		/* fd_cnx_getcred */
+		CHECK( 0, fd_cnx_getcred(server_side, &cert_list, &cert_list_size) );
+		CHECK( 1, (cert_list_size > 0) ? 1 : 0 );
+		/* We could also verify that the cert_list really contains the client_cert and ca certificates */
+		
+		/* fd_cnx_getremoteid */
+		str = fd_cnx_getremoteid(server_side);
+		CHECK( 1, str ? 1 : 0 );
+		CHECK( 1, (str[0] != '\0') ? 1 : 0 );
+		
+		/* fd_cnx_recv_setaltfifo */
+		CHECK( 0, fd_cnx_send(client_side, cer_buf, cer_sz));
+		CHECK( 0, fd_fifo_new(&myfifo, 0) );
+		CHECK( 0, fd_cnx_recv_setaltfifo(server_side, myfifo) );
+		CHECK( 0, clock_gettime(CLOCK_REALTIME, &now) );
+		do {
+			CHECK( 0, fd_event_timedget(myfifo, &now, ETIMEDOUT, &ev_code, NULL, (void *)&rcv_buf) );
+			free(rcv_buf);
+		} while (ev_code != FDEVP_CNX_MSG_RECV);
+		
+		/* Now close the connection */
+		CHECK( 0, pthread_create(&thr, NULL, destroy_thr, client_side) );
+		fd_cnx_destroy(server_side);
+		CHECK( 0, pthread_join(thr, NULL) );
+		
+		fd_event_destroy(&myfifo, free);
+		
+		/* Free the credentials */
+		gnutls_certificate_free_keys(hf.creds);
+		gnutls_certificate_free_cas(hf.creds);
+		gnutls_certificate_free_credentials(hf.creds);
+	}
+	
+#ifndef DISABLE_SCTP
+	/* And re-test with a SCTP connection */
+	TODO("Enabled after DTLS implementation");
+	if (0)
+	{
+		struct connect_flags cf;
+		struct handshake_flags hf;
+		char * str;
+		const gnutls_datum_t *cert_list;
+		unsigned int cert_list_size;
+		struct fifo * myfifo = NULL;
+		struct timespec now;
+		int ev_code;
+		
+		memset(&cf, 0, sizeof(cf));
+		cf.proto = IPPROTO_SCTP;
+		
+		memset(&hf, 0, sizeof(hf));
+		
+		/* Initialize remote certificate */
+		CHECK_GNUTLS_DO( ret = gnutls_certificate_allocate_credentials (&hf.creds), );
+		CHECK( GNUTLS_E_SUCCESS, ret );
+		/* Set the CA */
+		CHECK_GNUTLS_DO( ret = gnutls_certificate_set_x509_trust_mem( hf.creds, &ca, GNUTLS_X509_FMT_PEM), );
+		CHECK( 1, ret );
+		/* Set the key */
+		CHECK_GNUTLS_DO( ret = gnutls_certificate_set_x509_key_mem( hf.creds, &client_cert, &client_priv, GNUTLS_X509_FMT_PEM), );
+		CHECK( GNUTLS_E_SUCCESS, ret );
+		
+		/* Start the client thread */
+		CHECK( 0, pthread_create(&thr, NULL, connect_thr, &cf) );
+
+		/* Accept the connection of the client */
+		server_side = fd_cnx_serv_accept(listener_sctp);
+		CHECK( 1, server_side ? 1 : 0 );
+		
+		/* Retrieve the client connection object */
+		CHECK( 0, pthread_join( thr, (void *)&client_side ) );
+		CHECK( 1, client_side ? 1 : 0 );
+		hf.cnx = client_side;
+		
+		/* Start the handshake */
+		CHECK( 0, pthread_create(&thr, NULL, handshake_thr, &hf) );
+		CHECK( 0, fd_cnx_handshake(server_side, GNUTLS_SERVER, ALGO_HANDSHAKE_DEFAULT, NULL, NULL) );
+		CHECK( 0, pthread_join(thr, NULL) );
+		CHECK( 0, hf.ret );
+		
+		/* Test some simple functions */
+		
+		/* fd_cnx_getid */
+		str = fd_cnx_getid(server_side);
+		CHECK( 1, str ? 1 : 0 );
+		CHECK( 1, (str[0] != '\0') ? 1 : 0 );
+		
+		/* fd_cnx_getproto */
+		i = fd_cnx_getproto(server_side);
+		CHECK( IPPROTO_SCTP, i);
+		
+		/* fd_cnx_getTLS */
+		i = fd_cnx_getTLS(server_side);
+		CHECK( 1, i ? 1 : 0 );
+		
+		/* fd_cnx_getcred */
+		CHECK( 0, fd_cnx_getcred(server_side, &cert_list, &cert_list_size) );
+		CHECK( 1, (cert_list_size > 0) ? 1 : 0 );
+		/* We could also verify that the cert_list really contains the client_cert and ca certificates */
+		
+		/* fd_cnx_getremoteid */
+		str = fd_cnx_getremoteid(server_side);
+		CHECK( 1, str ? 1 : 0 );
+		CHECK( 1, (str[0] != '\0') ? 1 : 0 );
+		
+		/* fd_cnx_recv_setaltfifo */
+		CHECK( 0, fd_cnx_send(client_side, cer_buf, cer_sz));
+		CHECK( 0, fd_fifo_new(&myfifo, 0) );
+		CHECK( 0, fd_cnx_recv_setaltfifo(server_side, myfifo) );
+		CHECK( 0, clock_gettime(CLOCK_REALTIME, &now) );
+		do {
+			CHECK( 0, fd_event_timedget(myfifo, &now, ETIMEDOUT, &ev_code, NULL, (void *)&rcv_buf) );
+			free(rcv_buf);
+		} while (ev_code != FDEVP_CNX_MSG_RECV);
+		
+		/* Now close the connection */
+		CHECK( 0, pthread_create(&thr, NULL, destroy_thr, client_side) );
+		fd_cnx_destroy(server_side);
+		CHECK( 0, pthread_join(thr, NULL) );
+		
+		fd_event_destroy(&myfifo, free);
+		
+		/* Free the credentials */
+		gnutls_certificate_free_keys(hf.creds);
+		gnutls_certificate_free_cas(hf.creds);
+		gnutls_certificate_free_credentials(hf.creds);
+	}
+	
+	/* TLS */
+	{
+		struct connect_flags cf;
+		struct handshake_flags hf;
+		char * str;
+		const gnutls_datum_t *cert_list;
+		unsigned int cert_list_size;
+		struct fifo * myfifo = NULL;
+		struct timespec now;
+		int ev_code;
+		
+		memset(&cf, 0, sizeof(cf));
+		cf.proto = IPPROTO_SCTP;
+		
+		memset(&hf, 0, sizeof(hf));
+		hf.algo = ALGO_HANDSHAKE_3436;
+		
+		/* Initialize remote certificate */
+		CHECK_GNUTLS_DO( ret = gnutls_certificate_allocate_credentials (&hf.creds), );
+		CHECK( GNUTLS_E_SUCCESS, ret );
+		/* Set the CA */
+		CHECK_GNUTLS_DO( ret = gnutls_certificate_set_x509_trust_mem( hf.creds, &ca, GNUTLS_X509_FMT_PEM), );
+		CHECK( 1, ret );
+		/* Set the key */
+		CHECK_GNUTLS_DO( ret = gnutls_certificate_set_x509_key_mem( hf.creds, &client_cert, &client_priv, GNUTLS_X509_FMT_PEM), );
+		CHECK( GNUTLS_E_SUCCESS, ret );
+		
+		/* Start the client thread */
+		CHECK( 0, pthread_create(&thr, NULL, connect_thr, &cf) );
+
+		/* Accept the connection of the client */
+		server_side = fd_cnx_serv_accept(listener_sctp);
+		CHECK( 1, server_side ? 1 : 0 );
+		
+		/* Retrieve the client connection object */
+		CHECK( 0, pthread_join( thr, (void *)&client_side ) );
+		CHECK( 1, client_side ? 1 : 0 );
+		hf.cnx = client_side;
+		
+		/* Start the handshake */
+		CHECK( 0, pthread_create(&thr, NULL, handshake_thr, &hf) );
+		CHECK( 0, fd_cnx_handshake(server_side, GNUTLS_SERVER, ALGO_HANDSHAKE_3436, NULL, NULL) );
+		CHECK( 0, pthread_join(thr, NULL) );
+		CHECK( 0, hf.ret );
+		
+		/* Test some simple functions */
+		
+		/* fd_cnx_getid */
+		str = fd_cnx_getid(server_side);
+		CHECK( 1, str ? 1 : 0 );
+		CHECK( 1, (str[0] != '\0') ? 1 : 0 );
+		
+		/* fd_cnx_getproto */
+		i = fd_cnx_getproto(server_side);
+		CHECK( IPPROTO_SCTP, i);
+		
+		/* fd_cnx_getTLS */
+		i = fd_cnx_getTLS(server_side);
+		CHECK( 1, i ? 1 : 0 );
+		
+		/* fd_cnx_getcred */
+		CHECK( 0, fd_cnx_getcred(server_side, &cert_list, &cert_list_size) );
+		CHECK( 1, (cert_list_size > 0) ? 1 : 0 );
+		/* We could also verify that the cert_list really contains the client_cert and ca certificates */
+		
+		/* fd_cnx_getremoteid */
+		str = fd_cnx_getremoteid(server_side);
+		CHECK( 1, str ? 1 : 0 );
+		CHECK( 1, (str[0] != '\0') ? 1 : 0 );
+		
+		/* fd_cnx_recv_setaltfifo */
+		CHECK( 0, fd_cnx_send(client_side, cer_buf, cer_sz));
+		CHECK( 0, fd_fifo_new(&myfifo, 0) );
+		CHECK( 0, fd_cnx_recv_setaltfifo(server_side, myfifo) );
+		CHECK( 0, clock_gettime(CLOCK_REALTIME, &now) );
+		do {
+			CHECK( 0, fd_event_timedget(myfifo, &now, ETIMEDOUT, &ev_code, NULL, (void *)&rcv_buf) );
+			free(rcv_buf);
+		} while (ev_code != FDEVP_CNX_MSG_RECV);
+		
+		/* Now close the connection */
+		CHECK( 0, pthread_create(&thr, NULL, destroy_thr, client_side) );
+		fd_cnx_destroy(server_side);
+		CHECK( 0, pthread_join(thr, NULL) );
+		
+		fd_event_destroy(&myfifo, free);
+		
+		/* Free the credentials */
+		gnutls_certificate_free_keys(hf.creds);
+		gnutls_certificate_free_cas(hf.creds);
+		gnutls_certificate_free_credentials(hf.creds);
+	}
+#endif /* DISABLE_SCTP */
+	
+
+	/* Destroy the servers */
+	{
+		fd_cnx_destroy(listener);
+#ifndef DISABLE_SCTP
+		fd_cnx_destroy(listener_sctp);
+#endif /* DISABLE_SCTP */
+	}
+	
+	/* Check that connection attempt fails then */
+	{
+		struct connect_flags cf;
+		
+		memset(&cf, 0, sizeof(cf));
+		cf.proto = IPPROTO_TCP;
+		cf.expect_failure = 1;
+		
+		/* Start the client thread, that should fail */
+		CHECK( 0, pthread_create(&thr, NULL, connect_thr, &cf) );
+		CHECK( 0, pthread_join( thr, (void *)&client_side ) );
+		CHECK( 0, client_side ? 1 : 0 );
+	}
+		
+#ifndef DISABLE_SCTP
+	{
+		struct connect_flags cf;
+		
+		memset(&cf, 0, sizeof(cf));
+		cf.proto = IPPROTO_SCTP;
+		cf.expect_failure = 1;
+		
+		/* Start the client thread, that should fail */
+		CHECK( 0, pthread_create(&thr, NULL, connect_thr, &cf) );
+		CHECK( 0, pthread_join( thr, (void *)&client_side ) );
+		CHECK( 0, client_side ? 1 : 0 );
+	}
+#endif /* DISABLE_SCTP */
+	
+	
+	/* That's all for the tests yet */
+	PASSTEST();
+} 
+	
diff --git a/tests/testdict.c b/tests/testdict.c
new file mode 100644
index 0000000..d45d52e
--- /dev/null
+++ b/tests/testdict.c
@@ -0,0 +1,211 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@freediameter.net>							 *
+*													 *
+* Copyright (c) 2013, 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"
+
+/* Test for the dict_iterate_rules function */
+int iter_test(void * data, struct dict_rule_data * rule)
+{
+	struct dict_avp_data avpdata;
+	(*(int *)data)++;
+	
+	CHECK( 0, fd_dict_getval ( rule->rule_avp, &avpdata ) );
+	TRACE_DEBUG(FULL, "rule #%d: avp '%s'", *(int *)data, avpdata.avp_name);
+	return 0;
+}
+
+/* Main test routine */
+int main(int argc, char *argv[])
+{
+	/* First, initialize the daemon modules */
+	INIT_FD();
+	
+	/* Test creating and searching all types of objects */
+	{
+		struct dict_object * obj1 = NULL;
+		struct dict_object * obj2 = NULL;
+		struct dict_object * obj3 = NULL;
+
+		vendor_id_t vendor_id = 735671;
+		struct dict_vendor_data vendor1_data = { 735671, "Vendor test 1" };
+		struct dict_vendor_data vendor2_data = { 735672, "Vendor test 2" };
+		struct dict_application_data app1_data = { 735674, "Application test 1" };
+		
+		
+		/* Create two vendors */
+		CHECK( 0, fd_dict_new ( fd_g_config->cnf_dict, DICT_VENDOR, &vendor1_data , NULL, &obj1 ) );
+		CHECK( 0, fd_dict_new ( fd_g_config->cnf_dict, DICT_VENDOR, &vendor2_data , NULL, NULL ) );
+		
+		/* Check we always retrieve the correct vendor object */
+		CHECK( 0, fd_dict_search ( fd_g_config->cnf_dict, DICT_VENDOR, VENDOR_BY_ID, &vendor_id, &obj2, ENOENT ) );
+		CHECK( obj1, obj2);
+		CHECK( 0, fd_dict_search ( fd_g_config->cnf_dict, DICT_VENDOR, VENDOR_BY_NAME, "Vendor test 1", &obj2, ENOENT ) );
+		CHECK( obj1, obj2);
+		
+		/* Check the error conditions */
+		CHECK( 0, fd_dict_search ( fd_g_config->cnf_dict, DICT_VENDOR, VENDOR_BY_ID, &vendor_id, NULL, ENOENT ) );
+		
+		vendor_id = 735673; /* Not defined */
+		CHECK( ENOENT, fd_dict_search ( fd_g_config->cnf_dict, DICT_VENDOR, VENDOR_BY_ID, &vendor_id, NULL, ENOENT ) );
+		CHECK( ENOENT, fd_dict_search ( fd_g_config->cnf_dict, DICT_VENDOR, VENDOR_BY_NAME, "Vendor test 3", NULL, ENOENT ) );
+		CHECK( ENOENT, fd_dict_search ( fd_g_config->cnf_dict, DICT_VENDOR, VENDOR_BY_ID, &vendor_id, &obj2, ENOENT ) );
+		CHECK( ENOENT, fd_dict_search ( fd_g_config->cnf_dict, DICT_VENDOR, VENDOR_BY_NAME, "Vendor test 3", &obj2, ENOENT ) );
+		CHECK( ENOTSUP, fd_dict_search ( fd_g_config->cnf_dict, DICT_VENDOR, VENDOR_BY_NAME, "Vendor test 3", &obj2, ENOTSUP ) );
+		
+		/* Check the get_* functions */
+		CHECK( 0, fd_dict_getval ( obj1, &vendor1_data ) );
+		CHECK( 735671, vendor1_data.vendor_id );
+		CHECK( 0, strcmp(vendor1_data.vendor_name, "Vendor test 1") );
+		/* error conditions */
+		CHECK( EINVAL, fd_dict_getval ( (struct dict_object *)"not an object", &vendor1_data ) );
+		
+		/* Create the application with vendor1 as parent */
+		CHECK( EINVAL, fd_dict_new ( fd_g_config->cnf_dict, DICT_APPLICATION, &app1_data , (struct dict_object *)"bad object", &obj2 ) );
+		CHECK( 0, fd_dict_new ( fd_g_config->cnf_dict, DICT_APPLICATION, &app1_data , obj1, &obj2 ) );
+		
+		CHECK( 0, fd_dict_search ( fd_g_config->cnf_dict, DICT_VENDOR, VENDOR_OF_APPLICATION, obj2, &obj3, ENOENT ) );
+		CHECK( obj1, obj3);
+		
+		/* Creating and searching the other objects is already done in dictionary initialization */
+	}
+
+	/* Test creation of the "Example-AVP" grouped AVP from the RFC */
+	{
+		int nbr = 0;
+		struct dict_object * origin_host_avp = NULL;
+		struct dict_object * session_id_avp = NULL;
+		struct dict_object * example_avp_avp = NULL;
+		struct dict_rule_data rule_data = { NULL, RULE_REQUIRED, -1, -1 };
+		struct dict_avp_data example_avp_data = { 999999, 0, "Example-AVP", AVP_FLAG_VENDOR , 0, AVP_TYPE_GROUPED };
+
+		CHECK( 0, fd_dict_search ( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Origin-Host", &origin_host_avp, ENOENT ) );
+		CHECK( 0, fd_dict_search ( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Session-Id", &session_id_avp, ENOENT ) );
+		
+		CHECK( 0, fd_dict_new ( fd_g_config->cnf_dict, DICT_AVP, &example_avp_data , NULL, &example_avp_avp ) );
+		
+		rule_data.rule_avp = origin_host_avp;
+		rule_data.rule_min = 1;
+		rule_data.rule_max = 1;
+		CHECK( 0, fd_dict_new ( fd_g_config->cnf_dict, DICT_RULE, &rule_data, example_avp_avp, NULL ) );
+		
+		rule_data.rule_avp = session_id_avp;
+		rule_data.rule_min = 1;
+		rule_data.rule_max = -1;
+		CHECK( 0, fd_dict_new ( fd_g_config->cnf_dict, DICT_RULE, &rule_data, example_avp_avp, NULL ) );
+		
+		CHECK( 0, fd_dict_iterate_rules ( example_avp_avp, &nbr, iter_test) );
+		CHECK( 2, nbr );
+	}
+	
+	/* Test list function */
+	{
+		struct fd_list * li = NULL;
+		struct fd_list * sentinel = NULL;
+		enum dict_object_type	type;
+		struct dict_object * defvnd=NULL;
+		vendor_id_t vid = 0;
+		int first = 1;
+		
+		CHECK( 0, fd_dict_getlistof(VENDOR_BY_ID, fd_g_config->cnf_dict, &sentinel));
+		
+		for (li = sentinel; (li != sentinel) || (first != 0); li = li->next) {
+			first = 0;
+			CHECK(0, fd_dict_gettype(li->o, &type));
+			CHECK(DICT_VENDOR, type);
+#if 0
+			struct dict_vendor_data data;
+			CHECK( 0, fd_dict_getval(li->o, &data) );
+			printf("%d : %s\n", data.vendor_id, data.vendor_name);
+#endif
+		}
+		
+		CHECK( 0, fd_dict_search(fd_g_config->cnf_dict, DICT_VENDOR, VENDOR_BY_ID, &vid, &defvnd, ENOENT) );
+		
+		CHECK( 0, fd_dict_getlistof(AVP_BY_NAME, defvnd, &sentinel));
+		for (li = sentinel->next; li != sentinel; li = li->next) {
+			CHECK(0, fd_dict_gettype(li->o, &type));
+			CHECK(DICT_AVP, type);
+#if 0
+			struct dict_avp_data data;
+			CHECK( 0, fd_dict_getval(li->o, &data) );
+			printf("%d : %s\n", data.avp_code, data.avp_name);
+#endif
+		}
+	}
+
+	/* Test delete function */
+	{
+		struct fd_list * li = NULL;
+		struct fd_list * sentinel = NULL;
+		struct dict_object * obj=NULL;
+		vendor_id_t vid = 0;
+		int count = 0, cntbkp;
+		
+		CHECK( 0, fd_dict_search(fd_g_config->cnf_dict, DICT_VENDOR, VENDOR_BY_ID, &vid, &obj, ENOENT) );
+		
+		CHECK( EINVAL, fd_dict_delete(obj) );
+			
+		
+		CHECK( 0, fd_dict_getlistof(AVP_BY_NAME, obj, &sentinel));
+		obj = NULL;
+		
+		for (li = sentinel->next; li != sentinel; li = li->next) {
+			struct dict_avp_data data;
+			CHECK( 0, fd_dict_getval(li->o, &data) );
+			count++;
+			if (data.avp_basetype != AVP_TYPE_GROUPED)
+				obj = li->o;
+		}
+		
+		CHECK(1, obj ? 1 : 0 );
+#if 1
+		fd_log_debug("%s", fd_dict_dump_object(FD_DUMP_TEST_PARAMS, obj));
+#endif
+		CHECK( 0, fd_dict_delete(obj) );
+		cntbkp = count;
+		count = 0;
+		for (li = sentinel->next; li != sentinel; li = li->next) {
+			count++;
+		}
+		CHECK( 1, cntbkp - count );
+		
+	}
+	
+	LOG_D( "Dictionary at the end of %s: %s", __FILE__, fd_dict_dump(FD_DUMP_TEST_PARAMS, fd_g_config->cnf_dict) ?: "error");
+	
+	/* That's all for the tests yet */
+	PASSTEST();
+} 
+	
diff --git a/tests/testdisp.c b/tests/testdisp.c
new file mode 100644
index 0000000..7625a69
--- /dev/null
+++ b/tests/testdisp.c
@@ -0,0 +1,729 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@freediameter.net>							 *
+*													 *
+* Copyright (c) 2013, 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 Define_cb( __int, __extra )												\
+int cb_##__int( struct msg ** msg, struct avp * avp, struct session * session, void * opaque, enum disp_action * action )	\
+{																\
+	CHECK( 1, msg ? 1 : 0 );												\
+	CHECK( 1, action ? 1 : 0 );												\
+	CHECK( sess, session );													\
+	if (opaque) {														\
+		CHECK( 1, opaque == g_opaque ? 1 : 0 );										\
+	}															\
+	*action = DISP_ACT_CONT;												\
+	cbcalled[__int] += 1;													\
+	do {															\
+		__extra ;													\
+	} while (0);														\
+	return 0;														\
+}
+
+#define NB_CB	10
+char cbcalled[NB_CB];
+struct session * sess;
+void * g_opaque = (void *)"test";
+
+/* cb_0 */  Define_cb( 0, );
+/* cb_1 */  Define_cb( 1, );
+/* cb_2 */  Define_cb( 2, );
+/* cb_3 */  Define_cb( 3, );
+/* cb_4 */  Define_cb( 4, );
+/* cb_5 */  Define_cb( 5, );
+/* cb_6 */  Define_cb( 6, return 12345 );
+/* cb_7 */  Define_cb( 7, { CHECK( 1, avp ? 1 : 0 ); } );
+/* cb_8 */  Define_cb( 8, { CHECK( 0, fd_msg_free( *msg ) ); *msg = NULL; } );
+/* cb_9 */  Define_cb( 9, *action = DISP_ACT_SEND );
+/* max: cb_<NB_CB - 1> */
+
+/* Create a new message containing what we want */
+struct msg * new_msg(int appid, struct dict_object * cmd, struct dict_object * avp1, struct dict_object * avp2, int val)
+{
+	struct msg *new;
+	struct avp *avp;
+	union avp_value value;
+	struct msg_hdr * msg_hdr = NULL;
+	
+	CHECK( 0, fd_msg_new ( cmd, 0, &new ) );
+	CHECK( 0, fd_msg_hdr ( new, &msg_hdr ) );
+	msg_hdr->msg_appl = appid;
+	
+	if (avp1) {
+		CHECK( 0, fd_msg_avp_new ( avp1, 0, &avp ) );
+		value.u32 = 0;
+		CHECK( 0, fd_msg_avp_setvalue ( avp, &value ) );
+		CHECK( 0, fd_msg_avp_add ( new, MSG_BRW_LAST_CHILD, avp ) );
+	}
+	
+	if (avp2) {
+		CHECK( 0, fd_msg_avp_new ( avp2, 0, &avp ) );
+		value.u32 = val;
+		CHECK( 0, fd_msg_avp_setvalue ( avp, &value ) );
+		CHECK( 0, fd_msg_avp_add ( new, MSG_BRW_LAST_CHILD, avp ) );
+	}
+	
+	return new;	
+}
+
+/* Main test routine */
+int main(int argc, char *argv[])
+{
+	struct dict_object * app1, * app2;
+	struct dict_object * cmd1, * cmd2;
+	struct dict_object * avp1, * avp2; /* avp2 is enumerated; they are both unsigned32 types */
+	struct dict_object * enu1, * enu2;
+	struct msg * msg = NULL, *error;
+	enum disp_action action;
+	struct disp_hdl * hdl[NB_CB];
+	struct disp_when when;
+	char * ec, *em;
+	
+	/* First, initialize the daemon modules */
+	INIT_FD();
+	
+	/* Create a dummy session, we don't use it anyway */
+	#define DUMMY_SID "test.disp"
+	CHECK( 0, fd_sess_new( &sess, DUMMY_SID, CONSTSTRLEN(DUMMY_SID), NULL, 0 ) );
+	
+	memset(&when, 0xff, sizeof(when)); /* check that we don't use un-initialized parts */
+	
+	/* Initialize dictionary objects */
+	{
+		struct dict_object * enutype;
+		struct dict_application_data app1_data = { 1, "Application test 1" };
+		struct dict_application_data app2_data = { 2, "Application test 2" };
+		struct dict_cmd_data cmd1_data = { 1, "Command test 1 (req)", CMD_FLAG_REQUEST,	CMD_FLAG_REQUEST };
+		struct dict_cmd_data cmd2_data = { 1, "Command test 2 (ans)", CMD_FLAG_REQUEST,	0 };
+		struct dict_type_data type_data = { AVP_TYPE_UNSIGNED32, "Type test", NULL, NULL };
+		struct dict_avp_data avp1_data = { 10001, 0, "AVP test 1", 0, 0, AVP_TYPE_UNSIGNED32 };
+		struct dict_avp_data avp2_data = { 10002, 0, "AVP test 2", 0, 0, AVP_TYPE_UNSIGNED32 };
+		struct dict_enumval_data enu1_data = { "ENU test 1", { .u32 = 1 }};
+		struct dict_enumval_data enu2_data = { "ENU test 2", { .u32 = 2 }};
+		
+		CHECK( 0, fd_dict_new ( fd_g_config->cnf_dict, DICT_APPLICATION, &app1_data, NULL, &app1 ) );
+		CHECK( 0, fd_dict_new ( fd_g_config->cnf_dict, DICT_APPLICATION, &app2_data, NULL, &app2 ) );
+		CHECK( 0, fd_dict_new ( fd_g_config->cnf_dict, DICT_COMMAND, &cmd1_data, NULL, &cmd1 ) );
+		CHECK( 0, fd_dict_new ( fd_g_config->cnf_dict, DICT_COMMAND, &cmd2_data, NULL, &cmd2 ) );
+		CHECK( 0, fd_dict_new ( fd_g_config->cnf_dict, DICT_TYPE, &type_data, NULL, &enutype ) );
+		CHECK( 0, fd_dict_new ( fd_g_config->cnf_dict, DICT_AVP, &avp1_data, NULL,    &avp1 ) );
+		CHECK( 0, fd_dict_new ( fd_g_config->cnf_dict, DICT_AVP, &avp2_data, enutype, &avp2 ) );
+		CHECK( 0, fd_dict_new ( fd_g_config->cnf_dict, DICT_ENUMVAL, &enu1_data, enutype, &enu1 ) );
+		CHECK( 0, fd_dict_new ( fd_g_config->cnf_dict, DICT_ENUMVAL, &enu2_data, enutype, &enu2 ) );
+	}
+	
+	/* Register first handler, very simple test */
+	{
+		CHECK( 0, fd_disp_register( cb_0, DISP_HOW_ANY, NULL, NULL, &hdl[0] ) );
+		CHECK( 1, hdl[0] ? 1 : 0 );
+		CHECK( 0, fd_disp_unregister( &hdl[0], NULL ) );
+		CHECK( NULL, hdl[0] );
+		CHECK( 0, fd_disp_register( cb_0, DISP_HOW_ANY, NULL, NULL, &hdl[0] ) );
+	
+		/* Check this handler is called for a message */
+		msg = new_msg( 0, cmd1, avp1, NULL, 0 );
+		memset(cbcalled, 0, sizeof(cbcalled));
+		CHECK( 0, fd_msg_dispatch ( &msg, sess, &action, &ec, &em, &error ) );
+		CHECK( 1, cbcalled[0] );
+		CHECK( DISP_ACT_CONT, action );
+		
+		/* Delete the message */
+		CHECK( 0, fd_msg_free( msg ) );
+		CHECK( 0, fd_disp_unregister( &hdl[0], NULL ) );
+	}
+	
+	/* Handlers for applications */
+	{
+		CHECK( 0, fd_disp_register( cb_0, DISP_HOW_ANY, &when, NULL, &hdl[0] ) );
+		when.app = app1;
+		CHECK( 0, fd_disp_register( cb_1, DISP_HOW_APPID, &when, NULL, &hdl[1] ) );
+		when.app = app2;
+		CHECK( 0, fd_disp_register( cb_2, DISP_HOW_APPID, &when, NULL, &hdl[2] ) );
+		when.avp = avp2;
+		CHECK( 0, fd_disp_register( cb_3, DISP_HOW_APPID, &when, NULL, &hdl[3] ) );
+		when.avp = avp1;
+		CHECK( 0, fd_disp_register( cb_4, DISP_HOW_APPID, &when, NULL, &hdl[4] ) );
+	
+		/* Check the callbacks are called as appropriate */
+		memset(cbcalled, 0, sizeof(cbcalled));
+		msg = new_msg( 0, cmd1, avp1, NULL, 0 );
+		CHECK( 0, fd_msg_dispatch ( &msg, sess, &action, &ec, &em, &error ) );
+		CHECK( 1, cbcalled[0] );
+		CHECK( 0, cbcalled[1] );
+		CHECK( 0, cbcalled[2] );
+		CHECK( 0, cbcalled[3] );
+		CHECK( 0, cbcalled[4] );
+		CHECK( DISP_ACT_CONT, action );
+		CHECK( 0, fd_msg_free( msg ) );
+		
+		memset(cbcalled, 0, sizeof(cbcalled));
+		msg = new_msg( 1, cmd1, avp1, NULL, 0 );
+		CHECK( 0, fd_msg_dispatch ( &msg, sess, &action, &ec, &em, &error ) );
+		CHECK( 1, cbcalled[0] );
+		CHECK( 1, cbcalled[1] );
+		CHECK( 0, cbcalled[2] );
+		CHECK( 0, cbcalled[3] );
+		CHECK( 0, cbcalled[4] );
+		CHECK( DISP_ACT_CONT, action );
+		CHECK( 0, fd_msg_free( msg ) );
+		
+		memset(cbcalled, 0, sizeof(cbcalled));
+		msg = new_msg( 2, cmd1, avp1, NULL, 0 );
+		CHECK( 0, fd_msg_dispatch ( &msg, sess, &action, &ec, &em, &error ) );
+		CHECK( 1, cbcalled[0] );
+		CHECK( 0, cbcalled[1] );
+		CHECK( 1, cbcalled[2] );
+		CHECK( 1, cbcalled[3] );
+		CHECK( 1, cbcalled[4] );
+		CHECK( DISP_ACT_CONT, action );
+		CHECK( 0, fd_msg_free( msg ) );
+		
+		CHECK( 0, fd_disp_unregister( &hdl[0], NULL ) );
+		CHECK( 0, fd_disp_unregister( &hdl[1], NULL ) );
+		CHECK( 0, fd_disp_unregister( &hdl[2], NULL ) );
+		CHECK( 0, fd_disp_unregister( &hdl[3], NULL ) );
+		CHECK( 0, fd_disp_unregister( &hdl[4], NULL ) );
+	}
+	
+	/* Handlers for commands */
+	{
+		when.app = NULL;
+		when.command = NULL;
+		CHECK( 0, fd_disp_register( cb_0, DISP_HOW_ANY, &when, NULL, &hdl[0] ) );
+		CHECK( EINVAL, fd_disp_register( cb_1, DISP_HOW_CC, &when, NULL, &hdl[1] ) );
+		when.command = cmd1;
+		CHECK( 0, fd_disp_register( cb_1, DISP_HOW_CC, &when, NULL, &hdl[1] ) ); /* cmd1 */
+		when.app = app2;
+		CHECK( 0, fd_disp_register( cb_2, DISP_HOW_CC, &when, NULL, &hdl[2] ) ); /* app2 + cmd1 */
+		when.command = cmd2;
+		when.app = NULL;
+		when.avp = avp1;
+		CHECK( 0, fd_disp_register( cb_3, DISP_HOW_CC, &when, NULL, &hdl[3] ) ); /* cmd2 (avp1 ignored) */
+		
+		/* Check the callbacks are called as appropriate */
+		memset(cbcalled, 0, sizeof(cbcalled));
+		msg = new_msg( 0, cmd1, avp1, NULL, 0 );
+		CHECK( 0, fd_msg_dispatch ( &msg, sess, &action, &ec, &em, &error ) );
+		CHECK( 1, cbcalled[0] );
+		CHECK( 1, cbcalled[1] );
+		CHECK( 0, cbcalled[2] );
+		CHECK( 0, cbcalled[3] );
+		CHECK( DISP_ACT_CONT, action );
+		CHECK( 0, fd_msg_free( msg ) );
+		
+		memset(cbcalled, 0, sizeof(cbcalled));
+		msg = new_msg( 2, cmd1, avp1, NULL, 0 );
+		CHECK( 0, fd_msg_dispatch ( &msg, sess, &action, &ec, &em, &error ) );
+		CHECK( 1, cbcalled[0] );
+		CHECK( 1, cbcalled[1] );
+		CHECK( 1, cbcalled[2] );
+		CHECK( 0, cbcalled[3] );
+		CHECK( DISP_ACT_CONT, action );
+		CHECK( 0, fd_msg_free( msg ) );
+		
+		memset(cbcalled, 0, sizeof(cbcalled));
+		msg = new_msg( 2, cmd2, avp1, NULL, 0 );
+		CHECK( 0, fd_msg_dispatch ( &msg, sess, &action, &ec, &em, &error ) );
+		CHECK( 1, cbcalled[0] );
+		CHECK( 0, cbcalled[1] );
+		CHECK( 0, cbcalled[2] );
+		CHECK( 1, cbcalled[3] );
+		CHECK( DISP_ACT_CONT, action );
+		CHECK( 0, fd_msg_free( msg ) );
+		
+		memset(cbcalled, 0, sizeof(cbcalled));
+		msg = new_msg( 1, cmd2, NULL, avp2, 0 );
+		CHECK( 0, fd_msg_dispatch ( &msg, sess, &action, &ec, &em, &error ) );
+		CHECK( 1, cbcalled[0] );
+		CHECK( 0, cbcalled[1] );
+		CHECK( 0, cbcalled[2] );
+		CHECK( 1, cbcalled[3] );
+		CHECK( DISP_ACT_CONT, action );
+		CHECK( 0, fd_msg_free( msg ) );
+		
+		CHECK( 0, fd_disp_unregister( &hdl[0], NULL ) );
+		CHECK( 0, fd_disp_unregister( &hdl[1], NULL ) );
+		CHECK( 0, fd_disp_unregister( &hdl[2], NULL ) );
+		CHECK( 0, fd_disp_unregister( &hdl[3], NULL ) );
+	}
+	
+	/* Handlers for AVPs */
+	{
+		when.app = NULL;
+		when.command = NULL;
+		when.avp = NULL;
+	
+		CHECK( 0, fd_disp_register( cb_0, DISP_HOW_ANY, &when, NULL, &hdl[0] ) ); /* all */
+		CHECK( EINVAL, fd_disp_register( cb_1, DISP_HOW_AVP, &when, NULL, &hdl[1] ) );
+		
+		when.avp = avp1;
+		CHECK( 0, fd_disp_register( cb_1, DISP_HOW_AVP, &when, NULL, &hdl[1] ) ); /* avp1 */
+		
+		when.command = cmd1;
+		CHECK( 0, fd_disp_register( cb_2, DISP_HOW_AVP, &when, NULL, &hdl[2] ) ); /* avp1 + cmd1 */
+		
+		when.command = NULL;
+		when.app = app1;
+		CHECK( 0, fd_disp_register( cb_3, DISP_HOW_AVP, &when, NULL, &hdl[3] ) ); /* avp1 + app1 */
+		
+		when.command = cmd1;
+		CHECK( 0, fd_disp_register( cb_4, DISP_HOW_AVP, &when, NULL, &hdl[4] ) ); /* avp1 + cmd1 + app1 */
+		
+		when.app = NULL;
+		when.command = NULL;
+		when.avp = avp2;
+		when.value = enu1;
+		CHECK( 0, fd_disp_register( cb_5, DISP_HOW_AVP, &when, NULL, &hdl[5] ) ); /* avp2 */
+		
+		when.value = enu2;
+		CHECK( 0, fd_disp_register( cb_7, DISP_HOW_AVP, &when, NULL, &hdl[6] ) ); /* avp2 */
+		
+		
+		
+		/* Check the callbacks are called as appropriate */
+		memset(cbcalled, 0, sizeof(cbcalled));
+		msg = new_msg( 0, cmd1, NULL, NULL, 0 );
+		CHECK( 0, fd_msg_dispatch ( &msg, sess, &action, &ec, &em, &error ) );
+		CHECK( 1, cbcalled[0] );
+		CHECK( 0, cbcalled[1] );
+		CHECK( 0, cbcalled[2] );
+		CHECK( 0, cbcalled[3] );
+		CHECK( 0, cbcalled[4] );
+		CHECK( 0, cbcalled[5] );
+		CHECK( DISP_ACT_CONT, action );
+		CHECK( 0, fd_msg_free( msg ) );
+		
+		memset(cbcalled, 0, sizeof(cbcalled));
+		msg = new_msg( 0, cmd1, avp1, NULL, 0 );
+		CHECK( 0, fd_msg_dispatch ( &msg, sess, &action, &ec, &em, &error ) );
+		CHECK( 1, cbcalled[0] );
+		CHECK( 1, cbcalled[1] );
+		CHECK( 1, cbcalled[2] );
+		CHECK( 0, cbcalled[3] );
+		CHECK( 0, cbcalled[4] );
+		CHECK( 0, cbcalled[5] );
+		CHECK( DISP_ACT_CONT, action );
+		CHECK( 0, fd_msg_free( msg ) );
+		
+		memset(cbcalled, 0, sizeof(cbcalled));
+		msg = new_msg( 1, cmd2, avp1, NULL, 0 );
+		CHECK( 0, fd_msg_dispatch ( &msg, sess, &action, &ec, &em, &error ) );
+		CHECK( 1, cbcalled[0] );
+		CHECK( 1, cbcalled[1] );
+		CHECK( 0, cbcalled[2] );
+		CHECK( 1, cbcalled[3] );
+		CHECK( 0, cbcalled[4] );
+		CHECK( 0, cbcalled[5] );
+		CHECK( DISP_ACT_CONT, action );
+		CHECK( 0, fd_msg_free( msg ) );
+		
+		memset(cbcalled, 0, sizeof(cbcalled));
+		msg = new_msg( 1, cmd1, avp1, NULL, 0 );
+		CHECK( 0, fd_msg_dispatch ( &msg, sess, &action, &ec, &em, &error ) );
+		CHECK( 1, cbcalled[0] );
+		CHECK( 1, cbcalled[1] );
+		CHECK( 1, cbcalled[2] );
+		CHECK( 1, cbcalled[3] );
+		CHECK( 1, cbcalled[4] );
+		CHECK( 0, cbcalled[5] );
+		CHECK( DISP_ACT_CONT, action );
+		CHECK( 0, fd_msg_free( msg ) );
+		
+		memset(cbcalled, 0, sizeof(cbcalled));
+		msg = new_msg( 1, cmd1, avp1, avp2, 0 );
+		CHECK( 0, fd_msg_dispatch ( &msg, sess, &action, &ec, &em, &error ) );
+		CHECK( 1, cbcalled[0] );
+		CHECK( 1, cbcalled[1] );
+		CHECK( 1, cbcalled[2] );
+		CHECK( 1, cbcalled[3] );
+		CHECK( 1, cbcalled[4] );
+		CHECK( 1, cbcalled[5] );
+		CHECK( 1, cbcalled[7] );
+		CHECK( DISP_ACT_CONT, action );
+		CHECK( 0, fd_msg_free( msg ) );
+		
+		memset(cbcalled, 0, sizeof(cbcalled));
+		msg = new_msg( 1, cmd1, NULL, avp2, 1 );
+		CHECK( 0, fd_msg_dispatch ( &msg, sess, &action, &ec, &em, &error ) );
+		CHECK( 1, cbcalled[0] );
+		CHECK( 0, cbcalled[1] );
+		CHECK( 0, cbcalled[2] );
+		CHECK( 0, cbcalled[3] );
+		CHECK( 0, cbcalled[4] );
+		CHECK( 1, cbcalled[5] );
+		CHECK( 1, cbcalled[7] );
+		CHECK( DISP_ACT_CONT, action );
+		CHECK( 0, fd_msg_free( msg ) );
+		
+		memset(cbcalled, 0, sizeof(cbcalled));
+		msg = new_msg( 1, cmd1, NULL, avp2, 2 );
+		CHECK( 0, fd_msg_dispatch ( &msg, sess, &action, &ec, &em, &error ) );
+		CHECK( 1, cbcalled[0] );
+		CHECK( 0, cbcalled[1] );
+		CHECK( 0, cbcalled[2] );
+		CHECK( 0, cbcalled[3] );
+		CHECK( 0, cbcalled[4] );
+		CHECK( 1, cbcalled[5] );
+		CHECK( 1, cbcalled[7] );
+		CHECK( DISP_ACT_CONT, action );
+		CHECK( 0, fd_msg_free( msg ) );
+		
+		CHECK( 0, fd_disp_unregister( &hdl[0], NULL ) );
+		CHECK( 0, fd_disp_unregister( &hdl[1], NULL ) );
+		CHECK( 0, fd_disp_unregister( &hdl[2], NULL ) );
+		CHECK( 0, fd_disp_unregister( &hdl[3], NULL ) );
+		CHECK( 0, fd_disp_unregister( &hdl[4], NULL ) );
+		CHECK( 0, fd_disp_unregister( &hdl[5], NULL ) );
+		CHECK( 0, fd_disp_unregister( &hdl[6], NULL ) );
+	}
+		
+	/* Handlers for enum values */
+	{
+		when.app = NULL;
+		when.command = NULL;
+		when.avp = NULL;
+		when.value = NULL;
+		
+		CHECK( 0, fd_disp_register( cb_0, DISP_HOW_ANY, &when, NULL, &hdl[0] ) ); /* all */
+		CHECK( EINVAL, fd_disp_register( cb_1, DISP_HOW_AVP_ENUMVAL, &when, NULL, &hdl[1] ) );
+		when.value = enu1;
+		CHECK( EINVAL, fd_disp_register( cb_1, DISP_HOW_AVP_ENUMVAL, &when, NULL, &hdl[1] ) );
+		when.avp = avp1;
+		CHECK( EINVAL, fd_disp_register( cb_1, DISP_HOW_AVP_ENUMVAL, &when, NULL, &hdl[1] ) );
+		when.avp = avp2;
+		CHECK( 0, fd_disp_register( cb_1, DISP_HOW_AVP_ENUMVAL, &when, NULL, &hdl[1] ) ); /* avp2, enu1 */
+		
+		when.command = cmd1;
+		CHECK( 0, fd_disp_register( cb_2, DISP_HOW_AVP_ENUMVAL, &when, NULL, &hdl[2] ) ); /* avp2, enu1 + cmd1 */
+		
+		when.command = NULL;
+		when.app = app1;
+		when.value = enu2;
+		CHECK( 0, fd_disp_register( cb_3, DISP_HOW_AVP_ENUMVAL, &when, NULL, &hdl[3] ) ); /* avp2, enu2 + app1 */
+		
+		/* Check the callbacks are called as appropriate */
+		memset(cbcalled, 0, sizeof(cbcalled));
+		msg = new_msg( 0, cmd1, avp1, NULL, 0 );
+		CHECK( 0, fd_msg_dispatch ( &msg, sess, &action, &ec, &em, &error ) );
+		CHECK( 1, cbcalled[0] );
+		CHECK( 0, cbcalled[1] );
+		CHECK( 0, cbcalled[2] );
+		CHECK( 0, cbcalled[3] );
+		CHECK( DISP_ACT_CONT, action );
+		CHECK( 0, fd_msg_free( msg ) );
+		
+		memset(cbcalled, 0, sizeof(cbcalled));
+		msg = new_msg( 1, cmd2, avp1, avp2, 0 );
+		CHECK( 0, fd_msg_dispatch ( &msg, sess, &action, &ec, &em, &error ) );
+		CHECK( 1, cbcalled[0] );
+		CHECK( 0, cbcalled[1] );
+		CHECK( 0, cbcalled[2] );
+		CHECK( 0, cbcalled[3] );
+		CHECK( DISP_ACT_CONT, action );
+		CHECK( 0, fd_msg_free( msg ) );
+		
+		memset(cbcalled, 0, sizeof(cbcalled));
+		msg = new_msg( 1, cmd2, avp1, avp2, 1 );
+		CHECK( 0, fd_msg_dispatch ( &msg, sess, &action, &ec, &em, &error ) );
+		CHECK( 1, cbcalled[0] );
+		CHECK( 1, cbcalled[1] );
+		CHECK( 0, cbcalled[2] );
+		CHECK( 0, cbcalled[3] );
+		CHECK( DISP_ACT_CONT, action );
+		CHECK( 0, fd_msg_free( msg ) );
+		
+		memset(cbcalled, 0, sizeof(cbcalled));
+		msg = new_msg( 1, cmd2, avp1, avp2, 2 );
+		CHECK( 0, fd_msg_dispatch ( &msg, sess, &action, &ec, &em, &error ) );
+		CHECK( 1, cbcalled[0] );
+		CHECK( 0, cbcalled[1] );
+		CHECK( 0, cbcalled[2] );
+		CHECK( 1, cbcalled[3] );
+		CHECK( DISP_ACT_CONT, action );
+		CHECK( 0, fd_msg_free( msg ) );
+		
+		memset(cbcalled, 0, sizeof(cbcalled));
+		msg = new_msg( 1, cmd1, avp1, avp2, 1 );
+		CHECK( 0, fd_msg_dispatch ( &msg, sess, &action, &ec, &em, &error ) );
+		CHECK( 1, cbcalled[0] );
+		CHECK( 1, cbcalled[1] );
+		CHECK( 1, cbcalled[2] );
+		CHECK( 0, cbcalled[3] );
+		CHECK( DISP_ACT_CONT, action );
+		CHECK( 0, fd_msg_free( msg ) );
+		
+		memset(cbcalled, 0, sizeof(cbcalled));
+		msg = new_msg( 1, cmd2, avp1, avp2, 1 );
+		{
+			struct avp *avp;
+			union avp_value value;
+			CHECK( 0, fd_msg_avp_new ( avp2, 0, &avp ) );
+			value.u32 = 2;
+			CHECK( 0, fd_msg_avp_setvalue ( avp, &value ) );
+			CHECK( 0, fd_msg_avp_add ( msg, MSG_BRW_LAST_CHILD, avp ) );
+		}
+		CHECK( 0, fd_msg_dispatch ( &msg, sess, &action, &ec, &em, &error ) );
+		CHECK( 1, cbcalled[0] );
+		CHECK( 1, cbcalled[1] );
+		CHECK( 0, cbcalled[2] );
+		CHECK( 1, cbcalled[3] );
+		CHECK( DISP_ACT_CONT, action );
+		CHECK( 0, fd_msg_free( msg ) );
+		
+		CHECK( 0, fd_disp_unregister( &hdl[0], NULL ) );
+		CHECK( 0, fd_disp_unregister( &hdl[1], NULL ) );
+		CHECK( 0, fd_disp_unregister( &hdl[2], NULL ) );
+		CHECK( 0, fd_disp_unregister( &hdl[3], NULL ) );
+	}
+	
+	/* Test behavior of handlers */
+	{
+		CHECK( 0, fd_disp_register( cb_0, DISP_HOW_ANY, &when, NULL, &hdl[0] ) );
+		CHECK( 0, fd_disp_register( cb_1, DISP_HOW_ANY, &when, NULL, &hdl[1] ) );
+		CHECK( 0, fd_disp_register( cb_6, DISP_HOW_ANY, &when, NULL, &hdl[2] ) );
+		CHECK( 0, fd_disp_register( cb_2, DISP_HOW_ANY, &when, NULL, &hdl[3] ) );
+		CHECK( 0, fd_disp_register( cb_3, DISP_HOW_ANY, &when, NULL, &hdl[4] ) );
+		
+		memset(cbcalled, 0, sizeof(cbcalled));
+		msg = new_msg( 1, cmd1, avp1, avp2, 1 );
+		CHECK( 0, fd_msg_dispatch ( &msg, sess, &action, &ec, &em, &error ) );
+		CHECK( 1, cbcalled[0] );
+		CHECK( 1, cbcalled[1] );
+		CHECK( 1, cbcalled[6] );
+		CHECK( 0, cbcalled[2] );
+		CHECK( 0, cbcalled[3] );
+		CHECK( 0, msg ? 1 : 0);
+		CHECK( 1, em ? 1 : 0);
+		CHECK( 0, fd_msg_free( error ) );
+		
+		CHECK( 0, fd_disp_unregister( &hdl[0], NULL ) );
+		CHECK( 0, fd_disp_unregister( &hdl[1], NULL ) );
+		CHECK( 0, fd_disp_unregister( &hdl[2], NULL ) );
+		CHECK( 0, fd_disp_unregister( &hdl[3], NULL ) );
+		CHECK( 0, fd_disp_unregister( &hdl[4], NULL ) );
+		
+		CHECK( 0, fd_disp_register( cb_0, DISP_HOW_ANY, &when, NULL, &hdl[0] ) );
+		CHECK( 0, fd_disp_register( cb_1, DISP_HOW_ANY, &when, NULL, &hdl[1] ) );
+		CHECK( 0, fd_disp_register( cb_8, DISP_HOW_ANY, &when, NULL, &hdl[2] ) );
+		CHECK( 0, fd_disp_register( cb_2, DISP_HOW_ANY, &when, NULL, &hdl[3] ) );
+		CHECK( 0, fd_disp_register( cb_3, DISP_HOW_ANY, &when, NULL, &hdl[4] ) );
+		
+		memset(cbcalled, 0, sizeof(cbcalled));
+		msg = new_msg( 1, cmd1, avp1, avp2, 1 );
+		CHECK( 0, fd_msg_dispatch ( &msg, sess, &action, &ec, &em, &error ) );
+		CHECK( 1, cbcalled[0] );
+		CHECK( 1, cbcalled[1] );
+		CHECK( 1, cbcalled[8] );
+		CHECK( 0, cbcalled[2] );
+		CHECK( 0, cbcalled[3] );
+		CHECK( NULL, msg );
+		CHECK( NULL, em );
+		
+		CHECK( 0, fd_disp_unregister( &hdl[0], NULL ) );
+		CHECK( 0, fd_disp_unregister( &hdl[1], NULL ) );
+		CHECK( 0, fd_disp_unregister( &hdl[2], NULL ) );
+		CHECK( 0, fd_disp_unregister( &hdl[3], NULL ) );
+		CHECK( 0, fd_disp_unregister( &hdl[4], NULL ) );
+		
+		CHECK( 0, fd_disp_register( cb_0, DISP_HOW_ANY, &when, NULL, &hdl[0] ) );
+		CHECK( 0, fd_disp_register( cb_1, DISP_HOW_ANY, &when, NULL, &hdl[1] ) );
+		CHECK( 0, fd_disp_register( cb_9, DISP_HOW_ANY, &when, NULL, &hdl[2] ) );
+		CHECK( 0, fd_disp_register( cb_2, DISP_HOW_ANY, &when, NULL, &hdl[3] ) );
+		CHECK( 0, fd_disp_register( cb_3, DISP_HOW_ANY, &when, NULL, &hdl[4] ) );
+		
+		memset(cbcalled, 0, sizeof(cbcalled));
+		msg = new_msg( 1, cmd1, avp1, avp2, 1 );
+		CHECK( 0, fd_msg_dispatch ( &msg, sess, &action, &ec, &em, &error ) );
+		CHECK( 1, cbcalled[0] );
+		CHECK( 1, cbcalled[1] );
+		CHECK( 1, cbcalled[9] );
+		CHECK( 0, cbcalled[2] );
+		CHECK( 0, cbcalled[3] );
+		CHECK( DISP_ACT_SEND, action );
+		CHECK( 0, fd_msg_free( msg ) );
+		
+		CHECK( 0, fd_disp_unregister( &hdl[0], NULL ) );
+		CHECK( 0, fd_disp_unregister( &hdl[1], NULL ) );
+		CHECK( 0, fd_disp_unregister( &hdl[2], NULL ) );
+		CHECK( 0, fd_disp_unregister( &hdl[3], NULL ) );
+		CHECK( 0, fd_disp_unregister( &hdl[4], NULL ) );
+	}
+		
+	/* Test order of handlers */
+	{
+		when.app = app2;
+		when.command = cmd2;
+		when.avp = avp2;
+		when.value = enu2;
+		
+		CHECK( 0, fd_disp_register( cb_0, DISP_HOW_ANY, &when, NULL, &hdl[0] ) );
+		CHECK( 0, fd_disp_register( cb_1, DISP_HOW_AVP_ENUMVAL, &when, NULL, &hdl[1] ) );
+		CHECK( 0, fd_disp_register( cb_2, DISP_HOW_AVP, &when, NULL, &hdl[2] ) );
+		CHECK( 0, fd_disp_register( cb_3, DISP_HOW_CC, &when, NULL, &hdl[3] ) );
+		CHECK( 0, fd_disp_register( cb_4, DISP_HOW_APPID, &when, NULL, &hdl[4] ) );
+		
+		memset(cbcalled, 0, sizeof(cbcalled));
+		msg = new_msg( 2, cmd2, avp1, avp2, 2 );
+		CHECK( 0, fd_msg_dispatch ( &msg, sess, &action, &ec, &em, &error ) );
+		CHECK( 1, cbcalled[0] );
+		CHECK( 1, cbcalled[1] );
+		CHECK( 1, cbcalled[2] );
+		CHECK( 1, cbcalled[3] );
+		CHECK( 1, cbcalled[4] );
+		CHECK( 0, cbcalled[9] );
+		CHECK( 0, fd_msg_free( msg ) );
+		
+		CHECK( 0, fd_disp_register( cb_9, DISP_HOW_ANY, &when, NULL, &hdl[5] ) );
+		memset(cbcalled, 0, sizeof(cbcalled));
+		msg = new_msg( 2, cmd2, avp1, avp2, 2 );
+		CHECK( 0, fd_msg_dispatch ( &msg, sess, &action, &ec, &em, &error ) );
+		CHECK( 1, cbcalled[0] );
+		CHECK( 0, cbcalled[1] );
+		CHECK( 0, cbcalled[2] );
+		CHECK( 0, cbcalled[3] );
+		CHECK( 0, cbcalled[4] );
+		CHECK( 1, cbcalled[9] );
+		CHECK( 0, fd_msg_free( msg ) );
+		CHECK( 0, fd_disp_unregister( &hdl[5], NULL ) );
+		
+		CHECK( 0, fd_disp_register( cb_9, DISP_HOW_AVP_ENUMVAL, &when, NULL, &hdl[5] ) );
+		memset(cbcalled, 0, sizeof(cbcalled));
+		msg = new_msg( 2, cmd2, avp1, avp2, 2 );
+		CHECK( 0, fd_msg_dispatch ( &msg, sess, &action, &ec, &em, &error ) );
+		CHECK( 1, cbcalled[0] );
+		CHECK( 1, cbcalled[1] );
+		CHECK( 1, cbcalled[2] );
+		CHECK( 0, cbcalled[3] );
+		CHECK( 0, cbcalled[4] );
+		CHECK( 1, cbcalled[9] );
+		CHECK( 0, fd_msg_free( msg ) );
+		CHECK( 0, fd_disp_unregister( &hdl[5], NULL ) );
+		
+		CHECK( 0, fd_disp_register( cb_9, DISP_HOW_AVP, &when, NULL, &hdl[5] ) );
+		memset(cbcalled, 0, sizeof(cbcalled));
+		msg = new_msg( 2, cmd2, avp1, avp2, 2 );
+		CHECK( 0, fd_msg_dispatch ( &msg, sess, &action, &ec, &em, &error ) );
+		CHECK( 1, cbcalled[0] );
+		CHECK( 1, cbcalled[1] );
+		CHECK( 1, cbcalled[2] );
+		CHECK( 0, cbcalled[3] );
+		CHECK( 0, cbcalled[4] );
+		CHECK( 1, cbcalled[9] );
+		CHECK( 0, fd_msg_free( msg ) );
+		CHECK( 0, fd_disp_unregister( &hdl[5], NULL ) );
+		
+		CHECK( 0, fd_disp_register( cb_9, DISP_HOW_CC, &when, NULL, &hdl[5] ) );
+		memset(cbcalled, 0, sizeof(cbcalled));
+		msg = new_msg( 2, cmd2, avp1, avp2, 2 );
+		CHECK( 0, fd_msg_dispatch ( &msg, sess, &action, &ec, &em, &error ) );
+		CHECK( 1, cbcalled[0] );
+		CHECK( 1, cbcalled[1] );
+		CHECK( 1, cbcalled[2] );
+		CHECK( 1, cbcalled[3] );
+		CHECK( 0, cbcalled[4] );
+		CHECK( 1, cbcalled[9] );
+		CHECK( 0, fd_msg_free( msg ) );
+		CHECK( 0, fd_disp_unregister( &hdl[5], NULL ) );
+		
+		CHECK( 0, fd_disp_register( cb_9, DISP_HOW_APPID, &when, NULL, &hdl[5] ) );
+		memset(cbcalled, 0, sizeof(cbcalled));
+		msg = new_msg( 2, cmd2, avp1, avp2, 2 );
+		CHECK( 0, fd_msg_dispatch ( &msg, sess, &action, &ec, &em, &error ) );
+		CHECK( 1, cbcalled[0] );
+		CHECK( 1, cbcalled[1] );
+		CHECK( 1, cbcalled[2] );
+		CHECK( 1, cbcalled[3] );
+		CHECK( 1, cbcalled[4] );
+		CHECK( 1, cbcalled[9] );
+		CHECK( 0, fd_msg_free( msg ) );
+		CHECK( 0, fd_disp_unregister( &hdl[5], NULL ) );
+		
+		CHECK( 0, fd_disp_unregister( &hdl[0], NULL ) );
+		CHECK( 0, fd_disp_unregister( &hdl[1], NULL ) );
+		CHECK( 0, fd_disp_unregister( &hdl[2], NULL ) );
+		CHECK( 0, fd_disp_unregister( &hdl[3], NULL ) );
+		CHECK( 0, fd_disp_unregister( &hdl[4], NULL ) );
+	}			
+	
+	/* Test application support advertisement */
+	{
+		struct dict_object * vnd;
+		struct dict_vendor_data vnd_data = { 1, "Vendor test" };
+		struct fd_app * app;
+		
+		CHECK( 0, fd_dict_new ( fd_g_config->cnf_dict, DICT_VENDOR, &vnd_data, NULL, &vnd ) );
+		
+		CHECK( EINVAL, fd_disp_app_support ( vnd, NULL, 1, 0 ) );
+		CHECK( EINVAL, fd_disp_app_support ( app1, NULL, 0, 0 ) );
+		CHECK( 0, fd_disp_app_support ( app1, NULL, 1, 0 ) );
+		CHECK( 0, fd_disp_app_support ( app1, NULL, 0, 1 ) );
+		CHECK( 0, fd_disp_app_support ( app2, vnd, 1, 0 ) );
+		
+		app = (struct fd_app *)(fd_g_config->cnf_apps.next);
+		CHECK( 1, app->appid );
+		CHECK( 1, app->flags.auth );
+		CHECK( 1, app->flags.acct );
+		app = (struct fd_app *)(fd_g_config->cnf_apps.prev);
+		CHECK( 2, app->appid );
+		CHECK( 1, app->flags.auth );
+		CHECK( 0, app->flags.acct );
+		
+		#if 0
+		fd_log_debug("%s", fd_conf_dump(FD_DUMP_TEST_PARAMS));
+		#endif
+	}
+	
+	/* Test opaque pointer management */
+	{
+		void * ptr;
+		CHECK( 0, fd_disp_register( cb_0, DISP_HOW_ANY, NULL, g_opaque, &hdl[0] ) );
+	
+		/* Check this handler is called for a message */
+		msg = new_msg( 0, cmd1, avp1, NULL, 0 );
+		memset(cbcalled, 0, sizeof(cbcalled));
+		CHECK( 0, fd_msg_dispatch ( &msg, sess, &action, &ec, &em, &error ) );
+		CHECK( 1, cbcalled[0] );
+		CHECK( DISP_ACT_CONT, action );
+		
+		/* Delete the message */
+		CHECK( 0, fd_msg_free( msg ) );
+		CHECK( 0, fd_disp_unregister( &hdl[0], &ptr ) );
+		CHECK( 1, ptr == g_opaque ? 1 : 0 );
+	}
+	
+	/* That's all for the tests yet */
+	PASSTEST();
+} 
+	
diff --git a/tests/testfifo.c b/tests/testfifo.c
new file mode 100644
index 0000000..c1c7cf1
--- /dev/null
+++ b/tests/testfifo.c
@@ -0,0 +1,579 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@freediameter.net>							 *
+*													 *
+* Copyright (c) 2013, 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"
+#include <unistd.h>
+#include <limits.h>
+
+/* Wrapper for pthread_barrier stuff on Mac OS X */
+#ifndef HAVE_PTHREAD_BAR
+
+#define PTHREAD_BARRIER_SERIAL_THREAD 1
+typedef struct {
+	int count;
+	int entered;
+	int serial;
+	pthread_mutex_t mutex;
+	pthread_cond_t cond;
+} pthread_barrier_t;
+
+int pthread_barrier_init(pthread_barrier_t * barrier, int * barrier_attr, int count)
+{
+	memset(barrier, 0, sizeof(pthread_barrier_t));
+	barrier->count = count;
+	pthread_mutex_init(&barrier->mutex, NULL);
+	pthread_cond_init(&barrier->cond, NULL);
+	return 0;
+}
+
+int pthread_barrier_destroy(pthread_barrier_t * barrier)
+{
+	pthread_mutex_destroy(&barrier->mutex);
+	pthread_cond_destroy(&barrier->cond);
+	return 0;
+}
+
+int pthread_barrier_wait(pthread_barrier_t * barrier)
+{
+	int ret = 0;
+	int serial;
+	pthread_mutex_lock(&barrier->mutex);
+	serial = barrier->serial;
+	
+	/* first thread gets the special value */
+	if (barrier->entered++ == 0) 
+		ret = PTHREAD_BARRIER_SERIAL_THREAD;
+	
+	/* Count was achieved? */
+	if (barrier->entered == barrier->count) {
+		/* Ok, increase serial, reset number of threads, and signal everyone */
+		barrier->entered = 0;
+		barrier->serial++;
+		pthread_cond_broadcast(&barrier->cond);
+	} else {
+		do {
+			pthread_cond_wait(&barrier->cond, &barrier->mutex);
+		} while (barrier->serial == serial);
+		/* this protects against spurious wakes */
+	}
+	pthread_mutex_unlock(&barrier->mutex);
+	return 0;
+}
+
+#endif /* HAVE_PTHREAD_BAR */
+
+/* Structure for testing threshold function */
+static struct thrh_test {
+	struct fifo *   queue; /* pointer to the queue */
+	int		h_calls; /* number of calls of h_cb */
+	int		l_calls; /* number of calls of l_cb */
+} thrh_td;
+
+/* Callbacks for threasholds test */
+void thrh_cb_h(struct fifo *queue, void **data)
+{
+	if (thrh_td.h_calls == thrh_td.l_calls) {
+		CHECK( NULL, *data );
+		*data = &thrh_td;
+	} else {
+		CHECK( *data, &thrh_td );
+	}
+	CHECK( queue, thrh_td.queue );
+	
+	/* Update the count */
+	thrh_td.h_calls ++;
+}
+void thrh_cb_l(struct fifo *queue, void **data)
+{
+	CHECK( 1, data ? 1 : 0 );
+	CHECK( *data, &thrh_td );
+
+	/* Check the queue parameter is correct */
+	CHECK( queue, thrh_td.queue );
+	
+	/* Update the count */
+	thrh_td.l_calls ++;
+	/* Cleanup the data ptr if needed */
+	if (thrh_td.l_calls == thrh_td.h_calls)
+		*data = NULL;
+	/* done */
+}
+
+
+/* Structure that is passed to the test function */
+struct test_data {
+	struct fifo     * queue; /* pointer to the queue */
+	pthread_barrier_t * bar;   /* if not NULL, barrier to synchronize before getting messages */
+	struct timespec   * ts;	   /* if not NULL, use a timedget instead of a get */
+	int		    nbr;   /* number of messages to retrieve from the queue */
+};
+
+/* The test function, to be threaded */
+static void * test_fct(void * data)
+{
+	int ret = 0, i;
+	struct msg * msg = NULL;
+	struct test_data * td = (struct test_data *) data;
+	
+	if (td->bar != NULL) {
+		ret = pthread_barrier_wait(td->bar);
+		if (ret != PTHREAD_BARRIER_SERIAL_THREAD) {
+			CHECK( 0, ret);
+		} else {
+			CHECK( PTHREAD_BARRIER_SERIAL_THREAD, ret); /* just for the traces */
+		}
+	}
+	
+	for (i=0; i< td->nbr; i++) {
+		if (td->ts != NULL) {
+			CHECK( 0, fd_fifo_timedget(td->queue, &msg, td->ts) );
+		} else {
+			CHECK( 0, fd_fifo_get(td->queue, &msg) );
+		}
+	}
+	
+	return NULL;
+}
+
+/* The test function, to be threaded */
+static int iter  = 0;
+static void * test_fct2(void * data)
+{
+	int i;
+	int * item;
+	struct test_data * td = (struct test_data *) data;
+	
+	for (i=0; i< td->nbr; i++) {
+		item = malloc(sizeof(int));
+		CHECK( 1, item ? 1 : 0 );
+		*item = i;
+		CHECK( 0, fd_fifo_post(td->queue, &item) );
+		iter++;
+	}
+	
+	return NULL;
+}
+
+
+/* Main test routine */
+int main(int argc, char *argv[])
+{
+	struct timespec ts;
+	
+	struct msg * msg1 = NULL;
+	struct msg * msg2 = NULL;
+	struct msg * msg3 = NULL;
+	
+	/* First, initialize the daemon modules */
+	INIT_FD();
+	
+	/* Prolog: create the messages */
+	{
+		struct dict_object * acr_model = NULL;
+		struct dict_object * cer_model = NULL;
+		struct dict_object * dwr_model = NULL;
+
+		CHECK( 0, fd_dict_search ( fd_g_config->cnf_dict, DICT_COMMAND, CMD_BY_NAME, "Accounting-Request", 			&acr_model, ENOENT ) );
+		CHECK( 0, fd_dict_search ( fd_g_config->cnf_dict, DICT_COMMAND, CMD_BY_NAME, "Capabilities-Exchange-Request", 	&cer_model, ENOENT ) );
+		CHECK( 0, fd_dict_search ( fd_g_config->cnf_dict, DICT_COMMAND, CMD_BY_NAME, "Device-Watchdog-Request",		&dwr_model, ENOENT ) );
+		CHECK( 0, fd_msg_new ( acr_model, 0, &msg1 ) );
+		CHECK( 0, fd_msg_new ( cer_model, 0, &msg2 ) );
+		CHECK( 0, fd_msg_new ( dwr_model, 0, &msg3 ) );
+	}
+	
+	/* Basic operation */
+	{
+		struct fifo * queue = NULL;
+		struct msg * msg  = NULL;
+		int max;
+		long long count;
+		
+		/* Create the queue */
+		CHECK( 0, fd_fifo_new(&queue, 0) );
+		
+		/* Check the count is 0 */
+		CHECK( 0, fd_fifo_length(queue) );
+		
+		/* Now enqueue */
+		msg = msg1;
+		CHECK( 0, fd_fifo_post(queue, &msg) );
+		msg = msg2;
+		CHECK( 0, fd_fifo_post(queue, &msg) );
+		msg = msg3;
+		CHECK( 0, fd_fifo_post(queue, &msg) );
+		
+		/* Check the count is 3 */
+		CHECK( 3, fd_fifo_length(queue) );
+		
+		/* Retrieve the first message using fd_fifo_get */
+		CHECK( 0, fd_fifo_get(queue, &msg) );
+		CHECK( msg1, msg);
+		CHECK( 2, fd_fifo_length(queue) );
+		
+		/* Retrieve the second message using fd_fifo_timedget */
+		CHECK(0, clock_gettime(CLOCK_REALTIME, &ts));
+		ts.tv_sec += 1; /* Set the timeout to 1 second */
+		CHECK( 0, fd_fifo_timedget(queue, &msg, &ts) );
+		CHECK( msg2, msg);
+		CHECK( 1, fd_fifo_length(queue) );
+		
+		/* Retrieve the third message using meq_tryget */
+		CHECK( 0, fd_fifo_tryget(queue, &msg) );
+		CHECK( msg3, msg);
+		CHECK( 0, fd_fifo_length(queue) );
+		
+		/* Check that another meq_tryget does not block */
+		CHECK( EWOULDBLOCK, fd_fifo_tryget(queue, &msg) );
+		CHECK( 0, fd_fifo_length(queue) );
+		
+		/* Check the timedget actually timesout */
+		CHECK(0, clock_gettime(CLOCK_REALTIME, &ts));
+		ts.tv_nsec += 1000000; /* 1 millisecond */
+		if (ts.tv_nsec >= 1000000000L) {
+			ts.tv_nsec -= 1000000000L;
+			ts.tv_sec += 1;
+		}
+		CHECK( ETIMEDOUT, fd_fifo_timedget(queue, &msg, &ts) );
+		CHECK( 0, fd_fifo_length(queue) );
+		
+		/* Post & get another message */
+		msg = msg1;
+		CHECK( 0, fd_fifo_post(queue, &msg) );
+		CHECK( 0, fd_fifo_timedget(queue, &msg, &ts) );
+		CHECK( msg1, msg);		
+		
+		/* Check some statistics */
+		CHECK( 0, fd_fifo_getstats(queue, NULL, NULL, &max, &count, NULL, NULL, NULL) );
+		CHECK( 3, max );
+		CHECK( 4, count );	
+		
+		/* We're done for basic tests */
+		CHECK( 0, fd_fifo_del(&queue) );
+	}
+	
+	/* Test robustness, ensure no messages are lost */
+	{
+#define NBR_MSG		200
+#define NBR_THREADS	60
+		struct fifo  		*queue = NULL;
+		pthread_barrier_t	 bar;
+		struct test_data	 td_1;
+		struct test_data	 td_2;
+		struct msg   		*msgs[NBR_MSG * NBR_THREADS * 2], *msg;
+		pthread_t  		 thr [NBR_THREADS * 2];
+		struct dict_object	*dwr_model = NULL;
+		int			 i;
+		int			 nbr_threads;
+#ifdef _POSIX_THREAD_THREADS_MAX
+		nbr_threads = _POSIX_THREAD_THREADS_MAX;
+#else /* _POSIX_THREAD_THREADS_MAX */
+		nbr_threads = sysconf(_SC_THREAD_THREADS_MAX);
+#endif /* _POSIX_THREAD_THREADS_MAX */
+		if ((nbr_threads <= 0) || (nbr_threads > NBR_THREADS * 2)) {
+			nbr_threads = NBR_THREADS;
+		} else {
+			TRACE_DEBUG(INFO, "Local limit on number of threads: %d", nbr_threads);
+			/* The local limit is below NBR_THREADS */
+			nbr_threads = (nbr_threads / 2) - 1;
+			/* Ensure we create at least a few threads! */
+			CHECK( 1, nbr_threads >= 10 ? 1 : 0 );
+		}
+		
+		/* Create the queue */
+		CHECK( 0, fd_fifo_new(&queue, 0) );
+		
+		/* Create the barrier */
+		CHECK( 0, pthread_barrier_init(&bar, NULL, nbr_threads * 2 + 1) );
+		
+		/* Initialize the ts */
+		CHECK(0, clock_gettime(CLOCK_REALTIME, &ts));
+		ts.tv_sec += 20; /* Set the timeout to 20 second */
+		
+		/* Create the messages */
+		CHECK( 0, fd_dict_search ( fd_g_config->cnf_dict, DICT_COMMAND, CMD_BY_NAME, "Device-Watchdog-Request",		&dwr_model, ENOENT ) );
+		for (i = 0; i < NBR_MSG * nbr_threads * 2; i++) {
+			CHECK( 0, fd_msg_new ( dwr_model, 0, &msgs[i] ) );
+		}
+		
+		/* Initialize the test data structures */
+		td_1.queue = queue;
+		td_1.bar = &bar;
+		td_1.ts  = &ts;
+		td_1.nbr = NBR_MSG;
+		td_2.queue = queue;
+		td_2.bar = &bar;
+		td_2.ts  = NULL;
+		td_2.nbr = NBR_MSG;
+		
+		/* Create the threads */
+		for (i=0; i < nbr_threads * 2; i++) {
+			CHECK( 0, pthread_create( &thr[i], NULL, test_fct, (i & 1) ? &td_1 : &td_2 ) );
+		}
+		
+		/* Synchronize everyone */
+		{
+			int ret = pthread_barrier_wait(&bar);
+			if (ret != PTHREAD_BARRIER_SERIAL_THREAD) {
+				CHECK( 0, ret);
+			} else {
+				CHECK( PTHREAD_BARRIER_SERIAL_THREAD, ret); /* for trace only */
+			}
+		}
+		
+		/* Now post all the messages */
+		for (i=0; i < NBR_MSG * nbr_threads * 2; i++) {
+			msg = msgs[i];
+			CHECK( 0, fd_fifo_post(queue, &msg) );
+		}
+		
+		/* Join all threads. This blocks if messages are lost... */
+		for (i=0; i < nbr_threads * 2; i++) {
+			CHECK( 0, pthread_join( thr[i], NULL ) );
+		}
+		
+		/* Check the count of the queue is back to 0 */
+		CHECK( 0, fd_fifo_length(queue) );
+		
+		/* Destroy this queue and the messages */
+		CHECK( 0, fd_fifo_del(&queue) );
+		for (i=0; i < NBR_MSG * nbr_threads * 2; i++) {
+			CHECK( 0, fd_msg_free(  msgs[i] ) );
+		}
+	}
+	
+	/* Test thread cancelation */
+	{
+		struct fifo      	*queue = NULL;
+		pthread_barrier_t	 bar;
+		struct test_data	 td;
+		pthread_t		 th;
+		
+		/* Create the queue */
+		CHECK( 0, fd_fifo_new(&queue, 0) );
+		
+		/* Create the barrier */
+		CHECK( 0, pthread_barrier_init(&bar, NULL, 2) );
+		
+		/* Initialize the ts */
+		CHECK(0, clock_gettime(CLOCK_REALTIME, &ts));
+		ts.tv_sec += 10; /* Set the timeout to 10 second */
+		
+		/* Initialize the test data structures */
+		td.queue = queue;
+		td.bar = &bar;
+		td.ts  = &ts;
+		td.nbr = 1;
+		
+		/* Create the thread */
+		CHECK( 0, pthread_create( &th, NULL, test_fct, &td ) );
+		
+		/* Wait for the thread to be running */
+		{
+			int ret = pthread_barrier_wait(&bar);
+			if (ret != PTHREAD_BARRIER_SERIAL_THREAD) {
+				CHECK( 0, ret);
+			} else {
+				CHECK( PTHREAD_BARRIER_SERIAL_THREAD, ret );
+			}
+		}
+		
+		/* Now cancel the thread */
+		CHECK( 0, pthread_cancel( th ) );
+		
+		/* Join it */
+		CHECK( 0, pthread_join( th, NULL ) );
+		
+		/* Do the same with the other function */
+		td.ts  = NULL;
+		
+		/* Create the thread */
+		CHECK( 0, pthread_create( &th, NULL, test_fct, &td ) );
+		
+		/* Wait for the thread to be running */
+		{
+			int ret = pthread_barrier_wait(&bar);
+			if (ret != PTHREAD_BARRIER_SERIAL_THREAD) {
+				CHECK( 0, ret);
+			} else {
+				CHECK( PTHREAD_BARRIER_SERIAL_THREAD, ret );
+			}
+		}
+		
+		/* Now cancel the thread */
+		CHECK( 0, pthread_cancel( th ) );
+		
+		/* Join it */
+		CHECK( 0, pthread_join( th, NULL ) );
+		
+		/* Destroy the queue */
+		CHECK( 0, fd_fifo_del(&queue) );
+	}
+	
+	/* Test the threashold function */
+	{
+		struct fifo * queue = NULL;
+		int i;
+		struct msg * msg  = NULL;
+		
+		/* Create the queue */
+		CHECK( 0, fd_fifo_new(&queue, 0) );
+		
+		/* Prepare the test data */
+		memset(&thrh_td, 0, sizeof(thrh_td));
+		thrh_td.queue = queue;
+		
+		/* Set the thresholds for the queue */
+		CHECK( 0, fd_fifo_setthrhd ( queue, NULL, 6, thrh_cb_h, 4, thrh_cb_l ) );
+		
+		/* Post 5 messages, no cb must be called. */
+		for (i=0; i<5; i++) {
+			msg = msg1;
+			CHECK( 0, fd_fifo_post(queue, &msg) );
+		} /* 5 msg in queue */
+		CHECK( 0, thrh_td.h_calls );
+		CHECK( 0, thrh_td.l_calls );
+		
+		/* Get all these messages, and check again */
+		for (i=0; i<5; i++) {
+			CHECK( 0, fd_fifo_get(queue, &msg) );
+		} /* 0 msg in queue */
+		CHECK( 0, thrh_td.h_calls );
+		CHECK( 0, thrh_td.l_calls );
+		
+		/* Now, post 6 messages, the high threashold */
+		for (i=0; i<6; i++) {
+			msg = msg1;
+			CHECK( 0, fd_fifo_post(queue, &msg) );
+		} /* 6 msg in queue */
+		CHECK( 1, thrh_td.h_calls );
+		CHECK( 0, thrh_td.l_calls );
+		
+		/* Remove 2 messages, to reach the low threshold */
+		for (i=0; i<2; i++) {
+			CHECK( 0, fd_fifo_get(queue, &msg) );
+		} /* 4 msg in queue */
+		CHECK( 1, thrh_td.h_calls );
+		CHECK( 1, thrh_td.l_calls );
+		
+		/* Come again at the high threshold */
+		for (i=0; i<2; i++) {
+			msg = msg1;
+			CHECK( 0, fd_fifo_post(queue, &msg) );
+		} /* 6 msg in queue */
+		CHECK( 2, thrh_td.h_calls );
+		CHECK( 1, thrh_td.l_calls );
+		
+		/* Suppose the queue continues to grow */
+		for (i=0; i<6; i++) {
+			msg = msg1;
+			CHECK( 0, fd_fifo_post(queue, &msg) );
+		} /* 12 msg in queue */
+		CHECK( 3, thrh_td.h_calls );
+		CHECK( 1, thrh_td.l_calls );
+		for (i=0; i<5; i++) {
+			msg = msg1;
+			CHECK( 0, fd_fifo_post(queue, &msg) );
+		} /* 17 msg in queue */
+		CHECK( 3, thrh_td.h_calls );
+		CHECK( 1, thrh_td.l_calls );
+		
+		/* Now the queue goes back to 0 messages */
+		for (i=0; i<17; i++) {
+			CHECK( 0, fd_fifo_get(queue, &msg) );
+		} /* 0 msg in queue */
+		CHECK( 3, thrh_td.h_calls );
+		CHECK( 3, thrh_td.l_calls );
+		
+		/* We're done for this test */
+		CHECK( 0, fd_fifo_del(&queue) );
+	}
+	
+	/* Test max queue limit */
+	{
+		struct fifo      	*queue = NULL;
+		struct test_data	 td;
+		pthread_t		 th;
+		int *			item, i;
+		
+		/* Create the queue */
+		CHECK( 0, fd_fifo_new(&queue, 10) );
+		
+		/* Initialize the test data structures */
+		td.queue = queue;
+		td.nbr = 15;
+		
+		CHECK( 0, pthread_create( &th, NULL, test_fct2, &td ) );
+		
+		usleep(100000); /* 100 millisec */
+		
+		CHECK( 10, iter );
+		
+		CHECK( 0, fd_fifo_tryget(queue, &item) );
+		CHECK( 0, *item);
+		free(item);
+		
+		usleep(100000); /* 100 millisec */
+		
+		CHECK( 11, iter );
+		
+		for (i=1; i<4; i++) {
+			CHECK( 0, fd_fifo_get(queue, &item) );
+			CHECK( i, *item);
+			free(item);
+		}
+		
+		usleep(100000); /* 100 millisec */
+		
+		CHECK( 14, iter );
+		
+		for (; i < td.nbr; i++) {
+			CHECK( 0, fd_fifo_tryget(queue, &item) );
+			CHECK( i, *item);
+			free(item);
+		}
+		
+		CHECK( 0, pthread_join( th, NULL ) );
+		CHECK( 15, iter );
+		
+	}
+	
+	/* Delete the messages */
+	CHECK( 0, fd_msg_free( msg1 ) );
+	CHECK( 0, fd_msg_free( msg2 ) );
+	CHECK( 0, fd_msg_free( msg3 ) );
+
+	/* That's all for the tests yet */
+	PASSTEST();
+} 
diff --git a/tests/testloadext.c b/tests/testloadext.c
new file mode 100644
index 0000000..452737f
--- /dev/null
+++ b/tests/testloadext.c
@@ -0,0 +1,100 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@freediameter.net>							 *
+*													 *
+* Copyright (c) 2011, 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"
+
+#ifndef BUILD_DIR
+#error "Missing BUILD_DIR information"
+#endif /* BUILD_DIR */
+
+#include <sys/types.h>
+#include <dirent.h>
+#include <libgen.h>
+#include <dlfcn.h>
+
+/* Main test routine */
+int main(int argc, char *argv[])
+{
+	DIR *dir;
+	struct dirent *dp;
+	char fullname[512];
+	int pathlen;
+
+	/* First, initialize the daemon modules */
+	INIT_FD();
+	CHECK( 0, fd_queues_init()  );
+	CHECK( 0, fd_msg_init()  );
+	CHECK( 0, fd_rtdisp_init()  );
+	
+	/* Find all extensions which have been compiled along the test */
+	TRACE_DEBUG(INFO, "Loading from: '%s'", BUILD_DIR "/extensions");
+	CHECK( 0, (dir = opendir (BUILD_DIR "/extensions")) == NULL ? 1 : 0 );
+	pathlen = snprintf(fullname, sizeof(fullname), BUILD_DIR "/extensions/");
+	
+	while ((dp = readdir (dir)) != NULL) {
+		char * dot = strrchr(dp->d_name, '.');
+		if (dot && !(strcmp(dot, ".fdx"))) {
+			/* We found a file with name *.fdx, attempt to load it */
+			void *hdl, * ep;
+			snprintf(fullname + pathlen, sizeof(fullname) - pathlen, "%s", dp->d_name);
+			
+			TRACE_DEBUG(INFO, "Extension: '%s'", dp->d_name);
+			
+			/* load */
+			hdl = dlopen(fullname, RTLD_NOW | RTLD_GLOBAL);
+			if (!hdl) {
+				TRACE_DEBUG(INFO, "Unable to load '%s': %s.", fullname, dlerror());
+			}
+			CHECK( 0, hdl == NULL ? 1 : 0 );
+			
+			/* resolve entry */
+			ep = dlsym( hdl, "fd_ext_init" );
+			if (!ep) {
+				TRACE_DEBUG(INFO, "No 'fd_ext_init' entry point in '%s': %s.", fullname, dlerror());
+			}
+			CHECK( 0, ep == NULL ? 1 : 0 );
+			
+			/* Done, now unload */
+#ifndef SKIP_DLCLOSE
+			CHECK( 0, dlclose(hdl) );
+#endif /* SKIP_DLCLOSE */
+		}
+	}
+	
+	CHECK( 0, closedir(dir) );
+	
+	PASSTEST();
+} 
+	
diff --git a/tests/testmesg.c b/tests/testmesg.c
new file mode 100644
index 0000000..5525ff4
--- /dev/null
+++ b/tests/testmesg.c
@@ -0,0 +1,1455 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@freediameter.net>							 *
+*													 *
+* Copyright (c) 2015, 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"
+
+/* Main test routine */
+int main(int argc, char *argv[])
+{
+	struct msg * acr = NULL;
+	struct avp * pi = NULL, *avp1, *avp2;
+	unsigned char * buf = NULL;
+	
+	/* First, initialize the daemon modules */
+	INIT_FD();
+	
+	/* Create the message object from model */
+	{
+		struct dict_object * acr_model = NULL;
+
+		/* Now find the ACR dictionary object */
+		CHECK( 0, fd_dict_search ( fd_g_config->cnf_dict, DICT_COMMAND, CMD_BY_NAME, "Accounting-Request", &acr_model, ENOENT ) );
+
+		/* Create the instance, using the templates */
+		CHECK( 0, fd_msg_new ( acr_model, 0, &acr ) );
+
+		/* Check there is no child */
+		CHECK( ENOENT, fd_msg_browse ( acr, MSG_BRW_FIRST_CHILD, NULL, NULL) );
+		
+		#if 0
+		/* For debug: dump the object */
+		fd_log_debug("Dumping Accounting-Request empty message:");
+		fd_log_debug("%s", fd_msg_dump_treeview(FD_DUMP_TEST_PARAMS, acr, fd_g_config->cnf_dict, 0, 1));
+		#endif
+	}
+	
+	/* Create the Proxy-Info AVP from model */
+	{
+		struct dict_object * pi_model = NULL;
+
+		/* Now find the ACR dictionary object */
+		CHECK( 0, fd_dict_search ( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Proxy-Info", &pi_model, ENOENT ) );
+
+		/* Create the instance, using the templates */
+		CHECK( 0, fd_msg_avp_new ( pi_model, 0, &pi ) );
+
+		#if 0
+		/* For debug: dump the object */
+		fd_log_debug("Dumping Proxy-Info AVP");
+		fd_log_debug("%s", fd_msg_dump_treeview(FD_DUMP_TEST_PARAMS, pi, fd_g_config->cnf_dict, 0, 1));
+		fd_log_debug("Dumping dictionary model");
+		fd_log_debug("%s", fd_dict_dump_object(FD_DUMP_TEST_PARAMS, pi_model));
+		#endif
+		
+	}
+	
+	/* Get a reference to the current last AVP in the message */
+	{
+		int diff = 0;
+		
+		CHECK( 0, fd_msg_avp_new ( NULL, 0, &avp1 ) );
+		CHECK( 0, fd_msg_avp_add ( acr, MSG_BRW_LAST_CHILD, avp1) );
+		
+		CHECK( 0, fd_msg_browse ( acr, MSG_BRW_LAST_CHILD, &avp2, &diff) );
+		CHECK( 1, diff );
+		CHECK( avp1, avp2 );
+		
+		/* Check that we cannot add this AVP to another object since it is already linked */
+		CHECK( EINVAL, fd_msg_avp_add( pi, MSG_BRW_LAST_CHILD, avp1) );
+	}
+
+	/* Now add the Proxy-Info AVP at the end of the message */
+	{
+		CHECK( 0, fd_msg_avp_add( acr, MSG_BRW_LAST_CHILD, pi) );
+		#if 0
+		/* For debug: dump the object */
+		fd_log_debug("Dumping Accounting-Request with Proxy-Info AVP at the end");
+		fd_log_debug("%s", fd_msg_dump_treeview(FD_DUMP_TEST_PARAMS, acr, fd_g_config->cnf_dict, 0, 1));
+		#endif
+	}
+	
+	/* Check the last child is now the proxy-Info */
+	{
+		CHECK( 0, fd_msg_browse ( acr, MSG_BRW_LAST_CHILD, &avp2, NULL) );
+		CHECK( pi, avp2 );
+	}
+	
+	/* Check that the avp before the proxy-info is the previous last one */
+	{
+		int diff = 0;
+		CHECK( 0, fd_msg_browse ( pi, MSG_BRW_PREV, &avp2, &diff) );
+		CHECK( avp1, avp2 );
+		CHECK( 0, diff);
+	}
+	
+	/* Check that there are no AVP after the proxy-info */
+	CHECK( ENOENT, fd_msg_browse ( pi, MSG_BRW_NEXT, NULL, NULL) );
+	
+	/* Test the fd_msg_free function unlinks the object properly */
+	{
+		struct dict_object * rr_model = NULL;
+
+		/* Now find the dictionary object */
+		CHECK( 0, fd_dict_search ( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Route-Record", &rr_model, ENOENT ) );
+
+		/* Create the instance, using the templates */
+		CHECK( 0, fd_msg_avp_new ( rr_model, 0, &avp1 ) );
+		
+		/* Add the AVP at the end of the message */
+		CHECK( 0, fd_msg_avp_add( pi, MSG_BRW_NEXT, avp1) );
+		
+		/* Check the last AVP of the message is now this one */
+		CHECK( 0, fd_msg_browse ( acr, MSG_BRW_LAST_CHILD, &avp2, NULL) );
+		CHECK( avp1, avp2 );
+		
+		/* Now delete it */
+		CHECK( 0, fd_msg_free( avp1 ) );
+		
+		/* Check the last AVP of the message is back to pi */
+		CHECK( 0, fd_msg_browse ( acr, MSG_BRW_LAST_CHILD, &avp2, NULL) );
+		CHECK( pi, avp2 );
+		
+		/* Delete the whole message */
+		CHECK( 0, fd_msg_free( acr ) );
+	}
+	
+	/* Recreate the message object */
+	{
+		struct dict_object * acr_model = NULL;
+
+		/* Now find the ACR dictionary object */
+		CHECK( 0, fd_dict_search ( fd_g_config->cnf_dict, DICT_COMMAND, CMD_BY_NAME, "Accounting-Request", &acr_model, ENOENT ) );
+
+		/* Create the instance, using the templates */
+		CHECK( 0, fd_msg_new ( acr_model, 0, &acr ) );
+	}
+	
+	/* Now let's create some additional Dictionary objects for the test */
+	{
+		/* The constant values used here are totally arbitrary chosen */
+		struct dict_object * vendor;
+		{
+			struct dict_vendor_data vendor_data = { 73565, "Vendor test" };
+			CHECK( 0, fd_dict_new ( fd_g_config->cnf_dict, DICT_VENDOR, &vendor_data , NULL, &vendor ) );
+		}
+		
+		{
+			struct dict_application_data app_data = { 73566, "Application test" };
+			CHECK( 0, fd_dict_new ( fd_g_config->cnf_dict, DICT_APPLICATION, &app_data , vendor, NULL ) );
+		}
+		
+		{
+			struct dict_avp_data avp_data = { 73567, 0, "AVP Test - no vendor - f32", 0, 0, AVP_TYPE_FLOAT32 };
+			CHECK( 0, fd_dict_new ( fd_g_config->cnf_dict, DICT_AVP, &avp_data , NULL, NULL ) );
+		}
+		
+		{
+			struct dict_avp_data avp_data = { 139103, 0, "AVP Test - no vendor - f64", 0, 0, AVP_TYPE_FLOAT64 };
+			CHECK( 0, fd_dict_new ( fd_g_config->cnf_dict, DICT_AVP, &avp_data , NULL, NULL ) );
+		}
+		
+		{ 
+			struct dict_object  * type = NULL;
+			struct dict_type_data type_data = { AVP_TYPE_INTEGER64, "Int64 test" };
+			struct dict_avp_data  avp_data = { 73568, 73565, "AVP Test - i64", AVP_FLAG_VENDOR, AVP_FLAG_VENDOR, AVP_TYPE_INTEGER64 };
+			CHECK( 0, fd_dict_new ( fd_g_config->cnf_dict, DICT_TYPE, &type_data , NULL, &type ) );
+			CHECK( 0, fd_dict_new ( fd_g_config->cnf_dict, DICT_AVP, &avp_data , type, NULL ) );
+		}
+		
+		{
+			struct dict_object     * type = NULL;
+			struct dict_type_data    type_data = { AVP_TYPE_INTEGER32, "Enum32 test" };
+			struct dict_enumval_data val1 = { "i32 const test (val 1)", { .i32 = 1 } };
+			struct dict_enumval_data val2 = { "i32 const test (val 2)", { .i32 = 2 } };
+			struct dict_enumval_data val3 = { "i32 const test (val -5)",{ .i32 = -5 } };
+			struct dict_avp_data     avp_data = { 73569, 73565, "AVP Test - enumi32", AVP_FLAG_VENDOR, AVP_FLAG_VENDOR, AVP_TYPE_INTEGER32 };
+			
+			CHECK( 0, fd_dict_new ( fd_g_config->cnf_dict, DICT_TYPE, &type_data , NULL, &type ) );
+			CHECK( 0, fd_dict_new ( fd_g_config->cnf_dict, DICT_AVP, &avp_data , type, NULL ) );
+			CHECK( 0, fd_dict_new ( fd_g_config->cnf_dict, DICT_ENUMVAL, &val1 , type, NULL ) );
+			CHECK( 0, fd_dict_new ( fd_g_config->cnf_dict, DICT_ENUMVAL, &val2 , type, NULL ) );
+			CHECK( 0, fd_dict_new ( fd_g_config->cnf_dict, DICT_ENUMVAL, &val3 , type, NULL ) );
+		}
+			
+		{ 
+			struct dict_object  * type = NULL;
+			struct dict_type_data type_data = { AVP_TYPE_OCTETSTRING, "OS test" };
+			struct dict_avp_data  avp_data = { 73570, 73565, "AVP Test - os", AVP_FLAG_VENDOR, AVP_FLAG_VENDOR, AVP_TYPE_OCTETSTRING };
+			CHECK( 0, fd_dict_new ( fd_g_config->cnf_dict, DICT_TYPE, &type_data , NULL, &type ) );
+			CHECK( 0, fd_dict_new ( fd_g_config->cnf_dict, DICT_AVP, &avp_data , type, NULL ) );
+		}
+		
+		{
+			struct dict_object     * type = NULL;
+			struct dict_type_data    type_data = { AVP_TYPE_OCTETSTRING, "OS enum test" };
+			struct dict_enumval_data val1 = { "os const test (Test)", { .os = { (unsigned char *)"Test", 4 } } };
+			struct dict_enumval_data val2 = { "os const test (waaad)", { .os = { (unsigned char *)"waaad", 5 } } };
+			struct dict_enumval_data val3 = { "os const test (waa)", { .os = { (unsigned char *)"waaad", 3 } } };
+			struct dict_avp_data     avp_data = { 73571, 73565, "AVP Test - enumos", AVP_FLAG_VENDOR, AVP_FLAG_VENDOR, AVP_TYPE_OCTETSTRING };
+			
+			CHECK( 0, fd_dict_new ( fd_g_config->cnf_dict, DICT_TYPE, &type_data , NULL, &type ) );
+			CHECK( 0, fd_dict_new ( fd_g_config->cnf_dict, DICT_AVP, &avp_data , type, NULL ) );
+			CHECK( 0, fd_dict_new ( fd_g_config->cnf_dict, DICT_ENUMVAL, &val1 , type, NULL ) );
+			CHECK( 0, fd_dict_new ( fd_g_config->cnf_dict, DICT_ENUMVAL, &val2 , type, NULL ) );
+			CHECK( 0, fd_dict_new ( fd_g_config->cnf_dict, DICT_ENUMVAL, &val3 , type, NULL ) );
+		}
+		
+		{
+			struct dict_object * gavp = NULL;
+			struct dict_avp_data avp_data = { 73572, 73565, "AVP Test - grouped", AVP_FLAG_VENDOR, AVP_FLAG_VENDOR, AVP_TYPE_GROUPED };
+			
+			CHECK( 0, fd_dict_new ( fd_g_config->cnf_dict, DICT_AVP, &avp_data , NULL, &gavp ) );
+			
+			/* Macro to search AVP and create a rule */		
+			#define ADD_RULE( _parent, _vendor, _avpname, _pos, _min, _max, _ord ) {		\
+				struct dict_object * _avp = NULL;						\
+				struct dict_avp_request _req = { (_vendor), 0, (_avpname) };			\
+				struct dict_rule_data _data;							\
+				CHECK( 0, fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME_AND_VENDOR, &_req, &_avp, ENOENT));\
+				_data.rule_avp = _avp;								\
+				_data.rule_position = (_pos);							\
+				_data.rule_order = (_ord);							\
+				_data.rule_min = (_min);							\
+				_data.rule_max = (_max);							\
+				CHECK( 0, fd_dict_new ( fd_g_config->cnf_dict, DICT_RULE, &_data , (_parent), NULL ) );	\
+			}
+			
+			ADD_RULE(gavp, 73565, "AVP Test - os", RULE_OPTIONAL,   -1, -1,  0);
+			
+		}
+			
+		{
+			struct dict_object  * application = NULL;
+			struct dict_object  * command = NULL;
+			struct dict_cmd_data  cmd_data = { 73573, "Test-Command-Request", CMD_FLAG_REQUEST, CMD_FLAG_REQUEST };
+			
+			CHECK( 0, fd_dict_search ( fd_g_config->cnf_dict, DICT_APPLICATION, APPLICATION_BY_NAME, "Application test", &application, ENOENT ) );
+			CHECK( 0, fd_dict_new ( fd_g_config->cnf_dict, DICT_COMMAND, &cmd_data , application, &command ) );
+			ADD_RULE(command, 0,     "AVP Test - no vendor - f32", 	RULE_FIXED_HEAD, -1,  1,  1);
+			ADD_RULE(command, 73565, "AVP Test - i64",		RULE_REQUIRED,   -1, -1,  0);
+			ADD_RULE(command, 73565, "AVP Test - enumi32", 		RULE_OPTIONAL,   -1, -1,  0);
+			ADD_RULE(command, 73565, "AVP Test - os", 		RULE_OPTIONAL,   -1, -1,  0);
+			ADD_RULE(command, 73565, "AVP Test - enumos", 		RULE_OPTIONAL,   -1, -1,  0);
+			ADD_RULE(command, 73565, "AVP Test - grouped", 		RULE_OPTIONAL,   -1, -1,  0);
+		}
+		
+		{
+			struct dict_object  * application = NULL;
+			struct dict_object  * command = NULL;
+			struct dict_cmd_data  cmd_data = { 73573, "Test-Command-Answer", CMD_FLAG_REQUEST, 0 };
+			
+			CHECK( 0, fd_dict_search ( fd_g_config->cnf_dict, DICT_APPLICATION, APPLICATION_BY_NAME, "Application test", &application, ENOENT ) );
+			CHECK( 0, fd_dict_new ( fd_g_config->cnf_dict, DICT_COMMAND, &cmd_data , application, &command ) );
+		}
+		
+		{
+			struct dict_object  * gavp = NULL;
+			struct dict_avp_data  avp_data = { 73574, 73565, "AVP Test - rules", AVP_FLAG_VENDOR, AVP_FLAG_VENDOR, AVP_TYPE_GROUPED };
+			
+			CHECK( 0, fd_dict_new ( fd_g_config->cnf_dict, DICT_AVP, &avp_data , NULL, &gavp ) );
+			
+			ADD_RULE(gavp,     0, "AVP Test - no vendor - f32", RULE_FIXED_HEAD,   0, 1, 1);
+			ADD_RULE(gavp, 73565, "AVP Test - i64", 	    RULE_FIXED_HEAD,  -1, 1, 2);
+			ADD_RULE(gavp, 73565, "AVP Test - enumi32", 	    RULE_FIXED_HEAD,  -1, 1, 3);
+			ADD_RULE(gavp, 73565, "AVP Test - os", 	    	    RULE_REQUIRED,     2, 3, 0);
+			ADD_RULE(gavp, 73565, "AVP Test - enumos",     	    RULE_OPTIONAL,     0, 1, 0);
+			ADD_RULE(gavp, 73565, "AVP Test - grouped",         RULE_FIXED_TAIL,  -1, 1, 1);
+			/* ABNF : 
+				< no vendor - f32 >
+				< i64 >
+				< enumi32 >
+			    2*3 { os }
+			     *1 [ enumos ]
+				< grouped >
+						*/
+			#if 0
+			fd_log_debug("%s", fd_dict_dump_object(FD_DUMP_TEST_PARAMS, gavp));
+			#endif
+		}
+
+		{ 
+			struct dict_object  * type = NULL;
+			struct dict_type_data type_data = { AVP_TYPE_OCTETSTRING, "OS test2", NULL, NULL, NULL, fd_dictfct_CharInOS_check, "@." };
+			struct dict_avp_data  avp_data = { 73575, 73565, "AVP Test - os2", AVP_FLAG_VENDOR, AVP_FLAG_VENDOR, AVP_TYPE_OCTETSTRING };
+			CHECK( 0, fd_dict_new ( fd_g_config->cnf_dict, DICT_TYPE, &type_data , NULL, &type ) );
+			CHECK( 0, fd_dict_new ( fd_g_config->cnf_dict, DICT_AVP, &avp_data , type, NULL ) );
+		}
+		
+		#if 0
+		{
+			fd_log_debug("%s", fd_dict_dump_object(FD_DUMP_TEST_PARAMS, vendor));
+		}
+		#endif
+	}
+	
+	/* Now create some values and check the length is correctly handled */
+	{
+		struct dict_object * cmd_model = NULL;
+		struct msg         * msg = NULL;
+		struct dict_object * avp_model = NULL;
+		struct avp         * avp = NULL;
+		union avp_value      value;
+		
+		CHECK( 0, fd_dict_search ( fd_g_config->cnf_dict, DICT_COMMAND, CMD_BY_NAME, "Test-Command-Request", &cmd_model, ENOENT ) );
+		
+		/* Check an error is trigged if the AVP has no value set */
+		{
+			CHECK( 0, fd_dict_search ( fd_g_config->cnf_dict, DICT_AVP,     AVP_BY_NAME,     "AVP Test - no vendor - f32", &avp_model, ENOENT ) );
+			
+			CHECK( 0, fd_msg_new ( cmd_model, 0, &msg ) );
+			CHECK( 0, fd_msg_avp_new ( avp_model, 0, &avp ) );
+			
+			CHECK( 0, fd_msg_avp_add ( msg, MSG_BRW_FIRST_CHILD, avp ) );
+			
+			CHECK( EINVAL, fd_msg_update_length ( avp ) );
+			CHECK( EINVAL, fd_msg_update_length ( msg ) );
+			
+			CHECK( 0, fd_msg_free( msg ) );
+		}
+		
+		/* Check the sizes are handled properly */
+		{
+			struct avp * avpi = NULL;
+			struct avp * avpch = NULL;
+			struct avp_hdr * avpdata = NULL;
+			struct msg_hdr * msgdata = NULL;
+			#define ADD_AVP( _parent, _position, _avpi, _avpvendor, _avpname) {			\
+				struct dict_object * _avp = NULL;						\
+				struct dict_avp_request _req = { (_avpvendor), 0, (_avpname) };			\
+				CHECK( 0, fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME_AND_VENDOR, &_req, &_avp, ENOENT));\
+				CHECK( 0, fd_msg_avp_new ( _avp, 0, &_avpi ) );					\
+				CHECK( 0, fd_msg_avp_add ( (_parent), (_position), _avpi ) );			\
+			}
+			/* Create a message with many AVP inside */
+			CHECK( 0, fd_msg_new ( cmd_model, 0, &msg ) );
+			CHECK( 0, fd_msg_hdr ( msg, &msgdata ) );
+			
+			/* Avp no vendor, float32 => size = 12 */
+			ADD_AVP( msg, MSG_BRW_LAST_CHILD, avpi, 0,     "AVP Test - no vendor - f32" );
+			value.f32 = 3.1415;
+			CHECK( 0, fd_msg_avp_setvalue ( avpi, &value ) );
+			CHECK( 0, fd_msg_update_length ( avpi ) );
+			#if 0
+			fd_log_debug("AVP no vendor, value 3.1415:");
+			fd_log_debug("%s", fd_msg_dump_treeview(FD_DUMP_TEST_PARAMS, avpi, fd_g_config->cnf_dict, 0, 0));
+			#endif
+			CHECK( 0, fd_msg_avp_hdr ( avpi, &avpdata ) );
+			CHECK( 12, avpdata->avp_len );
+			
+			/* Check what happens when we delete the value */
+			CHECK( 0, fd_msg_avp_setvalue ( avpi, NULL ) );
+			CHECK( EINVAL, fd_msg_update_length ( avpi ) );
+			CHECK( 0, fd_msg_avp_setvalue ( avpi, &value ) );
+			
+			/* Add a vendor AVP, integer64 => size = 20 */
+			ADD_AVP( msg, MSG_BRW_LAST_CHILD, avpi, 73565, "AVP Test - i64" );
+			value.i64 = 0x123456789abcdeLL;
+			CHECK( 0, fd_msg_avp_setvalue ( avpi, &value ) );
+			CHECK( 0, fd_msg_update_length ( avpi ) );
+			#if 0
+			fd_log_debug("AVP vendor, value 0x123456789abcdeL:");
+			fd_log_debug("%s", fd_msg_dump_treeview(FD_DUMP_TEST_PARAMS, avpi, fd_g_config->cnf_dict, 0, 0));
+			#endif
+			CHECK( 0, fd_msg_avp_hdr ( avpi, &avpdata ) );
+			CHECK( 20, avpdata->avp_len );
+			
+			/* Check the size of the message is 20 (header) + 12 + 20 = 52 */
+			CHECK( 0, fd_msg_update_length ( msg ) );
+			CHECK( 52, msgdata->msg_length );
+			
+			/* Add an AVP with an enum value */
+			ADD_AVP( msg, MSG_BRW_LAST_CHILD, avpi, 73565, "AVP Test - enumi32" );
+			{
+				struct dict_object * type_model = NULL;
+				struct dict_object * value_model = NULL;
+				struct dict_enumval_request request;
+				
+				CHECK( 0, fd_msg_model ( avpi, &avp_model ) );
+				CHECK( 0, fd_dict_search ( fd_g_config->cnf_dict, DICT_TYPE, TYPE_OF_AVP, avp_model, &type_model, ENOENT ) );
+				memset(&request, 0, sizeof(request));
+				request.type_obj = type_model;
+				request.search.enum_name = "i32 const test (val 2)";
+				CHECK( 0, fd_dict_search ( fd_g_config->cnf_dict, DICT_ENUMVAL, ENUMVAL_BY_STRUCT, &request, &value_model, ENOENT ) );
+				CHECK( 0, fd_dict_getval ( value_model, &request.search ) );
+				CHECK( 0, fd_msg_avp_setvalue ( avpi, &request.search.enum_value ) );
+				#if 0
+				fd_log_debug("AVP enum i32, value 2 (from const):");
+				fd_log_debug("%s", fd_msg_dump_treeview(FD_DUMP_TEST_PARAMS, avpi, fd_g_config->cnf_dict, 0, 0));
+				#endif
+			}
+			
+			/* Add an AVP with an enum value, negative */
+			ADD_AVP( msg, MSG_BRW_LAST_CHILD, avpi, 73565, "AVP Test - enumi32" );
+			{
+				struct dict_object  * type_model = NULL;
+				struct dict_object  * value_model = NULL;
+				struct dict_enumval_request request;
+				
+				CHECK( 0, fd_msg_model ( avpi, &avp_model ) );
+				CHECK( 0, fd_dict_search ( fd_g_config->cnf_dict, DICT_TYPE, TYPE_OF_AVP, avp_model, &type_model, ENOENT ) );
+				memset(&request, 0, sizeof(request));
+				request.type_obj = type_model;
+				request.search.enum_name = "i32 const test (val -5)";
+				CHECK( 0, fd_dict_search ( fd_g_config->cnf_dict, DICT_ENUMVAL, ENUMVAL_BY_STRUCT, &request, &value_model, ENOENT ) );
+				CHECK( 0, fd_dict_getval ( value_model, &request.search ) );
+				CHECK( 0, fd_msg_avp_setvalue ( avpi, &request.search.enum_value ) );
+				#if 0
+				fd_log_debug("AVP enum i32, value -5 (from const):");
+				fd_log_debug("%s", fd_msg_dump_treeview(FD_DUMP_TEST_PARAMS, avpi, fd_g_config->cnf_dict, 0, 0));
+				#endif
+				/* Check the size is correct ( 12 for header + 4 for value ) */
+				CHECK( 0, fd_msg_update_length ( avpi ) );
+				CHECK( 0, fd_msg_avp_hdr ( avpi, &avpdata ) );
+				CHECK( 16, avpdata->avp_len );
+			}
+			
+			/* Now add a value which is not a constant into an enumerated AVP */
+			ADD_AVP( msg, MSG_BRW_LAST_CHILD, avpi, 73565, "AVP Test - enumi32" );
+			value.i32 = -10;
+			CHECK( 0, fd_msg_avp_setvalue ( avpi, &value ) );
+			CHECK( 0, fd_msg_update_length ( avpi ) );
+			#if 0
+			fd_log_debug("AVP vendor enum i32, value -10 (not const):");
+			fd_log_debug("%s", fd_msg_dump_treeview(FD_DUMP_TEST_PARAMS, avpi, fd_g_config->cnf_dict, 0, 0));
+			#endif
+			
+			/* Add an octetstring AVP */
+			ADD_AVP( msg, MSG_BRW_LAST_CHILD, avpi, 73565, "AVP Test - os" );
+			{
+				unsigned char buf[90];
+				memcpy(&buf, "This\0 is a buffer of dat\a. It is not a string so we can have any c\0ntr\0l character here...\0\0", 89);
+				value.os.data = buf;
+				value.os.len = 89;
+				CHECK( 0, fd_msg_avp_setvalue ( avpi, &value ) );
+				memset(&buf, 0, sizeof(buf)); /* Test that the OS value is really copied */
+				CHECK( 0, fd_msg_update_length ( avpi ) );
+				#if 0
+				fd_log_debug("AVP octet string, 'This\\0 is a b...'");
+				fd_log_debug("%s", fd_msg_dump_treeview(FD_DUMP_TEST_PARAMS, avpi, fd_g_config->cnf_dict, 0, 0));
+				#endif
+				CHECK( 0, fd_msg_avp_hdr ( avpi, &avpdata ) );
+				CHECK( 101, avpdata->avp_len );
+				CHECK( 'T', avpdata->avp_value->os.data[0] );
+				CHECK( 'i', avpdata->avp_value->os.data[6] );
+			}
+
+			/* Check the size of the message is 20 (header) + 12 + 20 + 16 * 3 + 101 + 3 (padding) = 204 */
+			CHECK( 0, fd_msg_update_length ( msg ) );
+			CHECK( 204, msgdata->msg_length );
+			
+			/* Add an octetstring from an enumerated constant */
+			ADD_AVP( msg, MSG_BRW_LAST_CHILD, avpi, 73565, "AVP Test - enumos" );
+			{
+				struct dict_object  * type_model = NULL;
+				struct dict_object  * value_model = NULL;
+				struct dict_enumval_request request;
+				
+				CHECK( 0, fd_msg_model ( avpi, &avp_model ) );
+				CHECK( 0, fd_dict_search ( fd_g_config->cnf_dict, DICT_TYPE, TYPE_OF_AVP, avp_model, &type_model, ENOENT ) );
+				memset(&request, 0, sizeof(request));
+				request.type_obj = type_model;
+				request.search.enum_name = "os const test (waaad)";
+				CHECK( 0, fd_dict_search ( fd_g_config->cnf_dict, DICT_ENUMVAL, ENUMVAL_BY_STRUCT, &request, &value_model, ENOENT ) );
+				CHECK( 0, fd_dict_getval ( value_model, &request.search ) );
+				CHECK( 0, fd_msg_avp_setvalue ( avpi, &request.search.enum_value ) );
+				#if 0
+				fd_log_debug("AVP Enumuerated OctetString (from const):");
+				fd_log_debug("%s", fd_msg_dump_treeview(FD_DUMP_TEST_PARAMS, avpi, fd_g_config->cnf_dict, 0, 0));
+				#endif
+				/* Check the size is correct ( 12 for header + 5 for value ) */
+				CHECK( 0, fd_msg_update_length ( avpi ) );
+				CHECK( 0, fd_msg_avp_hdr ( avpi, &avpdata ) );
+				CHECK( 17, avpdata->avp_len );
+			}
+				
+			/* Add an octetstring from an enumerated constant */
+			ADD_AVP( msg, MSG_BRW_LAST_CHILD, avpi, 73565, "AVP Test - enumos" );
+			{
+				struct dict_object  * type_model = NULL;
+				struct dict_object  * value_model = NULL;
+				struct dict_enumval_request request;
+				
+				CHECK( 0, fd_msg_model ( avpi, &avp_model ) );
+				CHECK( 0, fd_dict_search ( fd_g_config->cnf_dict, DICT_TYPE, TYPE_OF_AVP, avp_model, &type_model, ENOENT ) );
+				memset(&request, 0, sizeof(request));
+				request.type_obj = type_model;
+				request.search.enum_name = "os const test (waa)";
+				CHECK( 0, fd_dict_search ( fd_g_config->cnf_dict, DICT_ENUMVAL, ENUMVAL_BY_STRUCT, &request, &value_model, ENOENT ) );
+				CHECK( 0, fd_dict_getval ( value_model, &request.search ) );
+				CHECK( 0, fd_msg_avp_setvalue ( avpi, &request.search.enum_value ) );
+				#if 0
+				fd_log_debug("AVP Enumuerated OctetString (from const):");
+				fd_log_debug("%s", fd_msg_dump_treeview(FD_DUMP_TEST_PARAMS, avpi, fd_g_config->cnf_dict, 0, 0));
+				#endif
+				/* Check the size is correct ( 12 for header + 3 for value ) */
+				CHECK( 0, fd_msg_update_length ( avpi ) );
+				CHECK( 0, fd_msg_avp_hdr ( avpi, &avpdata ) );
+				CHECK( 15, avpdata->avp_len );
+			}
+				
+
+			/* Check the size of the message is 20 (header) + 12 + 20 + 16 * 3 + (101 + 3) + (17 + 3) + (15 + 1) = 240 */
+			CHECK( 0, fd_msg_update_length ( msg ) );
+			CHECK( 240, msgdata->msg_length );
+			
+			/* Now test the grouped AVPs */	
+			ADD_AVP( msg, MSG_BRW_LAST_CHILD, avpi, 73565, "AVP Test - grouped" );
+			  ADD_AVP( avpi, MSG_BRW_LAST_CHILD, avpch, 73565, "AVP Test - os" );
+			  {
+				value.os.data = (unsigned char *)"12345678";
+				value.os.len = 8;
+				CHECK( 0, fd_msg_avp_setvalue ( avpch, &value ) );
+				#if 0
+				fd_log_debug("AVP octet string, '1234678'");
+				fd_log_debug("%s", fd_msg_dump_treeview(FD_DUMP_TEST_PARAMS, avpch, fd_g_config->cnf_dict, 0, 0));
+				#endif
+				CHECK( 0, fd_msg_update_length ( avpch ) );
+				CHECK( 0, fd_msg_avp_hdr ( avpch, &avpdata ) );
+				CHECK( 20, avpdata->avp_len );
+			  }
+			  ADD_AVP( avpi, MSG_BRW_LAST_CHILD, avpch, 73565, "AVP Test - os" );
+			  {
+				value.os.data = (unsigned char *)"123456789";
+				value.os.len = 9;
+				CHECK( 0, fd_msg_avp_setvalue ( avpch, &value ) );
+				#if 0
+				fd_log_debug("AVP octet string, '12346789'");
+				fd_log_debug("%s", fd_msg_dump_treeview(FD_DUMP_TEST_PARAMS, avpch, fd_g_config->cnf_dict, 0, 0));
+				#endif
+			  }
+			
+			/* Check the size is updated recursively: (gavp hdr: 12) + (avp1: 20) + (avp2: 21 + 3) = 56 */
+			CHECK( 0, fd_msg_update_length ( avpi ) );
+			CHECK( 0, fd_msg_avp_hdr ( avpi, &avpdata ) );
+			CHECK( 56, avpdata->avp_len );
+			
+			/* Add another similar grouped AVP, to have lot of padding */
+			ADD_AVP( msg, MSG_BRW_LAST_CHILD, avpi, 73565, "AVP Test - grouped" );
+			  ADD_AVP( avpi, MSG_BRW_LAST_CHILD, avpch, 73565, "AVP Test - os" );
+			  {
+				value.os.data = (unsigned char *)"1";
+				value.os.len = 1;
+				CHECK( 0, fd_msg_avp_setvalue ( avpch, &value ) );
+			  }
+			  ADD_AVP( avpi, MSG_BRW_LAST_CHILD, avpch, 73565, "AVP Test - os" );
+			  {
+				value.os.data = (unsigned char *)"1234567";
+				value.os.len = 7;
+				CHECK( 0, fd_msg_avp_setvalue ( avpch, &value ) );
+			  }
+			
+			/* Now check the global size of the message, if padding is correctly handled */
+			/* size = 20 (header) + 12 + 20 + 16 * 3 + (101 + 3) + (17 + 3) + (15 + 1) 
+			 *        + ( 12 + ( 20 + 21) + 3 )         # padding for the grouped AVP = 3
+			 *        + ( 12 + ( (13 + 3) + 19 ) + 1 )  # and 1 for this one
+			 * size = 240 + 56 + 48 = 344
+			 */
+			CHECK( 0, fd_msg_update_length ( msg ) );
+			#if 0
+			fd_log_debug("%s", fd_msg_dump_treeview(FD_DUMP_TEST_PARAMS, msg, fd_g_config->cnf_dict, 0, 1));
+			#endif
+			CHECK( 344, msgdata->msg_length );
+			
+			/* Set the application to the test application: 73566 */
+			msgdata->msg_appl = 73566;
+			
+			/* Set the hop-by-hop ID to a random value: 0x4b44b41d */
+			msgdata->msg_hbhid = 0x4b44b41d;
+			/* Set the end-to-end ID to a random value: 0xe2ee2e1d */
+			msgdata->msg_eteid = 0xe2ee2e1d;
+		}
+		
+		/* Test the msg_bufferize function */
+		{
+			
+			CHECK( 0, fd_msg_bufferize( msg, &buf, NULL ) );
+			
+			/* Test the first bytes */
+			CHECK( 0x01, buf[0] ); /* Version */
+			CHECK( 0x00, buf[1] ); /* Length: 344 = 0x000158 */
+			CHECK( 0x01, buf[2] );
+			CHECK( 0x58, buf[3] );
+			CHECK( 0x80, buf[4] ); /* flags: only "R" is set. */
+			CHECK( 0x01, buf[5] ); /* Command code: 73573 = 0x011F65 */
+			CHECK( 0x1F, buf[6] );
+			CHECK( 0x65, buf[7] );
+			CHECK( 0x00, buf[8] ); /* App ID: 73566 = 0x00011F5E */
+			CHECK( 0x01, buf[9] ); 
+			CHECK( 0x1F, buf[10] );
+			CHECK( 0x5E, buf[11] );
+			CHECK( 0x4b, buf[12] ); /* hop-by-hop id: 0x4b44b41d */
+			CHECK( 0x44, buf[13] );
+			CHECK( 0xb4, buf[14] );
+			CHECK( 0x1d, buf[15] );
+			CHECK( 0xe2, buf[16] ); /* end-to-end id: 0xe2ee2e1d */
+			CHECK( 0xee, buf[17] );
+			CHECK( 0x2e, buf[18] );
+			CHECK( 0x1d, buf[19] );
+			
+			CHECK( 0x00, buf[20] ); /* First AVP (AVP Test - no vendor - f32) begin: code 73567 = 0x00011F5F */
+			CHECK( 0x01, buf[21] );
+			CHECK( 0x1F, buf[22] );
+			CHECK( 0x5F, buf[23] );
+			CHECK( 0x00, buf[24] ); /* flags: 0 */
+			CHECK( 0x00, buf[25] ); /* length: 12 = 0x00000c */
+			CHECK( 0x00, buf[26] );
+			CHECK( 0x0C, buf[27] );
+			CHECK( 0x40, buf[28] ); /* Value: 3.1415:  sign = '+' => most significant bit = 0 */
+			CHECK( 0x49, buf[29] ); /* 2 <= 3.1415 < 4 => exponent = 1 => biaised (on 8 bits) = (decimal) 128 = (binary) 100 0000 0 */
+			CHECK( 0x0e, buf[30] ); /* significand = (decimal) 1.57075 = (binary) 1.100 1001 0000 1110 0101 0110 */
+			CHECK( 0x56, buf[31] ); /* total => 0100 0000 0100 1001 0000 1110 0101 0110 = (hexa) 40 49 0e 56*/
+			
+			/* The other AVPs will be tested by successful parsing... */
+		}
+		
+		/* Now free the message, we keep only the buffer. */
+		CHECK( 0, fd_msg_free( msg ) );
+		
+	}
+	
+	/* Test the parsing of buffers and messages */
+	{
+		unsigned char * buf_cpy = NULL;
+		struct msg * msg;
+		
+		#define CPYBUF() {			\
+			buf_cpy = malloc(344);		\
+			CHECK( buf_cpy ? 1 : 0, 1);	\
+			memcpy(buf_cpy, buf, 344);	\
+		}
+		
+		/* Test the msg_parse_buffer function */
+		{
+			CPYBUF();
+			CHECK( EBADMSG, fd_msg_parse_buffer( &buf_cpy, 340, &msg) );
+			
+			CPYBUF();
+			CHECK( 0, fd_msg_parse_buffer( &buf_cpy, 344, &msg) );
+			#if 0
+			fd_log_debug("%s", fd_msg_dump_treeview(FD_DUMP_TEST_PARAMS, msg, fd_g_config->cnf_dict, 0, 1));
+			#endif
+			
+			/* reinit the msg */
+			CHECK( 0, fd_msg_free ( msg ) );
+				
+		}
+		
+		/* Test the fd_msg_search_avp function */
+		{
+			struct dict_object * avp_model;
+			struct avp 	   * found;
+			struct avp_hdr     * avpdata = NULL;
+			
+			/* Now find the ACR dictionary object */
+			CHECK( 0, fd_dict_search ( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "AVP Test - no vendor - f32", &avp_model, ENOENT ) );
+			
+			CPYBUF();
+			CHECK( 0, fd_msg_parse_buffer( &buf_cpy, 344, &msg) );
+			
+			/* Search this AVP instance in the msg */
+			CHECK( 0, fd_msg_search_avp( msg, avp_model, &found ) );
+			
+			/* Check the AVP value is 3.1415 */
+			CHECK( 0, fd_msg_avp_hdr ( found, &avpdata ) );
+			CHECK( 3.1415F, avpdata->avp_value->f32 );
+			
+			/* reinit the msg */
+			CHECK( 0, fd_msg_free ( msg ) );
+				
+		}
+		
+		/* Test the msg_parse_dict function */
+		{
+			/* Test with an unknown command code */
+			{
+				CPYBUF();
+				
+				/* Change the command-code */
+				buf_cpy[5] = 0x11;
+				CHECK( 0, fd_msg_parse_buffer( &buf_cpy, 344, &msg) );
+				CHECK( ENOTSUP, fd_msg_parse_dict( msg, fd_g_config->cnf_dict, NULL ) );
+				
+				/* reset */
+				CHECK( 0, fd_msg_free ( msg ) );
+			}
+			
+			/* Test with an unknown Mandatory AVP */
+			{
+				CPYBUF();
+				
+				buf_cpy[20] = 0x11;	/* New AVP code = 0x11011F5F, undefined */
+				buf_cpy[24] = 0x40; 	/* Add the 'M' flag */
+				
+				/* Check that we cannot support this message now */
+				CHECK( 0, fd_msg_parse_buffer( &buf_cpy, 344, &msg) );
+				CHECK( ENOTSUP, fd_msg_parse_dict( msg, fd_g_config->cnf_dict, NULL ) );
+				
+				/* reset */
+				CHECK( 0, fd_msg_free ( msg ) );
+			}
+			
+			/* Test with an unknown optional AVP */
+			{
+				CPYBUF();
+				
+				buf_cpy[20] = 0x11;	/* New AVP code = 0x11011F5F, undefined */
+				
+				/* Check that we can support this message now */
+				CHECK( 0, fd_msg_parse_buffer( &buf_cpy, 344, &msg) );
+				CHECK( 0, fd_msg_parse_dict( msg, fd_g_config->cnf_dict, NULL ) );
+				
+				#if 0
+				fd_log_debug("%s", fd_msg_dump_treeview(FD_DUMP_TEST_PARAMS, msg, fd_g_config->cnf_dict, 0, 1));
+				#endif
+				
+				/* reset */
+				CHECK( 0, fd_msg_free ( msg ) );
+			}
+			
+			/* Test with an invalid AVP (definition mismatch with the dictionary) */
+			{
+				CPYBUF();
+				
+				buf_cpy[21] = 0x02;	/* New AVP code = 0x00021F5F, f64 type in the dictionary */
+				
+				
+				/* Check that we cannot support this message now */
+				CHECK( 0, fd_msg_parse_buffer( &buf_cpy, 344, &msg) );
+				CHECK( EBADMSG, fd_msg_parse_dict( msg, fd_g_config->cnf_dict, NULL ) );
+				
+				/* reset */
+				CHECK( 0, fd_msg_free ( msg ) );
+			}
+			
+			/* Test with a type verifier */
+			{
+				struct fd_pei error_info;
+				CPYBUF();
+				buf_cpy[103] = 0x67;	/* Replaced AVP code = 0x00011F67, OS test2 type in the dictionary */
+				
+				/* Check that we cannot support this message now */
+				CHECK( 0, fd_msg_parse_buffer( &buf_cpy, 344, &msg) );
+				CHECK( EBADMSG, fd_msg_parse_dict( msg, fd_g_config->cnf_dict, NULL ) );
+				
+				/* reset */
+				CHECK( 0, fd_msg_free ( msg ) );
+
+				CPYBUF();
+				buf_cpy[103] = 0x67;	/* Replaced AVP code = 0x00011F67, OS test2 type in the dictionary */
+				
+				/* Check error reporting works */
+				CHECK( 0, fd_msg_parse_buffer( &buf_cpy, 344, &msg) );
+				CHECK( EBADMSG, fd_msg_parse_dict( msg, fd_g_config->cnf_dict, &error_info ) );
+				
+				#if 1
+				fd_log_debug("Error reported: %s\n in AVP: %s", error_info.pei_message, fd_msg_dump_treeview(FD_DUMP_TEST_PARAMS, error_info.pei_avp, fd_g_config->cnf_dict, 0, 1));
+				#endif
+				
+				/* reset */
+				CHECK( 0, fd_msg_free ( msg ) );
+				
+				CPYBUF();
+				buf_cpy[103] = 0x67;	/* Replaced AVP code = 0x00011F67, OS test2 type in the dictionary */
+				buf_cpy[130] = '@';
+				buf_cpy[140] = '.';     /* now we comply to the constraints */
+				
+				/* Check that we cannot support this message now */
+				CHECK( 0, fd_msg_parse_buffer( &buf_cpy, 344, &msg) );
+				CHECK( 0, fd_msg_parse_dict( msg, fd_g_config->cnf_dict, NULL ) );
+				
+				/* reset */
+				CHECK( 0, fd_msg_free ( msg ) );
+				
+				
+			}
+			
+			{
+				unsigned char * buftmp = NULL;
+				struct msg * error;
+				/* Check the parse or error works as expected */
+				CPYBUF();
+				
+				buf_cpy[21] = 0x02;	/* New AVP code = 0x00021F5F, f64 type in the dictionary */
+				
+				/* Check that we cannot support this message now */
+				CHECK( 0, fd_msg_init() );
+				CHECK( 0, fd_msg_parse_buffer( &buf_cpy, 344, &msg) );
+				CHECK( EBADMSG, fd_msg_parse_or_error( &msg, &error ) );
+				CHECK( NULL, msg );
+				msg = error;
+				
+				CHECK( 0, fd_msg_bufferize( msg, &buftmp, NULL ) );				
+				
+				fd_log_debug("%s", fd_msg_dump_treeview(FD_DUMP_TEST_PARAMS, msg, fd_g_config->cnf_dict, 0, 1));
+				
+				TODO("Check the Failed-AVP is as expected");
+				
+				
+				/* reset */
+				CHECK( 0, fd_msg_free ( msg ) );
+				free(buftmp);
+			}
+			
+			
+			CHECK( 0, fd_msg_parse_buffer( &buf, 344, &msg) );
+			CHECK( 0, fd_msg_parse_dict( msg, fd_g_config->cnf_dict, NULL ) );
+			#if 0
+			fd_log_debug("%s", fd_msg_dump_treeview(FD_DUMP_TEST_PARAMS, msg, fd_g_config->cnf_dict, 0, 1));
+			#endif
+		}
+		
+		/* Now test the msg_parse_rule function */
+		{
+			struct fd_pei pei;
+			
+			CHECK( 0, fd_msg_parse_rules( msg, fd_g_config->cnf_dict, &pei ) );
+			
+			/* Use the "AVP Test - rules" AVP to test the rules */
+			{
+				struct avp * tavp = NULL;
+				struct avp * tempavp = NULL;
+				struct avp * childavp = NULL;
+				
+				ADD_AVP( msg, MSG_BRW_LAST_CHILD, tavp, 73565, "AVP Test - rules" );
+				
+				/* Create a conforming message first */
+				ADD_AVP( tavp, MSG_BRW_LAST_CHILD, childavp,     0, "AVP Test - no vendor - f32" );
+				ADD_AVP( tavp, MSG_BRW_LAST_CHILD, childavp, 73565, "AVP Test - i64" );
+				ADD_AVP( tavp, MSG_BRW_LAST_CHILD, childavp, 73565, "AVP Test - enumi32" );
+				ADD_AVP( tavp, MSG_BRW_LAST_CHILD, childavp, 73565, "AVP Test - os" );
+				ADD_AVP( tavp, MSG_BRW_LAST_CHILD, childavp, 73565, "AVP Test - os" );
+				ADD_AVP( tavp, MSG_BRW_LAST_CHILD, childavp, 73565, "AVP Test - grouped" );
+				
+				/* Check the message is still conform */
+				CHECK( 0, fd_msg_parse_rules( msg, fd_g_config->cnf_dict, &pei ) );
+				
+				/* The first avp is optional in fixed position, so remove it and check the message is still OK */
+				CHECK( 0, fd_msg_browse ( tavp, MSG_BRW_FIRST_CHILD, &childavp, NULL) );
+				CHECK( 0, fd_msg_free ( childavp ) );
+				CHECK( 0, fd_msg_parse_rules( msg, fd_g_config->cnf_dict, &pei ) );
+				ADD_AVP( tavp, MSG_BRW_FIRST_CHILD, childavp,     0, "AVP Test - no vendor - f32" );
+				
+				
+				/* Now break some rules and check it is detected */
+				#define CHECK_CONFLICT( _msg, _error, _conflictavp_name, _conflictavp_vnd )		{	\
+					struct fd_pei _pei;									\
+					CHECK( EBADMSG,  fd_msg_parse_rules( _msg, fd_g_config->cnf_dict, &_pei ) );		\
+					if (_error) {										\
+						CHECK( 0, strcmp( _error, _pei.pei_errcode ) );					\
+					}											\
+					if ((_conflictavp_name) == NULL) {							\
+						CHECK( NULL, _pei.pei_avp);							\
+					} else {										\
+						struct dict_avp_request _req = { (_conflictavp_vnd), 0, (_conflictavp_name) };	\
+						struct dict_object *    _avp;							\
+						struct dict_object * _conflict;							\
+						CHECK( 1, (_pei.pei_avp) ? 1 : 0 );						\
+						CHECK( 0, fd_msg_model( _pei.pei_avp, &_conflict ) );				\
+						CHECK( 0, fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME_AND_VENDOR, &_req, &_avp, ENOENT));	\
+						CHECK( _avp, _conflict );							\
+					}											\
+				}
+
+			/* ABNF : 
+				< no vendor - f32 >
+				< i64 >
+				< enumi32 >
+			    2*3 { os }
+			     *1 [ enumos ]
+				< grouped >
+						*/
+				{
+					/* Test the FIXED_HEAD rules positions: add another AVP before the third */
+					CHECK( 0, fd_msg_browse ( tavp, MSG_BRW_FIRST_CHILD, &tempavp, NULL) ); /* tempavp is the novendor avp */
+					CHECK( 0, fd_msg_browse ( tempavp, MSG_BRW_NEXT, &tempavp, NULL) );     /* tempavp is the i64 avp */
+					ADD_AVP( tempavp, MSG_BRW_NEXT, childavp, 73565, "AVP Test - os" );
+					
+					CHECK_CONFLICT( msg, "DIAMETER_MISSING_AVP", "AVP Test - enumi32", 73565 );
+					
+					/* Now remove this AVP */
+					CHECK( 0, fd_msg_free ( childavp ) );
+				}
+				{
+					/* Remove the third AVP, same rule must conflict */
+					CHECK( 0, fd_msg_browse ( tempavp, MSG_BRW_NEXT, &childavp, NULL) );     /* childavp is the enumi32 avp */
+					CHECK( 0, fd_msg_free ( childavp ) );
+					
+					CHECK_CONFLICT( msg, "DIAMETER_MISSING_AVP", "AVP Test - enumi32", 73565 );
+					
+					/* Add the AVP back */
+					ADD_AVP( tempavp, MSG_BRW_NEXT, childavp, 73565, "AVP Test - enumi32" );
+				}
+				
+				{
+					/* Test the minimum value in the REQUIRED rule: delete one of the os AVPs */
+					CHECK( 0, fd_msg_browse ( childavp, MSG_BRW_NEXT, &tempavp, NULL) );     /* tempavp is the os avp */
+					CHECK( 0, fd_msg_free ( tempavp ) );
+					
+					CHECK_CONFLICT( msg, "DIAMETER_MISSING_AVP", "AVP Test - os", 73565 ); /* The rule requires at least 2 AVP, we have only 1 */
+					
+					/* Now add this AVP */
+					ADD_AVP( childavp, MSG_BRW_NEXT, tempavp, 73565, "AVP Test - os" );
+				}
+				{
+					/* Test the maximum value in the REQUIRED rule: add more of the os AVPs */
+					ADD_AVP( childavp, MSG_BRW_NEXT, tempavp, 73565, "AVP Test - os" );
+					ADD_AVP( childavp, MSG_BRW_NEXT, tempavp, 73565, "AVP Test - os" );
+					
+					CHECK_CONFLICT( msg, "DIAMETER_AVP_OCCURS_TOO_MANY_TIMES", "AVP Test - os", 73565 ); /* The rule requires at most 3 AVP, we have 4 */
+					
+					/* Now delete these AVP */
+					CHECK( 0, fd_msg_free ( tempavp ) );
+					CHECK( 0, fd_msg_browse ( childavp, MSG_BRW_NEXT, &tempavp, NULL) );
+					CHECK( 0, fd_msg_free ( tempavp ) );
+				}
+				
+				{
+					/* Test the maximum value in the OPTIONAL rule: add 2 enumos AVPs */
+					ADD_AVP( childavp, MSG_BRW_NEXT, tempavp, 73565, "AVP Test - enumos" );
+					
+					/* The message is still conform */
+					CHECK( 0, fd_msg_parse_rules( msg, fd_g_config->cnf_dict, &pei ) );
+					
+					/* Now break the rule */
+					ADD_AVP( childavp, MSG_BRW_NEXT, tempavp, 73565, "AVP Test - enumos" );
+					
+					CHECK_CONFLICT( msg, "DIAMETER_AVP_OCCURS_TOO_MANY_TIMES", "AVP Test - enumos", 73565 );
+					
+					/* Now delete this AVP */
+					CHECK( 0, fd_msg_free ( tempavp ) );
+				}
+				
+				{
+					/* Test the RULE_FIXED_TAIL rules positions: add another AVP at the end */
+					ADD_AVP( tavp, MSG_BRW_LAST_CHILD, childavp, 73565, "AVP Test - os" );
+					
+					CHECK_CONFLICT( msg, "DIAMETER_MISSING_AVP", "AVP Test - grouped", 73565 );
+					
+					/* Now remove this AVP */
+					CHECK( 0, fd_msg_free ( childavp ) );
+				}
+			}
+		}
+		
+		/* Test the fd_msg_new_answer_from_req function */
+		{
+			struct dict_object * cmd_model = NULL;
+			struct msg         * msg = NULL;
+			struct avp * pi1, *pi2, *avp;
+			char * host1="host1", * host2="host2";
+			union avp_value      value;
+			struct msg_hdr * msgdata = NULL;
+			
+			CHECK( 0, fd_dict_search ( fd_g_config->cnf_dict, DICT_COMMAND, CMD_BY_NAME, "Test-Command-Request", &cmd_model, ENOENT ) );
+			
+			/* Test default behavior without flags */
+			{ 
+				/* Create a message with some AVPs inside */
+				CHECK( 0, fd_msg_new ( cmd_model, 0, &msg ) );
+				CHECK( 0, fd_msg_hdr ( msg, &msgdata ) );
+				
+				/* Add a session id */
+				CHECK( 0, fd_msg_new_session( msg, (os0_t)"testmsg", strlen("testmsg") ) );
+				
+				/* Create two instances of Proxy-Info */
+				ADD_AVP( msg, MSG_BRW_LAST_CHILD, pi1, 0, "Proxy-Info");
+				ADD_AVP( msg, MSG_BRW_LAST_CHILD, pi2, 0, "Proxy-Info");
+
+				ADD_AVP( pi1, MSG_BRW_LAST_CHILD, avp, 0, "Proxy-State");
+				value.os.data = (os0_t)"ps_pi1";
+				value.os.len = strlen((char *)value.os.data);
+				CHECK( 0, fd_msg_avp_setvalue ( avp, &value ) );
+				
+				ADD_AVP( pi2, MSG_BRW_LAST_CHILD, avp, 0, "Proxy-State");
+				value.os.data = (os0_t)"pi2_state";
+				value.os.len = strlen((char *)value.os.data);
+				CHECK( 0, fd_msg_avp_setvalue ( avp, &value ) );
+				
+				ADD_AVP( pi1, MSG_BRW_FIRST_CHILD, avp, 0, "Proxy-Host");
+				value.os.data = (os0_t)host1;
+				value.os.len = strlen(host1);
+				CHECK( 0, fd_msg_avp_setvalue ( avp, &value ) );
+				
+				ADD_AVP( pi2, MSG_BRW_LAST_CHILD, avp, 0, "Proxy-Host");
+				value.os.data = (os0_t)host2;
+				value.os.len = strlen(host2);
+				CHECK( 0, fd_msg_avp_setvalue ( avp, &value ) );
+				
+				ADD_AVP( pi2, MSG_BRW_LAST_CHILD, avp, 73565, "AVP Test - i64");
+				value.i64 = 0x123456789abcdeLL;
+				CHECK( 0, fd_msg_avp_setvalue ( avp, &value ) );
+				
+				
+				/* Now call the fd_msg_new_answer_from_req function */
+				CHECK( 0, fd_msg_new_answer_from_req ( fd_g_config->cnf_dict, &msg, 0 ) );
+				
+				/* Check there is a Session-Id AVP */
+				{
+					struct session * sess;
+					int new;
+					CHECK( 0, fd_msg_sess_get(fd_g_config->cnf_dict, msg, &sess, &new) );
+					CHECK( 1, sess == NULL ? 0 : 1 );
+					CHECK( 0, new ? 1 : 0 );
+				}
+				
+				/* Check there are two Proxy-Info with the two hosts */
+				{
+					int got_h1 = 0, got_h2=0;
+					CHECK( 0, fd_msg_browse ( msg, MSG_BRW_FIRST_CHILD, &avp, NULL) );
+					while(avp) {
+						struct avp_hdr * avpdata = NULL;
+						CHECK( 0, fd_msg_avp_hdr ( avp, &avpdata ) );
+						if (avpdata->avp_code == AC_PROXY_INFO) {
+							struct avp * iavp;
+							CHECK( 0, fd_msg_browse ( avp, MSG_BRW_FIRST_CHILD, &iavp, NULL) );
+							while(iavp) {
+								struct avp_hdr * iavpdata = NULL;
+								CHECK( 0, fd_msg_avp_hdr ( iavp, &iavpdata ) );
+								if (iavpdata->avp_code == AC_PROXY_HOST) {
+									if (!memcmp(host1, iavpdata->avp_value->os.data, strlen(host1)))
+										got_h1++;
+									if (!memcmp(host2, iavpdata->avp_value->os.data, strlen(host2)))
+										got_h2++;
+								}
+								CHECK( 0, fd_msg_browse ( iavp, MSG_BRW_NEXT, &iavp, NULL) );
+							}
+						}
+						
+						CHECK( 0, fd_msg_browse ( avp, MSG_BRW_NEXT, &avp, NULL) );
+					}
+					
+					CHECK(1, got_h1);
+					CHECK(1, got_h2);
+				}
+				
+				/* Now test the behavior of fd_msg_rescode_set with a grouped AVP */
+				CHECK( 0, fd_msg_rescode_set(msg, "DIAMETER_AVP_OCCURS_TOO_MANY_TIMES", NULL, pi1, 1) );
+				
+				fd_log_debug("%s", fd_msg_dump_treeview(FD_DUMP_TEST_PARAMS, msg, fd_g_config->cnf_dict, 0, 1));
+				
+				TODO("Check the Failed-AVP is as expected");
+			}
+			
+		}
+	}
+	
+	/* Test the msg_avp_value_interpret and msg_avp_value_encode functions. use the Address type and Host-IP-Address AVPs */
+	{
+		struct dict_object * cer_model = NULL;
+		struct msg * cer = NULL;
+		
+		struct dict_object * hia_model = NULL;
+		struct avp *avp4, *avp6;
+		#define TEST_IP4 "192.168.100.101"
+		char buf4[INET_ADDRSTRLEN];
+		#define	TEST_IP6 "1111:2222:3333:4444:1234:5678:9abc:def0"
+		char buf6[INET6_ADDRSTRLEN];
+		
+		struct sockaddr_storage ss;
+		struct sockaddr_in  sin,  *psin;
+		struct sockaddr_in6 sin6, *psin6;
+		
+		/* Find the CER dictionary object */
+		CHECK( 0, fd_dict_search ( fd_g_config->cnf_dict, DICT_COMMAND, CMD_BY_NAME, "Capabilities-Exchange-Request", &cer_model, ENOENT ) );
+
+		/* Now find the Host-IP-Address dictionary object */
+		CHECK( 0, fd_dict_search ( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Host-IP-Address", &hia_model, ENOENT ) );
+
+		/* Create the msg instance */
+		CHECK( 0, fd_msg_new ( cer_model, 0, &cer ) );
+
+		/* Create the avp instances */
+		CHECK( 0, fd_msg_avp_new ( hia_model, 0, &avp4 ) );
+		CHECK( 0, fd_msg_avp_new ( hia_model, 0, &avp6 ) );
+		
+		/* Set the value of the IP avp */
+		sin.sin_family = AF_INET;
+		CHECK( 1, inet_pton( AF_INET, TEST_IP4, &sin.sin_addr.s_addr ) );
+		CHECK( 0, fd_msg_avp_value_encode ( &sin, avp4 ) );
+		
+		/* Set the value of the IP6 avp */
+		sin6.sin6_family = AF_INET6;
+		CHECK( 1, inet_pton( AF_INET6, TEST_IP6, &sin6.sin6_addr.s6_addr ) );
+		CHECK( 0, fd_msg_avp_value_encode ( &sin6, avp6 ) );
+		
+		/* Add these AVPs in the message */
+		CHECK( 0, fd_msg_avp_add( cer, MSG_BRW_LAST_CHILD, avp4) );
+		CHECK( 0, fd_msg_avp_add( cer, MSG_BRW_LAST_CHILD, avp6) );
+		
+		/* Create the buffer for this message */
+		CHECK( 0, fd_msg_bufferize( cer, &buf, NULL ) );
+		
+		/* Now free the message, we keep only the buffer. */
+		CHECK( 0, fd_msg_free( cer ) );
+		
+		/* Check the content of the buffer is correct (skip command header) */
+		CHECK( 0x00, buf[20] ); /* First AVP (IP4) begins: code 257 = 0x00000101 */
+		CHECK( 0x00, buf[21] );
+		CHECK( 0x01, buf[22] );
+		CHECK( 0x01, buf[23] );
+		CHECK( 0x40, buf[24] ); /* flags: M */
+		CHECK( 0x00, buf[25] ); /* length: 8+6 = 0x00000e */
+		CHECK( 0x00, buf[26] );
+		CHECK( 0x0E, buf[27] );
+		CHECK( 0x00, buf[28] ); /* Value: AddressType 1 */
+		CHECK( 0x01, buf[29] ); 
+		CHECK(  192, buf[30] ); /* 192.168.100.101 */
+		CHECK(  168, buf[31] ); 
+		CHECK(  100, buf[32] ); 
+		CHECK(  101, buf[33] );
+		
+		CHECK( 0x00, buf[34] ); /* Padding */
+		CHECK( 0x00, buf[35] );
+		
+		CHECK( 0x00, buf[36] ); /* Second AVP (IP6) begins: code 257 = 0x00000101 */
+		CHECK( 0x00, buf[37] );
+		CHECK( 0x01, buf[38] );
+		CHECK( 0x01, buf[39] );
+		CHECK( 0x40, buf[40] ); /* flags: M */
+		CHECK( 0x00, buf[41] ); /* length: 8+18 = 0x00001a */
+		CHECK( 0x00, buf[42] );
+		CHECK( 0x1A, buf[43] );
+		CHECK( 0x00, buf[44] ); /* Value: AddressType 2 */
+		CHECK( 0x02, buf[45] ); 
+		CHECK( 0x11, buf[46] ); /* 1111:2222:3333:4444:1234:5678:9abc:def0 */
+		CHECK( 0x11, buf[47] ); 
+		CHECK( 0x22, buf[48] ); 
+		CHECK( 0x22, buf[49] );
+		CHECK( 0x33, buf[50] );
+		CHECK( 0x33, buf[51] );
+		CHECK( 0x44, buf[52] );
+		CHECK( 0x44, buf[53] );
+		CHECK( 0x12, buf[54] );
+		CHECK( 0x34, buf[55] );
+		CHECK( 0x56, buf[56] );
+		CHECK( 0x78, buf[57] );
+		CHECK( 0x9a, buf[58] );
+		CHECK( 0xbc, buf[59] );
+		CHECK( 0xde, buf[60] );
+		CHECK( 0xf0, buf[61] );
+		
+		/* Ok, now let's recreate the message */
+		CHECK( 0, fd_msg_parse_buffer( &buf, 64, &cer) );
+		CHECK( 0, fd_msg_parse_dict( cer, fd_g_config->cnf_dict, NULL ) );
+		
+		/* Get the pointers to the first and last AVP */
+		CHECK( 0, fd_msg_browse( cer, MSG_BRW_FIRST_CHILD, &avp4, NULL) );
+		CHECK( 0, fd_msg_browse( cer, MSG_BRW_LAST_CHILD,  &avp6, NULL) );
+		
+		/* Try and interpret the data in the AVPs */
+		CHECK( 0, fd_msg_avp_value_interpret ( avp4, &ss ) );
+		psin = (struct sockaddr_in *)&ss;
+		CHECK( AF_INET, psin->sin_family );
+		CHECK( 0, (inet_ntop( AF_INET, &psin->sin_addr.s_addr, buf4, sizeof(buf4) ) == NULL) ? errno : 0 );
+		CHECK( 0, strcmp( buf4, TEST_IP4 ) );
+		
+		CHECK( 0, fd_msg_avp_value_interpret ( avp6, &ss ) );
+		psin6 = (struct sockaddr_in6 *)&ss;
+		CHECK( AF_INET6, psin6->sin6_family );
+		CHECK( 0, (inet_ntop( AF_INET6, &psin6->sin6_addr.s6_addr, buf6, sizeof(buf6) ) == NULL) ? errno : 0 );
+		CHECK( 0, strcasecmp( buf6, TEST_IP6 ) );
+		
+		/* Ok, it's done */
+		CHECK( 0, fd_msg_free( cer ) );
+	}
+	
+	/* Check proper encoding / decoding for all basic types of AVP */
+	{
+		{
+			struct dict_avp_data avp_data = { 91001, 0, "AVP Test 2 - os", 0, 0, AVP_TYPE_OCTETSTRING };
+			CHECK( 0, fd_dict_new ( fd_g_config->cnf_dict, DICT_AVP, &avp_data , NULL, NULL ) );
+		}
+		{
+			struct dict_avp_data avp_data = { 91002, 0, "AVP Test 2 - i32", 0, 0, AVP_TYPE_INTEGER32 };
+			CHECK( 0, fd_dict_new ( fd_g_config->cnf_dict, DICT_AVP, &avp_data , NULL, NULL ) );
+		}
+		{
+			struct dict_avp_data avp_data = { 91003, 0, "AVP Test 2 - i64", 0, 0, AVP_TYPE_INTEGER64 };
+			CHECK( 0, fd_dict_new ( fd_g_config->cnf_dict, DICT_AVP, &avp_data , NULL, NULL ) );
+		}
+		{
+			struct dict_avp_data avp_data = { 91004, 0, "AVP Test 2 - u32", 0, 0, AVP_TYPE_UNSIGNED32 };
+			CHECK( 0, fd_dict_new ( fd_g_config->cnf_dict, DICT_AVP, &avp_data , NULL, NULL ) );
+		}
+		{
+			struct dict_avp_data avp_data = { 91005, 0, "AVP Test 2 - u64", 0, 0, AVP_TYPE_UNSIGNED64 };
+			CHECK( 0, fd_dict_new ( fd_g_config->cnf_dict, DICT_AVP, &avp_data , NULL, NULL ) );
+		}
+		{
+			struct dict_avp_data avp_data = { 91006, 0, "AVP Test 2 - f32", 0, 0, AVP_TYPE_FLOAT32 };
+			CHECK( 0, fd_dict_new ( fd_g_config->cnf_dict, DICT_AVP, &avp_data , NULL, NULL ) );
+		}
+		{
+			struct dict_avp_data avp_data = { 91007, 0, "AVP Test 2 - f64", 0, 0, AVP_TYPE_FLOAT64 };
+			CHECK( 0, fd_dict_new ( fd_g_config->cnf_dict, DICT_AVP, &avp_data , NULL, NULL ) );
+		}
+		
+		{
+			struct dict_object * cmd_model = NULL;
+			struct msg         * msg = NULL;
+			struct avp         * avp = NULL;
+			union avp_value      value;
+			struct avp         * avpi = NULL;
+			struct avp_hdr     * avpdata = NULL;
+			struct msg_hdr     * msgdata = NULL;
+
+			CHECK( 0, fd_dict_search ( fd_g_config->cnf_dict, DICT_COMMAND, CMD_BY_NAME, "Test-Command-Request", &cmd_model, ENOENT ) );
+
+			/* Create a message */
+			CHECK( 0, fd_msg_new ( cmd_model, 0, &msg ) );
+			CHECK( 0, fd_msg_hdr ( msg, &msgdata ) );
+			
+			ADD_AVP( msg, MSG_BRW_LAST_CHILD, avpi, 0, 	"AVP Test 2 - os" );
+			value.os.data = (unsigned char *) "waaad";
+			value.os.len = 6;
+			CHECK( 0, fd_msg_avp_setvalue ( avpi, &value ) );
+
+			ADD_AVP( msg, MSG_BRW_LAST_CHILD, avpi, 0,	"AVP Test 2 - i32" );
+			value.i32 = 0x123456;
+			CHECK( 0, fd_msg_avp_setvalue ( avpi, &value ) );
+
+			ADD_AVP( msg, MSG_BRW_LAST_CHILD, avpi, 0,	"AVP Test 2 - i32" );
+			value.i32 = -0x123456;
+			CHECK( 0, fd_msg_avp_setvalue ( avpi, &value ) );
+
+			ADD_AVP( msg, MSG_BRW_LAST_CHILD, avpi, 0,	"AVP Test 2 - i64" );
+			value.i64 = 0x11223344556677LL;
+			CHECK( 0, fd_msg_avp_setvalue ( avpi, &value ) );
+
+			ADD_AVP( msg, MSG_BRW_LAST_CHILD, avpi, 0,	"AVP Test 2 - i64" );
+			value.i64 = -0x11223344556677LL;
+			CHECK( 0, fd_msg_avp_setvalue ( avpi, &value ) );
+
+			ADD_AVP( msg, MSG_BRW_LAST_CHILD, avpi, 0,	"AVP Test 2 - u32" );
+			value.u32 = 0xFEDCBA98;
+			CHECK( 0, fd_msg_avp_setvalue ( avpi, &value ) );
+
+			ADD_AVP( msg, MSG_BRW_LAST_CHILD, avpi, 0,	"AVP Test 2 - u64" );
+			value.u64 = 0x123456789abcdef0LL;
+			CHECK( 0, fd_msg_avp_setvalue ( avpi, &value ) );
+
+			ADD_AVP( msg, MSG_BRW_LAST_CHILD, avpi, 0,	"AVP Test 2 - f32" );
+			value.f32 = 2097153.0F;
+			CHECK( 0, fd_msg_avp_setvalue ( avpi, &value ) );
+
+			ADD_AVP( msg, MSG_BRW_LAST_CHILD, avpi, 0,	"AVP Test 2 - f64" );
+			value.f64 = -1099511627777LL;
+			CHECK( 0, fd_msg_avp_setvalue ( avpi, &value ) );
+			
+			/* Ok now bufferize */
+			CHECK( 0, fd_msg_bufferize( msg, &buf, NULL ) );
+			
+			/* Test the first bytes */
+			CHECK( 0x01, buf[0] ); /* Version */
+			CHECK( 0x00, buf[1] ); /* Length: 148 = 0x000094 */
+			CHECK( 0x00, buf[2] );
+			CHECK( 0x94, buf[3] );
+			CHECK( 0x80, buf[4] ); /* flags: only "R" is set. */
+			CHECK( 0x01, buf[5] ); /* Command code: 73573 = 0x011F65 */
+			CHECK( 0x1F, buf[6] );
+			CHECK( 0x65, buf[7] );
+			CHECK( 0x00, buf[8] ); /* App ID */
+			CHECK( 0x01, buf[9] ); 
+			CHECK( 0x1F, buf[10] );
+			CHECK( 0x5E, buf[11] );
+			CHECK( 0x00, buf[12] ); /* hop-by-hop id */
+			CHECK( 0x00, buf[13] );
+			CHECK( 0x00, buf[14] );
+			CHECK( 0x00, buf[15] );
+			CHECK( 0x00, buf[16] ); /* end-to-end id */
+			CHECK( 0x00, buf[17] );
+			CHECK( 0x00, buf[18] );
+			CHECK( 0x00, buf[19] );
+			
+			CHECK( 0x00, buf[20] ); /* First AVP (AVP Test 2 - os) begin: code 91001 = 0x00016379 */
+			CHECK( 0x01, buf[21] );
+			CHECK( 0x63, buf[22] );
+			CHECK( 0x79, buf[23] );
+			CHECK( 0x00, buf[24] ); /* flags: 0 */
+			CHECK( 0x00, buf[25] ); /* length: 14 = 0x00000e */
+			CHECK( 0x00, buf[26] );
+			CHECK( 0x0e, buf[27] );
+			
+			CHECK( 0x77, buf[28] ); /* "waaad\0" + padding */
+			CHECK( 0x61, buf[29] );
+			CHECK( 0x61, buf[30] );
+			CHECK( 0x61, buf[31] );
+			CHECK( 0x64, buf[32] );
+			CHECK( 0x00, buf[33] );
+			CHECK( 0x00, buf[34] );
+			CHECK( 0x00, buf[35] );
+			
+			/* 36 ~ 43 : 2nd AVP header (size at last octet) */
+			CHECK( 0x0c, buf[43] );
+			CHECK( 0x00, buf[44] ); /* 0x123456 stored in integer32 in network byte order */
+			CHECK( 0x12, buf[45] );
+			CHECK( 0x34, buf[46] );
+			CHECK( 0x56, buf[47] );
+			
+			/* 48 ~ 55 : next AVP header */
+			CHECK( 0xff, buf[56] ); /* -0x123456 stored in integer32 in network byte order. */ 
+			CHECK( 0xed, buf[57] ); /* We assume that two's complement is the correct representation, although it's not clearly specified. */
+			CHECK( 0xcb, buf[58] ); /* 00 12 34 56 inversed => FF ED CB A9 */
+			CHECK( 0xaa, buf[59] ); /* then "+1" => FF ED CB AA */
+			
+			/* 60 ~ 67 : next header */
+			CHECK( 0x10, buf[67] ); /* (the size) */
+			CHECK( 0x00, buf[68] ); /* 0x11223344556677 in network byte order */
+			CHECK( 0x11, buf[69] );
+			CHECK( 0x22, buf[70] );
+			CHECK( 0x33, buf[71] );
+			CHECK( 0x44, buf[72] );
+			CHECK( 0x55, buf[73] );
+			CHECK( 0x66, buf[74] );
+			CHECK( 0x77, buf[75] );
+			
+			/* 76 ~ 83 : next header */
+			CHECK( 0xFF, buf[84] ); /*  - 0x11223344556677 (in two's complement) */
+			CHECK( 0xEE, buf[85] ); /* gives FF EE DD CC BB AA 99 89 */
+			CHECK( 0xDD, buf[86] );
+			CHECK( 0xCC, buf[87] );
+			CHECK( 0xBB, buf[88] );
+			CHECK( 0xAA, buf[89] );
+			CHECK( 0x99, buf[90] );
+			CHECK( 0x89, buf[91] );
+			
+			/* 92 ~ 99 : next header */
+			CHECK( 0x0c, buf[99] ); /* (the size) */
+			CHECK( 0xFE, buf[100]); /* 0xFEDCBA98 in network byte order */
+			CHECK( 0xDC, buf[101]);
+			CHECK( 0xBA, buf[102]);
+			CHECK( 0x98, buf[103]);
+			
+			/* 104 ~ 111 : next header */
+			CHECK( 0x10, buf[111] ); /* (the size) */
+			CHECK( 0x12, buf[112]); /* 0x123456789abcdef0LL in network byte order */
+			CHECK( 0x34, buf[113]);
+			CHECK( 0x56, buf[114]);
+			CHECK( 0x78, buf[115]);
+			CHECK( 0x9a, buf[116]);
+			CHECK( 0xbc, buf[117]);
+			CHECK( 0xde, buf[118]);
+			CHECK( 0xf0, buf[119]);
+			
+			/* 120 ~ 127 : next header */
+			CHECK( 0x0c, buf[127] ); /* (the size) */
+			CHECK( 0x4a, buf[128]); /* http://en.wikipedia.org/wiki/IEEE_754-1985 to get descvription of the format */
+			CHECK( 0x00, buf[129]); /* v = 2097153 = 2^21 + 2 ^ 0; sign : "+", 2^21 <= v < 2^22 => exponent = 21; biaised on 8 bits => 21 + 127 => 100 1010 0 */
+			CHECK( 0x00, buf[130]); /* v = (+1) * (1 ^ 21) * ( 1 + 2^-21 ) => significand 000 0000 0000 0000 0000 0100 */
+			CHECK( 0x04, buf[131]); /* result: 4a 00 00 04 */
+			
+			/* 132 ~ 139 : next header */
+			CHECK( 0x10, buf[139] ); /* (the size) */
+			CHECK( 0xc2, buf[140]); /* -1099511627777L ( 2^40 + 1 ) in network byte order */
+			CHECK( 0x70, buf[141]); /* sign: - => most significant bit = 1 */
+			CHECK( 0x00, buf[142]); /* 2^40 <= v < 2^41 => biaised exponent on 11 bits: 1023 + 40: 100 0010  0111 */
+			CHECK( 0x00, buf[143]); /* significand: 1 + 2^-40 => 0000  0000 0000  0000 0000  0000 0000  0000 0000  0001 0000  0000 0000 */
+			CHECK( 0x00, buf[144]); /* result: c2 70 00 00 00 00 10 00 */
+			CHECK( 0x00, buf[145]);
+			CHECK( 0x10, buf[146]);
+			CHECK( 0x00, buf[147]);
+			
+			
+			
+			/* Okay, now delete the message and parse the buffer, then check we obtain the same values back */
+			#if 0
+			fd_log_debug("%s", fd_msg_dump_treeview(FD_DUMP_TEST_PARAMS, msg, fd_g_config->cnf_dict, 0, 1));
+			#endif
+			CHECK( 0, fd_msg_free( msg ) );
+			
+			CHECK( 0, fd_msg_parse_buffer( &buf, 148, &msg) );
+			CHECK( 0, fd_msg_parse_dict( msg, fd_g_config->cnf_dict, NULL ) );
+			#if 0
+			fd_log_debug("%s", fd_msg_dump_treeview(FD_DUMP_TEST_PARAMS, msg, fd_g_config->cnf_dict, 0, 1));
+			#endif
+			
+			CHECK( 0, fd_msg_browse ( msg, MSG_BRW_FIRST_CHILD, &avp, NULL) );
+			CHECK( 0, fd_msg_avp_hdr ( avp, &avpdata ) );
+			CHECK( 6, avpdata->avp_value->os.len );
+			CHECK( 'w', (char)(avpdata->avp_value->os.data[0]) );
+			CHECK( 'a', (char)(avpdata->avp_value->os.data[1]) );
+			CHECK( 'd', (char)(avpdata->avp_value->os.data[4]) );
+			CHECK( '\0', (char)(avpdata->avp_value->os.data[5]) );
+			
+			CHECK( 0, fd_msg_browse ( avp, MSG_BRW_NEXT, &avp, NULL) );
+			CHECK( 0, fd_msg_avp_hdr ( avp, &avpdata ) );
+			CHECK( 0x123456, avpdata->avp_value->i32 );
+			
+			CHECK( 0, fd_msg_browse ( avp, MSG_BRW_NEXT, &avp, NULL) );
+			CHECK( 0, fd_msg_avp_hdr ( avp, &avpdata ) );
+			CHECK( -0x123456, avpdata->avp_value->i32 );
+			
+			CHECK( 0, fd_msg_browse ( avp, MSG_BRW_NEXT, &avp, NULL) );
+			CHECK( 0, fd_msg_avp_hdr ( avp, &avpdata ) );
+			CHECK( 0x11223344556677LL, avpdata->avp_value->i64 );
+			
+			CHECK( 0, fd_msg_browse ( avp, MSG_BRW_NEXT, &avp, NULL) );
+			CHECK( 0, fd_msg_avp_hdr ( avp, &avpdata ) );
+			CHECK( -0x11223344556677LL, avpdata->avp_value->i64 );
+			
+			CHECK( 0, fd_msg_browse ( avp, MSG_BRW_NEXT, &avp, NULL) );
+			CHECK( 0, fd_msg_avp_hdr ( avp, &avpdata ) );
+			CHECK( 0xFEDCBA98, avpdata->avp_value->u32 );
+			
+			CHECK( 0, fd_msg_browse ( avp, MSG_BRW_NEXT, &avp, NULL) );
+			CHECK( 0, fd_msg_avp_hdr ( avp, &avpdata ) );
+			CHECK( 0x123456789abcdef0LL, avpdata->avp_value->u64 );
+			
+			CHECK( 0, fd_msg_browse ( avp, MSG_BRW_NEXT, &avp, NULL) );
+			CHECK( 0, fd_msg_avp_hdr ( avp, &avpdata ) );
+			CHECK( 2097153.0F, avpdata->avp_value->f32 );
+			
+			CHECK( 0, fd_msg_browse ( avp, MSG_BRW_NEXT, &avp, NULL) );
+			CHECK( 0, fd_msg_avp_hdr ( avp, &avpdata ) );
+			CHECK( -1099511627777LL, avpdata->avp_value->f64 );
+			
+			CHECK( 0, fd_msg_free( msg ) );
+		}
+	}
+	
+	/* That's all for the tests yet */
+	PASSTEST();
+} 
+	
diff --git a/tests/testmesg_stress.c b/tests/testmesg_stress.c
new file mode 100644
index 0000000..310a9d2
--- /dev/null
+++ b/tests/testmesg_stress.c
@@ -0,0 +1,689 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@freediameter.net>							 *
+*													 *
+* Copyright (c) 2013, 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"
+#include <dirent.h>
+#include <libgen.h>
+#include <dlfcn.h>
+
+#ifndef BUILD_DIR
+#error "Missing BUILD_DIR information"
+#endif /* BUILD_DIR */
+
+
+/* The number of times each operation is repeated to measure the average operation time */
+#define DEFAULT_NUMBER_OF_SAMPLES	100000
+
+static void display_result(int nr, struct timespec * start, struct timespec * end, char * fct, char * type, char *op)
+{
+	long double dur = (long double)end->tv_sec + (long double)end->tv_nsec/1000000000;
+	dur -= (long double)start->tv_sec + (long double)start->tv_nsec/1000000000;
+	long double thrp = (long double)nr / dur;
+	printf("%-19s: %d %-8s %-7s in %.6LFs (%.1LFmsg/s)\n", fct, nr, type, op, dur, thrp);
+}
+
+struct ext_info {
+	struct fd_list	chain;		/* link in the list */
+	void 		*handler;	/* object returned by dlopen() */
+	int 		(*init_cb)(int, int, char *);
+	char		*ext_name;	/* points to the extension name, either inside depends, or basename(filename) */
+	int		free_ext_name;	/* must be freed if it was malloc'd */
+	const char 	**depends;	/* names of the other extensions this one depends on (if provided) */
+};
+	
+static void load_all_extensions(char * prefix)
+{
+	DIR *dir;
+	struct dirent *dp;
+	char fullname[512];
+	int pathlen;
+	struct fd_list all_extensions = FD_LIST_INITIALIZER(all_extensions);
+	struct fd_list ext_with_depends = FD_LIST_INITIALIZER(ext_with_depends);
+
+	/* Find all extensions which have been compiled along the test */
+	LOG_D("Loading %s*.fdx from: '%s'", BUILD_DIR "/extensions", prefix ?: "");
+	CHECK( 0, (dir = opendir (BUILD_DIR "/extensions")) == NULL ? 1 : 0 );
+	pathlen = snprintf(fullname, sizeof(fullname), BUILD_DIR "/extensions/");
+	
+	while ((dp = readdir (dir)) != NULL) {
+		char * dot = strrchr(dp->d_name, '.');
+		if (dot && ((!prefix) || !(strncmp(dp->d_name, prefix, strlen(prefix)))) && (!(strcmp(dot, ".fdx")))) {
+			/* We found a file with name dict_*.fdx, attempt to load it */
+			struct ext_info * new = malloc(sizeof(struct ext_info));
+			CHECK( 1, new ? 1:0);
+			fd_list_init(&new->chain, new);
+			
+			snprintf(fullname + pathlen, sizeof(fullname) - pathlen, "%s", dp->d_name);
+			
+			LOG_D("Extension: '%s'", dp->d_name);
+			
+			/* load */
+			new->handler = dlopen(fullname, RTLD_NOW | RTLD_GLOBAL);
+			if (!new->handler) {
+				TRACE_DEBUG(INFO, "Unable to load '%s': %s.", fullname, dlerror());
+			}
+			CHECK( 0, new->handler == NULL ? 1 : 0 );
+			
+			/* resolve entry */
+			new->init_cb = dlsym( new->handler, "fd_ext_init" );
+			if (!new->init_cb) {
+				TRACE_DEBUG(INFO, "No 'fd_ext_init' entry point in '%s': %s.", fullname, dlerror());
+			}
+			CHECK( 0, new->init_cb == NULL ? 1 : 0 );
+			
+			new->depends = dlsym( new->handler, "fd_ext_depends" );
+			if (new->depends) {
+				new->ext_name = (char *)new->depends[0];
+				new->free_ext_name = 0;
+				if ( new->depends[1] ) {
+					fd_list_insert_before(&ext_with_depends, &new->chain);
+				} else {
+					fd_list_insert_before(&all_extensions, &new->chain);
+				}
+			} else {
+				new->ext_name = strdup(basename(dp->d_name));
+				new->free_ext_name = 1;
+				fd_list_insert_before(&all_extensions, &new->chain);
+			}
+			
+		}
+	}
+	
+	/* Now, reorder the list by dependencies */
+	{
+		int count, prevcount = 0;
+		struct fd_list * li;
+		do {
+			count = 0;
+			for (li=ext_with_depends.next; li != &ext_with_depends; li=li->next) {
+				struct ext_info * e = li->o;
+				int d;
+				int satisfied=0;
+				
+				/* Can we satisfy all dependencies? */
+				for (d=1;  ;d++) {
+					struct fd_list * eli;
+					if (!e->depends[d]) {
+						satisfied = 1;
+						break;
+					}
+					
+					/* can we find this dependency in the list? */
+					for (eli=all_extensions.next; eli != &all_extensions; eli = eli->next) {
+						struct ext_info * de = eli->o;
+						if (!strcasecmp(de->ext_name, e->depends[d]))
+							break; /* this dependency is satisfied */
+					}
+					
+					if (eli == &all_extensions) {
+						satisfied = 0;
+						break;
+					}
+				}
+				
+				if (satisfied) {
+					/* OK, we have all our dependencies in the list */
+					li=li->prev;
+					fd_list_unlink(&e->chain);
+					fd_list_insert_before(&all_extensions, &e->chain);
+				} else {
+					count++;
+				}
+			}
+			
+			if (prevcount && (prevcount == count)) {
+				LOG_E("Some extensions cannot have their dependencies satisfied, e.g.: %s", ((struct ext_info *)ext_with_depends.next->o)->ext_name);
+				CHECK(0, 1);
+			}
+			prevcount = count;
+			
+			if (FD_IS_LIST_EMPTY(&ext_with_depends))
+				break;
+		} while (1);
+	}
+	
+	/* Now, load all the extensions */
+	{
+		struct fd_list * li;
+		for (li=all_extensions.next; li != &all_extensions; li=li->next) {
+			struct ext_info * e = li->o;
+			int ret = (*e->init_cb)( FD_PROJECT_VERSION_MAJOR, FD_PROJECT_VERSION_MINOR, NULL );
+			LOG_N("Initializing extension '%s': %s", e->ext_name, ret ? strerror(ret) : "Success");
+		}
+	}
+	
+	/* We should probably clean the list here ? */
+}
+
+/* Main test routine */
+int main(int argc, char *argv[])
+{
+	struct msg * acr = NULL;
+	unsigned char * buf = NULL;
+	
+	int dictionaries_loaded = 0;
+	
+	test_parameter = DEFAULT_NUMBER_OF_SAMPLES;
+	
+	/* First, initialize the daemon modules */
+	INIT_FD();
+	CHECK( 0, fd_queues_init()  );
+	CHECK( 0, fd_msg_init()  );
+	CHECK( 0, fd_rtdisp_init()  );
+	
+	
+	{
+		struct dict_object * acr_model = NULL;
+
+		/* Now find the ACR dictionary object */
+		CHECK( 0, fd_dict_search ( fd_g_config->cnf_dict, DICT_COMMAND, CMD_BY_NAME, "Accounting-Request", &acr_model, ENOENT ) );
+
+		/* Create the instance, using the templates */
+		CHECK( 0, fd_msg_new ( acr_model, 0, &acr ) );
+	}
+	
+	/* Now let's create some additional Dictionary objects for the test */
+	{
+		/* The constant values used here are totally arbitrary chosen */
+		struct dict_object * vendor;
+		{
+			struct dict_vendor_data vendor_data = { 73565, "Vendor test" };
+			CHECK( 0, fd_dict_new ( fd_g_config->cnf_dict, DICT_VENDOR, &vendor_data , NULL, &vendor ) );
+		}
+		
+		{
+			struct dict_application_data app_data = { 73566, "Application test" };
+			CHECK( 0, fd_dict_new ( fd_g_config->cnf_dict, DICT_APPLICATION, &app_data , vendor, NULL ) );
+		}
+		
+		{
+			struct dict_avp_data avp_data = { 73567, 0, "AVP Test - no vendor - f32", 0, 0, AVP_TYPE_FLOAT32 };
+			CHECK( 0, fd_dict_new ( fd_g_config->cnf_dict, DICT_AVP, &avp_data , NULL, NULL ) );
+		}
+		
+		{
+			struct dict_avp_data avp_data = { 139103, 0, "AVP Test - no vendor - f64", 0, 0, AVP_TYPE_FLOAT64 };
+			CHECK( 0, fd_dict_new ( fd_g_config->cnf_dict, DICT_AVP, &avp_data , NULL, NULL ) );
+		}
+		
+		{ 
+			struct dict_object  * type = NULL;
+			struct dict_type_data type_data = { AVP_TYPE_INTEGER64, "Int64 test" };
+			struct dict_avp_data  avp_data = { 73568, 73565, "AVP Test - i64", AVP_FLAG_VENDOR, AVP_FLAG_VENDOR, AVP_TYPE_INTEGER64 };
+			CHECK( 0, fd_dict_new ( fd_g_config->cnf_dict, DICT_TYPE, &type_data , NULL, &type ) );
+			CHECK( 0, fd_dict_new ( fd_g_config->cnf_dict, DICT_AVP, &avp_data , type, NULL ) );
+		}
+		
+		{
+			struct dict_object     * type = NULL;
+			struct dict_type_data    type_data = { AVP_TYPE_INTEGER32, "Enum32 test" };
+			struct dict_enumval_data val1 = { "i32 const test (val 1)", { .i32 = 1 } };
+			struct dict_enumval_data val2 = { "i32 const test (val 2)", { .i32 = 2 } };
+			struct dict_enumval_data val3 = { "i32 const test (val -5)",{ .i32 = -5 } };
+			struct dict_avp_data     avp_data = { 73569, 73565, "AVP Test - enumi32", AVP_FLAG_VENDOR, AVP_FLAG_VENDOR, AVP_TYPE_INTEGER32 };
+			
+			CHECK( 0, fd_dict_new ( fd_g_config->cnf_dict, DICT_TYPE, &type_data , NULL, &type ) );
+			CHECK( 0, fd_dict_new ( fd_g_config->cnf_dict, DICT_AVP, &avp_data , type, NULL ) );
+			CHECK( 0, fd_dict_new ( fd_g_config->cnf_dict, DICT_ENUMVAL, &val1 , type, NULL ) );
+			CHECK( 0, fd_dict_new ( fd_g_config->cnf_dict, DICT_ENUMVAL, &val2 , type, NULL ) );
+			CHECK( 0, fd_dict_new ( fd_g_config->cnf_dict, DICT_ENUMVAL, &val3 , type, NULL ) );
+		}
+			
+		{ 
+			struct dict_object  * type = NULL;
+			struct dict_type_data type_data = { AVP_TYPE_OCTETSTRING, "OS test" };
+			struct dict_avp_data  avp_data = { 73570, 73565, "AVP Test - os", AVP_FLAG_VENDOR, AVP_FLAG_VENDOR, AVP_TYPE_OCTETSTRING };
+			CHECK( 0, fd_dict_new ( fd_g_config->cnf_dict, DICT_TYPE, &type_data , NULL, &type ) );
+			CHECK( 0, fd_dict_new ( fd_g_config->cnf_dict, DICT_AVP, &avp_data , type, NULL ) );
+		}
+		
+		{
+			struct dict_object     * type = NULL;
+			struct dict_type_data    type_data = { AVP_TYPE_OCTETSTRING, "OS enum test" };
+			struct dict_enumval_data val1 = { "os const test (Test)", { .os = { (unsigned char *)"Test", 4 } } };
+			struct dict_enumval_data val2 = { "os const test (waaad)", { .os = { (unsigned char *)"waaad", 5 } } };
+			struct dict_enumval_data val3 = { "os const test (waa)", { .os = { (unsigned char *)"waaad", 3 } } };
+			struct dict_avp_data     avp_data = { 73571, 73565, "AVP Test - enumos", AVP_FLAG_VENDOR, AVP_FLAG_VENDOR, AVP_TYPE_OCTETSTRING };
+			
+			CHECK( 0, fd_dict_new ( fd_g_config->cnf_dict, DICT_TYPE, &type_data , NULL, &type ) );
+			CHECK( 0, fd_dict_new ( fd_g_config->cnf_dict, DICT_AVP, &avp_data , type, NULL ) );
+			CHECK( 0, fd_dict_new ( fd_g_config->cnf_dict, DICT_ENUMVAL, &val1 , type, NULL ) );
+			CHECK( 0, fd_dict_new ( fd_g_config->cnf_dict, DICT_ENUMVAL, &val2 , type, NULL ) );
+			CHECK( 0, fd_dict_new ( fd_g_config->cnf_dict, DICT_ENUMVAL, &val3 , type, NULL ) );
+		}
+		
+		{
+			struct dict_object * gavp = NULL;
+			struct dict_avp_data avp_data = { 73572, 73565, "AVP Test - grouped", AVP_FLAG_VENDOR, AVP_FLAG_VENDOR, AVP_TYPE_GROUPED };
+			
+			CHECK( 0, fd_dict_new ( fd_g_config->cnf_dict, DICT_AVP, &avp_data , NULL, &gavp ) );
+			
+			/* Macro to search AVP and create a rule */		
+			#define ADD_RULE( _parent, _vendor, _avpname, _pos, _min, _max, _ord ) {		\
+				struct dict_object * _avp = NULL;						\
+				struct dict_avp_request _req = { (_vendor), 0, (_avpname) };			\
+				struct dict_rule_data _data;							\
+				CHECK( 0, fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME_AND_VENDOR, &_req, &_avp, ENOENT));\
+				_data.rule_avp = _avp;								\
+				_data.rule_position = (_pos);							\
+				_data.rule_order = (_ord);							\
+				_data.rule_min = (_min);							\
+				_data.rule_max = (_max);							\
+				CHECK( 0, fd_dict_new ( fd_g_config->cnf_dict, DICT_RULE, &_data , (_parent), NULL ) );	\
+			}
+			
+			ADD_RULE(gavp, 73565, "AVP Test - os", RULE_OPTIONAL,   -1, -1,  0);
+			
+		}
+			
+		{
+			struct dict_object  * application = NULL;
+			struct dict_object  * command = NULL;
+			struct dict_cmd_data  cmd_data = { 73573, "Test-Command-Request", CMD_FLAG_REQUEST, CMD_FLAG_REQUEST };
+			
+			CHECK( 0, fd_dict_search ( fd_g_config->cnf_dict, DICT_APPLICATION, APPLICATION_BY_NAME, "Application test", &application, ENOENT ) );
+			CHECK( 0, fd_dict_new ( fd_g_config->cnf_dict, DICT_COMMAND, &cmd_data , application, &command ) );
+			ADD_RULE(command, 0,     "AVP Test - no vendor - f32", 	RULE_FIXED_HEAD, -1,  1,  1);
+			ADD_RULE(command, 73565, "AVP Test - i64",		RULE_REQUIRED,   -1, -1,  0);
+			ADD_RULE(command, 73565, "AVP Test - enumi32", 		RULE_OPTIONAL,   -1, -1,  0);
+			ADD_RULE(command, 73565, "AVP Test - os", 		RULE_OPTIONAL,   -1, -1,  0);
+			ADD_RULE(command, 73565, "AVP Test - enumos", 		RULE_OPTIONAL,   -1, -1,  0);
+			ADD_RULE(command, 73565, "AVP Test - grouped", 		RULE_OPTIONAL,   -1, -1,  0);
+		}
+		
+		{
+			struct dict_object  * application = NULL;
+			struct dict_object  * command = NULL;
+			struct dict_cmd_data  cmd_data = { 73573, "Test-Command-Answer", CMD_FLAG_REQUEST, 0 };
+			
+			CHECK( 0, fd_dict_search ( fd_g_config->cnf_dict, DICT_APPLICATION, APPLICATION_BY_NAME, "Application test", &application, ENOENT ) );
+			CHECK( 0, fd_dict_new ( fd_g_config->cnf_dict, DICT_COMMAND, &cmd_data , application, &command ) );
+		}
+		
+		{
+			struct dict_object  * gavp = NULL;
+			struct dict_avp_data  avp_data = { 73574, 73565, "AVP Test - rules", AVP_FLAG_VENDOR, AVP_FLAG_VENDOR, AVP_TYPE_GROUPED };
+			
+			CHECK( 0, fd_dict_new ( fd_g_config->cnf_dict, DICT_AVP, &avp_data , NULL, &gavp ) );
+			
+			ADD_RULE(gavp,     0, "AVP Test - no vendor - f32", RULE_FIXED_HEAD,   0, 1, 1);
+			ADD_RULE(gavp, 73565, "AVP Test - i64", 	    RULE_FIXED_HEAD,  -1, 1, 2);
+			ADD_RULE(gavp, 73565, "AVP Test - enumi32", 	    RULE_FIXED_HEAD,  -1, 1, 3);
+			ADD_RULE(gavp, 73565, "AVP Test - os", 	    	    RULE_REQUIRED,     2, 3, 0);
+			ADD_RULE(gavp, 73565, "AVP Test - enumos",     	    RULE_OPTIONAL,     0, 1, 0);
+			ADD_RULE(gavp, 73565, "AVP Test - grouped",         RULE_FIXED_TAIL,  -1, 1, 1);
+			/* ABNF : 
+				< no vendor - f32 >
+				< i64 >
+				< enumi32 >
+			    2*3 { os }
+			     *1 [ enumos ]
+				< grouped >
+						*/
+		}
+	}
+	
+	/* Now create some values and check the length is correctly handled */
+	{
+		struct dict_object * cmd_model = NULL;
+		struct msg         * msg = NULL;
+		struct dict_object * avp_model = NULL;
+		union avp_value      value;
+		
+		CHECK( 0, fd_dict_search ( fd_g_config->cnf_dict, DICT_COMMAND, CMD_BY_NAME, "Test-Command-Request", &cmd_model, ENOENT ) );
+		
+		/* Check the sizes are handled properly */
+		{
+			struct avp * avpi = NULL;
+			struct avp * avpch = NULL;
+			struct msg_hdr * msgdata = NULL;
+			#define ADD_AVP( _parent, _position, _avpi, _avpvendor, _avpname) {			\
+				struct dict_object * _avp = NULL;						\
+				struct dict_avp_request _req = { (_avpvendor), 0, (_avpname) };			\
+				CHECK( 0, fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME_AND_VENDOR, &_req, &_avp, ENOENT));\
+				CHECK( 0, fd_msg_avp_new ( _avp, 0, &_avpi ) );					\
+				CHECK( 0, fd_msg_avp_add ( (_parent), (_position), _avpi ) );			\
+			}
+			/* Create a message with many AVP inside */
+			CHECK( 0, fd_msg_new ( cmd_model, 0, &msg ) );
+			CHECK( 0, fd_msg_hdr ( msg, &msgdata ) );
+			
+			/* Avp no vendor, float32 => size = 12 */
+			ADD_AVP( msg, MSG_BRW_LAST_CHILD, avpi, 0,     "AVP Test - no vendor - f32" );
+			value.f32 = 3.1415;
+			CHECK( 0, fd_msg_avp_setvalue ( avpi, &value ) );
+			
+			/* Add a vendor AVP, integer64 => size = 20 */
+			ADD_AVP( msg, MSG_BRW_LAST_CHILD, avpi, 73565, "AVP Test - i64" );
+			value.i64 = 0x123456789abcdeLL;
+			CHECK( 0, fd_msg_avp_setvalue ( avpi, &value ) );
+			
+			/* Add an AVP with an enum value */
+			ADD_AVP( msg, MSG_BRW_LAST_CHILD, avpi, 73565, "AVP Test - enumi32" );
+			{
+				struct dict_object * type_model = NULL;
+				struct dict_object * value_model = NULL;
+				struct dict_enumval_request request;
+				
+				CHECK( 0, fd_msg_model ( avpi, &avp_model ) );
+				CHECK( 0, fd_dict_search ( fd_g_config->cnf_dict, DICT_TYPE, TYPE_OF_AVP, avp_model, &type_model, ENOENT ) );
+				memset(&request, 0, sizeof(request));
+				request.type_obj = type_model;
+				request.search.enum_name = "i32 const test (val 2)";
+				CHECK( 0, fd_dict_search ( fd_g_config->cnf_dict, DICT_ENUMVAL, ENUMVAL_BY_STRUCT, &request, &value_model, ENOENT ) );
+				CHECK( 0, fd_dict_getval ( value_model, &request.search ) );
+				CHECK( 0, fd_msg_avp_setvalue ( avpi, &request.search.enum_value ) );
+			}
+			
+			/* Add an AVP with an enum value, negative */
+			ADD_AVP( msg, MSG_BRW_LAST_CHILD, avpi, 73565, "AVP Test - enumi32" );
+			{
+				struct dict_object  * type_model = NULL;
+				struct dict_object  * value_model = NULL;
+				struct dict_enumval_request request;
+				
+				CHECK( 0, fd_msg_model ( avpi, &avp_model ) );
+				CHECK( 0, fd_dict_search ( fd_g_config->cnf_dict, DICT_TYPE, TYPE_OF_AVP, avp_model, &type_model, ENOENT ) );
+				memset(&request, 0, sizeof(request));
+				request.type_obj = type_model;
+				request.search.enum_name = "i32 const test (val -5)";
+				CHECK( 0, fd_dict_search ( fd_g_config->cnf_dict, DICT_ENUMVAL, ENUMVAL_BY_STRUCT, &request, &value_model, ENOENT ) );
+				CHECK( 0, fd_dict_getval ( value_model, &request.search ) );
+				CHECK( 0, fd_msg_avp_setvalue ( avpi, &request.search.enum_value ) );
+			}
+			
+			/* Now add a value which is not a constant into an enumerated AVP */
+			ADD_AVP( msg, MSG_BRW_LAST_CHILD, avpi, 73565, "AVP Test - enumi32" );
+			value.i32 = -10;
+			CHECK( 0, fd_msg_avp_setvalue ( avpi, &value ) );
+			
+			/* Add an octetstring AVP */
+			ADD_AVP( msg, MSG_BRW_LAST_CHILD, avpi, 73565, "AVP Test - os" );
+			{
+				unsigned char buf[90];
+				memcpy(&buf, "This\0 is a buffer of dat\a. It is not a string so we can have any c\0ntr\0l character here...\0\0", 89);
+				value.os.data = buf;
+				value.os.len = 89;
+				CHECK( 0, fd_msg_avp_setvalue ( avpi, &value ) );
+				memset(&buf, 0, sizeof(buf)); /* Test that the OS value is really copied */
+			}
+
+			/* Add an octetstring from an enumerated constant */
+			ADD_AVP( msg, MSG_BRW_LAST_CHILD, avpi, 73565, "AVP Test - enumos" );
+			{
+				struct dict_object  * type_model = NULL;
+				struct dict_object  * value_model = NULL;
+				struct dict_enumval_request request;
+				
+				CHECK( 0, fd_msg_model ( avpi, &avp_model ) );
+				CHECK( 0, fd_dict_search ( fd_g_config->cnf_dict, DICT_TYPE, TYPE_OF_AVP, avp_model, &type_model, ENOENT ) );
+				memset(&request, 0, sizeof(request));
+				request.type_obj = type_model;
+				request.search.enum_name = "os const test (waaad)";
+				CHECK( 0, fd_dict_search ( fd_g_config->cnf_dict, DICT_ENUMVAL, ENUMVAL_BY_STRUCT, &request, &value_model, ENOENT ) );
+				CHECK( 0, fd_dict_getval ( value_model, &request.search ) );
+				CHECK( 0, fd_msg_avp_setvalue ( avpi, &request.search.enum_value ) );
+			}
+				
+			/* Add an octetstring from an enumerated constant */
+			ADD_AVP( msg, MSG_BRW_LAST_CHILD, avpi, 73565, "AVP Test - enumos" );
+			{
+				struct dict_object  * type_model = NULL;
+				struct dict_object  * value_model = NULL;
+				struct dict_enumval_request request;
+				
+				CHECK( 0, fd_msg_model ( avpi, &avp_model ) );
+				CHECK( 0, fd_dict_search ( fd_g_config->cnf_dict, DICT_TYPE, TYPE_OF_AVP, avp_model, &type_model, ENOENT ) );
+				memset(&request, 0, sizeof(request));
+				request.type_obj = type_model;
+				request.search.enum_name = "os const test (waa)";
+				CHECK( 0, fd_dict_search ( fd_g_config->cnf_dict, DICT_ENUMVAL, ENUMVAL_BY_STRUCT, &request, &value_model, ENOENT ) );
+				CHECK( 0, fd_dict_getval ( value_model, &request.search ) );
+				CHECK( 0, fd_msg_avp_setvalue ( avpi, &request.search.enum_value ) );
+			}
+				
+			/* Now test the grouped AVPs */	
+			ADD_AVP( msg, MSG_BRW_LAST_CHILD, avpi, 73565, "AVP Test - grouped" );
+			  ADD_AVP( avpi, MSG_BRW_LAST_CHILD, avpch, 73565, "AVP Test - os" );
+			  {
+				value.os.data = (unsigned char *)"12345678";
+				value.os.len = 8;
+				CHECK( 0, fd_msg_avp_setvalue ( avpch, &value ) );
+			  }
+			  ADD_AVP( avpi, MSG_BRW_LAST_CHILD, avpch, 73565, "AVP Test - os" );
+			  {
+				value.os.data = (unsigned char *)"123456789";
+				value.os.len = 9;
+				CHECK( 0, fd_msg_avp_setvalue ( avpch, &value ) );
+			  }
+			
+			/* Add another similar grouped AVP, to have lot of padding */
+			ADD_AVP( msg, MSG_BRW_LAST_CHILD, avpi, 73565, "AVP Test - grouped" );
+			  ADD_AVP( avpi, MSG_BRW_LAST_CHILD, avpch, 73565, "AVP Test - os" );
+			  {
+				value.os.data = (unsigned char *)"1";
+				value.os.len = 1;
+				CHECK( 0, fd_msg_avp_setvalue ( avpch, &value ) );
+			  }
+			  ADD_AVP( avpi, MSG_BRW_LAST_CHILD, avpch, 73565, "AVP Test - os" );
+			  {
+				value.os.data = (unsigned char *)"1234567";
+				value.os.len = 7;
+				CHECK( 0, fd_msg_avp_setvalue ( avpch, &value ) );
+			  }
+			
+			/* Set the application to the test application: 73566 */
+			msgdata->msg_appl = 73566;
+			
+			/* Set the hop-by-hop ID to a random value: 0x4b44b41d */
+			msgdata->msg_hbhid = 0x4b44b41d;
+			/* Set the end-to-end ID to a random value: 0xe2ee2e1d */
+			msgdata->msg_eteid = 0xe2ee2e1d;
+		}
+		
+		CHECK( 0, fd_msg_bufferize( msg, &buf, NULL ) );
+		
+		LOG_D( "Test message: %s", fd_msg_dump_treeview(FD_DUMP_TEST_PARAMS, msg, NULL, 0, 1));
+		
+		/* Now free the message, we keep only the buffer. */
+		CHECK( 0, fd_msg_free( msg ) );
+		
+	}
+	
+	/* We have our "buf" now, length is 344 -- cf. testmesg.c. */
+redo:	
+	/* Test the throughput of the different functions function */
+	{
+		struct stress_struct {
+			struct msg * m;
+			uint8_t * b;
+		} * stress_array;
+		int i;
+		struct timespec start, end;
+		
+		/* Create the copies of the message buffer */
+		stress_array = calloc(test_parameter, sizeof(struct stress_struct));
+		CHECK( stress_array ? 1 : 0, 1);
+		
+		for (i=0; i < test_parameter; i++) {
+			stress_array[i].b = malloc(344);
+			if (!stress_array[i].b)
+				break;
+			memcpy(stress_array[i].b, buf, 344);
+		}
+		CHECK( test_parameter, i ); /* if false, a malloc failed */
+		
+	/* fd_msg_parse_buffer */
+		
+		CHECK( 0, clock_gettime(CLOCK_REALTIME, &start) );
+		
+		/* Test the msg_parse_buffer function */
+		for (i=0; i < test_parameter; i++) {
+			if (0 != fd_msg_parse_buffer( &stress_array[i].b, 344, &stress_array[i].m) )
+				break;
+		}
+		CHECK( test_parameter, i ); /* if false, a call failed */
+		
+		CHECK( 0, clock_gettime(CLOCK_REALTIME, &end) );
+		display_result(test_parameter, &start, &end, "fd_msg_parse_buffer", "buffers", "parsed");
+		
+	/* fd_msg_parse_dict */
+		
+		CHECK( 0, clock_gettime(CLOCK_REALTIME, &start) );
+		
+		/* Test the fd_msg_parse_dict function */
+		for (i=0; i < test_parameter; i++) {
+			if (0 != fd_msg_parse_dict( stress_array[i].m, fd_g_config->cnf_dict, NULL ) )
+				break;
+		}
+		CHECK( test_parameter, i ); /* if false, a call failed */
+		
+		CHECK( 0, clock_gettime(CLOCK_REALTIME, &end) );
+		display_result(test_parameter, &start, &end, "fd_msg_parse_dict", "messages", "parsed");
+		
+		
+	/* fd_msg_parse_rules */
+		
+		CHECK( 0, clock_gettime(CLOCK_REALTIME, &start) );
+		
+		/* Test the fd_msg_parse_rules function */
+		for (i=0; i < test_parameter; i++) {
+			if (0 != fd_msg_parse_rules( stress_array[i].m, fd_g_config->cnf_dict, NULL ) )
+				break;
+		}
+		CHECK( test_parameter, i ); /* if false, a call failed */
+		
+		CHECK( 0, clock_gettime(CLOCK_REALTIME, &end) );
+		display_result(test_parameter, &start, &end, "fd_msg_parse_rules", "messages", "parsed");
+		
+		
+	/* fd_msg_new_answer_from_req (0) */
+		
+		CHECK( 0, clock_gettime(CLOCK_REALTIME, &start) );
+		
+		/* Test the fd_msg_new_answer_from_req function */
+		for (i=0; i < test_parameter; i++) {
+			if (0 != fd_msg_new_answer_from_req( fd_g_config->cnf_dict, &stress_array[i].m, 0 ) )
+				break;
+		}
+		CHECK( test_parameter, i ); /* if false, a call failed */
+		
+		CHECK( 0, clock_gettime(CLOCK_REALTIME, &end) );
+		display_result(test_parameter, &start, &end, "new_answer(normal)", "messages", "created");
+		
+		/* unlink answers and go back to request messages */
+		for (i=0; i < test_parameter; i++) {
+			struct msg * ans = stress_array[i].m;
+			if (0 != fd_msg_answ_getq( ans, &stress_array[i].m ) )
+				break;
+			if (0 != fd_msg_answ_detach( ans ) )
+				break;
+			fd_msg_free( ans );
+		}
+		CHECK( test_parameter, i ); /* if false, a call failed */
+		
+		
+	/* fd_msg_new_answer_from_req (MSGFL_ANSW_ERROR) */
+		
+		CHECK( 0, clock_gettime(CLOCK_REALTIME, &start) );
+		
+		/* Test the fd_msg_new_answer_from_req function */
+		for (i=0; i < test_parameter; i++) {
+			if ( 0 != fd_msg_new_answer_from_req( fd_g_config->cnf_dict, &stress_array[i].m, MSGFL_ANSW_ERROR ) )
+				break;
+		}
+		CHECK( test_parameter, i ); /* if false, a call failed */
+		
+		CHECK( 0, clock_gettime(CLOCK_REALTIME, &end) );
+		display_result(test_parameter, &start, &end, "new_answer(error)", "messages", "created");
+		
+		/* unlink answers and go back to request messages */
+		for (i=0; i < test_parameter; i++) {
+			struct msg * ans = stress_array[i].m;
+			if (0 != fd_msg_answ_getq( ans, &stress_array[i].m ) )
+				break;
+			if (0 != fd_msg_answ_detach( ans ) )
+				break;
+			fd_msg_free( ans );
+		}
+		
+		
+	/* fd_msg_bufferize */
+		
+
+		CHECK( 0, clock_gettime(CLOCK_REALTIME, &start) );
+		
+		/* Test the fd_msg_bufferize function */
+		for (i=0; i < test_parameter; i++) {
+			size_t len = 0;
+			if (0 != fd_msg_bufferize( stress_array[i].m, &stress_array[i].b, &len ) )
+				break;
+		}
+		CHECK( test_parameter, i ); /* if false, a call failed */
+		
+		CHECK( 0, clock_gettime(CLOCK_REALTIME, &end) );
+		display_result(test_parameter, &start, &end, "fd_msg_bufferize", "buffers", "created");
+		
+		
+	/* fd_msg_free */
+		
+		CHECK( 0, clock_gettime(CLOCK_REALTIME, &start) );
+		
+		/* Free those messages */
+		for (i=0; i < test_parameter; i++) {
+			fd_msg_free( stress_array[i].m );
+		}
+		
+		CHECK( 0, clock_gettime(CLOCK_REALTIME, &end) );
+		display_result(test_parameter, &start, &end, "fd_msg_free", "messages", "freed");
+		
+		
+		for (i=0; i < test_parameter; i++) {
+			free(stress_array[i].b);
+		}
+		free(stress_array);
+	}
+	
+	if (!dictionaries_loaded) {
+		load_all_extensions("dict_");
+		dictionaries_loaded = 1;
+		printf("Loaded all dictionary extensions, restarting...\n");
+		goto redo;
+	}
+	
+	/* That's all for the tests yet */
+	PASSTEST();
+} 
+	
diff --git a/tests/testostr.c b/tests/testostr.c
new file mode 100644
index 0000000..14a64c5
--- /dev/null
+++ b/tests/testostr.c
@@ -0,0 +1,202 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@freediameter.net>							 *
+*													 *
+* Copyright (c) 2011, 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 (os0_t)"This is my test string (with extra unused data)"
+
+/* The following string contains UTF-8 encoded characters (Chinese characters) */
+#define TEST_IDN_UTF8  "freeDiameter.中国"
+#define TEST_IDN_CONV  "freeDiameter.xn--fiqs8s"
+
+/* Main test routine */
+int main(int argc, char *argv[])
+{
+	/* First, initialize the daemon modules */
+	INIT_FD();
+	
+	/* Check the hash function */
+	{
+		uint8_t buf[30];
+		
+		uint32_t hash = fd_os_hash(TEST_STR, CONSTSTRLEN(TEST_STR)); /* reference value */
+		
+		/* Check that a hash of a substring / surstring is different */
+		CHECK( 1, hash != fd_os_hash(TEST_STR, CONSTSTRLEN(TEST_STR) - 1) ? 1 : 0 );
+		CHECK( 1, hash != fd_os_hash(TEST_STR, CONSTSTRLEN(TEST_STR) + 1) ? 1 : 0 );
+		
+		/* Check alignment of the string is not important */
+		memcpy(buf + 4, TEST_STR, CONSTSTRLEN(TEST_STR));
+		CHECK( hash, fd_os_hash(buf + 4, CONSTSTRLEN(TEST_STR)) );
+		
+		memcpy(buf + 3, TEST_STR, CONSTSTRLEN(TEST_STR));
+		CHECK( hash, fd_os_hash(buf + 3, CONSTSTRLEN(TEST_STR)) );
+		
+		memcpy(buf + 2, TEST_STR, CONSTSTRLEN(TEST_STR));
+		CHECK( hash, fd_os_hash(buf + 2, CONSTSTRLEN(TEST_STR)) );
+		
+		memcpy(buf + 1, TEST_STR, CONSTSTRLEN(TEST_STR));
+		CHECK( hash, fd_os_hash(buf + 1, CONSTSTRLEN(TEST_STR)) );
+	}
+	
+	/* Check the Diameter Identity functions */
+	{
+		char * res;
+		size_t len=0;
+		
+		/* A valid ASCII domain name */
+		res = TEST_IDN_CONV;
+		CHECK( 0, fd_os_validate_DiameterIdentity(&res, &len, 1) );
+		CHECK( 0, strcasecmp(res, TEST_IDN_CONV) ); /* the function does not change a valid DN */
+		CHECK( 0, fd_os_validate_DiameterIdentity(&res, &len, 0) );
+		CHECK( 0, strcasecmp(res, TEST_IDN_CONV) );
+		CHECK( CONSTSTRLEN(TEST_IDN_CONV), len );
+		free(res);
+		
+		/* Now, an invalid string */
+		res = TEST_IDN_UTF8;
+		len = 0;
+		
+		#ifdef DIAMID_IDNA_IGNORE
+		
+		/* The UTF-8 chars are considered valid */
+		CHECK( 1, fd_os_is_valid_DiameterIdentity((os0_t)TEST_IDN_UTF8, CONSTSTRLEN(TEST_IDN_UTF8) ) );
+		
+		/* The string should be passed unmodified */
+		CHECK( 0, fd_os_validate_DiameterIdentity(&res, &len, 1) );
+		CHECK( 0, strcasecmp(res, TEST_IDN_UTF8) );
+		CHECK( 0, fd_os_cmp(res, len, TEST_IDN_UTF8, CONSTSTRLEN(TEST_IDN_UTF8)) );
+		CHECK( 0, fd_os_almostcasesrch(res, len, TEST_IDN_UTF8, CONSTSTRLEN(TEST_IDN_UTF8), NULL) );
+		CHECK( 0, fd_os_validate_DiameterIdentity(&res, &len, 0) );
+		CHECK( 0, strcasecmp(res, TEST_IDN_UTF8) );
+		CHECK( CONSTSTRLEN(TEST_IDN_UTF8), len );
+		free(res);
+		
+		#else /* DIAMID_IDNA_IGNORE */
+		
+		/* The UTF-8 chars are recognized as invalid DiameterIdentity */
+		CHECK( 0, fd_os_is_valid_DiameterIdentity((os0_t)TEST_IDN_UTF8, CONSTSTRLEN(TEST_IDN_UTF8) ));
+		
+		# ifdef DIAMID_IDNA_REJECT
+		
+		/* The string must be rejected */
+		CHECK( EINVAL, fd_os_validate_DiameterIdentity(&res, &len, 1) );
+		
+		# else /* DIAMID_IDNA_REJECT */
+		
+		/* The string should be transformed into TEST_IDN_CONV */
+		CHECK( 0, fd_os_validate_DiameterIdentity(&res, &len, 1) );
+		CHECK( 0, strcasecmp(res, TEST_IDN_CONV) );
+		CHECK( CONSTSTRLEN(TEST_IDN_CONV), len );
+		free(res);
+		
+		# endif /* DIAMID_IDNA_REJECT */
+		#endif /* DIAMID_IDNA_IGNORE */
+
+	}
+	
+	{
+		/* test fd_os_cmp and fd_os_almostcasesrch and that they are compatible */
+		char *t1 = "a";
+		char *t2 = "b";
+		char *t3 = "C";
+		char *t4 = "d";
+		char *t5 = "aa";
+		char *t6 = "aB";
+		char *t7 = "Ac";
+		char *t8 = "aD";
+		char *t9 = "AAA";
+		
+		char *t5b = "Aa";
+		char *t6b = "ab";
+		
+		/* First, create a list with all the elements in order given by fd_os_cmp */
+		char *t[] = { t1, t2, t3, t4, t5, t6,t7, t8, t9 };
+		int i;
+		struct fd_list *li, l = FD_LIST_INITIALIZER(l);
+		for (i = 0; i < sizeof(t) / sizeof(t[0]); i++) {
+			/* insert t[i] */
+			struct fd_list *n = malloc(sizeof(struct fd_list));
+			CHECK( 1, n ? 1 : 0 );
+			fd_list_init(n, t[i]);
+			for (li = l.next; li != &l; li = li->next) {
+				if ( fd_os_cmp(t[i], strlen(t[i]), li->o, strlen(li->o)) < 0 )
+					break;
+			} 
+			fd_list_insert_before(li, n);
+		}
+		/* in principle the result is: [ "C", "a", "b", "d", "Ac", "aB", "aD", "aa", "AAA" ] */
+		
+		/* Since there is no equal value in the list (even case-insensitive), check that the order is valid also for the caseinsensitive variant */
+		for (li = l.next; li != l.prev; li = li->next) {
+			CHECK( 1, fd_os_almostcasesrch(li->o, strlen(li->o), li->next->o, strlen(li->next->o), NULL) < 0 ? 1 : 0 );
+		}
+		
+		/* Now check that we can case-insentively find t5b and t6b to be equal to t5 and t6 resp. (this is how we use it in the daemon) */
+		for (li = l.next; li != &l; li = li->next) {
+			int cont, cmp;
+			cmp = fd_os_almostcasesrch(t5b, strlen(t5b), li->o, strlen(li->o), &cont);
+			TRACE_DEBUG(FULL, "Comp '%s' : %d, %d", (char *)li->o, cmp, cont);
+			if (cmp == 0)
+				break;
+			if (!cont)
+				break;
+		}
+		CHECK( li->o, t5 );
+		
+		for (li = l.next; li != &l; li = li->next) {
+			int cont, cmp;
+			cmp = fd_os_almostcasesrch(t6b, strlen(t6b), li->o, strlen(li->o), &cont);
+			TRACE_DEBUG(FULL, "Comp '%s' : %d, %d", (char *)li->o, cmp, cont);
+			if (cmp == 0)
+				break;
+			if (!cont)
+				break;
+		}
+		CHECK( li->o, t6 );
+		
+		
+		/* done */
+		while (!FD_IS_LIST_EMPTY(&l)) {
+			li = l.next;
+			fd_list_unlink(li);
+			free(li);
+		}
+	}
+	
+	/* That's all for the tests yet */
+	PASSTEST();
+} 
+	
diff --git a/tests/testpeers.c b/tests/testpeers.c
new file mode 100644
index 0000000..50d252a
--- /dev/null
+++ b/tests/testpeers.c
@@ -0,0 +1,78 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@freediameter.net>							 *
+*													 *
+* Copyright (c) 2013, 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"
+
+const char * ids[] = { "b11", "b14", "b1", "b4" };
+#define DomainName "localdomain"
+
+/* Main test routine */
+int main(int argc, char *argv[])
+{
+	/* First, initialize the daemon modules */
+	INIT_FD();
+	
+	/* Create 4 peers with these ids */
+	{
+		int i;
+		struct peer_info inf;
+		char locid[255];
+		memset(&inf, 0, sizeof(inf));
+		inf.pi_diamid = (char *)locid;
+		for (i=0; i < sizeof(ids) / sizeof(ids[0]); i++) {
+			snprintf(locid, sizeof(locid), "%s." DomainName, ids[i]);
+			CHECK( 0, fd_peer_add(&inf, __FILE__, NULL, NULL));
+		}
+	}
+	fd_log_debug("%s", fd_peer_dump_list(FD_DUMP_TEST_PARAMS, 0));
+	/* Check we are able to find again any of these */
+	{
+		int i;
+		char locid[255];
+		struct peer_hdr *p;
+		for (i=0; i < sizeof(ids) / sizeof(ids[0]); i++) {
+			snprintf(locid, sizeof(locid), "%s." DomainName, ids[i]);
+			CHECK( 0, fd_peer_getbyid((DiamId_t)locid, strlen((char *)locid), 0, &p));
+			CHECK( 0, strcmp((char *)locid, p->info.pi_diamid));
+			CHECK( 0, fd_peer_getbyid((DiamId_t)locid, strlen((char *)locid), 1, &p));
+			CHECK( 0, strcmp((char *)locid, p->info.pi_diamid));
+		}
+	}
+	
+	
+	/* That's all for the tests yet */
+	PASSTEST();
+} 
+	
diff --git a/tests/tests.h b/tests/tests.h
new file mode 100644
index 0000000..85e6d9b
--- /dev/null
+++ b/tests/tests.h
@@ -0,0 +1,239 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@freediameter.net>							 *
+*													 *
+* Copyright (c) 2013, 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.								 *
+*********************************************************************************************************/
+
+/* This file contains the definition of our test harness.
+ * The harness is very simple yet.
+ * It may be interessant to go to dejagnu later...
+ *
+ */
+#ifndef _TESTS_H
+#define _TESTS_H
+
+#include "fdproto-internal.h"
+#include "fdcore-internal.h"
+
+#include <pthread.h>
+#include <errno.h>
+GCC_DIAG_OFF("-Wdeprecated-declarations")
+#include <gcrypt.h>
+GCC_DIAG_ON("-Wdeprecated-declarations")
+
+/* Test timeout duration, unless -n is passed on the command line */
+#ifndef TEST_TIMEOUT
+#define TEST_TIMEOUT	120	/* in seconds */
+#endif /* TEST_TIMEOUT */
+
+/* Standard includes */
+#include <getopt.h>
+#include <time.h>
+#include <libgen.h>
+#include <signal.h>
+
+/* Define the return code values */
+#define PASS	0
+#define FAIL	1
+
+/* Define the macro to fail a test with a message */
+#define FAILTEST( message... ){				\
+	LOG_F(message);					\
+	LOG_F("FAILED: %s ", __STRIPPED_FILE__);	\
+	free(tbuf);					\
+	exit(FAIL);					\
+}
+
+/* Define the macro to pass a test */
+#define PASSTEST( ){					\
+	LOG_N("PASS: %s", __STRIPPED_FILE__);		\
+	(void)fd_core_shutdown();			\
+	(void)fd_core_wait_shutdown_complete();		\
+	(void)fd_thr_term(&signal_thr);			\
+	free(tbuf);					\
+	exit(PASS);					\
+}
+
+static struct fd_config conf;
+extern struct fd_config * fd_g_config;
+
+/* for dumps */
+static char * tbuf = NULL; size_t tbuflen = 0;
+#define FD_DUMP_TEST_PARAMS &tbuf, &tbuflen, NULL
+
+
+/* Define the standard check routines */
+#define CHECK( _val, _assert ){				\
+	LOG_D("CHECK( %s == %s )",			\
+				#_assert,		\
+				#_val);			\
+	{						\
+	__typeof__ (_val) __ret = (_assert);		\
+	if (__ret != (_val)) {				\
+		FAILTEST( "%s:%d: CHECK FAILED : %s == %lx != %lx",	\
+			__STRIPPED_FILE__,		\
+			__LINE__,			\
+			#_assert,			\
+			(unsigned long)__ret,		\
+			(unsigned long)(_val));		\
+	}}						\
+}
+
+static pthread_t signal_thr;
+static void * signal_catch(void * arg)
+{
+	int sig;
+	sigset_t ss;
+	fd_log_threadname ( "Signal catcher" );
+	
+	sigemptyset(&ss);
+	
+	/* We use SIGALRM */
+	sigaddset(&ss, SIGALRM);
+	
+	/* Unblock any other signal for this thread, so that default handler is enabled */
+	CHECK_SYS_DO( pthread_sigmask( SIG_SETMASK, &ss, NULL ), );
+	
+	/* Now wait for sigwait or cancelation */
+	CHECK_POSIX_DO( sigwait(&ss, &sig),  );
+	FAILTEST("The timeout (" _stringize(TEST_TIMEOUT) " sec) was reached. Use -n or change TEST_TIMEOUT if the test needs more time to execute.");
+	
+	return NULL;
+}
+	
+
+GCRY_THREAD_OPTION_PTHREAD_IMPL;
+
+/* gnutls debug. */
+static void fd_gnutls_debug(int level, const char * str) {
+	const char * __thn = ((char *)pthread_getspecific(fd_log_thname) ?: "unnamed");	
+	fd_log_debug("tid:%-20s[gnutls:%d] %s", __thn, level, str);
+}
+static int gnutls_debug = 0;
+
+static int test_parameter = 0;
+
+static inline void parse_cmdline(int argc, char * argv[]) {
+	int c;
+	int no_timeout = 0;
+	while ((c = getopt (argc, argv, "dqnf:F:g:p:")) != -1) {
+		switch (c) {
+			case 'd':	/* Increase verbosity of debug messages.  */
+				fd_g_debug_lvl--;
+				break;
+				
+			case 'q':	/* Decrease verbosity.  */
+				fd_g_debug_lvl++;
+				break;
+			
+			case 'n':	/* Disable the timeout of the test.  */
+				no_timeout = 1;
+				break;
+			
+			case 'f':	/* Full debug for the function with this name.  */
+				#ifdef DEBUG
+				fd_debug_one_function = optarg;
+				#else /* DEBUG */
+				TRACE_DEBUG(INFO, "Error: must compile with DEBUG support to use this feature");
+				#endif /* DEBUG */
+				break;
+				
+			case 'F':	/* Full debug for the functions in file with this name.  */
+				#ifdef DEBUG
+				fd_debug_one_file = optarg;
+				#else /* DEBUG */
+				TRACE_DEBUG(INFO, "Error: must compile with DEBUG support to use this feature");
+				#endif /* DEBUG */
+				break;
+				
+			case 'g':	/* Set a debug level and function for GNU TLS calls.  */
+				gnutls_debug = (int)atoi(optarg);
+				break;
+				
+			case 'p':	/* Set a debug level and function for GNU TLS calls.  */
+				test_parameter = (int)atoi(optarg);
+				break;
+				
+			default:	/* bug: option not considered.  */
+				return;
+		}
+	}
+	if (!no_timeout) {
+		alarm(TEST_TIMEOUT);
+	}
+	CHECK( 0, pthread_create(&signal_thr, NULL, signal_catch, NULL) );
+}
+ 
+static inline void test_init(int argc, char * argv[], char *fname)
+{
+	sigset_t sig_all;
+	sigfillset(&sig_all);
+	
+	CHECK( 0, pthread_sigmask(SIG_BLOCK, &sig_all, NULL));
+	
+	fd_g_config = &conf;
+	memset(fd_g_config, 0, sizeof(struct fd_config));
+	
+	CHECK( 0, fd_libproto_init() );
+	
+	CHECK( 0, fd_hooks_init() );	
+	
+	fd_log_threadname(fname);
+	
+	/* Parse the command line */
+	parse_cmdline(argc, argv);
+	
+	/* Initialize gcrypt and gnutls */
+	(void) gcry_control (GCRYCTL_SET_THREAD_CBS, &gcry_threads_pthread);
+	(void) gcry_control (GCRYCTL_ENABLE_QUICK_RANDOM, 0);
+	CHECK( 0, gnutls_global_init());
+	/* Set gnutls debug level ? */
+	if (gnutls_debug) {
+		gnutls_global_set_log_function((gnutls_log_func)fd_gnutls_debug);
+		gnutls_global_set_log_level (gnutls_debug);
+		TRACE_DEBUG(INFO, "Enabled GNUTLS debug at level %d", gnutls_debug);
+	}
+	
+	/* Initialize the config */
+	CHECK( 0, fd_conf_init() );
+
+	/* Add definitions of the base protocol */
+	CHECK( 0, fd_dict_base_protocol(fd_g_config->cnf_dict) );
+	
+	/* Initialize only the sessions */
+	CHECK( 0, fd_sess_start()  );
+	
+	return;
+}
+#define INIT_FD()  test_init(argc, argv, __STRIPPED_FILE__)
+
+#endif /* _TESTS_H */
diff --git a/tests/testsctp.c b/tests/testsctp.c
new file mode 100644
index 0000000..46453c8
--- /dev/null
+++ b/tests/testsctp.c
@@ -0,0 +1,144 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@freediameter.net>							 *
+*													 *
+* Copyright (c) 2013, 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"
+
+#include <cnxctx.h>
+
+#ifndef TEST_PORT
+#define TEST_PORT	3868
+#endif /* TEST_PORT */
+
+#ifndef NB_STREAMS
+#define NB_STREAMS	10
+#endif /* NB_STREAMS */
+
+
+
+/* Main test routine */
+int main(int argc, char *argv[])
+{
+#ifdef DISABLE_SCTP
+	INIT_FD();
+	/* In this case, we don't perform this simple test */
+	PASSTEST();
+#else /* DISABLE_SCTP */
+	struct cnxctx cli, srv; /* we use only their cc_socket & cc_state */
+	int sock;
+	char buf1[]="abcdef";
+	char *buf2;
+	size_t sz;
+	struct iovec iov;
+	struct fd_list eps = FD_LIST_INITIALIZER(eps);
+	uint16_t str;
+	int ev;
+	
+	/* Initialize the server addresses */
+	{
+		struct addrinfo hints, *ai, *aip;
+		memset(&hints, 0, sizeof(hints));
+		hints.ai_flags  = AI_NUMERICSERV;
+		hints.ai_family = AF_INET;
+		CHECK( 0, getaddrinfo("localhost", _stringize(TEST_PORT), &hints, &ai) );
+		aip = ai;
+		while (aip) {
+			CHECK( 0, fd_ep_add_merge( &eps, aip->ai_addr, aip->ai_addrlen, EP_FL_DISC | EP_ACCEPTALL ));
+			aip = aip->ai_next;
+		};
+		freeaddrinfo(ai);
+		
+		CHECK( 0, FD_IS_LIST_EMPTY(&eps) ? 1 : 0 );
+	}
+	
+	memset(&cli, 0, sizeof(cli));
+	memset(&srv, 0, sizeof(srv));
+	
+	/* First, initialize the daemon modules */
+	INIT_FD();
+	
+	/* Restrain the # of streams */
+	fd_g_config->cnf_sctp_str = NB_STREAMS;
+	
+	/* Create the server socket */
+	CHECK( 0, fd_sctp_create_bind_server( &sock, AF_INET6, &eps, TEST_PORT ));
+	
+	/* Accept incoming clients */
+	CHECK( 0, fd_sctp_listen( sock ));
+	
+	/* Now, create the client socket */
+	CHECK( 0, fd_sctp_client( &cli.cc_socket, 0, TEST_PORT, &eps ));
+	
+	/* Accept this connection */
+	srv.cc_socket = accept(sock, NULL, NULL);
+	
+	/* Send a first message */
+	iov.iov_base = buf1;
+	iov.iov_len = sizeof(buf1);
+	CHECK( sizeof(buf1), fd_sctp_sendstrv(&srv, 1, &iov, 1 ) );
+	CHECK( 0, srv.cc_state);
+	
+	/* Receive this message */
+redo1:
+	CHECK( 0, fd_sctp_recvmeta(&cli, &str, (uint8_t **)&buf2, &sz, &ev) );
+	if (ev == FDEVP_CNX_EP_CHANGE)
+		goto redo1;
+	CHECK( FDEVP_CNX_MSG_RECV, ev);
+	CHECK( 0, cli.cc_state);
+	CHECK( 1, str);
+	CHECK( sizeof(buf1), sz );
+	CHECK( 0, memcmp(buf1, buf2, sz) );
+	free(buf2); buf2 = NULL;
+	
+	/* Send in the other direction */
+	CHECK( sizeof(buf1), fd_sctp_sendstrv(&cli, 2, &iov, 1) );
+	CHECK( 0, cli.cc_state);
+	
+	/* Receive this message */
+redo2:
+	CHECK( 0, fd_sctp_recvmeta(&srv, &str, (uint8_t **)&buf2, &sz, &ev) );
+	if (ev == FDEVP_CNX_EP_CHANGE)
+		goto redo2;
+	CHECK( FDEVP_CNX_MSG_RECV, ev);
+	CHECK( 0, srv.cc_state);
+	CHECK( 2, str);
+	CHECK( sizeof(buf1), sz );
+	CHECK( 0, memcmp(buf1, buf2, sz) );
+	free(buf2); buf2 = NULL;
+	
+	/* That's all for the tests yet */
+	PASSTEST();
+#endif /* DISABLE_SCTP */
+} 
+	
diff --git a/tests/testsess.c b/tests/testsess.c
new file mode 100644
index 0000000..5acc336
--- /dev/null
+++ b/tests/testsess.c
@@ -0,0 +1,400 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@freediameter.net>							 *
+*													 *
+* Copyright (c) 2013, 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_IN	"suffix"
+#define TEST_OPT	(os0_t)TEST_OPT_IN
+#define TEST_SID_IN	TEST_DIAM_ID ";1234;5678;" TEST_OPT_IN
+#define TEST_SID	(os0_t)TEST_SID_IN
+
+#define TEST_EYEC	0x7e57e1ec
+struct sess_state {
+	int	eyec;	/* TEST_EYEC */
+	os0_t   sid; 	/* the session with which the data was registered */
+	int  *  freed;	/* location where to write the freed status */
+	void *  opaque; /* if opaque was provided, this is the value we expect */
+};
+
+static void mycleanup( struct sess_state * data, os0_t sid, void * opaque )
+{
+	/* sanity */
+	CHECK( 1, sid ? 1 : 0 );
+	CHECK( 1, data? 1 : 0 );
+	CHECK( TEST_EYEC, data->eyec );
+	CHECK( 0, strcmp((char *)sid, (char *)data->sid) );
+	if (data->freed)
+		*(data->freed) += 1;
+	if (data->opaque) {
+		CHECK( 1, opaque == data->opaque ? 1 : 0 );  
+	}
+	/* Now, free the data */
+	free(data->sid);
+	free(data);
+}
+
+static __inline__ struct sess_state * new_state(os0_t sid, int *freed) 
+{
+	struct sess_state *new;
+	new = malloc(sizeof(struct sess_state));
+	CHECK( 1, new ? 1 : 0 );
+	memset(new, 0, sizeof(struct sess_state));
+	new->eyec = TEST_EYEC;
+	new->sid = os0dup(sid, strlen((char *)sid));
+	CHECK( 1, new->sid ? 1 : 0 );
+	new->freed = freed;
+	return new;
+}
+
+void * g_opaque = (void *)"test";
+
+/* Avoid a lot of casts */
+#undef strlen
+#define strlen(s) strlen((char *)s)
+#undef strncmp
+#define strncmp(s1,s2,l) strncmp((char *)s1, (char *)s2, l)
+#undef strcmp
+#define strcmp(s1,s2) strcmp((char *)s1, (char *)s2)
+	
+
+/* Main test routine */
+int main(int argc, char *argv[])
+{
+	struct session_handler * hdl1, *hdl2;
+	struct session *sess1, *sess2, *sess3;
+	os0_t str1, str2;
+	size_t str1len, str2len;
+	int new;
+	
+	/* First, initialize the daemon modules */
+	INIT_FD();
+	
+	/* Test functions related to handlers (simple situation) */
+	{
+		void * testptr = NULL;
+		CHECK( 0, fd_sess_handler_create ( &hdl1, mycleanup, NULL, NULL ) );
+		CHECK( 0, fd_sess_handler_create ( &hdl2, mycleanup, NULL, NULL ) );
+		CHECK( 0, fd_sess_handler_destroy( &hdl2, &testptr ) );
+		CHECK( 1, testptr == NULL ? 1 : 0 );
+		CHECK( 0, fd_sess_handler_create ( &hdl2, mycleanup, NULL, g_opaque ) );
+		#if 0
+		fd_log_debug("%s", fd_sess_dump_hdl(FD_DUMP_TEST_PARAMS, hdl1));
+		fd_log_debug("%s", fd_sess_dump_hdl(FD_DUMP_TEST_PARAMS, hdl2));
+		#endif
+	}
+	
+	/* Test Session Id generation (fd_sess_new) */
+	{
+		/* DiamId is provided, not opt */
+		CHECK( 0, fd_sess_new( &sess1, TEST_DIAM_ID, CONSTSTRLEN(TEST_DIAM_ID), NULL, 0 ) );
+		CHECK( 0, fd_sess_new( &sess2, TEST_DIAM_ID, CONSTSTRLEN(TEST_DIAM_ID), NULL, 0 ) );
+		#if 0
+		fd_log_debug("%s", fd_sess_dump(FD_DUMP_TEST_PARAMS, sess1, 1));
+		fd_log_debug("%s", fd_sess_dump(FD_DUMP_TEST_PARAMS, sess2, 1));
+		#endif
+		
+		/* Check both string start with the diameter Id, but are different */
+		CHECK( 0, fd_sess_getsid(sess1, &str1, &str1len) );
+		CHECK( 1, (strlen(str1) == str1len) ? 1 : 0 );
+		CHECK( 0, strncmp(str1, TEST_DIAM_ID ";", CONSTSTRLEN(TEST_DIAM_ID) + 1) );
+		CHECK( 0, fd_sess_getsid(sess2, &str2, &str2len) );
+		CHECK( 0, strncmp(str2, TEST_DIAM_ID ";", CONSTSTRLEN(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, 0, TEST_OPT, 0 ) );
+		CHECK( 0, fd_sess_new( &sess2, TEST_DIAM_ID, CONSTSTRLEN(TEST_DIAM_ID), TEST_OPT, CONSTSTRLEN(TEST_OPT_IN) - 1 ) );
+		#if 0
+		fd_log_debug("%s", fd_sess_dump(FD_DUMP_TEST_PARAMS, sess1, 1));
+		fd_log_debug("%s", fd_sess_dump(FD_DUMP_TEST_PARAMS, sess2, 1));
+		#endif
+		
+		CHECK( 0, fd_sess_getsid(sess1, &str1, &str1len) );
+		CHECK( 0, strncmp(str1, TEST_DIAM_ID ";", CONSTSTRLEN(TEST_DIAM_ID) + 1) );
+		CHECK( 0, strcmp(str1 + str1len - CONSTSTRLEN(TEST_OPT_IN) - 1, ";" TEST_OPT_IN) );
+		
+		CHECK( 0, fd_sess_getsid(sess2, &str2, &str2len) );
+		CHECK( 0, strncmp(str2, TEST_DIAM_ID ";", CONSTSTRLEN(TEST_DIAM_ID) + 1) );
+		CHECK( 0, strncmp(str2 + str2len - CONSTSTRLEN(TEST_OPT_IN), ";" TEST_OPT_IN, CONSTSTRLEN(TEST_OPT_IN)) );
+		
+		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, 0, TEST_SID, 0 ) );
+		CHECK( EALREADY, fd_sess_new( &sess2, NULL, 0, TEST_SID, 0 ) );
+		CHECK( sess2, sess1 );
+		CHECK( EALREADY, fd_sess_new( &sess3, NULL, 0, TEST_SID, CONSTSTRLEN(TEST_SID_IN) ) );
+		CHECK( sess3, sess1 );
+		CHECK( 0, fd_sess_new( &sess2, NULL, 0, TEST_SID, CONSTSTRLEN(TEST_SID_IN) - 1 ) );
+		#if 0
+		fd_log_debug("%s", fd_sess_dump(FD_DUMP_TEST_PARAMS, sess1, 1));
+		fd_log_debug("%s", fd_sess_dump(FD_DUMP_TEST_PARAMS, sess2, 1));
+		#endif
+		CHECK( 0, fd_sess_getsid(sess1, &str1, &str1len) );
+		CHECK( 0, fd_sess_getsid(sess2, &str2, &str2len) );
+		CHECK( 0, strncmp( str1, str2, CONSTSTRLEN(TEST_SID_IN) - 1 ) );
+		CHECK( 0, strcmp( str1, TEST_SID ) );
+		CHECK( 1, strcmp( str1, str2 ) ? 1 : 0 );
+		
+		CHECK( 0, fd_sess_destroy( &sess2 ) );
+		CHECK( 0, fd_sess_destroy( &sess1 ) );
+	}
+	
+	/* Test fd_sess_getcount */
+	{
+		uint32_t cnt;
+		CHECK( 0, fd_sess_new( &sess1, TEST_DIAM_ID, CONSTSTRLEN(TEST_DIAM_ID), NULL, 0 ) );
+		CHECK( 0, fd_sess_new( &sess2, TEST_DIAM_ID, CONSTSTRLEN(TEST_DIAM_ID), NULL, 0 ) );
+		CHECK( 0, fd_sess_getcount(&cnt));
+		CHECK( 2, cnt);
+		CHECK( 0, fd_sess_destroy( &sess2 ) );
+		CHECK( 0, fd_sess_getcount(&cnt));
+		CHECK( 1, cnt);
+		CHECK( 0, fd_sess_destroy( &sess1 ) );
+		CHECK( 0, fd_sess_getcount(&cnt));
+		CHECK( 0, cnt);
+		
+	}
+		
+	/* Test fd_sess_fromsid */
+	{
+		CHECK( 0, fd_sess_fromsid( TEST_SID, CONSTSTRLEN(TEST_SID_IN), &sess1, &new ) );
+		CHECK( 1, new ? 1 : 0 );
+		
+		CHECK( 0, fd_sess_fromsid( TEST_SID, CONSTSTRLEN(TEST_SID_IN), &sess2, &new ) );
+		CHECK( 0, new );
+		CHECK( sess1, sess2 );
+		
+		CHECK( 0, fd_sess_fromsid( TEST_SID, CONSTSTRLEN(TEST_SID_IN), &sess3, NULL ) );
+		CHECK( sess1, sess3 );
+		
+		CHECK( 0, fd_sess_destroy( &sess1 ) );
+	}
+	
+	/* Test fd_sess_reclaim */
+	{
+		struct sess_state *tms;
+		
+		CHECK( 0, fd_sess_fromsid( TEST_SID, CONSTSTRLEN(TEST_SID_IN), &sess1, &new ) );
+		CHECK( 1, new ? 1 : 0 );
+		
+		CHECK( 0, fd_sess_reclaim( &sess1 ) );
+		CHECK( NULL, sess1 );
+		
+		CHECK( 0, fd_sess_fromsid( TEST_SID, CONSTSTRLEN(TEST_SID_IN), &sess1, &new ) );
+		CHECK( 1, new ? 1 : 0 );
+		
+		tms = new_state(TEST_SID, NULL);
+		CHECK( 0, fd_sess_state_store ( hdl1, sess1, &tms ) );
+		
+		CHECK( 0, fd_sess_reclaim( &sess1 ) );
+		CHECK( NULL, sess1 );
+		
+		CHECK( 0, fd_sess_fromsid( TEST_SID, CONSTSTRLEN(TEST_SID_IN), &sess1, &new ) );
+		CHECK( 0, new );
+		
+		CHECK( 0, fd_sess_destroy( &sess1 ) );
+		
+		CHECK( 0, fd_sess_fromsid( TEST_SID, CONSTSTRLEN(TEST_SID_IN), &sess1, &new ) );
+		CHECK( 1, new ? 1 : 0 );
+		
+		CHECK( 0, fd_sess_destroy( &sess1 ) );
+	}
+	
+	/* Test timeout function */
+	{
+		struct timespec timeout;
+		
+		CHECK( 0, fd_sess_fromsid( TEST_SID, CONSTSTRLEN(TEST_SID_IN), &sess1, &new ) );
+		CHECK( 1, new ? 1 : 0 );
+		
+		CHECK( 0, clock_gettime(CLOCK_REALTIME, &timeout) );
+		CHECK( 0, fd_sess_settimeout( sess1, &timeout) ); /* expire now */
+		timeout.tv_sec = 0;
+		timeout.tv_nsec= 50000000; /* 50 ms */
+		CHECK( 0, nanosleep(&timeout, NULL) );
+		
+		CHECK( 0, fd_sess_fromsid( TEST_SID, CONSTSTRLEN(TEST_SID_IN), &sess1, &new ) );
+		CHECK( 1, new ? 1 : 0 );
+		
+		CHECK( 0, clock_gettime(CLOCK_REALTIME, &timeout) );
+		timeout.tv_sec += 2678500; /* longer that SESS_DEFAULT_LIFETIME */
+		CHECK( 0, fd_sess_settimeout( sess1, &timeout) );
+		
+		/* Create a second session */
+		CHECK( 0, fd_sess_new( &sess2, TEST_DIAM_ID, 0, NULL, 0 ) );
+		
+		/* We don't really have a way to verify the expiry list is in proper order automatically here... */
+		
+		CHECK( 0, fd_sess_destroy( &sess2 ) );
+		CHECK( 0, fd_sess_destroy( &sess1 ) );
+	}
+	
+	
+	/* Test states operations */
+	{
+		struct sess_state * ms[6], *tms;
+		int freed[6];
+		struct timespec timeout;
+		void * testptr = NULL;
+		
+		/* Create three sessions */
+		CHECK( 0, fd_sess_new( &sess1, TEST_DIAM_ID, 0, NULL, 0 ) );
+		CHECK( 0, fd_sess_new( &sess2, TEST_DIAM_ID, 0, NULL, 0 ) );
+		CHECK( 0, fd_sess_new( &sess3, TEST_DIAM_ID, 0, NULL, 0 ) );
+		
+		/* Create 2 states */
+		CHECK( 0, fd_sess_getsid(sess1, &str1, &str1len) );
+		freed[0] = 0;
+		ms[0] = new_state(str1, &freed[0]);
+		ms[1] = new_state(str1, NULL);
+
+		tms = ms[0]; /* save a copy */
+		CHECK( 0, fd_sess_state_store ( hdl1, sess1, &ms[0] ) );
+		CHECK( NULL, ms[0] );
+		CHECK( EINVAL, fd_sess_state_store ( hdl1, sess1, NULL ) );
+		CHECK( EALREADY, fd_sess_state_store ( hdl1, sess1, &ms[1] ) );
+		CHECK( 1, ms[1] ? 1 : 0 );
+		
+		#if 0
+		fd_log_debug("%s", fd_sess_dump(FD_DUMP_TEST_PARAMS, sess1, 1));
+		#endif
+		
+		CHECK( 0, fd_sess_state_retrieve( hdl1, sess1, &ms[0] ) );
+		CHECK( tms, ms[0] );
+		CHECK( 0, freed[0] );
+		
+		CHECK( 0, fd_sess_state_retrieve( hdl1, sess2, &tms ) );
+		CHECK( NULL, tms );
+		
+		mycleanup(ms[0], str1, NULL);
+		mycleanup(ms[1], str1, NULL);
+		
+		/* Now create 6 states */
+		memset(&freed[0], 0, sizeof(freed));
+		CHECK( 0, fd_sess_getsid(sess1, &str1, &str1len) );
+		ms[0] = new_state(str1, &freed[0]);
+		ms[1] = new_state(str1, &freed[1]);
+		CHECK( 0, fd_sess_getsid(sess2, &str1, &str1len) );
+		ms[2] = new_state(str1, &freed[2]);
+		ms[3] = new_state(str1, &freed[3]);
+		CHECK( 0, fd_sess_getsid(sess3, &str1, &str1len) );
+		ms[4] = new_state(str1, &freed[4]);
+		ms[5] = new_state(str1, &freed[5]);
+		ms[5]->opaque = g_opaque;
+		str2 = os0dup(str1, str1len);
+		CHECK( 1, str2 ? 1 : 0 );
+		
+		/* Store the six states */
+		CHECK( 0, fd_sess_state_store ( hdl1, sess1, &ms[0] ) );
+		CHECK( 0, fd_sess_state_store ( hdl2, sess1, &ms[1] ) );
+		CHECK( 0, fd_sess_state_store ( hdl1, sess2, &ms[2] ) );
+		CHECK( 0, fd_sess_state_store ( hdl2, sess2, &ms[3] ) );
+		CHECK( 0, fd_sess_state_store ( hdl1, sess3, &ms[4] ) );
+		CHECK( 0, fd_sess_state_store ( hdl2, sess3, &ms[5] ) );
+		
+		#if 0
+		fd_log_debug("%s", fd_sess_dump(FD_DUMP_TEST_PARAMS, sess1, 1));
+		fd_log_debug("%s", fd_sess_dump(FD_DUMP_TEST_PARAMS, sess2, 1));
+		fd_log_debug("%s", fd_sess_dump(FD_DUMP_TEST_PARAMS, sess3, 1));
+		#endif
+		
+		/* Destroy session 3 */
+		CHECK( 0, fd_sess_destroy( &sess3 ) );
+		CHECK( 0, freed[0] );
+		CHECK( 0, freed[1] );
+		CHECK( 0, freed[2] );
+		CHECK( 0, freed[3] );
+		CHECK( 1, freed[4] );
+		CHECK( 1, freed[5] );
+		
+		/* Destroy handler 2 */
+		CHECK( 0, fd_sess_handler_destroy( &hdl2, &testptr ) );
+		CHECK( 0, freed[0] );
+		CHECK( 1, freed[1] );
+		CHECK( 0, freed[2] );
+		CHECK( 1, freed[3] );
+		CHECK( 1, freed[4] );
+		CHECK( 1, freed[5] );
+		CHECK( 1, testptr == g_opaque ? 1 : 0 );
+		
+		#if 1
+		fd_log_debug("%s", fd_sess_dump(FD_DUMP_TEST_PARAMS, sess1, 1));
+		fd_log_debug("%s", fd_sess_dump(FD_DUMP_TEST_PARAMS, sess2, 1));
+		#endif
+		
+		/* Create again session 3, check that no data is associated to it */
+		CHECK( 0, fd_sess_fromsid( str2, str1len, &sess3, &new ) );
+		CHECK( 1, new ? 1 : 0 );
+		CHECK( 0, fd_sess_state_retrieve( hdl1, sess3, &tms ) );
+		CHECK( NULL, tms );
+		CHECK( 0, fd_sess_destroy( &sess3 ) );
+		free(str2);
+		
+		/* Timeout does call cleanups */
+		CHECK( 0, clock_gettime(CLOCK_REALTIME, &timeout) );
+		CHECK( 0, fd_sess_settimeout( sess2, &timeout) );
+		#if 1
+		fd_log_debug("%s", fd_sess_dump(FD_DUMP_TEST_PARAMS, sess1, 1));
+		fd_log_debug("%s", fd_sess_dump(FD_DUMP_TEST_PARAMS, sess2, 1));
+		#endif
+		timeout.tv_sec = 0;
+		timeout.tv_nsec= 50000000; /* 50 ms */
+		CHECK( 0, nanosleep(&timeout, NULL) );
+		CHECK( 0, freed[0] );
+		CHECK( 1, freed[1] );
+		CHECK( 1, freed[2] );
+		CHECK( 1, freed[3] );
+		CHECK( 1, freed[4] );
+		CHECK( 1, freed[5] );
+		
+		/* Check the last data can still be retrieved */
+		CHECK( 0, fd_sess_state_retrieve( hdl1, sess1, &tms ) );
+		CHECK( 1, tms ? 1 : 0);
+		CHECK( 0, fd_sess_getsid(sess1, &str1, &str1len) );
+		mycleanup(tms, str1, NULL);
+	}
+	
+	/* TODO: add tests on messages referencing sessions */
+	
+	/* That's all for the tests yet */
+	PASSTEST();
+} 
+