Index: .hgtags
===================================================================
--- .hgtags	(revision 657)
+++ .hgtags	(revision 644)
@@ -11,3 +11,2 @@
 69d3579f6c6cd3b16daeedc60ca94ca6056a3748 1.0.3-rc3
 f065e3cb846ee557b13279b99272032a4ad913a9 1.0.3
-5b05d85682f1a23fcd0ecfc87649a65b841e7ca8 1.0.x-last
Index: CMakeLists.txt
===================================================================
--- CMakeLists.txt	(revision 647)
+++ CMakeLists.txt	(revision 658)
@@ -11,9 +11,9 @@
 # Version of the source code
 SET(FD_PROJECT_VERSION_MAJOR 1)
-SET(FD_PROJECT_VERSION_MINOR 0)
-SET(FD_PROJECT_VERSION_REV 4)
+SET(FD_PROJECT_VERSION_MINOR 1)
+SET(FD_PROJECT_VERSION_REV 0)
 
 # Version of the API with the library
-SET(FD_PROJECT_VERSION_API 3)
+SET(FD_PROJECT_VERSION_API 4)
 
 # The test framework, using CTest and CDash.
@@ -31,5 +31,5 @@
 SET(INSTALL_HEADERS_SUFFIX 		include/freeDiameter 	CACHE PATH "Directory where the headers are installed (relative to CMAKE_INSTALL_PREFIX).")
 SET(INSTALL_DAEMON_SUFFIX 		bin 			CACHE PATH "Directory where the daemon binary is installed (relative to CMAKE_INSTALL_PREFIX).")
-SET(INSTALL_LIBRARY_SUFFIX 		lib 			CACHE PATH "Directory where the libfreeDiameter library is installed (relative to CMAKE_INSTALL_PREFIX).")
+SET(INSTALL_LIBRARY_SUFFIX 		lib 			CACHE PATH "Directory where the freeDiameter libraries are installed (relative to CMAKE_INSTALL_PREFIX).")
 SET(INSTALL_EXTENSIONS_SUFFIX 	${INSTALL_LIBRARY_SUFFIX}/freeDiameter	CACHE PATH "Directory where the extensions are installed / searched (relative to CMAKE_INSTALL_PREFIX).")
 
@@ -77,7 +77,14 @@
 
 # Location for the source code
-SUBDIRS(libfreeDiameter)
-SUBDIRS(freeDiameter)
+SUBDIRS(libfdproto)
+SUBDIRS(libfdcore)
+SUBDIRS(freeDiameterd)
 
 # Extensions (there is no use of freeDiameter without any extension)
 SUBDIRS(extensions)
+
+# The unary tests directory
+IF ( BUILD_TESTING )
+	SUBDIRS(tests)
+ENDIF ( BUILD_TESTING )
+
Index: contrib/debian/control
===================================================================
--- contrib/debian/control	(revision 631)
+++ contrib/debian/control	(revision 658)
@@ -13,6 +13,6 @@
 Architecture: any
 Depends: ${shlibs:Depends}, ${misc:Depends}
-Description: The libfreeDiameter library.
- This library is required by all freeDiameter components.
+Description: The freeDiameter libraries.
+ This package contains the libraries required by all freeDiameter components.
  It may also be useful for other projects which need to 
  perform operations on Diameter messages. 
Index: contrib/debian/freediameter-common.install
===================================================================
--- contrib/debian/freediameter-common.install	(revision 328)
+++ contrib/debian/freediameter-common.install	(revision 658)
@@ -1,1 +1,2 @@
-usr/lib/libfreeDiameter.so*
+usr/lib/libfdproto.so*
+usr/lib/libfdcore.so*
Index: extensions/dbg_interactive/CMakeLists.txt
===================================================================
--- extensions/dbg_interactive/CMakeLists.txt	(revision 641)
+++ extensions/dbg_interactive/CMakeLists.txt	(revision 658)
@@ -16,6 +16,6 @@
 SET(SWIG_MODULE_fDpy_EXTRA_DEPS 
 	${CMAKE_BINARY_DIR}/include/freeDiameter/freeDiameter-host.h 
-	${CMAKE_SOURCE_DIR}/include/freeDiameter/libfreeDiameter.h 
-	${CMAKE_SOURCE_DIR}/include/freeDiameter/freeDiameter.h
+	${CMAKE_SOURCE_DIR}/include/freeDiameter/libfdcore.h 
+	${CMAKE_SOURCE_DIR}/include/freeDiameter/libfdproto.h 
 	lists.i
 	dictionary.i
Index: extensions/dbg_interactive/dbg_interactive.c
===================================================================
--- extensions/dbg_interactive/dbg_interactive.c	(revision 639)
+++ extensions/dbg_interactive/dbg_interactive.c	(revision 658)
@@ -59,5 +59,5 @@
 	fd_log_threadname ( "fDpy" );
 	
-	CHECK_FCT_DO(fd_wait_initialization_complete(), goto end);
+	CHECK_FCT_DO(fd_core_waitstartcomplete(), goto end);
 	
 	fd_log_debug("\nStarting interactive python interpreter [experimental].\n");
Index: extensions/dbg_interactive/dbg_interactive.i
===================================================================
--- extensions/dbg_interactive/dbg_interactive.i	(revision 641)
+++ extensions/dbg_interactive/dbg_interactive.i	(revision 658)
@@ -159,6 +159,6 @@
  *********************************************************/
 %include "freeDiameter/freeDiameter-host.h"
-%include "freeDiameter/libfreeDiameter.h"
-%include "freeDiameter/freeDiameter.h"
+%include "freeDiameter/libfdproto.h"
+%include "freeDiameter/libfdcore.h"
 
 /* Most of the functions from the API are not directly usable "as is".
Index: extensions/dbg_monitor/dbg_monitor.c
===================================================================
--- extensions/dbg_monitor/dbg_monitor.c	(revision 258)
+++ extensions/dbg_monitor/dbg_monitor.c	(revision 658)
@@ -78,5 +78,5 @@
 
 /* Function called on receipt of MONITOR_SIGNAL */
-static void got_sig(int signal)
+static void got_sig()
 {
 	fd_log_debug("[dbg_monitor] Dumping extra information\n");
@@ -92,5 +92,5 @@
 	
 	/* Catch signal SIGUSR1 */
-	CHECK_FCT( fd_sig_register(MONITOR_SIGNAL, "dbg_monitor", got_sig));
+	CHECK_FCT( fd_event_trig_regcb(MONITOR_SIGNAL, "dbg_monitor", got_sig));
 	
 	CHECK_POSIX( pthread_create( &thr, NULL, mn_thr, NULL ) );
Index: extensions/dict_eap/dict_eap.c
===================================================================
--- extensions/dict_eap/dict_eap.c	(revision 10)
+++ extensions/dict_eap/dict_eap.c	(revision 658)
@@ -296,5 +296,5 @@
 					268, 					/* Code */
 					"Diameter-EAP-Request", 		/* Name */
-					CMD_FLAG_REQUEST | CMD_FLAG_PROXIABLE | CMD_FLAG_RETRANSMIT | CMD_FLAG_ERROR, 	/* Fixed flags */
+					CMD_FLAG_REQUEST | CMD_FLAG_PROXIABLE | CMD_FLAG_ERROR, /* Fixed flags */
 					CMD_FLAG_REQUEST | CMD_FLAG_PROXIABLE 			/* Fixed flag values */
 					};
Index: extensions/dict_legacy_xml/dict_lxml_xml.c
===================================================================
--- extensions/dict_legacy_xml/dict_lxml_xml.c	(revision 616)
+++ extensions/dict_legacy_xml/dict_lxml_xml.c	(revision 658)
@@ -933,4 +933,11 @@
 					{ TRACE_DEBUG(INFO, "Invalid 'type' tag found without 'name' attribute."); goto xml_tree_error; } );
 				
+				/* Check there is only 1 type */
+				if (!FD_IS_LIST_EMPTY(&data->cur_avp->type)) {
+					TRACE_DEBUG(INFO, "Multiple 'type' tags found for AVP.");
+					goto xml_tree_error;
+				}
+				
+				/* Add the new type */
 				CHECK_FCT_DO( new_avptype(&data->cur_avp->type, xname),
 					{ TRACE_DEBUG(INFO, "An error occurred while parsing a type tag. Entry ignored."); goto xml_tree_error; } )
@@ -1011,4 +1018,13 @@
 	}
 	data->error_depth += 1;
+	if (data->cur_app || data->cur_cmd || data->cur_avp) {
+		TRACE_DEBUG(INFO, "Error encountered while parsing tag of:");
+		if (data->cur_app)
+			fd_log_debug("  Application: '%s'\n", data->cur_app->name);
+		if (data->cur_cmd)
+			fd_log_debug("  Command    : '%s'\n", data->cur_cmd->name);
+		if (data->cur_avp)
+			fd_log_debug("  AVP        : '%s'\n", data->cur_avp->name);
+	}
 	return;
 }
@@ -1387,5 +1403,6 @@
 		
 		/* Now create the new rule */
-		CHECK_FCT( fd_dict_new ( fD_dict, DICT_RULE, &rd, parent, NULL ) );
+		CHECK_FCT_DO( ret = fd_dict_new ( fD_dict, DICT_RULE, &rd, parent, NULL ),
+			{ TRACE_DEBUG(INFO, "Error creating rule for sub-AVP '%s'", r->avpname); return ret; } );
 		if (nb_added)
 			*nb_added += 1;
@@ -1398,10 +1415,15 @@
 static int rules_to_fD(struct dictionary * fD_dict, struct dict_object * parent, struct fd_list * fixed, struct fd_list * required, struct fd_list * optional, int * nb_added)
 {
+	int ret;
+	
 	TRACE_ENTRY("%p %p %p %p %p %p", fD_dict, parent, fixed, required, optional, nb_added);
 	
 	/* Process the rules */
-	CHECK_FCT( rules_to_fD_onelist(fD_dict, parent, RULE_FIXED_HEAD, fixed, nb_added) );
-	CHECK_FCT( rules_to_fD_onelist(fD_dict, parent, RULE_REQUIRED, required, nb_added) );
-	CHECK_FCT( rules_to_fD_onelist(fD_dict, parent, RULE_OPTIONAL, optional, nb_added) );
+	CHECK_FCT_DO( ret = rules_to_fD_onelist(fD_dict, parent, RULE_FIXED_HEAD, fixed, nb_added),
+		{ TRACE_DEBUG(INFO, "Error processing FIXED rules"); return ret; } );
+	CHECK_FCT_DO( ret = rules_to_fD_onelist(fD_dict, parent, RULE_REQUIRED, required, nb_added),
+		{ TRACE_DEBUG(INFO, "Error processing REQUIRED rules"); return ret; } );
+	CHECK_FCT_DO( ret = rules_to_fD_onelist(fD_dict, parent, RULE_OPTIONAL, optional, nb_added),
+		{ TRACE_DEBUG(INFO, "Error processing OPTIONAL rules"); return ret; } );
 	
 	return 0;
@@ -1425,10 +1447,13 @@
 	ad.avp_vendor = a->vendor;
 	ad.avp_name   = (char *)a->name;
-	ad.avp_flag_mask = a->fmask;
+	ad.avp_flag_mask = a->fmask | AVP_FLAG_VENDOR;
 	ad.avp_flag_val  = a->flags;
 	
 	if (!FD_IS_LIST_EMPTY(&a->type)) {
 		/* special exception: we use per-AVP enumerated types in fD */
-		if (strcasecmp("Enumerated", (char *)((struct t_avptype *)a->type.next)->type_name))
+		if (!strcasecmp("Enumerated", (char *)((struct t_avptype *)a->type.next)->type_name))
+			goto enumerated;
+		/* Let's allow "Unsigned32" instead of "Enumerated" also... */
+		if ((!FD_IS_LIST_EMPTY(&a->enums)) && (!strcasecmp("Unsigned32", (char *)((struct t_avptype *)a->type.next)->type_name)))
 			goto enumerated;
 		
@@ -1441,10 +1466,11 @@
 			|| !FD_IS_LIST_EMPTY(&a->grouped_fixed) ) {
 			/* The AVP has rules, it is a grouped AVP */
-			CHECK_PARAMS( FD_IS_LIST_EMPTY(&a->enums) );
+			CHECK_PARAMS_DO( FD_IS_LIST_EMPTY(&a->enums), 
+				{ TRACE_DEBUG(INFO, "Conflict: The AVP '%s' has both enum values and rules.", ad.avp_name); return EINVAL; } );
 			ad.avp_basetype = AVP_TYPE_GROUPED;
 		} else {
 			/* It should be an enumerated AVP... */
 			if (FD_IS_LIST_EMPTY(&a->enums)) {
-				TRACE_DEBUG(INFO, "[dict_legacy_xml] Error: Missing type information for AVP '%s'", ad.avp_name);
+				TRACE_DEBUG(INFO, "Error: Missing type information for AVP '%s'", ad.avp_name);
 				return EINVAL;
 			} else {
@@ -1492,6 +1518,10 @@
 	/* Now, the inner elements, if any */
 	
+	if ( (!FD_IS_LIST_EMPTY(&a->enums)) && (ad.avp_basetype != AVP_TYPE_UNSIGNED32)) {
+		TRACE_DEBUG(INFO, "AVP '%s' type is not an Unsigned32 but it has enum values (invalid in this extension).", ad.avp_name);
+		return EINVAL;
+	}
+
 	/* In case of enumeration, define the enum values */
-	ASSERT( FD_IS_LIST_EMPTY(&a->enums) || (type && (ad.avp_basetype == AVP_TYPE_UNSIGNED32)) ); /* u32 type must be defined for enumerators */
 	for (li = a->enums.next; li != &a->enums; li = li->next) {
 		struct t_enum * e = (struct t_enum *)li;
@@ -1502,5 +1532,9 @@
 		ed.enum_value.u32 = e->code;
 		
-		CHECK_FCT( fd_dict_new ( fD_dict, DICT_ENUMVAL, &ed, type, NULL ) );
+		CHECK_FCT_DO( ret = fd_dict_new ( fD_dict, DICT_ENUMVAL, &ed, type, NULL ),
+			{
+				TRACE_DEBUG(INFO, "Error defining constant value '%s' for AVP '%s': %s", ed.enum_name, ad.avp_name, strerror(ret));
+				return ret;
+			} );
 		if (nb_added)
 			*nb_added += 1;
@@ -1511,6 +1545,8 @@
 	  || !FD_IS_LIST_EMPTY(&a->grouped_required)
 	  || !FD_IS_LIST_EMPTY(&a->grouped_fixed) ) {
-		CHECK_PARAMS( ad.avp_basetype == AVP_TYPE_GROUPED );
-		CHECK_FCT( rules_to_fD(fD_dict, prev, &a->grouped_fixed, &a->grouped_required, &a->grouped_optional, nb_added) );
+		CHECK_PARAMS_DO( ad.avp_basetype == AVP_TYPE_GROUPED, 
+			{ TRACE_DEBUG(INFO, "Got rules for non-grouped AVP '%s'", ad.avp_name); return EINVAL;} );
+		CHECK_FCT_DO( ret = rules_to_fD(fD_dict, prev, &a->grouped_fixed, &a->grouped_required, &a->grouped_optional, nb_added),
+			{ TRACE_DEBUG(INFO, "Error processing rules for AVP '%s': %s", ad.avp_name, strerror(ret)); return ret; } );
 	}
 	
@@ -1536,5 +1572,5 @@
 	snprintf(cmdname, sizeof(cmdname), "%s-Request", (char *)c->name);
 	cd.cmd_name = &cmdname[0];
-	cd.cmd_flag_mask = c->fmask | CMD_FLAG_REQUEST;
+	cd.cmd_flag_mask = c->fmask | CMD_FLAG_REQUEST | CMD_FLAG_ERROR;
 	cd.cmd_flag_val  = c->flags | CMD_FLAG_REQUEST;
 	
@@ -1564,4 +1600,5 @@
 	snprintf(cmdname, sizeof(cmdname), "%s-Answer", (char *)c->name);
 	cd.cmd_flag_val &= ~CMD_FLAG_REQUEST;
+	cd.cmd_flag_mask &= ~CMD_FLAG_ERROR;
 	
 	ret = fd_dict_new ( fD_dict, DICT_COMMAND, &cd, fd_appl, &ans );
@@ -1585,6 +1622,14 @@
 rules:
 	/* Now process the rules inside the command */
-	CHECK_FCT( rules_to_fD(fD_dict, req, &c->reqrules_fixed, &c->reqrules_required, &c->reqrules_optional, nb_added) );
-	CHECK_FCT( rules_to_fD(fD_dict, ans, &c->ansrules_fixed, &c->ansrules_required, &c->ansrules_optional, nb_added) );
+	CHECK_FCT_DO( ret = rules_to_fD(fD_dict, req, &c->reqrules_fixed, &c->reqrules_required, &c->reqrules_optional, nb_added),
+			{
+				TRACE_DEBUG(INFO, "Error converting data from request rules: %s", strerror(ret));
+				return ret;
+			}   );
+	CHECK_FCT_DO( ret = rules_to_fD(fD_dict, ans, &c->ansrules_fixed, &c->ansrules_required, &c->ansrules_optional, nb_added),
+			{
+				TRACE_DEBUG(INFO, "Error converting data from answer rules: %s", strerror(ret));
+				return ret;
+			}   );
 	
 	/* Done */
@@ -1641,15 +1686,27 @@
 	/* First, define all the types */
 	for (li = a->types.next; li != &a->types; li = li->next) {
-		CHECK_FCT( typdefn_to_fD((struct t_typedefn *)li, fD_dict, prev, NULL, nb_added) );
+		CHECK_FCT_DO( ret = typdefn_to_fD((struct t_typedefn *)li, fD_dict, prev, NULL, nb_added),
+			{
+				TRACE_DEBUG(INFO, "Error converting data from typedefn '%s': %s", ((struct t_typedefn *)li)->name, strerror(ret));
+				return ret;
+			}  );
 	}
 	
 	/* Then, AVPs, enums, and grouped AVP rules */
 	for (li = a->avps.next; li != &a->avps; li = li->next) {
-		CHECK_FCT( avp_to_fD((struct t_avp *)li, fD_dict, prev, NULL, nb_added) );
+		CHECK_FCT_DO( ret = avp_to_fD((struct t_avp *)li, fD_dict, prev, NULL, nb_added),
+			{
+				TRACE_DEBUG(INFO, "Error converting data from AVP '%s': %s", ((struct t_avp *)li)->name, strerror(ret));
+				return ret;
+			}  );
 	}
 	
 	/* Finally, the commands and rules */
 	for (li = a->commands.next; li != &a->commands; li = li->next) {
-		CHECK_FCT( cmd_to_fD((struct t_cmd *)li, fD_dict, prev, NULL, nb_added) );
+		CHECK_FCT_DO( ret = cmd_to_fD((struct t_cmd *)li, fD_dict, prev, NULL, nb_added),
+			{
+				TRACE_DEBUG(INFO, "Error converting data from command '%s': %s", ((struct t_cmd *)li)->name, strerror(ret));
+				return ret;
+			}  );
 	}
 	
@@ -1662,4 +1719,5 @@
 {
 	struct fd_list * li;
+	int ret;
 	
 	TRACE_ENTRY("%p %p %p", fD_dict, xmldict, nb_added);
@@ -1671,11 +1729,23 @@
 	/* Create all the vendors */
 	for (li = xmldict->vendors.next; li != &xmldict->vendors; li = li->next) {
-		CHECK_FCT( vend_to_fD((struct t_vend *)li, fD_dict, NULL, nb_added) );
+		CHECK_FCT_DO( ret = vend_to_fD((struct t_vend *)li, fD_dict, NULL, nb_added),
+			{
+				TRACE_DEBUG(INFO, "Error converting data from vendor '%s': %s", ((struct t_vend *)li)->name, strerror(ret));
+				return ret;
+			} );
 	}
 	
 	/* Now, process each application */
-	CHECK_FCT( appl_to_fD(&xmldict->base_and_applications, fD_dict, NULL, nb_added) );
+	CHECK_FCT_DO( ret = appl_to_fD(&xmldict->base_and_applications, fD_dict, NULL, nb_added),
+			{
+				TRACE_DEBUG(INFO, "Error converting data from Base application: %s", strerror(ret));
+				return ret;
+			} );
 	for (li = xmldict->base_and_applications.chain.next; li != &xmldict->base_and_applications.chain; li = li->next) {
-		CHECK_FCT( appl_to_fD((struct t_appl *) li, fD_dict, NULL, nb_added) );
+		CHECK_FCT_DO( ret = appl_to_fD((struct t_appl *) li, fD_dict, NULL, nb_added),
+			{
+				TRACE_DEBUG(INFO, "Error converting data from application '%s': %s", ((struct t_appl *)li)->name, strerror(ret));
+				return ret;
+			}  );
 	}
 	
@@ -1725,5 +1795,5 @@
 	}
 	
-	TRACE_DEBUG(FULL, "XML file parsing complete.");
+	TRACE_DEBUG(FULL, "XML file parsing, 1st pass completed.");
 	if (TRACE_BOOL(ANNOYING)) {
 		dump_dict(&data.dict);
@@ -1731,7 +1801,12 @@
 	
 	/* Now, convert all the objects from the temporary tree into the freeDiameter dictionary */
-	CHECK_FCT_DO( dict_to_fD(fd_g_config->cnf_dict, &data.dict, &ret), { del_dict_contents(&data.dict); return -1; } );
-	
-	TRACE_DEBUG(FULL, "Conversion to freeDiameter internal format complete.");
+	CHECK_FCT_DO( dict_to_fD(fd_g_config->cnf_dict, &data.dict, &ret), 
+		{ 
+			TRACE_DEBUG(INFO, "Error while converting data read from file '%s'", xmlfilename);
+			del_dict_contents(&data.dict); 
+			return -1; 
+		} );
+	
+	TRACE_DEBUG(FULL, "Conversion from '%s' to freeDiameter internal format complete.", xmlfilename);
 	if (TRACE_BOOL(ANNOYING)) {
 		fd_dict_dump(fd_g_config->cnf_dict);
Index: extensions/dict_nasreq/dict_nasreq.c
===================================================================
--- extensions/dict_nasreq/dict_nasreq.c	(revision 523)
+++ extensions/dict_nasreq/dict_nasreq.c	(revision 658)
@@ -3005,5 +3005,5 @@
 					265, 					/* Code */
 					"AA-Request", 				/* Name */
-					CMD_FLAG_REQUEST | CMD_FLAG_PROXIABLE | CMD_FLAG_RETRANSMIT | CMD_FLAG_ERROR, 	/* Fixed flags */
+					CMD_FLAG_REQUEST | CMD_FLAG_PROXIABLE | CMD_FLAG_ERROR, /* Fixed flags */
 					CMD_FLAG_REQUEST | CMD_FLAG_PROXIABLE 			/* Fixed flag values */
 					};
Index: extensions/test_app/ta_bench.c
===================================================================
--- extensions/test_app/ta_bench.c	(revision 575)
+++ extensions/test_app/ta_bench.c	(revision 658)
@@ -208,5 +208,5 @@
 
 /* The function called when the signal is received */
-static void ta_bench_start(int sig) {
+static void ta_bench_start() {
 	struct timespec end_time, now;
 	struct ta_stats start, end;
@@ -274,5 +274,5 @@
 	CHECK_SYS( sem_init( &ta_sem, 0, ta_conf->bench_concur) );
 
-	CHECK_FCT( fd_sig_register(ta_conf->signal, "test_app.bench", ta_bench_start ) );
+	CHECK_FCT( fd_event_trig_regcb(ta_conf->signal, "test_app.bench", ta_bench_start ) );
 	
 	return 0;
@@ -281,5 +281,5 @@
 void ta_bench_fini(void)
 {
-	CHECK_FCT_DO( fd_sig_unregister(ta_conf->signal), /* continue */ );
+	// CHECK_FCT_DO( fd_sig_unregister(ta_conf->signal), /* continue */ );
 	
 	CHECK_SYS_DO( sem_destroy(&ta_sem), );
Index: extensions/test_app/ta_cli.c
===================================================================
--- extensions/test_app/ta_cli.c	(revision 639)
+++ extensions/test_app/ta_cli.c	(revision 658)
@@ -136,5 +136,5 @@
 
 /* Create a test message */
-static void ta_cli_test_message(int sig)
+static void ta_cli_test_message()
 {
 	struct msg * req = NULL;
@@ -236,5 +236,5 @@
 	CHECK_FCT( fd_sess_handler_create(&ta_cli_reg, free, NULL) );
 	
-	CHECK_FCT( fd_sig_register(ta_conf->signal, "test_app.cli", ta_cli_test_message ) );
+	CHECK_FCT( fd_event_trig_regcb(ta_conf->signal, "test_app.cli", ta_cli_test_message ) );
 	
 	return 0;
@@ -243,5 +243,5 @@
 void ta_cli_fini(void)
 {
-	CHECK_FCT_DO( fd_sig_unregister(ta_conf->signal), /* continue */ );
+	// CHECK_FCT_DO( fd_sig_unregister(ta_conf->signal), /* continue */ );
 	
 	CHECK_FCT_DO( fd_sess_handler_destroy(&ta_cli_reg, NULL), /* continue */ );
Index: extensions/test_sip/test_sip.c
===================================================================
--- extensions/test_sip/test_sip.c	(revision 639)
+++ extensions/test_sip/test_sip.c	(revision 658)
@@ -215,7 +215,7 @@
 	
 	CHECK_FCT(fd_sess_handler_create(&ts_sess_hdl, free, NULL));
-	//CHECK_FCT( fd_sig_register(30, "test_sip", (void *)test_sipSL_LIR_cb ) );
-	CHECK_FCT( fd_sig_register(30, "test_sip", (void *)test_sip_SAR_cb ) );
-	CHECK_FCT( fd_sig_register(31, "test_sip", (void *)test_sip_LIR_cb ) );
+	//CHECK_FCT( fd_event_trig_regcb(30, "test_sip", (void *)test_sipSL_LIR_cb ) );
+	CHECK_FCT( fd_event_trig_regcb(30, "test_sip", (void *)test_sip_SAR_cb ) );
+	CHECK_FCT( fd_event_trig_regcb(31, "test_sip", (void *)test_sip_LIR_cb ) );
 	
 	return 0;
Index: eeDiameter/CMakeLists.txt
===================================================================
--- freeDiameter/CMakeLists.txt	(revision 592)
+++ 	(revision )
@@ -1,94 +1,0 @@
-# The subproject name
-Project("freeDiameterd" C)
-
-# Configuration parser
-BISON_FILE(fdd.y)
-FLEX_FILE(fdd.l)
-SET_SOURCE_FILES_PROPERTIES(lex.fdd.c fdd.tab.c PROPERTIES COMPILE_FLAGS "-I ${CMAKE_CURRENT_SOURCE_DIR}")
-
-# List of source files
-SET(FD_COMMON_SRC
-	fD.h
-	apps.c
-	cnxctx.h
-	config.c
-	cnxctx.c
-	endpoints.c
-	events.c
-	extensions.c
-	dict_base_proto.c
-	messages.c
-	queues.c
-	peers.c
-	p_ce.c
-	p_cnx.c
-	p_dw.c
-	p_dp.c
-	p_expiry.c
-	p_out.c
-	p_psm.c
-	p_sr.c
-	routing_dispatch.c
-	server.c
-	tcp.c
-	)
-
-IF(NOT DISABLE_SCTP)
-	SET(FD_COMMON_SRC ${FD_COMMON_SRC} sctp.c sctps.c)
-ENDIF(NOT DISABLE_SCTP)
-
-SET(FD_COMMON_GEN_SRC
-		lex.fdd.c
-		fdd.tab.c
-		fdd.tab.h
-	)
-	
-# Save the list of files for the tests 
-SET(FD_COMMON_SRC ${FD_COMMON_SRC} PARENT_SCOPE)
-SET(FD_COMMON_GEN_SRC ${FD_COMMON_GEN_SRC} PARENT_SCOPE)
-
-
-# Require GNU TLS for building the daemon
-FIND_PACKAGE(GnuTLS REQUIRED)
-INCLUDE_DIRECTORIES(${GNUTLS_INCLUDE_DIR})
-SET(FD_LIBS ${FD_LIBS} ${GNUTLS_LIBRARIES})
-find_path(GCRYPT_INCLUDE_DIR NAMES gcrypt.h)
-If ( NOT GCRYPT_INCLUDE_DIR )
-	MESSAGE(SEND_ERROR "Unable to find gcrypt.h, please install libgcrypt-dev or equivalent")
-Endif ( NOT GCRYPT_INCLUDE_DIR )
-MARK_AS_ADVANCED(GCRYPT_INCLUDE_DIR)
-INCLUDE_DIRECTORIES(${GCRYPT_INCLUDE_DIR})
-
-# Also we need libgcrypt to... display its version :(
-find_library(GCRYPT_LIBRARY 
-  NAMES gcrypt
-)
-If ( NOT GCRYPT_LIBRARY )
-	MESSAGE(SEND_ERROR "Unable to find libgcrypt, please install libgcrypt or equivalent")
-Endif ( NOT GCRYPT_LIBRARY )
-SET(FD_LIBS ${FD_LIBS} ${GCRYPT_LIBRARY})
-
-
-
-# Build the executable
-ADD_EXECUTABLE(freeDiameterd ${FD_COMMON_SRC} ${FD_COMMON_GEN_SRC} main.c)
-
-# The version
-SET_TARGET_PROPERTIES(freeDiameterd PROPERTIES 
-	VERSION ${FD_PROJECT_VERSION_MAJOR}.${FD_PROJECT_VERSION_MINOR}.${FD_PROJECT_VERSION_REV})
-
-# The link command
-LINK_DIRECTORIES(${CURRENT_BINARY_DIR}/../libfreeDiameter)
-TARGET_LINK_LIBRARIES(freeDiameterd libfreeDiameter ${FD_LIBS})
-
-# The unary tests directory
-IF ( BUILD_TESTING )
-	SUBDIRS(tests)
-ENDIF ( BUILD_TESTING )
-
-####
-## INSTALL section ##
-
-INSTALL(TARGETS freeDiameterd
-	RUNTIME DESTINATION ${INSTALL_DAEMON_SUFFIX}
-	COMPONENT freeDiameter-daemon)
Index: eeDiameter/apps.c
===================================================================
--- freeDiameter/apps.c	(revision 648)
+++ 	(revision )
@@ -1,152 +1,0 @@
-/*********************************************************************************************************
-* Software License Agreement (BSD License)                                                               *
-* Author: Sebastien Decugis <sdecugis@nict.go.jp>							 *
-*													 *
-* Copyright (c) 2010, 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 "fD.h"
-
-/* Merge information into a list of apps */
-int fd_app_merge(struct fd_list * list, application_id_t aid, vendor_id_t vid, int auth, int acct)
-{
-	struct fd_list * li;
-	int skip = 0;
-	
-	/* List is ordered by appid. Avoid duplicates */
-	for (li = list; li->next != list; li = li->next) {
-		struct fd_app * na = (struct fd_app *)(li->next);
-		if (na->appid < aid)
-			continue;
-		
-		if (na->appid > aid)
-			break;
-		
-		/* Otherwise, we merge with existing entry -- ignore vendor id in this case */
-		skip = 1;
-		
-		if (auth)
-			na->flags.auth = 1;
-		if (acct)
-			na->flags.acct = 1;
-		break;
-	}
-	
-	if (!skip) {			
-		struct fd_app  * new = NULL;
-
-		CHECK_MALLOC( new = malloc(sizeof(struct fd_app)) );
-		memset(new, 0, sizeof(struct fd_app));
-		fd_list_init(&new->chain, NULL);
-		new->flags.auth = (auth ? 1 : 0);
-		new->flags.acct = (acct ? 1 : 0);
-		new->vndid = vid;
-		new->appid = aid;
-		fd_list_insert_after(li, &new->chain);
-	}
-	
-	return 0;
-}
-
-/* Check if a given application id is in a list */
-int fd_app_check(struct fd_list * list, application_id_t aid, struct fd_app **detail)
-{
-	struct fd_list * li;
-	
-	TRACE_ENTRY("%p %d %p", list, aid, detail);
-	CHECK_PARAMS(list && detail);
-	
-	*detail = NULL;
-	
-	/* Search in the list */
-	for (li = list->next; li != list; li = li->next) {
-		struct fd_app * a = (struct fd_app *)li;
-		if (a->appid < aid)
-			continue;
-		
-		if (a->appid == aid)
-			*detail = a;
-		break;
-	}
-	
-	return 0;
-}
-
-/* Check if two lists have at least one common application */
-int fd_app_check_common(struct fd_list * list1, struct fd_list * list2, int * common_found)
-{
-	struct fd_list * li1, *li2;
-	
-	TRACE_ENTRY("%p %p %p", list1, list2, common_found);
-	CHECK_PARAMS( list1 && list2 && common_found );
-	
-	/* Both lists are ordered, so advance both pointers at the same time */
-	for (li1 = list1->next, li2 = list2->next;  (li1 != list1) && (li2 != list2); ) {
-		struct fd_app * a1 = (struct fd_app *)li1, *a2 = (struct fd_app *)li2;
-		if (a1->appid < a2->appid) {
-			li1 = li1->next;
-			continue;
-		}
-		if (a1->appid > a2->appid) {
-			li2 = li2->next;
-			continue;
-		}
-		/* They are equal, compare the applications */
-		if ((a1->flags.auth && a2->flags.auth) || (a1->flags.acct && a2->flags.acct)) {
-			/* found! */
-			*common_found = 1;
-			return 0;
-		}
-		
-		/* This application is not common, advance both lists */
-		li1 = li1->next;
-		li2 = li2->next;
-	}
-	
-	/* We did not find a common app */
-	*common_found = 0;
-	return 0;
-}
-
-/* Remove the apps from a list */
-int fd_app_empty(struct fd_list * list)
-{
-	TRACE_ENTRY("%p", list);
-	CHECK_PARAMS( list );
-	
-	while (!FD_IS_LIST_EMPTY(list)) {
-		struct fd_list * li = list->next;
-		fd_list_unlink(li);
-		free(li);
-	}
-	
-	return 0;
-}
Index: eeDiameter/cnxctx.c
===================================================================
--- freeDiameter/cnxctx.c	(revision 591)
+++ 	(revision )
@@ -1,1583 +1,0 @@
-/*********************************************************************************************************
-* Software License Agreement (BSD License)                                                               *
-* Author: Sebastien Decugis <sdecugis@nict.go.jp>							 *
-*													 *
-* Copyright (c) 2010, 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 "fD.h"
-#include "cnxctx.h"
-
-#include <net/if.h>
-#include <ifaddrs.h> /* for getifaddrs */
-
-/* The maximum size of Diameter message we accept to receive (<= 2^24) to avoid too big mallocs in case of trashed headers */
-#ifndef DIAMETER_MSG_SIZE_MAX
-#define DIAMETER_MSG_SIZE_MAX	65535	/* in bytes */
-#endif /* DIAMETER_MSG_SIZE_MAX */
-
-
-/* Connections contexts (cnxctx) in freeDiameter are wrappers around the sockets and TLS operations .
- * They are used to hide the details of the processing to the higher layers of the daemon.
- * They are always oriented on connections (TCP or SCTP), connectionless modes (UDP or SCTP) are not supported.
- */
-
-/* Note: this file could be moved to libfreeDiameter instead, but since it uses gnuTLS we prefer to keep it in the daemon */
-
-/* Lifetime of a cnxctx object:
- * 1) Creation
- *    a) a server socket:
- *       - create the object with fd_cnx_serv_tcp or fd_cnx_serv_sctp
- *       - start listening incoming connections: fd_cnx_serv_listen
- *       - accept new clients with fd_cnx_serv_accept.
- *    b) a client socket:
- *       - connect to a remote server with fd_cnx_cli_connect
- *
- * 2) Initialization
- *    - if TLS is started first, call fd_cnx_handshake
- *    - otherwise to receive clear messages, call fd_cnx_start_clear. fd_cnx_handshake can be called later.
- *
- * 3) Usage
- *    - fd_cnx_receive, fd_cnx_send : exchange messages on this connection (send is synchronous, receive is not, but blocking).
- *    - fd_cnx_recv_setaltfifo : when a message is received, the event is sent to an external fifo list. fd_cnx_receive does not work when the alt_fifo is set.
- *    - fd_cnx_getid : retrieve a descriptive string for the connection (for debug)
- *    - fd_cnx_getremoteid : identification of the remote peer (IP address or fqdn)
- *    - fd_cnx_getcred : get the remote peer TLS credentials, after handshake
- *
- * 4) End
- *    - fd_cnx_destroy
- */
-
-/*******************************************/
-/*     Creation of a connection object     */
-/*******************************************/
-
-/* Initialize a context structure */
-static struct cnxctx * fd_cnx_init(int full)
-{
-	struct cnxctx * conn = NULL;
-
-	TRACE_ENTRY("%d", full);
-
-	CHECK_MALLOC_DO( conn = malloc(sizeof(struct cnxctx)), return NULL );
-	memset(conn, 0, sizeof(struct cnxctx));
-
-	if (full) {
-		CHECK_FCT_DO( fd_fifo_new ( &conn->cc_incoming ), return NULL );
-	}
-
-	return conn;
-}
-
-/* Create and bind a server socket to the given endpoint and port */
-struct cnxctx * fd_cnx_serv_tcp(uint16_t port, int family, struct fd_endpoint * ep)
-{
-	struct cnxctx * cnx = NULL;
-	sSS dummy;
-	sSA * sa = (sSA *) &dummy;
-
-	TRACE_ENTRY("%hu %d %p", port, family, ep);
-
-	CHECK_PARAMS_DO( port, return NULL );
-	CHECK_PARAMS_DO( ep || family, return NULL );
-	CHECK_PARAMS_DO( (! family) || (family == AF_INET) || (family == AF_INET6), return NULL );
-	CHECK_PARAMS_DO( (! ep) || (!family) || (ep->ss.ss_family == family), return NULL );
-
-	/* The connection object */
-	CHECK_MALLOC_DO( cnx = fd_cnx_init(0), return NULL );
-
-	/* Prepare the socket address information */
-	if (ep) {
-		memcpy(sa, &ep->ss, sizeof(sSS));
-	} else {
-		memset(&dummy, 0, sizeof(dummy));
-		sa->sa_family = family;
-	}
-	if (sa->sa_family == AF_INET) {
-		((sSA4 *)sa)->sin_port = htons(port);
-		cnx->cc_family = AF_INET;
-	} else {
-		((sSA6 *)sa)->sin6_port = htons(port);
-		cnx->cc_family = AF_INET6;
-	}
-
-	/* Create the socket */
-	CHECK_FCT_DO( fd_tcp_create_bind_server( &cnx->cc_socket, sa, sSAlen(sa) ), goto error );
-
-	/* Generate the name for the connection object */
-	{
-		char addrbuf[INET6_ADDRSTRLEN];
-		int  rc;
-		rc = getnameinfo(sa, sSAlen(sa), addrbuf, sizeof(addrbuf), NULL, 0, NI_NUMERICHOST);
-		if (rc)
-			snprintf(addrbuf, sizeof(addrbuf), "[err:%s]", gai_strerror(rc));
-		snprintf(cnx->cc_id, sizeof(cnx->cc_id), "TCP srv [%s]:%hu (%d)", addrbuf, port, cnx->cc_socket);
-	}
-
-	cnx->cc_proto = IPPROTO_TCP;
-
-	return cnx;
-
-error:
-	fd_cnx_destroy(cnx);
-	return NULL;
-}
-
-/* Same function for SCTP, with a list of local endpoints to bind to */
-struct cnxctx * fd_cnx_serv_sctp(uint16_t port, struct fd_list * ep_list)
-{
-#ifdef DISABLE_SCTP
-	TRACE_DEBUG(INFO, "This function should never been called when SCTP is disabled...");
-	ASSERT(0);
-	CHECK_FCT_DO( ENOTSUP, );
-	return NULL;
-#else /* DISABLE_SCTP */
-	struct cnxctx * cnx = NULL;
-
-	TRACE_ENTRY("%hu %p", port, ep_list);
-
-	CHECK_PARAMS_DO( port, return NULL );
-
-	/* The connection object */
-	CHECK_MALLOC_DO( cnx = fd_cnx_init(0), return NULL );
-
-	if (fd_g_config->cnf_flags.no_ip6) {
-		cnx->cc_family = AF_INET;
-	} else {
-		cnx->cc_family = AF_INET6; /* can create socket for both IP and IPv6 */
-	}
-	
-	/* Create the socket */
-	CHECK_FCT_DO( fd_sctp_create_bind_server( &cnx->cc_socket, cnx->cc_family, ep_list, port ), goto error );
-
-	/* Generate the name for the connection object */
-	snprintf(cnx->cc_id, sizeof(cnx->cc_id), "SCTP srv :%hu (%d)", port, cnx->cc_socket);
-
-	cnx->cc_proto = IPPROTO_SCTP;
-
-	return cnx;
-
-error:
-	fd_cnx_destroy(cnx);
-	return NULL;
-#endif /* DISABLE_SCTP */
-}
-
-/* Allow clients to connect on the server socket */
-int fd_cnx_serv_listen(struct cnxctx * conn)
-{
-	CHECK_PARAMS( conn );
-
-	switch (conn->cc_proto) {
-		case IPPROTO_TCP:
-			CHECK_FCT(fd_tcp_listen(conn->cc_socket));
-			break;
-
-#ifndef DISABLE_SCTP
-		case IPPROTO_SCTP:
-			CHECK_FCT(fd_sctp_listen(conn->cc_socket));
-			break;
-#endif /* DISABLE_SCTP */
-
-		default:
-			CHECK_PARAMS(0);
-	}
-
-	return 0;
-}
-
-/* Accept a client (blocking until a new client connects) -- cancelable */
-struct cnxctx * fd_cnx_serv_accept(struct cnxctx * serv)
-{
-	struct cnxctx * cli = NULL;
-	sSS ss;
-	socklen_t ss_len = sizeof(ss);
-	int cli_sock = 0;
-
-	TRACE_ENTRY("%p", serv);
-	CHECK_PARAMS_DO(serv, return NULL);
-	
-	/* Accept the new connection -- this is blocking until new client enters or until cancellation */
-	CHECK_SYS_DO( cli_sock = accept(serv->cc_socket, (sSA *)&ss, &ss_len), return NULL );
-	
-	if (TRACE_BOOL(INFO)) {
-		fd_log_debug("%s : accepted new client [", fd_cnx_getid(serv));
-		sSA_DUMP_NODE( &ss, NI_NUMERICHOST );
-		fd_log_debug("].\n");
-	}
-	
-	CHECK_MALLOC_DO( cli = fd_cnx_init(1), { shutdown(cli_sock, SHUT_RDWR); close(cli_sock); return NULL; } );
-	cli->cc_socket = cli_sock;
-	cli->cc_family = serv->cc_family;
-	cli->cc_proto = serv->cc_proto;
-	
-	/* Set the timeout */
-	fd_cnx_s_setto(cli->cc_socket);
-	
-	/* Generate the name for the connection object */
-	{
-		char addrbuf[INET6_ADDRSTRLEN];
-		char portbuf[10];
-		int  rc;
-		
-		/* Numeric values for debug */
-		rc = getnameinfo((sSA *)&ss, sSAlen(&ss), addrbuf, sizeof(addrbuf), portbuf, sizeof(portbuf), NI_NUMERICHOST | NI_NUMERICSERV);
-		if (rc) {
-			snprintf(addrbuf, sizeof(addrbuf), "[err:%s]", gai_strerror(rc));
-			portbuf[0] = '\0';
-		}
-		
-		snprintf(cli->cc_id, sizeof(cli->cc_id), "{%s} (%d) <- [%s]:%s (%d)", 
-				IPPROTO_NAME(cli->cc_proto), serv->cc_socket, 
-				addrbuf, portbuf, cli->cc_socket);
-		
-		/* Name for log messages */
-		rc = getnameinfo((sSA *)&ss, sSAlen(&ss), cli->cc_remid, sizeof(cli->cc_remid), NULL, 0, 0);
-		if (rc)
-			snprintf(cli->cc_remid, sizeof(cli->cc_remid), "[err:%s]", gai_strerror(rc));
-	}
-
-#ifndef DISABLE_SCTP
-	/* SCTP-specific handlings */
-	if (cli->cc_proto == IPPROTO_SCTP) {
-		/* Retrieve the number of streams */
-		CHECK_FCT_DO( fd_sctp_get_str_info( cli->cc_socket, &cli->cc_sctp_para.str_in, &cli->cc_sctp_para.str_out, NULL ), {fd_cnx_destroy(cli); return NULL;} );
-		if (cli->cc_sctp_para.str_out < cli->cc_sctp_para.str_in)
-			cli->cc_sctp_para.pairs = cli->cc_sctp_para.str_out;
-		else
-			cli->cc_sctp_para.pairs = cli->cc_sctp_para.str_in;
-		
-		TRACE_DEBUG(FULL,"%s : client '%s' (SCTP:%d, %d/%d streams)", fd_cnx_getid(serv), fd_cnx_getid(cli), cli->cc_socket, cli->cc_sctp_para.str_in, cli->cc_sctp_para.str_out);
-	}
-#endif /* DISABLE_SCTP */
-
-	return cli;
-}
-
-/* Client side: connect to a remote server -- cancelable */
-struct cnxctx * fd_cnx_cli_connect_tcp(sSA * sa /* contains the port already */, socklen_t addrlen)
-{
-	int sock = 0;
-	struct cnxctx * cnx = NULL;
-	
-	TRACE_ENTRY("%p %d", sa, addrlen);
-	CHECK_PARAMS_DO( sa && addrlen, return NULL );
-	
-	/* Create the socket and connect, which can take some time and/or fail */
-	{
-		int ret = fd_tcp_client( &sock, sa, addrlen );
-		if (ret != 0) {
-			int lvl;
-			switch (ret) {
-				case ECONNREFUSED:
-
-					/* "Normal" errors */
-					lvl = FULL;
-					break;
-				default:
-					lvl = INFO;
-			}
-			/* Some errors are expected, we log at different level */
-			TRACE_DEBUG( lvl, "fd_tcp_client returned an error: %s", strerror(ret));
-			return NULL;
-		}
-	}
-	
-	if (TRACE_BOOL(INFO)) {
-		fd_log_debug("Connection established to server '");
-		sSA_DUMP_NODE_SERV( sa, NI_NUMERICSERV);
-		fd_log_debug("' (TCP:%d).\n", sock);
-	}
-	
-	/* Once the socket is created successfuly, prepare the remaining of the cnx */
-	CHECK_MALLOC_DO( cnx = fd_cnx_init(1), { shutdown(sock, SHUT_RDWR); close(sock); return NULL; } );
-	
-	cnx->cc_socket = sock;
-	cnx->cc_family = sa->sa_family;
-	cnx->cc_proto  = IPPROTO_TCP;
-	
-	/* Set the timeout */
-	fd_cnx_s_setto(cnx->cc_socket);
-	
-	/* Generate the names for the object */
-	{
-		char addrbuf[INET6_ADDRSTRLEN];
-		char portbuf[10];
-		int  rc;
-		
-		/* Numeric values for debug */
-		rc = getnameinfo(sa, addrlen, addrbuf, sizeof(addrbuf), portbuf, sizeof(portbuf), NI_NUMERICHOST | NI_NUMERICSERV);
-		if (rc) {
-			snprintf(addrbuf, sizeof(addrbuf), "[err:%s]", gai_strerror(rc));
-			portbuf[0] = '\0';
-		}
-		
-		snprintf(cnx->cc_id, sizeof(cnx->cc_id), "{TCP} -> [%s]:%s (%d)", addrbuf, portbuf, cnx->cc_socket);
-		
-		/* Name for log messages */
-		rc = getnameinfo(sa, addrlen, cnx->cc_remid, sizeof(cnx->cc_remid), NULL, 0, 0);
-		if (rc)
-			snprintf(cnx->cc_remid, sizeof(cnx->cc_remid), "[err:%s]", gai_strerror(rc));
-	}
-	
-	return cnx;
-}
-
-/* Same for SCTP, accepts a list of remote addresses to connect to (see sctp_connectx for how they are used) */
-struct cnxctx * fd_cnx_cli_connect_sctp(int no_ip6, uint16_t port, struct fd_list * list)
-{
-#ifdef DISABLE_SCTP
-	TRACE_DEBUG(INFO, "This function should never been called when SCTP is disabled...");
-	ASSERT(0);
-	CHECK_FCT_DO( ENOTSUP, );
-	return NULL;
-#else /* DISABLE_SCTP */
-	int sock = 0;
-	struct cnxctx * cnx = NULL;
-	sSS primary;
-	
-	TRACE_ENTRY("%p", list);
-	CHECK_PARAMS_DO( list && !FD_IS_LIST_EMPTY(list), return NULL );
-	
-	{
-		int ret = fd_sctp_client( &sock, no_ip6, port, list );
-		if (ret != 0) {
-			int lvl;
-			switch (ret) {
-				case ECONNREFUSED:
-
-					/* "Normal" errors */
-					lvl = FULL;
-					break;
-				default:
-					lvl = INFO;
-			}
-			/* Some errors are expected, we log at different level */
-			TRACE_DEBUG( lvl, "fd_sctp_client returned an error: %s", strerror(ret));
-			return NULL;
-		}
-	}
-	
-	/* Once the socket is created successfuly, prepare the remaining of the cnx */
-	CHECK_MALLOC_DO( cnx = fd_cnx_init(1), { shutdown(sock, SHUT_RDWR); close(sock); return NULL; } );
-	
-	cnx->cc_socket = sock;
-	cnx->cc_family = no_ip6 ? AF_INET : AF_INET6;
-	cnx->cc_proto  = IPPROTO_SCTP;
-	
-	/* Set the timeout */
-	fd_cnx_s_setto(cnx->cc_socket);
-	
-	/* Retrieve the number of streams and primary address */
-	CHECK_FCT_DO( fd_sctp_get_str_info( sock, &cnx->cc_sctp_para.str_in, &cnx->cc_sctp_para.str_out, &primary ), goto error );
-	if (cnx->cc_sctp_para.str_out < cnx->cc_sctp_para.str_in)
-		cnx->cc_sctp_para.pairs = cnx->cc_sctp_para.str_out;
-	else
-		cnx->cc_sctp_para.pairs = cnx->cc_sctp_para.str_in;
-	
-	if (TRACE_BOOL(INFO)) {
-		fd_log_debug("Connection established to server '");
-		sSA_DUMP_NODE_SERV( &primary, NI_NUMERICSERV);
-		fd_log_debug("' (SCTP:%d, %d/%d streams).\n", sock, cnx->cc_sctp_para.str_in, cnx->cc_sctp_para.str_out);
-	}
-	
-	/* Generate the names for the object */
-	{
-		char addrbuf[INET6_ADDRSTRLEN];
-		char portbuf[10];
-		int  rc;
-		
-		/* Numeric values for debug */
-		rc = getnameinfo((sSA *)&primary, sSAlen(&primary), addrbuf, sizeof(addrbuf), portbuf, sizeof(portbuf), NI_NUMERICHOST | NI_NUMERICSERV);
-		if (rc) {
-			snprintf(addrbuf, sizeof(addrbuf), "[err:%s]", gai_strerror(rc));
-			portbuf[0] = '\0';
-		}
-		
-		snprintf(cnx->cc_id, sizeof(cnx->cc_id), "{SCTP} -> [%s]:%s (%d)", addrbuf, portbuf, cnx->cc_socket);
-		
-		/* Name for log messages */
-		rc = getnameinfo((sSA *)&primary, sSAlen(&primary), cnx->cc_remid, sizeof(cnx->cc_remid), NULL, 0, 0);
-		if (rc)
-			snprintf(cnx->cc_remid, sizeof(cnx->cc_remid), "[err:%s]", gai_strerror(rc));
-	}
-	
-	return cnx;
-
-error:
-	fd_cnx_destroy(cnx);
-	return NULL;
-#endif /* DISABLE_SCTP */
-}
-
-/* Return a string describing the connection, for debug */
-char * fd_cnx_getid(struct cnxctx * conn)
-{
-	CHECK_PARAMS_DO( conn, return "" );
-	return conn->cc_id;
-}
-
-/* Return the protocol of a connection */
-int fd_cnx_getproto(struct cnxctx * conn)
-{
-	CHECK_PARAMS_DO( conn, return 0 );
-	return conn->cc_proto;
-}
-
-/* Set the hostname to check during handshake */
-void fd_cnx_sethostname(struct cnxctx * conn, char * hn)
-{
-	CHECK_PARAMS_DO( conn, return );
-	conn->cc_tls_para.cn = hn;
-}
-
-/* Return the TLS state of a connection */
-int fd_cnx_getTLS(struct cnxctx * conn)
-{
-	CHECK_PARAMS_DO( conn, return 0 );
-	fd_cpu_flush_cache();
-	return conn->cc_status & CC_STATUS_TLS;
-}
-
-/* Get the list of endpoints (IP addresses) of the local and remote peers on this connection */
-int fd_cnx_getremoteeps(struct cnxctx * conn, struct fd_list * eps)
-{
-	TRACE_ENTRY("%p %p %p", conn, eps);
-	CHECK_PARAMS(conn && eps);
-	
-	/* Check we have a full connection object, not a listening socket (with no remote) */
-	CHECK_PARAMS( conn->cc_incoming );
-
-	/* Retrieve the peer endpoint(s) of the connection */
-	switch (conn->cc_proto) {
-		case IPPROTO_TCP: {
-			sSS ss;
-			socklen_t sl;
-			CHECK_FCT(fd_tcp_get_remote_ep(conn->cc_socket, &ss, &sl));
-			CHECK_FCT(fd_ep_add_merge( eps, (sSA *)&ss, sl, EP_FL_LL | EP_FL_PRIMARY ));
-		}
-		break;
-
-		#ifndef DISABLE_SCTP
-		case IPPROTO_SCTP: {
-			CHECK_FCT(fd_sctp_get_remote_ep(conn->cc_socket, eps));
-		}
-		break;
-		#endif /* DISABLE_SCTP */
-
-		default:
-			CHECK_PARAMS(0);
-	}
-
-	return 0;
-}
-
-/* Get a string describing the remote peer address (ip address or fqdn) */
-char * fd_cnx_getremoteid(struct cnxctx * conn)
-{
-	CHECK_PARAMS_DO( conn, return "" );
-	return conn->cc_remid;
-}
-
-/* Retrieve a list of all IP addresses of the local system from the kernel, using  */
-int fd_cnx_get_local_eps(struct fd_list * list)
-{
-	struct ifaddrs *iflist, *cur;
-	CHECK_SYS(getifaddrs(&iflist));
-	
-	for (cur = iflist; cur != NULL; cur = cur->ifa_next) {
-		if (cur->ifa_flags & IFF_LOOPBACK)
-			continue;
-		
-		if (fd_g_config->cnf_flags.no_ip4 && (cur->ifa_addr->sa_family == AF_INET))
-			continue;
-		
-		if (fd_g_config->cnf_flags.no_ip6 && (cur->ifa_addr->sa_family == AF_INET6))
-			continue;
-		
-		CHECK_FCT(fd_ep_add_merge( list, cur->ifa_addr, sSAlen(cur->ifa_addr), EP_FL_LL ));
-	}
-	
-	freeifaddrs(iflist);
-	
-	return 0;
-}
-
-
-/**************************************/
-/*     Use of a connection object     */
-/**************************************/
-
-/* An error occurred on the socket */
-void fd_cnx_markerror(struct cnxctx * conn)
-{
-	TRACE_ENTRY("%p", conn);
-	CHECK_PARAMS_DO( conn, goto fatal );
-	
-	TRACE_DEBUG(FULL, "Error flag set for socket %d (%s / %s)", conn->cc_socket, conn->cc_remid, conn->cc_id);
-	
-	/* Mark the error */
-	fd_cpu_flush_cache();
-	conn->cc_status |= CC_STATUS_ERROR;
-	
-	/* Report the error if not reported yet, and not closing */
-	if ((!(conn->cc_status & CC_STATUS_CLOSING )) && (!(conn->cc_status & CC_STATUS_SIGNALED )))  {
-		TRACE_DEBUG(FULL, "Sending FDEVP_CNX_ERROR event");
-		CHECK_FCT_DO( fd_event_send( Target_Queue(conn), FDEVP_CNX_ERROR, 0, NULL), goto fatal);
-		conn->cc_status |= CC_STATUS_SIGNALED;
-	}
-	fd_cpu_flush_cache();
-	return;
-fatal:
-	/* An unrecoverable error occurred, stop the daemon */
-	CHECK_FCT_DO(fd_event_send(fd_g_config->cnf_main_ev, FDEV_TERMINATE, 0, NULL), );	
-}
-
-/* Set the timeout option on the socket */
-void fd_cnx_s_setto(int sock) 
-{
-	struct timeval tv;
-	
-	/* Set a timeout on the socket so that in any case we are not stuck waiting for something */
-	memset(&tv, 0, sizeof(tv));
-	tv.tv_sec = 3;	/* allow 3 seconds timeout for TLS session cleanup */
-	CHECK_SYS_DO( setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)), /* best effort only */ );
-	CHECK_SYS_DO( setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)), /* Also timeout for sending, to avoid waiting forever */ );
-}
-
-/* A recv-like function, taking a cnxctx object instead of socket as entry. We use it to quickly react to timeouts without traversing GNUTLS wrapper each time */
-ssize_t fd_cnx_s_recv(struct cnxctx * conn, void *buffer, size_t length)
-{
-	ssize_t ret = 0;
-	int timedout = 0;
-again:
-	ret = recv(conn->cc_socket, buffer, length, 0);
-	/* Handle special case of timeout */
-	if ((ret < 0) && (errno == EAGAIN)) {
-		fd_cpu_flush_cache();
-		if (! (conn->cc_status & CC_STATUS_CLOSING))
-			goto again; /* don't care, just ignore */
-		if (!timedout) {
-			timedout ++; /* allow for one timeout while closing */
-			goto again;
-		}
-	}
-	
-	CHECK_SYS_DO(ret, /* continue */);
-	
-	/* Mark the error */
-	if (ret <= 0)
-		fd_cnx_markerror(conn);
-	
-	return ret;
-}
-
-/* Send */
-static ssize_t fd_cnx_s_send(struct cnxctx * conn, void *buffer, size_t length)
-{
-	ssize_t ret = 0;
-	int timedout = 0;
-again:
-	ret = send(conn->cc_socket, buffer, length, 0);
-	/* Handle special case of timeout */
-	if ((ret < 0) && (errno == EAGAIN)) {
-		fd_cpu_flush_cache();
-		if (! (conn->cc_status & CC_STATUS_CLOSING))
-			goto again; /* don't care, just ignore */
-		if (!timedout) {
-			timedout ++; /* allow for one timeout while closing */
-			goto again;
-		}
-		CHECK_SYS_DO(ret, /* continue */);
-	}
-	
-	/* Mark the error */
-	if (ret <= 0)
-		fd_cnx_markerror(conn);
-	
-	return ret;
-}
-
-/* Receiver thread (TCP & noTLS) : incoming message is directly saved into the target queue */
-static void * rcvthr_notls_tcp(void * arg)
-{
-	struct cnxctx * conn = arg;
-	
-	TRACE_ENTRY("%p", arg);
-	CHECK_PARAMS_DO(conn && (conn->cc_socket > 0), goto out);
-	
-	/* Set the thread name */
-	{
-		char buf[48];
-		snprintf(buf, sizeof(buf), "Receiver (%d) TCP/noTLS)", conn->cc_socket);
-		fd_log_threadname ( buf );
-	}
-	
-	ASSERT( conn->cc_proto == IPPROTO_TCP );
-	ASSERT( ! (conn->cc_status & CC_STATUS_TLS) );
-	ASSERT( Target_Queue(conn) );
-	
-	/* Receive from a TCP connection: we have to rebuild the message boundaries */
-	do {
-		uint8_t header[4];
-		uint8_t * newmsg;
-		size_t  length;
-		ssize_t ret = 0;
-		size_t	received = 0;
-
-		do {
-			ret = fd_cnx_s_recv(conn, &header[received], sizeof(header) - received);
-			if (ret <= 0) {
-				goto out; /* Stop the thread, the event was already sent */
-			}
-
-			received += ret;
-		} while (received < sizeof(header));
-
-		length = ((size_t)header[1] << 16) + ((size_t)header[2] << 8) + (size_t)header[3];
-
-		/* Check the received word is a valid begining of a Diameter message */
-		if ((header[0] != DIAMETER_VERSION)	/* defined in <libfreeDiameter.h> */
-		   || (length > DIAMETER_MSG_SIZE_MAX)) { /* to avoid too big mallocs */
-			/* The message is suspect */
-			TRACE_DEBUG(INFO, "Received suspect header [ver: %d, size: %zd], assume disconnection", (int)header[0], length);
-			fd_cnx_markerror(conn);
-			goto out; /* Stop the thread, the recipient of the event will cleanup */
-		}
-
-		/* Ok, now we can really receive the data */
-		CHECK_MALLOC_DO(  newmsg = malloc( length ), goto fatal );
-		memcpy(newmsg, header, sizeof(header));
-
-		while (received < length) {
-			pthread_cleanup_push(free, newmsg); /* In case we are canceled, clean the partialy built buffer */
-			ret = fd_cnx_s_recv(conn, newmsg + received, length - received);
-			pthread_cleanup_pop(0);
-
-			if (ret <= 0) {
-				free(newmsg);
-				goto out;
-			}
-			received += ret;
-		}
-		
-		/* We have received a complete message, pass it to the daemon */
-		fd_cpu_flush_cache();
-		CHECK_FCT_DO( fd_event_send( Target_Queue(conn), FDEVP_CNX_MSG_RECV, length, newmsg), /* continue or destroy everything? */);
-		
-	} while (conn->cc_loop);
-	
-out:
-	TRACE_DEBUG(FULL, "Thread terminated");	
-	return NULL;
-	
-fatal:
-	/* An unrecoverable error occurred, stop the daemon */
-	CHECK_FCT_DO(fd_event_send(fd_g_config->cnf_main_ev, FDEV_TERMINATE, 0, NULL), );
-	goto out;
-}
-
-#ifndef DISABLE_SCTP
-/* Receiver thread (SCTP & noTLS) : incoming message is directly saved into cc_incoming, no need to care for the stream ID */
-static void * rcvthr_notls_sctp(void * arg)
-{
-	struct cnxctx * conn = arg;
-	uint8_t * buf;
-	size_t    bufsz;
-	int	  event;
-	
-	TRACE_ENTRY("%p", arg);
-	CHECK_PARAMS_DO(conn && (conn->cc_socket > 0), goto fatal);
-	
-	/* Set the thread name */
-	{
-		char buf[48];
-		snprintf(buf, sizeof(buf), "Receiver (%d) SCTP/noTLS)", conn->cc_socket);
-		fd_log_threadname ( buf );
-	}
-	
-	ASSERT( conn->cc_proto == IPPROTO_SCTP );
-	ASSERT( ! (conn->cc_status & CC_STATUS_TLS) );
-	ASSERT( Target_Queue(conn) );
-	
-	do {
-		fd_cpu_flush_cache();
-		CHECK_FCT_DO( fd_sctp_recvmeta(conn->cc_socket, NULL, &buf, &bufsz, &event, &conn->cc_status), goto fatal );
-		if (event == FDEVP_CNX_ERROR) {
-			fd_cnx_markerror(conn);
-			goto out;
-		}
-		
-		if (event == FDEVP_CNX_SHUTDOWN) {
-			/* Just ignore the notification for now, we will get another error later anyway */
-			continue;
-		}
-		
-		fd_cpu_flush_cache();
-		CHECK_FCT_DO( fd_event_send( Target_Queue(conn), event, bufsz, buf), goto fatal );
-		
-	} while (conn->cc_loop || (event != FDEVP_CNX_MSG_RECV));
-	
-out:
-	TRACE_DEBUG(FULL, "Thread terminated");	
-	return NULL;
-
-fatal:
-	/* An unrecoverable error occurred, stop the daemon */
-	CHECK_FCT_DO(fd_event_send(fd_g_config->cnf_main_ev, FDEV_TERMINATE, 0, NULL), );
-	goto out;
-}
-#endif /* DISABLE_SCTP */
-
-/* Start receving messages in clear (no TLS) on the connection */
-int fd_cnx_start_clear(struct cnxctx * conn, int loop)
-{
-	TRACE_ENTRY("%p %i", conn, loop);
-	
-	CHECK_PARAMS( conn && Target_Queue(conn) && (!(conn->cc_status & CC_STATUS_TLS)) && (!conn->cc_loop));
-	
-	/* Release resources in case of a previous call was already made */
-	CHECK_FCT_DO( fd_thr_term(&conn->cc_rcvthr), /* continue */);
-	
-	/* Save the loop request */
-	conn->cc_loop = loop;
-	
-	switch (conn->cc_proto) {
-		case IPPROTO_TCP:
-			/* Start the tcp_notls thread */
-			CHECK_POSIX( pthread_create( &conn->cc_rcvthr, NULL, rcvthr_notls_tcp, conn ) );
-			break;
-#ifndef DISABLE_SCTP
-		case IPPROTO_SCTP:
-			/* Start the tcp_notls thread */
-			CHECK_POSIX( pthread_create( &conn->cc_rcvthr, NULL, rcvthr_notls_sctp, conn ) );
-			break;
-#endif /* DISABLE_SCTP */
-		default:
-			TRACE_DEBUG(INFO, "Unknown protocol: %d", conn->cc_proto);
-			ASSERT(0);
-			return ENOTSUP;
-	}
-			
-	return 0;
-}
-
-
-
-
-/* Returns 0 on error, received data size otherwise (always >= 0) */
-static ssize_t fd_tls_recv_handle_error(struct cnxctx * conn, gnutls_session_t session, void * data, size_t sz)
-{
-	ssize_t ret;
-again:	
-	CHECK_GNUTLS_DO( ret = gnutls_record_recv(session, data, sz),
-		{
-			switch (ret) {
-				case GNUTLS_E_REHANDSHAKE: 
-					fd_cpu_flush_cache();
-					if (!(conn->cc_status & CC_STATUS_CLOSING))
-						CHECK_GNUTLS_DO( ret = gnutls_handshake(session),
-							{
-								if (TRACE_BOOL(INFO)) {
-									fd_log_debug("TLS re-handshake failed on socket %d (%s) : %s\n", conn->cc_socket, conn->cc_id, gnutls_strerror(ret));
-								}
-								goto end;
-							} );
-
-				case GNUTLS_E_AGAIN:
-				case GNUTLS_E_INTERRUPTED:
-					fd_cpu_flush_cache();
-					if (!(conn->cc_status & CC_STATUS_CLOSING))
-						goto again;
-					TRACE_DEBUG(FULL, "Connection is closing, so abord gnutls_record_recv now.");
-					break;
-
-				case GNUTLS_E_UNEXPECTED_PACKET_LENGTH:
-					/* The connection is closed */
-					TRACE_DEBUG(FULL, "Got 0 size while reading the socket, probably connection closed...");
-					break;
-				
-				default:
-					TRACE_DEBUG(INFO, "This GNU TLS error is not handled, assume unrecoverable error");
-			}
-		} );
-		
-	if (ret == 0)
-		CHECK_GNUTLS_DO( gnutls_bye(session, GNUTLS_SHUT_RDWR),  );
-	
-end:	
-	if (ret <= 0)
-		fd_cnx_markerror(conn);
-	return ret;
-}
-
-/* Wrapper around gnutls_record_send to handle some error codes */
-static ssize_t fd_tls_send_handle_error(struct cnxctx * conn, gnutls_session_t session, void * data, size_t sz)
-{
-	ssize_t ret;
-again:	
-	CHECK_GNUTLS_DO( ret = gnutls_record_send(session, data, sz),
-		{
-			switch (ret) {
-				case GNUTLS_E_REHANDSHAKE: 
-					fd_cpu_flush_cache();
-					if (!(conn->cc_status & CC_STATUS_CLOSING))
-						CHECK_GNUTLS_DO( ret = gnutls_handshake(session),
-							{
-								if (TRACE_BOOL(INFO)) {
-									fd_log_debug("TLS re-handshake failed on socket %d (%s) : %s\n", conn->cc_socket, conn->cc_id, gnutls_strerror(ret));
-								}
-								goto end;
-							} );
-
-				case GNUTLS_E_AGAIN:
-				case GNUTLS_E_INTERRUPTED:
-					fd_cpu_flush_cache();
-					if (!(conn->cc_status & CC_STATUS_CLOSING))
-						goto again;
-					TRACE_DEBUG(INFO, "Connection is closing, so abord gnutls_record_send now.");
-					break;
-
-				default:
-					TRACE_DEBUG(INFO, "This TLS error is not handled, assume unrecoverable error");
-			}
-		} );
-end:	
-	if (ret <= 0)
-		fd_cnx_markerror(conn);
-		
-	return ret;
-}
-
-
-/* The function that receives TLS data and re-builds a Diameter message -- it exits only on error or cancelation */
-int fd_tls_rcvthr_core(struct cnxctx * conn, gnutls_session_t session)
-{
-	/* No guarantee that GnuTLS preserves the message boundaries, so we re-build it as in TCP */
-	do {
-		uint8_t header[4];
-		uint8_t * newmsg;
-		size_t  length;
-		ssize_t ret = 0;
-		size_t	received = 0;
-
-		do {
-			ret = fd_tls_recv_handle_error(conn, session, &header[received], sizeof(header) - received);
-			if (ret <= 0) {
-				/* The connection is closed */
-				goto out;
-			}
-			received += ret;
-		} while (received < sizeof(header));
-
-		length = ((size_t)header[1] << 16) + ((size_t)header[2] << 8) + (size_t)header[3];
-
-		/* Check the received word is a valid beginning of a Diameter message */
-		if ((header[0] != DIAMETER_VERSION)	/* defined in <libfreeDiameter.h> */
-		   || (length > DIAMETER_MSG_SIZE_MAX)) { /* to avoid too big mallocs */
-			/* The message is suspect */
-			TRACE_DEBUG(INFO, "Received suspect header [ver: %d, size: %zd], assume disconnection", (int)header[0], length);
-			fd_cnx_markerror(conn);
-			goto out;
-		}
-
-		/* Ok, now we can really receive the data */
-		CHECK_MALLOC(  newmsg = malloc( length ) );
-		memcpy(newmsg, header, sizeof(header));
-
-		while (received < length) {
-			pthread_cleanup_push(free, newmsg); /* In case we are canceled, clean the partialy built buffer */
-			ret = fd_tls_recv_handle_error(conn, session, newmsg + received, length - received);
-			pthread_cleanup_pop(0);
-
-			if (ret <= 0) {
-				free(newmsg);
-				goto out;
-			}
-			received += ret;
-		}
-		
-		/* We have received a complete message, pass it to the daemon */
-		fd_cpu_flush_cache();
-		CHECK_FCT_DO( ret = fd_event_send( Target_Queue(conn), FDEVP_CNX_MSG_RECV, length, newmsg), 
-			{ 
-				free(newmsg); 
-				CHECK_FCT_DO(fd_event_send(fd_g_config->cnf_main_ev, FDEV_TERMINATE, 0, NULL), );
-				return ret; 
-			} );
-		
-	} while (1);
-	
-out:
-	return ENOTCONN;
-}
-
-/* Receiver thread (TLS & 1 stream SCTP or TCP)  */
-static void * rcvthr_tls_single(void * arg)
-{
-	struct cnxctx * conn = arg;
-	
-	TRACE_ENTRY("%p", arg);
-	CHECK_PARAMS_DO(conn && (conn->cc_socket > 0), return NULL );
-	
-	/* Set the thread name */
-	{
-		char buf[48];
-		snprintf(buf, sizeof(buf), "Receiver (%d) TLS/single stream", conn->cc_socket);
-		fd_log_threadname ( buf );
-	}
-	
-	ASSERT( conn->cc_status & CC_STATUS_TLS );
-	ASSERT( Target_Queue(conn) );
-
-	/* The next function only returns when there is an error on the socket */	
-	CHECK_FCT_DO(fd_tls_rcvthr_core(conn, conn->cc_tls_para.session), /* continue */);
-
-	TRACE_DEBUG(FULL, "Thread terminated");	
-	return NULL;
-}
-
-/* Prepare a gnutls session object for handshake */
-int fd_tls_prepare(gnutls_session_t * session, int mode, char * priority, void * alt_creds)
-{
-	/* Create the session context */
-	CHECK_GNUTLS_DO( gnutls_init (session, mode), return ENOMEM );
-
-	/* Set the algorithm suite */
-	if (priority) {
-		const char * errorpos;
-		CHECK_GNUTLS_DO( gnutls_priority_set_direct( *session, priority, &errorpos ), 
-			{ TRACE_DEBUG(INFO, "Error in priority string '%s' at position: '%s'\n", priority, errorpos); return EINVAL; } );
-	} else {
-		CHECK_GNUTLS_DO( gnutls_priority_set( *session, fd_g_config->cnf_sec_data.prio_cache ), return EINVAL );
-	}
-
-	/* Set the credentials of this side of the connection */
-	CHECK_GNUTLS_DO( gnutls_credentials_set (*session, GNUTLS_CRD_CERTIFICATE, alt_creds ?: fd_g_config->cnf_sec_data.credentials), return EINVAL );
-
-	/* Request the remote credentials as well */
-	if (mode == GNUTLS_SERVER) {
-		gnutls_certificate_server_set_request (*session, GNUTLS_CERT_REQUIRE);
-	}
-	
-	return 0;
-}
-
-/* Verify remote credentials after successful handshake (return 0 if OK, EINVAL otherwise) */
-int fd_tls_verify_credentials(gnutls_session_t session, struct cnxctx * conn, int verbose)
-{
-	int i, ret = 0;
-	unsigned int gtret;
-	const gnutls_datum_t *cert_list;
-	unsigned int cert_list_size;
-	gnutls_x509_crt_t cert;
-	time_t now;
-	
-	TRACE_ENTRY("%p %d", conn, verbose);
-	CHECK_PARAMS(conn);
-	
-	/* Trace the session information -- http://www.gnu.org/software/gnutls/manual/gnutls.html#Obtaining-session-information */
-	if (verbose && TRACE_BOOL(FULL)) {
-		const char *tmp;
-		gnutls_kx_algorithm_t kx;
-  		gnutls_credentials_type_t cred;
-		
-		fd_log_debug("TLS Session information for connection '%s':\n", conn->cc_id);
-
-		/* print the key exchange's algorithm name */
-		GNUTLS_TRACE( kx = gnutls_kx_get (session) );
-		GNUTLS_TRACE( tmp = gnutls_kx_get_name (kx) );
-		fd_log_debug("\t - Key Exchange: %s\n", tmp);
-
-		/* Check the authentication type used and switch
-		* to the appropriate. */
-		GNUTLS_TRACE( cred = gnutls_auth_get_type (session) );
-		switch (cred)
-		{
-			case GNUTLS_CRD_IA:
-				fd_log_debug("\t - TLS/IA session\n");
-				break;
-
-			case GNUTLS_CRD_PSK:
-				/* This returns NULL in server side. */
-				if (gnutls_psk_client_get_hint (session) != NULL)
-					fd_log_debug("\t - PSK authentication. PSK hint '%s'\n",
-						gnutls_psk_client_get_hint (session));
-				/* This returns NULL in client side. */
-				if (gnutls_psk_server_get_username (session) != NULL)
-					fd_log_debug("\t - PSK authentication. Connected as '%s'\n",
-						gnutls_psk_server_get_username (session));
-				break;
-
-			case GNUTLS_CRD_ANON:	/* anonymous authentication */
-				fd_log_debug("\t - Anonymous DH using prime of %d bits\n",
-					gnutls_dh_get_prime_bits (session));
-				break;
-
-			case GNUTLS_CRD_CERTIFICATE:	/* certificate authentication */
-				/* Check if we have been using ephemeral Diffie-Hellman. */
-				if (kx == GNUTLS_KX_DHE_RSA || kx == GNUTLS_KX_DHE_DSS) {
-					fd_log_debug("\t - Ephemeral DH using prime of %d bits\n",
-						gnutls_dh_get_prime_bits (session));
-				}
-				break;
-#ifdef ENABLE_SRP				
-			case GNUTLS_CRD_SRP:
-				fd_log_debug("\t - SRP session with username %s\n",
-					gnutls_srp_server_get_username (session));
-				break;
-#endif /* ENABLE_SRP */
-
-			default:
-				fd_log_debug("\t - Different type of credentials for the session (%d).\n", cred);
-				break;
-
-		}
-
-		/* print the protocol's name (ie TLS 1.0) */
-		tmp = gnutls_protocol_get_name (gnutls_protocol_get_version (session));
-		fd_log_debug("\t - Protocol: %s\n", tmp);
-
-		/* print the certificate type of the peer. ie X.509 */
-		tmp = gnutls_certificate_type_get_name (gnutls_certificate_type_get (session));
-		fd_log_debug("\t - Certificate Type: %s\n", tmp);
-
-		/* print the compression algorithm (if any) */
-		tmp = gnutls_compression_get_name (gnutls_compression_get (session));
-		fd_log_debug("\t - Compression: %s\n", tmp);
-
-		/* print the name of the cipher used. ie 3DES. */
-		tmp = gnutls_cipher_get_name (gnutls_cipher_get (session));
-		fd_log_debug("\t - Cipher: %s\n", tmp);
-
-		/* Print the MAC algorithms name. ie SHA1 */
-		tmp = gnutls_mac_get_name (gnutls_mac_get (session));
-		fd_log_debug("\t - MAC: %s\n", tmp);
-	}
-	
-	/* First, use built-in verification */
-	CHECK_GNUTLS_DO( gnutls_certificate_verify_peers2 (session, &gtret), return EINVAL );
-	if (gtret) {
-		if (TRACE_BOOL(INFO)) {
-			fd_log_debug("TLS: Remote certificate invalid on socket %d (Remote: '%s')(Connection: '%s') :\n", conn->cc_socket, conn->cc_remid, conn->cc_id);
-			if (gtret & GNUTLS_CERT_INVALID)
-				fd_log_debug(" - The certificate is not trusted (unknown CA? expired?)\n");
-			if (gtret & GNUTLS_CERT_REVOKED)
-				fd_log_debug(" - The certificate has been revoked.\n");
-			if (gtret & GNUTLS_CERT_SIGNER_NOT_FOUND)
-				fd_log_debug(" - The certificate hasn't got a known issuer.\n");
-			if (gtret & GNUTLS_CERT_SIGNER_NOT_CA)
-				fd_log_debug(" - The certificate signer is not a CA, or uses version 1, or 3 without basic constraints.\n");
-			if (gtret & GNUTLS_CERT_INSECURE_ALGORITHM)
-				fd_log_debug(" - The certificate signature uses a weak algorithm.\n");
-		}
-		return EINVAL;
-	}
-	
-	/* Code from http://www.gnu.org/software/gnutls/manual/gnutls.html#Verifying-peer_0027s-certificate */
-	if (gnutls_certificate_type_get (session) != GNUTLS_CRT_X509)
-		return EINVAL;
-	
-	GNUTLS_TRACE( cert_list = gnutls_certificate_get_peers (session, &cert_list_size) );
-	if (cert_list == NULL)
-		return EINVAL;
-	
-	now = time(NULL);
-	
-	if (verbose && TRACE_BOOL(FULL)) {
-		char serial[40];
-		char dn[128];
-		size_t size;
-		unsigned int algo, bits;
-		time_t expiration_time, activation_time;
-		
-		fd_log_debug("TLS Certificate information for connection '%s' (%d certs provided):\n", conn->cc_id, cert_list_size);
-		for (i = 0; i < cert_list_size; i++)
-		{
-
-			CHECK_GNUTLS_DO( gnutls_x509_crt_init (&cert), return EINVAL);
-			CHECK_GNUTLS_DO( gnutls_x509_crt_import (cert, &cert_list[i], GNUTLS_X509_FMT_DER), return EINVAL);
-		
-			fd_log_debug(" Certificate %d info:\n", i);
-
-			GNUTLS_TRACE( expiration_time = gnutls_x509_crt_get_expiration_time (cert) );
-			GNUTLS_TRACE( activation_time = gnutls_x509_crt_get_activation_time (cert) );
-
-			fd_log_debug("\t - Certificate is valid since: %s", ctime (&activation_time));
-			fd_log_debug("\t - Certificate expires: %s", ctime (&expiration_time));
-
-			/* Print the serial number of the certificate. */
-			size = sizeof (serial);
-			gnutls_x509_crt_get_serial (cert, serial, &size);
-			
-			fd_log_debug("\t - Certificate serial number: ");
-			{
-				int j;
-				for (j = 0; j < size; j++) {
-					fd_log_debug("%02.2hhx", serial[j]);
-				}
-			}
-			fd_log_debug("\n");
-
-			/* Extract some of the public key algorithm's parameters */
-			GNUTLS_TRACE( algo = gnutls_x509_crt_get_pk_algorithm (cert, &bits) );
-			fd_log_debug("\t - Certificate public key: %s\n",
-			      gnutls_pk_algorithm_get_name (algo));
-
-			/* Print the version of the X.509 certificate. */
-			fd_log_debug("\t - Certificate version: #%d\n",
-			      gnutls_x509_crt_get_version (cert));
-
-			size = sizeof (dn);
-			GNUTLS_TRACE( gnutls_x509_crt_get_dn (cert, dn, &size) );
-			fd_log_debug("\t - DN: %s\n", dn);
-
-			size = sizeof (dn);
-			GNUTLS_TRACE( gnutls_x509_crt_get_issuer_dn (cert, dn, &size) );
-			fd_log_debug("\t - Issuer's DN: %s\n", dn);
-
-			GNUTLS_TRACE( gnutls_x509_crt_deinit (cert) );
-		}
-	}
-
-	/* Check validity of all the certificates */
-	for (i = 0; i < cert_list_size; i++)
-	{
-		time_t deadline;
-		
-		CHECK_GNUTLS_DO( gnutls_x509_crt_init (&cert), return EINVAL);
-		CHECK_GNUTLS_DO( gnutls_x509_crt_import (cert, &cert_list[i], GNUTLS_X509_FMT_DER), return EINVAL);
-		
-		GNUTLS_TRACE( deadline = gnutls_x509_crt_get_expiration_time(cert) );
-		if ((deadline != (time_t)-1) && (deadline < now)) {
-			if (TRACE_BOOL(INFO)) {
-				fd_log_debug("TLS: Remote certificate invalid on socket %d (Remote: '%s')(Connection: '%s') :\n", conn->cc_socket, conn->cc_remid, conn->cc_id);
-				fd_log_debug(" - The certificate %d in the chain is expired\n", i);
-			}
-			ret = EINVAL;
-		}
-		
-		GNUTLS_TRACE( deadline = gnutls_x509_crt_get_activation_time(cert) );
-		if ((deadline != (time_t)-1) && (deadline > now)) {
-			if (TRACE_BOOL(INFO)) {
-				fd_log_debug("TLS: Remote certificate invalid on socket %d (Remote: '%s')(Connection: '%s') :\n", conn->cc_socket, conn->cc_remid, conn->cc_id);
-				fd_log_debug(" - The certificate %d in the chain is not yet activated\n", i);
-			}
-			ret = EINVAL;
-		}
-		
-		if ((i == 0) && (conn->cc_tls_para.cn)) {
-			if (!gnutls_x509_crt_check_hostname (cert, conn->cc_tls_para.cn)) {
-				if (TRACE_BOOL(INFO)) {
-					fd_log_debug("TLS: Remote certificate invalid on socket %d (Remote: '%s')(Connection: '%s') :\n", conn->cc_socket, conn->cc_remid, conn->cc_id);
-					fd_log_debug(" - The certificate hostname does not match '%s'\n", conn->cc_tls_para.cn);
-				}
-				ret = EINVAL;
-			}
-		}
-		
-		GNUTLS_TRACE( gnutls_x509_crt_deinit (cert) );
-	}
-
-	return ret;
-}
-
-/* TLS handshake a connection; no need to have called start_clear before. Reception is active if handhsake is successful */
-int fd_cnx_handshake(struct cnxctx * conn, int mode, char * priority, void * alt_creds)
-{
-	TRACE_ENTRY( "%p %d %p %p", conn, mode, priority, alt_creds);
-	CHECK_PARAMS( conn && (!(conn->cc_status & CC_STATUS_TLS)) && ( (mode == GNUTLS_CLIENT) || (mode == GNUTLS_SERVER) ) && (!conn->cc_loop) );
-
-	/* Save the mode */
-	conn->cc_tls_para.mode = mode;
-	
-	/* Cancel receiving thread if any -- it should already be terminated anyway, we just release the resources */
-	CHECK_FCT_DO( fd_thr_term(&conn->cc_rcvthr), /* continue */);
-	
-	/* Once TLS handshake is done, we don't stop after the first message */
-	conn->cc_loop = 1;
-	
-	/* Prepare the master session credentials and priority */
-	CHECK_FCT( fd_tls_prepare(&conn->cc_tls_para.session, mode, priority, alt_creds) );
-
-	/* Special case: multi-stream TLS is not natively managed in GNU TLS, we use a wrapper library */
-	if (conn->cc_sctp_para.pairs > 1) {
-#ifdef DISABLE_SCTP
-		ASSERT(0);
-		CHECK_FCT( ENOTSUP );
-#else /* DISABLE_SCTP */
-		/* Initialize the wrapper, start the demux thread */
-		CHECK_FCT( fd_sctps_init(conn) );
-#endif /* DISABLE_SCTP */
-	} else {
-		/* Set the transport pointer passed to push & pull callbacks */
-		GNUTLS_TRACE( gnutls_transport_set_ptr( conn->cc_tls_para.session, (gnutls_transport_ptr_t) conn ) );
-
-		/* Set the push and pull callbacks */
-		GNUTLS_TRACE( gnutls_transport_set_pull_function(conn->cc_tls_para.session, (void *)fd_cnx_s_recv) );
-		GNUTLS_TRACE( gnutls_transport_set_push_function(conn->cc_tls_para.session, (void *)fd_cnx_s_send) );
-	}
-
-	/* Mark the connection as protected from here, so that the gnutls credentials will be freed */
-	fd_cpu_flush_cache();
-	conn->cc_status |= CC_STATUS_TLS;
-
-	/* Handshake master session */
-	{
-		int ret;
-		
-		/* When gnutls 2.10.1 is around, we should use gnutls_certificate_set_verify_function and fd_tls_verify_credentials, so that handshake fails directly. */
-		
-		CHECK_GNUTLS_DO( ret = gnutls_handshake(conn->cc_tls_para.session),
-			{
-				if (TRACE_BOOL(INFO)) {
-					fd_log_debug("TLS Handshake failed on socket %d (%s) : %s\n", conn->cc_socket, conn->cc_id, gnutls_strerror(ret));
-				}
-				fd_cnx_markerror(conn);
-				return EINVAL;
-			} );
-
-		/* Now verify the remote credentials are valid -- only simple tests here */
-		CHECK_FCT_DO( fd_tls_verify_credentials(conn->cc_tls_para.session, conn, 1), 
-			{  
-				CHECK_GNUTLS_DO( gnutls_bye(conn->cc_tls_para.session, GNUTLS_SHUT_RDWR),  );
-				fd_cnx_markerror(conn);
-				return EINVAL;
-			});
-	}
-
-	/* Multi-stream TLS: handshake other streams as well */
-	if (conn->cc_sctp_para.pairs > 1) {
-#ifndef DISABLE_SCTP
-		/* Start reading the messages from the master session. That way, if the remote peer closed, we are not stuck inside handshake */
-		CHECK_FCT(fd_sctps_startthreads(conn, 0));
-		
-		/* Resume all additional sessions from the master one. */
-		CHECK_FCT(fd_sctps_handshake_others(conn, priority, alt_creds));
-
-		/* Start decrypting the messages from all threads and queuing them in target queue */
-		CHECK_FCT(fd_sctps_startthreads(conn, 1));
-#endif /* DISABLE_SCTP */
-	} else {
-		/* Start decrypting the data */
-		CHECK_POSIX( pthread_create( &conn->cc_rcvthr, NULL, rcvthr_tls_single, conn ) );
-	}
-	
-	return 0;
-}
-
-/* Retrieve TLS credentials of the remote peer, after handshake */
-int fd_cnx_getcred(struct cnxctx * conn, const gnutls_datum_t **cert_list, unsigned int *cert_list_size)
-{
-	TRACE_ENTRY("%p %p %p", conn, cert_list, cert_list_size);
-	CHECK_PARAMS( conn && (conn->cc_status & CC_STATUS_TLS) && cert_list && cert_list_size );
-	
-	/* This function only works for X.509 certificates. */
-	CHECK_PARAMS( gnutls_certificate_type_get (conn->cc_tls_para.session) == GNUTLS_CRT_X509 );
-	
-	GNUTLS_TRACE( *cert_list = gnutls_certificate_get_peers (conn->cc_tls_para.session, cert_list_size) );
-	if (*cert_list == NULL) {
-		TRACE_DEBUG(INFO, "No certificate was provided by remote peer / an error occurred.");
-		return EINVAL;
-	}
-
-	TRACE_DEBUG( FULL, "Saved certificate chain (%d certificates) in peer structure.", *cert_list_size);
-	
-	return 0;
-}
-
-/* Receive next message. if timeout is not NULL, wait only until timeout. This function only pulls from a queue, mgr thread is filling that queue aynchrounously. */
-/* if the altfifo has been set on this conn object, this function must not be called */
-int fd_cnx_receive(struct cnxctx * conn, struct timespec * timeout, unsigned char **buf, size_t * len)
-{
-	int    ev;
-	size_t ev_sz;
-	void * ev_data;
-	
-	TRACE_ENTRY("%p %p %p %p", conn, timeout, buf, len);
-	CHECK_PARAMS(conn && (conn->cc_socket > 0) && buf && len);
-	CHECK_PARAMS(conn->cc_rcvthr != (pthread_t)NULL);
-	CHECK_PARAMS(conn->cc_alt == NULL);
-
-	/* Now, pull the first event */
-get_next:
-	if (timeout) {
-		CHECK_FCT( fd_event_timedget(conn->cc_incoming, timeout, FDEVP_PSM_TIMEOUT, &ev, &ev_sz, &ev_data) );
-	} else {
-		CHECK_FCT( fd_event_get(conn->cc_incoming, &ev, &ev_sz, &ev_data) );
-	}
-	
-	switch (ev) {
-		case FDEVP_CNX_MSG_RECV:
-			/* We got one */
-			*len = ev_sz;
-			*buf = ev_data;
-			return 0;
-			
-		case FDEVP_PSM_TIMEOUT:
-			TRACE_DEBUG(FULL, "Timeout event received");
-			return ETIMEDOUT;
-			
-		case FDEVP_CNX_EP_CHANGE:
-			/* We ignore this event */
-			goto get_next;
-			
-		case FDEVP_CNX_ERROR:
-			TRACE_DEBUG(FULL, "Received ERROR event on the connection");
-			return ENOTCONN;
-	}
-	
-	TRACE_DEBUG(INFO, "Received unexpected event %d (%s)", ev, fd_pev_str(ev));
-	return EINVAL;
-}
-
-/* Set an alternate FIFO list to send FDEVP_CNX_* events to */
-int fd_cnx_recv_setaltfifo(struct cnxctx * conn, struct fifo * alt_fifo)
-{
-	TRACE_ENTRY( "%p %p", conn, alt_fifo );
-	CHECK_PARAMS( conn && alt_fifo && conn->cc_incoming );
-	
-	/* The magic function does it all */
-	CHECK_FCT( fd_fifo_move( conn->cc_incoming, alt_fifo, &conn->cc_alt ) );
-	
-	return 0;
-}
-
-/* Send function when no multi-stream is involved, or sending on stream #0 (send() always use stream 0)*/
-static int send_simple(struct cnxctx * conn, unsigned char * buf, size_t len)
-{
-	ssize_t ret;
-	size_t sent = 0;
-	TRACE_ENTRY("%p %p %zd", conn, buf, len);
-	do {
-		fd_cpu_flush_cache();
-		if (conn->cc_status & CC_STATUS_TLS) {
-			CHECK_GNUTLS_DO( ret = fd_tls_send_handle_error(conn, conn->cc_tls_para.session, buf + sent, len - sent),  );
-		} else {
-			/* Maybe better to replace this call with sendmsg for atomic sending? */
-			CHECK_SYS_DO( ret = fd_cnx_s_send(conn, buf + sent, len - sent), );
-		}
-		if (ret <= 0)
-			return ENOTCONN;
-		
-		sent += ret;
-	} while ( sent < len );
-	return 0;
-}
-
-/* Send a message -- this is synchronous -- and we assume it's never called by several threads at the same time, so we don't protect. */
-int fd_cnx_send(struct cnxctx * conn, unsigned char * buf, size_t len, uint32_t flags)
-{
-	TRACE_ENTRY("%p %p %zd %x", conn, buf, len, flags);
-	
-	CHECK_PARAMS(conn && (conn->cc_socket > 0) && (! (conn->cc_status & CC_STATUS_ERROR)) && buf && len);
-
-	TRACE_DEBUG(FULL, "Sending %zdb %sdata on connection %s", len, (conn->cc_status & CC_STATUS_TLS) ? "TLS-protected ":"", conn->cc_id);
-	
-	switch (conn->cc_proto) {
-		case IPPROTO_TCP:
-			CHECK_FCT( send_simple(conn, buf, len) );
-			break;
-		
-#ifndef DISABLE_SCTP
-		case IPPROTO_SCTP: {
-			if (flags & FD_CNX_BROADCAST) {
-				/* Send the buffer over all other streams */
-				uint16_t str;
-				fd_cpu_flush_cache();
-				if (conn->cc_status & CC_STATUS_TLS) {
-					for ( str=1; str < conn->cc_sctp_para.pairs; str++) {
-						ssize_t ret;
-						size_t sent = 0;
-						do {
-							CHECK_GNUTLS_DO( ret = fd_tls_send_handle_error(conn, conn->cc_sctps_data.array[str].session, buf + sent, len - sent), );
-							if (ret <= 0)
-								return ENOTCONN;
-
-							sent += ret;
-						} while ( sent < len );
-					}
-				} else {
-					for ( str=1; str < conn->cc_sctp_para.str_out; str++) {
-						CHECK_FCT_DO( fd_sctp_sendstr(conn->cc_socket, str, buf, len, &conn->cc_status), { fd_cnx_markerror(conn); return ENOTCONN; } );
-					}
-				}
-				
-				/* Set the ORDERED flag also so that it is sent over stream 0 as well */
-				flags &= FD_CNX_ORDERED;
-			}
-			
-			if (flags & FD_CNX_ORDERED) {
-				/* We send over stream #0 */
-				CHECK_FCT( send_simple(conn, buf, len) );
-			} else {
-				/* Default case : no flag specified */
-			
-				int another_str = 0; /* do we send over stream #0 ? */
-				
-				if ((conn->cc_sctp_para.str_out > 1) && ((! (conn->cc_status & CC_STATUS_TLS)) || (conn->cc_sctp_para.pairs > 1)))  {
-					/* Update the id of the stream we will send this message over */
-					conn->cc_sctp_para.next += 1;
-					conn->cc_sctp_para.next %= ((conn->cc_status & CC_STATUS_TLS) ? conn->cc_sctp_para.pairs : conn->cc_sctp_para.str_out);
-					another_str = (conn->cc_sctp_para.next ? 1 : 0);
-				}
-
-				if ( ! another_str ) {
-					CHECK_FCT( send_simple(conn, buf, len) );
-				} else {
-					if (!(conn->cc_status & CC_STATUS_TLS)) {
-						CHECK_FCT_DO( fd_sctp_sendstr(conn->cc_socket, conn->cc_sctp_para.next, buf, len, &conn->cc_status), { fd_cnx_markerror(conn); return ENOTCONN; } );
-					} else {
-						/* push the record to the appropriate session */
-						ssize_t ret;
-						size_t sent = 0;
-						ASSERT(conn->cc_sctps_data.array != NULL);
-						do {
-							CHECK_GNUTLS_DO( ret = fd_tls_send_handle_error(conn, conn->cc_sctps_data.array[conn->cc_sctp_para.next].session, buf + sent, len - sent), );
-							if (ret <= 0)
-								return ENOTCONN;
-
-							sent += ret;
-						} while ( sent < len );
-					}
-				}
-			}
-		}
-		break;
-#endif /* DISABLE_SCTP */
-	
-		default:
-			TRACE_DEBUG(INFO, "Unknwon protocol: %d", conn->cc_proto);
-			ASSERT(0);
-			return ENOTSUP;	/* or EINVAL... */
-	}
-	
-	return 0;
-}
-
-
-/**************************************/
-/*     Destruction of connection      */
-/**************************************/
-
-/* Destroy a conn structure, and shutdown the socket */
-void fd_cnx_destroy(struct cnxctx * conn)
-{
-	TRACE_ENTRY("%p", conn);
-	
-	CHECK_PARAMS_DO(conn, return);
-	
-	fd_cpu_flush_cache();
-	conn->cc_status |= CC_STATUS_CLOSING;
-	
-	/* Initiate shutdown of the TLS session(s): call gnutls_bye(WR), then read until error */
-	if (conn->cc_status & CC_STATUS_TLS) {
-#ifndef DISABLE_SCTP
-		if (conn->cc_sctp_para.pairs > 1) {
-			if (! (conn->cc_status & CC_STATUS_ERROR )) {
-				/* Bye on master session */
-				CHECK_GNUTLS_DO( gnutls_bye(conn->cc_tls_para.session, GNUTLS_SHUT_WR), fd_cnx_markerror(conn) );
-			}
-
-			if (! (conn->cc_status & CC_STATUS_ERROR ) ) {
-				/* and other stream pairs */
-				fd_sctps_bye(conn);
-			}
-
-			if (! (conn->cc_status & CC_STATUS_ERROR ) ) {
-				/* Now wait for all decipher threads to terminate */
-				fd_sctps_waitthreadsterm(conn);
-			} else {
-				/* Abord the threads, the connection is dead already */
-				fd_sctps_stopthreads(conn);
-			}
-
-			/* Deinit gnutls resources */
-			fd_sctps_gnutls_deinit_others(conn);
-			if (conn->cc_tls_para.session) {
-				GNUTLS_TRACE( gnutls_deinit(conn->cc_tls_para.session) );
-				conn->cc_tls_para.session = NULL;
-			}
-			
-			/* Destroy the wrapper (also stops the demux thread) */
-			fd_sctps_destroy(conn);
-
-		} else {
-#endif /* DISABLE_SCTP */
-		/* We are not using the sctps wrapper layer */
-			if (! (conn->cc_status & CC_STATUS_ERROR ) ) {
-				/* Master session */
-				CHECK_GNUTLS_DO( gnutls_bye(conn->cc_tls_para.session, GNUTLS_SHUT_WR), fd_cnx_markerror(conn) );
-			}
-
-			if (! (conn->cc_status & CC_STATUS_ERROR ) ) {
-				/* In this case, just wait for thread rcvthr_tls_single to terminate */
-				if (conn->cc_rcvthr != (pthread_t)NULL) {
-					CHECK_POSIX_DO(  pthread_join(conn->cc_rcvthr, NULL), /* continue */  );
-					conn->cc_rcvthr = (pthread_t)NULL;
-				}
-			} else {
-				/* Cancel the receiver thread in case it did not already terminate */
-				CHECK_FCT_DO( fd_thr_term(&conn->cc_rcvthr), /* continue */ );
-			}
-			
-			/* Free the resources of the TLS session */
-			if (conn->cc_tls_para.session) {
-				GNUTLS_TRACE( gnutls_deinit(conn->cc_tls_para.session) );
-				conn->cc_tls_para.session = NULL;
-			}
-		
-#ifndef DISABLE_SCTP
-		}
-#endif /* DISABLE_SCTP */
-	}
-	
-	/* Terminate the thread in case it is not done yet -- is there any such case left ?*/
-	CHECK_FCT_DO( fd_thr_term(&conn->cc_rcvthr), /* continue */ );
-		
-	/* Shut the connection down */
-	if (conn->cc_socket > 0) {
-		shutdown(conn->cc_socket, SHUT_RDWR);
-		close(conn->cc_socket);
-		conn->cc_socket = -1;
-	}
-	
-	/* Empty and destroy FIFO list */
-	if (conn->cc_incoming) {
-		fd_event_destroy( &conn->cc_incoming, free );
-	}
-	
-	/* Free the object */
-	free(conn);
-	
-	/* Done! */
-	return;
-}
Index: eeDiameter/cnxctx.h
===================================================================
--- freeDiameter/cnxctx.h	(revision 543)
+++ 	(revision )
@@ -1,141 +1,0 @@
-/*********************************************************************************************************
-* Software License Agreement (BSD License)                                                               *
-* Author: Sebastien Decugis <sdecugis@nict.go.jp>							 *
-*													 *
-* Copyright (c) 2010, 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 definitions for internal use in the connection context files */
-
-#ifndef _CNXCTX_H
-#define _CNXCTX_H
-
-/* The connection context structure */
-struct cnxctx {
-	char		cc_id[60];	/* The name of this connection */
-	char		cc_remid[60];	/* Id of remote peer */
-	
-	int 		cc_socket;	/* The socket object of the connection -- <=0 if no socket is created */
-
-	int 		cc_family;	/* AF_INET or AF_INET6 (mixed) */
-	int 		cc_proto;	/* IPPROTO_TCP or IPPROTO_SCTP */
-	uint32_t	cc_status;	/* True if the object is being destroyed: we don't send events anymore */
-	#define 	CC_STATUS_CLOSING	1
-	#define 	CC_STATUS_ERROR		2
-	#define 	CC_STATUS_SIGNALED	4
-	#define 	CC_STATUS_TLS		8
-
-	pthread_t	cc_rcvthr;	/* thread for receiving messages on the connection */
-	int		cc_loop;	/* tell the thread if it loops or stops after the first message is received */
-	
-	struct fifo *	cc_incoming;	/* FIFO queue of events received on the connection, FDEVP_CNX_* */
-	struct fifo *	cc_alt;		/* alternate fifo to send FDEVP_CNX_* events to. */
-	#define Target_Queue(cnx)	((cnx)->cc_alt ?: (cnx)->cc_incoming)
-
-	/* If cc_tls == true */
-	struct {
-		char 				*cn;		/* If not NULL, remote certif will be checked to match this Common Name */
-		int				 mode; 		/* GNUTLS_CLIENT / GNUTLS_SERVER */
-		gnutls_session_t 		 session;	/* Session object (stream #0 in case of SCTP) */
-	}		cc_tls_para;
-
-	/* If cc_proto == SCTP */
-	struct	{
-		uint16_t str_out;	/* Out streams */
-		uint16_t str_in;	/* In streams */
-		uint16_t pairs;		/* max number of pairs ( = min(in, out)) */
-		uint16_t next;		/* # of stream the next message will be sent to */
-	} 		cc_sctp_para;
-
-	/* If both conditions */
-	struct {
-		struct sctps_ctx *array; /* an array of cc_sctp_para.pairs elements -- the #0 is special (session is outside)*/
-		struct sr_store	 *sess_store; /* Session data of the master session, to resume the children sessions */
-	} 		cc_sctps_data;
-};
-
-void fd_cnx_markerror(struct cnxctx * conn);
-
-/* Socket */
-ssize_t fd_cnx_s_recv(struct cnxctx * conn, void *buffer, size_t length);
-void fd_cnx_s_setto(int sock);
-
-/* TLS */
-int fd_tls_rcvthr_core(struct cnxctx * conn, gnutls_session_t session);
-int fd_tls_prepare(gnutls_session_t * session, int mode, char * priority, void * alt_creds);
-int fd_tls_verify_credentials(gnutls_session_t session, struct cnxctx * conn, int verbose);
-
-/* TCP */
-int fd_tcp_create_bind_server( int * sock, sSA * sa, socklen_t salen );
-int fd_tcp_listen( int sock );
-int fd_tcp_client( int *sock, sSA * sa, socklen_t salen );
-int fd_tcp_get_local_ep(int sock, sSS * ss, socklen_t *sl);
-int fd_tcp_get_remote_ep(int sock, sSS * ss, socklen_t *sl);
-
-#ifndef DISABLE_SCTP
-/* SCTP */
-int fd_sctp_create_bind_server( int * sock, int family, struct fd_list * list, uint16_t port );
-int fd_sctp_listen( int sock );
-int fd_sctp_client( int *sock, int no_ip6, uint16_t port, struct fd_list * list );
-int fd_sctp_get_local_ep(int sock,  struct fd_list * list);
-int fd_sctp_get_remote_ep(int sock, struct fd_list * list);
-int fd_sctp_get_str_info( int sock, uint16_t *in, uint16_t *out, sSS *primary );
-int fd_sctp_sendstr(int sock, uint16_t strid, uint8_t * buf, size_t len, uint32_t * cc_closing);
-int fd_sctp_recvmeta(int sock, uint16_t * strid, uint8_t ** buf, size_t * len, int *event, uint32_t * cc_closing);
-
-/* TLS over SCTP (multi-stream) */
-struct sctps_ctx {
-	struct cnxctx 	*parent; 	/* for info such as socket, conn name, event list */
-	uint16_t	 strid;		/* Stream # of this session */
-	struct fifo	*raw_recv;	/* Raw data received on this stream, for demux */
-	struct {
-		uint8_t *buf;
-		size_t   bufsz;
-		size_t   offset;
-	} 		 partial;	/* If the pull function did not read the full content of first message in raw, it stores it here for next read call. */
-	pthread_t	 thr;		/* Thread to decrypt raw data in this pair of streams */
-	gnutls_session_t session;	/* TLS context using this pair of streams -- except if strid == 0, in that case session is outside the array */
-};
-
-int fd_sctps_init(struct cnxctx * conn);
-int fd_sctps_handshake_others(struct cnxctx * conn, char * priority, void * alt_creds);
-int fd_sctps_startthreads(struct cnxctx * conn, int others);
-void fd_sctps_bye(struct cnxctx * conn);
-void fd_sctps_waitthreadsterm(struct cnxctx * conn);
-void fd_sctps_gnutls_deinit_others(struct cnxctx * conn);
-void fd_sctps_stopthreads(struct cnxctx * conn);
-void fd_sctps_destroy(struct cnxctx * conn);
-
-#endif /* DISABLE_SCTP */
-
-
-#endif /* _CNXCTX_H */
-
Index: eeDiameter/config.c
===================================================================
--- freeDiameter/config.c	(revision 578)
+++ 	(revision )
@@ -1,488 +1,0 @@
-/*********************************************************************************************************
-* Software License Agreement (BSD License)                                                               *
-* Author: Sebastien Decugis <sdecugis@nict.go.jp>							 *
-*													 *
-* Copyright (c) 2010, 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 "fD.h"
-#include <sys/stat.h>
-
-/* Configuration management */
-
-#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 */
-
-/* Initialize the fd_g_config structure to default values */
-int fd_conf_init()
-{
-	TRACE_ENTRY();
-	
-	fd_g_config->cnf_eyec = EYEC_CONFIG;
-	
-	fd_g_config->cnf_timer_tc = 30;
-	fd_g_config->cnf_timer_tw = 30;
-	
-	fd_g_config->cnf_port     = 3868;
-	fd_g_config->cnf_port_tls = 3869;
-	fd_g_config->cnf_sctp_str = 30;
-	fd_g_config->cnf_dispthr  = 4;
-	fd_list_init(&fd_g_config->cnf_endpoints, NULL);
-	fd_list_init(&fd_g_config->cnf_apps, NULL);
-	#ifdef DISABLE_SCTP
-	fd_g_config->cnf_flags.no_sctp = 1;
-	#endif /* DISABLE_SCTP */
-	
-	fd_g_config->cnf_orstateid = (uint32_t) time(NULL);
-	
-	CHECK_FCT( fd_dict_init(&fd_g_config->cnf_dict) );
-	CHECK_FCT( fd_fifo_new(&fd_g_config->cnf_main_ev) );
-	
-	/* TLS parameters */
-	CHECK_GNUTLS_DO( gnutls_certificate_allocate_credentials (&fd_g_config->cnf_sec_data.credentials), return ENOMEM );
-	CHECK_GNUTLS_DO( gnutls_dh_params_init (&fd_g_config->cnf_sec_data.dh_cache), return ENOMEM );
-
-	return 0;
-}
-
-void fd_conf_dump()
-{
-	if (!TRACE_BOOL(INFO))
-		return;
-	
-	fd_log_debug("-- Configuration :\n");
-	fd_log_debug("  Debug trace level ...... : %+d\n", fd_g_debug_lvl);
-	fd_log_debug("  Configuration file ..... : %s\n", fd_g_config->cnf_file);
-	fd_log_debug("  Diameter Identity ...... : %s (l:%Zi)\n", fd_g_config->cnf_diamid, fd_g_config->cnf_diamid_len);
-	fd_log_debug("  Diameter Realm ......... : %s (l:%Zi)\n", fd_g_config->cnf_diamrlm, fd_g_config->cnf_diamrlm_len);
-	fd_log_debug("  Tc Timer ............... : %u\n", fd_g_config->cnf_timer_tc);
-	fd_log_debug("  Tw Timer ............... : %u\n", fd_g_config->cnf_timer_tw);
-	fd_log_debug("  Local port ............. : %hu\n", fd_g_config->cnf_port);
-	fd_log_debug("  Local secure port ...... : %hu\n", fd_g_config->cnf_port_tls);
-	fd_log_debug("  Number of SCTP streams . : %hu\n", fd_g_config->cnf_sctp_str);
-	fd_log_debug("  Number of server threads : %hu\n", fd_g_config->cnf_dispthr);
-	if (FD_IS_LIST_EMPTY(&fd_g_config->cnf_endpoints)) {
-		fd_log_debug("  Local endpoints ........ : Default (use all available)\n");
-	} else {
-		fd_log_debug("  Local endpoints ........ : \n");
-		fd_ep_dump( 29, &fd_g_config->cnf_endpoints );
-	}
-	if (FD_IS_LIST_EMPTY(&fd_g_config->cnf_apps)) {
-		fd_log_debug("  Local applications ..... : (none)\n");
-	} else {
-		struct fd_list * li = fd_g_config->cnf_apps.next;
-		fd_log_debug("  Local applications ..... : ");
-		while (li != &fd_g_config->cnf_apps) {
-			struct fd_app * app = (struct fd_app *)li;
-			if (li != fd_g_config->cnf_apps.next) fd_log_debug("                             ");
-			fd_log_debug("App: %u\t%s%s\tVnd: %u\n", 
-					app->appid,
-					app->flags.auth ? "Au" : "--",
-					app->flags.acct ? "Ac" : "--",
-					app->vndid);
-			li = li->next;
-		}
-	}
-	
-	fd_log_debug("  Flags : - IP ........... : %s\n", fd_g_config->cnf_flags.no_ip4 ? "DISABLED" : "Enabled");
-	fd_log_debug("          - IPv6 ......... : %s\n", fd_g_config->cnf_flags.no_ip6 ? "DISABLED" : "Enabled");
-	fd_log_debug("          - Relay app .... : %s\n", fd_g_config->cnf_flags.no_fwd ? "DISABLED" : "Enabled");
-	fd_log_debug("          - TCP .......... : %s\n", fd_g_config->cnf_flags.no_tcp ? "DISABLED" : "Enabled");
-	#ifdef DISABLE_SCTP
-	fd_log_debug("          - SCTP ......... : DISABLED (at compilation)\n");
-	#else /* DISABLE_SCTP */
-	fd_log_debug("          - SCTP ......... : %s\n", fd_g_config->cnf_flags.no_sctp ? "DISABLED" : "Enabled");
-	#endif /* DISABLE_SCTP */
-	fd_log_debug("          - Pref. proto .. : %s\n", fd_g_config->cnf_flags.pr_tcp ? "TCP" : "SCTP");
-	fd_log_debug("          - TLS method ... : %s\n", fd_g_config->cnf_flags.tls_alg ? "INBAND" : "Separate port");
-	
-	fd_log_debug("  TLS :   - Certificate .. : %s\n", fd_g_config->cnf_sec_data.cert_file ?: "(NONE)");
-	fd_log_debug("          - Private key .. : %s\n", fd_g_config->cnf_sec_data.key_file ?: "(NONE)");
-	fd_log_debug("          - CA (trust) ... : %s (%d certs)\n", fd_g_config->cnf_sec_data.ca_file ?: "(none)", fd_g_config->cnf_sec_data.ca_file_nr);
-	fd_log_debug("          - CRL .......... : %s\n", fd_g_config->cnf_sec_data.crl_file ?: "(none)");
-	fd_log_debug("          - Priority ..... : %s\n", fd_g_config->cnf_sec_data.prio_string ?: "(default: '" GNUTLS_DEFAULT_PRIORITY "')");
-	if (fd_g_config->cnf_sec_data.dh_file)
-		fd_log_debug("          - DH file ...... : %s\n", fd_g_config->cnf_sec_data.dh_file);
-	else
-		fd_log_debug("          - DH bits ...... : %d\n", fd_g_config->cnf_sec_data.dh_bits ?: GNUTLS_DEFAULT_DHBITS);
-	
-	fd_log_debug("  Origin-State-Id ........ : %u\n", fd_g_config->cnf_orstateid);
-}
-
-/* Parse the configuration file (using the yacc parser) */
-int fd_conf_parse()
-{
-	extern FILE * fddin;
-	
-	/* Attempt to find the configuration file */
-	if (!fd_g_config->cnf_file)
-		fd_g_config->cnf_file = FD_DEFAULT_CONF_FILENAME;
-	
-	fddin = fopen(fd_g_config->cnf_file, "r");
-	if ((fddin == NULL) && (*fd_g_config->cnf_file != '/')) {
-		/* We got a relative path, attempt to add the default directory prefix */
-		char * bkp = fd_g_config->cnf_file;
-		CHECK_MALLOC( fd_g_config->cnf_file = malloc(strlen(bkp) + strlen(DEFAULT_CONF_PATH) + 2) ); /* we will not free it, but not important */
-		sprintf( fd_g_config->cnf_file, DEFAULT_CONF_PATH "/%s", bkp );
-		fddin = fopen(fd_g_config->cnf_file, "r");
-	}
-	if (fddin == NULL) {
-		int ret = errno;
-		fprintf(stderr, "Unable to open configuration file %s for reading: %s\n", fd_g_config->cnf_file, strerror(ret));
-		return ret;
-	}
-	
-	/* call yacc parser */
-	TRACE_DEBUG (FULL, "Parsing configuration file: %s", fd_g_config->cnf_file);
-	CHECK_FCT(  fddparse(fd_g_config)  );
-	
-	/* close the file */
-	fclose(fddin);
-	
-	/* Check that TLS private key was given */
-	if (! fd_g_config->cnf_sec_data.key_file) {
-		fprintf(stderr, "Missing private key configuration for TLS. Please provide the TLS_cred configuration directive.\n");
-		return EINVAL;
-	}
-	
-	/* Resolve hostname if not provided */
-	if (fd_g_config->cnf_diamid == NULL) {
-#ifndef HOST_NAME_MAX
-#define HOST_NAME_MAX 1024
-#endif /* HOST_NAME_MAX */
-		char buf[HOST_NAME_MAX + 1];
-		struct addrinfo hints, *info;
-		int ret;
-		
-		/* local host name */
-		CHECK_SYS(gethostname(buf, sizeof(buf)));
-		
-		/* get FQDN */
-		memset(&hints, 0, sizeof hints);
-		hints.ai_flags = AI_CANONNAME;
-
-		ret = getaddrinfo(buf, NULL, &hints, &info);
-		if (ret != 0) {
-			fprintf(stderr, "Error resolving local FQDN :\n"
-					" '%s' : %s\n"
-					"Please provide Identity in configuration file.\n",
-					buf, gai_strerror(ret));
-			return EINVAL;
-		}
-		CHECK_MALLOC( fd_g_config->cnf_diamid = strdup(info->ai_canonname) );
-		freeaddrinfo(info);
-	}
-	
-	/* cache the length of the diameter id for the session module */
-	fd_g_config->cnf_diamid_len = strlen(fd_g_config->cnf_diamid);
-	
-	/* Handle the realm part */
-	if (fd_g_config->cnf_diamrlm == NULL) {
-		char * start = NULL;
-		
-		/* Check the diameter identity is a fqdn */
-		start = strchr(fd_g_config->cnf_diamid, '.');
-		if ((start == NULL) || (start[1] == '\0')) {
-			fprintf(stderr, "Unable to extract realm from the Identity '%s'.\n"
-					"Please fix your Identity setting or provide Realm.\n",
-					fd_g_config->cnf_diamid);
-			return EINVAL;
-		}		
-		
-		CHECK_MALLOC( fd_g_config->cnf_diamrlm = strdup( start + 1 )  ); 
-	}
-	fd_g_config->cnf_diamrlm_len = strlen(fd_g_config->cnf_diamrlm);
-	
-	/* Validate some flags */
-	if (fd_g_config->cnf_flags.no_ip4 && fd_g_config->cnf_flags.no_ip6) {
-		fprintf(stderr, "IP and IPv6 cannot be disabled at the same time.\n");
-		return EINVAL;
-	}
-	if (fd_g_config->cnf_flags.no_tcp && fd_g_config->cnf_flags.no_sctp) {
-		fprintf(stderr, "TCP and SCTP cannot be disabled at the same time.\n");
-		return EINVAL;
-	}
-	
-	/* Validate local endpoints */
-	if ((!FD_IS_LIST_EMPTY(&fd_g_config->cnf_endpoints)) && (fd_g_config->cnf_flags.no_ip4 || fd_g_config->cnf_flags.no_ip6)) {
-		struct fd_list * li;
-		for ( li = fd_g_config->cnf_endpoints.next; li != &fd_g_config->cnf_endpoints; li = li->next) {
-			struct fd_endpoint * ep = (struct fd_endpoint *)li;
-			if ( (fd_g_config->cnf_flags.no_ip4 && (ep->sa.sa_family == AF_INET))
-			   ||(fd_g_config->cnf_flags.no_ip6 && (ep->sa.sa_family == AF_INET6)) ) {
-				li = li->prev;
-				fd_list_unlink(&ep->chain);
-				if (TRACE_BOOL(INFO)) {
-					fd_log_debug("Info: Removing local address conflicting with the flags no_IP / no_IP6 : ");
-					sSA_DUMP_NODE( &ep->sa, NI_NUMERICHOST );
-					fd_log_debug("\n");
-				}
-				free(ep);
-			}
-		}
-	}
-	
-	/* Configure TLS default parameters */
-	if (! fd_g_config->cnf_sec_data.prio_string) {
-		const char * err_pos = NULL;
-		CHECK_GNUTLS_DO( gnutls_priority_init( 
-					&fd_g_config->cnf_sec_data.prio_cache,
-					GNUTLS_DEFAULT_PRIORITY,
-					&err_pos),
-				 { TRACE_DEBUG(INFO, "Error in priority string at position : %s", err_pos); return EINVAL; } );
-	}
-	
-	/* Verify that our certificate is valid -- otherwise remote peers will reject it */
-	{
-		int ret = 0, i;
-		
-		gnutls_datum_t certfile;
-		size_t alloc = 0;
-		
-		gnutls_x509_crt_t * certs = NULL;
-		unsigned int cert_max = 0;
-		
-		gnutls_x509_crt_t * CA_list;
-		int CA_list_length;
-		
-		gnutls_x509_crl_t * CRL_list;
-		int CRL_list_length;
-		
-		unsigned int verify;
-		time_t now;
-		
-		memset(&certfile, 0, sizeof(certfile));
-		
-		/* Read the certificate file */
-		FILE *stream = fopen (fd_g_config->cnf_sec_data.cert_file, "rb");
-		if (!stream) {
-			int err = errno;
-			TRACE_DEBUG(INFO, "An error occurred while opening '%s': %s\n", fd_g_config->cnf_sec_data.cert_file, strerror(err));
-			return err; 
-		}
-		do {
-			uint8_t * realloced = NULL;
-			size_t read = 0;
-			
-			if (alloc < certfile.size + BUFSIZ + 1) {
-				alloc += alloc / 2 + BUFSIZ + 1;
-				CHECK_MALLOC_DO( realloced = realloc(certfile.data, alloc),
-					{
-						free(certfile.data);
-						return ENOMEM;
-					} )
-				certfile.data = realloced;
-			}
-			
-			read = fread( certfile.data + certfile.size, 1, alloc - certfile.size - 1, stream );
-			certfile.size += read;
-			
-			if (ferror(stream)) {
-				int err = errno;
-				TRACE_DEBUG(INFO, "An error occurred while reading '%s': %s\n", fd_g_config->cnf_sec_data.cert_file, strerror(err));
-				return err; 
-			}
-		} while (!feof(stream));
-		certfile.data[certfile.size] = '\0';
-		fclose(stream);
-		
-		/* Import the certificate(s) */
-		GNUTLS_TRACE( ret = gnutls_x509_crt_list_import(NULL, &cert_max, &certfile, GNUTLS_X509_FMT_PEM, GNUTLS_X509_CRT_LIST_IMPORT_FAIL_IF_EXCEED) );
-		if (ret != GNUTLS_E_SHORT_MEMORY_BUFFER) {
-			CHECK_GNUTLS_DO(ret, return EINVAL);
-		}
-		
-		CHECK_MALLOC( certs = calloc(cert_max, sizeof(gnutls_x509_crt_t)) );
-		CHECK_GNUTLS_DO( gnutls_x509_crt_list_import(certs, &cert_max, &certfile, GNUTLS_X509_FMT_PEM, 0),
-			{
-				TRACE_DEBUG(INFO, "Failed to import the data from file '%s'", fd_g_config->cnf_sec_data.cert_file);
-				free(certfile.data);
-				return EINVAL;
-			} );
-		free(certfile.data);
-		
-		ASSERT(cert_max >= 1);
-		
-		/* Now, verify the list against the local CA and CRL */
-		GNUTLS_TRACE( gnutls_certificate_get_x509_cas (fd_g_config->cnf_sec_data.credentials, &CA_list, (unsigned int *) &CA_list_length) );
-		GNUTLS_TRACE( gnutls_certificate_get_x509_crls (fd_g_config->cnf_sec_data.credentials, &CRL_list, (unsigned int *) &CRL_list_length) );
-		CHECK_GNUTLS_DO( gnutls_x509_crt_list_verify(certs, cert_max, CA_list, CA_list_length, CRL_list, CRL_list_length, 0, &verify),
-			{
-				TRACE_DEBUG(INFO, "Failed to verify the local certificate '%s' against local credentials. Please check your certificate is valid.", fd_g_config->cnf_sec_data.cert_file);
-				return EINVAL;
-			} );
-		if (verify) {
-			fd_log_debug("TLS: Local certificate chain '%s' is invalid :\n", fd_g_config->cnf_sec_data.cert_file);
-			if (verify & GNUTLS_CERT_INVALID)
-				fd_log_debug(" - The certificate is not trusted (unknown CA? expired?)\n");
-			if (verify & GNUTLS_CERT_REVOKED)
-				fd_log_debug(" - The certificate has been revoked.\n");
-			if (verify & GNUTLS_CERT_SIGNER_NOT_FOUND)
-				fd_log_debug(" - The certificate hasn't got a known issuer.\n");
-			if (verify & GNUTLS_CERT_SIGNER_NOT_CA)
-				fd_log_debug(" - The certificate signer is not a CA, or uses version 1, or 3 without basic constraints.\n");
-			if (verify & GNUTLS_CERT_INSECURE_ALGORITHM)
-				fd_log_debug(" - The certificate signature uses a weak algorithm.\n");
-			return EINVAL;
-		}
-	
-		/* Check the local Identity is valid with the certificate */
-		if (!gnutls_x509_crt_check_hostname (certs[0], fd_g_config->cnf_diamid)) {
-			fd_log_debug("TLS: Local certificate '%s' is invalid :\n", fd_g_config->cnf_sec_data.cert_file);
-			fd_log_debug(" - The certificate hostname does not match '%s'\n", fd_g_config->cnf_diamid);
-			return EINVAL;
-		}
-		
-		/* Check validity of all the certificates in the chain */
-		now = time(NULL);
-		for (i = 0; i < cert_max; i++)
-		{
-			time_t deadline;
-
-			GNUTLS_TRACE( deadline = gnutls_x509_crt_get_expiration_time(certs[i]) );
-			if ((deadline != (time_t)-1) && (deadline < now)) {
-				fd_log_debug("TLS: Local certificate chain '%s' is invalid :\n", fd_g_config->cnf_sec_data.cert_file);
-				fd_log_debug(" - The certificate %d in the chain is expired\n", i);
-				return EINVAL;
-			}
-
-			GNUTLS_TRACE( deadline = gnutls_x509_crt_get_activation_time(certs[i]) );
-			if ((deadline != (time_t)-1) && (deadline > now)) {
-				fd_log_debug("TLS: Local certificate chain '%s' is invalid :\n", fd_g_config->cnf_sec_data.cert_file);
-				fd_log_debug(" - The certificate %d in the chain is not yet activated\n", i);
-				return EINVAL;
-			}
-		}
-		
-		/* Everything checked OK, free the certificate list */
-		for (i = 0; i < cert_max; i++)
-		{
-			GNUTLS_TRACE( gnutls_x509_crt_deinit (certs[i]) );
-		}
-		free(certs);
-	}
-	
-	
-	/* gnutls_certificate_set_verify_limits -- so far the default values are fine... */
-	
-	/* DH */
-	if (fd_g_config->cnf_sec_data.dh_file) {
-		gnutls_datum_t dhparams = { NULL, 0 };
-		size_t alloc = 0;
-		FILE *stream = fopen (fd_g_config->cnf_sec_data.dh_file, "rb");
-		if (!stream) {
-			int err = errno;
-			TRACE_DEBUG(INFO, "An error occurred while opening '%s': %s\n", fd_g_config->cnf_sec_data.dh_file, strerror(err));
-			return err; 
-		}
-		do {
-			uint8_t * realloced = NULL;
-			size_t read = 0;
-			
-			if (alloc < dhparams.size + BUFSIZ + 1) {
-				alloc += alloc / 2 + BUFSIZ + 1;
-				CHECK_MALLOC_DO( realloced = realloc(dhparams.data, alloc),
-					{
-						free(dhparams.data);
-						return ENOMEM;
-					} )
-				dhparams.data = realloced;
-			}
-			
-			read = fread( dhparams.data + dhparams.size, 1, alloc - dhparams.size - 1, stream );
-			dhparams.size += read;
-			
-			if (ferror(stream)) {
-				int err = errno;
-				TRACE_DEBUG(INFO, "An error occurred while reading '%s': %s\n", fd_g_config->cnf_sec_data.dh_file, strerror(err));
-				return err; 
-			}
-		} while (!feof(stream));
-		dhparams.data[dhparams.size] = '\0';
-		fclose(stream);
-		CHECK_GNUTLS_DO( gnutls_dh_params_import_pkcs3( 
-					fd_g_config->cnf_sec_data.dh_cache,
-					&dhparams,
-					GNUTLS_X509_FMT_PEM),
-					 { TRACE_DEBUG(INFO, "Error in DH bits value : %d", fd_g_config->cnf_sec_data.dh_bits ?: GNUTLS_DEFAULT_DHBITS); return EINVAL; } );
-		free(dhparams.data);
-		
-	} else {
-		TRACE_DEBUG(INFO, "Generating fresh Diffie-Hellman parameters of size %d (this takes some time)... ", fd_g_config->cnf_sec_data.dh_bits ?: GNUTLS_DEFAULT_DHBITS);
-		CHECK_GNUTLS_DO( gnutls_dh_params_generate2( 
-					fd_g_config->cnf_sec_data.dh_cache,
-					fd_g_config->cnf_sec_data.dh_bits ?: GNUTLS_DEFAULT_DHBITS),
-					 { TRACE_DEBUG(INFO, "Error in DH bits value : %d", fd_g_config->cnf_sec_data.dh_bits ?: GNUTLS_DEFAULT_DHBITS); return EINVAL; } );
-	}			
-	
-	return 0;
-}
-
-
-/* Destroy contents of fd_g_config structure */
-int fd_conf_deinit()
-{
-	TRACE_ENTRY();
-	
-	/* Free the TLS parameters */
-	gnutls_priority_deinit(fd_g_config->cnf_sec_data.prio_cache);
-	gnutls_dh_params_deinit(fd_g_config->cnf_sec_data.dh_cache);
-	gnutls_certificate_free_credentials(fd_g_config->cnf_sec_data.credentials);
-	
-	free(fd_g_config->cnf_sec_data.cert_file); fd_g_config->cnf_sec_data.cert_file = NULL;
-	free(fd_g_config->cnf_sec_data.key_file); fd_g_config->cnf_sec_data.key_file = NULL;
-	free(fd_g_config->cnf_sec_data.ca_file); fd_g_config->cnf_sec_data.ca_file = NULL;
-	free(fd_g_config->cnf_sec_data.crl_file); fd_g_config->cnf_sec_data.crl_file = NULL;
-	free(fd_g_config->cnf_sec_data.prio_string); fd_g_config->cnf_sec_data.prio_string = NULL;
-	free(fd_g_config->cnf_sec_data.dh_file); fd_g_config->cnf_sec_data.dh_file = NULL;
-	
-	/* Destroy dictionary */
-	CHECK_FCT_DO( fd_dict_fini(&fd_g_config->cnf_dict), );
-	
-	/* Destroy the main event queue */
-	CHECK_FCT_DO( fd_fifo_del(&fd_g_config->cnf_main_ev), );
-	
-	/* Destroy the local endpoints and applications */
-	CHECK_FCT_DO(fd_ep_filter(&fd_g_config->cnf_endpoints, 0 ), );
-	CHECK_FCT_DO(fd_app_empty(&fd_g_config->cnf_apps ), );
-	
-	/* Destroy the local identity */	
-	free(fd_g_config->cnf_diamid); fd_g_config->cnf_diamid = NULL;
-	free(fd_g_config->cnf_diamrlm); fd_g_config->cnf_diamrlm = NULL;
-	
-	return 0;
-}
-
-
Index: eeDiameter/dict_base_proto.c
===================================================================
--- freeDiameter/dict_base_proto.c	(revision 336)
+++ 	(revision )
@@ -1,3569 +1,0 @@
-/*********************************************************************************************************
-* Software License Agreement (BSD License)                                                               *
-* Author: Sebastien Decugis <sdecugis@nict.go.jp>							 *
-*													 *
-* Copyright (c) 2010, 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.								 *
-*********************************************************************************************************/
-
-/* Diameter Base protocol definitions.
- */
-
-#include "fD.h"
-
-#include <netinet/in.h>
-#include <sys/socket.h>
-
-/* The pointer for the global dictionary (initialized from main) */
-struct dictionary * fd_g_dict = NULL;
-
-/* The functions to encode and interpret the derived types defined in the base protocol */
-
-/* Address AVP <-> struct sockaddr_storage */
-static int Address_encode(void * data, union avp_value * avp_value)
-{
-	sSS * ss = (sSS *) data;
-	uint16_t AddressType = 0;
-	size_t	size = 0;
-	unsigned char * buf = NULL;
-	
-	TRACE_ENTRY("%p %p", data, avp_value);
-	CHECK_PARAMS( data && avp_value  );
-	
-	switch (ss->ss_family) {
-		case AF_INET:
-			{
-				/* We are encoding an IP address */
-				sSA4 * sin = (sSA4 *)ss;
-				
-				AddressType = 1;/* see http://www.iana.org/assignments/address-family-numbers/ */
-				size = 6;	/* 2 for AddressType + 4 for data */
-				
-				CHECK_MALLOC(  buf = malloc(size)  );
-				
-				/* may not work because of alignment: *(uint32_t *)(buf+2) = htonl(sin->sin_addr.s_addr); */
-				memcpy(buf + 2, &sin->sin_addr.s_addr, 4);
-			}
-			break;
-				
-		case AF_INET6:
-			{
-				/* We are encoding an IPv6 address */
-				sSA6 * sin6 = (sSA6 *)ss;
-				
-				AddressType = 2;/* see http://www.iana.org/assignments/address-family-numbers/ */
-				size = 18;	/* 2 for AddressType + 16 for data */
-				
-				CHECK_MALLOC(  buf = malloc(size)  );
-				
-				/* The order is already good here */
-				memcpy(buf + 2, &sin6->sin6_addr.s6_addr, 16);
-			}
-			break;
-				
-		default:
-			CHECK_PARAMS( AddressType = 0 );
-	}
-	
-	*(uint16_t *)buf = htons(AddressType);
-
-	avp_value->os.len = size;
-	avp_value->os.data = buf;
-	
-	return 0;
-}
-
-static int Address_interpret(union avp_value * avp_value, void * interpreted)
-{
-	uint16_t AddressType = 0;
-	unsigned char * buf;
-	
-	TRACE_ENTRY("%p %p", avp_value, interpreted);
-	
-	CHECK_PARAMS( avp_value && interpreted && (avp_value->os.len >= 2)  );
-	
-	AddressType = ntohs(*(uint16_t *)avp_value->os.data);
-	buf = &avp_value->os.data[2];
-	
-	switch (AddressType) {
-		case 1 /* IP */:
-			{
-				sSA4 * sin = (sSA4 *)interpreted;
-				
-				CHECK_PARAMS(  avp_value->os.len == 6  );
-				
-				sin->sin_family = AF_INET;
-				/* sin->sin_addr.s_addr = ntohl( * (uint32_t *) buf); -- may not work because of bad alignment */
-				memcpy(&sin->sin_addr.s_addr, buf, 4);
-			}
-			break;
-				
-		case 2 /* IP6 */:
-			{
-				sSA6 * sin6 = (sSA6 *)interpreted;
-				
-				CHECK_PARAMS(  avp_value->os.len == 18  );
-				
-				sin6->sin6_family = AF_INET6;
-				memcpy(&sin6->sin6_addr.s6_addr, buf, 16);
-			}
-			break;
-				
-		default:
-			CHECK_PARAMS( AddressType = 0 );
-	}
-	
-	return 0;
-}
-
-/* Dump the content of an Address AVP */
-static void Address_dump(union avp_value * avp_value)
-{
-	union {
-		sSA	sa;
-		sSS	ss;
-		sSA4	sin;
-		sSA6	sin6;
-	} s;
-	uint16_t fam;
-	
-	memset(&s, 0, sizeof(s));
-	
-	/* The first two octets represent the address family, http://www.iana.org/assignments/address-family-numbers/ */
-	if (avp_value->os.len < 2) {
-		fd_log_debug("[invalid length: %d]", avp_value->os.len);
-		return;
-	}
-	
-	/* Following octets are the address in network byte order already */
-	fam = avp_value->os.data[0] << 8 | avp_value->os.data[1];
-	switch (fam) {
-		case 1:
-			/* IP */
-			s.sa.sa_family = AF_INET;
-			if (avp_value->os.len != 6) {
-				fd_log_debug("[invalid IP length: %d]", avp_value->os.len);
-				return;
-			}
-			memcpy(&s.sin.sin_addr.s_addr, avp_value->os.data + 2, 4);
-			break;
-		case 2:
-			/* IP6 */
-			s.sa.sa_family = AF_INET6;
-			if (avp_value->os.len != 18) {
-				fd_log_debug("[invalid IP6 length: %d]", avp_value->os.len);
-				return;
-			}
-			memcpy(&s.sin6.sin6_addr.s6_addr, avp_value->os.data + 2, 16);
-			break;
-		default:
-			fd_log_debug("[unsupported family: 0x%hx]", fam);
-			return;
-	}
-
-	sSA_DUMP_NODE(&s.sa, NI_NUMERICHOST);
-}
-
-static void UTF8String_dump(union avp_value * avp_value)
-{
-	size_t len = avp_value->os.len;
-	if (len > 42)
-		len = 42; /* avoid very long strings */
-	fd_log_debug("%.*s", len, avp_value->os.data);
-}
-
-
-
-
-#define CHECK_dict_new( _type, _data, _parent, _ref )				\
-	CHECK_FCT(  fd_dict_new( dict, (_type), (_data), (_parent), (_ref))  );
-
-#define CHECK_dict_search( _type, _criteria, _what, _result )					\
-	CHECK_FCT(  fd_dict_search( dict, (_type), (_criteria), (_what), (_result), ENOENT)  );
-
-struct local_rules_definition {
-	char 			*avp_name;
-	enum rule_position	position;
-	int 			min;
-	int			max;
-};
-
-#define RULE_ORDER( _position ) ((((_position) == RULE_FIXED_HEAD) || ((_position) == RULE_FIXED_TAIL)) ? 1 : 0 )
-
-#define PARSE_loc_rules( _rulearray, _parent) {						\
-	int __ar;									\
-	for (__ar=0; __ar < sizeof(_rulearray) / sizeof((_rulearray)[0]); __ar++) {	\
-		struct dict_rule_data __data = { NULL, 					\
-			(_rulearray)[__ar].position,					\
-			0, 								\
-			(_rulearray)[__ar].min,						\
-			(_rulearray)[__ar].max};					\
-		__data.rule_order = RULE_ORDER(__data.rule_position);			\
-		CHECK_FCT(  fd_dict_search( 						\
-			dict,								\
-			DICT_AVP, 							\
-			AVP_BY_NAME, 							\
-			(_rulearray)[__ar].avp_name, 					\
-			&__data.rule_avp, 0 ) );					\
-		if ( !__data.rule_avp ) {						\
-			TRACE_DEBUG(INFO, "AVP Not found: '%s'", (_rulearray)[__ar].avp_name );	\
-			return ENOENT;							\
-		}									\
-		CHECK_FCT_DO( fd_dict_new( dict, DICT_RULE, &__data, _parent, NULL),	\
-			{								\
-				TRACE_DEBUG(INFO, "Error on rule with AVP '%s'",	\
-					 (_rulearray)[__ar].avp_name );			\
-				return EINVAL;						\
-			} );								\
-	}										\
-}
-
-int fd_dict_base_protocol(struct dictionary * dict)
-{
-	TRACE_ENTRY("%p", dict);
-	CHECK_PARAMS(dict);
-	
-	/* Vendors section */
-	{
-		/* The base RFC has no vendor information */
-		;
-	}
-	
-	/* Applications section */
-	{
-		/* base accounting application */
-		{
-			struct dict_application_data data = {          3, "Diameter Base Accounting" 	};
-			CHECK_dict_new( DICT_APPLICATION, &data, NULL, NULL);
-		}
-		
-		/* relay application */
-		{
-			struct dict_application_data data  = { 0xffffffff, "Relay" 				};
-			#if AI_RELAY != 0xffffffff
-			#error "AI_RELAY definition mismatch"
-			#endif
-			CHECK_dict_new( DICT_APPLICATION, &data , NULL, NULL);
-		}
-	}
-	
-	/* Derived AVP types section */
-	{
-		/* Address */
-		{
-			/*
-				The Address format is derived from the OctetString AVP Base
-				Format.  It is a discriminated union, representing, for example a
-				32-bit (IPv4) [RFC791] or 128-bit (IPv6) [RFC4291] address, most
-				significant octet first.  The first two octets of the Address AVP
-				represents the AddressType, which contains an Address Family
-				defined in [IANAADFAM].  The AddressType is used to discriminate
-				the content and format of the remaining octets.
-			*/
-			struct dict_type_data data = { AVP_TYPE_OCTETSTRING,	"Address"		, Address_interpret	, Address_encode,	Address_dump	};
-			CHECK_dict_new( DICT_TYPE, &data , NULL, NULL);
-		}
-		
-		/* Time */
-		{
-			/*
-				The Time format is derived from the OctetString AVP Base Format.
-				The string MUST contain four octets, in the same format as the
-				first four bytes are in the NTP timestamp format.  The NTP
-				Timestamp format is defined in chapter 3 of [RFC4330].
-
-				This represents the number of seconds since 0h on 1 January 1900
-				with respect to the Coordinated Universal Time (UTC).
-
-				On 6h 28m 16s UTC, 7 February 2036 the time value will overflow.
-				SNTP [RFC4330] describes a procedure to extend the time to 2104.
-				This procedure MUST be supported by all DIAMETER nodes.
-			*/
-			struct dict_type_data data = { AVP_TYPE_OCTETSTRING,	"Time"			, NULL			, NULL		, NULL		};
-			CHECK_dict_new( DICT_TYPE, &data , NULL, NULL);
-		}
-		
-		/* UTF8String */
-		{
-			/*
-				The UTF8String format is derived from the OctetString AVP Base
-				Format.  This is a human readable string represented using the
-				ISO/IEC IS 10646-1 character set, encoded as an OctetString using
-				the UTF-8 [RFC3629] transformation format described in RFC 3629.
-
-				Since additional code points are added by amendments to the 10646
-				standard from time to time, implementations MUST be prepared to
-				encounter any code point from 0x00000001 to 0x7fffffff.  Byte
-				sequences that do not correspond to the valid encoding of a code
-				point into UTF-8 charset or are outside this range are prohibited.
-
-				The use of control codes SHOULD be avoided.  When it is necessary
-				to represent a new line, the control code sequence CR LF SHOULD be
-				used.
-
-				The use of leading or trailing white space SHOULD be avoided.
-
-				For code points not directly supported by user interface hardware
-				or software, an alternative means of entry and display, such as
-				hexadecimal, MAY be provided.
-
-				For information encoded in 7-bit US-ASCII, the UTF-8 charset is
-				identical to the US-ASCII charset.
-
-				UTF-8 may require multiple bytes to represent a single character /
-				code point; thus the length of an UTF8String in octets may be
-				different from the number of characters encoded.
-
-				Note that the AVP Length field of an UTF8String is measured in
-				octets, not characters.
-			*/
-			struct dict_type_data data = { AVP_TYPE_OCTETSTRING,	"UTF8String"		, NULL			, NULL	, UTF8String_dump	};
-			CHECK_dict_new( DICT_TYPE, &data , NULL, NULL);
-		}
-		
-		/* DiameterIdentity */
-		{
-			/*
-				The DiameterIdentity format is derived from the OctetString AVP
-				Base Format.
-
-                				DiameterIdentity  = FQDN
-
-
-				DiameterIdentity value is used to uniquely identify a Diameter
-				node for purposes of duplicate connection and routing loop
-				detection.
-
-				The contents of the string MUST be the FQDN of the Diameter node.
-				If multiple Diameter nodes run on the same host, each Diameter
-				node MUST be assigned a unique DiameterIdentity.  If a Diameter
-
-				node can be identified by several FQDNs, a single FQDN should be
-				picked at startup, and used as the only DiameterIdentity for that
-				node, whatever the connection it is sent on.  Note that in this
-				document, DiameterIdentity is in ASCII form in order to be
-				compatible with existing DNS infrastructure.  See Appendix D for
-				interactions between the Diameter protocol and Internationalized
-				Domain Name (IDNs).
-			*/
-			struct dict_type_data data = { AVP_TYPE_OCTETSTRING,	"DiameterIdentity"	, NULL			, NULL		, UTF8String_dump	};
-			CHECK_dict_new( DICT_TYPE, &data , NULL, NULL);
-		}
-		
-		/* DiameterURI */
-		{
-			/*
-				The DiameterURI MUST follow the Uniform Resource Identifiers (URI)
-				syntax [RFC3986] rules specified below:
-
-				 "aaa://" FQDN [ port ] [ transport ] [ protocol ]
-
-                				 ; No transport security
-
-				 "aaas://" FQDN [ port ] [ transport ] [ protocol ]
-
-                				 ; Transport security used
-
-				 FQDN               = Fully Qualified Host Name
-
-				 port               = ":" 1*DIGIT
-
-                				 ; One of the ports used to listen for
-                				 ; incoming connections.
-                				 ; If absent,
-                				 ; the default Diameter port (3868) is
-                				 ; assumed.
-
-				 transport          = ";transport=" transport-protocol
-
-                				 ; One of the transports used to listen
-                				 ; for incoming connections.  If absent,
-                				 ; the default SCTP [RFC2960] protocol is
-                				 ; assumed. UDP MUST NOT be used when
-                				 ; the aaa-protocol field is set to
-                				 ; diameter.
-
-				transport-protocol = ( "tcp" / "sctp" / "udp" )
-
-				protocol           = ";protocol=" aaa-protocol
-
-                				 ; If absent, the default AAA protocol
-                				 ; is diameter.
-
-				aaa-protocol       = ( "diameter" / "radius" / "tacacs+" )
-
-				The following are examples of valid Diameter host identities:
-
-				aaa://host.example.com;transport=tcp
-				aaa://host.example.com:6666;transport=tcp
-				aaa://host.example.com;protocol=diameter
-				aaa://host.example.com:6666;protocol=diameter
-				aaa://host.example.com:6666;transport=tcp;protocol=diameter
-				aaa://host.example.com:1813;transport=udp;protocol=radius
-			*/
-			struct dict_type_data data = { AVP_TYPE_OCTETSTRING,	"DiameterURI"		, NULL			, NULL		, UTF8String_dump	};
-			CHECK_dict_new( DICT_TYPE, &data , NULL, NULL);
-		}
-		
-		/* Enumerated */
-		{
-			/*
-				Enumerated is derived from the Integer32 AVP Base Format.  The
-				definition contains a list of valid values and their
-				interpretation and is described in the Diameter application
-				introducing the AVP.
-			*/
-			
-			/* We don't use a generic "Enumerated" type in freeDiameter. Instead, we define
-			 * types of the form "Enumerated(<avpname>)" where <avpname> is replaced 
-			 * by the name of the AVP to which the type applies.
-			 *  Example: Enumerated(Disconnect-Cause)
-			 */
-			;
-		}
-		
-		/* IPFilterRule */
-		{
-			/*
-				The IPFilterRule format is derived from the OctetString AVP Base
-				Format and uses the ASCII charset.  The rule syntax is a modified
-				subset of ipfw(8) from FreeBSD.  Packets may be filtered based on
-				the following information that is associated with it:
-
-				    Direction                          (in or out)
-				    Source and destination IP address  (possibly masked)
-				    Protocol
-				    Source and destination port        (lists or ranges)
-				    TCP flags
-				    IP fragment flag
-				    IP options
-				    ICMP types
-
-				Rules for the appropriate direction are evaluated in order, with
-				the first matched rule terminating the evaluation.  Each packet is
-				evaluated once.  If no rule matches, the packet is dropped if the
-				last rule evaluated was a permit, and passed if the last rule was
-				a deny.
-
-				IPFilterRule filters MUST follow the format:
-				
-				    action dir proto from src to dst [options]
-				
-			(...skipped loooong explanation...)
-				
-				There is one kind of packet that the access device MUST always
-				discard, that is an IP fragment with a fragment offset of one.
-				This is a valid packet, but it only has one use, to try to
-				circumvent firewalls.
-
-				An access device that is unable to interpret or apply a deny rule
-				MUST terminate the session.  An access device that is unable to
-				interpret or apply a permit rule MAY apply a more restrictive
-				rule.  An access device MAY apply deny rules of its own before the
-				supplied rules, for example to protect the access device owner's
-				infrastructure.
-			*/
-			struct dict_type_data data = { AVP_TYPE_OCTETSTRING,	"IPFilterRule"		, NULL			, NULL		, UTF8String_dump	};
-			CHECK_dict_new( DICT_TYPE, &data , NULL, NULL);
-		}
-	}
-	
-	/* AVP section */
-	{
-		struct dict_object * Address_type;
-		struct dict_object * UTF8String_type;
-		struct dict_object * DiameterIdentity_type;
-		struct dict_object * DiameterURI_type;
-		struct dict_object * Time_type;
-		
-		CHECK_dict_search( DICT_TYPE, TYPE_BY_NAME, "Address", &Address_type);
-		CHECK_dict_search( DICT_TYPE, TYPE_BY_NAME, "UTF8String", &UTF8String_type);
-		CHECK_dict_search( DICT_TYPE, TYPE_BY_NAME, "DiameterIdentity", &DiameterIdentity_type);
-		CHECK_dict_search( DICT_TYPE, TYPE_BY_NAME, "DiameterURI", &DiameterURI_type);
-		CHECK_dict_search( DICT_TYPE, TYPE_BY_NAME, "Time", &Time_type);
-		
-		/* Vendor-Id */
-		{
-			/*
-				The Vendor-Id AVP (AVP Code 266) is of type Unsigned32 and contains
-				the IANA "SMI Network Management Private Enterprise Codes" [RFC3232]
-				value assigned to the vendor of the Diameter device.  It is
-				envisioned that the combination of the Vendor-Id, Product-Name
-				(Section 5.3.7) and the Firmware-Revision (Section 5.3.4) AVPs may
-				provide useful debugging information.
-
-				A Vendor-Id value of zero in the CER or CEA messages is reserved and
-				indicates that this field is ignored.
-			*/
-			struct dict_avp_data data = { 
-					266, 					/* Code */
-					#if AC_VENDOR_ID != 266
-					#error "AC_VENDOR_ID definition mismatch"
-					#endif
-					0, 					/* Vendor */
-					"Vendor-Id", 				/* Name */
-					AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, 	/* Fixed flags */
-					AVP_FLAG_MANDATORY, 			/* Fixed flag values */
-					AVP_TYPE_UNSIGNED32 			/* base type of data */
-					};
-			CHECK_dict_new( DICT_AVP, &data, NULL, NULL);
-		}
-		
-		/* Firmware-Revision */
-		{
-			/*
-				The Firmware-Revision AVP (AVP Code 267) is of type Unsigned32 and is
-				used to inform a Diameter peer of the firmware revision of the
-				issuing device.
-
-				For devices that do not have a firmware revision (general purpose
-				computers running Diameter software modules, for instance), the
-				revision of the Diameter software module may be reported instead.
-			*/
-			struct dict_avp_data data = { 
-					267, 					/* Code */
-					#if AC_FIRMWARE_REVISION != 267
-					#error "AC_FIRMWARE_REVISION definition mismatch"
-					#endif
-					0, 					/* Vendor */
-					"Firmware-Revision", 			/* Name */
-					AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, 	/* Fixed flags */
-					0,		 			/* Fixed flag values */
-					AVP_TYPE_UNSIGNED32 			/* base type of data */
-					};
-			CHECK_dict_new( DICT_AVP, &data , NULL, NULL);
-		}
-		
-		/* Host-IP-Address */
-		{
-			/*
-				The Host-IP-Address AVP (AVP Code 257) is of type Address and is used
-				to inform a Diameter peer of the sender's IP address.  All source
-				addresses that a Diameter node expects to use with SCTP [RFC2960]
-				MUST be advertised in the CER and CEA messages by including a
-				Host-IP- Address AVP for each address.  This AVP MUST ONLY be used in
-				the CER and CEA messages.
-			*/
-			struct dict_avp_data data = { 
-					257, 					/* Code */
-					#if AC_HOST_IP_ADDRESS != 257
-					#error "AC_HOST_IP_ADDRESS definition mismatch"
-					#endif
-					0, 					/* Vendor */
-					"Host-IP-Address", 			/* Name */
-					AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, 	/* Fixed flags */
-					AVP_FLAG_MANDATORY,			/* Fixed flag values */
-					AVP_TYPE_OCTETSTRING 			/* base type of data */
-					};
-			CHECK_dict_new( DICT_AVP, &data , Address_type, NULL);
-		}
-		
-		/* Supported-Vendor-Id */
-		{
-			/*
-				The Supported-Vendor-Id AVP (AVP Code 265) is of type Unsigned32 and
-				contains the IANA "SMI Network Management Private Enterprise Codes"
-				[RFC3232] value assigned to a vendor other than the device vendor but
-				including the application vendor.  This is used in the CER and CEA
-				messages in order to inform the peer that the sender supports (a
-				subset of) the vendor-specific AVPs defined by the vendor identified
-				in this AVP.  The value of this AVP SHOULD NOT be set to zero.
-				Multiple instances of this AVP containing the same value SHOULD NOT
-				be sent.
-			*/
-			struct dict_avp_data data = { 
-					265, 					/* Code */
-					#if AC_SUPPORTED_VENDOR_ID != 265
-					#error "AC_SUPPORTED_VENDOR_ID definition mismatch"
-					#endif
-					0, 					/* Vendor */
-					"Supported-Vendor-Id", 			/* Name */
-					AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, 	/* Fixed flags */
-					AVP_FLAG_MANDATORY,			/* Fixed flag values */
-					AVP_TYPE_UNSIGNED32 			/* base type of data */
-					};
-			CHECK_dict_new( DICT_AVP, &data , NULL, NULL);
-		}
-		
-		/* Product-Name */
-		{
-			/*
-				The Product-Name AVP (AVP Code 269) is of type UTF8String, and
-				contains the vendor assigned name for the product.  The Product-Name
-				AVP SHOULD remain constant across firmware revisions for the same
-				product.
-			*/
-			struct dict_avp_data data = { 
-					269, 					/* Code */
-					#if AC_PRODUCT_NAME != 269
-					#error "AC_PRODUCT_NAME definition mismatch"
-					#endif
-					0, 					/* Vendor */
-					"Product-Name", 			/* Name */
-					AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, 	/* Fixed flags */
-					0,					/* Fixed flag values */
-					AVP_TYPE_OCTETSTRING 			/* base type of data */
-					};
-			CHECK_dict_new( DICT_AVP, &data , UTF8String_type, NULL);
-		}
-		
-		/* Disconnect-Cause */
-		{
-			/*
-				The Disconnect-Cause AVP (AVP Code 273) is of type Enumerated.  A
-				Diameter node MUST include this AVP in the Disconnect-Peer-Request
-				message to inform the peer of the reason for its intention to
-				shutdown the transport connection.  The following values are
-				supported:
-
-				REBOOTING                         0
-				A scheduled reboot is imminent. Receiver of DPR with above result
-				code MAY attempt reconnection.
-
-				BUSY                              1
-				The peer's internal resources are constrained, and it has
-				determined that the transport connection needs to be closed.
-				Receiver of DPR with above result code SHOULD NOT attempt
-				reconnection.
-
-				DO_NOT_WANT_TO_TALK_TO_YOU        2
-				The peer has determined that it does not see a need for the
-				transport connection to exist, since it does not expect any
-				messages to be exchanged in the near future. Receiver of DPR
-				with above result code SHOULD NOT attempt reconnection.
-			*/
-			struct dict_object 	* 	type;
-			struct dict_type_data 		tdata = { AVP_TYPE_INTEGER32,	"Enumerated(Disconnect-Cause)"	, NULL, NULL, NULL };
-			struct dict_enumval_data 	t_0 = { "REBOOTING", 			{ .i32 = 0 }};
-			struct dict_enumval_data 	t_1 = { "BUSY", 			{ .i32 = 1 }};
-			struct dict_enumval_data 	t_2 = { "DO_NOT_WANT_TO_TALK_TO_YOU", 	{ .i32 = 2 }};
-			struct dict_avp_data 		data = { 
-					273, 					/* Code */
-					#if AC_DISCONNECT_CAUSE != 273
-					#error "AC_DISCONNECT_CAUSE definition mismatch"
-					#endif
-					0, 					/* Vendor */
-					"Disconnect-Cause", 			/* Name */
-					AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, 	/* Fixed flags */
-					AVP_FLAG_MANDATORY,			/* Fixed flag values */
-					AVP_TYPE_INTEGER32 			/* base type of data */
-					};
-			/* Create the Enumerated type, and then the AVP */
-			CHECK_dict_new( DICT_TYPE, &tdata , NULL, &type);
-			CHECK_dict_new( DICT_ENUMVAL, &t_0 , type, NULL);
-			CHECK_dict_new( DICT_ENUMVAL, &t_1 , type, NULL);
-			CHECK_dict_new( DICT_ENUMVAL, &t_2 , type, NULL);
-			CHECK_dict_new( DICT_AVP, &data , type, NULL);
-		}
-		
-		/* Origin-Host */
-		{
-			/*
-				The Origin-Host AVP (AVP Code 264) is of type DiameterIdentity, and
-				MUST be present in all Diameter messages.  This AVP identifies the
-				endpoint that originated the Diameter message.  Relay agents MUST NOT
-				modify this AVP.
-
-				The value of the Origin-Host AVP is guaranteed to be unique within a
-				single host.
-
-				Note that the Origin-Host AVP may resolve to more than one address as
-				the Diameter peer may support more than one address.
-
-				This AVP SHOULD be placed as close to the Diameter header as
-				possible.
-			*/
-			struct dict_avp_data data = { 
-					264, 					/* Code */
-					#if AC_ORIGIN_HOST != 264
-					#error "AC_ORIGIN_HOST definition mismatch"
-					#endif
-					0, 					/* Vendor */
-					"Origin-Host", 				/* Name */
-					AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, 	/* Fixed flags */
-					AVP_FLAG_MANDATORY,			/* Fixed flag values */
-					AVP_TYPE_OCTETSTRING 			/* base type of data */
-					};
-			CHECK_dict_new( DICT_AVP, &data , DiameterIdentity_type, NULL);
-		}
-		
-		/* Origin-Realm */
-		{
-			/*
-				The Origin-Realm AVP (AVP Code 296) is of type DiameterIdentity.
-				This AVP contains the Realm of the originator of any Diameter message
-				and MUST be present in all messages.
-
-				This AVP SHOULD be placed as close to the Diameter header as
-				possible.
-			*/
-			struct dict_avp_data data = { 
-					296, 					/* Code */
-					#if AC_ORIGIN_REALM != 296
-					#error "AC_ORIGIN_REALM definition mismatch"
-					#endif
-					0, 					/* Vendor */
-					"Origin-Realm", 			/* Name */
-					AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, 	/* Fixed flags */
-					AVP_FLAG_MANDATORY,			/* Fixed flag values */
-					AVP_TYPE_OCTETSTRING 			/* base type of data */
-					};
-			CHECK_dict_new( DICT_AVP, &data , DiameterIdentity_type, NULL);
-		}
-		
-		/* Destination-Host */
-		{
-			/*
-				The Destination-Host AVP (AVP Code 293) is of type DiameterIdentity.
-				This AVP MUST be present in all unsolicited agent initiated messages,
-				MAY be present in request messages, and MUST NOT be present in Answer
-				messages.
-
-				The absence of the Destination-Host AVP will cause a message to be
-				sent to any Diameter server supporting the application within the
-				realm specified in Destination-Realm AVP.
-
-				This AVP SHOULD be placed as close to the Diameter header as
-				possible.
-			*/
-			struct dict_avp_data data = { 
-					293, 					/* Code */
-					#if AC_DESTINATION_HOST != 293
-					#error "AC_DESTINATION_HOST definition mismatch"
-					#endif
-					0, 					/* Vendor */
-					"Destination-Host", 			/* Name */
-					AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, 	/* Fixed flags */
-					AVP_FLAG_MANDATORY,			/* Fixed flag values */
-					AVP_TYPE_OCTETSTRING 			/* base type of data */
-					};
-			CHECK_dict_new( DICT_AVP, &data , DiameterIdentity_type, NULL);
-		}
-		
-		/* Destination-Realm */
-		{
-			/*
-				The Destination-Realm AVP (AVP Code 283) is of type DiameterIdentity,
-				and contains the realm the message is to be routed to.  The
-				Destination-Realm AVP MUST NOT be present in Answer messages.
-				Diameter Clients insert the realm portion of the User-Name AVP.
-				Diameter servers initiating a request message use the value of the
-				Origin-Realm AVP from a previous message received from the intended
-				target host (unless it is known a priori).  When present, the
-				Destination-Realm AVP is used to perform message routing decisions.
-
-				Request messages whose ABNF does not list the Destination-Realm AVP
-				as a mandatory AVP are inherently non-routable messages.
-
-				This AVP SHOULD be placed as close to the Diameter header as
-				possible.
-			*/
-			struct dict_avp_data data = { 
-					283, 					/* Code */
-					#if AC_DESTINATION_REALM != 283
-					#error "AC_DESTINATION_REALM definition mismatch"
-					#endif
-					0, 					/* Vendor */
-					"Destination-Realm", 			/* Name */
-					AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, 	/* Fixed flags */
-					AVP_FLAG_MANDATORY,			/* Fixed flag values */
-					AVP_TYPE_OCTETSTRING 			/* base type of data */
-					};
-			CHECK_dict_new( DICT_AVP, &data , DiameterIdentity_type, NULL);
-		}
-		
-		/* Route-Record */
-		{
-			/*
-				The Route-Record AVP (AVP Code 282) is of type DiameterIdentity.  The
-				identity added in this AVP MUST be the same as the one received in
-				the Origin-Host of the Capabilities Exchange message.
-			*/
-			struct dict_avp_data data = { 
-					282, 					/* Code */
-					#if AC_ROUTE_RECORD != 282
-					#error "AC_ROUTE_RECORD definition mismatch"
-					#endif
-					0, 					/* Vendor */
-					"Route-Record", 			/* Name */
-					AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, 	/* Fixed flags */
-					AVP_FLAG_MANDATORY,			/* Fixed flag values */
-					AVP_TYPE_OCTETSTRING 			/* base type of data */
-					};
-			CHECK_dict_new( DICT_AVP, &data , DiameterIdentity_type, NULL);
-		}
-		
-		/* Proxy-Host */
-		{
-			/*
-				The Proxy-Host AVP (AVP Code 280) is of type DiameterIdentity.  This
-				AVP contains the identity of the host that added the Proxy-Info AVP.
-			*/
-			struct dict_avp_data adata = { 
-					280, 					/* Code */
-					#if AC_PROXY_HOST != 280
-					#error "AC_PROXY_HOST definition mismatch"
-					#endif
-					0, 					/* Vendor */
-					"Proxy-Host", 				/* Name */
-					AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, 	/* Fixed flags */
-					AVP_FLAG_MANDATORY,			/* Fixed flag values */
-					AVP_TYPE_OCTETSTRING 			/* base type of data */
-					};
-			CHECK_dict_new( DICT_AVP, &adata , DiameterIdentity_type, NULL);
-		}
-		
-		/* Proxy-State */
-		{
-			/*
-				The Proxy-State AVP (AVP Code 33) is of type OctetString, and
-				contains state local information, and MUST be treated as opaque data.
-			*/
-			struct dict_avp_data adata = { 
-					33, 					/* Code */
-					#if AC_PROXY_STATE != 33
-					#error "AC_PROXY_STATE definition mismatch"
-					#endif
-					0, 					/* Vendor */
-					"Proxy-State", 				/* Name */
-					AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, 	/* Fixed flags */
-					AVP_FLAG_MANDATORY,			/* Fixed flag values */
-					AVP_TYPE_OCTETSTRING 			/* base type of data */
-					};
-			CHECK_dict_new( DICT_AVP, &adata , NULL, NULL);
-		}
-			
-		/* Proxy-Info */
-		{
-			/*
-				The Proxy-Info AVP (AVP Code 284) is of type Grouped.  The Grouped
-				Data field has the following ABNF grammar:
-
-				 Proxy-Info ::= < AVP Header: 284 >
-                				{ Proxy-Host }
-                				{ Proxy-State }
-        				      * [ AVP ]
-			*/
-			struct dict_object * avp;
-			struct dict_avp_data data = { 
-					284, 					/* Code */
-					#if AC_PROXY_INFO != 284
-					#error "AC_PROXY_INFO definition mismatch"
-					#endif
-					0, 					/* Vendor */
-					"Proxy-Info", 				/* Name */
-					AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, 	/* Fixed flags */
-					AVP_FLAG_MANDATORY,			/* Fixed flag values */
-					AVP_TYPE_GROUPED 			/* base type of data */
-					};
-			struct local_rules_definition rules[] = 
-						{ 	 {  "Proxy-Host", 			RULE_REQUIRED, -1, 1 }
-							,{  "Proxy-State",			RULE_REQUIRED, -1, 1 }
-						};
-			
-			CHECK_dict_new( DICT_AVP, &data , NULL, &avp);
-			PARSE_loc_rules( rules, avp );
-		}
-		
-		/* Auth-Application-Id */
-		{
-			/*
-				The Auth-Application-Id AVP (AVP Code 258) is of type Unsigned32 and
-				is used in order to advertise support of the Authentication and
-				Authorization portion of an application (see Section 2.4).  If
-				present in a message other than CER and CEA, the value of the Auth-
-				Application-Id AVP MUST match the Application Id present in the
-				Diameter message header.
-			*/
-			struct dict_avp_data data = { 
-					258, 					/* Code */
-					#if AC_AUTH_APPLICATION_ID != 258
-					#error "AC_AUTH_APPLICATION_ID definition mismatch"
-					#endif
-					0, 					/* Vendor */
-					"Auth-Application-Id", 			/* Name */
-					AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, 	/* Fixed flags */
-					AVP_FLAG_MANDATORY,			/* Fixed flag values */
-					AVP_TYPE_UNSIGNED32 			/* base type of data */
-					};
-			CHECK_dict_new( DICT_AVP, &data , NULL, NULL);
-		}
-		
-		/* Acct-Application-Id */
-		{
-			/*
-				The Acct-Application-Id AVP (AVP Code 259) is of type Unsigned32 and
-				is used in order to advertise support of the Accounting portion of an
-				application (see Section 2.4).  If present in a message other than
-				CER and CEA, the value of the Acct-Application-Id AVP MUST match the
-				Application Id present in the Diameter message header.
-			*/
-			struct dict_avp_data data = { 
-					259, 					/* Code */
-					#if AC_ACCT_APPLICATION_ID != 259
-					#error "AC_ACCT_APPLICATION_ID definition mismatch"
-					#endif
-					0, 					/* Vendor */
-					"Acct-Application-Id", 			/* Name */
-					AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, 	/* Fixed flags */
-					AVP_FLAG_MANDATORY,			/* Fixed flag values */
-					AVP_TYPE_UNSIGNED32 			/* base type of data */
-					};
-			CHECK_dict_new( DICT_AVP, &data , NULL, NULL);
-		}
-		
-		/* Inband-Security-Id */
-		{
-			/*
-				The Inband-Security-Id AVP (AVP Code 299) is of type Unsigned32 and
-				is used in order to advertise support of the Security portion of the
-				application.
-
-				Currently, the following values are supported, but there is ample
-				room to add new security Ids.
-
-
-				NO_INBAND_SECURITY 0
-
-				This peer does not support TLS.  This is the default value, if the
-				AVP is omitted.
-
-				TLS 1
-
-				This node supports TLS security, as defined by [RFC4346].
-			*/
-			
-			/* Although the RFC does not specify an "Enumerated" type here, we go forward and create one.
-			 * This is the reason for the "*" in the type name
-			 */
-			
-			struct dict_object 	* 	type;
-			struct dict_type_data	 	tdata = { AVP_TYPE_UNSIGNED32,	"Enumerated*(Inband-Security-Id)"	, NULL, NULL, NULL };
-			struct dict_enumval_data 	t_0 = { "NO_INBAND_SECURITY", 		{ .u32 = ACV_ISI_NO_INBAND_SECURITY }};
-			struct dict_enumval_data 	t_1 = { "TLS", 			{ .u32 = ACV_ISI_TLS }};
-			struct dict_avp_data 		data = { 
-					299, 					/* Code */
-					#if AC_INBAND_SECURITY_ID != 299
-					#error "AC_INBAND_SECURITY_ID definition mismatch"
-					#endif
-					0, 					/* Vendor */
-					"Inband-Security-Id", 			/* Name */
-					AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, 	/* Fixed flags */
-					AVP_FLAG_MANDATORY,			/* Fixed flag values */
-					AVP_TYPE_UNSIGNED32 			/* base type of data */
-					};
-			/* Create the Enumerated type, and then the AVP */
-			CHECK_dict_new( DICT_TYPE, &tdata , NULL, &type);
-			CHECK_dict_new( DICT_ENUMVAL, &t_0 , type, NULL);
-			CHECK_dict_new( DICT_ENUMVAL, &t_1 , type, NULL);
-			CHECK_dict_new( DICT_AVP, &data , type, NULL);
-		}
-		
-		/* Vendor-Specific-Application-Id */
-		{
-			/*
-				The Vendor-Specific-Application-Id AVP (AVP Code 260) is of type
-				Grouped and is used to advertise support of a vendor-specific
-				Diameter Application.  Exactly one instance of either Auth-
-				Application-Id or Acct-Application-Id AVP MUST be present.  The
-				Application Id carried by either Auth-Application-Id or Acct-
-				Application-Id AVP MUST comply with vendor specific Application Id
-				assignment described in Sec 11.3.  It MUST also match the Application
-				Id present in the diameter header except when used in a CER or CEA
-				messages.
-
-				The Vendor-Id AVP is an informational AVP pertaining to the vendor
-				who may have authorship of the vendor-specific Diameter application.
-				It MUST NOT be used as a means of defining a completely separate
-				vendor-specific Application Id space.
-
-				This AVP MUST also be present as the first AVP in all experimental
-				commands defined in the vendor-specific application.
-
-				This AVP SHOULD be placed as close to the Diameter header as
-				possible.
-
-				AVP Format
-
-				<Vendor-Specific-Application-Id> ::= < AVP Header: 260 >
-                                				   { Vendor-Id }
-                                				   [ Auth-Application-Id ]
-                                				   [ Acct-Application-Id ]
-
-				A Vendor-Specific-Application-Id AVP MUST contain exactly one of
-				either Auth-Application-Id or Acct-Application-Id.  If a Vendor-
-				Specific-Application-Id is received without any of these two AVPs,
-				then the recipient SHOULD issue an answer with a Result-Code set to
-				DIAMETER_MISSING_AVP.  The answer SHOULD also include a Failed-AVP
-				which MUST contain an example of an Auth-Application-Id AVP and an
-				Acct-Application-Id AVP.
-
-				If a Vendor-Specific-Application-Id is received that contains both
-				Auth-Application-Id and Acct-Application-Id, then the recipient
-				SHOULD issue an answer with Result-Code set to
-				DIAMETER_AVP_OCCURS_TOO_MANY_TIMES.  The answer SHOULD also include a
-				Failed-AVP which MUST contain the received Auth-Application-Id AVP
-				and Acct-Application-Id AVP.
-			*/
-			struct dict_object 	* avp;
-			struct dict_avp_data	  data = { 
-					260, 					/* Code */
-					#if AC_VENDOR_SPECIFIC_APPLICATION_ID != 260
-					#error "AC_VENDOR_SPECIFIC_APPLICATION_ID definition mismatch"
-					#endif
-					0, 					/* Vendor */
-					"Vendor-Specific-Application-Id", 	/* Name */
-					AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, 	/* Fixed flags */
-					AVP_FLAG_MANDATORY,			/* Fixed flag values */
-					AVP_TYPE_GROUPED 			/* base type of data */
-					};
-					
-			struct local_rules_definition rules[] = 
-						{ 	 {  "Vendor-Id", 			RULE_REQUIRED, -1, 1 }
-							,{  "Auth-Application-Id",		RULE_OPTIONAL, -1, 1 }
-							,{  "Acct-Application-Id",		RULE_OPTIONAL, -1, 1 }
-						};
-			
-			/* Create the grouped AVP */
-			CHECK_dict_new( DICT_AVP, &data , NULL, &avp);
-			PARSE_loc_rules( rules, avp );
-			
-		}
-		
-		/* Redirect-Host */
-		{
-			/*
-				One or more of instances of this AVP MUST be present if the answer
-				message's 'E' bit is set and the Result-Code AVP is set to
-				DIAMETER_REDIRECT_INDICATION.
-
-				Upon receiving the above, the receiving Diameter node SHOULD forward
-				the request directly to one of the hosts identified in these AVPs.
-				The server contained in the selected Redirect-Host AVP SHOULD be used
-				for all messages pertaining to this session.
-			*/
-			struct dict_avp_data data = { 
-					292, 					/* Code */
-					#if AC_REDIRECT_HOST != 292
-					#error "AC_REDIRECT_HOST definition mismatch"
-					#endif
-					0, 					/* Vendor */
-					"Redirect-Host", 			/* Name */
-					AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, 	/* Fixed flags */
-					AVP_FLAG_MANDATORY,			/* Fixed flag values */
-					AVP_TYPE_OCTETSTRING 			/* base type of data */
-					};
-			CHECK_dict_new( DICT_AVP, &data , DiameterURI_type, NULL);
-		}
-		
-		/* Redirect-Host-Usage */
-		{
-			/*
-				The Redirect-Host-Usage AVP (AVP Code 261) is of type Enumerated.
-				This AVP MAY be present in answer messages whose 'E' bit is set and
-				the Result-Code AVP is set to DIAMETER_REDIRECT_INDICATION.
-
-				When present, this AVP dictates how the routing entry resulting from
-				the Redirect-Host is to be used.  The following values are supported:
-
-
-				DONT_CACHE 0
-
-				The host specified in the Redirect-Host AVP should not be cached.
-				This is the default value.
-
-
-				ALL_SESSION 1
-
-				All messages within the same session, as defined by the same value
-				of the Session-ID AVP MAY be sent to the host specified in the
-				Redirect-Host AVP.
-
-
-				ALL_REALM 2
-
-				All messages destined for the realm requested MAY be sent to the
-				host specified in the Redirect-Host AVP.
-
-
-				REALM_AND_APPLICATION 3
-
-				All messages for the application requested to the realm specified
-				MAY be sent to the host specified in the Redirect-Host AVP.
-
-				ALL_APPLICATION 4
-
-				All messages for the application requested MAY be sent to the host
-				specified in the Redirect-Host AVP.
-
-
-				ALL_HOST 5
-
-				All messages that would be sent to the host that generated the
-				Redirect-Host MAY be sent to the host specified in the Redirect-
-				Host AVP.
-
-
-				ALL_USER 6
-
-				All messages for the user requested MAY be sent to the host
-				specified in the Redirect-Host AVP.
-
-
-				When multiple cached routes are created by redirect indications and
-				they differ only in redirect usage and peers to forward requests to
-				(see Section 6.1.8), a precedence rule MUST be applied to the
-				redirect usage values of the cached routes during normal routing to
-				resolve contentions that may occur.  The precedence rule is the order
-				that dictate which redirect usage should be considered before any
-				other as they appear.  The order is as follows:
-
-
-				1.  ALL_SESSION
-
-				2.  ALL_USER
-
-				3.  REALM_AND_APPLICATION
-
-				4.  ALL_REALM
-
-				5.  ALL_APPLICATION
-
-				6.  ALL_HOST
-			*/
-			struct dict_object 	* 	type;
-			struct dict_type_data	 	tdata = { AVP_TYPE_INTEGER32,	"Enumerated(Redirect-Host-Usage)"	, NULL, NULL, NULL };
-			struct dict_enumval_data 	t_0 = { "DONT_CACHE", 			{ .i32 = 0 }};
-			struct dict_enumval_data 	t_1 = { "ALL_SESSION", 			{ .i32 = 1 }};
-			struct dict_enumval_data 	t_2 = { "ALL_REALM", 			{ .i32 = 2 }};
-			struct dict_enumval_data 	t_3 = { "REALM_AND_APPLICATION", 	{ .i32 = 3 }};
-			struct dict_enumval_data 	t_4 = { "ALL_APPLICATION", 		{ .i32 = 4 }};
-			struct dict_enumval_data 	t_5 = { "ALL_HOST", 			{ .i32 = 5 }};
-			struct dict_enumval_data 	t_6 = { "ALL_USER", 			{ .i32 = 6 }};
-			struct dict_avp_data 		data = { 
-					261, 					/* Code */
-					#if AC_REDIRECT_HOST_USAGE != 261
-					#error "AC_REDIRECT_HOST_USAGE definition mismatch"
-					#endif
-					0, 					/* Vendor */
-					"Redirect-Host-Usage", 			/* Name */
-					AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, 	/* Fixed flags */
-					AVP_FLAG_MANDATORY,			/* Fixed flag values */
-					AVP_TYPE_INTEGER32 			/* base type of data */
-					};
-			/* Create the Enumerated type, and then the AVP */
-			CHECK_dict_new( DICT_TYPE, &tdata , NULL, &type);
-			CHECK_dict_new( DICT_ENUMVAL, &t_0 , type, NULL);
-			CHECK_dict_new( DICT_ENUMVAL, &t_1 , type, NULL);
-			CHECK_dict_new( DICT_ENUMVAL, &t_2 , type, NULL);
-			CHECK_dict_new( DICT_ENUMVAL, &t_3 , type, NULL);
-			CHECK_dict_new( DICT_ENUMVAL, &t_4 , type, NULL);
-			CHECK_dict_new( DICT_ENUMVAL, &t_5 , type, NULL);
-			CHECK_dict_new( DICT_ENUMVAL, &t_6 , type, NULL);
-			CHECK_dict_new( DICT_AVP, &data , type, NULL);
-		}
-		
-		/* Redirect-Max-Cache-Time */
-		{
-			/*
-				The Redirect-Max-Cache-Time AVP (AVP Code 262) is of type Unsigned32.
-				This AVP MUST be present in answer messages whose 'E' bit is set, the
-				Result-Code AVP is set to DIAMETER_REDIRECT_INDICATION and the
-				Redirect-Host-Usage AVP set to a non-zero value.
-
-				This AVP contains the maximum number of seconds the peer and route
-				table entries, created as a result of the Redirect-Host, will be
-				cached.  Note that once a host created due to a redirect indication
-				is no longer reachable, any associated peer and routing table entries
-				MUST be deleted.
-			*/
-			struct dict_avp_data data = { 
-					262, 					/* Code */
-					#if AC_REDIRECT_MAX_CACHE_TIME != 262
-					#error "AC_REDIRECT_MAX_CACHE_TIME definition mismatch"
-					#endif
-					0, 					/* Vendor */
-					"Redirect-Max-Cache-Time", 		/* Name */
-					AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, 	/* Fixed flags */
-					AVP_FLAG_MANDATORY,			/* Fixed flag values */
-					AVP_TYPE_UNSIGNED32 			/* base type of data */
-					};
-			CHECK_dict_new( DICT_AVP, &data , NULL, NULL);
-		}
-		
-		/* Result-Code */
-		{
-			/*
-				The Result-Code AVP (AVP Code 268) is of type Unsigned32 and
-				indicates whether a particular request was completed successfully or
-				whether an error occurred.  All Diameter answer messages defined in
-				IETF applications MUST include one Result-Code AVP.  A non-successful
-				Result-Code AVP (one containing a non 2xxx value other than
-				DIAMETER_REDIRECT_INDICATION) MUST include the Error-Reporting-Host
-				AVP if the host setting the Result-Code AVP is different from the
-				identity encoded in the Origin-Host AVP.
-
-				The Result-Code data field contains an IANA-managed 32-bit address
-				space representing errors (see Section 11.4).  Diameter provides the
-				following classes of errors, all identified by the thousands digit in
-				the decimal notation:
-
-				o  1xxx (Informational)
-
-				o  2xxx (Success)
-
-				o  3xxx (Protocol Errors)
-
-				o  4xxx (Transient Failures)
-
-				o  5xxx (Permanent Failure)
-
-				A non-recognized class (one whose first digit is not defined in this
-				section) MUST be handled as a permanent failure.
-			*/
-			
-			/* Although the RFC does not specify an "Enumerated" type here, we go forward and create one.
-			 * This is the reason for the "*" in the type name
-			 */
-			struct dict_object * 	type;
-			struct dict_type_data 	tdata = { AVP_TYPE_UNSIGNED32,	"Enumerated*(Result-Code)"	, NULL, NULL, NULL };
-			struct dict_avp_data 	data = { 
-					268, 					/* Code */
-					#if AC_RESULT_CODE != 268
-					#error "AC_RESULT_CODE definition mismatch"
-					#endif
-					0, 					/* Vendor */
-					"Result-Code", 				/* Name */
-					AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, 	/* Fixed flags */
-					AVP_FLAG_MANDATORY,			/* Fixed flag values */
-					AVP_TYPE_UNSIGNED32 			/* base type of data */
-					};
-			/* Create the Enumerated type, and then the AVP */
-			CHECK_dict_new( DICT_TYPE, &tdata , NULL, &type);
-			CHECK_dict_new( DICT_AVP, &data , type, NULL);
-			
-			/* Informational */
-			{
-				/* 1001 */
-				{
-					/*
-						This informational error is returned by a Diameter server to
-						inform the access device that the authentication mechanism being
-						used requires multiple round trips, and a subsequent request needs
-						to be issued in order for access to be granted.
-					*/
-					struct dict_enumval_data 	error_code = { "DIAMETER_MULTI_ROUND_AUTH", 	{ .u32 = 1001 }};
-					CHECK_dict_new( DICT_ENUMVAL, &error_code , type, NULL);
-				}
-			}
-			/* Success */
-			{
-				/* 2001 */
-				{
-					/*
-						The Request was successfully completed.
-					*/
-					struct dict_enumval_data 	error_code = { "DIAMETER_SUCCESS", 		{ .u32 = 2001 }};
-					#if ER_DIAMETER_SUCCESS != 2001
-					#error "ER_DIAMETER_SUCCESS definition mismatch"
-					#endif
-					CHECK_dict_new( DICT_ENUMVAL, &error_code , type, NULL);
-				}
-				/* 2002 */
-				{
-					/*
-						When returned, the request was successfully completed, but
-						additional processing is required by the application in order to
-						provide service to the user.
-					*/
-					struct dict_enumval_data 	error_code = { "DIAMETER_LIMITED_SUCCESS", 	{ .u32 = 2002 }};
-					CHECK_dict_new( DICT_ENUMVAL, &error_code , type, NULL);
-				}
-			}
-			/* Protocol Errors */
-			{
-				/* 3001 */
-				{
-					/*
-						The Request contained a Command-Code that the receiver did not
-						recognize or support.  This MUST be used when a Diameter node
-						receives an experimental command that it does not understand.
-					*/
-					/* (old): it has been changed to 5019 in 3588bis */
-					struct dict_enumval_data 	error_code = { "DIAMETER_COMMAND_UNSUPPORTED(old)", 	{ .u32 = 3001 }};
-					CHECK_dict_new( DICT_ENUMVAL, &error_code , type, NULL);
-				}
-				/* 3002 */
-				{
-					/*
-						This error is given when Diameter can not deliver the message to
-						the destination, either because no host within the realm
-						supporting the required application was available to process the
-						request, or because Destination-Host AVP was given without the
-						associated Destination-Realm AVP.
-					*/
-					struct dict_enumval_data 	error_code = { "DIAMETER_UNABLE_TO_DELIVER", 	{ .u32 = 3002 }};
-					CHECK_dict_new( DICT_ENUMVAL, &error_code , type, NULL);
-				}
-				/* 3003 */
-				{
-					/*
-						The intended realm of the request is not recognized.
-					*/
-					struct dict_enumval_data 	error_code = { "DIAMETER_REALM_NOT_SERVED", 	{ .u32 = 3003 }};
-					#if ER_DIAMETER_REALM_NOT_SERVED != 3003
-					#error "ER_DIAMETER_REALM_NOT_SERVED definition mismatch"
-					#endif
-					CHECK_dict_new( DICT_ENUMVAL, &error_code , type, NULL);
-				}
-				/* 3004 */
-				{
-					/*
-						When returned, a Diameter node SHOULD attempt to send the message
-						to an alternate peer.  This error MUST only be used when a
-						specific server is requested, and it cannot provide the requested
-						service.
-					*/
-					struct dict_enumval_data 	error_code = { "DIAMETER_TOO_BUSY", 		{ .u32 = 3004 }};
-					#if ER_DIAMETER_TOO_BUSY != 3004
-					#error "ER_DIAMETER_TOO_BUSY definition mismatch"
-					#endif
-					CHECK_dict_new( DICT_ENUMVAL, &error_code , type, NULL);
-				}
-				/* 3005 */
-				{
-					/*
-						An agent detected a loop while trying to get the message to the
-						intended recipient.  The message MAY be sent to an alternate peer,
-						if one is available, but the peer reporting the error has
-						identified a configuration problem.
-					*/
-					struct dict_enumval_data 	error_code = { "DIAMETER_LOOP_DETECTED", 	{ .u32 = 3005 }};
-					CHECK_dict_new( DICT_ENUMVAL, &error_code , type, NULL);
-				}
-				/* 3006 */
-				{
-					/*
-						A redirect agent has determined that the request could not be
-						satisfied locally and the initiator of the request should direct
-						the request directly to the server, whose contact information has
-						been added to the response.  When set, the Redirect-Host AVP MUST
-						be present.
-					*/
-					struct dict_enumval_data 	error_code = { "DIAMETER_REDIRECT_INDICATION", 	{ .u32 = 3006 }};
-					#if ER_DIAMETER_REDIRECT_INDICATION != 3006
-					#error "ER_DIAMETER_REDIRECT_INDICATION definition mismatch"
-					#endif
-					CHECK_dict_new( DICT_ENUMVAL, &error_code , type, NULL);
-				}
-				/* 3007 */
-				{
-					/*
-						A request was sent for an application that is not supported.
-					*/
-					struct dict_enumval_data 	error_code = { "DIAMETER_APPLICATION_UNSUPPORTED",	{ .u32 = 3007 }};
-					CHECK_dict_new( DICT_ENUMVAL, &error_code , type, NULL);
-				}
-				/* 3008 */
-				{
-					/*
-						A request was received whose bits in the Diameter header were
-						either set to an invalid combination, or to a value that is
-						inconsistent with the command code's definition.
-					*/
-					/* (old): it has been changed in 3588bis */
-					struct dict_enumval_data 	error_code = { "DIAMETER_INVALID_HDR_BITS(old)", 	{ .u32 = 3008 }};
-					CHECK_dict_new( DICT_ENUMVAL, &error_code , type, NULL);
-				}
-				/* 3009 */
-				{
-					/*
-						A request was received that included an AVP whose flag bits are
-						set to an unrecognized value, or that is inconsistent with the
-						AVP's definition.
-					*/
-					/* (old): it has been changed in 3588bis */
-					struct dict_enumval_data 	error_code = { "DIAMETER_INVALID_AVP_BITS(old)", 	{ .u32 = 3009 }};
-					CHECK_dict_new( DICT_ENUMVAL, &error_code , type, NULL);
-				}
-				/* 3010 */
-				{
-					/*
-						 A CER was received from an unknown peer.
-					*/
-					/* (old): it has been changed in 3588bis */
-					struct dict_enumval_data 	error_code = { "DIAMETER_UNKNOWN_PEER(old)", 	{ .u32 = 3010 }};
-					CHECK_dict_new( DICT_ENUMVAL, &error_code , type, NULL);
-				}
-				/* 3011 */
-				{
-					/*
-						This error is returned when a reserved bit in the Diameter header
-						is set to one (1) or the bits in the Diameter header defined in
-						Sec 3 are set incorrectly.
-					*/
-					struct dict_enumval_data 	error_code = { "DIAMETER_INVALID_BIT_IN_HEADER",	{ .u32 = 3011 }};
-					CHECK_dict_new( DICT_ENUMVAL, &error_code , type, NULL);
-				}
-				/* 3012 */
-				{
-					/*
-						This error is returned when a request is received with an invalid
-						message length.
-					*/
-					struct dict_enumval_data 	error_code = { "DIAMETER_INVALID_MESSAGE_LENGTH",	{ .u32 = 3012 }};
-					CHECK_dict_new( DICT_ENUMVAL, &error_code , type, NULL);
-				}
-			}
-			/* Transient Failures */
-			{
-				/* 4001 */
-				{
-					/*
-						The authentication process for the user failed, most likely due to
-						an invalid password used by the user.  Further attempts MUST only
-						be tried after prompting the user for a new password.
-					*/
-					struct dict_enumval_data 	error_code = { "DIAMETER_AUTHENTICATION_REJECTED", 	{ .u32 = 4001 }};
-					CHECK_dict_new( DICT_ENUMVAL, &error_code , type, NULL);
-				}
-				/* 4002 */
-				{
-					/*
-						A Diameter node received the accounting request but was unable to
-						commit it to stable storage due to a temporary lack of space.
-					*/
-					struct dict_enumval_data 	error_code = { "DIAMETER_OUT_OF_SPACE", 		{ .u32 = 4002 }};
-					CHECK_dict_new( DICT_ENUMVAL, &error_code , type, NULL);
-				}
-				/* 4003 */
-				{
-					/*
-						The peer has determined that it has lost the election process and
-						has therefore disconnected the transport connection.
-					*/
-					struct dict_enumval_data 	error_code = { "ELECTION_LOST", 			{ .u32 = 4003 }};
-					CHECK_dict_new( DICT_ENUMVAL, &error_code , type, NULL);
-				}
-			}
-			/* Permanent Failures */
-			{
-				/* 5001 */
-				{
-					/*
-						The peer received a message that contained an AVP that is not
-						recognized or supported and was marked with the Mandatory bit.  A
-						Diameter message with this error MUST contain one or more Failed-
-						AVP AVP containing the AVPs that caused the failure.
-					*/
-					struct dict_enumval_data 	error_code = { "DIAMETER_AVP_UNSUPPORTED", 	{ .u32 = 5001 }};
-					CHECK_dict_new( DICT_ENUMVAL, &error_code , type, NULL);
-				}
-				/* 5002 */
-				{
-					/*
-						The request contained an unknown Session-Id.
-					*/
-					struct dict_enumval_data 	error_code = { "DIAMETER_UNKNOWN_SESSION_ID", 	{ .u32 = 5002 }};
-					CHECK_dict_new( DICT_ENUMVAL, &error_code , type, NULL);
-				}
-				/* 5003 */
-				{
-					/*
-						A request was received for which the user could not be authorized.
-						This error could occur if the service requested is not permitted
-						to the user.
-					*/
-					struct dict_enumval_data 	error_code = { "DIAMETER_AUTHORIZATION_REJECTED",{ .u32 = 5003 }};
-					CHECK_dict_new( DICT_ENUMVAL, &error_code , type, NULL);
-				}
-				/* 5004 */
-				{
-					/*
-						The request contained an AVP with an invalid value in its data
-						portion.  A Diameter message indicating this error MUST include
-						the offending AVPs within a Failed-AVP AVP.
-					*/
-					struct dict_enumval_data 	error_code = { "DIAMETER_INVALID_AVP_VALUE",	{ .u32 = 5004 }};
-					CHECK_dict_new( DICT_ENUMVAL, &error_code , type, NULL);
-				}
-				/* 5005 */
-				{
-					/*
-						The request did not contain an AVP that is required by the Command
-						Code definition.  If this value is sent in the Result-Code AVP, a
-						Failed-AVP AVP SHOULD be included in the message.  The Failed-AVP
-						AVP MUST contain an example of the missing AVP complete with the
-						Vendor-Id if applicable.  The value field of the missing AVP
-						should be of correct minimum length and contain zeroes.
-					*/
-					struct dict_enumval_data 	error_code = { "DIAMETER_MISSING_AVP",		{ .u32 = 5005 }};
-					CHECK_dict_new( DICT_ENUMVAL, &error_code , type, NULL);
-				}
-				/* 5006 */
-				{
-					/*
-						A request was received that cannot be authorized because the user
-						has already expended allowed resources.  An example of this error
-						condition is a user that is restricted to one dial-up PPP port,
-						attempts to establish a second PPP connection.
-					*/
-					struct dict_enumval_data 	error_code = { "DIAMETER_RESOURCES_EXCEEDED",	{ .u32 = 5006 }};
-					CHECK_dict_new( DICT_ENUMVAL, &error_code , type, NULL);
-				}
-				/* 5007 */
-				{
-					/*
-						The Home Diameter server has detected AVPs in the request that
-						contradicted each other, and is not willing to provide service to
-						the user.  The Failed-AVP AVPs MUST be present which contains the
-						AVPs that contradicted each other.
-					*/
-					struct dict_enumval_data 	error_code = { "DIAMETER_CONTRADICTING_AVPS",	{ .u32 = 5007 }};
-					CHECK_dict_new( DICT_ENUMVAL, &error_code , type, NULL);
-				}
-				/* 5008 */
-				{
-					/*
-						A message was received with an AVP that MUST NOT be present.  The
-						Failed-AVP AVP MUST be included and contain a copy of the
-						offending AVP.
-					*/
-					struct dict_enumval_data 	error_code = { "DIAMETER_AVP_NOT_ALLOWED",	{ .u32 = 5008 }};
-					CHECK_dict_new( DICT_ENUMVAL, &error_code , type, NULL);
-				}
-				/* 5009 */
-				{
-					/*
-						A message was received that included an AVP that appeared more
-						often than permitted in the message definition.  The Failed-AVP
-						AVP MUST be included and contain a copy of the first instance of
-						the offending AVP that exceeded the maximum number of occurrences
-					*/
-					struct dict_enumval_data 	error_code = { "DIAMETER_AVP_OCCURS_TOO_MANY_TIMES",{ .u32 = 5009 }};
-					CHECK_dict_new( DICT_ENUMVAL, &error_code , type, NULL);
-				}
-				/* 5010 */
-				{
-					/*
-						This error is returned by a Diameter node that is not acting as a
-						relay when it receives a CER which advertises a set of
-						applications that it does not support.
-					*/
-					struct dict_enumval_data 	error_code = { "DIAMETER_NO_COMMON_APPLICATION",{ .u32 = 5010 }};
-					CHECK_dict_new( DICT_ENUMVAL, &error_code , type, NULL);
-				}
-				/* 5011 */
-				{
-					/*
-						This error is returned when a request was received, whose version
-						number is unsupported.
-					*/
-					struct dict_enumval_data 	error_code = { "DIAMETER_UNSUPPORTED_VERSION",	{ .u32 = 5011 }};
-					CHECK_dict_new( DICT_ENUMVAL, &error_code , type, NULL);
-				}
-				/* 5012 */
-				{
-					/*
-						This error is returned when a request is rejected for unspecified
-						reasons.
-					*/
-					struct dict_enumval_data 	error_code = { "DIAMETER_UNABLE_TO_COMPLY",	{ .u32 = 5012 }};
-					CHECK_dict_new( DICT_ENUMVAL, &error_code , type, NULL);
-				}
-				/* 5013 */
-				{
-					/*
-						This error is returned when an unrecognized bit in the Diameter
-						header is set to one (1).
-					*/
-					/* (old): it has been changed in 3588bis */
-					struct dict_enumval_data 	error_code = { "DIAMETER_INVALID_BIT_IN_HEADER(old)", 	{ .u32 = 5013 }};
-					CHECK_dict_new( DICT_ENUMVAL, &error_code , type, NULL);
-				}
-				/* 5014 */
-				{
-					/*
-						The request contained an AVP with an invalid length.  A Diameter
-						message indicating this error MUST include the offending AVPs
-						within a Failed-AVP AVP.  In cases where the erroneous avp length
-						value exceeds the message length or is less than the minimum AVP
-						header length, it is sufficient to include the offending AVP
-						header and a zero filled payload of the minimum required length
-						for the payloads data type.  If the AVP is a grouped AVP, the
-						grouped AVP header with an empty payload would be sufficient to
-						indicate the offending AVP.  In the case where the offending AVP
-						header cannot be fully decoded when avp length is less than the
-						minimum AVP header length, it is sufficient to include an
-						offending AVP header that is formulated by padding the incomplete
-						AVP header with zero up to the minimum AVP header length.
-					*/
-					struct dict_enumval_data 	error_code = { "DIAMETER_INVALID_AVP_LENGTH",	{ .u32 = 5014 }};
-					CHECK_dict_new( DICT_ENUMVAL, &error_code , type, NULL);
-				}
-				/* 5015 */
-				{
-					/*
-						This error is returned when a request is received with an invalid
-						message length.
-					*/
-					/* (old): it has been changed in 3588bis */
-					struct dict_enumval_data 	error_code = { "DIAMETER_INVALID_MESSAGE_LENGTH(old)", 	{ .u32 = 5015 }};
-					CHECK_dict_new( DICT_ENUMVAL, &error_code , type, NULL);
-				}
-				/* 5016 */
-				{
-					/*
-						The request contained an AVP with which is not allowed to have the
-						given value in the AVP Flags field.  A Diameter message indicating
-						this error MUST include the offending AVPs within a Failed-AVP
-						AVP.
-					*/
-					/* (old): it has been changed in 3588bis */
-					struct dict_enumval_data 	error_code = { "DIAMETER_INVALID_AVP_BIT_COMBO(old)", 	{ .u32 = 5016 }};
-					CHECK_dict_new( DICT_ENUMVAL, &error_code , type, NULL);
-				}
-				/* 5017 */
-				{
-					/*
-						This error is returned when a CER message is received, and there
-						are no common security mechanisms supported between the peers.  A
-						Capabilities-Exchange-Answer (CEA) MUST be returned with the
-						Result-Code AVP set to DIAMETER_NO_COMMON_SECURITY.
-					*/
-					struct dict_enumval_data 	error_code = { "DIAMETER_NO_COMMON_SECURITY",	{ .u32 = 5017 }};
-					CHECK_dict_new( DICT_ENUMVAL, &error_code , type, NULL);
-				}
-				/* 5018 */
-				{
-					/*
-						A CER was received from an unknown peer.
-					*/
-					struct dict_enumval_data 	error_code = { "DIAMETER_UNKNOWN_PEER",		{ .u32 = 5018 }};
-					CHECK_dict_new( DICT_ENUMVAL, &error_code , type, NULL);
-				}
-				/* 5019 */
-				{
-					/*
-						The Request contained a Command-Code that the receiver did not
-						recognize or support.  This MUST be used when a Diameter node
-						receives an experimental command that it does not understand.
-					*/
-					struct dict_enumval_data 	error_code = { "DIAMETER_COMMAND_UNSUPPORTED",	{ .u32 = 5019 }};
-					CHECK_dict_new( DICT_ENUMVAL, &error_code , type, NULL);
-				}
-				/* 5020 */
-				{
-					/*
-						A request was received whose bits in the Diameter header were
-						either set to an invalid combination, or to a value that is
-						inconsistent with the command code's definition.
-					*/
-					struct dict_enumval_data 	error_code = { "DIAMETER_INVALID_HDR_BITS",	{ .u32 = 5020 }};
-					CHECK_dict_new( DICT_ENUMVAL, &error_code , type, NULL);
-				}
-				/* 5021 */
-				{
-					/*
-						A request was received that included an AVP whose flag bits are
-						set to an unrecognized value, or that is inconsistent with the
-						AVP's definition.
-					*/
-					struct dict_enumval_data 	error_code = { "DIAMETER_INVALID_AVP_BITS",	{ .u32 = 5021 }};
-					CHECK_dict_new( DICT_ENUMVAL, &error_code , type, NULL);
-				}
-			}
-		}
-		
-		/* Error-Message */
-		{
-			/*
-				The Error-Message AVP (AVP Code 281) is of type UTF8String.  It MAY
-				accompany a Result-Code AVP as a human readable error message.  The
-				Error-Message AVP is not intended to be useful in real-time, and
-				SHOULD NOT be expected to be parsed by network entities.
-			*/
-			struct dict_avp_data data = { 
-					281, 					/* Code */
-					#if AC_ERROR_MESSAGE != 281
-					#error "AC_ERROR_MESSAGE definition mismatch"
-					#endif
-					0, 					/* Vendor */
-					"Error-Message", 			/* Name */
-					AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, 	/* Fixed flags */
-					0,					/* Fixed flag values */
-					AVP_TYPE_OCTETSTRING 			/* base type of data */
-					};
-			CHECK_dict_new( DICT_AVP, &data , UTF8String_type, NULL);
-		}
-		
-		/* Error-Reporting-Host */
-		{
-			/*
-				The Error-Reporting-Host AVP (AVP Code 294) is of type
-				DiameterIdentity.  This AVP contains the identity of the Diameter
-				host that sent the Result-Code AVP to a value other than 2001
-				(Success), only if the host setting the Result-Code is different from
-				the one encoded in the Origin-Host AVP.  This AVP is intended to be
-				used for troubleshooting purposes, and MUST be set when the Result-
-				Code AVP indicates a failure.
-			*/
-			struct dict_avp_data data = { 
-					294, 					/* Code */
-					#if AC_ERROR_REPORTING_HOST != 294
-					#error "AC_ERROR_REPORTING_HOST definition mismatch"
-					#endif
-					0, 					/* Vendor */
-					"Error-Reporting-Host", 		/* Name */
-					AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, 	/* Fixed flags */
-					0,					/* Fixed flag values */
-					AVP_TYPE_OCTETSTRING 			/* base type of data */
-					};
-			CHECK_dict_new( DICT_AVP, &data , DiameterIdentity_type, NULL);
-		}
-		
-		/* Failed-AVP */
-		{
-			/*
-				The Failed-AVP AVP (AVP Code 279) is of type Grouped and provides
-				debugging information in cases where a request is rejected or not
-				fully processed due to erroneous information in a specific AVP.  The
-				value of the Result-Code AVP will provide information on the reason
-				for the Failed-AVP AVP.  A Diameter message SHOULD contain only one
-				Failed-AVP that corresponds to the error indicated by the Result-Code
-				AVP.  For practical purposes, this Failed-AVP would typically refer
-				to the first AVP processing error that a Diameter node encounters.
-
-				The possible reasons for this AVP are the presence of an improperly
-				constructed AVP, an unsupported or unrecognized AVP, an invalid AVP
-				value, the omission of a required AVP, the presence of an explicitly
-				excluded AVP (see tables in Section 10), or the presence of two or
-				more occurrences of an AVP which is restricted to 0, 1, or 0-1
-				occurrences.
-
-				A Diameter message SHOULD contain one Failed-AVP AVP, containing the
-				entire AVP that could not be processed successfully.  If the failure
-				reason is omission of a required AVP, an AVP with the missing AVP
-				code, the missing vendor id, and a zero filled payload of the minimum
-				required length for the omitted AVP will be added.  If the failure
-				reason is an invalid AVP length where the reported length is less
-				than the minimum AVP header length or greater than the reported
-				message length, a copy of the offending AVP header and a zero filled
-				payload of the minimum required length SHOULD be added.
-
-				In the case where the offending AVP is embedded within a grouped AVP,
-				the Failed-AVP MAY contain the grouped AVP which in turn contains the
-				single offending AVP.  The same method MAY be employed if the grouped
-				AVP itself is embedded in yet another grouped AVP and so on.  In this
-				case, the Failed-AVP MAY contain the grouped AVP heirarchy up to the
-				single offending AVP.  This enables the recipient to detect the
-				location of the offending AVP when embedded in a group.
-
-				AVP Format
-
-				 <Failed-AVP> ::= < AVP Header: 279 >
-        				       1* {AVP}
-			*/
-			struct dict_avp_data data = { 
-					279, 					/* Code */
-					#if AC_FAILED_AVP != 279
-					#error "AC_FAILED_AVP definition mismatch"
-					#endif
-					0, 					/* Vendor */
-					"Failed-AVP", 				/* Name */
-					AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, 	/* Fixed flags */
-					AVP_FLAG_MANDATORY,			/* Fixed flag values */
-					AVP_TYPE_GROUPED 			/* base type of data */
-					};
-			CHECK_dict_new( DICT_AVP, &data , NULL, NULL);
-		}
-		
-		/* Experimental-Result */
-		{
-			/*
-				The Experimental-Result AVP (AVP Code 297) is of type Grouped, and
-				indicates whether a particular vendor-specific request was completed
-				successfully or whether an error occurred.  Its Data field has the
-				following ABNF grammar:
-
-				AVP Format
-
-				 Experimental-Result ::= < AVP Header: 297 >
-                        				 { Vendor-Id }
-                        				 { Experimental-Result-Code }
-
-				The Vendor-Id AVP (see Section 5.3.3) in this grouped AVP identifies
-				the vendor responsible for the assignment of the result code which
-				follows.  All Diameter answer messages defined in vendor-specific
-				applications MUST include either one Result-Code AVP or one
-				Experimental-Result AVP.
-			*/
-			struct dict_avp_data data = { 
-					297, 					/* Code */
-					0, 					/* Vendor */
-					"Experimental-Result", 			/* Name */
-					AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, 	/* Fixed flags */
-					AVP_FLAG_MANDATORY,			/* Fixed flag values */
-					AVP_TYPE_GROUPED 			/* base type of data */
-					};
-			CHECK_dict_new( DICT_AVP, &data , NULL, NULL);
-		}
-		
-		/* Experimental-Result-Code */
-		{
-			/*
-				The Experimental-Result-Code AVP (AVP Code 298) is of type Unsigned32
-				and contains a vendor-assigned value representing the result of
-				processing the request.
-
-				It is recommended that vendor-specific result codes follow the same
-				conventions given for the Result-Code AVP regarding the different
-				types of result codes and the handling of errors (for non 2xxx
-				values).
-			*/
-			/* Although the RFC does not specify an "Enumerated" type here, we go forward and create one.
-			 * This is the reason for the "*" in the type name. Vendors will have to define their values.
-			 */
-			struct dict_object * 	type;
-			struct dict_type_data 	tdata = { AVP_TYPE_UNSIGNED32,	"Enumerated*(Experimental-Result-Code)"	, NULL, NULL, NULL };
-			struct dict_avp_data 	data = { 
-					298, 					/* Code */
-					0, 					/* Vendor */
-					"Experimental-Result-Code", 		/* Name */
-					AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, 	/* Fixed flags */
-					AVP_FLAG_MANDATORY,			/* Fixed flag values */
-					AVP_TYPE_UNSIGNED32 			/* base type of data */
-					};
-			
-			CHECK_dict_new( DICT_TYPE, &tdata , NULL, &type);
-			CHECK_dict_new( DICT_AVP, &data , type, NULL);
-		}
-		
-		/* Auth-Request-Type */
-		{
-			/*
-				The Auth-Request-Type AVP (AVP Code 274) is of type Enumerated and is
-				included in application-specific auth requests to inform the peers
-				whether a user is to be authenticated only, authorized only or both.
-				Note any value other than both MAY cause RADIUS interoperability
-				issues.  The following values are defined:
-
-
-				AUTHENTICATE_ONLY 1
-
-				The request being sent is for authentication only, and MUST
-				contain the relevant application specific authentication AVPs that
-				are needed by the Diameter server to authenticate the user.
-
-
-				AUTHORIZE_ONLY 2
-
-				The request being sent is for authorization only, and MUST contain
-				the application specific authorization AVPs that are necessary to
-				identify the service being requested/offered.
-
-
-				AUTHORIZE_AUTHENTICATE 3
-
-				The request contains a request for both authentication and
-				authorization.  The request MUST include both the relevant
-				application specific authentication information, and authorization
-				information necessary to identify the service being requested/
-				offered.
-			*/
-			struct dict_object 	* 	type;
-			struct dict_type_data 		tdata = { AVP_TYPE_INTEGER32,	"Enumerated(Auth-Request-Type)"	, NULL, NULL, NULL };
-			struct dict_enumval_data 	t_1 = { "AUTHENTICATE_ONLY", 		{ .i32 = 1 }};
-			struct dict_enumval_data 	t_2 = { "AUTHORIZE_ONLY", 		{ .i32 = 2 }};
-			struct dict_enumval_data 	t_3 = { "AUTHORIZE_AUTHENTICATE", 	{ .i32 = 3 }};
-			struct dict_avp_data 	data = { 
-					274, 					/* Code */
-					0, 					/* Vendor */
-					"Auth-Request-Type", 			/* Name */
-					AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, 	/* Fixed flags */
-					AVP_FLAG_MANDATORY,			/* Fixed flag values */
-					AVP_TYPE_INTEGER32 			/* base type of data */
-					};
-			/* Create the Enumerated type, and then the AVP */
-			CHECK_dict_new( DICT_TYPE, &tdata , NULL, &type);
-			CHECK_dict_new( DICT_ENUMVAL, &t_1 , type, NULL);
-			CHECK_dict_new( DICT_ENUMVAL, &t_2 , type, NULL);
-			CHECK_dict_new( DICT_ENUMVAL, &t_3 , type, NULL);
-			CHECK_dict_new( DICT_AVP, &data , type, NULL);
-		}
-		
-		/* Session-Id */
-		{
-			/*
-				The Session-Id AVP (AVP Code 263) is of type UTF8String and is used
-				to identify a specific session (see Section 8).  All messages
-				pertaining to a specific session MUST include only one Session-Id AVP
-				and the same value MUST be used throughout the life of a session.
-				When present, the Session-Id SHOULD appear immediately following the
-				Diameter Header (see Section 3).
-
-				The Session-Id MUST be globally and eternally unique, as it is meant
-				to uniquely identify a user session without reference to any other
-				information, and may be needed to correlate historical authentication
-				information with accounting information.  The Session-Id includes a
-				mandatory portion and an implementation-defined portion; a
-				recommended format for the implementation-defined portion is outlined
-				below.
-				
-				(skipped, see RFC for detail)
-			*/
-			struct dict_avp_data data = { 
-					263, 					/* Code */
-					#if AC_SESSION_ID != 263
-					#error "AC_SESSION_ID definition mismatch"
-					#endif
-					0, 					/* Vendor */
-					"Session-Id", 				/* Name */
-					AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, 	/* Fixed flags */
-					AVP_FLAG_MANDATORY,			/* Fixed flag values */
-					AVP_TYPE_OCTETSTRING 			/* base type of data */
-					};
-			CHECK_dict_new( DICT_AVP, &data , UTF8String_type, NULL);
-		}
-		
-		/* Authorization-Lifetime */
-		{
-			/*
-				The Authorization-Lifetime AVP (AVP Code 291) is of type Unsigned32
-				and contains the maximum number of seconds of service to be provided
-				to the user before the user is to be re-authenticated and/or re-
-				authorized.  Great care should be taken when the Authorization-
-				Lifetime value is determined, since a low, non-zero, value could
-				create significant Diameter traffic, which could congest both the
-				network and the agents.
-
-				A value of zero (0) means that immediate re-auth is necessary by the
-				access device.  This is typically used in cases where multiple
-				authentication methods are used, and a successful auth response with
-				this AVP set to zero is used to signal that the next authentication
-				method is to be immediately initiated.  The absence of this AVP, or a
-				value of all ones (meaning all bits in the 32 bit field are set to
-				one) means no re-auth is expected.
-
-				If both this AVP and the Session-Timeout AVP are present in a
-				message, the value of the latter MUST NOT be smaller than the
-				Authorization-Lifetime AVP.
-
-				An Authorization-Lifetime AVP MAY be present in re-authorization
-				messages, and contains the number of seconds the user is authorized
-				to receive service from the time the re-auth answer message is
-				received by the access device.
-
-				This AVP MAY be provided by the client as a hint of the maximum
-				lifetime that it is willing to accept.  However, the server MAY
-				return a value that is equal to, or smaller, than the one provided by
-				the client.
-			*/
-			struct dict_avp_data data = { 
-					291, 					/* Code */
-					0, 					/* Vendor */
-					"Authorization-Lifetime", 		/* Name */
-					AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, 	/* Fixed flags */
-					AVP_FLAG_MANDATORY,			/* Fixed flag values */
-					AVP_TYPE_UNSIGNED32 			/* base type of data */
-					};
-			CHECK_dict_new( DICT_AVP, &data , NULL, NULL);
-		}
-		
-		/* Auth-Grace-Period */
-		{
-			/*
-				The Auth-Grace-Period AVP (AVP Code 276) is of type Unsigned32 and
-				contains the number of seconds the Diameter server will wait
-				following the expiration of the Authorization-Lifetime AVP before
-				cleaning up resources for the session.
-			*/
-			struct dict_avp_data data = { 
-					276, 					/* Code */
-					0, 					/* Vendor */
-					"Auth-Grace-Period", 			/* Name */
-					AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, 	/* Fixed flags */
-					AVP_FLAG_MANDATORY,			/* Fixed flag values */
-					AVP_TYPE_UNSIGNED32 			/* base type of data */
-					};
-			CHECK_dict_new( DICT_AVP, &data , NULL, NULL);
-		}
-		
-		/* Auth-Session-State */
-		{
-			/*
-				The Auth-Session-State AVP (AVP Code 277) is of type Enumerated and
-				specifies whether state is maintained for a particular session.  The
-				client MAY include this AVP in requests as a hint to the server, but
-				the value in the server's answer message is binding.  The following
-				values are supported:
-
-
-				STATE_MAINTAINED 0
-
-				This value is used to specify that session state is being
-				maintained, and the access device MUST issue a session termination
-				message when service to the user is terminated.  This is the
-				default value.
-
-
-				NO_STATE_MAINTAINED 1
-
-				This value is used to specify that no session termination messages
-				will be sent by the access device upon expiration of the
-				Authorization-Lifetime.
-			*/
-			struct dict_object 	* 	type;
-			struct dict_type_data	 	tdata = { AVP_TYPE_INTEGER32,	"Enumerated(Auth-Session-State)"	, NULL, NULL, NULL };
-			struct dict_enumval_data 	t_0 = { "STATE_MAINTAINED", 		{ .i32 = 0 }};
-			struct dict_enumval_data 	t_1 = { "NO_STATE_MAINTAINED", 		{ .i32 = 1 }};
-			struct dict_avp_data	 	data = { 
-					277, 					/* Code */
-					0, 					/* Vendor */
-					"Auth-Session-State", 			/* Name */
-					AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, 	/* Fixed flags */
-					AVP_FLAG_MANDATORY,			/* Fixed flag values */
-					AVP_TYPE_INTEGER32 			/* base type of data */
-					};
-			/* Create the Enumerated type, and then the AVP */
-			CHECK_dict_new( DICT_TYPE, &tdata , NULL, &type);
-			CHECK_dict_new( DICT_ENUMVAL, &t_0 , type, NULL);
-			CHECK_dict_new( DICT_ENUMVAL, &t_1 , type, NULL);
-			CHECK_dict_new( DICT_AVP, &data , type, NULL);
-		}
-		
-		/* Re-Auth-Request-Type */
-		{
-			/*
-				The Re-Auth-Request-Type AVP (AVP Code 285) is of type Enumerated and
-				is included in application-specific auth answers to inform the client
-				of the action expected upon expiration of the Authorization-Lifetime.
-				If the answer message contains an Authorization-Lifetime AVP with a
-				positive value, the Re-Auth-Request-Type AVP MUST be present in an
-				answer message.  The following values are defined:
-
-
-				AUTHORIZE_ONLY 0
-
-				An authorization only re-auth is expected upon expiration of the
-				Authorization-Lifetime.  This is the default value if the AVP is
-				not present in answer messages that include the Authorization-
-				Lifetime.
-
-
-				AUTHORIZE_AUTHENTICATE 1
-
-				An authentication and authorization re-auth is expected upon
-				expiration of the Authorization-Lifetime.
-			*/
-			struct dict_object 	* 	type;
-			struct dict_type_data	 	tdata = { AVP_TYPE_INTEGER32,	"Enumerated(Re-Auth-Request-Type)"	, NULL, NULL, NULL };
-			struct dict_enumval_data 	t_0 = { "AUTHORIZE_ONLY", 		{ .i32 = 0 }};
-			struct dict_enumval_data 	t_1 = { "AUTHORIZE_AUTHENTICATE",	{ .i32 = 1 }};
-			struct dict_avp_data	 	data = { 
-					285, 					/* Code */
-					0, 					/* Vendor */
-					"Re-Auth-Request-Type",			/* Name */
-					AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, 	/* Fixed flags */
-					AVP_FLAG_MANDATORY,			/* Fixed flag values */
-					AVP_TYPE_INTEGER32 			/* base type of data */
-					};
-			/* Create the Enumerated type, and then the AVP */
-			CHECK_dict_new( DICT_TYPE, &tdata , NULL, &type);
-			CHECK_dict_new( DICT_ENUMVAL, &t_0 , type, NULL);
-			CHECK_dict_new( DICT_ENUMVAL, &t_1 , type, NULL);
-			CHECK_dict_new( DICT_AVP, &data , type, NULL);
-		}
-		
-		/* Session-Timeout */
-		{
-			/*
-				The Session-Timeout AVP (AVP Code 27) [RFC2865] is of type Unsigned32
-				and contains the maximum number of seconds of service to be provided
-				to the user before termination of the session.  When both the
-				Session-Timeout and the Authorization-Lifetime AVPs are present in an
-				answer message, the former MUST be equal to or greater than the value
-				of the latter.
-
-				A session that terminates on an access device due to the expiration
-				of the Session-Timeout MUST cause an STR to be issued, unless both
-				the access device and the home server had previously agreed that no
-				session termination messages would be sent (see Section 8.11).
-
-				A Session-Timeout AVP MAY be present in a re-authorization answer
-				message, and contains the remaining number of seconds from the
-				beginning of the re-auth.
-
-				A value of zero, or the absence of this AVP, means that this session
-				has an unlimited number of seconds before termination.
-
-				This AVP MAY be provided by the client as a hint of the maximum
-				timeout that it is willing to accept.  However, the server MAY return
-				a value that is equal to, or smaller, than the one provided by the
-				client.
-			*/
-			struct dict_avp_data data = { 
-					27, 					/* Code */
-					0, 					/* Vendor */
-					"Session-Timeout", 			/* Name */
-					AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, 	/* Fixed flags */
-					AVP_FLAG_MANDATORY,			/* Fixed flag values */
-					AVP_TYPE_UNSIGNED32 			/* base type of data */
-					};
-			CHECK_dict_new( DICT_AVP, &data , NULL, NULL);
-		}
-		
-		/* User-Name */
-		{
-			/*
-				The User-Name AVP (AVP Code 1) [RFC2865] is of type UTF8String, which
-				contains the User-Name, in a format consistent with the NAI
-				specification [RFC4282].
-			*/
-			struct dict_avp_data data = { 
-					1, 					/* Code */
-					0, 					/* Vendor */
-					"User-Name", 				/* Name */
-					AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, 	/* Fixed flags */
-					AVP_FLAG_MANDATORY,			/* Fixed flag values */
-					AVP_TYPE_OCTETSTRING 			/* base type of data */
-					};
-			CHECK_dict_new( DICT_AVP, &data , UTF8String_type, NULL);
-		}
-		
-		/* Termination-Cause */
-		{
-			/*
-				The Termination-Cause AVP (AVP Code 295) is of type Enumerated, and
-				is used to indicate the reason why a session was terminated on the
-				access device.  The following values are defined:
-
-
-				DIAMETER_LOGOUT 1
-
-				The user initiated a disconnect
-
-
-				DIAMETER_SERVICE_NOT_PROVIDED 2
-
-				This value is used when the user disconnected prior to the receipt
-				of the authorization answer message.
-
-
-				DIAMETER_BAD_ANSWER 3
-
-				This value indicates that the authorization answer received by the
-				access device was not processed successfully.
-
-
-				DIAMETER_ADMINISTRATIVE 4
-
-				The user was not granted access, or was disconnected, due to
-				administrative reasons, such as the receipt of a Abort-Session-
-				Request message.
-
-
-				DIAMETER_LINK_BROKEN 5
-
-				The communication to the user was abruptly disconnected.
-
-
-				DIAMETER_AUTH_EXPIRED 6
-
-				The user's access was terminated since its authorized session time
-				has expired.
-
-
-				DIAMETER_USER_MOVED 7
-
-				The user is receiving services from another access device.
-
-
-				DIAMETER_SESSION_TIMEOUT 8
-
-				The user's session has timed out, and service has been terminated.
-			*/
-			struct dict_object 	* 	type;
-			struct dict_type_data	 	tdata = { AVP_TYPE_INTEGER32,	"Enumerated(Termination-Cause)"	, NULL, NULL, NULL };
-			struct dict_enumval_data 	t_1 = { "DIAMETER_LOGOUT",			{ .i32 = 1 }};
-			struct dict_enumval_data 	t_2 = { "DIAMETER_SERVICE_NOT_PROVIDED", 	{ .i32 = 2 }};
-			struct dict_enumval_data 	t_3 = { "DIAMETER_BAD_ANSWER",			{ .i32 = 3 }};
-			struct dict_enumval_data 	t_4 = { "DIAMETER_ADMINISTRATIVE", 		{ .i32 = 4 }};
-			struct dict_enumval_data 	t_5 = { "DIAMETER_LINK_BROKEN",			{ .i32 = 5 }};
-			struct dict_enumval_data 	t_6 = { "DIAMETER_AUTH_EXPIRED", 		{ .i32 = 6 }};
-			struct dict_enumval_data 	t_7 = { "DIAMETER_USER_MOVED",			{ .i32 = 7 }};
-			struct dict_enumval_data 	t_8 = { "DIAMETER_SESSION_TIMEOUT", 		{ .i32 = 8 }};
-			struct dict_avp_data 	data = { 
-					295, 					/* Code */
-					0, 					/* Vendor */
-					"Termination-Cause",			/* Name */
-					AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, 	/* Fixed flags */
-					AVP_FLAG_MANDATORY,			/* Fixed flag values */
-					AVP_TYPE_INTEGER32 			/* base type of data */
-					};
-			/* Create the Enumerated type, and then the AVP */
-			CHECK_dict_new( DICT_TYPE, &tdata , NULL, &type);
-			CHECK_dict_new( DICT_ENUMVAL, &t_1 , type, NULL);
-			CHECK_dict_new( DICT_ENUMVAL, &t_2 , type, NULL);
-			CHECK_dict_new( DICT_ENUMVAL, &t_3 , type, NULL);
-			CHECK_dict_new( DICT_ENUMVAL, &t_4 , type, NULL);
-			CHECK_dict_new( DICT_ENUMVAL, &t_5 , type, NULL);
-			CHECK_dict_new( DICT_ENUMVAL, &t_6 , type, NULL);
-			CHECK_dict_new( DICT_ENUMVAL, &t_7 , type, NULL);
-			CHECK_dict_new( DICT_ENUMVAL, &t_8 , type, NULL);
-			CHECK_dict_new( DICT_AVP, &data , type, NULL);
-		}
-		
-		/* Origin-State-Id */
-		{
-			/*
-				The Origin-State-Id AVP (AVP Code 278), of type Unsigned32, is a
-				monotonically increasing value that is advanced whenever a Diameter
-				entity restarts with loss of previous state, for example upon reboot.
-				Origin-State-Id MAY be included in any Diameter message, including
-				CER.
-
-				A Diameter entity issuing this AVP MUST create a higher value for
-				this AVP each time its state is reset.  A Diameter entity MAY set
-				Origin-State-Id to the time of startup, or it MAY use an incrementing
-				counter retained in non-volatile memory across restarts.
-
-				The Origin-State-Id, if present, MUST reflect the state of the entity
-				indicated by Origin-Host.  If a proxy modifies Origin-Host, it MUST
-				either remove Origin-State-Id or modify it appropriately as well.
-				Typically, Origin-State-Id is used by an access device that always
-				starts up with no active sessions; that is, any session active prior
-				to restart will have been lost.  By including Origin-State-Id in a
-				message, it allows other Diameter entities to infer that sessions
-				associated with a lower Origin-State-Id are no longer active.  If an
-				access device does not intend for such inferences to be made, it MUST
-				either not include Origin-State-Id in any message, or set its value
-				to 0.
-			*/
-			struct dict_avp_data data = { 
-					278, 					/* Code */
-					0, 					/* Vendor */
-					"Origin-State-Id", 			/* Name */
-					AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, 	/* Fixed flags */
-					AVP_FLAG_MANDATORY,			/* Fixed flag values */
-					AVP_TYPE_UNSIGNED32 			/* base type of data */
-					};
-			CHECK_dict_new( DICT_AVP, &data , NULL, NULL);
-		}
-		
-		/* Session-Binding */
-		{
-			/*
-				The Session-Binding AVP (AVP Code 270) is of type Unsigned32, and MAY
-				be present in application-specific authorization answer messages.  If
-				present, this AVP MAY inform the Diameter client that all future
-				application-specific re-auth messages for this session MUST be sent
-				to the same authorization server.  This AVP MAY also specify that a
-				Session-Termination-Request message for this session MUST be sent to
-				the same authorizing server.
-
-				This field is a bit mask, and the following bits have been defined:
-
-
-				RE_AUTH 1
-
-				When set, future re-auth messages for this session MUST NOT
-				include the Destination-Host AVP.  When cleared, the default
-				value, the Destination-Host AVP MUST be present in all re-auth
-				messages for this session.
-
-
-				STR 2
-
-				When set, the STR message for this session MUST NOT include the
-				Destination-Host AVP.  When cleared, the default value, the
-				Destination-Host AVP MUST be present in the STR message for this
-				session.
-
-
-				ACCOUNTING 4
-
-				When set, all accounting messages for this session MUST NOT
-				include the Destination-Host AVP.  When cleared, the default
-				value, the Destination-Host AVP, if known, MUST be present in all
-				accounting messages for this session.
-			*/
-			
-			/* Although the RFC does not specify an "Enumerated" type here, we go forward and create one.
-			 * This is the reason for the "*" in the type name
-			 * The actual values of the AVP will not always be defined here, but at least it can be used in some cases.
-			 *  ... maybe the code will be changed later to support bitfields AVP ...
-			 */
-			
-			struct dict_object 	* 	type;
-			struct dict_type_data	 	tdata = { AVP_TYPE_UNSIGNED32,	"Enumerated*(Session-Binding)"	, NULL, NULL, NULL };
-			struct dict_enumval_data 	t_1 = { "RE_AUTH", 		{ .u32 = 1 }};
-			struct dict_enumval_data 	t_2 = { "STR", 			{ .u32 = 2 }};
-			struct dict_enumval_data 	t_4 = { "ACCOUNTING", 		{ .u32 = 4 }};
-			struct dict_avp_data 	data = { 
-					270, 					/* Code */
-					0, 					/* Vendor */
-					"Session-Binding", 			/* Name */
-					AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, 	/* Fixed flags */
-					AVP_FLAG_MANDATORY,			/* Fixed flag values */
-					AVP_TYPE_UNSIGNED32 			/* base type of data */
-					};
-			/* Create the Enumerated type, and then the AVP */
-			CHECK_dict_new( DICT_TYPE, &tdata , NULL, &type);
-			CHECK_dict_new( DICT_ENUMVAL, &t_1 , type, NULL);
-			CHECK_dict_new( DICT_ENUMVAL, &t_2 , type, NULL);
-			CHECK_dict_new( DICT_ENUMVAL, &t_4 , type, NULL);
-			CHECK_dict_new( DICT_AVP, &data , type, NULL);
-		}
-		
-		/* Session-Server-Failover */
-		{
-			/*
-				The Session-Server-Failover AVP (AVP Code 271) is of type Enumerated,
-				and MAY be present in application-specific authorization answer
-				messages that either do not include the Session-Binding AVP or
-				include the Session-Binding AVP with any of the bits set to a zero
-				value.  If present, this AVP MAY inform the Diameter client that if a
-				re-auth or STR message fails due to a delivery problem, the Diameter
-				client SHOULD issue a subsequent message without the Destination-Host
-				AVP.  When absent, the default value is REFUSE_SERVICE.
-
-				The following values are supported:
-
-
-				REFUSE_SERVICE 0
-
-				If either the re-auth or the STR message delivery fails, terminate
-				service with the user, and do not attempt any subsequent attempts.
-
-
-				TRY_AGAIN 1
-
-				If either the re-auth or the STR message delivery fails, resend
-				the failed message without the Destination-Host AVP present.
-
-
-				ALLOW_SERVICE 2
-
-				If re-auth message delivery fails, assume that re-authorization
-				succeeded.  If STR message delivery fails, terminate the session.
-
-
-				TRY_AGAIN_ALLOW_SERVICE 3
-
-				If either the re-auth or the STR message delivery fails, resend
-				the failed message without the Destination-Host AVP present.  If
-				the second delivery fails for re-auth, assume re-authorization
-				succeeded.  If the second delivery fails for STR, terminate the
-				session.
-			*/
-			struct dict_object  	* 	type;
-			struct dict_type_data	 	tdata = { AVP_TYPE_INTEGER32,	"Enumerated(Session-Server-Failover)"	, NULL, NULL, NULL };
-			struct dict_enumval_data 	t_0 = { "REFUSE_SERVICE", 		{ .i32 = 0 }};
-			struct dict_enumval_data 	t_1 = { "TRY_AGAIN",			{ .i32 = 1 }};
-			struct dict_enumval_data 	t_2 = { "ALLOW_SERVICE", 		{ .i32 = 2 }};
-			struct dict_enumval_data 	t_3 = { "TRY_AGAIN_ALLOW_SERVICE",	{ .i32 = 3 }};
-			struct dict_avp_data	 	data = { 
-					271, 					/* Code */
-					0, 					/* Vendor */
-					"Session-Server-Failover",		/* Name */
-					AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, 	/* Fixed flags */
-					AVP_FLAG_MANDATORY,			/* Fixed flag values */
-					AVP_TYPE_INTEGER32 			/* base type of data */
-					};
-			/* Create the Enumerated type, and then the AVP */
-			CHECK_dict_new( DICT_TYPE, &tdata , NULL, &type);
-			CHECK_dict_new( DICT_ENUMVAL, &t_0 , type, NULL);
-			CHECK_dict_new( DICT_ENUMVAL, &t_1 , type, NULL);
-			CHECK_dict_new( DICT_ENUMVAL, &t_2 , type, NULL);
-			CHECK_dict_new( DICT_ENUMVAL, &t_3 , type, NULL);
-			CHECK_dict_new( DICT_AVP, &data , type, NULL);
-		}
-		
-		/* Multi-Round-Time-Out */
-		{
-			/*
-				The Multi-Round-Time-Out AVP (AVP Code 272) is of type Unsigned32,
-				and SHOULD be present in application-specific authorization answer
-				messages whose Result-Code AVP is set to DIAMETER_MULTI_ROUND_AUTH.
-				This AVP contains the maximum number of seconds that the access
-				device MUST provide the user in responding to an authentication
-				request.
-			*/
-			struct dict_avp_data data = { 
-					272, 					/* Code */
-					0, 					/* Vendor */
-					"Multi-Round-Time-Out", 			/* Name */
-					AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, 	/* Fixed flags */
-					AVP_FLAG_MANDATORY,			/* Fixed flag values */
-					AVP_TYPE_UNSIGNED32 			/* base type of data */
-					};
-			CHECK_dict_new( DICT_AVP, &data , NULL, NULL);
-		}
-		
-		/* Class */
-		{
-			/*
-				The Class AVP (AVP Code 25) is of type OctetString and is used to by
-				Diameter servers to return state information to the access device.
-				When one or more Class AVPs are present in application-specific
-				authorization answer messages, they MUST be present in subsequent re-
-				authorization, session termination and accounting messages.  Class
-				AVPs found in a re-authorization answer message override the ones
-				found in any previous authorization answer message.  Diameter server
-				implementations SHOULD NOT return Class AVPs that require more than
-				4096 bytes of storage on the Diameter client.  A Diameter client that
-				receives Class AVPs whose size exceeds local available storage MUST
-				terminate the session.
-			*/
-			struct dict_avp_data data = { 
-					25, 					/* Code */
-					0, 					/* Vendor */
-					"Class", 				/* Name */
-					AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, 	/* Fixed flags */
-					AVP_FLAG_MANDATORY,			/* Fixed flag values */
-					AVP_TYPE_OCTETSTRING 			/* base type of data */
-					};
-			CHECK_dict_new( DICT_AVP, &data , NULL, NULL);
-		}
-		
-		/* Event-Timestamp */
-		{
-			/*
-				The Event-Timestamp (AVP Code 55) is of type Time, and MAY be
-				included in an Accounting-Request and Accounting-Answer messages to
-				record the time that the reported event occurred, in seconds since
-				January 1, 1900 00:00 UTC.
-			*/
-			struct dict_avp_data data = { 
-					55, 					/* Code */
-					0, 					/* Vendor */
-					"Event-Timestamp", 			/* Name */
-					AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, 	/* Fixed flags */
-					AVP_FLAG_MANDATORY,			/* Fixed flag values */
-					AVP_TYPE_OCTETSTRING 			/* base type of data */
-					};
-			CHECK_dict_new( DICT_AVP, &data , Time_type, NULL);
-		}
-
-				
-		/* Accounting-Record-Type */
-		{
-			/*
-				The Accounting-Record-Type AVP (AVP Code 480) is of type Enumerated
-				and contains the type of accounting record being sent.  The following
-				values are currently defined for the Accounting-Record-Type AVP:
-
-
-				EVENT_RECORD 1
-
-				An Accounting Event Record is used to indicate that a one-time
-				event has occurred (meaning that the start and end of the event
-				are simultaneous).  This record contains all information relevant
-				to the service, and is the only record of the service.
-
-
-				START_RECORD 2
-
-				An Accounting Start, Interim, and Stop Records are used to
-				indicate that a service of a measurable length has been given.  An
-				Accounting Start Record is used to initiate an accounting session,
-				and contains accounting information that is relevant to the
-				initiation of the session.
-
-
-				INTERIM_RECORD 3
-
-				An Interim Accounting Record contains cumulative accounting
-				information for an existing accounting session.  Interim
-				Accounting Records SHOULD be sent every time a re-authentication
-				or re-authorization occurs.  Further, additional interim record
-				triggers MAY be defined by application-specific Diameter
-				applications.  The selection of whether to use INTERIM_RECORD
-				records is done by the Acct-Interim-Interval AVP.
-
-
-				STOP_RECORD 4
-
-				An Accounting Stop Record is sent to terminate an accounting
-				session and contains cumulative accounting information relevant to
-				the existing session.
-			*/
-			struct dict_object 	* 	type;
-			struct dict_type_data	  	tdata = { AVP_TYPE_INTEGER32,	"Enumerated(Accounting-Record-Type)"	, NULL, NULL, NULL };
-			struct dict_enumval_data 	t_1 = { "EVENT_RECORD",			{ .i32 = 1 }};
-			struct dict_enumval_data 	t_2 = { "START_RECORD", 		{ .i32 = 2 }};
-			struct dict_enumval_data 	t_3 = { "INTERIM_RECORD",		{ .i32 = 3 }};
-			struct dict_enumval_data 	t_4 = { "STOP_RECORD", 			{ .i32 = 4 }};
-			struct dict_avp_data 	data = { 
-					480, 					/* Code */
-					0, 					/* Vendor */
-					"Accounting-Record-Type",		/* Name */
-					AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, 	/* Fixed flags */
-					AVP_FLAG_MANDATORY,			/* Fixed flag values */
-					AVP_TYPE_INTEGER32 			/* base type of data */
-					};
-			/* Create the Enumerated type, and then the AVP */
-			CHECK_dict_new( DICT_TYPE, &tdata , NULL, &type);
-			CHECK_dict_new( DICT_ENUMVAL, &t_1 , type, NULL);
-			CHECK_dict_new( DICT_ENUMVAL, &t_2 , type, NULL);
-			CHECK_dict_new( DICT_ENUMVAL, &t_3 , type, NULL);
-			CHECK_dict_new( DICT_ENUMVAL, &t_4 , type, NULL);
-			CHECK_dict_new( DICT_AVP, &data , type, NULL);
-		}
-		
-		/* Acct-Interim-Interval */
-		{
-			/*
-				The Acct-Interim-Interval AVP (AVP Code 85) is of type Unsigned32 and
-				is sent from the Diameter home authorization server to the Diameter
-				client.  The client uses information in this AVP to decide how and
-				when to produce accounting records.  With different values in this
-				AVP, service sessions can result in one, two, or two+N accounting
-				records, based on the needs of the home-organization.  The following
-				accounting record production behavior is directed by the inclusion of
-				this AVP:
-
-
-				1.  The omission of the Acct-Interim-Interval AVP or its inclusion
-				with Value field set to 0 means that EVENT_RECORD, START_RECORD,
-				and STOP_RECORD are produced, as appropriate for the service.
-
-
-				2.  The inclusion of the AVP with Value field set to a non-zero value
-				means that INTERIM_RECORD records MUST be produced between the
-				START_RECORD and STOP_RECORD records.  The Value field of this
-				AVP is the nominal interval between these records in seconds.
-
-				The Diameter node that originates the accounting information,
-				known as the client, MUST produce the first INTERIM_RECORD record
-				roughly at the time when this nominal interval has elapsed from
-				the START_RECORD, the next one again as the interval has elapsed
-				once more, and so on until the session ends and a STOP_RECORD
-				record is produced.
-
-				The client MUST ensure that the interim record production times
-				are randomized so that large accounting message storms are not
-				created either among records or around a common service start
-				time.
-			*/
-			struct dict_avp_data data = { 
-					85, 					/* Code */
-					0, 					/* Vendor */
-					"Acct-Interim-Interval", 		/* Name */
-					AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, 	/* Fixed flags */
-					AVP_FLAG_MANDATORY,			/* Fixed flag values */
-					AVP_TYPE_UNSIGNED32 			/* base type of data */
-					};
-			CHECK_dict_new( DICT_AVP, &data , NULL, NULL);
-		}
-		
-		/* Accounting-Record-Number */
-		{
-			/*
-				The Accounting-Record-Number AVP (AVP Code 485) is of type Unsigned32
-				and identifies this record within one session.  As Session-Id AVPs
-				are globally unique, the combination of Session-Id and Accounting-
-				Record-Number AVPs is also globally unique, and can be used in
-				matching accounting records with confirmations.  An easy way to
-				produce unique numbers is to set the value to 0 for records of type
-				EVENT_RECORD and START_RECORD, and set the value to 1 for the first
-				INTERIM_RECORD, 2 for the second, and so on until the value for
-				STOP_RECORD is one more than for the last INTERIM_RECORD.
-			*/
-			struct dict_avp_data data = { 
-					485, 					/* Code */
-					0, 					/* Vendor */
-					"Accounting-Record-Number", 		/* Name */
-					AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, 	/* Fixed flags */
-					AVP_FLAG_MANDATORY,			/* Fixed flag values */
-					AVP_TYPE_UNSIGNED32 			/* base type of data */
-					};
-			CHECK_dict_new( DICT_AVP, &data , NULL, NULL);
-		}
-		
-		/* Acct-Session-Id */
-		{
-			/*
-				The Acct-Session-Id AVP (AVP Code 44) is of type OctetString is only
-				used when RADIUS/Diameter translation occurs.  This AVP contains the
-				contents of the RADIUS Acct-Session-Id attribute.
-			*/
-			struct dict_avp_data data = { 
-					44, 					/* Code */
-					0, 					/* Vendor */
-					"Acct-Session-Id", 			/* Name */
-					AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, 	/* Fixed flags */
-					AVP_FLAG_MANDATORY,			/* Fixed flag values */
-					AVP_TYPE_OCTETSTRING 			/* base type of data */
-					};
-			CHECK_dict_new( DICT_AVP, &data , NULL, NULL);
-		}
-		
-		/* Acct-Multi-Session-Id */
-		{
-			/*
-				The Acct-Multi-Session-Id AVP (AVP Code 50) is of type UTF8String,
-				following the format specified in Section 8.8.  The Acct-Multi-
-				Session-Id AVP is used to link together multiple related accounting
-				sessions, where each session would have a unique Session-Id, but the
-				same Acct-Multi-Session-Id AVP.  This AVP MAY be returned by the
-				Diameter server in an authorization answer, and MUST be used in all
-				accounting messages for the given session.
-			*/
-			struct dict_avp_data data = { 
-					50, 					/* Code */
-					0, 					/* Vendor */
-					"Acct-Multi-Session-Id", 		/* Name */
-					AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, 	/* Fixed flags */
-					AVP_FLAG_MANDATORY,			/* Fixed flag values */
-					AVP_TYPE_OCTETSTRING 			/* base type of data */
-					};
-			CHECK_dict_new( DICT_AVP, &data , UTF8String_type, NULL);
-		}
-		
-		/* Accounting-Sub-Session-Id */
-		{
-			/*
-				The Accounting-Sub-Session-Id AVP (AVP Code 287) is of type
-				Unsigned64 and contains the accounting sub-session identifier.  The
-				combination of the Session-Id and this AVP MUST be unique per sub-
-				session, and the value of this AVP MUST be monotonically increased by
-				one for all new sub-sessions.  The absence of this AVP implies no
-				sub-sessions are in use, with the exception of an Accounting-Request
-				whose Accounting-Record-Type is set to STOP_RECORD.  A STOP_RECORD
-				message with no Accounting-Sub-Session-Id AVP present will signal the
-				termination of all sub-sessions for a given Session-Id.
-			*/
-			struct dict_avp_data data = { 
-					287, 					/* Code */
-					0, 					/* Vendor */
-					"Accounting-Sub-Session-Id", 		/* Name */
-					AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, 	/* Fixed flags */
-					AVP_FLAG_MANDATORY,			/* Fixed flag values */
-					AVP_TYPE_UNSIGNED64 			/* base type of data */
-					};
-			CHECK_dict_new( DICT_AVP, &data , NULL, NULL);
-		}
-		
-		/* Accounting-Realtime-Required */
-		{
-			/*
-				The Accounting-Realtime-Required AVP (AVP Code 483) is of type
-				Enumerated and is sent from the Diameter home authorization server to
-				the Diameter client or in the Accounting-Answer from the accounting
-				server.  The client uses information in this AVP to decide what to do
-				if the sending of accounting records to the accounting server has
-				been temporarily prevented due to, for instance, a network problem.
-
-
-				DELIVER_AND_GRANT 1
-
-				The AVP with Value field set to DELIVER_AND_GRANT means that the
-				service MUST only be granted as long as there is a connection to
-				an accounting server.  Note that the set of alternative accounting
-				servers are treated as one server in this sense.  Having to move
-				the accounting record stream to a backup server is not a reason to
-				discontinue the service to the user.
-
-
-				GRANT_AND_STORE 2
-
-				The AVP with Value field set to GRANT_AND_STORE means that service
-				SHOULD be granted if there is a connection, or as long as records
-				can still be stored as described in Section 9.4.
-
-				This is the default behavior if the AVP isn't included in the
-				reply from the authorization server.
-
-
-				GRANT_AND_LOSE 3
-
-				The AVP with Value field set to GRANT_AND_LOSE means that service
-				SHOULD be granted even if the records can not be delivered or
-				stored.
-			*/
-			struct dict_object  	* 	type;
-			struct dict_type_data	 	tdata = { AVP_TYPE_INTEGER32,	"Enumerated(Accounting-Realtime-Required)"	, NULL, NULL, NULL };
-			struct dict_enumval_data 	t_1 = { "DELIVER_AND_GRANT",		{ .i32 = 1 }};
-			struct dict_enumval_data 	t_2 = { "GRANT_AND_STORE", 		{ .i32 = 2 }};
-			struct dict_enumval_data 	t_3 = { "GRANT_AND_LOSE",		{ .i32 = 3 }};
-			struct dict_avp_data 		data = { 
-					483, 					/* Code */
-					0, 					/* Vendor */
-					"Accounting-Realtime-Required",		/* Name */
-					AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, 	/* Fixed flags */
-					AVP_FLAG_MANDATORY,			/* Fixed flag values */
-					AVP_TYPE_INTEGER32 			/* base type of data */
-					};
-			/* Create the Enumerated type, and then the AVP */
-			CHECK_dict_new( DICT_TYPE, &tdata , NULL, &type);
-			CHECK_dict_new( DICT_ENUMVAL, &t_1 , type, NULL);
-			CHECK_dict_new( DICT_ENUMVAL, &t_2 , type, NULL);
-			CHECK_dict_new( DICT_ENUMVAL, &t_3 , type, NULL);
-			CHECK_dict_new( DICT_AVP, &data , type, NULL);
-		}
-		
-	}
-	
-	/* Commands section */
-	{
-		/* To avoid defining global variables for all the AVP that we use here, we do search the dictionary in each sub-block.
-		 * This is far from optimal, but the code is clearer like this, and the time it requires at execution is not noticeable.
-		 */
-		
-		/* Generic message syntax when the 'E' bit is set */
-		{
-			/*
-				The 'E' (Error Bit) in the Diameter header is set when the request
-				caused a protocol-related error (see Section 7.1.3).  A message with
-				the 'E' bit MUST NOT be sent as a response to an answer message.
-				Note that a message with the 'E' bit set is still subjected to the
-				processing rules defined in Section 6.2.  When set, the answer
-				message will not conform to the ABNF specification for the command,
-				and will instead conform to the following ABNF:
-
-				Message Format
-
-				<answer-message> ::= < Diameter Header: code, ERR [PXY] >
-                				0*1< Session-Id >
-                				   { Origin-Host }
-                				   { Origin-Realm }
-                				   { Result-Code }
-                				   [ Origin-State-Id ]
-                				   [ Error-Message ]
-                				   [ Error-Reporting-Host ]
-                				   [ Failed-AVP ]
-                				 * [ Proxy-Info ]
-                				 * [ AVP ]
-
-				Note that the code used in the header is the same than the one found
-				in the request message, but with the 'R' bit cleared and the 'E' bit
-				set.  The 'P' bit in the header is set to the same value as the one
-				found in the request message.
-			*/
-			struct dict_object * cmd_error;
-			struct local_rules_definition rules[] = 
-						{ 	 {  "Session-Id", 			RULE_FIXED_HEAD,0, 1 }
-							,{  "Origin-Host",			RULE_REQUIRED, -1, 1 }
-							,{  "Origin-Realm",			RULE_REQUIRED, -1, 1 }
-							,{  "Result-Code",			RULE_REQUIRED, -1, 1 }
-							,{  "Origin-State-Id",			RULE_OPTIONAL, -1, 1 }
-							,{  "Error-Message",			RULE_OPTIONAL, -1, 1 }
-							,{  "Error-Reporting-Host",		RULE_OPTIONAL, -1, 1 }
-							,{  "Failed-AVP",			RULE_OPTIONAL, -1, 1 }
-							,{  "Proxy-Info",			RULE_OPTIONAL, -1,-1 }
-						};
-			CHECK_FCT( fd_dict_get_error_cmd(dict, &cmd_error) );
-			PARSE_loc_rules( rules, cmd_error );
-		}		
-		
-		/* Capabilities-Exchange-Request */
-		{
-			/*
-				The Capabilities-Exchange-Request (CER), indicated by the Command-
-				Code set to 257 and the Command Flags' 'R' bit set, is sent to
-				exchange local capabilities.  Upon detection of a transport failure,
-				this message MUST NOT be sent to an alternate peer.
-
-				When Diameter is run over SCTP [RFC2960], which allows for
-				connections to span multiple interfaces and multiple IP addresses,
-				the Capabilities-Exchange-Request message MUST contain one Host-IP-
-				Address AVP for each potential IP address that MAY be locally used
-				when transmitting Diameter messages.
-
-				Message Format
-
-				 <CER> ::= < Diameter Header: 257, REQ >
-        				   { Origin-Host }
-        				   { Origin-Realm }
-        				1* { Host-IP-Address }
-        				   { Vendor-Id }
-        				   { Product-Name }
-        				   [ Origin-State-Id ]
-        				 * [ Supported-Vendor-Id ]
-        				 * [ Auth-Application-Id ]
-        				 * [ Inband-Security-Id ]
-        				 * [ Acct-Application-Id ]
-        				 * [ Vendor-Specific-Application-Id ]
-        				   [ Firmware-Revision ]
-        				 * [ AVP ]
-			*/
-			struct dict_object * cmd;
-			struct dict_cmd_data data = { 
-					257, 					/* Code */
-					#if CC_CAPABILITIES_EXCHANGE != 257
-					#error "CC_CAPABILITIES_EXCHANGE definition mismatch"
-					#endif
-					"Capabilities-Exchange-Request", 	/* Name */
-					CMD_FLAG_REQUEST | CMD_FLAG_PROXIABLE | CMD_FLAG_RETRANSMIT | CMD_FLAG_ERROR, 	/* Fixed flags */
-					CMD_FLAG_REQUEST 			/* Fixed flag values */
-					};
-			struct local_rules_definition rules[] = 
-						{ 	 {  "Origin-Host", 			RULE_REQUIRED, -1, 1 }
-							,{  "Origin-Realm",			RULE_REQUIRED, -1, 1 }
-							,{  "Host-IP-Address",			RULE_REQUIRED, -1,-1 }
-							,{  "Vendor-Id",			RULE_REQUIRED, -1, 1 }
-							,{  "Product-Name",			RULE_REQUIRED, -1, 1 }
-							,{  "Origin-State-Id",			RULE_OPTIONAL, -1, 1 }
-							,{  "Supported-Vendor-Id",		RULE_OPTIONAL, -1,-1 }
-							,{  "Auth-Application-Id",		RULE_OPTIONAL, -1,-1 }
-							,{  "Inband-Security-Id",		RULE_OPTIONAL, -1,-1 }
-							,{  "Acct-Application-Id",		RULE_OPTIONAL, -1,-1 }
-							,{  "Vendor-Specific-Application-Id",	RULE_OPTIONAL, -1,-1 }
-							,{  "Firmware-Revision",		RULE_OPTIONAL, -1, 1 }
-						};
-			
-			CHECK_dict_new( DICT_COMMAND, &data , NULL, &cmd);
-			PARSE_loc_rules( rules, cmd );
-		}
-		
-		/* Capabilities-Exchange-Answer */
-		{
-			/*
-				The Capabilities-Exchange-Answer (CEA), indicated by the Command-Code
-				set to 257 and the Command Flags' 'R' bit cleared, is sent in
-				response to a CER message.
-
-				When Diameter is run over SCTP [RFC2960], which allows connections to
-				span multiple interfaces, hence, multiple IP addresses, the
-				Capabilities-Exchange-Answer message MUST contain one Host-IP-Address
-				AVP for each potential IP address that MAY be locally used when
-				transmitting Diameter messages.
-
-				Message Format
-
-				 <CEA> ::= < Diameter Header: 257 >
-        				   { Result-Code }
-        				   { Origin-Host }
-        				   { Origin-Realm }
-        				1* { Host-IP-Address }
-        				   { Vendor-Id }
-        				   { Product-Name }
-        				   [ Origin-State-Id ]
-        				   [ Error-Message ]
-        				   [ Failed-AVP ]
-        				 * [ Supported-Vendor-Id ]
-        				 * [ Auth-Application-Id ]
-        				 * [ Inband-Security-Id ]
-        				 * [ Acct-Application-Id ]
-        				 * [ Vendor-Specific-Application-Id ]
-        				   [ Firmware-Revision ]
-        				 * [ AVP ]
-			*/
-			struct dict_object * cmd;
-			struct dict_cmd_data data = { 
-					257, 					/* Code */
-					#if CC_CAPABILITIES_EXCHANGE != 257
-					#error "CC_CAPABILITIES_EXCHANGE definition mismatch"
-					#endif
-					"Capabilities-Exchange-Answer", 	/* Name */
-					CMD_FLAG_REQUEST | CMD_FLAG_PROXIABLE | CMD_FLAG_RETRANSMIT, 	/* Fixed flags */
-					0 					/* Fixed flag values */
-					};
-			struct local_rules_definition rules[] = 
-						{ 	 {  "Result-Code", 			RULE_REQUIRED, -1, 1 }
-						 	,{  "Origin-Host", 			RULE_REQUIRED, -1, 1 }
-							,{  "Origin-Realm",			RULE_REQUIRED, -1, 1 }
-							,{  "Host-IP-Address",			RULE_REQUIRED, -1,-1 }
-							,{  "Vendor-Id",			RULE_REQUIRED, -1, 1 }
-							,{  "Product-Name",			RULE_REQUIRED, -1, 1 }
-							,{  "Origin-State-Id",			RULE_OPTIONAL, -1, 1 }
-							,{  "Error-Message",			RULE_OPTIONAL, -1, 1 }
-							,{  "Failed-AVP",			RULE_OPTIONAL, -1, 1 }
-							,{  "Supported-Vendor-Id",		RULE_OPTIONAL, -1,-1 }
-							,{  "Auth-Application-Id",		RULE_OPTIONAL, -1,-1 }
-							,{  "Inband-Security-Id",		RULE_OPTIONAL, -1,-1 }
-							,{  "Acct-Application-Id",		RULE_OPTIONAL, -1,-1 }
-							,{  "Vendor-Specific-Application-Id",	RULE_OPTIONAL, -1,-1 }
-							,{  "Firmware-Revision",		RULE_OPTIONAL, -1, 1 }
-						};
-			
-			CHECK_dict_new( DICT_COMMAND, &data , NULL, &cmd);
-			PARSE_loc_rules( rules, cmd );
-		}
-		
-		/* Disconnect-Peer-Request */
-		{
-			/*
-				The Disconnect-Peer-Request (DPR), indicated by the Command-Code set
-				to 282 and the Command Flags' 'R' bit set, is sent to a peer to
-				inform its intentions to shutdown the transport connection.  Upon
-				detection of a transport failure, this message MUST NOT be sent to an
-				alternate peer.
-
-				Message Format
-
-				 <DPR>  ::= < Diameter Header: 282, REQ >
-        				    { Origin-Host }
-        				    { Origin-Realm }
-        				    { Disconnect-Cause }
-			*/
-			struct dict_object * cmd;
-			struct dict_cmd_data data = { 
-					282, 					/* Code */
-					#if CC_DISCONNECT_PEER != 282
-					#error "CC_DISCONNECT_PEER definition mismatch"
-					#endif
-					"Disconnect-Peer-Request", 		/* Name */
-					CMD_FLAG_REQUEST | CMD_FLAG_PROXIABLE | CMD_FLAG_RETRANSMIT | CMD_FLAG_ERROR, 	/* Fixed flags */
-					CMD_FLAG_REQUEST 			/* Fixed flag values */
-					};
-			struct local_rules_definition rules[] = 
-						{ 	 {  "Origin-Host", 			RULE_REQUIRED, -1, 1 }
-							,{  "Origin-Realm",			RULE_REQUIRED, -1, 1 }
-							,{  "Disconnect-Cause",			RULE_REQUIRED, -1, 1 }
-						};
-			
-			CHECK_dict_new( DICT_COMMAND, &data , NULL, &cmd);
-			PARSE_loc_rules( rules, cmd );
-		}
-		
-		/* Disconnect-Peer-Answer */
-		{
-			/*
-				The Disconnect-Peer-Answer (DPA), indicated by the Command-Code set
-				to 282 and the Command Flags' 'R' bit cleared, is sent as a response
-				to the Disconnect-Peer-Request message.  Upon receipt of this
-				message, the transport connection is shutdown.
-
-				Message Format
-
-				 <DPA>  ::= < Diameter Header: 282 >
-        				    { Result-Code }
-        				    { Origin-Host }
-        				    { Origin-Realm }
-        				    [ Error-Message ]
-        				    [ Failed-AVP ]
-			*/
-			struct dict_object * cmd;
-			struct dict_cmd_data data = { 
-					282, 					/* Code */
-					#if CC_DISCONNECT_PEER != 282
-					#error "CC_DISCONNECT_PEER definition mismatch"
-					#endif
-					"Disconnect-Peer-Answer", 		/* Name */
-					CMD_FLAG_REQUEST | CMD_FLAG_PROXIABLE | CMD_FLAG_RETRANSMIT, 	/* Fixed flags */
-					0 					/* Fixed flag values */
-					};
-			struct local_rules_definition rules[] = 
-						{ 	 {  "Result-Code", 			RULE_REQUIRED, -1, 1 }
-						 	,{  "Origin-Host", 			RULE_REQUIRED, -1, 1 }
-							,{  "Origin-Realm",			RULE_REQUIRED, -1, 1 }
-							,{  "Error-Message",			RULE_OPTIONAL, -1, 1 }
-							,{  "Failed-AVP",			RULE_OPTIONAL, -1, 1 }
-						};
-			
-			CHECK_dict_new( DICT_COMMAND, &data , NULL, &cmd);
-			PARSE_loc_rules( rules, cmd );
-		}
-
-		/* Device-Watchdog-Request */
-		{
-			/*
-				The Device-Watchdog-Request (DWR), indicated by the Command-Code set
-				to 280 and the Command Flags' 'R' bit set, is sent to a peer when no
-				traffic has been exchanged between two peers (see Section 5.5.3).
-				Upon detection of a transport failure, this message MUST NOT be sent
-				to an alternate peer.
-
-				Message Format
-
-				 <DWR>  ::= < Diameter Header: 280, REQ >
-        				    { Origin-Host }
-        				    { Origin-Realm }
-        				    [ Origin-State-Id ]
-			*/
-			struct dict_object * cmd;
-			struct dict_cmd_data data = { 
-					280, 					/* Code */
-					#if CC_DEVICE_WATCHDOG != 280
-					#error "CC_DEVICE_WATCHDOG definition mismatch"
-					#endif
-					"Device-Watchdog-Request", 		/* Name */
-					CMD_FLAG_REQUEST | CMD_FLAG_PROXIABLE | CMD_FLAG_RETRANSMIT | CMD_FLAG_ERROR, 	/* Fixed flags */
-					CMD_FLAG_REQUEST 			/* Fixed flag values */
-					};
-			struct local_rules_definition rules[] = 
-						{ 	 {  "Origin-Host", 			RULE_REQUIRED, -1, 1 }
-							,{  "Origin-Realm",			RULE_REQUIRED, -1, 1 }
-							,{  "Origin-State-Id",			RULE_OPTIONAL, -1, 1 }
-						};
-			
-			CHECK_dict_new( DICT_COMMAND, &data , NULL, &cmd);
-			PARSE_loc_rules( rules, cmd );
-		}
-		
-		/* Device-Watchdog-Answer */
-		{
-			/*
-				The Device-Watchdog-Answer (DWA), indicated by the Command-Code set
-				to 280 and the Command Flags' 'R' bit cleared, is sent as a response
-				to the Device-Watchdog-Request message.
-
-				Message Format
-
-				 <DWA>  ::= < Diameter Header: 280 >
-        				    { Result-Code }
-        				    { Origin-Host }
-        				    { Origin-Realm }
-        				    [ Error-Message ]
-        				    [ Failed-AVP ]
-        				    [ Origin-State-Id ]
-			*/
-			struct dict_object * cmd;
-			struct dict_cmd_data data = { 
-					280, 					/* Code */
-					#if CC_DEVICE_WATCHDOG != 280
-					#error "CC_DEVICE_WATCHDOG definition mismatch"
-					#endif
-					"Device-Watchdog-Answer", 		/* Name */
-					CMD_FLAG_REQUEST | CMD_FLAG_PROXIABLE | CMD_FLAG_RETRANSMIT, 	/* Fixed flags */
-					0 					/* Fixed flag values */
-					};
-			struct local_rules_definition rules[] = 
-						{ 	 {  "Result-Code", 			RULE_REQUIRED, -1, 1 }
-						 	,{  "Origin-Host", 			RULE_REQUIRED, -1, 1 }
-							,{  "Origin-Realm",			RULE_REQUIRED, -1, 1 }
-							,{  "Error-Message",			RULE_OPTIONAL, -1, 1 }
-							,{  "Failed-AVP",			RULE_OPTIONAL, -1, 1 }
-							,{  "Origin-State-Id",			RULE_OPTIONAL, -1, 1 }
-						};
-			
-			CHECK_dict_new( DICT_COMMAND, &data , NULL, &cmd);
-			PARSE_loc_rules( rules, cmd );
-		}
-		
-		/* Re-Auth-Request */
-		{
-			/*
-				The Re-Auth-Request (RAR), indicated by the Command-Code set to 258
-				and the message flags' 'R' bit set, may be sent by any server to the
-				access device that is providing session service, to request that the
-				user be re-authenticated and/or re-authorized.
-
-
-				Message Format
-
-				 <RAR>  ::= < Diameter Header: 258, REQ, PXY >
-        				    < Session-Id >
-        				    { Origin-Host }
-        				    { Origin-Realm }
-        				    { Destination-Realm }
-        				    { Destination-Host }
-        				    { Auth-Application-Id }
-        				    { Re-Auth-Request-Type }
-        				    [ User-Name ]
-        				    [ Origin-State-Id ]
-        				  * [ Proxy-Info ]
-        				  * [ Route-Record ]
-        				  * [ AVP ]
-			*/
-			struct dict_object * cmd;
-			struct dict_cmd_data data = { 
-					258, 					/* Code */
-					#if CC_RE_AUTH != 258
-					#error "CC_RE_AUTH definition mismatch"
-					#endif
-					"Re-Auth-Request", 			/* Name */
-					CMD_FLAG_REQUEST | CMD_FLAG_PROXIABLE | CMD_FLAG_ERROR,	/* Fixed flags */
-					CMD_FLAG_REQUEST | CMD_FLAG_PROXIABLE	/* Fixed flag values */
-					};
-			struct local_rules_definition rules[] = 
-						{ 	 {  "Session-Id", 			RULE_FIXED_HEAD, -1, 1 }
-						 	,{  "Origin-Host", 			RULE_REQUIRED,   -1, 1 }
-							,{  "Origin-Realm",			RULE_REQUIRED,   -1, 1 }
-							,{  "Destination-Realm",		RULE_REQUIRED,   -1, 1 }
-						 	,{  "Destination-Host", 		RULE_REQUIRED,   -1, 1 }
-						 	,{  "Auth-Application-Id", 		RULE_REQUIRED,   -1, 1 }
-						 	,{  "Re-Auth-Request-Type", 		RULE_REQUIRED,   -1, 1 }
-							,{  "User-Name",			RULE_OPTIONAL,   -1, 1 }
-							,{  "Origin-State-Id",			RULE_OPTIONAL,   -1, 1 }
-							,{  "Proxy-Info",			RULE_OPTIONAL,   -1,-1 }
-							,{  "Route-Record",			RULE_OPTIONAL,   -1,-1 }
-						};
-			
-			CHECK_dict_new( DICT_COMMAND, &data , NULL, &cmd);
-			PARSE_loc_rules( rules, cmd );
-		}
-		
-		/* Re-Auth-Answer */
-		{
-			/*
-				The Re-Auth-Answer (RAA), indicated by the Command-Code set to 258
-				and the message flags' 'R' bit clear, is sent in response to the RAR.
-				The Result-Code AVP MUST be present, and indicates the disposition of
-				the request.
-
-				A successful RAA message MUST be followed by an application-specific
-				authentication and/or authorization message.
-
-
-				Message Format
-
-				 <RAA>  ::= < Diameter Header: 258, PXY >
-        				    < Session-Id >
-        				    { Result-Code }
-        				    { Origin-Host }
-        				    { Origin-Realm }
-        				    [ User-Name ]
-        				    [ Origin-State-Id ]
-        				    [ Error-Message ]
-        				    [ Error-Reporting-Host ]
-        				    [ Failed-AVP ]
-        				  * [ Redirect-Host ]
-        				    [ Redirect-Host-Usage ]
-        				    [ Redirect-Max-Cache-Time ]
-        				  * [ Proxy-Info ]
-        				  * [ AVP ]
-			*/
-			struct dict_object * cmd;
-			struct dict_cmd_data data = { 
-					258, 					/* Code */
-					#if CC_RE_AUTH != 258
-					#error "CC_RE_AUTH definition mismatch"
-					#endif
-					"Re-Auth-Answer", 			/* Name */
-					CMD_FLAG_REQUEST | CMD_FLAG_PROXIABLE,	/* Fixed flags */
-							   CMD_FLAG_PROXIABLE	/* Fixed flag values */
-					};
-			struct local_rules_definition rules[] = 
-						{ 	 {  "Session-Id", 			RULE_FIXED_HEAD, -1, 1 }
- 							,{  "Result-Code", 			RULE_REQUIRED,   -1, 1 }
-						 	,{  "Origin-Host", 			RULE_REQUIRED,   -1, 1 }
-							,{  "Origin-Realm",			RULE_REQUIRED,   -1, 1 }
-							,{  "User-Name",			RULE_OPTIONAL,   -1, 1 }
-							,{  "Origin-State-Id",			RULE_OPTIONAL,   -1, 1 }
-							,{  "Error-Message",			RULE_OPTIONAL,   -1, 1 }
-							,{  "Error-Reporting-Host",		RULE_OPTIONAL,   -1, 1 }
-							,{  "Failed-AVP",			RULE_OPTIONAL,   -1, 1 }
-							,{  "Redirect-Host",			RULE_OPTIONAL,   -1,-1 }
-							,{  "Redirect-Host-Usage",		RULE_OPTIONAL,   -1, 1 }
-							,{  "Redirect-Max-Cache-Time",		RULE_OPTIONAL,   -1, 1 }
-							,{  "Proxy-Info",			RULE_OPTIONAL,   -1,-1 }
-						};
-			
-			CHECK_dict_new( DICT_COMMAND, &data , NULL, &cmd);
-			PARSE_loc_rules( rules, cmd );
-		}
-		
-		/* Session-Termination-Request */
-		{
-			/*
-				The Session-Termination-Request (STR), indicated by the Command-Code
-				set to 275 and the Command Flags' 'R' bit set, is sent by the access
-				device to inform the Diameter Server that an authenticated and/or
-				authorized session is being terminated.
-
-
-        				   Message Format
-
-				 <STR> ::= < Diameter Header: 275, REQ, PXY >
-        				   < Session-Id >
-        				   { Origin-Host }
-        				   { Origin-Realm }
-        				   { Destination-Realm }
-        				   { Auth-Application-Id }
-        				   { Termination-Cause }
-        				   [ User-Name ]
-        				   [ Destination-Host ]
-        				 * [ Class ]
-        				   [ Origin-State-Id ]
-        				 * [ Proxy-Info ]
-        				 * [ Route-Record ]
-        				 * [ AVP ]
-			*/
-			struct dict_object * cmd;
-			struct dict_cmd_data data = { 
-					275, 					/* Code */
-					#if CC_SESSION_TERMINATION != 275
-					#error "CC_SESSION_TERMINATION definition mismatch"
-					#endif
-					"Session-Termination-Request", 		/* Name */
-					CMD_FLAG_REQUEST | CMD_FLAG_PROXIABLE | CMD_FLAG_ERROR,	/* Fixed flags */
-					CMD_FLAG_REQUEST | CMD_FLAG_PROXIABLE	/* Fixed flag values */
-					};
-			struct local_rules_definition rules[] = 
-						{ 	 {  "Session-Id", 			RULE_FIXED_HEAD, -1, 1 }
-						 	,{  "Origin-Host", 			RULE_REQUIRED,   -1, 1 }
-							,{  "Origin-Realm",			RULE_REQUIRED,   -1, 1 }
-							,{  "Destination-Realm",		RULE_REQUIRED,   -1, 1 }
-						 	,{  "Auth-Application-Id", 		RULE_REQUIRED,   -1, 1 }
-						 	,{  "Termination-Cause", 		RULE_REQUIRED,   -1, 1 }
-							,{  "User-Name",			RULE_OPTIONAL,   -1, 1 }
-							,{  "Destination-Host",			RULE_OPTIONAL,   -1, 1 }
-							,{  "Class",				RULE_OPTIONAL,   -1,-1 }
-							,{  "Origin-State-Id",			RULE_OPTIONAL,   -1, 1 }
-							,{  "Proxy-Info",			RULE_OPTIONAL,   -1,-1 }
-							,{  "Route-Record",			RULE_OPTIONAL,   -1,-1 }
-						};
-			
-			CHECK_dict_new( DICT_COMMAND, &data , NULL, &cmd);
-			PARSE_loc_rules( rules, cmd );
-		}
-		
-		/* Session-Termination-Answer */
-		{
-			/*
-				The Session-Termination-Answer (STA), indicated by the Command-Code
-				set to 275 and the message flags' 'R' bit clear, is sent by the
-				Diameter Server to acknowledge the notification that the session has
-				been terminated.  The Result-Code AVP MUST be present, and MAY
-				contain an indication that an error occurred while servicing the STR.
-
-				Upon sending or receipt of the STA, the Diameter Server MUST release
-				all resources for the session indicated by the Session-Id AVP.  Any
-				intermediate server in the Proxy-Chain MAY also release any
-				resources, if necessary.
-
-        				    Message Format
-
-				 <STA>  ::= < Diameter Header: 275, PXY >
-        				    < Session-Id >
-        				    { Result-Code }
-        				    { Origin-Host }
-        				    { Origin-Realm }
-        				    [ User-Name ]
-        				  * [ Class ]
-        				    [ Error-Message ]
-        				    [ Error-Reporting-Host ]
-        				    [ Failed-AVP ]
-        				    [ Origin-State-Id ]
-        				  * [ Redirect-Host ]
-        				    [ Redirect-Host-Usage ]
-        				    [ Redirect-Max-Cache-Time ]
-        				  * [ Proxy-Info ]
-        				  * [ AVP ]
-			*/
-			struct dict_object * cmd;
-			struct dict_cmd_data data = { 
-					275, 					/* Code */
-					#if CC_SESSION_TERMINATION != 275
-					#error "CC_SESSION_TERMINATION definition mismatch"
-					#endif
-					"Session-Termination-Answer", 		/* Name */
-					CMD_FLAG_REQUEST | CMD_FLAG_PROXIABLE,	/* Fixed flags */
-							   CMD_FLAG_PROXIABLE	/* Fixed flag values */
-					};
-			struct local_rules_definition rules[] = 
-						{ 	 {  "Session-Id", 			RULE_FIXED_HEAD, -1, 1 }
- 							,{  "Result-Code", 			RULE_REQUIRED,   -1, 1 }
-						 	,{  "Origin-Host", 			RULE_REQUIRED,   -1, 1 }
-							,{  "Origin-Realm",			RULE_REQUIRED,   -1, 1 }
-							,{  "User-Name",			RULE_OPTIONAL,   -1, 1 }
-							,{  "Class",				RULE_OPTIONAL,   -1,-1 }
-							,{  "Error-Message",			RULE_OPTIONAL,   -1, 1 }
-							,{  "Error-Reporting-Host",		RULE_OPTIONAL,   -1, 1 }
-							,{  "Failed-AVP",			RULE_OPTIONAL,   -1, 1 }
-							,{  "Origin-State-Id",			RULE_OPTIONAL,   -1, 1 }
-							,{  "Redirect-Host",			RULE_OPTIONAL,   -1,-1 }
-							,{  "Redirect-Host-Usage",		RULE_OPTIONAL,   -1, 1 }
-							,{  "Redirect-Max-Cache-Time",		RULE_OPTIONAL,   -1, 1 }
-							,{  "Proxy-Info",			RULE_OPTIONAL,   -1,-1 }
-						};
-			
-			CHECK_dict_new( DICT_COMMAND, &data , NULL, &cmd);
-			PARSE_loc_rules( rules, cmd );
-		}
-		
-		/* Abort-Session-Request */
-		{
-			/*
-				The Abort-Session-Request (ASR), indicated by the Command-Code set to
-				274 and the message flags' 'R' bit set, may be sent by any server to
-				the access device that is providing session service, to request that
-				the session identified by the Session-Id be stopped.
-
-
-        				    Message Format
-
-				 <ASR>  ::= < Diameter Header: 274, REQ, PXY >
-        				    < Session-Id >
-        				    { Origin-Host }
-        				    { Origin-Realm }
-        				    { Destination-Realm }
-        				    { Destination-Host }
-        				    { Auth-Application-Id }
-        				    [ User-Name ]
-        				    [ Origin-State-Id ]
-        				  * [ Proxy-Info ]
-        				  * [ Route-Record ]
-        				  * [ AVP ]
-			*/
-			struct dict_object * cmd;
-			struct dict_cmd_data data = { 
-					274, 					/* Code */
-					#if CC_ABORT_SESSION != 274
-					#error "CC_ABORT_SESSION definition mismatch"
-					#endif
-					"Abort-Session-Request", 		/* Name */
-					CMD_FLAG_REQUEST | CMD_FLAG_PROXIABLE | CMD_FLAG_ERROR,	/* Fixed flags */
-					CMD_FLAG_REQUEST | CMD_FLAG_PROXIABLE	/* Fixed flag values */
-					};
-			struct local_rules_definition rules[] = 
-						{ 	 {  "Session-Id", 			RULE_FIXED_HEAD, -1, 1 }
-						 	,{  "Origin-Host", 			RULE_REQUIRED,   -1, 1 }
-							,{  "Origin-Realm",			RULE_REQUIRED,   -1, 1 }
-							,{  "Destination-Realm",		RULE_REQUIRED,   -1, 1 }
-							,{  "Destination-Host",			RULE_REQUIRED,   -1, 1 }
-						 	,{  "Auth-Application-Id", 		RULE_REQUIRED,   -1, 1 }
-							,{  "User-Name",			RULE_OPTIONAL,   -1, 1 }
-							,{  "Origin-State-Id",			RULE_OPTIONAL,   -1, 1 }
-							,{  "Proxy-Info",			RULE_OPTIONAL,   -1,-1 }
-							,{  "Route-Record",			RULE_OPTIONAL,   -1,-1 }
-						};
-			
-			CHECK_dict_new( DICT_COMMAND, &data , NULL, &cmd);
-			PARSE_loc_rules( rules, cmd );
-		}
-		
-		/* Abort-Session-Answer */
-		{
-			/*
-				The Abort-Session-Answer (ASA), indicated by the Command-Code set to
-				274 and the message flags' 'R' bit clear, is sent in response to the
-				ASR.  The Result-Code AVP MUST be present, and indicates the
-				disposition of the request.
-
-				If the session identified by Session-Id in the ASR was successfully
-				terminated, Result-Code is set to DIAMETER_SUCCESS.  If the session
-				is not currently active, Result-Code is set to
-				DIAMETER_UNKNOWN_SESSION_ID.  If the access device does not stop the
-				session for any other reason, Result-Code is set to
-				DIAMETER_UNABLE_TO_COMPLY.
-
-        				    Message Format
-
-				 <ASA>  ::= < Diameter Header: 274, PXY >
-        				    < Session-Id >
-        				    { Result-Code }
-        				    { Origin-Host }
-        				    { Origin-Realm }
-        				    [ User-Name ]
-        				    [ Origin-State-Id ]
-        				    [ Error-Message ]
-        				    [ Error-Reporting-Host ]
-        				    [ Failed-AVP ]
-        				  * [ Redirect-Host ]
-        				    [ Redirect-Host-Usage ]
-        				    [ Redirect-Max-Cache-Time ]
-        				  * [ Proxy-Info ]
-        				  * [ AVP ]
-			*/
-			struct dict_object * cmd;
-			struct dict_cmd_data data = { 
-					274, 					/* Code */
-					#if CC_ABORT_SESSION != 274
-					#error "CC_ABORT_SESSION definition mismatch"
-					#endif
-					"Abort-Session-Answer", 		/* Name */
-					CMD_FLAG_REQUEST | CMD_FLAG_PROXIABLE,	/* Fixed flags */
-							   CMD_FLAG_PROXIABLE	/* Fixed flag values */
-					};
-			struct local_rules_definition rules[] = 
-						{ 	 {  "Session-Id", 			RULE_FIXED_HEAD, -1, 1 }
- 							,{  "Result-Code", 			RULE_REQUIRED,   -1, 1 }
-						 	,{  "Origin-Host", 			RULE_REQUIRED,   -1, 1 }
-							,{  "Origin-Realm",			RULE_REQUIRED,   -1, 1 }
-							,{  "User-Name",			RULE_OPTIONAL,   -1, 1 }
-							,{  "Origin-State-Id",			RULE_OPTIONAL,   -1, 1 }
-							,{  "Error-Message",			RULE_OPTIONAL,   -1, 1 }
-							,{  "Error-Reporting-Host",		RULE_OPTIONAL,   -1, 1 }
-							,{  "Failed-AVP",			RULE_OPTIONAL,   -1, 1 }
-							,{  "Redirect-Host",			RULE_OPTIONAL,   -1,-1 }
-							,{  "Redirect-Host-Usage",		RULE_OPTIONAL,   -1, 1 }
-							,{  "Redirect-Max-Cache-Time",		RULE_OPTIONAL,   -1, 1 }
-							,{  "Proxy-Info",			RULE_OPTIONAL,   -1,-1 }
-						};
-			
-			CHECK_dict_new( DICT_COMMAND, &data , NULL, &cmd);
-			PARSE_loc_rules( rules, cmd );
-		}
-		
-		/* Accounting-Request */
-		{
-			/*
-				The Accounting-Request (ACR) command, indicated by the Command-Code
-				field set to 271 and the Command Flags' 'R' bit set, is sent by a
-				Diameter node, acting as a client, in order to exchange accounting
-				information with a peer.
-
-				One of Acct-Application-Id and Vendor-Specific-Application-Id AVPs
-				MUST be present.  If the Vendor-Specific-Application-Id grouped AVP
-				is present, it MUST include an Acct-Application-Id AVP.
-
-				The AVP listed below SHOULD include service specific accounting AVPs,
-				as described in Section 9.3.
-
-
-				Message Format
-
-				 <ACR> ::= < Diameter Header: 271, REQ, PXY >
-        				   < Session-Id >
-        				   { Origin-Host }
-        				   { Origin-Realm }
-        				   { Destination-Realm }
-        				   { Accounting-Record-Type }
-        				   { Accounting-Record-Number }
-        				   [ Acct-Application-Id ]
-        				   [ Vendor-Specific-Application-Id ]
-        				   [ User-Name ]
-        				   [ Destination-Host ]
-        				   [ Accounting-Sub-Session-Id ]
-        				   [ Acct-Session-Id ]
-        				   [ Acct-Multi-Session-Id ]
-        				   [ Acct-Interim-Interval ]
-        				   [ Accounting-Realtime-Required ]
-        				   [ Origin-State-Id ]
-        				   [ Event-Timestamp ]
-        				 * [ Proxy-Info ]
-        				 * [ Route-Record ]
-        				 * [ AVP ]
-			*/
-			struct dict_object * cmd;
-			struct dict_cmd_data data = { 
-					271, 					/* Code */
-					#if CC_ACCOUNTING != 271
-					#error "CC_ACCOUNTING definition mismatch"
-					#endif
-					"Accounting-Request", 			/* Name */
-					CMD_FLAG_REQUEST | CMD_FLAG_PROXIABLE | CMD_FLAG_ERROR,	/* Fixed flags */
-					CMD_FLAG_REQUEST | CMD_FLAG_PROXIABLE	/* Fixed flag values */
-					};
-			struct local_rules_definition rules[] = 
-						{ 	 {  "Session-Id", 			RULE_FIXED_HEAD, -1, 1 }
-						 	,{  "Origin-Host", 			RULE_REQUIRED,   -1, 1 }
-							,{  "Origin-Realm",			RULE_REQUIRED,   -1, 1 }
-							,{  "Destination-Realm",		RULE_REQUIRED,   -1, 1 }
-							,{  "Accounting-Record-Type",		RULE_REQUIRED,   -1, 1 }
-							,{  "Accounting-Record-Number",		RULE_REQUIRED,   -1, 1 }
-							,{  "Acct-Application-Id",		RULE_OPTIONAL,   -1, 1 }
-							,{  "Vendor-Specific-Application-Id",	RULE_OPTIONAL,   -1, 1 }
-							,{  "User-Name",			RULE_OPTIONAL,   -1, 1 }
-							,{  "Destination-Host",			RULE_OPTIONAL,   -1, 1 }
-							,{  "Accounting-Sub-Session-Id",	RULE_OPTIONAL,   -1, 1 }
-							,{  "Acct-Session-Id",			RULE_OPTIONAL,   -1, 1 }
-							,{  "Acct-Multi-Session-Id",		RULE_OPTIONAL,   -1, 1 }
-							,{  "Acct-Interim-Interval",		RULE_OPTIONAL,   -1, 1 }
-							,{  "Accounting-Realtime-Required",	RULE_OPTIONAL,   -1, 1 }
-							,{  "Origin-State-Id",			RULE_OPTIONAL,   -1, 1 }
-							,{  "Event-Timestamp",			RULE_OPTIONAL,   -1, 1 }
-							,{  "Proxy-Info",			RULE_OPTIONAL,   -1,-1 }
-							,{  "Route-Record",			RULE_OPTIONAL,   -1,-1 }
-						};
-			
-			CHECK_dict_new( DICT_COMMAND, &data , NULL, &cmd);
-			PARSE_loc_rules( rules, cmd );
-		}
-		
-		/* Accounting-Answer */
-		{
-			/*
-				The Accounting-Answer (ACA) command, indicated by the Command-Code
-				field set to 271 and the Command Flags' 'R' bit cleared, is used to
-				acknowledge an Accounting-Request command.  The Accounting-Answer
-				command contains the same Session-Id as the corresponding request.
-
-				Only the target Diameter Server, known as the home Diameter Server,
-				SHOULD respond with the Accounting-Answer command.
-
-				One of Acct-Application-Id and Vendor-Specific-Application-Id AVPs
-				MUST be present.  If the Vendor-Specific-Application-Id grouped AVP
-				is present, it MUST contain an Acct-Application-Id AVP.
-
-				The AVP listed below SHOULD include service specific accounting AVPs,
-				as described in Section 9.3.
-
-
-				Message Format
-
-				 <ACA> ::= < Diameter Header: 271, PXY >
-        				   < Session-Id >
-        				   { Result-Code }
-        				   { Origin-Host }
-        				   { Origin-Realm }
-        				   { Accounting-Record-Type }
-        				   { Accounting-Record-Number }
-        				   [ Acct-Application-Id ]
-        				   [ Vendor-Specific-Application-Id ]
-        				   [ User-Name ]
-        				   [ Accounting-Sub-Session-Id ]
-        				   [ Acct-Session-Id ]
-        				   [ Acct-Multi-Session-Id ]
-        				   [ Error-Message ]
-        				   [ Error-Reporting-Host ]
-        				   [ Failed-AVP ]
-        				   [ Acct-Interim-Interval ]
-        				   [ Accounting-Realtime-Required ]
-        				   [ Origin-State-Id ]
-        				   [ Event-Timestamp ]
-        				 * [ Proxy-Info ]
-        				 * [ AVP ]
-			*/
-			struct dict_object * cmd;
-			struct dict_cmd_data data = { 
-					271, 					/* Code */
-					#if CC_ACCOUNTING != 271
-					#error "CC_ACCOUNTING definition mismatch"
-					#endif
-					"Accounting-Answer", 			/* Name */
-					CMD_FLAG_REQUEST | CMD_FLAG_PROXIABLE,	/* Fixed flags */
-							   CMD_FLAG_PROXIABLE	/* Fixed flag values */
-					};
-			struct local_rules_definition rules[] = 
-						{ 	 {  "Session-Id", 			RULE_FIXED_HEAD, -1, 1 }
- 							,{  "Result-Code", 			RULE_REQUIRED,   -1, 1 }
-						 	,{  "Origin-Host", 			RULE_REQUIRED,   -1, 1 }
-							,{  "Origin-Realm",			RULE_REQUIRED,   -1, 1 }
-							,{  "Accounting-Record-Type",		RULE_REQUIRED,   -1, 1 }
-							,{  "Accounting-Record-Number",		RULE_REQUIRED,   -1, 1 }
-							,{  "Acct-Application-Id",		RULE_OPTIONAL,   -1, 1 }
-							,{  "Vendor-Specific-Application-Id",	RULE_OPTIONAL,   -1, 1 }
-							,{  "User-Name",			RULE_OPTIONAL,   -1, 1 }
-							,{  "Accounting-Sub-Session-Id",	RULE_OPTIONAL,   -1, 1 }
-							,{  "Acct-Session-Id",			RULE_OPTIONAL,   -1, 1 }
-							,{  "Acct-Multi-Session-Id",		RULE_OPTIONAL,   -1, 1 }
-							,{  "Error-Message",			RULE_OPTIONAL,   -1, 1 }
-							,{  "Error-Reporting-Host",		RULE_OPTIONAL,   -1, 1 }
-							,{  "Failed-AVP",			RULE_OPTIONAL,   -1, 1 }
-							,{  "Acct-Interim-Interval",		RULE_OPTIONAL,   -1, 1 }
-							,{  "Accounting-Realtime-Required",	RULE_OPTIONAL,   -1, 1 }
-							,{  "Origin-State-Id",			RULE_OPTIONAL,   -1, 1 }
-							,{  "Event-Timestamp",			RULE_OPTIONAL,   -1, 1 }
-							,{  "Proxy-Info",			RULE_OPTIONAL,   -1,-1 }
-						};
-			
-			CHECK_dict_new( DICT_COMMAND, &data , NULL, &cmd);
-			PARSE_loc_rules( rules, cmd );
-		}
-	}
-
-	return 0;
-}
Index: eeDiameter/endpoints.c
===================================================================
--- freeDiameter/endpoints.c	(revision 640)
+++ 	(revision )
@@ -1,361 +1,0 @@
-/*********************************************************************************************************
-* Software License Agreement (BSD License)                                                               *
-* Author: Sebastien Decugis <sdecugis@nict.go.jp>							 *
-*													 *
-* Copyright (c) 2010, 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 "fD.h"
-
-
-/* Add an endpoint information in a list */
-int fd_ep_add_merge( struct fd_list * list, sSA * sa, socklen_t sl, uint32_t flags )
-{
-	struct fd_endpoint * ep;
-	struct fd_list * li;
-	union {
-		sSA * sa;
-		sSA4 *sin;
-		sSA6 *sin6;
-	} ptr;
-	in_port_t * port;
-	int cmp = -1;
-	
-	TRACE_ENTRY("%p %p %u %x", list, sa, sl, flags);
-	CHECK_PARAMS( list && sa && (sl <= sizeof(sSS)) );
-	
-	if (list->next == NULL) {
-		/* the list is not initialized yet, do it */
-		fd_list_init(list, NULL);
-	}
-	
-	if (TRACE_BOOL(ANNOYING + 1)) {
-		TRACE_DEBUG(ANNOYING, "  DEBUG:fd_ep_add_merge  Current list:");
-		fd_ep_dump( 4, list );
-		TRACE_DEBUG(ANNOYING, "  DEBUG:fd_ep_add_merge  Adding:");
-		fd_log_debug("    ");
-		sSA_DUMP_NODE_SERV( sa, NI_NUMERICHOST | NI_NUMERICSERV );
-		fd_log_debug(" {%s%s%s%s}\n", 
-				(flags & EP_FL_CONF) 	? "C" : "-",
-				(flags & EP_FL_DISC)	    ? "D" : "-",
-				(flags & EP_FL_ADV)	    ? "A" : "-",
-				(flags & EP_FL_LL)	    ? "L" : "-",
-				(flags & EP_FL_PRIMARY)     ? "P" : "-");
-	}
-	ptr.sa = sa;
-	
-	/* Filter out a bunch of invalid addresses */
-	switch (sa->sa_family) {
-		case AF_INET:
-			if (! (flags & EP_ACCEPTALL)) {
-				if (IN_IS_ADDR_UNSPECIFIED(&ptr.sin->sin_addr) 
-				 || IN_IS_ADDR_LOOPBACK(&ptr.sin->sin_addr)
-				    /* the next one filters both EXPERIMENTAL, BADCLASS and MULTICAST. */
-				 || ((ntohl(ptr.sin->sin_addr.s_addr) & 0xe0000000) == 0xe0000000)
-				 || (ptr.sin->sin_addr.s_addr == INADDR_BROADCAST)) {
-					if (TRACE_BOOL(ANNOYING + 1)) {
-						TRACE_DEBUG(ANNOYING, "  DEBUG:fd_ep_add_merge  Address was ignored, not added.");
-					}
-					return 0;
-				}
-			}
-			port = &ptr.sin->sin_port;
-			break;
-
-		case AF_INET6:
-			if (! (flags & EP_ACCEPTALL)) {
-				if (IN6_IS_ADDR_UNSPECIFIED(&ptr.sin6->sin6_addr) 
-				 || IN6_IS_ADDR_LOOPBACK(&ptr.sin6->sin6_addr)
-				 || IN6_IS_ADDR_MULTICAST(&ptr.sin6->sin6_addr)
-				 || IN6_IS_ADDR_LINKLOCAL(&ptr.sin6->sin6_addr)
-				 || IN6_IS_ADDR_SITELOCAL(&ptr.sin6->sin6_addr)) {
-					if (TRACE_BOOL(ANNOYING + 1)) {
-						TRACE_DEBUG(ANNOYING, "  DEBUG:fd_ep_add_merge  Address was ignored, not added.");
-					}
-					return 0;
-				}
-			}
-			port = &ptr.sin6->sin6_port;
-			break;
-
-		default:
-			if (TRACE_BOOL(ANNOYING + 1)) {
-				TRACE_DEBUG(ANNOYING, "  DEBUG:fd_ep_add_merge  Address family was unknown, not added.");
-			}
-			return 0;
-	}
-
-	/* remove the ACCEPTALL flag */
-	flags &= ~EP_ACCEPTALL;
-	
-	/* Search place in the list */
-	for (li = list->next; li != list; li = li->next) {
-		ep = (struct fd_endpoint *)li;
-		in_port_t * ep_port;
-		
-		/* First, compare the address family */
-		if (ep->sa.sa_family < sa->sa_family)
-			continue;
-		if (ep->sa.sa_family > sa->sa_family)
-			break;
-		
-		/* Then compare the address field */
-		switch (sa->sa_family) {
-			case AF_INET:
-				cmp = memcmp(&ep->sin.sin_addr, &ptr.sin->sin_addr, sizeof(struct in_addr));
-				ep_port = &ep->sin.sin_port;
-				break;
-			case AF_INET6:
-				cmp = memcmp(&ep->sin6.sin6_addr, &ptr.sin6->sin6_addr, sizeof(struct in6_addr));
-				ep_port = &ep->sin6.sin6_port;
-				break;
-			default:
-				ASSERT( 0 ); /* we got a different value previously in this same function */
-		}
-		if (cmp < 0)
-			continue;
-		if (cmp > 0)
-			break;
-		
-		/* Finally compare the port, only if not 0 */
-		if (*port == 0)
-			break;
-		if (*ep_port == 0) {
-			/* save the port information in the list, and break */
-			*ep_port = *port;
-			break;
-		}
-		if (*ep_port < *port)
-			continue;
-		if (*ep_port > *port)
-			cmp = 1;
-		break;
-	}
-	
-	if (cmp) {
-		/* new item to be added */
-		CHECK_MALLOC( ep = malloc(sizeof(struct fd_endpoint)) );
-		memset(ep, 0, sizeof(struct fd_endpoint));
-		fd_list_init(&ep->chain, NULL);
-		memcpy(&ep->ss, sa, sl);
-		
-		/* Insert in the list */
-		fd_list_insert_before(li, &ep->chain);
-	}
-	
-	/* Merge the flags */
-	ep->flags |= flags;
-	
-	if (TRACE_BOOL(ANNOYING + 1)) {
-		TRACE_DEBUG(ANNOYING, "  DEBUG:fd_ep_add_merge  New list:");
-		fd_ep_dump( 4, list );
-	}
-	return 0;
-}
-
-/* Delete endpoints that do not have a matching flag from a list (0: delete all endpoints) */
-int fd_ep_filter( struct fd_list * list, uint32_t flags )
-{
-	struct fd_list * li;
-	
-	TRACE_ENTRY("%p %x", list, flags);
-	CHECK_PARAMS(list);
-	
-	if (TRACE_BOOL(ANNOYING + 1)) {
-		TRACE_DEBUG(ANNOYING, "  DEBUG:fd_ep_filter  Filter this list for flags %x:", flags);
-		fd_ep_dump( 4, list );
-	}
-	for (li = list->next; li != list; li = li->next) {
-		struct fd_endpoint * ep = (struct fd_endpoint *)li;
-		
-		if (! (ep->flags & flags)) {
-			li = li->prev;
-			fd_list_unlink(&ep->chain);
-			free(ep);
-		}
-	}
-	
-	if (TRACE_BOOL(ANNOYING + 1)) {
-		TRACE_DEBUG(ANNOYING, "  DEBUG:fd_ep_filter  Resulting list:");
-		fd_ep_dump( 4, list );
-	}
-	return 0;
-}
-
-/* Keep only endpoints of the same family as af */
-int fd_ep_filter_family( struct fd_list * list, int af )
-{
-	struct fd_list * li;
-	
-	TRACE_ENTRY("%p %d", list, af);
-	CHECK_PARAMS(list);
-	
-	if (TRACE_BOOL(ANNOYING + 1)) {
-		TRACE_DEBUG(ANNOYING, "  DEBUG:fd_ep_filter_family  Filter this list for family %d:", af);
-		fd_ep_dump( 4, list );
-	}
-	for (li = list->next; li != list; li = li->next) {
-		struct fd_endpoint * ep = (struct fd_endpoint *)li;
-		
-		if (ep->sa.sa_family != af) {
-			li = li->prev;
-			fd_list_unlink(&ep->chain);
-			free(ep);
-		}
-	}
-	
-	if (TRACE_BOOL(ANNOYING + 1)) {
-		TRACE_DEBUG(ANNOYING, "  DEBUG:fd_ep_filter_family  Resulting list:");
-		fd_ep_dump( 4, list );
-	}
-	return 0;
-}
-
-/* Remove any endpoint from the exclude list in the list */
-int fd_ep_filter_list( struct fd_list * list, struct fd_list * exclude_list )
-{
-	struct fd_list * li_out, *li_ex, *li;
-	struct fd_endpoint * out, * ex;
-	
-	TRACE_ENTRY("%p %p", list, exclude_list);
-	CHECK_PARAMS(list && exclude_list);
-	
-	/* initialize. Both lists are ordered */
-	li_out = list->next;
-	li_ex = exclude_list->next;
-	
-	if (TRACE_BOOL(ANNOYING + 1)) {
-		TRACE_DEBUG(ANNOYING, "  DEBUG:fd_ep_filter_list  Filter this list ");
-		fd_ep_dump( 4, list );
-		TRACE_DEBUG(ANNOYING, "  DEBUG:fd_ep_filter_list  Removing from list");
-		fd_ep_dump( 6, exclude_list );
-	}
-	/* Now browse both lists in parallel */
-	while ((li_out != list) && (li_ex != exclude_list)) {
-		int cmp;
-
-		out = (struct fd_endpoint *)li_out;
-		ex = (struct fd_endpoint *)li_ex;
-
-		/* Compare the next elements families */
-		if (out->sa.sa_family < ex->sa.sa_family) {
-			li_out = li_out->next;
-			continue;
-		}
-		if (out->sa.sa_family > ex->sa.sa_family) {
-			li_ex = li_ex->next;
-			continue;
-		}
-
-		/* Then compare the address fields */
-		switch (out->sa.sa_family) {
-			case AF_INET:
-				cmp = memcmp(&out->sin.sin_addr, &ex->sin.sin_addr, sizeof(struct in_addr));
-				break;
-			case AF_INET6:
-				cmp = memcmp(&out->sin6.sin6_addr, &ex->sin6.sin6_addr, sizeof(struct in6_addr));
-				break;
-			default:
-				/* Filter this out */
-				cmp = 0;
-		}
-		if (cmp < 0) {
-			li_out = li_out->next;
-			continue;
-		}
-		if (cmp > 0) {
-			li_ex = li_ex->next;
-			continue;
-		}
-	
-		/* We remove this element then loop */
-		li = li_out;
-		li_out = li->next;
-		fd_list_unlink(li);
-		free(li);
-	}
-	
-	if (TRACE_BOOL(ANNOYING + 1)) {
-		TRACE_DEBUG(ANNOYING, "  DEBUG:fd_ep_filter_list  Resulting list:");
-		fd_ep_dump( 4, list );
-	}
-	return 0;
-
-}
-
-
-/* Reset the given flag(s) from all items in the list */
-int fd_ep_clearflags( struct fd_list * list, uint32_t flags )
-{
-	struct fd_list * li;
-	
-	TRACE_ENTRY("%p %x", list, flags);
-	CHECK_PARAMS(list);
-	
-	for (li = list->next; li != list; li = li->next) {
-		struct fd_endpoint * ep = (struct fd_endpoint *)li;
-		ep->flags &= ~flags;
-		if (ep->flags == 0) {
-			li = li->prev;
-			fd_list_unlink(&ep->chain);
-			free(ep);
-		}
-	}
-	
-	return 0;
-}
-
-void fd_ep_dump_one( char * prefix, struct fd_endpoint * ep, char * suffix )
-{
-	if (prefix)
-		fd_log_debug("%s", prefix);
-	
-	sSA_DUMP_NODE_SERV( &ep->sa, NI_NUMERICHOST | NI_NUMERICSERV );
-	fd_log_debug(" {%s%s%s%s}", 
-			(ep->flags & EP_FL_CONF) 	? "C" : "-",
-			(ep->flags & EP_FL_DISC) 	? "D" : "-",
-			(ep->flags & EP_FL_ADV) 	? "A" : "-",
-			(ep->flags & EP_FL_LL) 		? "L" : "-",
-			(ep->flags & EP_FL_PRIMARY) 	? "P" : "-");
-	if (suffix)
-		fd_log_debug("%s", suffix);
-}
-
-void fd_ep_dump( int indent, struct fd_list * eps )
-{
-	struct fd_list * li;
-	for (li = eps->next; li != eps; li = li->next) {
-		struct fd_endpoint * ep = (struct fd_endpoint *)li;
-		fd_log_debug("%*s", indent, "");
-		fd_ep_dump_one( NULL, ep, "\n" );
-	}
-}
-	
Index: eeDiameter/events.c
===================================================================
--- freeDiameter/events.c	(revision 82)
+++ 	(revision )
@@ -1,118 +1,0 @@
-/*********************************************************************************************************
-* Software License Agreement (BSD License)                                                               *
-* Author: Sebastien Decugis <sdecugis@nict.go.jp>							 *
-*													 *
-* Copyright (c) 2009, WIDE Project and NICT								 *
-* All rights reserved.											 *
-* 													 *
-* Redistribution and use of this software in source and binary forms, with or without modification, are  *
-* permitted provided that the following conditions are met:						 *
-* 													 *
-* * Redistributions of source code must retain the above 						 *
-*   copyright notice, this list of conditions and the 							 *
-*   following disclaimer.										 *
-*    													 *
-* * Redistributions in binary form must reproduce the above 						 *
-*   copyright notice, this list of conditions and the 							 *
-*   following disclaimer in the documentation and/or other						 *
-*   materials provided with the distribution.								 *
-* 													 *
-* * Neither the name of the WIDE Project or NICT nor the 						 *
-*   names of its contributors may be used to endorse or 						 *
-*   promote products derived from this software without 						 *
-*   specific prior written permission of WIDE Project and 						 *
-*   NICT.												 *
-* 													 *
-* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED *
-* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
-* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR *
-* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 	 *
-* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 	 *
-* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR *
-* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF   *
-* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.								 *
-*********************************************************************************************************/
-
-#include "fD.h"
-
-int fd_event_send(struct fifo *queue, int code, size_t datasz, void * data)
-{
-	struct fd_event * ev;
-	CHECK_MALLOC( ev = malloc(sizeof(struct fd_event)) );
-	ev->code = code;
-	ev->size = datasz;
-	ev->data = data;
-	CHECK_FCT( fd_fifo_post(queue, &ev) );
-	return 0;
-}
-
-int fd_event_get(struct fifo *queue, int *code, size_t *datasz, void ** data)
-{
-	struct fd_event * ev;
-	CHECK_FCT( fd_fifo_get(queue, &ev) );
-	if (code)
-		*code = ev->code;
-	if (datasz)
-		*datasz = ev->size;
-	if (data)
-		*data = ev->data;
-	free(ev);
-	return 0;
-}
-
-int fd_event_timedget(struct fifo *queue, struct timespec * timeout, int timeoutcode, int *code, size_t *datasz, void ** data)
-{
-	struct fd_event * ev;
-	int ret = 0;
-	ret = fd_fifo_timedget(queue, &ev, timeout);
-	if (ret == ETIMEDOUT) {
-		if (code)
-			*code = timeoutcode;
-		if (datasz)
-			*datasz = 0;
-		if (data)
-			*data = NULL;
-	} else {
-		CHECK_FCT( ret );
-		if (code)
-			*code = ev->code;
-		if (datasz)
-			*datasz = ev->size;
-		if (data)
-			*data = ev->data;
-		free(ev);
-	}
-	return 0;
-}
-
-void fd_event_destroy(struct fifo **queue, void (*free_cb)(void * data))
-{
-	struct fd_event * ev;
-	/* Purge all events, and free the associated data if any */
-	while (fd_fifo_tryget( *queue, &ev ) == 0) {
-		(*free_cb)(ev->data);
-		free(ev);
-	}
-	CHECK_FCT_DO( fd_fifo_del(queue), /* continue */ );
-	return ;
-} 
-
-const char * fd_ev_str(int event)
-{
-	switch (event) {
-	#define case_str( _val )\
-		case _val : return #_val
-		case_str(FDEV_TERMINATE);
-		case_str(FDEV_DUMP_DICT);
-		case_str(FDEV_DUMP_EXT);
-		case_str(FDEV_DUMP_SERV);
-		case_str(FDEV_DUMP_QUEUES);
-		case_str(FDEV_DUMP_CONFIG);
-		case_str(FDEV_DUMP_PEERS);
-		
-		default:
-			TRACE_DEBUG(FULL, "Unknown event : %d", event);
-			return "Unknown event";
-	}
-}
-
Index: eeDiameter/extensions.c
===================================================================
--- freeDiameter/extensions.c	(revision 287)
+++ 	(revision )
@@ -1,191 +1,0 @@
-/*********************************************************************************************************
-* Software License Agreement (BSD License)                                                               *
-* Author: Sebastien Decugis <sdecugis@nict.go.jp>							 *
-*													 *
-* Copyright (c) 2010, 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 "fD.h"
-
-#include <dlfcn.h>	/* We may use libtool's <ltdl.h> later for better portability.... */
-
-/* plugins management */
-
-/* List of extensions to load, from the configuration parsing */
-struct fd_ext_info {
-	struct fd_list	chain;		/* link in the list */
-	char 		*filename;	/* extension filename. must be a dynamic library with fd_ext_init symbol. */
-	char 		*conffile;	/* optional configuration file name for the extension */
-	void 		*handler;	/* object returned by dlopen() */
-	void		(*fini)(void);	/* optional address of the fd_ext_fini callback */
-};
-
-/* list of extensions */
-static struct fd_list ext_list = FD_LIST_INITIALIZER(ext_list);
-
-/* Add new extension */
-int fd_ext_add( char * filename, char * conffile )
-{
-	struct fd_ext_info * new;
-	
-	TRACE_ENTRY("%p(%s) %p(%s)", filename, filename?filename:"", conffile, conffile?conffile:"");
-	
-	/* Check the filename is valid */
-	CHECK_PARAMS( filename );
-	
-	/* Create a new object in the list */
-	CHECK_MALLOC(  new = malloc( sizeof(struct fd_ext_info) )  );
-	memset(new, 0, sizeof(struct fd_ext_info));
-	fd_list_init(&new->chain, NULL);
-	new->filename = filename;
-	new->conffile = conffile;
-	fd_list_insert_before( &ext_list, &new->chain );
-	TRACE_DEBUG (FULL, "Extension %s added to the list.", filename);
-	return 0;
-}
-
-/* Dump the list */
-void fd_ext_dump(void)
-{
-	struct fd_list * li;
-	
-	fd_log_debug("Dumping extensions list :\n");
-	
-	for (li = ext_list.next; li != &ext_list; li = li->next)
-	{
-		struct fd_ext_info * ext = (struct fd_ext_info *)li;
-		fd_log_debug(" - '%s'[%s] is %sloaded\n", ext->filename, ext->conffile?:"no conf", ext->handler ? "" : "not ");
-	}
-}
-
-/* Load all extensions in the list */
-int fd_ext_load()
-{
-	int ret;
-	int (*fd_ext_init)(int, int, char *) = NULL;
-	struct fd_list * li;
-	
-	TRACE_ENTRY();
-	
-	/* Loop on all extensions */
-	for (li = ext_list.next; li != &ext_list; li = li->next)
-	{
-		struct fd_ext_info * ext = (struct fd_ext_info *)li;
-		TRACE_DEBUG (INFO, "Loading : %s", ext->filename);
-		
-		/* Load the extension */
-#ifndef DEBUG
-		ext->handler = dlopen(ext->filename, RTLD_LAZY | RTLD_GLOBAL);
-#else /* DEBUG */
-		/* We resolve immediatly so it's easier to find problems in ABI */
-		ext->handler = dlopen(ext->filename, RTLD_NOW | RTLD_GLOBAL);
-#endif /* DEBUG */
-		if (ext->handler == NULL) {
-			/* An error occured */
-			TRACE_DEBUG( NONE, "Loading of extension %s failed:\n %s\n", ext->filename, dlerror());
-			return EINVAL;
-		}
-		
-		/* Resolve the entry point of the extension */
-		fd_ext_init = ( int (*) (int, int, char *) )dlsym( ext->handler, "fd_ext_init" );
-		
-		if (fd_ext_init == NULL) {
-			/* An error occured */
-			TRACE_DEBUG( NONE, "Unable to resolve symbol 'fd_ext_init' for extension %s:\n %s\n", ext->filename, dlerror());
-			return EINVAL;
-		}
-		
-		/* Resolve the exit point of the extension, which is optional for extensions */
-		ext->fini = ( void (*) (void) )dlsym( ext->handler, "fd_ext_fini" );
-		
-		if (ext->fini == NULL) {
-			/* Not provided */
-			TRACE_DEBUG (FULL, "Extension [%s] has no fd_ext_fini function.", ext->filename);
-		} else {
-			/* Provided */
-			TRACE_DEBUG (FULL, "Extension [%s] fd_ext_fini has been resolved successfully.", ext->filename);
-		}
-		
-		/* Now call the entry point to initialize the extension */
-		ret = (*fd_ext_init)( FD_PROJECT_VERSION_MAJOR, FD_PROJECT_VERSION_MINOR, ext->conffile );
-		if (ret != 0) {
-			/* The extension was unable to load cleanly */
-			TRACE_DEBUG( NONE, "Extension %s returned an error during initialization: %s\n", ext->filename, strerror(ret));
-			return ret;
-		}
-		
-		/* Proceed to the next extension */
-	}
-
-	TRACE_DEBUG (INFO, "All extensions loaded.");
-	
-	/* We have finished. */
-	return 0;
-}
-
-/* Now unload the extensions and free the memory */
-int fd_ext_term( void ) 
-{
-	TRACE_ENTRY();
-	
-	/* Loop on all extensions, in FIFO order */
-	while (!FD_IS_LIST_EMPTY(&ext_list))
-	{
-		struct fd_list * li = ext_list.next;
-		struct fd_ext_info * ext = (struct fd_ext_info *)li;
-	
-		/* Unlink this element from the list */
-		fd_list_unlink(li);
-		
-		/* Call the exit point of the extension, if it was resolved */
-		if (ext->fini != NULL) {
-			TRACE_DEBUG (FULL, "Calling [%s]->fd_ext_fini function.", ext->filename);
-			(*ext->fini)();
-		}
-		
-		/* Now unload the extension */
-		if (ext->handler) {
-			TRACE_DEBUG (FULL, "Unloading %s", ext->filename);
-			if ( dlclose(ext->handler) != 0 ) {
-				TRACE_DEBUG (INFO, "Unloading [%s] failed : %s\n", ext->filename, dlerror());
-			}
-		}
-		
-		/* Free the object and continue */
-		free(ext->filename);
-		free(ext->conffile);
-		free(ext);
-	}
-	
-	/* We always return 0 since we would not handle an error anyway... */
-	return 0;
-}
-
Index: eeDiameter/fD.h
===================================================================
--- freeDiameter/fD.h	(revision 649)
+++ 	(revision )
@@ -1,347 +1,0 @@
-/*********************************************************************************************************
-* Software License Agreement (BSD License)                                                               *
-* Author: Sebastien Decugis <sdecugis@nict.go.jp>							 *
-*													 *
-* Copyright (c) 2010, 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 definitions for internal use in the freeDiameter daemon */
-
-#ifndef _FD_H
-#define _FD_H
-
-#include <freeDiameter/freeDiameter-host.h>
-#include <freeDiameter/freeDiameter.h>
-
-#ifndef FD_DEFAULT_CONF_FILENAME
-#define FD_DEFAULT_CONF_FILENAME "freeDiameter.conf"
-#endif /* FD_DEFAULT_CONF_FILENAME */
-
-#ifdef DISABLE_SCTP
-#undef IPPROTO_SCTP
-#define IPPROTO_SCTP	(2 = 4) /* some compilation error to spot the references */
-#endif /* DISABLE_SCTP */
-
-/* Timeout for establishing a connection */
-#ifndef CNX_TIMEOUT
-#define  CNX_TIMEOUT	10	/* in seconds */
-#endif /* CNX_TIMEOUT */
-
-/* Timeout for receiving a CER after incoming connection is established */
-#ifndef INCNX_TIMEOUT
-#define  INCNX_TIMEOUT	 20	/* in seconds */
-#endif /* INCNX_TIMEOUT */
-
-/* Timeout for receiving a CEA after CER is sent */
-#ifndef CEA_TIMEOUT
-#define  CEA_TIMEOUT	10	/* in seconds */
-#endif /* CEA_TIMEOUT */
-
-/* The timeout value to wait for answer to a DPR */
-#ifndef DPR_TIMEOUT
-#define DPR_TIMEOUT 	15	/* in seconds */
-#endif /* DPR_TIMEOUT */
-
-/* The Vendor-Id to advertise in CER/CEA */
-#ifndef MY_VENDOR_ID
-#define MY_VENDOR_ID	0 	/* Reserved value to tell it must be ignored */
-#endif /* MY_VENDOR_ID */
-
-
-
-/* Configuration */
-int fd_conf_init();
-int fd_conf_deinit();
-void fd_conf_dump();
-int fd_conf_parse();
-int fddparse(struct fd_config * conf); /* yacc generated */
-
-/* Extensions */
-int fd_ext_add( char * filename, char * conffile );
-int fd_ext_load();
-void fd_ext_dump(void);
-int fd_ext_term(void);
-
-/* Messages */
-int fd_msg_init(void);
-struct dict_object * fd_dict_avp_OSI; /* Origin-State-Id */
-struct dict_object * fd_dict_cmd_CER; /* Capabilities-Exchange-Request */
-struct dict_object * fd_dict_cmd_DWR; /* Device-Watchdog-Request */
-struct dict_object * fd_dict_avp_DC;  /* Disconnect-Cause */
-struct dict_object * fd_dict_cmd_DPR; /* Disconnect-Peer-Request */
-
-/* Global message queues */
-extern struct fifo * fd_g_incoming; /* all messages received from other peers, except local messages (CER, ...) */
-extern struct fifo * fd_g_outgoing; /* messages to be sent to other peers on the network following routing procedure */
-extern struct fifo * fd_g_local; /* messages to be handled to local extensions */
-/* Message queues */
-int fd_queues_init(void);
-int fd_queues_fini(struct fifo ** queue);
-
-/* Create all the dictionary objects defined in the Diameter base RFC. */
-int fd_dict_base_protocol(struct dictionary * dict);
-
-/* Routing */
-int fd_rtdisp_init(void);
-int fd_rtdisp_cleanstop(void);
-int fd_rtdisp_fini(void);
-int fd_rtdisp_cleanup(void);
-
-/* Sentinel for the sent requests list */
-struct sr_list {
-	struct fd_list 	srs; /* requests ordered by hop-by-hop id */
-	struct fd_list  exp; /* requests that have a timeout set, ordered by timeout */
-	pthread_mutex_t	mtx; /* mutex to protect these lists */
-	pthread_cond_t  cnd; /* cond var used by the thread that handles timeouts */
-	pthread_t       thr; /* the thread that handles timeouts (and calls the anscb) */
-};
-
-/* Peers */
-struct fd_peer { /* The "real" definition of the peer structure */
-	
-	/* The public data */
-	struct peer_hdr	 p_hdr;
-	
-	/* Eye catcher, EYEC_PEER */
-	int		 p_eyec;
-	#define EYEC_PEER	0x373C9336
-	
-	/* Origin of this peer object, for debug */
-	char		*p_dbgorig;
-	
-	/* Chaining in peers sublists */
-	struct fd_list	 p_actives;	/* list of peers in the STATE_OPEN state -- used by routing */
-	struct fd_list	 p_expiry; 	/* list of expiring peers, ordered by their timeout value */
-	struct timespec	 p_exp_timer;	/* Timestamp where the peer will expire; updated each time activity is seen on the peer (except DW) */
-	
-	/* Some flags influencing the peer state machine */
-	struct {
-		unsigned pf_responder	: 1;	/* The peer has been created to handle incoming connection */
-		unsigned pf_delete	: 1;	/* Destroy the peer when the connection is terminated */
-		
-		unsigned pf_dw_pending 	: 1;	/* A DWR message was sent and not answered yet */
-		
-		unsigned pf_cnx_pb	: 1;	/* The peer was disconnected because of watchdogs; must exchange 3 watchdogs before putting back to normal */
-		unsigned pf_reopen_cnt	: 2;	/* remaining DW to be exchanged after re-established connection */
-		
-	}		 p_flags;
-	
-	/* The events queue, peer state machine thread, timer for states timeouts */
-	struct fifo	*p_events;	/* The mutex of this FIFO list protects also the state and timer information */
-	pthread_t	 p_psm;
-	struct timespec	 p_psm_timer;
-	
-	/* Outgoing message queue, and thread managing sending the messages */
-	struct fifo	*p_tosend;
-	pthread_t	 p_outthr;
-	
-	/* The next hop-by-hop id value for the link, only read & modified by p_outthr */
-	uint32_t	 p_hbh;
-	
-	/* Sent requests (for fallback), list of struct sentreq ordered by hbh */
-	struct sr_list	 p_sr;
-	
-	/* Data for transitional states before the peer is in OPEN state */
-	struct {
-		struct cnxctx * p_receiver;	/* Only used in case of election */
-		struct msg    * p_cer;		/* Only used in case of election */
-		
-		pthread_t	p_ini_thr;	/* Initiator thread for establishing a connection */
-		struct fd_list  p_connparams;	/* The list of connection attempts, see p_cnx.c */
-	};
-	
-	/* connection context: socket and related information */
-	struct cnxctx	*p_cnxctx;
-	
-	/* Callback for peer validation after the handshake */
-	int		(*p_cb2)(struct peer_info *);
-	
-	/* Callback on initial connection success / failure after the peer was added */
-	void 		(*p_cb)(struct peer_info *, void *);
-	void 		*p_cb_data;
-	
-};
-#define CHECK_PEER( _p ) \
-	(((_p) != NULL) && (((struct fd_peer *)(_p))->p_eyec == EYEC_PEER))
-
-/* Events codespace for struct fd_peer->p_events */
-enum {
-	/* Dump all info about this peer in the debug log */
-	 FDEVP_DUMP_ALL = 1500
-	
-	/* request to terminate this peer : disconnect, requeue all messages */
-	,FDEVP_TERMINATE
-	
-	/* A connection object has received a message. (data contains the buffer) */
-	,FDEVP_CNX_MSG_RECV
-			 
-	/* A connection object has encountered an error (disconnected). */
-	,FDEVP_CNX_ERROR
-	
-	/* Endpoints of a connection have been changed (multihomed SCTP). */
-	,FDEVP_CNX_EP_CHANGE
-	
-	/* The connection is being shutdown (SCTP notification). */
-	,FDEVP_CNX_SHUTDOWN
-	
-	/* A new connection (with a CER) has been received */
-	,FDEVP_CNX_INCOMING
-	
-	/* A new connection has been established to the remote peer (event data is the cnxctx object) */
-	,FDEVP_CNX_ESTABLISHED
-	
-	/* A connection attempt (initiator side) has failed */
-	,FDEVP_CNX_FAILED
-	
-	/* The PSM state is expired */
-	,FDEVP_PSM_TIMEOUT
-	
-};
-#define CHECK_PEVENT( _e ) \
-	(((int)(_e) >= FDEVP_DUMP_ALL) && ((int)(_e) <= FDEVP_PSM_TIMEOUT))
-/* The following macro is actually called in p_psm.c -- another solution would be to declare it static inline */
-#define DECLARE_PEV_STR()				\
-const char * fd_pev_str(int event)			\
-{							\
-	switch (event) {				\
-		case_str(FDEVP_DUMP_ALL);		\
-		case_str(FDEVP_TERMINATE);		\
-		case_str(FDEVP_CNX_MSG_RECV);		\
-		case_str(FDEVP_CNX_ERROR);		\
-		case_str(FDEVP_CNX_EP_CHANGE);		\
-		case_str(FDEVP_CNX_INCOMING);		\
-		case_str(FDEVP_CNX_ESTABLISHED);	\
-		case_str(FDEVP_CNX_FAILED);		\
-		case_str(FDEVP_PSM_TIMEOUT);		\
-	}						\
-	TRACE_DEBUG(FULL, "Unknown event : %d", event);	\
-	return "Unknown event";				\
-}
-const char * fd_pev_str(int event);
-
-/* The data structure for FDEVP_CNX_INCOMING event */
-struct cnx_incoming {
-	struct msg	* cer;		/* the CER message received on this connection */
-	struct cnxctx	* cnx;		/* The connection context */
-	int  		  validate;	/* The peer is new, it must be validated (by an extension) or error CEA to be sent */
-};
-
-
-/* Functions */
-int  fd_peer_fini();
-void fd_peer_dump_list(int details);
-void fd_peer_dump(struct fd_peer * peer, int details);
-int  fd_peer_alloc(struct fd_peer ** ptr);
-int  fd_peer_free(struct fd_peer ** ptr);
-int fd_peer_handle_newCER( struct msg ** cer, struct cnxctx ** cnx );
-/* fd_peer_add declared in freeDiameter.h */
-int fd_peer_validate( struct fd_peer * peer );
-void fd_peer_failover_msg(struct fd_peer * peer);
-
-/* Peer expiry */
-int fd_p_expi_init(void);
-int fd_p_expi_fini(void);
-int fd_p_expi_update(struct fd_peer * peer );
-
-/* Peer state machine */
-int  fd_psm_start();
-int  fd_psm_begin(struct fd_peer * peer );
-int  fd_psm_terminate(struct fd_peer * peer, char * reason );
-void fd_psm_abord(struct fd_peer * peer );
-void fd_psm_next_timeout(struct fd_peer * peer, int add_random, int delay);
-int fd_psm_change_state(struct fd_peer * peer, int new_state);
-void fd_psm_cleanup(struct fd_peer * peer, int terminate);
-
-/* Peer out */
-int fd_out_send(struct msg ** msg, struct cnxctx * cnx, struct fd_peer * peer, uint32_t flags);
-int fd_out_start(struct fd_peer * peer);
-int fd_out_stop(struct fd_peer * peer);
-
-/* Initiating connections */
-int fd_p_cnx_init(struct fd_peer * peer);
-void fd_p_cnx_abort(struct fd_peer * peer, int cleanup_all);
-
-/* Peer sent requests cache */
-int fd_p_sr_store(struct sr_list * srlist, struct msg **req, uint32_t *hbhloc, uint32_t hbh_restore);
-int fd_p_sr_fetch(struct sr_list * srlist, uint32_t hbh, struct msg **req);
-int fd_p_sr_start(struct sr_list * srlist);
-int fd_p_sr_stop(struct sr_list * srlist);
-void fd_p_sr_failover(struct sr_list * srlist);
-
-/* Local Link messages (CER/CEA, DWR/DWA, DPR/DPA) */
-int fd_p_ce_msgrcv(struct msg ** msg, int req, struct fd_peer * peer);
-int fd_p_ce_handle_newCER(struct msg ** msg, struct fd_peer * peer, struct cnxctx ** cnx, int valid);
-int fd_p_ce_handle_newcnx(struct fd_peer * peer, struct cnxctx * initiator);
-int fd_p_ce_process_receiver(struct fd_peer * peer);
-void fd_p_ce_clear_cnx(struct fd_peer * peer, struct cnxctx ** cnx_kept);
-int fd_p_dw_handle(struct msg ** msg, int req, struct fd_peer * peer);
-int fd_p_dw_timeout(struct fd_peer * peer);
-int fd_p_dw_reopen(struct fd_peer * peer);
-int fd_p_dp_handle(struct msg ** msg, int req, struct fd_peer * peer);
-int fd_p_dp_initiate(struct fd_peer * peer, char * reason);
-
-/* Active peers -- routing process should only ever take the read lock, the write lock is managed by PSMs */
-extern struct fd_list fd_g_activ_peers;
-extern pthread_rwlock_t fd_g_activ_peers_rw; /* protect the list */
-
-
-/* Server sockets */
-void fd_servers_dump();
-int  fd_servers_start();
-int  fd_servers_stop();
-
-/* Connection contexts -- there are also definitions in cnxctx.h for the relevant files */
-struct cnxctx * fd_cnx_serv_tcp(uint16_t port, int family, struct fd_endpoint * ep);
-struct cnxctx * fd_cnx_serv_sctp(uint16_t port, struct fd_list * ep_list);
-int             fd_cnx_serv_listen(struct cnxctx * conn);
-struct cnxctx * fd_cnx_serv_accept(struct cnxctx * serv);
-struct cnxctx * fd_cnx_cli_connect_tcp(sSA * sa, socklen_t addrlen);
-struct cnxctx * fd_cnx_cli_connect_sctp(int no_ip6, uint16_t port, struct fd_list * list);
-int             fd_cnx_start_clear(struct cnxctx * conn, int loop);
-void		fd_cnx_sethostname(struct cnxctx * conn, char * hn);
-int             fd_cnx_handshake(struct cnxctx * conn, int mode, char * priority, void * alt_creds);
-char *          fd_cnx_getid(struct cnxctx * conn);
-int		fd_cnx_getproto(struct cnxctx * conn);
-int		fd_cnx_getTLS(struct cnxctx * conn);
-int             fd_cnx_getcred(struct cnxctx * conn, const gnutls_datum_t **cert_list, unsigned int *cert_list_size);
-int 		fd_cnx_get_local_eps(struct fd_list * list);
-int             fd_cnx_getremoteeps(struct cnxctx * conn, struct fd_list * eps);
-char *          fd_cnx_getremoteid(struct cnxctx * conn);
-int             fd_cnx_receive(struct cnxctx * conn, struct timespec * timeout, unsigned char **buf, size_t * len);
-int             fd_cnx_recv_setaltfifo(struct cnxctx * conn, struct fifo * alt_fifo); /* send FDEVP_CNX_MSG_RECV event to the fifo list */
-int             fd_cnx_send(struct cnxctx * conn, unsigned char * buf, size_t len, uint32_t flags);
-void            fd_cnx_destroy(struct cnxctx * conn);
-
-/* Flags for the fd_cnx_send function : */
-#define FD_CNX_ORDERED		(1 << 0)	/* All messages sent with this flag set will be delivered in the same order. No guarantee on other messages */
-#define FD_CNX_BROADCAST	(1 << 1)	/* The message is sent over all stream pairs, in case of SCTP. No effect on TCP */
-
-#endif /* _FD_H */
Index: eeDiameter/fdd.l
===================================================================
--- freeDiameter/fdd.l	(revision 578)
+++ 	(revision )
@@ -1,153 +1,0 @@
-/*********************************************************************************************************
-* Software License Agreement (BSD License)                                                               *
-* Author: Sebastien Decugis <sdecugis@nict.go.jp>							 *
-*													 *
-* Copyright (c) 2010, 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.								 *
-*********************************************************************************************************/
-
-/* Lex configuration parser.
- *
- * This file defines the token for parsing the daemon's configuration file
- * Note that each extension has a separate independant configuration file.
- *
- * Note : This module is NOT thread-safe. All processing must be done from one thread only.
- */
-%{
-/* Include the daemon's header files */
-#include "fD.h"
-/* Include yacc tokens definitions */
-#include "fdd.tab.h"
-
-/* Update the column information */
-#ifdef DEBUG_LEX
-#define YY_USER_ACTION { 						\
-	yylloc->first_column = yylloc->last_column + 1; 		\
-	yylloc->last_column = yylloc->first_column + yyleng - 1;	\
-	TRACE_DEBUG(FULL, 						\
-		"(%d:%d-%d:%d) matched rule %d, length=%d, txt='%s'\n",	\
-		yylloc->first_line, yylloc->first_column, 		\
-		yylloc->last_line, yylloc->last_column, 		\
-		yy_act, yyleng, yytext); 				\
-}
-#else /* DEBUG_LEX */
-#define YY_USER_ACTION { 						\
-	yylloc->first_column = yylloc->last_column + 1; 		\
-	yylloc->last_column = yylloc->first_column + yyleng - 1;	\
-}
-#endif
-
-/* %option noinput ? */
-#define YY_NO_INPUT
-%}
-
-%option bison-bridge bison-locations
-%option noyywrap
-%option nounput
-
-/* Quoted string. Multilines do not match. */
-qstring		\"[^\"\n]*\"
-
-%%
-
-<*>\n			{ 
-				/* Update the line count */
-				yylloc->first_line++; 
-				yylloc->last_line++; 
-				yylloc->last_column=0; 
-			} 
-
-<*>([[:space:]]{-}[\n])+	; /* Eat all spaces, not new lines */
-<*>#.*$			; /* Eat all comments */
-
-{qstring}		{
-				/* First copy the string without the quotes for use in the yacc parser */
-				CHECK_MALLOC_DO(	yylval->string = strdup(yytext+1), /* This allocates one useless tail char but... it's easier :D */ 
-							return LEX_ERROR  );/* on error, trig an error in yacc parser */
-
-				yylval->string[yyleng-2] = '\0';
-				
-				/* the yacc parser will check the string is valid */
-				return QSTRING;
-			}
-			
-[[:digit:]]+		{
-				/* Convert this to an integer value */
-				int ret = sscanf(yytext, "%i", &yylval->integer);
-				if (ret != 1) {
-					/* No matching: an error occurred */
-					fprintf(stderr, "Unable to convert the value '%s' to a valid number: %s\n", yytext, strerror(errno));
-					return LEX_ERROR; /* trig an error in yacc parser */
-					/* Maybe we could REJECT instead of failing here? */
-				}
-				return INTEGER;
-			}
-				
-	/* Full words tokens (keywords) */
-(?i:"Identity")		{ return IDENTITY;	}
-(?i:"Realm")		{ return REALM;   	}
-(?i:"Port")		{ return PORT;    	}
-(?i:"SecPort")		{ return SECPORT;  	}
-(?i:"No_IPv6")		{ return NOIP6;		}
-(?i:"No_IP")		{ return NOIP;		}
-(?i:"No_TCP")		{ return NOTCP;		}
-(?i:"No_SCTP")		{ return NOSCTP;	}
-(?i:"Prefer_TCP")	{ return PREFERTCP;	}
-(?i:"TLS_old_method")	{ return OLDTLS;	}
-(?i:"SCTP_streams")	{ return SCTPSTREAMS;	}
-(?i:"AppServThreads")	{ return APPSERVTHREADS;}
-(?i:"ListenOn")		{ return LISTENON;	}
-(?i:"TcTimer")		{ return TCTIMER;	}
-(?i:"TwTimer")		{ return TWTIMER;	}
-(?i:"NoRelay")		{ return NORELAY;	}
-(?i:"LoadExtension")	{ return LOADEXT;	}
-(?i:"ConnectPeer")	{ return CONNPEER;	}
-(?i:"ConnectTo")	{ return CONNTO;	}
-(?i:"No_TLS")		{ return NOTLS;		}
-(?i:"TLS_Cred")		{ return TLS_CRED;	}
-(?i:"TLS_CA")		{ return TLS_CA;	}
-(?i:"TLS_CRL")		{ return TLS_CRL;	}
-(?i:"TLS_Prio")		{ return TLS_PRIO;	}
-(?i:"TLS_DH_bits")	{ return TLS_DH_BITS;	}
-(?i:"TLS_DH_file")	{ return TLS_DH_FILE;	}
-
-
-	/* Valid single characters for yyparse */
-<*>[=,:;{}]		{ return yytext[0]; }
-
-	/* Unrecognized token */
-<*>[[:alnum:]]+		|	/* This rule is only useful to print a complete token in error messages */
-	/* Unrecognized character */
-<*>.			{ 
-				fprintf(stderr, "Unrecognized text on line %d col %d: '%s'.\n", yylloc->first_line, yylloc->first_column, yytext);
-			 	return LEX_ERROR; 
-			}
-
-%%
Index: eeDiameter/fdd.y
===================================================================
--- freeDiameter/fdd.y	(revision 578)
+++ 	(revision )
@@ -1,600 +1,0 @@
-/*********************************************************************************************************
-* Software License Agreement (BSD License)                                                               *
-* Author: Sebastien Decugis <sdecugis@nict.go.jp>							 *
-*													 *
-* Copyright (c) 2010, 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.								 *
-*********************************************************************************************************/
-
-/* Yacc configuration parser.
- *
- * This file defines the grammar of the configuration file.
- * Note that each extension has a separate independant configuration file.
- *
- * Note : This module is NOT thread-safe. All processing must be done from one thread only.
- */
-
-/* For development only : */
-%debug 
-%error-verbose
-
-%parse-param {struct fd_config * conf}
-
-/* Keep track of location */
-%locations 
-%pure-parser
-
-%{
-#include "fD.h"
-#include "fdd.tab.h"	/* bug : bison does not define the YYLTYPE before including this bloc, so... */
-
-/* The Lex parser prototype */
-int fddlex(YYSTYPE *lvalp, YYLTYPE *llocp);
-
-/* Function to report error */
-void yyerror (YYLTYPE *ploc, struct fd_config * conf, char const *s)
-{
-	if (ploc->first_line != ploc->last_line)
-		fprintf(stderr, "%s:%d.%d-%d.%d : %s\n", conf->cnf_file, ploc->first_line, ploc->first_column, ploc->last_line, ploc->last_column, s);
-	else if (ploc->first_column != ploc->last_column)
-		fprintf(stderr, "%s:%d.%d-%d : %s\n", conf->cnf_file, ploc->first_line, ploc->first_column, ploc->last_column, s);
-	else
-		fprintf(stderr, "%s:%d.%d : %s\n", conf->cnf_file, ploc->first_line, ploc->first_column, s);
-}
-
-int got_peer_noip = 0;
-int got_peer_noipv6 = 0;
-int got_peer_notcp = 0;
-int got_peer_nosctp = 0;
-
-struct peer_info fddpi;
-
-%}
-
-/* Values returned by lex for token */
-%union {
-	char 		 *string;	/* The string is allocated by strdup in lex.*/
-	int		  integer;	/* Store integer values */
-}
-
-/* In case of error in the lexical analysis */
-%token 		LEX_ERROR
-
-%token <string>	QSTRING
-%token <integer> INTEGER
-
-%type <string> 	extconf
-
-%token		IDENTITY
-%token		REALM
-%token		PORT
-%token		SECPORT
-%token		NOIP
-%token		NOIP6
-%token		NOTCP
-%token		NOSCTP
-%token		PREFERTCP
-%token		OLDTLS
-%token		NOTLS
-%token		SCTPSTREAMS
-%token		APPSERVTHREADS
-%token		LISTENON
-%token		TCTIMER
-%token		TWTIMER
-%token		NORELAY
-%token		LOADEXT
-%token		CONNPEER
-%token		CONNTO
-%token		TLS_CRED
-%token		TLS_CA
-%token		TLS_CRL
-%token		TLS_PRIO
-%token		TLS_DH_BITS
-%token		TLS_DH_FILE
-
-
-/* -------------------------------------- */
-%%
-
-	/* The grammar definition - Sections blocs. */
-conffile:		/* Empty is OK -- for simplicity here, we reject in daemon later */
-			| conffile identity
-			| conffile realm
-			| conffile tctimer
-			| conffile twtimer
-			| conffile port
-			| conffile secport
-			| conffile sctpstreams
-			| conffile listenon
-			| conffile norelay
-			| conffile appservthreads
-			| conffile noip
-			| conffile noip6
-			| conffile notcp
-			| conffile nosctp
-			| conffile prefertcp
-			| conffile oldtls
-			| conffile loadext
-			| conffile connpeer
-			| conffile tls_cred
-			| conffile tls_ca
-			| conffile tls_crl
-			| conffile tls_prio
-			| conffile tls_dh
-			| conffile errors
-			{
-				yyerror(&yylloc, conf, "An error occurred while parsing the configuration file");
-				return EINVAL;
-			}
-			;
-
-			/* Lexical or syntax error */
-errors:			LEX_ERROR
-			| error
-			;
-
-identity:		IDENTITY '=' QSTRING ';'
-			{
-				conf->cnf_diamid = $3;
-			}
-			;
-
-realm:			REALM '=' QSTRING ';'
-			{
-				conf->cnf_diamrlm = $3;
-			}
-			;
-
-tctimer:		TCTIMER '=' INTEGER ';'
-			{
-				CHECK_PARAMS_DO( ($3 > 0),
-					{ yyerror (&yylloc, conf, "Invalid value"); YYERROR; } );
-				conf->cnf_timer_tc = (unsigned int)$3;
-			}
-			;
-
-twtimer:		TWTIMER '=' INTEGER ';'
-			{
-				CHECK_PARAMS_DO( ($3 > 5),
-					{ yyerror (&yylloc, conf, "Invalid value"); YYERROR; } );
-				conf->cnf_timer_tw = (unsigned int)$3;
-			}
-			;
-
-port:			PORT '=' INTEGER ';'
-			{
-				CHECK_PARAMS_DO( ($3 > 0) && ($3 < 1<<16),
-					{ yyerror (&yylloc, conf, "Invalid value"); YYERROR; } );
-				conf->cnf_port = (uint16_t)$3;
-			}
-			;
-
-secport:		SECPORT '=' INTEGER ';'
-			{
-				CHECK_PARAMS_DO( ($3 > 0) && ($3 < 1<<16),
-					{ yyerror (&yylloc, conf, "Invalid value"); YYERROR; } );
-				conf->cnf_port_tls = (uint16_t)$3;
-			}
-			;
-
-sctpstreams:		SCTPSTREAMS '=' INTEGER ';'
-			{
-				CHECK_PARAMS_DO( ($3 > 0) && ($3 < 1<<16),
-					{ yyerror (&yylloc, conf, "Invalid value"); YYERROR; } );
-				conf->cnf_sctp_str = (uint16_t)$3;
-			}
-			;
-
-listenon:		LISTENON '=' QSTRING ';'
-			{
-				struct addrinfo hints, *ai;
-				int ret;
-				
-				memset(&hints, 0, sizeof(hints));
-				hints.ai_flags = AI_PASSIVE | AI_NUMERICHOST;
-				ret = getaddrinfo($3, NULL, &hints, &ai);
-				if (ret) { yyerror (&yylloc, conf, gai_strerror(ret)); YYERROR; }
-				CHECK_FCT_DO( fd_ep_add_merge( &conf->cnf_endpoints, ai->ai_addr, ai->ai_addrlen, EP_FL_CONF ), YYERROR );
-				freeaddrinfo(ai);
-				free($3);
-			}
-			;
-
-norelay:		NORELAY ';'
-			{
-				conf->cnf_flags.no_fwd = 1;
-			}
-			;
-
-appservthreads:		APPSERVTHREADS '=' INTEGER ';'
-			{
-				CHECK_PARAMS_DO( ($3 > 0) && ($3 < 1024),
-					{ yyerror (&yylloc, conf, "Invalid value"); YYERROR; } );
-				conf->cnf_dispthr = (uint16_t)$3;
-			}
-			;
-
-noip:			NOIP ';'
-			{
-				if (got_peer_noipv6) { 
-					yyerror (&yylloc, conf, "No_IP conflicts with a ConnectPeer directive No_IPv6."); 
-					YYERROR; 
-				}
-				conf->cnf_flags.no_ip4 = 1;
-			}
-			;
-
-noip6:			NOIP6 ';'
-			{
-				if (got_peer_noip) { 
-					yyerror (&yylloc, conf, "No_IP conflicts with a ConnectPeer directive No_IP."); 
-					YYERROR; 
-				}
-				conf->cnf_flags.no_ip6 = 1;
-			}
-			;
-
-notcp:			NOTCP ';'
-			{
-				#ifdef DISABLE_SCTP
-				yyerror (&yylloc, conf, "No_TCP cannot be specified for daemon compiled with DISABLE_SCTP option."); 
-				YYERROR; 
-				#endif
-				if (conf->cnf_flags.no_sctp)
-				{
-					yyerror (&yylloc, conf, "No_TCP conflicts with No_SCTP directive." ); 
-					YYERROR; 
-				}
-				if (got_peer_nosctp) { 
-					yyerror (&yylloc, conf, "No_TCP conflicts with a ConnectPeer directive No_SCTP."); 
-					YYERROR; 
-				}
-				conf->cnf_flags.no_tcp = 1;
-			}
-			;
-
-nosctp:			NOSCTP ';'
-			{
-				if (conf->cnf_flags.no_tcp)
-				{
-					yyerror (&yylloc, conf, "No_SCTP conflicts with No_TCP directive." ); 
-					YYERROR; 
-				}
-				if (got_peer_notcp) { 
-					yyerror (&yylloc, conf, "No_SCTP conflicts with a ConnectPeer directive No_TCP.");
-					YYERROR;
-				}
-				conf->cnf_flags.no_sctp = 1;
-			}
-			;
-
-prefertcp:		PREFERTCP ';'
-			{
-				conf->cnf_flags.pr_tcp = 1;
-			}
-			;
-
-oldtls:			OLDTLS ';'
-			{
-				conf->cnf_flags.tls_alg = 1;
-			}
-			;
-
-loadext:		LOADEXT '=' QSTRING extconf ';'
-			{
-				char * fname;
-				char * cfname;
-				FILE * fd;
-				
-				/* Try and open the extension file */
-				fname = $3;
-				fd = fopen(fname, "r");
-				if ((fd == NULL) && (*fname != '/')) {
-					char * bkp = fname;
-					CHECK_MALLOC_DO( fname = malloc( strlen(bkp) + strlen(DEFAULT_EXTENSIONS_PATH) + 2 ),
-						{ yyerror (&yylloc, conf, "Not enough memory"); YYERROR; } );
-					sprintf(fname, DEFAULT_EXTENSIONS_PATH "/%s", bkp);
-					free(bkp);
-					fd = fopen(fname, "r");
-				}
-				if (fd == NULL) {
-					int ret = errno;
-					TRACE_DEBUG(INFO, "Unable to open extension file %s for reading: %s\n", fname, strerror(ret));
-					yyerror (&yylloc, conf, "Error adding extension"); 
-					YYERROR;
-				}
-				fclose(fd);
-				
-				/* Try and open the configuration file (optional) */
-				cfname = $4;
-				if (cfname) {
-					fd = fopen(cfname, "r");
-					if ((fd == NULL) && (*cfname != '/')) {
-						char * test;
-						CHECK_MALLOC_DO( test = malloc( strlen(cfname) + strlen(DEFAULT_CONF_PATH) + 2 ),
-							{ yyerror (&yylloc, conf, "Not enough memory"); YYERROR; } );
-						sprintf(test, DEFAULT_CONF_PATH "/%s", cfname);
-						fd = fopen(test, "r");
-						if (fd) {
-							free(cfname);
-							cfname=test;
-						} else {
-							/* This is not an error, we allow an extension to wait for something else than a real conf file. */
-							free(test);
-						}
-					}
-					if (fd)
-						fclose(fd);
-				}
-				
-				CHECK_FCT_DO( fd_ext_add( fname, cfname ),
-					{ yyerror (&yylloc, conf, "Error adding extension"); YYERROR; } );
-			}
-			;
-			
-extconf:		/* empty */
-			{
-				$$ = NULL;
-			}
-			| ':' QSTRING
-			{
-				$$ = $2;
-			}
-			;
-			
-connpeer:		{
-				memset(&fddpi, 0, sizeof(fddpi));
-				fddpi.config.pic_flags.persist = PI_PRST_ALWAYS;
-				fd_list_init( &fddpi.pi_endpoints, NULL );
-			}
-			CONNPEER '=' QSTRING peerinfo ';'
-			{
-				fddpi.pi_diamid = $4;
-				CHECK_FCT_DO( fd_peer_add ( &fddpi, conf->cnf_file, NULL, NULL ),
-					{ yyerror (&yylloc, conf, "Error adding ConnectPeer information"); YYERROR; } );
-					
-				/* Now destroy any content in the structure */
-				free(fddpi.pi_diamid);
-				free(fddpi.config.pic_realm);
-				free(fddpi.config.pic_priority);
-				while (!FD_IS_LIST_EMPTY(&fddpi.pi_endpoints)) {
-					struct fd_list * li = fddpi.pi_endpoints.next;
-					fd_list_unlink(li);
-					free(li);
-				}
-			}
-			;
-			
-peerinfo:		/* empty */
-			| '{' peerparams '}'
-			;
-			
-peerparams:		/* empty */
-			| peerparams NOIP ';'
-			{
-				if ((conf->cnf_flags.no_ip6) || (fddpi.config.pic_flags.pro3 == PI_P3_IP)) { 
-					yyerror (&yylloc, conf, "No_IP conflicts with a No_IPv6 directive.");
-					YYERROR;
-				}
-				got_peer_noip++;
-				fddpi.config.pic_flags.pro3 = PI_P3_IPv6;
-			}
-			| peerparams NOIP6 ';'
-			{
-				if ((conf->cnf_flags.no_ip4) || (fddpi.config.pic_flags.pro3 == PI_P3_IPv6)) { 
-					yyerror (&yylloc, conf, "No_IPv6 conflicts with a No_IP directive.");
-					YYERROR;
-				}
-				got_peer_noipv6++;
-				fddpi.config.pic_flags.pro3 = PI_P3_IP;
-			}
-			| peerparams NOTCP ';'
-			{
-				#ifdef DISABLE_SCTP
-					yyerror (&yylloc, conf, "No_TCP cannot be specified in daemon compiled with DISABLE_SCTP option.");
-					YYERROR;
-				#endif
-				if ((conf->cnf_flags.no_sctp) || (fddpi.config.pic_flags.pro4 == PI_P4_TCP)) { 
-					yyerror (&yylloc, conf, "No_TCP conflicts with a No_SCTP directive.");
-					YYERROR;
-				}
-				got_peer_notcp++;
-				fddpi.config.pic_flags.pro4 = PI_P4_SCTP;
-			}
-			| peerparams NOSCTP ';'
-			{
-				if ((conf->cnf_flags.no_tcp) || (fddpi.config.pic_flags.pro4 == PI_P4_SCTP)) { 
-					yyerror (&yylloc, conf, "No_SCTP conflicts with a No_TCP directive.");
-					YYERROR;
-				}
-				got_peer_nosctp++;
-				fddpi.config.pic_flags.pro4 = PI_P4_TCP;
-			}
-			| peerparams PREFERTCP ';'
-			{
-				fddpi.config.pic_flags.alg = PI_ALGPREF_TCP;
-			}
-			| peerparams OLDTLS ';'
-			{
-				fddpi.config.pic_flags.sec |= PI_SEC_TLS_OLD;
-			}
-			| peerparams NOTLS ';'
-			{
-				fddpi.config.pic_flags.sec |= PI_SEC_NONE;
-			}
-			| peerparams REALM '=' QSTRING ';'
-			{
-				fddpi.config.pic_realm = $4;
-			}
-			| peerparams PORT '=' INTEGER ';'
-			{
-				CHECK_PARAMS_DO( ($4 > 0) && ($4 < 1<<16),
-					{ yyerror (&yylloc, conf, "Invalid port value"); YYERROR; } );
-				fddpi.config.pic_port = (uint16_t)$4;
-			}
-			| peerparams TCTIMER '=' INTEGER ';'
-			{
-				fddpi.config.pic_tctimer = $4;
-			}
-			| peerparams TWTIMER '=' INTEGER ';'
-			{
-				fddpi.config.pic_twtimer = $4;
-			}
-			| peerparams TLS_PRIO '=' QSTRING ';'
-			{
-				fddpi.config.pic_priority = $4;
-			}
-			| peerparams CONNTO '=' QSTRING ';'
-			{
-				struct addrinfo hints, *ai;
-				int ret;
-				int disc = 0;
-				
-				memset(&hints, 0, sizeof(hints));
-				hints.ai_flags = AI_ADDRCONFIG | AI_NUMERICHOST;
-				ret = getaddrinfo($4, NULL, &hints, &ai);
-				if (ret == EAI_NONAME) {
-					/* The name was maybe not numeric, try again */
-					disc = EP_FL_DISC;
-					hints.ai_flags &= ~ AI_NUMERICHOST;
-					ret = getaddrinfo($4, NULL, &hints, &ai);
-				}
-				if (ret) { yyerror (&yylloc, conf, gai_strerror(ret)); YYERROR; }
-				
-				CHECK_FCT_DO( fd_ep_add_merge( &fddpi.pi_endpoints, ai->ai_addr, ai->ai_addrlen, EP_FL_CONF | disc ), YYERROR );
-				free($4);
-				freeaddrinfo(ai);
-			}
-			;
-
-tls_cred:		TLS_CRED '=' QSTRING ',' QSTRING ';'
-			{
-				FILE * fd;
-				fd = fopen($3, "r");
-				if (fd == NULL) {
-					int ret = errno;
-					TRACE_DEBUG(INFO, "Unable to open certificate file %s for reading: %s\n", $3, strerror(ret));
-					yyerror (&yylloc, conf, "Error on file name"); 
-					YYERROR;
-				}
-				fclose(fd);
-				fd = fopen($5, "r");
-				if (fd == NULL) {
-					int ret = errno;
-					TRACE_DEBUG(INFO, "Unable to open private key file %s for reading: %s\n", $5, strerror(ret));
-					yyerror (&yylloc, conf, "Error on file name"); 
-					YYERROR;
-				}
-				fclose(fd);
-				conf->cnf_sec_data.cert_file = $3;
-				conf->cnf_sec_data.key_file = $5;
-				
-				CHECK_GNUTLS_DO( gnutls_certificate_set_x509_key_file( 
-							conf->cnf_sec_data.credentials,
-							conf->cnf_sec_data.cert_file,
-							conf->cnf_sec_data.key_file,
-							GNUTLS_X509_FMT_PEM),
-						{ yyerror (&yylloc, conf, "Error opening certificate or private key file."); YYERROR; } );
-			}
-			;
-
-tls_ca:			TLS_CA '=' QSTRING ';'
-			{
-				FILE * fd;
-				fd = fopen($3, "r");
-				if (fd == NULL) {
-					int ret = errno;
-					TRACE_DEBUG(INFO, "Unable to open CA file %s for reading: %s\n", $3, strerror(ret));
-					yyerror (&yylloc, conf, "Error on file name"); 
-					YYERROR;
-				}
-				fclose(fd);
-				conf->cnf_sec_data.ca_file = $3;
-				CHECK_GNUTLS_DO( conf->cnf_sec_data.ca_file_nr += gnutls_certificate_set_x509_trust_file( 
-							conf->cnf_sec_data.credentials,
-							conf->cnf_sec_data.ca_file,
-							GNUTLS_X509_FMT_PEM),
-						{ yyerror (&yylloc, conf, "Error setting CA parameters."); YYERROR; } );
-			}
-			;
-			
-tls_crl:		TLS_CRL '=' QSTRING ';'
-			{
-				FILE * fd;
-				fd = fopen($3, "r");
-				if (fd == NULL) {
-					int ret = errno;
-					TRACE_DEBUG(INFO, "Unable to open CRL file %s for reading: %s\n", $3, strerror(ret));
-					yyerror (&yylloc, conf, "Error on file name"); 
-					YYERROR;
-				}
-				fclose(fd);
-				conf->cnf_sec_data.crl_file = $3;
-				CHECK_GNUTLS_DO( gnutls_certificate_set_x509_crl_file( 
-							conf->cnf_sec_data.credentials,
-							conf->cnf_sec_data.ca_file,
-							GNUTLS_X509_FMT_PEM),
-						{ yyerror (&yylloc, conf, "Error setting CRL parameters."); YYERROR; } );
-			}
-			;
-			
-tls_prio:		TLS_PRIO '=' QSTRING ';'
-			{
-				const char * err_pos = NULL;
-				conf->cnf_sec_data.prio_string = $3;
-				CHECK_GNUTLS_DO( gnutls_priority_init( 
-							&conf->cnf_sec_data.prio_cache,
-							conf->cnf_sec_data.prio_string,
-							&err_pos),
-						{ yyerror (&yylloc, conf, "Error setting Priority parameter.");
-						  fprintf(stderr, "Error at position : %s\n", err_pos);
-						  YYERROR; } );
-			}
-			;
-			
-tls_dh:			TLS_DH_BITS '=' INTEGER ';'
-			{
-				conf->cnf_sec_data.dh_bits = $3;
-			}
-			| TLS_DH_FILE '=' QSTRING ';'
-			{
-				FILE * fd;
-				free(conf->cnf_sec_data.dh_file);
-				conf->cnf_sec_data.dh_file = $3;
-				fd = fopen($3, "r");
-				if (fd == NULL) {
-					int ret = errno;
-					TRACE_DEBUG(INFO, "Unable to open DH file %s for reading: %s\n", $3, strerror(ret));
-					yyerror (&yylloc, conf, "Error on file name"); 
-					YYERROR;
-				}
-				fclose(fd);
-			}
-			;
Index: eeDiameter/main.c
===================================================================
--- freeDiameter/main.c	(revision 628)
+++ 	(revision )
@@ -1,380 +1,0 @@
-/*********************************************************************************************************
-* Software License Agreement (BSD License)                                                               *
-* Author: Sebastien Decugis <sdecugis@nict.go.jp>							 *
-*													 *
-* Copyright (c) 2010, 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 "fD.h"
-
-#include <signal.h>
-#include <getopt.h>
-#include <locale.h>
-#include <gcrypt.h>
-
-/* forward declarations */
-static void fd_shutdown(int signal);
-static int main_cmdline(int argc, char *argv[]);
-static void main_version(void);
-static void main_help( void );
-static int signal_framework_ready(void);
-
-/* The static configuration structure */
-static struct fd_config conf;
-struct fd_config * fd_g_config = &conf;
-
-/* gcrypt functions to support posix threads */
-GCRY_THREAD_OPTION_PTHREAD_IMPL;
-
-/* freeDiameter starting point */
-int main(int argc, char * argv[])
-{
-	int ret;
-	
-	memset(fd_g_config, 0, sizeof(struct fd_config));
-	
-	/* Initialize the library -- must come first since it initializes the debug facility */
-	CHECK_FCT( fd_lib_init(1) );
-	TRACE_DEBUG(INFO, "libfreeDiameter initialized.");
-	
-	/* Name this thread */
-	fd_log_threadname("Main");
-	
-	/* Initialize gcrypt and gnutls */
-	GNUTLS_TRACE( (void) gcry_control (GCRYCTL_SET_THREAD_CBS, &gcry_threads_pthread) );
-	GNUTLS_TRACE( (void) gcry_control (GCRYCTL_ENABLE_QUICK_RANDOM, 0) );
-	CHECK_GNUTLS_DO( gnutls_global_init(), return EINVAL );
-	if ( ! gnutls_check_version(GNUTLS_VERSION) ) {
-		fprintf(stderr, "The GNUTLS library is too old; found '%s', need '" GNUTLS_VERSION "'\n", gnutls_check_version(NULL));
-		return EINVAL;
-	} else {
-		TRACE_DEBUG(INFO, "libgnutls '%s', libgcrypt '%s', initialized.", gnutls_check_version(NULL), gcry_check_version(NULL) );
-	}
-	
-	/* Initialize the config */
-	CHECK_FCT( fd_conf_init() );
-
-	/* Parse the command-line */
-	CHECK_FCT(  main_cmdline(argc, argv)  );
-	
-	/* Allow SIGINT and SIGTERM from this point to terminate the application */
-	CHECK_FCT( fd_sig_register(SIGINT,  "freeDiameter.main", fd_shutdown) );
-	CHECK_FCT( fd_sig_register(SIGTERM, "freeDiameter.main", fd_shutdown) );
-	
-	/* Add definitions of the base protocol */
-	CHECK_FCT( fd_dict_base_protocol(fd_g_config->cnf_dict) );
-	
-	/* Initialize other modules */
-	CHECK_FCT(  fd_queues_init()  );
-	CHECK_FCT(  fd_msg_init()  );
-	CHECK_FCT(  fd_sess_start()  );
-	CHECK_FCT(  fd_p_expi_init()  );
-	
-	/* Parse the configuration file */
-	CHECK_FCT( fd_conf_parse() );
-	
-	/* Create the daemon's threads */
-	CHECK_FCT(  fd_rtdisp_init()  );
-	
-	/* Load the dynamic extensions */
-	CHECK_FCT(  fd_ext_load()  );
-	
-	fd_conf_dump();
-	fd_sig_dump(FULL, 1);
-	
-	/* Start the servers */
-	CHECK_FCT( fd_servers_start() );
-	
-	/* Start the peer state machines */
-	CHECK_FCT( fd_psm_start() );
-	
-	/* Now, just wait for events */
-	TRACE_DEBUG(INFO, FD_PROJECT_BINARY " daemon initialized.");
-	CHECK_FCT( signal_framework_ready() );
-	while (1) {
-		int code; size_t sz; void * data;
-		CHECK_FCT_DO(  fd_event_get(fd_g_config->cnf_main_ev, &code, &sz, &data),  break  );
-		switch (code) {
-			case FDEV_DUMP_DICT:
-				fd_dict_dump(fd_g_config->cnf_dict);
-				break;
-			
-			case FDEV_DUMP_EXT:
-				fd_ext_dump();
-				break;
-			
-			case FDEV_DUMP_SERV:
-				fd_servers_dump();
-				break;
-			
-			case FDEV_DUMP_QUEUES:
-				fd_fifo_dump(0, "Incoming messages", fd_g_incoming, fd_msg_dump_walk);
-				fd_fifo_dump(0, "Outgoing messages", fd_g_outgoing, fd_msg_dump_walk);
-				fd_fifo_dump(0, "Local messages",    fd_g_local,    fd_msg_dump_walk);
-				break;
-			
-			case FDEV_DUMP_CONFIG:
-				fd_conf_dump();
-				break;
-			
-			case FDEV_DUMP_PEERS:
-				fd_peer_dump_list(FULL);
-				break;
-			
-			case FDEV_TERMINATE:
-				ret = 0;
-				goto end;
-			
-			default:
-				TRACE_DEBUG(INFO, "Unexpected event in the daemon (%d), ignored.\n", code);
-		}
-	}
-	
-end:
-	TRACE_DEBUG(INFO, FD_PROJECT_BINARY " daemon is stopping...");
-	
-	/* cleanups */
-	CHECK_FCT_DO( fd_servers_stop(), /* Stop accepting new connections */ );
-	CHECK_FCT_DO( fd_rtdisp_cleanstop(), /* Stop dispatch thread(s) after a clean loop if possible */ );
-	CHECK_FCT_DO( fd_peer_fini(), /* Stop all connections */ );
-	CHECK_FCT_DO( fd_rtdisp_fini(), /* Stop routing threads and destroy routing queues */ );
-	
-	CHECK_FCT_DO( fd_ext_term(), /* Cleanup all extensions */ );
-	CHECK_FCT_DO( fd_rtdisp_cleanup(), /* destroy remaining handlers */ );
-	
-	GNUTLS_TRACE( gnutls_global_deinit() );
-	
-	CHECK_FCT_DO( fd_conf_deinit(), );
-	
-	fd_log_debug(FD_PROJECT_BINARY " daemon is terminated.\n");
-	
-	fd_lib_fini();
-	
-	return ret;
-}
-
-/* gnutls debug */
-static void fd_gnutls_debug(int level, const char * str) {
-	fd_log_debug(" [gnutls:%d] %s", level, str);
-}
-
-/* Parse the command-line */
-static int main_cmdline(int argc, char *argv[])
-{
-	int c;
-	int option_index = 0;
-	char * locale;
-	
-      	struct option long_options[] = {
-		{ "help",	no_argument, 		NULL, 'h' },
-		{ "version",	no_argument, 		NULL, 'V' },
-		{ "config",	required_argument, 	NULL, 'c' },
-		{ "debug",	no_argument, 		NULL, 'd' },
-		{ "quiet",	no_argument, 		NULL, 'q' },
-		{ "dbglocale",	optional_argument, 	NULL, 'l' },
-		{ "dbg_func",	required_argument, 	NULL, 'f' },
-		{ "dbg_file",	required_argument, 	NULL, 'F' },
-		{ "dbg_gnutls",	required_argument, 	NULL, 'g' },
-		{ NULL,		0, 			NULL, 0 }
-	};
-	
-	TRACE_ENTRY("%d %p", argc, argv);
-	
-	/* Loop on arguments */
-	while (1) {
-		c = getopt_long (argc, argv, "hVc:dql:", long_options, &option_index);
-		if (c == -1) 
-			break;	/* Exit from the loop.  */
-		
-		switch (c) {
-			case 'h':	/* Print help and exit.  */
-				main_help();
-				exit(0);
-
-			case 'V':	/* Print version and exit.  */
-				main_version();
-				exit(0);
-
-			case 'c':	/* Read configuration from this file instead of the default location..  */
-				CHECK_PARAMS( optarg );
-				fd_g_config->cnf_file = optarg;
-				break;
-
-			case 'l':	/* Change the locale.  */
-				locale = setlocale(LC_ALL, optarg?:"");
-				if (locale) {
-					TRACE_DEBUG(INFO, "Locale set to: %s", optarg ?: locale);
-				} else {
-					TRACE_DEBUG(INFO, "Unable to set locale (%s)", optarg);
-					return EINVAL;
-				}
-				break;
-
-			case 'd':	/* Increase verbosity of debug messages.  */
-				fd_g_debug_lvl++;
-				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");
-				return EINVAL;
-				#endif /* DEBUG */
-				break;
-				
-			case 'F':	/* Full debug for the file with this name.  */
-				#ifdef DEBUG
-				fd_debug_one_file = basename(optarg);
-				#else /* DEBUG */
-				TRACE_DEBUG(INFO, "Error: must compile with DEBUG support to use this feature");
-				return EINVAL;
-				#endif /* DEBUG */
-				break;
-				
-			case 'g':	/* Full debug for the function with this name.  */
-				{
-					int l = (int)atoi(optarg);
-					if (l) {
-						gnutls_global_set_log_function((gnutls_log_func)fd_gnutls_debug);
-						gnutls_global_set_log_level (l);
-						TRACE_DEBUG(INFO, "Enabled GNUTLS debug at level %d", l);
-					}
-				}
-				break;
-				
-			case 'q':	/* Decrease verbosity then remove debug messages.  */
-				fd_g_debug_lvl--;
-				break;
-
-			case '?':	/* Invalid option.  */
-				/* `getopt_long' already printed an error message.  */
-				TRACE_DEBUG(INFO, "getopt_long found an invalid character");
-				return EINVAL;
-
-			default:	/* bug: option not considered.  */
-				TRACE_DEBUG(INFO, "A command-line option is missing in parser: %c", c);
-				ASSERT(0);
-				return EINVAL;
-		}
-	}
-		
-	return 0;
-}
-
-/* Display package version */
-static void main_version_core(void)
-{
-	printf("%s, version %d.%d.%d"
-#ifdef HG_VERSION
-		" (r%s"
-# ifdef PACKAGE_HG_REVISION
-		"/%s"
-# endif /* PACKAGE_HG_VERSION */
-		")"
-#endif /* HG_VERSION */
-		"\n", 
-		FD_PROJECT_NAME, FD_PROJECT_VERSION_MAJOR, FD_PROJECT_VERSION_MINOR, FD_PROJECT_VERSION_REV
-#ifdef HG_VERSION
-		, HG_VERSION
-# ifdef PACKAGE_HG_REVISION
-		, PACKAGE_HG_REVISION
-# endif /* PACKAGE_HG_VERSION */
-#endif /* HG_VERSION */
-		);
-}
-
-/* Display package version and general info */
-static void main_version(void)
-{
-	main_version_core();
-	printf( "%s\n", FD_PROJECT_COPYRIGHT);
-	printf( "\nSee " FD_PROJECT_NAME " homepage at http://www.freediameter.net/\n"
-		" for information, updates and bug reports on this software.\n");
-}
-
-/* Print command-line options */
-static void main_help( void )
-{
-	main_version_core();
-	printf(	"  This daemon is an implementation of the Diameter protocol\n"
-		"  used for Authentication, Authorization, and Accounting (AAA).\n");
-	printf("\nUsage:  " FD_PROJECT_BINARY " [OPTIONS]...\n");
-	printf( "  -h, --help             Print help and exit\n"
-  		"  -V, --version          Print version and exit\n"
-  		"  -c, --config=filename  Read configuration from this file instead of the \n"
-		"                           default location (" DEFAULT_CONF_PATH "/" FD_DEFAULT_CONF_FILENAME ").\n");
- 	printf( "\nDebug:\n"
-  		"  These options are mostly useful for developers\n"
-  		"  -l, --dbglocale        Set the locale for error messages\n"
-  		"  -d, --debug            Increase verbosity of debug messages\n"
-  		"  -q, --quiet            Decrease verbosity then remove debug messages\n"
-  		"  --dbg_gnutls <int>     Enable GNU TLS debug at level <int>\n");
-}
-
-/* Terminate the application */
-static void fd_shutdown(int signal)
-{
-	TRACE_ENTRY("%d", signal);
-	
-	TRACE_DEBUG(INFO, "Received signal %s (%d), exiting", fd_sig_abbrev(signal), signal);
-
-	CHECK_FCT_DO( fd_event_send(fd_g_config->cnf_main_ev, FDEV_TERMINATE, 0, NULL), exit(2) );
-	
-	return;
-}
-
-
-/* Signal extensions when the framework is completly initialized */
-static int             is_ready = 0;
-static pthread_mutex_t is_ready_mtx = PTHREAD_MUTEX_INITIALIZER;
-static pthread_cond_t  is_ready_cnd = PTHREAD_COND_INITIALIZER;
-static int signal_framework_ready(void)
-{
-	TRACE_ENTRY("");
-	CHECK_POSIX( pthread_mutex_lock( &is_ready_mtx ) );
-	is_ready = 1;
-	CHECK_POSIX( pthread_cond_broadcast( &is_ready_cnd ) );
-	CHECK_POSIX( pthread_mutex_unlock( &is_ready_mtx ) );
-	return 0;
-}
-int fd_wait_initialization_complete(void)
-{
-	TRACE_ENTRY("");
-	CHECK_POSIX( pthread_mutex_lock( &is_ready_mtx ) );
-	pthread_cleanup_push( fd_cleanup_mutex, &is_ready_mtx );
-	while (!is_ready) {
-		CHECK_POSIX( pthread_cond_wait( &is_ready_cnd, &is_ready_mtx ) );
-	}
-	pthread_cleanup_pop( 0 );
-	CHECK_POSIX( pthread_mutex_unlock( &is_ready_mtx ) );
-	return 0;
-}
Index: eeDiameter/messages.c
===================================================================
--- freeDiameter/messages.c	(revision 648)
+++ 	(revision )
@@ -1,371 +1,0 @@
-/*********************************************************************************************************
-* Software License Agreement (BSD License)                                                               *
-* Author: Sebastien Decugis <sdecugis@nict.go.jp>							 *
-*													 *
-* 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 "fD.h"
-
-static struct dict_object * dict_avp_OH  = NULL; /* Origin-Host */
-static struct dict_object * dict_avp_OR  = NULL; /* Origin-Realm */
-static struct dict_object * dict_avp_EM  = NULL; /* Error-Message */
-static struct dict_object * dict_avp_ERH = NULL; /* Error-Reporting-Host */
-static struct dict_object * dict_avp_FAVP= NULL; /* Failed-AVP */
-static struct dict_object * dict_avp_RC  = NULL; /* Result-Code */
-struct dict_object * fd_dict_avp_OSI = NULL; /* Origin-State-Id */
-struct dict_object * fd_dict_cmd_CER = NULL; /* Capabilities-Exchange-Request */
-struct dict_object * fd_dict_cmd_DWR = NULL; /* Device-Watchdog-Request */
-struct dict_object * fd_dict_avp_DC  = NULL; /* Disconnect-Cause */
-struct dict_object * fd_dict_cmd_DPR = NULL; /* Disconnect-Peer-Request */
-
-/* Resolve the dictionary objects */
-int fd_msg_init(void)
-{
-	TRACE_ENTRY("");
-	
-	/* Initialize the dictionary objects that we may use frequently */
-	CHECK_FCT(  fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Origin-Host",     	&dict_avp_OH  , ENOENT)  );
-	CHECK_FCT(  fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Origin-Realm",    	&dict_avp_OR  , ENOENT)  );
-	CHECK_FCT(  fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Origin-State-Id", 	&fd_dict_avp_OSI , ENOENT)  );
-	
-	CHECK_FCT(  fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Result-Code",     	&dict_avp_RC  , ENOENT)  );
-	CHECK_FCT(  fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Error-Message",   	&dict_avp_EM  , ENOENT)  );
-	CHECK_FCT(  fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Error-Reporting-Host", &dict_avp_ERH , ENOENT)  );
-	CHECK_FCT(  fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Failed-AVP",      	&dict_avp_FAVP, ENOENT)  );
-	
-	CHECK_FCT(  fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Disconnect-Cause", 	&fd_dict_avp_DC , ENOENT)  );
-	
-	CHECK_FCT( fd_dict_search ( fd_g_config->cnf_dict, DICT_COMMAND, CMD_BY_NAME, "Capabilities-Exchange-Request", &fd_dict_cmd_CER, ENOENT ) );
-	CHECK_FCT( fd_dict_search ( fd_g_config->cnf_dict, DICT_COMMAND, CMD_BY_NAME, "Device-Watchdog-Request", &fd_dict_cmd_DWR, ENOENT ) );
-	CHECK_FCT( fd_dict_search ( fd_g_config->cnf_dict, DICT_COMMAND, CMD_BY_NAME, "Disconnect-Peer-Request", &fd_dict_cmd_DPR, ENOENT ) );
-	
-	
-	return 0;
-}
-
-/* Add Origin-Host, Origin-Realm, Origin-State-Id AVPS at the end of the message */
-int fd_msg_add_origin ( struct msg * msg, int osi )
-{
-	union avp_value val;
-	struct avp * avp_OH  = NULL;
-	struct avp * avp_OR  = NULL;
-	struct avp * avp_OSI = NULL;
-	
-	TRACE_ENTRY("%p", msg);
-	CHECK_PARAMS(  msg  );
-	
-	/* Create the Origin-Host AVP */
-	CHECK_FCT( fd_msg_avp_new( dict_avp_OH, 0, &avp_OH ) );
-	
-	/* Set its value */
-	memset(&val, 0, sizeof(val));
-	val.os.data = (unsigned char *)fd_g_config->cnf_diamid;
-	val.os.len  = fd_g_config->cnf_diamid_len;
-	CHECK_FCT( fd_msg_avp_setvalue( avp_OH, &val ) );
-	
-	/* Add it to the message */
-	CHECK_FCT( fd_msg_avp_add( msg, MSG_BRW_LAST_CHILD, avp_OH ) );
-	
-	
-	/* Create the Origin-Realm AVP */
-	CHECK_FCT( fd_msg_avp_new( dict_avp_OR, 0, &avp_OR ) );
-	
-	/* Set its value */
-	memset(&val, 0, sizeof(val));
-	val.os.data = (unsigned char *)fd_g_config->cnf_diamrlm;
-	val.os.len  = fd_g_config->cnf_diamrlm_len;
-	CHECK_FCT( fd_msg_avp_setvalue( avp_OR, &val ) );
-	
-	/* Add it to the message */
-	CHECK_FCT( fd_msg_avp_add( msg, MSG_BRW_LAST_CHILD, avp_OR ) );
-	
-	if (osi) {
-		/* Create the Origin-State-Id AVP */
-		CHECK_FCT( fd_msg_avp_new( fd_dict_avp_OSI, 0, &avp_OSI ) );
-
-		/* Set its value */
-		memset(&val, 0, sizeof(val));
-		val.u32 = fd_g_config->cnf_orstateid;
-		CHECK_FCT( fd_msg_avp_setvalue( avp_OSI, &val ) );
-
-		/* Add it to the message */
-		CHECK_FCT( fd_msg_avp_add( msg, MSG_BRW_LAST_CHILD, avp_OSI ) );
-	}
-	
-	return 0;
-}
-
-/* Add Result-Code and eventually Failed-AVP, Error-Message and Error-Reporting-Host AVPs */
-int fd_msg_rescode_set( struct msg * msg, char * rescode, char * errormsg, struct avp * optavp, int type_id )
-{
-	union avp_value val;
-	struct avp * avp_RC  = NULL;
-	struct avp * avp_EM  = NULL;
-	struct avp * avp_ERH = NULL;
-	struct avp * avp_FAVP= NULL;
-	uint32_t rc_val = 0;
-	int set_e_bit=0;
-	int std_err_msg=0;
-	
-	TRACE_ENTRY("%p %s %p %p %d", msg, rescode, errormsg, optavp, type_id);
-		
-	CHECK_PARAMS(  msg && rescode  );
-	
-	/* Find the enum value corresponding to the rescode string, this will give the class of error */
-	{
-		struct dict_object * enum_obj = NULL;
-		struct dict_enumval_request req;
-		memset(&req, 0, sizeof(struct dict_enumval_request));
-		
-		/* First, get the enumerated type of the Result-Code AVP */
-		CHECK_FCT(  fd_dict_search( fd_g_config->cnf_dict, DICT_TYPE, TYPE_OF_AVP, dict_avp_RC, &(req.type_obj), ENOENT  )  );
-		
-		/* Now search for the value given as parameter */
-		req.search.enum_name = rescode;
-		CHECK_FCT(  fd_dict_search( fd_g_config->cnf_dict, DICT_ENUMVAL, ENUMVAL_BY_STRUCT, &req, &enum_obj, ENOTSUP)  );
-		
-		/* finally retrieve its data */
-		CHECK_FCT_DO(  fd_dict_getval( enum_obj, &(req.search) ), return EINVAL );
-		
-		/* copy the found value, we're done */
-		rc_val = req.search.enum_value.u32;
-	}
-	
-	if (type_id == 1) {
-		/* Add the Origin-Host and Origin-Realm AVP */
-		CHECK_FCT( fd_msg_add_origin ( msg, 0 ) );
-	}
-	
-	/* Create the Result-Code AVP */
-	CHECK_FCT( fd_msg_avp_new( dict_avp_RC, 0, &avp_RC ) );
-	
-	/* Set its value */
-	memset(&val, 0, sizeof(val));
-	val.u32  = rc_val;
-	CHECK_FCT( fd_msg_avp_setvalue( avp_RC, &val ) );
-	
-	/* Add it to the message */
-	CHECK_FCT( fd_msg_avp_add( msg, MSG_BRW_LAST_CHILD, avp_RC ) );
-	
-	if (type_id == 2) {
-		/* Add the Error-Reporting-Host AVP */
-		
-		CHECK_FCT( fd_msg_avp_new( dict_avp_ERH, 0, &avp_ERH ) );
-
-		/* Set its value */
-		memset(&val, 0, sizeof(val));
-		val.os.data = (unsigned char *)fd_g_config->cnf_diamid;
-		val.os.len  = fd_g_config->cnf_diamid_len;
-		CHECK_FCT( fd_msg_avp_setvalue( avp_ERH, &val ) );
-
-		/* Add it to the message */
-		CHECK_FCT( fd_msg_avp_add( msg, MSG_BRW_LAST_CHILD, avp_ERH ) );
-	
-	}
-	
-	/* Now add the optavp in a FailedAVP if provided */
-	if (optavp) {
-		/* Create the Failed-AVP AVP */
-		CHECK_FCT( fd_msg_avp_new( dict_avp_FAVP, 0, &avp_FAVP ) );
-
-		/* Add the passed AVP inside it */
-		CHECK_FCT( fd_msg_avp_add( avp_FAVP, MSG_BRW_LAST_CHILD, optavp ) );
-		
-		/* And add to the message */
-		CHECK_FCT( fd_msg_avp_add( msg, MSG_BRW_LAST_CHILD, avp_FAVP ) );
-	}
-	
-	
-	/* Deal with the 'E' bit and the error message */
-	switch (rc_val / 1000) {
-		case 1:	/* Informational */
-		case 2: /* Success */
-			/* Nothing special here: no E bit, no error message unless one is specified */
-			break;
-			
-		case 3: /* Protocol Errors */
-			set_e_bit = 1;
-			std_err_msg = 1;
-			break;
-			
-		case 4: /* Transcient Failure */
-		case 5: /* Permanent Failure */
-		default:
-			std_err_msg = 1;
-			break;
-			
-	}
-	
-	{
-		struct msg_hdr * hdr = NULL;
-		
-		CHECK_FCT(  fd_msg_hdr( msg, &hdr )  );
-		
-		if (set_e_bit)
-			hdr->msg_flags |= CMD_FLAG_ERROR;
-		else
-			hdr->msg_flags &= ! CMD_FLAG_ERROR;
-	}
-	
-	if (std_err_msg || errormsg) {
-		/* Add the Error-Message AVP */
-		
-		CHECK_FCT( fd_msg_avp_new( dict_avp_EM, 0, &avp_EM ) );
-
-		/* Set its value */
-		memset(&val, 0, sizeof(val));
-		
-		if (errormsg) {
-			val.os.data = (unsigned char *)errormsg;
-			val.os.len  = strlen(errormsg);
-		} else {
-			val.os.data = (unsigned char *)rescode;
-			val.os.len  = strlen(rescode);
-		}
-		CHECK_FCT( fd_msg_avp_setvalue( avp_EM, &val ) );
-
-		/* Add it to the message */
-		CHECK_FCT( fd_msg_avp_add( msg, MSG_BRW_LAST_CHILD, avp_EM ) );
-	}
-	
-	return 0;
-}
-
-/* Send a message and optionaly register a callback for an answer */
-int fd_msg_send ( struct msg ** pmsg, void (*anscb)(void *, struct msg **), void * data )
-{
-	TRACE_ENTRY("%p %p %p", pmsg, anscb, data);
-	CHECK_PARAMS( pmsg );
-	
-	/* Save the callback in the message */
-	if (anscb) {
-		CHECK_FCT(  fd_msg_anscb_associate( *pmsg, anscb, data, NULL /* we should maybe use a safeguard here like 1 hour or so? */ )  );
-	}
-	
-	/* Post the message in the outgoing queue */
-	CHECK_FCT( fd_fifo_post(fd_g_outgoing, pmsg) );
-	
-	return 0;
-}
-
-/* The variation of the same function with a timeout callback */
-int fd_msg_send_timeout ( struct msg ** pmsg, void (*anscb)(void *, struct msg **), void * data, const struct timespec *timeout )
-{
-	TRACE_ENTRY("%p %p %p", pmsg, anscb, data, timeout);
-	CHECK_PARAMS( pmsg && anscb && timeout );
-	
-	/* Save the callback in the message, with the timeout */
-	CHECK_FCT(  fd_msg_anscb_associate( *pmsg, anscb, data, timeout )  );
-	
-	/* Post the message in the outgoing queue */
-	CHECK_FCT( fd_fifo_post(fd_g_outgoing, pmsg) );
-	
-	return 0;
-}
-
-
-/* Parse a message against our dictionary, and in case of error log and eventually build the error reply -- returns the parsing status */
-int fd_msg_parse_or_error( struct msg ** msg )
-{
-	int ret = 0;
-	struct msg * m;
-	struct msg_hdr * hdr = NULL;
-	struct fd_pei	pei;
-	
-	TRACE_ENTRY("%p", msg);
-	
-	CHECK_PARAMS(msg && *msg);
-	m = *msg;
-	
-	/* Parse the message against our dictionary */
-	ret = fd_msg_parse_rules ( m, fd_g_config->cnf_dict, &pei);
-	if 	((ret != EBADMSG) 	/* Parsing grouped AVP failed / Conflicting rule found */
-		&& (ret != ENOTSUP))	/* Command is not supported / Mandatory AVP is not supported */
-		return ret;
-	
-	fd_log_debug("The following message does not comply to the dictionary and/or rules (%s):\n", pei.pei_errcode);
-	fd_msg_dump_walk(NONE, m);
-	
-	CHECK_FCT( fd_msg_hdr(m, &hdr) );
-	
-	/* Now create an answer error if the message is a query */
-	if (hdr->msg_flags & CMD_FLAG_REQUEST) {
-		
-		/* Create the error message */
-		CHECK_FCT( fd_msg_new_answer_from_req ( fd_g_config->cnf_dict, msg, pei.pei_protoerr ? MSGFL_ANSW_ERROR : 0 ) );
-		
-		/* Set the error code */
-		CHECK_FCT( fd_msg_rescode_set(*msg, pei.pei_errcode, pei.pei_message, pei.pei_avp, 1 ) );
-		
-	} else {
-		do { /* Rescue error messages */
-			struct avp * avp;
-			union avp_value * rc = NULL;
-			
-			/* Search the Result-Code AVP */
-			CHECK_FCT_DO(  fd_msg_browse(*msg, MSG_BRW_FIRST_CHILD, &avp, NULL), break  );
-			while (avp) {
-				struct avp_hdr * ahdr;
-				CHECK_FCT_DO(  fd_msg_avp_hdr( avp, &ahdr ), break  );
-				
-				if ((ahdr->avp_code == AC_RESULT_CODE) && (! (ahdr->avp_flags & AVP_FLAG_VENDOR)) ) {
-					/* Parse this AVP */
-					ASSERT( ahdr->avp_value );
-					rc = ahdr->avp_value;
-					break;
-				}
-				
-				/* Go to next AVP */
-				CHECK_FCT_DO(  fd_msg_browse(avp, MSG_BRW_NEXT, &avp, NULL), break  );
-			}
-			
-			if (rc) {
-				switch (rc->u32 / 1000) {
-					case 1:	/* 1xxx : Informational */
-					case 2:	/* 2xxx : Sucess */
-						/* In these cases, we want the message to validate the ABNF, so we will discard the bad message */
-						break;
-						
-					default: /* Other errors */
-						/* We let the application decide what to do with the message, we rescue it */
-						return 0;
-				}
-			}
-		} while (0);
-		
-		/* Just discard */
-		CHECK_FCT( fd_msg_free( m ) );
-		*msg = NULL;
-	}
-	
-	return EBADMSG; /* We convert ENOTSUP to EBADMSG as well */
-}
Index: eeDiameter/p_ce.c
===================================================================
--- freeDiameter/p_ce.c	(revision 454)
+++ 	(revision )
@@ -1,972 +1,0 @@
-/*********************************************************************************************************
-* Software License Agreement (BSD License)                                                               *
-* Author: Sebastien Decugis <sdecugis@nict.go.jp>							 *
-*													 *
-* Copyright (c) 2010, 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 "fD.h"
-
-/* This file contains code to handle Capabilities Exchange messages (CER and CEA) and election process */
-
-/* Compilation option:
- USE_CEA_BROADCAST
- 	Define this to enable sending multiple copies of the CEA in case of SCTP connection.
-	This avoids a race condition when sending an application message over a different stream
-	than the CEA, it might be delivered first and thus ignored.
-*/
-
-/* Save a connection as peer's principal */
-static int set_peer_cnx(struct fd_peer * peer, struct cnxctx **cnx)
-{
-	CHECK_PARAMS( peer->p_cnxctx == NULL );
-	
-	/* Save the connection in peer */
-	peer->p_cnxctx = *cnx;
-	*cnx = NULL;
-	
-	/* Set the events to be sent to the PSM */
-	CHECK_FCT( fd_cnx_recv_setaltfifo(peer->p_cnxctx, peer->p_events) );
-	
-	/* Read the credentials if possible */
-	if (fd_cnx_getTLS(peer->p_cnxctx)) {
-		CHECK_FCT( fd_cnx_getcred(peer->p_cnxctx, &peer->p_hdr.info.runtime.pir_cert_list, &peer->p_hdr.info.runtime.pir_cert_list_size) );
-	}
-	
-	/* Read the endpoints, maybe used to reconnect to the peer later */
-	CHECK_FCT( fd_cnx_getremoteeps(peer->p_cnxctx, &peer->p_hdr.info.pi_endpoints) );
-	
-	/* Read the protocol */
-	peer->p_hdr.info.runtime.pir_proto = fd_cnx_getproto(peer->p_cnxctx);
-	
-	return 0;
-}
-
-/* Delete the peer connection, and cleanup associated information */
-void fd_p_ce_clear_cnx(struct fd_peer * peer, struct cnxctx ** cnx_kept)
-{
-	peer->p_hdr.info.runtime.pir_cert_list = NULL;
-	peer->p_hdr.info.runtime.pir_cert_list_size = 0;
-	peer->p_hdr.info.runtime.pir_proto = 0;
-	
-	if (peer->p_cnxctx) {
-		if (cnx_kept != NULL) {
-			*cnx_kept = peer->p_cnxctx;
-		} else {
-			fd_cnx_destroy(peer->p_cnxctx);
-		}
-		peer->p_cnxctx = NULL;
-	}
-}
-
-/* Election: compare the Diameter Ids, return true if the election is won */
-static __inline__ int election_result(struct fd_peer * peer)
-{
-	int ret = (strcasecmp(peer->p_hdr.info.pi_diamid, fd_g_config->cnf_diamid) < 0);
-	if (ret) {
-		TRACE_DEBUG(INFO, "Election WON against peer '%s'", peer->p_hdr.info.pi_diamid);
-	} else {
-		TRACE_DEBUG(INFO, "Election LOST against peer '%s'", peer->p_hdr.info.pi_diamid);
-	}
-	return ret;
-}
-
-/* Add AVPs about local information in a CER or CEA */
-static int add_CE_info(struct msg *msg, struct cnxctx * cnx, int isi_tls, int isi_none)
-{
-	struct dict_object * dictobj = NULL;
-	struct avp * avp = NULL;
-	union avp_value val;
-	struct fd_list *li;
-	
-	/* Add the Origin-* AVPs */
-	CHECK_FCT( fd_msg_add_origin ( msg, 1 ) );
-	
-	/* Find the model for Host-IP-Address AVP */
-	CHECK_FCT(  fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Host-IP-Address", &dictobj, ENOENT )  );
-		
-	/* Add the AVP(s) -- not sure what is the purpose... We could probably only add the primary one ? */
-	for (li = fd_g_config->cnf_endpoints.next; li != &fd_g_config->cnf_endpoints; li = li->next) {
-		struct fd_endpoint * ep = (struct fd_endpoint *)li;
-		
-		CHECK_FCT( fd_msg_avp_new ( dictobj, 0, &avp ) );
-		CHECK_FCT( fd_msg_avp_value_encode ( &ep->ss, avp ) );
-		CHECK_FCT( fd_msg_avp_add( msg, MSG_BRW_LAST_CHILD, avp ) );
-	}
-	
-	/* Vendor-Id, Product-Name, and Firmware-Revision AVPs */
-	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Vendor-Id", &dictobj, ENOENT )  );
-	CHECK_FCT( fd_msg_avp_new ( dictobj, 0, &avp ) );
-	val.u32 = MY_VENDOR_ID;
-	CHECK_FCT( fd_msg_avp_setvalue( avp, &val ) );
-	CHECK_FCT( fd_msg_avp_add( msg, MSG_BRW_LAST_CHILD, avp ) );
-	
-	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Product-Name", &dictobj, ENOENT )  );
-	CHECK_FCT( fd_msg_avp_new ( dictobj, 0, &avp ) );
-	val.os.data = (unsigned char *)FD_PROJECT_NAME;
-	val.os.len = strlen(FD_PROJECT_NAME);
-	CHECK_FCT( fd_msg_avp_setvalue( avp, &val ) );
-	CHECK_FCT( fd_msg_avp_add( msg, MSG_BRW_LAST_CHILD, avp ) );
-	
-	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Firmware-Revision", &dictobj, ENOENT )  );
-	CHECK_FCT( fd_msg_avp_new ( dictobj, 0, &avp ) );
-	val.u32 = (uint32_t)(FD_PROJECT_VERSION_MAJOR * 10000 + FD_PROJECT_VERSION_MINOR * 100 + FD_PROJECT_VERSION_REV);
-	CHECK_FCT( fd_msg_avp_setvalue( avp, &val ) );
-	CHECK_FCT( fd_msg_avp_add( msg, MSG_BRW_LAST_CHILD, avp ) );
-	
-	
-	/* Add the Inband-Security-Id AVP if needed */
-	if (isi_tls || isi_none) {
-		CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Inband-Security-Id", &dictobj, ENOENT )  );
-		
-		if (isi_none) {
-			CHECK_FCT( fd_msg_avp_new ( dictobj, 0, &avp ) );
-			val.u32 = ACV_ISI_NO_INBAND_SECURITY;
-			CHECK_FCT( fd_msg_avp_setvalue( avp, &val ) );
-			CHECK_FCT( fd_msg_avp_add( msg, MSG_BRW_LAST_CHILD, avp ) );
-		}
-		
-		if (isi_tls) {
-			CHECK_FCT( fd_msg_avp_new ( dictobj, 0, &avp ) );
-			val.u32 = ACV_ISI_TLS;
-			CHECK_FCT( fd_msg_avp_setvalue( avp, &val ) );
-			CHECK_FCT( fd_msg_avp_add( msg, MSG_BRW_LAST_CHILD, avp ) );
-		}
-	}
-	
-	/* List of local applications */
-	{
-		struct dict_object * dictobj_auth = NULL;
-		struct dict_object * dictobj_acct = NULL;
-		struct dict_object * dictobj_vid = NULL;
-		
-		CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Vendor-Specific-Application-Id", &dictobj, ENOENT )  );
-		CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Vendor-Id", &dictobj_vid, ENOENT )  );
-		CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Auth-Application-Id", &dictobj_auth, ENOENT )  );
-		CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Acct-Application-Id", &dictobj_acct, ENOENT )  );
-		
-		for (li = fd_g_config->cnf_apps.next; li != &fd_g_config->cnf_apps; li = li->next) {
-			struct fd_app * a = (struct fd_app *)(li);
-
-			if (a->flags.auth) {
-				CHECK_FCT( fd_msg_avp_new ( dictobj_auth, 0, &avp ) );
-				val.u32 = a->appid;
-				CHECK_FCT( fd_msg_avp_setvalue( avp, &val ) );
-				if (a->vndid != 0) {
-					struct avp * avp2 = NULL;
-					CHECK_FCT( fd_msg_avp_new ( dictobj, 0, &avp2 ) );
-					CHECK_FCT( fd_msg_avp_add( avp2, MSG_BRW_LAST_CHILD, avp ) );
-					avp = avp2;
-					CHECK_FCT( fd_msg_avp_new ( dictobj_vid, 0, &avp2 ) );
-					val.u32 = a->vndid;
-					CHECK_FCT( fd_msg_avp_setvalue( avp2, &val ) );
-					CHECK_FCT( fd_msg_avp_add( avp, MSG_BRW_LAST_CHILD, avp2 ) );
-				}
-				CHECK_FCT( fd_msg_avp_add( msg, MSG_BRW_LAST_CHILD, avp ) );
-			}
-			if (a->flags.acct) {
-				CHECK_FCT( fd_msg_avp_new ( dictobj_acct, 0, &avp ) );
-				val.u32 = a->appid;
-				CHECK_FCT( fd_msg_avp_setvalue( avp, &val ) );
-				if (a->vndid != 0) {
-					struct avp * avp2 = NULL;
-					CHECK_FCT( fd_msg_avp_new ( dictobj, 0, &avp2 ) );
-					CHECK_FCT( fd_msg_avp_add( avp2, MSG_BRW_LAST_CHILD, avp ) );
-					avp = avp2;
-					CHECK_FCT( fd_msg_avp_new ( dictobj_vid, 0, &avp2 ) );
-					val.u32 = a->vndid;
-					CHECK_FCT( fd_msg_avp_setvalue( avp2, &val ) );
-					CHECK_FCT( fd_msg_avp_add( avp, MSG_BRW_LAST_CHILD, avp2 ) );
-				}
-				CHECK_FCT( fd_msg_avp_add( msg, MSG_BRW_LAST_CHILD, avp ) );
-			}
-		}
-		
-		/* do not forget the relay application */
-		if (! fd_g_config->cnf_flags.no_fwd) {
-			CHECK_FCT( fd_msg_avp_new ( dictobj_auth, 0, &avp ) );
-			val.u32 = AI_RELAY;
-			CHECK_FCT( fd_msg_avp_setvalue( avp, &val ) );
-			CHECK_FCT( fd_msg_avp_add( msg, MSG_BRW_LAST_CHILD, avp ) );
-		}
-	}
-	
-	/* Add the list of supported vendors */
-	{
-		uint32_t * array = fd_dict_get_vendorid_list(fd_g_config->cnf_dict);
-		if (array) {
-			int i = 0;
-			CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Supported-Vendor-Id", &dictobj, ENOENT )  );
-			
-			while (array[i] != 0) {
-				CHECK_FCT( fd_msg_avp_new ( dictobj, 0, &avp ) );
-				val.u32 = array[i];
-				CHECK_FCT( fd_msg_avp_setvalue( avp, &val ) );
-				CHECK_FCT( fd_msg_avp_add( msg, MSG_BRW_LAST_CHILD, avp ) );
-				i++;
-			}
-			
-			free(array);
-		}
-	}
-	
-	return 0;
-}
-
-/* Remove any information saved from a previous CER/CEA exchange */
-static void cleanup_remote_CE_info(struct fd_peer * peer)
-{
-	free(peer->p_hdr.info.runtime.pir_realm);
-	peer->p_hdr.info.runtime.pir_realm = NULL;
-	peer->p_hdr.info.runtime.pir_vendorid = 0;
-	peer->p_hdr.info.runtime.pir_orstate = 0;
-	free(peer->p_hdr.info.runtime.pir_prodname);
-	peer->p_hdr.info.runtime.pir_prodname = NULL;
-	peer->p_hdr.info.runtime.pir_firmrev = 0;
-	peer->p_hdr.info.runtime.pir_relay = 0;
-	peer->p_hdr.info.runtime.pir_isi = 0;
-	while (!FD_IS_LIST_EMPTY(&peer->p_hdr.info.runtime.pir_apps)) {
-		struct fd_list * li = peer->p_hdr.info.runtime.pir_apps.next;
-		fd_list_unlink(li);
-		free(li);
-	}
-	
-	fd_ep_clearflags( &peer->p_hdr.info.pi_endpoints, EP_FL_ADV /* Remove previously advertised endpoints */ );
-}
-
-/* Extract information sent by the remote peer and save it in our peer structure */
-static int save_remote_CE_info(struct msg * msg, struct fd_peer * peer, char ** error_code, uint32_t *rc)
-{
-	struct avp * avp = NULL;
-	
-	cleanup_remote_CE_info(peer);
-	
-	CHECK_FCT( fd_msg_browse( msg, MSG_BRW_FIRST_CHILD, &avp, NULL) );
-	
-	/* Loop on all AVPs and save what we are interrested into */
-	while (avp) {
-		struct avp_hdr * hdr;
-
-		CHECK_FCT(  fd_msg_avp_hdr( avp, &hdr )  );
-
-		if (hdr->avp_flags & AVP_FLAG_VENDOR) {
-			/* Ignore all vendor-specific AVPs in CER/CEA because we don't support any currently */
-			TRACE_DEBUG(FULL, "Ignored a vendor AVP in CER / CEA");
-			fd_msg_dump_one(FULL, avp);
-			goto next;
-		}
-
-		switch (hdr->avp_code) {
-			case AC_RESULT_CODE: /* Result-Code */
-				if (hdr->avp_value == NULL) {
-					/* This is a sanity check */
-					TRACE_DEBUG(NONE, "Ignored an AVP with unset value in CER/CEA");
-					fd_msg_dump_one(NONE, avp);
-					ASSERT(0); /* To check if this really happens, and understand why... */
-					goto next;
-				}
-				
-				if (rc)
-					*rc = hdr->avp_value->u32;
-				break;
-		
-			case AC_ORIGIN_HOST: /* Origin-Host */
-				if (hdr->avp_value == NULL) {
-					/* This is a sanity check */
-					TRACE_DEBUG(NONE, "Ignored an AVP with unset value in CER/CEA");
-					fd_msg_dump_one(NONE, avp);
-					ASSERT(0); /* To check if this really happens, and understand why... */
-					goto next;
-				}
-				
-				/* We check that the value matches what we know, otherwise disconnect the peer */
-				/* here also, using strcasecmp on (supposed) UTF8 data might be bad idea... to be improved */
-				if (strncasecmp((char *)hdr->avp_value->os.data, peer->p_hdr.info.pi_diamid, hdr->avp_value->os.len)) {
-					TRACE_DEBUG(INFO, "Received a message with Origin-Host set to '%.*s' while expecting '%s'\n", 
-							hdr->avp_value->os.len, hdr->avp_value->os.data, peer->p_hdr.info.pi_diamid);
-					*error_code = "DIAMETER_UNKNOWN_PEER";
-					return EINVAL;
-				}
-
-				break;
-		
-			case AC_ORIGIN_REALM: /* Origin-Realm */
-				if (hdr->avp_value == NULL) {
-					/* This is a sanity check */
-					TRACE_DEBUG(NONE, "Ignored an AVP with unset value in CER/CEA");
-					fd_msg_dump_one(NONE, avp);
-					ASSERT(0); /* To check if this really happens, and understand why... */
-					goto next;
-				}
-				
-				/* In case of multiple AVPs */
-				if (peer->p_hdr.info.runtime.pir_realm) {
-					TRACE_DEBUG(INFO, "Ignored multiple instances of the Origin-Realm AVP");
-					goto next;
-				}
-				
-				/* Save the value -- we don't change the case to avoid risking breaking UTF-8 with poor tolower() impls. */
-				CHECK_MALLOC(  peer->p_hdr.info.runtime.pir_realm = calloc( hdr->avp_value->os.len + 1, 1 )  );
-				memcpy(peer->p_hdr.info.runtime.pir_realm, hdr->avp_value->os.data, hdr->avp_value->os.len);
-				break;
-
-			case AC_HOST_IP_ADDRESS: /* Host-IP-Address */
-				if (hdr->avp_value == NULL) {
-					/* This is a sanity check */
-					TRACE_DEBUG(NONE, "Ignored an AVP with unset value in CER/CEA");
-					fd_msg_dump_one(NONE, avp);
-					ASSERT(0); /* To check if this really happens, and understand why... */
-					goto next;
-				}
-				{
-					sSS	ss;
-
-					/* Get the sockaddr value */
-					memset(&ss, 0, sizeof(ss));
-					CHECK_FCT( fd_msg_avp_value_interpret( avp, &ss) );
-
-					/* Save this endpoint in the list as advertized */
-					CHECK_FCT( fd_ep_add_merge( &peer->p_hdr.info.pi_endpoints, (sSA *)&ss, sizeof(sSS), EP_FL_ADV ) );
-				}
-				break;
-
-			case AC_VENDOR_ID: /* Vendor-Id */
-				if (hdr->avp_value == NULL) {
-					/* This is a sanity check */
-					TRACE_DEBUG(NONE, "Ignored an AVP with unset value in CER/CEA");
-					fd_msg_dump_one(NONE, avp);
-					ASSERT(0); /* To check if this really happens, and understand why... */
-					goto next;
-				}
-				
-				/* In case of multiple AVPs */
-				if (peer->p_hdr.info.runtime.pir_vendorid) {
-					TRACE_DEBUG(INFO, "Ignored multiple instances of the Vendor-Id AVP");
-					goto next;
-				}
-				
-				peer->p_hdr.info.runtime.pir_vendorid = hdr->avp_value->u32;
-				break;
-
-			case AC_PRODUCT_NAME: /* Product-Name */
-				if (hdr->avp_value == NULL) {
-					/* This is a sanity check */
-					TRACE_DEBUG(NONE, "Ignored an AVP with unset value in CER/CEA");
-					fd_msg_dump_one(NONE, avp);
-					ASSERT(0); /* To check if this really happens, and understand why... */
-					goto next;
-				}
-				
-				/* In case of multiple AVPs */
-				if (peer->p_hdr.info.runtime.pir_prodname) {
-					TRACE_DEBUG(INFO, "Ignored multiple instances of the Product-Name AVP");
-					goto next;
-				}
-
-				CHECK_MALLOC( peer->p_hdr.info.runtime.pir_prodname = calloc( hdr->avp_value->os.len + 1, 1 )  );
-				memcpy(peer->p_hdr.info.runtime.pir_prodname, hdr->avp_value->os.data, hdr->avp_value->os.len);
-				break;
-
-			case AC_ORIGIN_STATE_ID: /* Origin-State-Id */
-				if (hdr->avp_value == NULL) {
-					/* This is a sanity check */
-					TRACE_DEBUG(NONE, "Ignored an AVP with unset value in CER/CEA");
-					fd_msg_dump_one(NONE, avp);
-					ASSERT(0); /* To check if this really happens, and understand why... */
-					goto next;
-				}
-				
-				/* In case of multiple AVPs */
-				if (peer->p_hdr.info.runtime.pir_orstate) {
-					TRACE_DEBUG(INFO, "Ignored multiple instances of the Origin-State-Id AVP");
-					goto next;
-				}
-				
-				peer->p_hdr.info.runtime.pir_orstate = hdr->avp_value->u32;
-				break;
-
-			case AC_SUPPORTED_VENDOR_ID: /* Supported-Vendor-Id */
-				if (hdr->avp_value == NULL) {
-					/* This is a sanity check */
-					TRACE_DEBUG(NONE, "Ignored an AVP with unset value in CER/CEA");
-					fd_msg_dump_one(NONE, avp);
-					ASSERT(0); /* To check if this really happens, and understand why... */
-					goto next;
-				}
-				
-				TRACE_DEBUG(FULL, "'%s' supports a subset of vendor %d features.", peer->p_hdr.info.pi_diamid, hdr->avp_value->u32);
-				break;
-
-			case AC_VENDOR_SPECIFIC_APPLICATION_ID: /* Vendor-Specific-Application-Id (grouped)*/
-				{
-					struct avp * inavp = NULL;
-					application_id_t aid = 0;
-					vendor_id_t vid = 0;
-					int auth = 0;
-					int acct = 0;
-
-					/* get the first child AVP */
-					CHECK_FCT(  fd_msg_browse(avp, MSG_BRW_FIRST_CHILD, &inavp, NULL)  );
-
-					while (inavp) {
-						struct avp_hdr * inhdr;
-						CHECK_FCT(  fd_msg_avp_hdr( inavp, &inhdr )  );
-
-						if (inhdr->avp_flags & AVP_FLAG_VENDOR) {
-							TRACE_DEBUG(FULL, "Ignored a vendor AVP inside Vendor-Specific-Application-Id AVP");
-							fd_msg_dump_one(FULL, avp);
-							goto innext;
-						}
-
-						if (inhdr->avp_value == NULL) {
-							/* This is a sanity check */
-							TRACE_DEBUG(NONE, "Ignored an AVP with unset value in CER/CEA");
-							fd_msg_dump_one(NONE, avp);
-							ASSERT(0); /* To check if this really happens, and understand why... */
-							goto innext;
-						}
-						switch (inhdr->avp_code) {
-							case AC_VENDOR_ID: /* Vendor-Id */
-								vid = inhdr->avp_value->u32;
-								break;
-							case AC_AUTH_APPLICATION_ID: /* Auth-Application-Id */
-								aid = inhdr->avp_value->u32;
-								auth += 1;
-								break;
-							case AC_ACCT_APPLICATION_ID: /* Acct-Application-Id */
-								aid = inhdr->avp_value->u32;
-								acct += 1;
-								break;
-							/* ignore other AVPs */
-						}
-
-					innext:			
-						/* Go to next in AVP */
-						CHECK_FCT( fd_msg_browse(inavp, MSG_BRW_NEXT, &inavp, NULL) );
-					}
-					
-					if (auth + acct != 1) {
-						TRACE_DEBUG(FULL, "Invalid Vendor-Specific-Application-Id AVP received, ignored");
-						fd_msg_dump_one(FULL, avp);
-					} else {
-						/* Add an entry in the list */
-						CHECK_FCT( fd_app_merge(&peer->p_hdr.info.runtime.pir_apps, aid, vid, auth, acct) );
-					}
-				}
-				break;
-
-			case AC_AUTH_APPLICATION_ID: /* Auth-Application-Id */
-				if (hdr->avp_value == NULL) {
-					/* This is a sanity check */
-					TRACE_DEBUG(NONE, "Ignored an AVP with unset value in CER/CEA");
-					fd_msg_dump_one(NONE, avp);
-					ASSERT(0); /* To check if this really happens, and understand why... */
-					goto next;
-				}
-				
-				if (hdr->avp_value->u32 == AI_RELAY) {
-					peer->p_hdr.info.runtime.pir_relay = 1;
-				} else {
-					CHECK_FCT( fd_app_merge(&peer->p_hdr.info.runtime.pir_apps, hdr->avp_value->u32, 0, 1, 0) );
-				}
-				break;
-
-			case AC_ACCT_APPLICATION_ID: /* Acct-Application-Id */
-				if (hdr->avp_value == NULL) {
-					/* This is a sanity check */
-					TRACE_DEBUG(NONE, "Ignored an AVP with unset value in CER/CEA");
-					fd_msg_dump_one(NONE, avp);
-					ASSERT(0); /* To check if this really happens, and understand why... */
-					goto next;
-				}
-				
-				if (hdr->avp_value->u32 == AI_RELAY) {
-					peer->p_hdr.info.runtime.pir_relay = 1;
-				} else {
-					/* Not clear if the relay application can be inside this AVP... */
-					CHECK_FCT( fd_app_merge(&peer->p_hdr.info.runtime.pir_apps, hdr->avp_value->u32, 0, 0, 1) );
-				}
-				break;
-
-			case AC_FIRMWARE_REVISION: /* Firmware-Revision */
-				if (hdr->avp_value == NULL) {
-					/* This is a sanity check */
-					TRACE_DEBUG(NONE, "Ignored an AVP with unset value in CER/CEA");
-					fd_msg_dump_one(NONE, avp);
-					ASSERT(0); /* To check if this really happens, and understand why... */
-					goto next;
-				}
-				
-				peer->p_hdr.info.runtime.pir_firmrev = hdr->avp_value->u32;
-				break;
-
-			case AC_INBAND_SECURITY_ID: /* Inband-Security-Id */
-				if (hdr->avp_value == NULL) {
-					/* This is a sanity check */
-					TRACE_DEBUG(NONE, "Ignored an AVP with unset value in CER/CEA");
-					fd_msg_dump_one(NONE, avp);
-					ASSERT(0); /* To check if this really happens, and understand why... */
-					goto next;
-				}
-				ASSERT( hdr->avp_value->u32 < 32 ); /* if false, we have to change the code bellow */
-				peer->p_hdr.info.runtime.pir_isi |= (1 << hdr->avp_value->u32);
-				break;
-		}
-
-next:			
-		/* Go to next AVP */
-		CHECK_FCT( fd_msg_browse(avp, MSG_BRW_NEXT, &avp, NULL) );
-	}
-	
-	return 0;
-}
-
-/* Create a CER message for sending */ 
-static int create_CER(struct fd_peer * peer, struct cnxctx * cnx, struct msg ** cer)
-{
-	int isi_tls = 0;
-	int isi_none = 0;
-	
-	/* Find CER dictionary object and create an instance */
-	CHECK_FCT( fd_msg_new ( fd_dict_cmd_CER, MSGFL_ALLOC_ETEID, cer ) );
-	
-	/* Do we need Inband-Security-Id AVPs ? */
-	if (!fd_cnx_getTLS(cnx)) {
-		isi_none = peer->p_hdr.info.config.pic_flags.sec & PI_SEC_NONE; /* we add it event if the peer does not use the old mechanism */
-		isi_tls  = peer->p_hdr.info.config.pic_flags.sec & PI_SEC_TLS_OLD;
-	}
-	
-	/* Add the information about the local peer */
-	CHECK_FCT( add_CE_info(*cer, cnx, isi_tls, isi_none) );
-	
-	/* Done! */
-	return 0;
-}
-
-
-/* Continue with the initiator side */
-static int to_waitcea(struct fd_peer * peer, struct cnxctx * cnx)
-{
-	/* We sent a CER on the connection, set the event queue so that we receive the CEA */
-	CHECK_FCT( set_peer_cnx(peer, &cnx) );
-	
-	/* Change state and reset the timer */
-	CHECK_FCT( fd_psm_change_state(peer, STATE_WAITCEA) );
-	fd_psm_next_timeout(peer, 0, CEA_TIMEOUT);
-	
-	return 0;
-}
-
-/* Reject an incoming connection attempt */
-static void receiver_reject(struct cnxctx * recv_cnx, struct msg ** cer, char * rescode, char * errormsg)
-{
-	/* Create and send the CEA with appropriate error code */
-	CHECK_FCT_DO( fd_msg_new_answer_from_req ( fd_g_config->cnf_dict, cer, MSGFL_ANSW_ERROR ), goto destroy );
-	CHECK_FCT_DO( fd_msg_rescode_set(*cer, rescode, errormsg, NULL, 1 ), goto destroy );
-	CHECK_FCT_DO( fd_out_send(cer, recv_cnx, NULL, FD_CNX_ORDERED), goto destroy );
-	
-	/* And now destroy this connection */
-destroy:
-	fd_cnx_destroy(recv_cnx);
-	if (*cer) {
-		fd_msg_free(*cer);
-		*cer = NULL;
-	}
-}
-
-/* We have established a new connection to the remote peer, send CER and eventually process the election */
-int fd_p_ce_handle_newcnx(struct fd_peer * peer, struct cnxctx * initiator)
-{
-	struct msg * cer = NULL;
-	
-	/* Send CER on the new connection */
-	CHECK_FCT( create_CER(peer, initiator, &cer) );
-	CHECK_FCT( fd_out_send(&cer, initiator, peer, FD_CNX_ORDERED) );
-	
-	/* Are we doing an election ? */
-	fd_cpu_flush_cache();
-	if (peer->p_hdr.info.runtime.pir_state == STATE_WAITCNXACK_ELEC) {
-		if (election_result(peer)) {
-			/* Close initiator connection */
-			fd_cnx_destroy(initiator);
-
-			/* Process with the receiver side */
-			CHECK_FCT( fd_p_ce_process_receiver(peer) );
-
-		} else {
-
-			/* Answer an ELECTION LOST to the receiver side */
-			receiver_reject(peer->p_receiver, &peer->p_cer, "ELECTION_LOST", NULL);
-			peer->p_receiver = NULL;
-			CHECK_FCT( to_waitcea(peer, initiator) );
-		}
-	} else {
-		/* No election (yet) */
-		CHECK_FCT( to_waitcea(peer, initiator) );
-	}
-	
-	return 0;
-}
-
-/* We have received a Capabilities Exchange message on the peer connection */
-int fd_p_ce_msgrcv(struct msg ** msg, int req, struct fd_peer * peer)
-{
-	char * ec;
-	uint32_t rc = 0;
-	TRACE_ENTRY("%p %p", msg, peer);
-	CHECK_PARAMS( msg && *msg && CHECK_PEER(peer) );
-	
-	/* The only valid situation where we are called is in WAITCEA and we receive a CEA (we may have won an election) */
-	
-	/* Note : to implement Capabilities Update, we would need to change here */
-	
-	/* If it is a CER, just reply an error */
-	if (req) {
-		/* Create the error message */
-		CHECK_FCT( fd_msg_new_answer_from_req ( fd_g_config->cnf_dict, msg, MSGFL_ANSW_ERROR ) );
-		
-		/* Set the error code */
-		CHECK_FCT( fd_msg_rescode_set(*msg, "DIAMETER_COMMAND_UNSUPPORTED", "No CER allowed in current state", NULL, 1 ) );
-
-		/* msg now contains an answer message to send back */
-		CHECK_FCT_DO( fd_out_send(msg, NULL, peer, FD_CNX_ORDERED), /* In case of error the message has already been dumped */ );
-	}
-	
-	/* If the state is not WAITCEA, just discard the message */
-	fd_cpu_flush_cache();
-	if (req || (peer->p_hdr.info.runtime.pir_state != STATE_WAITCEA)) {
-		if (*msg) {
-			fd_log_debug("Received CER/CEA message while in state '%s', discarded.\n", STATE_STR(peer->p_hdr.info.runtime.pir_state));
-			fd_msg_dump_walk(NONE, *msg);
-			CHECK_FCT_DO( fd_msg_free(*msg), /* continue */);
-			*msg = NULL;
-		}
-		
-		return 0;
-	}
-	
-	/* Save info from the CEA into the peer */
-	CHECK_FCT_DO( save_remote_CE_info(*msg, peer, &ec, &rc), goto cleanup );
-	
-	/* Dispose of the message, we don't need it anymore */
-	CHECK_FCT_DO( fd_msg_free(*msg), /* continue */ );
-	*msg = NULL;
-	
-	/* Check the Result-Code */
-	switch (rc) {
-		case ER_DIAMETER_SUCCESS:
-			/* No problem, we can continue */
-			break;
-			
-		case ER_DIAMETER_TOO_BUSY:
-			/* Retry later */
-			TRACE_DEBUG(INFO, "Peer %s replied a CEA with Result-Code AVP DIAMETER_TOO_BUSY, will retry later.", peer->p_hdr.info.pi_diamid);
-			fd_psm_cleanup(peer, 0);
-			fd_psm_next_timeout(peer, 0, 300);
-			return 0;
-		
-		case ER_ELECTION_LOST:
-			/* Ok, just wait for a little while for the CER to be processed on the other connection. */
-			TRACE_DEBUG(FULL, "Peer %s replied a CEA with Result-Code AVP ELECTION_LOST, waiting for events.", peer->p_hdr.info.pi_diamid);
-			return 0;
-		
-		default:
-			/* In any other case, we abort all attempts to connect to this peer */
-			TRACE_DEBUG(INFO, "Peer %s replied a CEA with Result-Code AVP %d, aborting connection attempts.", peer->p_hdr.info.pi_diamid, rc);
-			return EINVAL;
-	}
-	
-	/* Handshake if needed, start clear otherwise */
-	if ( ! fd_cnx_getTLS(peer->p_cnxctx) ) {
-		int todo = peer->p_hdr.info.config.pic_flags.sec & peer->p_hdr.info.runtime.pir_isi ;
-		/* Special case: if the peer did not send a ISI AVP */
-		if (peer->p_hdr.info.runtime.pir_isi == 0)
-			todo = peer->p_hdr.info.config.pic_flags.sec;
-		
-		if (todo == PI_SEC_NONE) {
-			/* Ok for clear connection */
-			TRACE_DEBUG(INFO, "No TLS protection negotiated with peer '%s'.", peer->p_hdr.info.pi_diamid);
-			CHECK_FCT( fd_cnx_start_clear(peer->p_cnxctx, 1) );
-		} else {
-			
-			fd_psm_change_state(peer, STATE_OPEN_HANDSHAKE);
-			CHECK_FCT_DO( fd_cnx_handshake(peer->p_cnxctx, GNUTLS_CLIENT, peer->p_hdr.info.config.pic_priority, NULL),
-				{
-					/* Handshake failed ...  */
-					fd_log_debug("TLS Handshake failed with peer '%s', resetting the connection\n", peer->p_hdr.info.pi_diamid);
-					goto cleanup;
-				} );
-
-			/* Retrieve the credentials */
-			CHECK_FCT( fd_cnx_getcred(peer->p_cnxctx, &peer->p_hdr.info.runtime.pir_cert_list, &peer->p_hdr.info.runtime.pir_cert_list_size) );
-		}
-	}
-	
-	/* Move to next state */
-	if (peer->p_flags.pf_cnx_pb) {
-		fd_psm_change_state(peer, STATE_REOPEN );
-		CHECK_FCT( fd_p_dw_reopen(peer) );
-	} else {
-		fd_psm_change_state(peer, STATE_OPEN );
-		fd_psm_next_timeout(peer, 1, peer->p_hdr.info.config.pic_twtimer ?: fd_g_config->cnf_timer_tw);
-	}
-	
-	return 0;
-	
-cleanup:
-	fd_p_ce_clear_cnx(peer, NULL);
-
-	/* Send the error to the peer */
-	CHECK_FCT( fd_event_send(peer->p_events, FDEVP_CNX_ERROR, 0, NULL) );
-
-	return 0;
-}
-
-/* Handle the receiver side to go to OPEN state (any election is resolved) */
-int fd_p_ce_process_receiver(struct fd_peer * peer)
-{
-	char * ec = NULL;
-	struct msg * msg = NULL;
-	int isi = 0;
-	int fatal = 0;
-	
-	TRACE_ENTRY("%p", peer);
-	
-	CHECK_FCT( set_peer_cnx(peer, &peer->p_receiver) );
-	msg = peer->p_cer;
-	peer->p_cer = NULL;
-	
-	/* Parse the content of the received CER */
-	CHECK_FCT_DO( save_remote_CE_info(msg, peer, &ec, NULL), goto error_abort );
-	
-	/* Validate the peer if needed */
-	if (peer->p_flags.pf_responder) {
-		int res = fd_peer_validate( peer );
-		if (res < 0) {
-			TRACE_DEBUG(INFO, "Rejected CER from peer '%s', validation failed (returning DIAMETER_UNKNOWN_PEER).\n", peer->p_hdr.info.pi_diamid);
-			ec = "DIAMETER_UNKNOWN_PEER";
-			goto error_abort;
-		}
-		CHECK_FCT( res );
-	}
-	
-	/* Check if we have common applications */
-	if ( fd_g_config->cnf_flags.no_fwd && (! peer->p_hdr.info.runtime.pir_relay) ) {
-		int got_common;
-		CHECK_FCT( fd_app_check_common( &fd_g_config->cnf_apps, &peer->p_hdr.info.runtime.pir_apps, &got_common) );
-		if (!got_common) {
-			TRACE_DEBUG(INFO, "No common application with peer '%s', sending DIAMETER_NO_COMMON_APPLICATION", peer->p_hdr.info.pi_diamid);
-			ec = "DIAMETER_NO_COMMON_APPLICATION";
-			fatal = 1;
-			goto error_abort;
-		}
-	}
-	
-	/* Do we agree on ISI ? */
-	if ( ! fd_cnx_getTLS(peer->p_cnxctx) ) {
-		
-		/* In case of responder, the validate callback must have set the config.pic_flags.sec value already */
-	
-		/* First case: we are not using old mechanism: ISI are deprecated, we ignore it. */
-		if ( ! (peer->p_hdr.info.config.pic_flags.sec & PI_SEC_TLS_OLD)) {
-			/* Just check then that the peer configuration allows for IPsec protection */
-			if (peer->p_hdr.info.config.pic_flags.sec & PI_SEC_NONE) {
-				isi = PI_SEC_NONE;
-			} else {
-				/* otherwise, we should have already been protected. Reject */
-				TRACE_DEBUG(INFO, "Non TLS-protected CER/CEA exchanges are not allowed with this peer, rejecting.");
-			}
-		} else {
-			/* The old mechanism is allowed with this peer. Now, look into the ISI AVP values */
-			
-			/* In case no ISI was present anyway: */
-			if (!peer->p_hdr.info.runtime.pir_isi) {
-				TRACE_DEBUG(INFO, "Inband-Security-Id AVP is missing in received CER.");
-				if (peer->p_hdr.info.config.pic_flags.sec & PI_SEC_NONE) {
-					isi = PI_SEC_NONE;
-					TRACE_DEBUG(INFO, "IPsec protection allowed by configuration, allowing this mechanism to be used.");
-				} else {
-					/* otherwise, we should have already been protected. Reject */
-					TRACE_DEBUG(INFO, "Rejecting the peer connection (please allow IPsec here or configure TLS in the remote peer).");
-				}
-			} else {
-				/* OK, the remote peer did send the ISI AVP. */
-				if ((peer->p_hdr.info.config.pic_flags.sec & PI_SEC_NONE) && (peer->p_hdr.info.runtime.pir_isi & PI_SEC_NONE)) {
-					/* We have allowed IPsec */
-					isi = PI_SEC_NONE;
-				} else if (peer->p_hdr.info.runtime.pir_isi & PI_SEC_TLS_OLD) {
-					/* We can agree on TLS */
-					isi = PI_SEC_TLS_OLD;
-				} else {
-					TRACE_DEBUG(INFO, "Remote peer requested IPsec protection, but local configuration forbids it.");
-				}
-			}
-		}
-	
-		/* If we did not find an agreement */
-		if (!isi) {
-			TRACE_DEBUG(INFO, "No common security mechanism with '%s', sending DIAMETER_NO_COMMON_SECURITY", peer->p_hdr.info.pi_diamid);
-			ec = "DIAMETER_NO_COMMON_SECURITY";
-			fatal = 1;
-			goto error_abort;
-		}
-		
-		/* Do not send the ISI IPsec if we are using the new mechanism */
-		if ((isi == PI_SEC_NONE) && (! (peer->p_hdr.info.config.pic_flags.sec & PI_SEC_TLS_OLD)))
-			isi = 0;
-	}
-	
-	/* Reply a CEA */
-	CHECK_FCT( fd_msg_new_answer_from_req ( fd_g_config->cnf_dict, &msg, 0 ) );
-	CHECK_FCT( fd_msg_rescode_set(msg, "DIAMETER_SUCCESS", NULL, NULL, 0 ) );
-	CHECK_FCT( add_CE_info(msg, peer->p_cnxctx, isi & PI_SEC_TLS_OLD, isi & PI_SEC_NONE) );
-#ifdef USE_CEA_BROADCAST
-	CHECK_FCT( fd_out_send(&msg, peer->p_cnxctx, peer, (isi & PI_SEC_TLS_OLD) ? FD_CNX_ORDERED : FD_CNX_BROADCAST) ); /* Broadcast in order to avoid further messages sent over a different stream be delivered first... */
-#else /* USE_CEA_BROADCAST */
-	CHECK_FCT( fd_out_send(&msg, peer->p_cnxctx, peer, FD_CNX_ORDERED ) );
-#endif /* USE_CEA_BROADCAST */
-	
-	/* Handshake if needed */
-	if (isi & PI_SEC_TLS_OLD) {
-		fd_psm_change_state(peer, STATE_OPEN_HANDSHAKE);
-		CHECK_FCT_DO( fd_cnx_handshake(peer->p_cnxctx, GNUTLS_SERVER, peer->p_hdr.info.config.pic_priority, NULL),
-			{
-				/* Handshake failed ...  */
-				fd_log_debug("TLS Handshake failed with peer '%s', resetting the connection\n", peer->p_hdr.info.pi_diamid);
-				goto cleanup;
-			} );
-		
-		/* Retrieve the credentials */
-		CHECK_FCT( fd_cnx_getcred(peer->p_cnxctx, &peer->p_hdr.info.runtime.pir_cert_list, &peer->p_hdr.info.runtime.pir_cert_list_size) );
-		
-		/* Call second validation callback if needed */
-		if (peer->p_cb2) {
-			TRACE_DEBUG(FULL, "Calling second validation callback for %s", peer->p_hdr.info.pi_diamid);
-			CHECK_FCT_DO( (*peer->p_cb2)( &peer->p_hdr.info ),
-				{
-					TRACE_DEBUG(INFO, "Validation callback rejected the peer %s after handshake", peer->p_hdr.info.pi_diamid);
-					CHECK_FCT( fd_psm_terminate( peer, "DO_NOT_WANT_TO_TALK_TO_YOU" ) );
-					return 0;
-				}  );
-		}
-		
-	} else {
-		if ( ! fd_cnx_getTLS(peer->p_cnxctx) ) {
-			TRACE_DEBUG(INFO, "No TLS protection negotiated with peer '%s'.", peer->p_hdr.info.pi_diamid);
-			CHECK_FCT( fd_cnx_start_clear(peer->p_cnxctx, 1) );
-		}
-	}
-		
-	/* Move to OPEN or REOPEN state */
-	if (peer->p_flags.pf_cnx_pb) {
-		fd_psm_change_state(peer, STATE_REOPEN );
-		CHECK_FCT( fd_p_dw_reopen(peer) );
-	} else {
-		fd_psm_change_state(peer, STATE_OPEN );
-		fd_psm_next_timeout(peer, 1, peer->p_hdr.info.config.pic_twtimer ?: fd_g_config->cnf_timer_tw);
-	}
-	
-	return 0;
-
-error_abort:
-	if (ec) {
-		/* Create the error message */
-		CHECK_FCT( fd_msg_new_answer_from_req ( fd_g_config->cnf_dict, &msg, MSGFL_ANSW_ERROR ) );
-
-		/* Set the error code */
-		CHECK_FCT( fd_msg_rescode_set(msg, ec, NULL, NULL, 1 ) );
-
-		/* msg now contains an answer message to send back */
-		CHECK_FCT_DO( fd_out_send(&msg, peer->p_cnxctx, peer, FD_CNX_ORDERED), /* In case of error the message has already been dumped */ );
-	}
-	
-cleanup:
-	if (msg) {
-		fd_msg_free(msg);
-	}
-	fd_p_ce_clear_cnx(peer, NULL);
-
-	/* Send the error to the peer */
-	CHECK_FCT( fd_event_send(peer->p_events, fatal ? FDEVP_TERMINATE : FDEVP_CNX_ERROR, 0, NULL) );
-
-	return 0;
-}
-
-/* We have received a CER on a new connection for this peer */
-int fd_p_ce_handle_newCER(struct msg ** msg, struct fd_peer * peer, struct cnxctx ** cnx, int valid)
-{
-	fd_cpu_flush_cache();
-	switch (peer->p_hdr.info.runtime.pir_state) {
-		case STATE_CLOSED:
-			peer->p_receiver = *cnx;
-			*cnx = NULL;
-			peer->p_cer = *msg;
-			*msg = NULL;
-			CHECK_FCT( fd_p_ce_process_receiver(peer) );
-			break;
-
-		case STATE_WAITCNXACK:
-			/* Save the parameters in the peer, move to STATE_WAITCNXACK_ELEC */
-			peer->p_receiver = *cnx;
-			*cnx = NULL;
-			peer->p_cer = *msg;
-			*msg = NULL;
-			CHECK_FCT( fd_psm_change_state(peer, STATE_WAITCNXACK_ELEC) );
-			break;
-			
-		case STATE_WAITCEA:
-			if (election_result(peer)) {
-				
-				/* Close initiator connection (was already set as principal) */
-				fd_p_ce_clear_cnx(peer, NULL);
-				
-				/* and go on with the receiver side */
-				peer->p_receiver = *cnx;
-				*cnx = NULL;
-				peer->p_cer = *msg;
-				*msg = NULL;
-				CHECK_FCT( fd_p_ce_process_receiver(peer) );
-
-			} else {
-
-				/* Answer an ELECTION LOST to the receiver side and continue */
-				receiver_reject(*cnx, msg, "ELECTION_LOST", "Please answer my CER instead, you won the election.");
-				*cnx = NULL;
-			}
-			break;
-
-		default:
-			receiver_reject(*cnx, msg, "DIAMETER_UNABLE_TO_COMPLY", "Invalid state to receive a new connection attempt");
-			*cnx = NULL;
-	}
-				
-	return 0;
-}
Index: eeDiameter/p_cnx.c
===================================================================
--- freeDiameter/p_cnx.c	(revision 379)
+++ 	(revision )
@@ -1,329 +1,0 @@
-/*********************************************************************************************************
-* Software License Agreement (BSD License)                                                               *
-* Author: Sebastien Decugis <sdecugis@nict.go.jp>							 *
-*													 *
-* Copyright (c) 2010, 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 "fD.h"
-
-/* This file contains code used by a peer state machine to initiate a connection to remote peer */
-
-struct next_conn {
-	struct fd_list	chain;
-	int		proto;	/* Protocol of the next attempt */
-	union {
-		sSS	ss;	/* The address, only for TCP */
-		sSA4	sin;
-		sSA6	sin6;
-	};
-	uint16_t	port;	/* The port, for SCTP (included in ss for TCP) */
-	int		dotls;	/* Handshake TLS after connection ? */
-};
-
-static __inline__ void failed_connection_attempt(struct fd_peer * peer)
-{
-	/* Simply remove the first item in the list if not empty */
-	if (! FD_IS_LIST_EMPTY(&peer->p_connparams) ) {
-		struct fd_list * li = peer->p_connparams.next;
-		fd_list_unlink(li);
-		free(li);
-	}
-}
-
-static void empty_connection_list(struct fd_peer * peer)
-{
-	/* Remove all items */
-	while (!FD_IS_LIST_EMPTY(&peer->p_connparams)) {
-		failed_connection_attempt(peer);
-	}
-}
-
-static int prepare_connection_list(struct fd_peer * peer)
-{
-	struct fd_list * li, *last_prio;
-	struct next_conn   * new; 
-	
-	uint16_t	port_no; /* network order */
-	int		dotls_immediate;
-	
-	TRACE_ENTRY("%p", peer);
-	 
-	/* Resolve peer address(es) if needed */
-	if (FD_IS_LIST_EMPTY(&peer->p_hdr.info.pi_endpoints)) {
-		struct addrinfo hints, *ai, *aip;
-		int ret;
-
-		memset(&hints, 0, sizeof(hints));
-		hints.ai_flags = AI_ADDRCONFIG;
-		ret = getaddrinfo(peer->p_hdr.info.pi_diamid, NULL, &hints, &ai);
-		if (ret) {
-			fd_log_debug("Unable to resolve address for peer '%s' (%s), aborting\n", peer->p_hdr.info.pi_diamid, gai_strerror(ret));
-			if (ret != EAI_AGAIN)
-				fd_psm_terminate( peer, NULL );
-			return 0;
-		}
-		
-		for (aip = ai; aip != NULL; aip = aip->ai_next) {
-			CHECK_FCT( fd_ep_add_merge( &peer->p_hdr.info.pi_endpoints, aip->ai_addr, aip->ai_addrlen, EP_FL_DISC ) );
-		}
-		freeaddrinfo(ai);
-	}
-	
-	/* Remove addresses from unwanted family */
-	if (peer->p_hdr.info.config.pic_flags.pro3) {
-		CHECK_FCT( fd_ep_filter_family(
-					&peer->p_hdr.info.pi_endpoints, 
-					(peer->p_hdr.info.config.pic_flags.pro3 == PI_P3_IP) ? 
-						AF_INET 
-						: AF_INET6));
-	}
-	if (fd_g_config->cnf_flags.no_ip4) {
-		CHECK_FCT( fd_ep_filter_family(
-					&peer->p_hdr.info.pi_endpoints, 
-					AF_INET6));
-	}
-	if (fd_g_config->cnf_flags.no_ip6) {
-		CHECK_FCT( fd_ep_filter_family(
-					&peer->p_hdr.info.pi_endpoints, 
-					AF_INET));
-	}
-	
-	/* Remove any local address that would be here, it should not happen but it does sometimes... */
-	CHECK_FCT( fd_ep_filter_list(&peer->p_hdr.info.pi_endpoints, &fd_g_config->cnf_endpoints) );
-	
-	/* Now check we have at least one address to attempt */
-	if (FD_IS_LIST_EMPTY(&peer->p_hdr.info.pi_endpoints)) {
-		fd_log_debug("No address %savailable to connect to peer '%s', aborting\n", peer->p_hdr.info.config.pic_flags.pro3 ? "in the configured family " : "", peer->p_hdr.info.pi_diamid);
-		fd_psm_terminate( peer, NULL );
-		return 0;
-	}
-	
-	/* Cleanup any previous list */
-	empty_connection_list(peer);
-	
-	/* Prepare the parameters */
-	if ((peer->p_hdr.info.config.pic_flags.sec != PI_SEC_DEFAULT) || (fd_g_config->cnf_flags.tls_alg)) {
-		dotls_immediate = 0;
-		port_no = htons(peer->p_hdr.info.config.pic_port ?: fd_g_config->cnf_port);
-	} else {
-		dotls_immediate = 1;
-		port_no = htons(peer->p_hdr.info.config.pic_port ?: fd_g_config->cnf_port_tls);
-	}
-	
-	last_prio = &peer->p_connparams;
-	
-	/* Create TCP parameters unless specified otherwise */
-	if ((!fd_g_config->cnf_flags.no_tcp) && (peer->p_hdr.info.config.pic_flags.pro4 != PI_P4_SCTP)) {
-		for (li = peer->p_hdr.info.pi_endpoints.next; li != &peer->p_hdr.info.pi_endpoints; li = li->next) {
-			struct fd_endpoint * ep = (struct fd_endpoint *)li;
-			
-			CHECK_MALLOC( new = malloc(sizeof(struct next_conn)) );
-			memset(new, 0, sizeof(struct next_conn));
-			fd_list_init(&new->chain, new);
-			
-			new->proto = IPPROTO_TCP;
-			
-			memcpy( &new->ss, &ep->ss, sizeof(sSS) );
-			switch (new->ss.ss_family) {
-				case AF_INET:
-					new->sin.sin_port = port_no;
-					break;
-				case AF_INET6:
-					new->sin6.sin6_port = port_no;
-					break;
-				default:
-					free(new);
-					continue; /* Move to the next endpoint */
-			}
-			
-			new->dotls = dotls_immediate;
-			
-			/* Add the new entry to the appropriate position (conf and disc go first) */
-			if (ep->flags & (EP_FL_CONF | EP_FL_DISC)) {
-				fd_list_insert_after(last_prio, &new->chain);
-				last_prio = &new->chain;
-			} else {
-				fd_list_insert_before(&peer->p_connparams, &new->chain);
-			}
-		}
-	}
-	
-	/* Now, add the SCTP entry, if not disabled */
-#ifndef DISABLE_SCTP
-	if ((!fd_g_config->cnf_flags.no_sctp) && (peer->p_hdr.info.config.pic_flags.pro4 != PI_P4_TCP)) {
-		struct next_conn   * new;
-		
-		CHECK_MALLOC( new = malloc(sizeof(struct next_conn)) );
-		memset(new, 0, sizeof(struct next_conn));
-		fd_list_init(&new->chain, new);
-
-		new->proto = IPPROTO_SCTP;
-		new->port  = ntohs(port_no); /* back to host byte order... */
-		new->dotls = dotls_immediate;
-
-		/* Add the new entry to the appropriate position (depending on preferences) */
-		if ((fd_g_config->cnf_flags.pr_tcp) || (peer->p_hdr.info.config.pic_flags.alg == PI_ALGPREF_TCP)) {
-			fd_list_insert_after(last_prio, &new->chain);
-		} else {
-			fd_list_insert_after(&peer->p_connparams, &new->chain); /* very first position */
-		}
-	}
-#endif /* DISABLE_SCTP */
-	
-	return 0;
-}
-
-
-/* The thread that attempts the connection */
-static void * connect_thr(void * arg)
-{
-	struct fd_peer * peer = arg;
-	struct cnxctx * cnx = NULL;
-	struct next_conn * nc = NULL;
-	int rebuilt = 0;
-	
-	TRACE_ENTRY("%p", arg);
-	CHECK_PARAMS_DO( CHECK_PEER(peer), return NULL );
-
-	/* Set the thread name */
-	{
-		char buf[48];
-		sprintf(buf, "ConnTo:%.*s", (int)(sizeof(buf)) - 8, peer->p_hdr.info.pi_diamid);
-		fd_log_threadname ( buf );
-	}
-	
-	do {
-		/* Rebuild the list if needed, if it is empty -- but at most once */
-		if (FD_IS_LIST_EMPTY(&peer->p_connparams)) {
-			if (! rebuilt) {
-				CHECK_FCT_DO( prepare_connection_list(peer), goto fatal_error );
-				rebuilt ++;
-			}
-			if (FD_IS_LIST_EMPTY(&peer->p_connparams)) {
-				/* We encountered an error or we have looped over all the addresses of the peer. */
-				TRACE_DEBUG(INFO, "Unable to connect to the peer %s, aborting attempts for now.", peer->p_hdr.info.pi_diamid);
-				CHECK_FCT_DO( fd_event_send(peer->p_events, FDEVP_CNX_FAILED, 0, NULL), goto fatal_error );
-				return NULL;
-			}
-		}
-		
-		/* Attempt connection to the first entry */
-		nc = (struct next_conn *)(peer->p_connparams.next);
-		
-		switch (nc->proto) {
-			case IPPROTO_TCP:
-				cnx = fd_cnx_cli_connect_tcp((sSA *)&nc->ss, sSAlen(&nc->ss));
-				break;
-#ifndef DISABLE_SCTP			
-			case IPPROTO_SCTP:
-				cnx = fd_cnx_cli_connect_sctp((peer->p_hdr.info.config.pic_flags.pro3 == PI_P3_IP) ?: fd_g_config->cnf_flags.no_ip6, nc->port, &peer->p_hdr.info.pi_endpoints);
-				break;
-#endif /* DISABLE_SCTP */
-		}
-		
-		if (cnx)
-			break;
-		
-		/* Pop these parameters and continue */
-		failed_connection_attempt(peer);
-		
-		pthread_testcancel();
-		
-	} while (!cnx); /* and until cancellation */
-	
-	/* Now, we have an established connection in cnx */
-	
-	pthread_cleanup_push((void *)fd_cnx_destroy, cnx);
-	
-	/* Set the hostname in the connection, so that handshake verifies the remote identity */
-	fd_cnx_sethostname(cnx,peer->p_hdr.info.pi_diamid);
-	
-	/* Handshake if needed (secure port) */
-	if (nc->dotls) {
-		CHECK_FCT_DO( fd_cnx_handshake(cnx, GNUTLS_CLIENT, peer->p_hdr.info.config.pic_priority, NULL),
-			{
-				/* Handshake failed ...  */
-				fd_log_debug("TLS Handshake failed with peer '%s', resetting the connection\n", peer->p_hdr.info.pi_diamid);
-				fd_cnx_destroy(cnx);
-				empty_connection_list(peer);
-				fd_ep_filter(&peer->p_hdr.info.pi_endpoints, EP_FL_CONF);
-				return NULL;
-			} );
-	} else {
-		/* Prepare to receive the next message */
-		CHECK_FCT_DO( fd_cnx_start_clear(cnx, 0), goto fatal_error );
-	}
-	
-	/* Upon success, generate FDEVP_CNX_ESTABLISHED */
-	CHECK_FCT_DO( fd_event_send(peer->p_events, FDEVP_CNX_ESTABLISHED, 0, cnx), goto fatal_error );
-	
-	pthread_cleanup_pop(0);
-	
-	return NULL;
-	
-fatal_error:
-	/* Cleanup the connection */
-	if (cnx)
-		fd_cnx_destroy(cnx);
-
-	/* Generate a termination event */
-	CHECK_FCT_DO(fd_event_send(fd_g_config->cnf_main_ev, FDEV_TERMINATE, 0, NULL), );
-	
-	return NULL;
-}
-
-
-/* Initiate a connection attempt to a remote peer */
-int fd_p_cnx_init(struct fd_peer * peer)
-{
-	TRACE_ENTRY("%p", peer);
-	
-	/* Start the connect thread */
-	CHECK_FCT( pthread_create(&peer->p_ini_thr, NULL, connect_thr, peer) );
-	return 0;
-}
-
-/* Cancel a connection attempt */
-void fd_p_cnx_abort(struct fd_peer * peer, int cleanup_all)
-{
-	TRACE_ENTRY("%p %d", peer, cleanup_all);
-	CHECK_PARAMS_DO( CHECK_PEER(peer), return );
-	
-	if (peer->p_ini_thr != (pthread_t)NULL) {
-		CHECK_FCT_DO( fd_thr_term(&peer->p_ini_thr), /* continue */);
-		failed_connection_attempt(peer);
-	}
-	
-	if (cleanup_all) {
-		empty_connection_list(peer);
-	}
-}
Index: eeDiameter/p_dp.c
===================================================================
--- freeDiameter/p_dp.c	(revision 454)
+++ 	(revision )
@@ -1,174 +1,0 @@
-/*********************************************************************************************************
-* Software License Agreement (BSD License)                                                               *
-* Author: Sebastien Decugis <sdecugis@nict.go.jp>							 *
-*													 *
-* Copyright (c) 2010, 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 "fD.h"
-
-/* This file contains code to handle Disconnect Peer messages (DPR and DPA) */
-
-/* Handle a received message */
-int fd_p_dp_handle(struct msg ** msg, int req, struct fd_peer * peer)
-{
-	TRACE_ENTRY("%p %d %p", msg, req, peer);
-	
-	if (req) {
-		/* We received a DPR, save the Disconnect-Cause and terminate the connection */
-		struct avp * dc;
-		int delay = peer->p_hdr.info.config.pic_tctimer ?: fd_g_config->cnf_timer_tc;
-		
-		CHECK_FCT( fd_msg_search_avp ( *msg, fd_dict_avp_DC, &dc ));
-		if (dc) {
-			/* Check the value is consistent with the saved one */
-			struct avp_hdr * hdr;
-			CHECK_FCT(  fd_msg_avp_hdr( dc, &hdr )  );
-			if (hdr->avp_value == NULL) {
-				/* This is a sanity check */
-				TRACE_DEBUG(NONE, "BUG: Unset value in Disconnect-Cause in DPR");
-				fd_msg_dump_one(NONE, dc);
-				ASSERT(0); /* To check if this really happens, and understand why... */
-			}
-
-			peer->p_hdr.info.runtime.pir_lastDC = hdr->avp_value->u32;
-			
-			switch (hdr->avp_value->u32) {
-				case ACV_DC_REBOOTING:
-				default:
-					/* We use TcTimer to attempt reconnection */
-					break;
-				case ACV_DC_BUSY:
-					/* No need to hammer the overloaded peer */
-					delay *= 10;
-					break;
-				case ACV_DC_NOT_FRIEND:
-					/* He does not want to speak to us... let's retry a lot later maybe */
-					delay *= 200;
-					break;
-			}
-		}
-		if (TRACE_BOOL(INFO)) {
-			if (dc) {
-				struct dict_object * dictobj = NULL;
-				struct dict_enumval_request er;
-				memset(&er, 0, sizeof(er));
-				CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_TYPE, TYPE_OF_AVP, fd_dict_avp_DC, &er.type_obj, ENOENT )  );
-				er.search.enum_value.u32 = peer->p_hdr.info.runtime.pir_lastDC;
-				CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_ENUMVAL, ENUMVAL_BY_STRUCT, &er, &dictobj, 0 )  );
-				if (dictobj) {
-					CHECK_FCT( fd_dict_getval( dictobj, &er.search ) );
-					TRACE_DEBUG(INFO, "Peer '%s' sent a DPR with cause: %s\n", peer->p_hdr.info.pi_diamid, er.search.enum_name);
-				} else {
-					TRACE_DEBUG(INFO, "Peer '%s' sent a DPR with unknown cause: %u\n", peer->p_hdr.info.pi_diamid, peer->p_hdr.info.runtime.pir_lastDC);
-				}
-			} else {
-				TRACE_DEBUG(INFO, "Peer '%s' sent a DPR without Disconnect-Cause AVP\n", peer->p_hdr.info.pi_diamid);
-			}
-		}
-		
-		/* Now reply with a DPA */
-		CHECK_FCT( fd_msg_new_answer_from_req ( fd_g_config->cnf_dict, msg, 0 ) );
-		CHECK_FCT( fd_msg_rescode_set( *msg, "DIAMETER_SUCCESS", NULL, NULL, 1 ) );
-		
-		/* Move to CLOSING state to failover outgoing messages (and avoid failing the DPA...) */
-		CHECK_FCT( fd_psm_change_state(peer, STATE_CLOSING) );
-		
-		/* Now send the DPA */
-		CHECK_FCT( fd_out_send( msg, NULL, peer, FD_CNX_ORDERED) );
-		
-		/* Move to CLOSED state */
-		fd_psm_cleanup(peer, 0);
-		
-		/* Reset the timer for next connection attempt -- we'll retry sooner or later depending on the disconnection cause */
-		fd_psm_next_timeout(peer, 1, delay);
-		
-	} else {
-		/* We received a DPA */
-		fd_cpu_flush_cache();
-		if (peer->p_hdr.info.runtime.pir_state != STATE_CLOSING) {
-			TRACE_DEBUG(INFO, "Ignoring DPA received in state %s", STATE_STR(peer->p_hdr.info.runtime.pir_state));
-		}
-			
-		/* In theory, we should control the Result-Code AVP. But since we will not go back to OPEN state here anyway, let's skip it */
-		CHECK_FCT_DO( fd_msg_free( *msg ), /* continue */ );
-		*msg = NULL;
-		
-		/* The calling function handles cleaning the PSM and terminating the peer since we return in CLOSING state */
-	}
-	
-	return 0;
-}
-
-/* Start disconnection of a peer: send DPR */
-int fd_p_dp_initiate(struct fd_peer * peer, char * reason)
-{
-	struct msg * msg = NULL;
-	struct dict_object * dictobj = NULL;
-	struct avp * avp = NULL;
-	struct dict_enumval_request er;
-	union avp_value val;
-	
-	TRACE_ENTRY("%p %p", peer, reason);
-	
-	/* Create a new DWR instance */
-	CHECK_FCT( fd_msg_new ( fd_dict_cmd_DPR, MSGFL_ALLOC_ETEID, &msg ) );
-	
-	/* Add the Origin information */
-	CHECK_FCT( fd_msg_add_origin ( msg, 0 ) );
-	
-	/* Add the Disconnect-Cause */
-	CHECK_FCT( fd_msg_avp_new ( fd_dict_avp_DC, 0, &avp ) );
-	
-	/* Search the value in the dictionary */
-	memset(&er, 0, sizeof(er));
-	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_TYPE, TYPE_OF_AVP, fd_dict_avp_DC, &er.type_obj, ENOENT )  );
-	er.search.enum_name = reason ?: "REBOOTING";
-	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_ENUMVAL, ENUMVAL_BY_STRUCT, &er, &dictobj, ENOENT )  );
-	CHECK_FCT( fd_dict_getval( dictobj, &er.search ) );
-	
-	/* Set the value in the AVP */
-	val.u32 = er.search.enum_value.u32;
-	CHECK_FCT( fd_msg_avp_setvalue( avp, &val ) );
-	CHECK_FCT( fd_msg_avp_add( msg, MSG_BRW_LAST_CHILD, avp ) );
-	
-	/* Save the value also in the peer */
-	peer->p_hdr.info.runtime.pir_lastDC = val.u32;
-	
-	/* Update the peer state and timer */
-	CHECK_FCT( fd_psm_change_state(peer, STATE_CLOSING) );
-	fd_psm_next_timeout(peer, 0, DPR_TIMEOUT);
-	
-	/* Now send the DPR message */
-	CHECK_FCT_DO( fd_out_send(&msg, NULL, peer, FD_CNX_ORDERED), /* ignore since we are on timeout anyway */ );
-	
-	return 0;
-}
Index: eeDiameter/p_dw.c
===================================================================
--- freeDiameter/p_dw.c	(revision 454)
+++ 	(revision )
@@ -1,171 +1,0 @@
-/*********************************************************************************************************
-* Software License Agreement (BSD License)                                                               *
-* Author: Sebastien Decugis <sdecugis@nict.go.jp>							 *
-*													 *
-* Copyright (c) 2010, 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 "fD.h"
-
-/* This file contains code to handle Device Watchdog messages (DWR and DWA) */
-
-/* Check the value of Origin-State-Id is consistent in a DWR or  DWA -- we just log if it is not the case */
-static void check_state_id(struct msg * msg, struct fd_peer * peer)
-{
-	struct avp * osi;
-	/* Check if the request contains the Origin-State-Id */
-	CHECK_FCT_DO( fd_msg_search_avp ( msg, fd_dict_avp_OSI, &osi ), return );
-	if (osi) {
-		/* Check the value is consistent with the saved one */
-		struct avp_hdr * hdr;
-		CHECK_FCT_DO(  fd_msg_avp_hdr( osi, &hdr ), return  );
-		if (hdr->avp_value == NULL) {
-			/* This is a sanity check */
-			TRACE_DEBUG(NONE, "BUG: Unset value in Origin-State-Id in DWR / DWA");
-			fd_msg_dump_one(NONE, osi);
-			ASSERT(0); /* To check if this really happens, and understand why... */
-		}
-
-		if (peer->p_hdr.info.runtime.pir_orstate != hdr->avp_value->u32) {
-			fd_log_debug("Received a new Origin-State-Id from peer %s! (%x / %x)\n", 
-				peer->p_hdr.info.pi_diamid, 
-				hdr->avp_value->u32,
-				peer->p_hdr.info.runtime.pir_orstate );
-		}
-	}
-}
-
-/* Create and send a DWR */
-static int send_DWR(struct fd_peer * peer)
-{
-	struct msg * msg = NULL;
-	
-	/* Create a new DWR instance */
-	CHECK_FCT( fd_msg_new ( fd_dict_cmd_DWR, MSGFL_ALLOC_ETEID, &msg ) );
-	
-	/* Add the content of the message (only the origin) */
-	CHECK_FCT( fd_msg_add_origin ( msg, 1 ) );
-	
-	/* Now send this message */
-	CHECK_FCT( fd_out_send(&msg, NULL, peer, FD_CNX_ORDERED) );
-	
-	/* And mark the pending DW */
-	peer->p_flags.pf_dw_pending = 1;
-	
-	return 0;
-}
-
-/* Handle an incoming message */
-int fd_p_dw_handle(struct msg ** msg, int req, struct fd_peer * peer)
-{
-	int reset_tmr = 0;
-	
-	TRACE_ENTRY("%p %d %p", msg, req, peer);
-	
-	/* Check the value of OSI for information */
-	check_state_id(*msg, peer);
-	
-	if (req) {
-		/* If we receive a DWR, send back a DWA */
-		CHECK_FCT( fd_msg_new_answer_from_req ( fd_g_config->cnf_dict, msg, 0 ) );
-		CHECK_FCT( fd_msg_rescode_set( *msg, "DIAMETER_SUCCESS", NULL, NULL, 0 ) );
-		CHECK_FCT( fd_msg_add_origin ( *msg, 1 ) );
-		CHECK_FCT( fd_out_send( msg, peer->p_cnxctx, peer, FD_CNX_ORDERED) );
-		
-	} else {
-		/* Just discard the DWA */
-		CHECK_FCT_DO( fd_msg_free(*msg), /* continue */ );
-		*msg = NULL;
-		
-		/* And clear the pending DW flag */
-		peer->p_flags.pf_dw_pending = 0;
-	}
-	
-	/* Now update timeout */
-	if (req) {
-		/* Update timeout only if we did not already send a DWR ourselves */
-		reset_tmr = !peer->p_flags.pf_dw_pending;
-	} else {
-		/* Reset the timer */
-		reset_tmr = 1;
-	}
-	if (reset_tmr) {
-		fd_psm_next_timeout(peer, 1, peer->p_hdr.info.config.pic_twtimer ?: fd_g_config->cnf_timer_tw);
-	}
-	
-	/* If we are in REOPEN state, increment the counter */
-	fd_cpu_flush_cache();
-	if (peer->p_hdr.info.runtime.pir_state == STATE_REOPEN) {
-		peer->p_flags.pf_reopen_cnt += 1;
-		
-		if (peer->p_flags.pf_reopen_cnt) {
-			/* Send a new DWR */
-			CHECK_FCT( send_DWR(peer) );
-		} else {
-			/* Move to OPEN state */
-			CHECK_FCT( fd_psm_change_state(peer, STATE_OPEN) );
-		}
-	}
-		
-	return 0;
-}
-
-/* Handle a timeout in the PSM (OPEN or REOPEN state only) */
-int fd_p_dw_timeout(struct fd_peer * peer)
-{
-	TRACE_ENTRY("%p", peer);
-
-	if (peer->p_flags.pf_dw_pending) {
-		/* We have sent a DWR and received no answer during TwTimer */
-		CHECK_FCT( fd_psm_change_state(peer, STATE_SUSPECT) );
-		fd_psm_next_timeout(peer, 0, 2 * (peer->p_hdr.info.config.pic_twtimer ?: fd_g_config->cnf_timer_tw) );
-	} else {
-		/* The timeout has expired, send a DWR */
-		CHECK_FCT( send_DWR(peer) );
-		fd_psm_next_timeout(peer, 0, peer->p_hdr.info.config.pic_twtimer ?: fd_g_config->cnf_timer_tw );
-	}
-	
-	return 0;
-}
-
-/* Handle DW exchanges after the peer has come alive again */
-int fd_p_dw_reopen(struct fd_peer * peer)
-{
-	TRACE_ENTRY("%p", peer);
-
-	peer->p_flags.pf_reopen_cnt = 1;
-	peer->p_flags.pf_cnx_pb = 0;
-	CHECK_FCT( send_DWR(peer) );
-	
-	return 0;
-}
-
-
Index: eeDiameter/p_expiry.c
===================================================================
--- freeDiameter/p_expiry.c	(revision 648)
+++ 	(revision )
@@ -1,207 +1,0 @@
-/*********************************************************************************************************
-* Software License Agreement (BSD License)                                                               *
-* Author: Sebastien Decugis <sdecugis@nict.go.jp>							 *
-*													 *
-* Copyright (c) 2010, 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 "fD.h"
-
-/* Delay for garbage collection of expired peers, in seconds */
-#define GC_TIME		120
-
-static pthread_t       exp_thr;
-static pthread_t       gc_thr;
-static struct fd_list  exp_list = FD_LIST_INITIALIZER( exp_list );
-static pthread_cond_t  exp_cnd  = PTHREAD_COND_INITIALIZER;
-static pthread_mutex_t exp_mtx  = PTHREAD_MUTEX_INITIALIZER;
-
-static void * gc_th_fct(void * arg)
-{
-	fd_log_threadname ( "Peers/garb. col." );
-	TRACE_ENTRY( "%p", arg );
-	
-	do {
-		struct fd_list * li, purge = FD_LIST_INITIALIZER(purge);
-		
-		sleep(GC_TIME);	/* sleep is a cancellation point */
-		
-		/* Now check in the peers list if any peer can be deleted */
-		CHECK_FCT_DO( pthread_rwlock_wrlock(&fd_g_peers_rw), goto error );
-		
-		for (li = fd_g_peers.next; li != &fd_g_peers; li = li->next) {
-			struct fd_peer * peer = (struct fd_peer *)li;
-			
-			fd_cpu_flush_cache();
-			if (peer->p_hdr.info.runtime.pir_state != STATE_ZOMBIE)
-				continue;
-			
-			if (peer->p_hdr.info.config.pic_flags.persist == PI_PRST_ALWAYS)
-				continue; /* This peer was not supposed to terminate, keep it in the list for debug */
-			
-			/* Ok, the peer was expired, let's remove it */
-			li = li->prev; /* to avoid breaking the loop */
-			fd_list_unlink(&peer->p_hdr.chain);
-			fd_list_insert_before(&purge, &peer->p_hdr.chain);
-		}
-
-		CHECK_FCT_DO( pthread_rwlock_unlock(&fd_g_peers_rw), goto error );
-		
-		/* Now delete peers that are in the purge list */
-		while (!FD_IS_LIST_EMPTY(&purge)) {
-			struct fd_peer * peer = (struct fd_peer *)(purge.next);
-			fd_list_unlink(&peer->p_hdr.chain);
-			TRACE_DEBUG(INFO, "Garbage Collect: delete zombie peer '%s'", peer->p_hdr.info.pi_diamid);
-			CHECK_FCT_DO( fd_peer_free(&peer), /* Continue... what else to do ? */ );
-		}
-	} while (1);
-	
-error:
-	TRACE_DEBUG(INFO, "An error occurred in peers module! GC thread is terminating...");
-	ASSERT(0);
-	CHECK_FCT_DO(fd_event_send(fd_g_config->cnf_main_ev, FDEV_TERMINATE, 0, NULL), );
-	return NULL;
-}
-
-
-static void * exp_th_fct(void * arg)
-{
-	fd_log_threadname ( "Peers/expire" );
-	TRACE_ENTRY( "%p", arg );
-	
-	CHECK_POSIX_DO( pthread_mutex_lock(&exp_mtx),  goto error );
-	pthread_cleanup_push( fd_cleanup_mutex, &exp_mtx );
-	
-	do {
-		struct timespec	now;
-		struct fd_peer * first;
-		
-		/* Check if there are expiring sessions available */
-		if (FD_IS_LIST_EMPTY(&exp_list)) {
-			/* Just wait for a change or cancelation */
-			CHECK_POSIX_DO( pthread_cond_wait( &exp_cnd, &exp_mtx ), goto error );
-			/* Restart the loop on wakeup */
-			continue;
-		}
-		
-		/* Get the pointer to the peer that expires first */
-		first = (struct fd_peer *)(exp_list.next->o);
-		ASSERT( CHECK_PEER(first) );
-		
-		/* Get the current time */
-		CHECK_SYS_DO(  clock_gettime(CLOCK_REALTIME, &now),  goto error  );
-
-		/* If first peer is not expired, we just wait until it happens */
-		if ( TS_IS_INFERIOR( &now, &first->p_exp_timer ) ) {
-			
-			CHECK_POSIX_DO2(  pthread_cond_timedwait( &exp_cnd, &exp_mtx, &first->p_exp_timer ),  
-					ETIMEDOUT, /* ETIMEDOUT is a normal return value, continue */,
-					/* on other error, */ goto error );
-	
-			/* on wakeup, loop */
-			continue;
-		}
-		
-		/* Now, the first peer in the list is expired; signal it */
-		fd_list_unlink( &first->p_expiry );
-		CHECK_FCT_DO( fd_event_send(first->p_events, FDEVP_TERMINATE, 0, "DO_NOT_WANT_TO_TALK_TO_YOU"), goto error );
-		
-	} while (1);
-	
-	pthread_cleanup_pop( 1 );
-error:
-	TRACE_DEBUG(INFO, "An error occurred in peers module! Expiry thread is terminating...");
-	ASSERT(0);
-	CHECK_FCT_DO(fd_event_send(fd_g_config->cnf_main_ev, FDEV_TERMINATE, 0, NULL), );
-	return NULL;
-}
-
-/* Initialize peers expiry mechanism */
-int fd_p_expi_init(void)
-{
-	TRACE_ENTRY();
-	CHECK_FCT( pthread_create( &exp_thr, NULL, exp_th_fct, NULL ) );
-	CHECK_FCT( pthread_create( &gc_thr,  NULL, gc_th_fct,  NULL ) );
-	return 0;
-}
-
-/* Finish peers expiry mechanism */
-int fd_p_expi_fini(void)
-{
-	CHECK_FCT_DO( fd_thr_term(&exp_thr), );
-	CHECK_POSIX( pthread_mutex_lock(&exp_mtx) );
-	while (!FD_IS_LIST_EMPTY(&exp_list)) {
-		struct fd_peer * peer = (struct fd_peer *)(exp_list.next->o);
-		fd_list_unlink(&peer->p_expiry );
-	}
-	CHECK_POSIX( pthread_mutex_unlock(&exp_mtx) );
-	
-	CHECK_FCT_DO( fd_thr_term(&gc_thr), );
-	return 0;
-}
-
-/* Add / requeue a peer in the expiry list */
-int fd_p_expi_update(struct fd_peer * peer )
-{
-	TRACE_ENTRY("%p", peer);
-	CHECK_PARAMS( CHECK_PEER(peer) );
-	
-	CHECK_POSIX( pthread_mutex_lock(&exp_mtx) );
-	
-	fd_list_unlink(&peer->p_expiry );
-	
-	/* if peer expires */
-	if (peer->p_hdr.info.config.pic_flags.exp) {
-		struct fd_list * li;
-		
-		/* update the p_exp_timer value */
-		CHECK_SYS(  clock_gettime(CLOCK_REALTIME, &peer->p_exp_timer)  );
-		peer->p_exp_timer.tv_sec += peer->p_hdr.info.config.pic_lft;
-		
-		/* add to the expiry list in appropriate position (probably around the end) */
-		for (li = exp_list.prev; li != &exp_list; li = li->prev) {
-			struct fd_peer * p = (struct fd_peer *)(li->o);
-			if (TS_IS_INFERIOR( &p->p_exp_timer, &peer->p_exp_timer ) )
-				break;
-		}
-		
-		fd_list_insert_after(li, &peer->p_expiry);
-		
-		/* signal the expiry thread if we added in first position */
-		if (li == &exp_list) {
-			CHECK_POSIX( pthread_cond_signal(&exp_cnd) );
-		}
-	}
-	
-	CHECK_POSIX( pthread_mutex_unlock(&exp_mtx) );
-	return 0;
-}
-
Index: eeDiameter/p_out.c
===================================================================
--- freeDiameter/p_out.c	(revision 455)
+++ 	(revision )
@@ -1,198 +1,0 @@
-/*********************************************************************************************************
-* Software License Agreement (BSD License)                                                               *
-* Author: Sebastien Decugis <sdecugis@nict.go.jp>							 *
-*													 *
-* Copyright (c) 2010, 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 "fD.h"
-
-/* Alloc a new hbh for requests, bufferize the message and send on the connection, save in sentreq if provided */
-static int do_send(struct msg ** msg, uint32_t flags, struct cnxctx * cnx, uint32_t * hbh, struct sr_list * srl)
-{
-	struct msg_hdr * hdr;
-	int msg_is_a_req;
-	uint8_t * buf;
-	size_t sz;
-	int ret;
-	uint32_t bkp_hbh = 0;
-	
-	TRACE_ENTRY("%p %x %p %p %p", msg, flags, cnx, hbh, srl);
-	
-	/* Retrieve the message header */
-	CHECK_FCT( fd_msg_hdr(*msg, &hdr) );
-	
-	msg_is_a_req = (hdr->msg_flags & CMD_FLAG_REQUEST);
-	if (msg_is_a_req) {
-		CHECK_PARAMS(hbh && srl);
-		/* Alloc the hop-by-hop id and increment the value for next message */
-		bkp_hbh = hdr->msg_hbhid;
-		hdr->msg_hbhid = *hbh;
-		*hbh = hdr->msg_hbhid + 1;
-	}
-	
-	/* Log the message */
-	if (TRACE_BOOL(FULL)) {
-		CHECK_FCT_DO(  fd_msg_update_length(*msg), /* continue */  );
-		TRACE_DEBUG(FULL, "Sending the following message on connection '%s':", fd_cnx_getid(cnx));
-		fd_msg_dump_walk(FULL, *msg);
-	}
-	
-	/* Create the message buffer */
-	CHECK_FCT(fd_msg_bufferize( *msg, &buf, &sz ));
-	pthread_cleanup_push( free, buf );
-	
-	/* Save a request before sending so that there is no race condition with the answer */
-	if (msg_is_a_req) {
-		CHECK_FCT_DO( ret = fd_p_sr_store(srl, msg, &hdr->msg_hbhid, bkp_hbh), { free(buf); return ret; } );
-	}
-	
-	/* Send the message */
-	CHECK_FCT_DO( ret = fd_cnx_send(cnx, buf, sz, flags), { free(buf); return ret; } );
-	pthread_cleanup_pop(1);
-	
-	/* Free remaining messages (i.e. answers) */
-	if (*msg) {
-		CHECK_FCT( fd_msg_free(*msg) );
-		*msg = NULL;
-	}
-	
-	return 0;
-}
-
-static void cleanup_requeue(void * arg)
-{
-	struct msg *msg = arg;
-	CHECK_FCT_DO(fd_fifo_post(fd_g_outgoing, &msg),
-			CHECK_FCT_DO(fd_msg_free(msg), /* What can we do more? */));
-}
-
-/* The code of the "out" thread */
-static void * out_thr(void * arg)
-{
-	struct fd_peer * peer = arg;
-	ASSERT( CHECK_PEER(peer) );
-	
-	/* Set the thread name */
-	{
-		char buf[48];
-		sprintf(buf, "OUT/%.*s", (int)sizeof(buf) - 5, peer->p_hdr.info.pi_diamid);
-		fd_log_threadname ( buf );
-	}
-	
-	/* Loop until cancelation */
-	while (1) {
-		struct msg * msg;
-		
-		/* Retrieve next message to send */
-		CHECK_FCT_DO( fd_fifo_get(peer->p_tosend, &msg), goto error );
-		
-		/* Now if we are cancelled, we requeue this message */
-		pthread_cleanup_push(cleanup_requeue, msg);
-		
-		/* Send the message, log any error */
-		CHECK_FCT_DO( do_send(&msg, 0, peer->p_cnxctx, &peer->p_hbh, &peer->p_sr),
-			{
-				if (msg) {
-					fd_log_debug("An error occurred while sending this message, it was lost:\n");
-					fd_msg_dump_walk(NONE, msg);
-					fd_msg_free(msg);
-				}
-			} );
-			
-		/* Loop */
-		pthread_cleanup_pop(0);
-	}
-	
-error:
-	/* It is not really a connection error, but the effect is the same, we are not able to send anymore message */
-	CHECK_FCT_DO( fd_event_send(peer->p_events, FDEVP_CNX_ERROR, 0, NULL), /* What do we do if it fails? */ );
-	return NULL;
-}
-
-/* Wrapper to sending a message either by out thread (peer in OPEN state) or directly; cnx or peer must be provided. Flags are valid only for direct sending, not through thread (unused) */
-int fd_out_send(struct msg ** msg, struct cnxctx * cnx, struct fd_peer * peer, uint32_t flags)
-{
-	TRACE_ENTRY("%p %p %p %x", msg, cnx, peer, flags);
-	CHECK_PARAMS( msg && *msg && (cnx || (peer && peer->p_cnxctx)));
-	
-	fd_cpu_flush_cache();
-	if (peer && (peer->p_hdr.info.runtime.pir_state == STATE_OPEN)) {
-		/* Normal case: just queue for the out thread to pick it up */
-		CHECK_FCT( fd_fifo_post(peer->p_tosend, msg) );
-		
-	} else {
-		uint32_t *hbh = NULL;
-		
-		/* In other cases, the thread is not running, so we handle the sending directly */
-		if (peer)
-			hbh = &peer->p_hbh;
-
-		if (!cnx)
-			cnx = peer->p_cnxctx;
-
-		/* Do send the message */
-		CHECK_FCT_DO( do_send(msg, flags, cnx, hbh, peer ? &peer->p_sr : NULL),
-			{
-				if (msg) {
-					fd_log_debug("An error occurred while sending this message, it was lost:\n");
-					fd_msg_dump_walk(NONE, *msg);
-					fd_msg_free(*msg);
-					*msg = NULL;
-				}
-			} );
-	}
-	
-	return 0;
-}
-
-/* Start the "out" thread that picks messages in p_tosend and send them on p_cnxctx */
-int fd_out_start(struct fd_peer * peer)
-{
-	TRACE_ENTRY("%p", peer);
-	CHECK_PARAMS( CHECK_PEER(peer) && (peer->p_outthr == (pthread_t)NULL) );
-	
-	CHECK_POSIX( pthread_create(&peer->p_outthr, NULL, out_thr, peer) );
-	
-	return 0;
-}
-
-/* Stop that thread */
-int fd_out_stop(struct fd_peer * peer)
-{
-	TRACE_ENTRY("%p", peer);
-	CHECK_PARAMS( CHECK_PEER(peer) );
-	
-	CHECK_FCT( fd_thr_term(&peer->p_outthr) );
-	
-	return 0;
-}
-		
Index: eeDiameter/p_psm.c
===================================================================
--- freeDiameter/p_psm.c	(revision 454)
+++ 	(revision )
@@ -1,775 +1,0 @@
-/*********************************************************************************************************
-* Software License Agreement (BSD License)                                                               *
-* Author: Sebastien Decugis <sdecugis@nict.go.jp>							 *
-*													 *
-* Copyright (c) 2010, 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 "fD.h"
-
-/* The actual declaration of peer_state_str */
-DECLARE_STATE_STR();
-
-/* Helper for next macro */
-#define case_str( _val )		\
-	case _val : return #_val
-
-DECLARE_PEV_STR();
-
-/************************************************************************/
-/*                      Delayed startup                                 */
-/************************************************************************/
-static int started = 0;
-static pthread_mutex_t  started_mtx = PTHREAD_MUTEX_INITIALIZER;
-static pthread_cond_t   started_cnd = PTHREAD_COND_INITIALIZER;
-
-/* Wait for start signal */
-static int fd_psm_waitstart()
-{
-	TRACE_ENTRY("");
-	CHECK_POSIX( pthread_mutex_lock(&started_mtx) );
-awake:	
-	if (! started) {
-		pthread_cleanup_push( fd_cleanup_mutex, &started_mtx );
-		CHECK_POSIX( pthread_cond_wait(&started_cnd, &started_mtx) );
-		pthread_cleanup_pop( 0 );
-		goto awake;
-	}
-	CHECK_POSIX( pthread_mutex_unlock(&started_mtx) );
-	return 0;
-}
-
-/* Allow the state machines to start */
-int fd_psm_start()
-{
-	TRACE_ENTRY("");
-	CHECK_POSIX( pthread_mutex_lock(&started_mtx) );
-	started = 1;
-	CHECK_POSIX( pthread_cond_broadcast(&started_cnd) );
-	CHECK_POSIX( pthread_mutex_unlock(&started_mtx) );
-	return 0;
-}
-
-
-/************************************************************************/
-/*                 Manage the list of active peers                      */
-/************************************************************************/
-
-/* Enter/leave OPEN state */
-static int enter_open_state(struct fd_peer * peer)
-{
-	struct fd_list * li;
-	CHECK_PARAMS( FD_IS_LIST_EMPTY(&peer->p_actives) );
-	
-	/* Callback registered by the credential validator (fd_peer_validate_register) */
-	if (peer->p_cb2) {
-		CHECK_FCT_DO( (*peer->p_cb2)(&peer->p_hdr.info),
-			{
-				TRACE_DEBUG(FULL, "Validation failed, terminating the connection");
-				fd_psm_terminate(peer, "DO_NOT_WANT_TO_TALK_TO_YOU" );
-			} );
-		peer->p_cb2 = NULL;
-		return 0;
-	}
-	/* Insert in the active peers list */
-	CHECK_POSIX( pthread_rwlock_wrlock(&fd_g_activ_peers_rw) );
-	for (li = fd_g_activ_peers.next; li != &fd_g_activ_peers; li = li->next) {
-		struct fd_peer * next_p = (struct fd_peer *)li->o;
-		int cmp = strcmp(peer->p_hdr.info.pi_diamid, next_p->p_hdr.info.pi_diamid);
-		if (cmp < 0)
-			break;
-	}
-	fd_list_insert_before(li, &peer->p_actives);
-	CHECK_POSIX( pthread_rwlock_unlock(&fd_g_activ_peers_rw) );
-	
-	/* Callback registered when the peer was added, by fd_peer_add */
-	if (peer->p_cb) {
-		TRACE_DEBUG(FULL, "Calling add callback for peer %s", peer->p_hdr.info.pi_diamid);
-		(*peer->p_cb)(&peer->p_hdr.info, peer->p_cb_data);
-		peer->p_cb = NULL;
-		peer->p_cb_data = NULL;
-	}
-	
-	/* Start the thread to handle outgoing messages */
-	CHECK_FCT( fd_out_start(peer) );
-	
-	/* Update the expiry timer now */
-	CHECK_FCT( fd_p_expi_update(peer) );
-	
-	return 0;
-}
-static int leave_open_state(struct fd_peer * peer)
-{
-	/* Remove from active peers list */
-	CHECK_POSIX( pthread_rwlock_wrlock(&fd_g_activ_peers_rw) );
-	fd_list_unlink( &peer->p_actives );
-	CHECK_POSIX( pthread_rwlock_unlock(&fd_g_activ_peers_rw) );
-	
-	/* Stop the "out" thread */
-	CHECK_FCT( fd_out_stop(peer) );
-	
-	/* Failover the messages */
-	fd_peer_failover_msg(peer);
-	
-	return 0;
-}
-
-
-/************************************************************************/
-/*                      Helpers for state changes                       */
-/************************************************************************/
-
-/* Cleanup pending events in the peer */
-void fd_psm_events_free(struct fd_peer * peer)
-{
-	struct fd_event * ev;
-	/* Purge all events, and free the associated data if any */
-	while (fd_fifo_tryget( peer->p_events, &ev ) == 0) {
-		switch (ev->code) {
-			case FDEVP_CNX_ESTABLISHED: {
-				fd_cnx_destroy(ev->data);
-			}
-			break;
-			
-			case FDEVP_TERMINATE:
-				/* Do not free the string since it is a constant */
-			break;
-			
-			case FDEVP_CNX_INCOMING: {
-				struct cnx_incoming * evd = ev->data;
-				CHECK_FCT_DO( fd_msg_free(evd->cer), /* continue */);
-				fd_cnx_destroy(evd->cnx);
-			}
-			default:
-				free(ev->data);
-		}
-		free(ev);
-	}
-}
-
-
-/* Change state */
-int fd_psm_change_state(struct fd_peer * peer, int new_state)
-{
-	int old;
-	
-	TRACE_ENTRY("%p %d(%s)", peer, new_state, STATE_STR(new_state));
-	CHECK_PARAMS( CHECK_PEER(peer) );
-	fd_cpu_flush_cache();
-	old = peer->p_hdr.info.runtime.pir_state;
-	if (old == new_state)
-		return 0;
-	
-	TRACE_DEBUG(((old == STATE_OPEN) || (new_state == STATE_OPEN)) ? INFO : FULL, "'%s'\t-> '%s'\t'%s'",
-			STATE_STR(old),
-			STATE_STR(new_state),
-			peer->p_hdr.info.pi_diamid);
-	
-	peer->p_hdr.info.runtime.pir_state = new_state;
-	fd_cpu_flush_cache();
-	
-	if (old == STATE_OPEN) {
-		CHECK_FCT( leave_open_state(peer) );
-	}
-	
-	if (new_state == STATE_OPEN) {
-		CHECK_FCT( enter_open_state(peer) );
-	}
-	
-	if (new_state == STATE_CLOSED) {
-		/* Purge event list */
-		fd_psm_events_free(peer);
-		
-		/* If the peer is not persistant, we destroy it */
-		if (peer->p_hdr.info.config.pic_flags.persist == PI_PRST_NONE) {
-			CHECK_FCT( fd_event_send(peer->p_events, FDEVP_TERMINATE, 0, NULL) );
-		}
-	}
-	
-	return 0;
-}
-
-/* Set timeout timer of next event */
-void fd_psm_next_timeout(struct fd_peer * peer, int add_random, int delay)
-{
-	TRACE_DEBUG(FULL, "Peer timeout reset to %d seconds%s", delay, add_random ? " (+/- 2)" : "" );
-	
-	/* Initialize the timer */
-	CHECK_POSIX_DO(  clock_gettime( CLOCK_REALTIME,  &peer->p_psm_timer ), ASSERT(0) );
-	
-	if (add_random) {
-		if (delay > 2)
-			delay -= 2;
-		else
-			delay = 0;
-
-		/* Add a random value between 0 and 4sec */
-		peer->p_psm_timer.tv_sec += random() % 4;
-		peer->p_psm_timer.tv_nsec+= random() % 1000000000L;
-		if (peer->p_psm_timer.tv_nsec > 1000000000L) {
-			peer->p_psm_timer.tv_nsec -= 1000000000L;
-			peer->p_psm_timer.tv_sec ++;
-		}
-	}
-	
-	peer->p_psm_timer.tv_sec += delay;
-	
-#ifdef SLOW_PSM
-	/* temporary for debug */
-	peer->p_psm_timer.tv_sec += 10;
-#endif
-}
-
-/* Cleanup the peer */
-void fd_psm_cleanup(struct fd_peer * peer, int terminate)
-{
-	/* Move to CLOSED state: failover messages, stop OUT thread, unlink peer from active list */
-	fd_cpu_flush_cache();
-	if (peer->p_hdr.info.runtime.pir_state != STATE_ZOMBIE) {
-		CHECK_FCT_DO( fd_psm_change_state(peer, STATE_CLOSED), /* continue */ );
-	}
-	
-	fd_p_cnx_abort(peer, terminate);
-	
-	fd_p_ce_clear_cnx(peer, NULL);
-	
-	if (peer->p_receiver) {
-		fd_cnx_destroy(peer->p_receiver);
-		peer->p_receiver = NULL;
-	}
-	
-	if (terminate) {
-		fd_psm_events_free(peer);
-		CHECK_FCT_DO( fd_fifo_del(&peer->p_events), /* continue */ );
-	}
-	
-}
-
-
-/************************************************************************/
-/*                      The PSM thread                                  */
-/************************************************************************/
-/* Cancelation cleanup : set ZOMBIE state in the peer */
-void cleanup_setstate(void * arg) 
-{
-	struct fd_peer * peer = (struct fd_peer *)arg;
-	CHECK_PARAMS_DO( CHECK_PEER(peer), return );
-	peer->p_hdr.info.runtime.pir_state = STATE_ZOMBIE;
-	fd_cpu_flush_cache();
-	return;
-}
-
-/* The state machine thread (controler) */
-static void * p_psm_th( void * arg )
-{
-	struct fd_peer * peer = (struct fd_peer *)arg;
-	int created_started = started ? 1 : 0;
-	int event;
-	size_t ev_sz;
-	void * ev_data;
-	
-	CHECK_PARAMS_DO( CHECK_PEER(peer), ASSERT(0) );
-	
-	pthread_cleanup_push( cleanup_setstate, arg );
-	
-	/* Set the thread name */
-	{
-		char buf[48];
-		sprintf(buf, "PSM/%.*s", (int)sizeof(buf) - 5, peer->p_hdr.info.pi_diamid);
-		fd_log_threadname ( buf );
-	}
-	
-	/* The state machine starts in CLOSED state */
-	peer->p_hdr.info.runtime.pir_state = STATE_CLOSED;
-
-	/* Wait that the PSM are authorized to start in the daemon */
-	CHECK_FCT_DO( fd_psm_waitstart(), goto psm_end );
-	
-	/* Initialize the timer */
-	if (peer->p_flags.pf_responder) {
-		fd_psm_next_timeout(peer, 0, INCNX_TIMEOUT);
-	} else {
-		fd_psm_next_timeout(peer, created_started, 0);
-	}
-	
-psm_loop:
-	/* Get next event */
-	TRACE_DEBUG(FULL, "'%s' in state '%s' waiting for next event.",
-			peer->p_hdr.info.pi_diamid, STATE_STR(peer->p_hdr.info.runtime.pir_state));
-	CHECK_FCT_DO( fd_event_timedget(peer->p_events, &peer->p_psm_timer, FDEVP_PSM_TIMEOUT, &event, &ev_sz, &ev_data), goto psm_end );
-	TRACE_DEBUG(FULL, "'%s'\t<-- '%s'\t(%p,%zd)\t'%s'",
-			STATE_STR(peer->p_hdr.info.runtime.pir_state),
-			fd_pev_str(event), ev_data, ev_sz,
-			peer->p_hdr.info.pi_diamid);
-
-	/* Now, the action depends on the current state and the incoming event */
-
-	/* The following states are impossible */
-	ASSERT( peer->p_hdr.info.runtime.pir_state != STATE_NEW );
-	ASSERT( peer->p_hdr.info.runtime.pir_state != STATE_ZOMBIE );
-	ASSERT( peer->p_hdr.info.runtime.pir_state != STATE_OPEN_HANDSHAKE ); /* because it should exist only between two loops */
-
-	/* Purge invalid events */
-	if (!CHECK_PEVENT(event)) {
-		TRACE_DEBUG(INFO, "Invalid event received in PSM '%s' : %d", peer->p_hdr.info.pi_diamid, event);
-		goto psm_loop;
-	}
-
-	/* Handle the (easy) debug event now */
-	if (event == FDEVP_DUMP_ALL) {
-		fd_peer_dump(peer, ANNOYING);
-		goto psm_loop;
-	}
-
-	/* Requests to terminate the peer object */
-	if (event == FDEVP_TERMINATE) {
-		switch (peer->p_hdr.info.runtime.pir_state) {
-			case STATE_OPEN:
-			case STATE_REOPEN:
-				/* We cannot just close the conenction, we have to send a DPR first */
-				CHECK_FCT_DO( fd_p_dp_initiate(peer, ev_data), goto psm_end );
-				goto psm_loop;
-			
-			/*	
-			case STATE_CLOSING:
-			case STATE_WAITCNXACK:
-			case STATE_WAITCNXACK_ELEC:
-			case STATE_WAITCEA:
-			case STATE_SUSPECT:
-			case STATE_CLOSED:
-			*/
-			default:
-				/* In these cases, we just cleanup the peer object (if needed) and terminate */
-				goto psm_end;
-		}
-	}
-	
-	/* A message was received */
-	if (event == FDEVP_CNX_MSG_RECV) {
-		struct msg * msg = NULL;
-		struct msg_hdr * hdr;
-		
-		/* If the current state does not allow receiving messages, just drop it */
-		if (peer->p_hdr.info.runtime.pir_state == STATE_CLOSED) {
-			TRACE_DEBUG(FULL, "Purging message in queue while in CLOSED state (%zdb)", ev_sz);
-			free(ev_data);
-			goto psm_loop;
-		}
-		
-		/* Parse the received buffer */
-		CHECK_FCT_DO( fd_msg_parse_buffer( (void *)&ev_data, ev_sz, &msg), 
-			{
-				fd_log_debug("Received invalid data from peer '%s', closing the connection\n", peer->p_hdr.info.pi_diamid);
-				free(ev_data);
-				CHECK_FCT_DO( fd_event_send(peer->p_events, FDEVP_CNX_ERROR, 0, NULL), goto psm_reset );
-				goto psm_loop;
-			} );
-		
-		TRACE_DEBUG(FULL, "Received a message (%zdb) from '%s'", ev_sz, peer->p_hdr.info.pi_diamid);
-		if (TRACE_BOOL(FULL+1)) {
-			CHECK_FCT_DO( fd_msg_parse_dict( msg, fd_g_config->cnf_dict, NULL ), );
-			fd_msg_dump_walk(FULL, msg);
-		} else {
-			fd_msg_dump_one(FULL, msg);
-		}
-	
-		/* Extract the header */
-		CHECK_FCT_DO( fd_msg_hdr(msg, &hdr), goto psm_end );
-		
-		/* If it is an answer, associate with the request or drop */
-		if (!(hdr->msg_flags & CMD_FLAG_REQUEST)) {
-			struct msg * req;
-			/* Search matching request (same hbhid) */
-			CHECK_FCT_DO( fd_p_sr_fetch(&peer->p_sr, hdr->msg_hbhid, &req), goto psm_end );
-			if (req == NULL) {
-				fd_log_debug("Received a Diameter answer message with no corresponding sent request, discarding.\n");
-				fd_msg_dump_walk(NONE, msg);
-				fd_msg_free(msg);
-				goto psm_loop;
-			}
-			
-			/* Associate */
-			CHECK_FCT_DO( fd_msg_answ_associate( msg, req ), goto psm_end );
-		}
-		
-		/* Now handle non-link-local messages */
-		if (fd_msg_is_routable(msg)) {
-			switch (peer->p_hdr.info.runtime.pir_state) {
-				/* To maximize compatibility -- should not be a security issue here */
-				case STATE_REOPEN:
-				case STATE_SUSPECT:
-				case STATE_CLOSING:
-					TRACE_DEBUG(FULL, "Accepted a message while not in OPEN state... ");
-				/* The standard situation : */
-				case STATE_OPEN:
-					/* We received a valid routable message, update the expiry timer */
-					CHECK_FCT_DO( fd_p_expi_update(peer), goto psm_end );
-
-					/* Set the message source and add the Route-Record */
-					CHECK_FCT_DO( fd_msg_source_set( msg, peer->p_hdr.info.pi_diamid, 1, fd_g_config->cnf_dict ), goto psm_end);
-
-					/* Requeue to the global incoming queue */
-					CHECK_FCT_DO(fd_fifo_post(fd_g_incoming, &msg), goto psm_end );
-
-					/* Update the peer timer (only in OPEN state) */
-					if ((peer->p_hdr.info.runtime.pir_state == STATE_OPEN) && (!peer->p_flags.pf_dw_pending)) {
-						fd_psm_next_timeout(peer, 1, peer->p_hdr.info.config.pic_twtimer ?: fd_g_config->cnf_timer_tw);
-					}
-					break;
-					
-				/* In other states, we discard the message, it is either old or invalid to send it for the remote peer */
-				case STATE_WAITCNXACK:
-				case STATE_WAITCNXACK_ELEC:
-				case STATE_WAITCEA:
-				case STATE_CLOSED:
-				default:
-					/* In such case, just discard the message */
-					fd_log_debug("Received a routable message while not in OPEN state from peer '%s', discarded.\n", peer->p_hdr.info.pi_diamid);
-					fd_msg_dump_walk(NONE, msg);
-					fd_msg_free(msg);
-			}
-			goto psm_loop;
-		}
-		
-		/* Link-local message: They must be understood by our dictionary, otherwise we return an error */
-		{
-			int ret = fd_msg_parse_or_error( &msg );
-			if (ret != EBADMSG) {
-				CHECK_FCT_DO( ret, goto psm_end );
-			} else {
-				if (msg) {
-					/* Send the error back to the peer */
-					CHECK_FCT_DO( fd_out_send(&msg, NULL, peer, FD_CNX_ORDERED), /* In case of error the message has already been dumped */ );
-					if (msg) {
-						CHECK_FCT_DO( fd_msg_free(msg), goto psm_end);
-					}
-				} else {
-					/* We received an invalid answer, let's disconnect */
-					CHECK_FCT_DO( fd_event_send(peer->p_events, FDEVP_CNX_ERROR, 0, NULL), goto psm_reset );
-				}
-				goto psm_loop;
-			}
-		}
-		
-		/* Handle the LL message and update the expiry timer appropriately */
-		switch (hdr->msg_code) {
-			case CC_CAPABILITIES_EXCHANGE:
-				CHECK_FCT_DO( fd_p_ce_msgrcv(&msg, (hdr->msg_flags & CMD_FLAG_REQUEST), peer), goto psm_reset );
-				break;
-			
-			case CC_DISCONNECT_PEER:
-				CHECK_FCT_DO( fd_p_dp_handle(&msg, (hdr->msg_flags & CMD_FLAG_REQUEST), peer), goto psm_reset );
-				if (peer->p_hdr.info.runtime.pir_state == STATE_CLOSING)
-					goto psm_end;
-				break;
-			
-			case CC_DEVICE_WATCHDOG:
-				CHECK_FCT_DO( fd_p_dw_handle(&msg, (hdr->msg_flags & CMD_FLAG_REQUEST), peer), goto psm_reset );
-				break;
-			
-			default:
-				/* Unknown / unexpected / invalid message */
-				fd_log_debug("Received an unknown local message from peer '%s', discarded.\n", peer->p_hdr.info.pi_diamid);
-				fd_msg_dump_walk(NONE, msg);
-				if (hdr->msg_flags & CMD_FLAG_REQUEST) {
-					do {
-						/* Reply with an error code */
-						CHECK_FCT_DO( fd_msg_new_answer_from_req ( fd_g_config->cnf_dict, &msg, MSGFL_ANSW_ERROR ), break );
-
-						/* Set the error code */
-						CHECK_FCT_DO( fd_msg_rescode_set(msg, "DIAMETER_INVALID_HDR_BITS", NULL, NULL, 1 ), break );
-
-						/* Send the answer */
-						CHECK_FCT_DO( fd_out_send(&msg, peer->p_cnxctx, peer, FD_CNX_ORDERED), break );
-					} while (0);
-				} else {
-					/* We did ASK for it ??? */
-					fd_log_debug("Invalid PXY flag in answer header ?\n");
-				}
-				
-				/* Cleanup the message if not done */
-				if (msg) {
-					CHECK_FCT_DO( fd_msg_free(msg), /* continue */);
-					msg = NULL;
-				}
-		};
-		
-		/* At this point the message must have been fully handled already */
-		if (msg) {
-			fd_log_debug("Internal error: unhandled message (discarded)!\n");
-			fd_msg_dump_walk(NONE, msg);
-			fd_msg_free(msg);
-		}
-		
-		goto psm_loop;
-	}
-	
-	/* The connection object is broken */
-	if (event == FDEVP_CNX_ERROR) {
-		switch (peer->p_hdr.info.runtime.pir_state) {
-			case STATE_WAITCNXACK_ELEC:
-				/* Abort the initiating side */
-				fd_p_cnx_abort(peer, 0);
-				/* Process the receiver side */
-				CHECK_FCT_DO( fd_p_ce_process_receiver(peer), goto psm_end );
-				break;
-			
-			case STATE_WAITCEA:
-			case STATE_OPEN:
-			case STATE_REOPEN:
-			case STATE_WAITCNXACK:
-			case STATE_SUSPECT:
-			default:
-				/* Mark the connection problem */
-				peer->p_flags.pf_cnx_pb = 1;
-				
-				/* Destroy the connection, restart the timer to a new connection attempt */
-				fd_psm_next_timeout(peer, 1, peer->p_hdr.info.config.pic_tctimer ?: fd_g_config->cnf_timer_tc);
-				
-			case STATE_CLOSED:
-				goto psm_reset;
-				
-			case STATE_CLOSING:
-				/* We sent a DPR so we are terminating, do not wait for DPA */
-				goto psm_end;
-				
-		}
-		goto psm_loop;
-	}
-	
-	/* The connection notified a change in endpoints */
-	if (event == FDEVP_CNX_EP_CHANGE) {
-		/* We actually don't care if we are in OPEN state here... */
-		
-		/* Cleanup the remote LL and primary addresses */
-		CHECK_FCT_DO( fd_ep_filter( &peer->p_hdr.info.pi_endpoints, EP_FL_CONF | EP_FL_DISC | EP_FL_ADV ), /* ignore the error */);
-		CHECK_FCT_DO( fd_ep_clearflags( &peer->p_hdr.info.pi_endpoints, EP_FL_PRIMARY ), /* ignore the error */);
-		
-		/* Get the new ones */
-		CHECK_FCT_DO( fd_cnx_getremoteeps(peer->p_cnxctx, &peer->p_hdr.info.pi_endpoints), /* ignore the error */);
-		
-		/* We do not support local endpoints change currently, but it could be added here if needed (refresh fd_g_config->cnf_endpoints)*/
-		
-		if (TRACE_BOOL(ANNOYING)) {
-			TRACE_DEBUG(ANNOYING, "New remote endpoint(s):" );
-			fd_ep_dump(6, &peer->p_hdr.info.pi_endpoints);
-		}
-		
-		/* Done */
-		goto psm_loop;
-	}
-	
-	/* A new connection was established and CER containing this peer id was received */
-	if (event == FDEVP_CNX_INCOMING) {
-		struct cnx_incoming * params = ev_data;
-		ASSERT(params);
-		
-		/* Handle the message */
-		CHECK_FCT_DO( fd_p_ce_handle_newCER(&params->cer, peer, &params->cnx, params->validate), goto psm_end );
-		
-		/* Cleanup if needed */
-		if (params->cnx) {
-			fd_cnx_destroy(params->cnx);
-			params->cnx = NULL;
-		}
-		if (params->cer) {
-			CHECK_FCT_DO( fd_msg_free(params->cer), );
-			params->cer = NULL;
-		}
-		
-		/* Loop */
-		free(ev_data);
-		goto psm_loop;
-	}
-	
-	/* A new connection has been established with the remote peer */
-	if (event == FDEVP_CNX_ESTABLISHED) {
-		struct cnxctx * cnx = ev_data;
-		
-		/* Release the resources of the connecting thread */
-		CHECK_POSIX_DO( pthread_join( peer->p_ini_thr, NULL), /* ignore, it is not a big deal */);
-		peer->p_ini_thr = (pthread_t)NULL;
-		
-		switch (peer->p_hdr.info.runtime.pir_state) {
-			case STATE_WAITCNXACK_ELEC:
-			case STATE_WAITCNXACK:
-				fd_p_ce_handle_newcnx(peer, cnx);
-				break;
-				
-			default:
-				/* Just abort the attempt and continue */
-				TRACE_DEBUG(FULL, "Connection attempt successful but current state is %s, closing...", STATE_STR(peer->p_hdr.info.runtime.pir_state));
-				fd_cnx_destroy(cnx);
-		}
-		
-		goto psm_loop;
-	}
-	
-	/* A new connection has not been established with the remote peer */
-	if (event == FDEVP_CNX_FAILED) {
-		
-		/* Release the resources of the connecting thread */
-		CHECK_POSIX_DO( pthread_join( peer->p_ini_thr, NULL), /* ignore, it is not a big deal */);
-		peer->p_ini_thr = (pthread_t)NULL;
-		
-		switch (peer->p_hdr.info.runtime.pir_state) {
-			case STATE_WAITCNXACK_ELEC:
-				/* Abort the initiating side */
-				fd_p_cnx_abort(peer, 0);
-				/* Process the receiver side */
-				CHECK_FCT_DO( fd_p_ce_process_receiver(peer), goto psm_end );
-				break;
-				
-			case STATE_WAITCNXACK:
-				/* Go back to CLOSE */
-				fd_psm_next_timeout(peer, 1, peer->p_hdr.info.config.pic_tctimer ?: fd_g_config->cnf_timer_tc);
-				goto psm_reset;
-				
-			default:
-				/* Just ignore */
-				TRACE_DEBUG(FULL, "Connection attempt failed but current state is %s, ignoring...", STATE_STR(peer->p_hdr.info.runtime.pir_state));
-		}
-		
-		goto psm_loop;
-	}
-	
-	/* The timeout for the current state has been reached */
-	if (event == FDEVP_PSM_TIMEOUT) {
-		switch (peer->p_hdr.info.runtime.pir_state) {
-			case STATE_OPEN:
-			case STATE_REOPEN:
-				CHECK_FCT_DO( fd_p_dw_timeout(peer), goto psm_end );
-				goto psm_loop;
-				
-			case STATE_CLOSED:
-				CHECK_FCT_DO( fd_psm_change_state(peer, STATE_WAITCNXACK), goto psm_end );
-				fd_psm_next_timeout(peer, 0, CNX_TIMEOUT);
-				CHECK_FCT_DO( fd_p_cnx_init(peer), goto psm_end );
-				goto psm_loop;
-				
-			case STATE_SUSPECT:
-				/* Mark the connection problem */
-				peer->p_flags.pf_cnx_pb = 1;
-				
-			case STATE_CLOSING:
-			case STATE_WAITCNXACK:
-			case STATE_WAITCEA:
-				/* Destroy the connection, restart the timer to a new connection attempt */
-				fd_psm_next_timeout(peer, 1, peer->p_hdr.info.config.pic_tctimer ?: fd_g_config->cnf_timer_tc);
-				goto psm_reset;
-				
-			case STATE_WAITCNXACK_ELEC:
-				/* Abort the initiating side */
-				fd_p_cnx_abort(peer, 0);
-				/* Process the receiver side */
-				CHECK_FCT_DO( fd_p_ce_process_receiver(peer), goto psm_end );
-				goto psm_loop;
-			
-			default:
-				ASSERT(0); /* implementation problem, we did not foresee this case? */
-		}
-	}
-	
-	/* Default action : the handling has not yet been implemented. [for debug only] */
-	TRACE_DEBUG(INFO, "Missing handler in PSM for '%s'\t<-- '%s'", STATE_STR(peer->p_hdr.info.runtime.pir_state), fd_pev_str(event));
-psm_reset:
-	if (peer->p_flags.pf_delete)
-		goto psm_end;
-	fd_psm_cleanup(peer, 0);
-	goto psm_loop;
-	
-psm_end:
-	fd_psm_cleanup(peer, 1);
-	TRACE_DEBUG(INFO, "'%s'\t-> STATE_ZOMBIE (terminated)\t'%s'",
-			STATE_STR(peer->p_hdr.info.runtime.pir_state),
-			peer->p_hdr.info.pi_diamid);
-	pthread_cleanup_pop(1); /* set STATE_ZOMBIE */
-	fd_cpu_flush_cache();
-	peer->p_psm = (pthread_t)NULL;
-	pthread_detach(pthread_self());
-	return NULL;
-}
-
-
-/************************************************************************/
-/*                      Functions to control the PSM                    */
-/************************************************************************/
-/* Create the PSM thread of one peer structure */
-int fd_psm_begin(struct fd_peer * peer )
-{
-	TRACE_ENTRY("%p", peer);
-	
-	/* Check the peer and state are OK */
-	CHECK_PARAMS( CHECK_PEER(peer) && (peer->p_hdr.info.runtime.pir_state == STATE_NEW) );
-	
-	/* Create the FIFO for events */
-	CHECK_FCT( fd_fifo_new(&peer->p_events) );
-	
-	/* Create the PSM controler thread */
-	CHECK_POSIX( pthread_create( &peer->p_psm, NULL, p_psm_th, peer ) );
-	
-	/* We're done */
-	return 0;
-}
-
-/* End the PSM (clean ending) */
-int fd_psm_terminate(struct fd_peer * peer, char * reason )
-{
-	TRACE_ENTRY("%p", peer);
-	CHECK_PARAMS( CHECK_PEER(peer) );
-	
-	fd_cpu_flush_cache();
-	if (peer->p_hdr.info.runtime.pir_state != STATE_ZOMBIE) {
-		CHECK_FCT( fd_event_send(peer->p_events, FDEVP_TERMINATE, 0, reason) );
-	} else {
-		TRACE_DEBUG(FULL, "Peer '%s' was already terminated", peer->p_hdr.info.pi_diamid);
-	}
-	return 0;
-}
-
-/* End the PSM & cleanup the peer structure */
-void fd_psm_abord(struct fd_peer * peer )
-{
-	TRACE_ENTRY("%p", peer);
-	
-	/* Cancel PSM thread */
-	CHECK_FCT_DO( fd_thr_term(&peer->p_psm), /* continue */ );
-	
-	/* Cleanup the data */
-	fd_psm_cleanup(peer, 1);
-	
-	/* Destroy the event list */
-	CHECK_FCT_DO( fd_fifo_del(&peer->p_events), /* continue */ );
-	
-	/* Remaining cleanups are performed in fd_peer_free */
-	return;
-}
-
Index: eeDiameter/p_sr.c
===================================================================
--- freeDiameter/p_sr.c	(revision 650)
+++ 	(revision )
@@ -1,321 +1,0 @@
-/*********************************************************************************************************
-* Software License Agreement (BSD License)                                                               *
-* Author: Sebastien Decugis <sdecugis@nict.go.jp>							 *
-*													 *
-* Copyright (c) 2010, 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 "fD.h"
-
-#ifndef SR_DEBUG_LVL
-#define SR_DEBUG_LVL ANNOYING
-#endif /* SR_DEBUG_LVL */
-
-/* Structure to store a sent request */
-struct sentreq {
-	struct fd_list	chain; 	/* the "o" field points directly to the hop-by-hop of the request (uint32_t *)  */
-	struct msg	*req;	/* A request that was sent and not yet answered. */
-	uint32_t	prevhbh;/* The value to set in the hbh header when the message is retrieved */
-	struct fd_list  expire; /* the list of expiring requests */
-	struct timespec added_on; /* the time the request was added */
-};
-
-/* Find an element in the hbh list, or the following one */
-static struct fd_list * find_or_next(struct fd_list * srlist, uint32_t hbh, int * match)
-{
-	struct fd_list * li;
-	*match = 0;
-	for (li = srlist->next; li != srlist; li = li->next) {
-		uint32_t * nexthbh = li->o;
-		if (*nexthbh < hbh)
-			continue;
-		if (*nexthbh == hbh)
-			*match = 1;
-		break;
-	}
-	return li;
-}
-
-static void srl_dump(const char * text, struct fd_list * srlist)
-{
-	struct fd_list * li;
-	struct timespec now;
-	if (!TRACE_BOOL(SR_DEBUG_LVL))
-		return;
-	CHECK_SYS_DO( clock_gettime(CLOCK_REALTIME, &now), );
-	fd_log_debug("%sSentReq list @%p:\n", text, srlist);
-	for (li = srlist->next; li != srlist; li = li->next) {
-		struct sentreq * sr = (struct sentreq *)li;
-		uint32_t * nexthbh = li->o;
-		fd_log_debug(" - Next req (%x): [since %ld.%06ld sec]\n", *nexthbh, 
-			(now.tv_nsec >= sr->added_on.tv_nsec) ? (now.tv_sec - sr->added_on.tv_sec) : (now.tv_sec - sr->added_on.tv_sec - 1),
-			(now.tv_nsec >= sr->added_on.tv_nsec) ? (now.tv_nsec - sr->added_on.tv_nsec) / 1000 : (now.tv_nsec - sr->added_on.tv_nsec + 1000000000) / 1000);
-		fd_msg_dump_one(SR_DEBUG_LVL + 1, sr->req);
-	}
-}
-
-/* (detached) thread that calls the anscb on expired messages. 
-  We do it in a separate thread to avoid blocking the reception of new messages during this time */
-static void * call_anscb_expire(void * arg) {
-	struct msg * expired_req = arg;
-	
-	void (*anscb)(void *, struct msg **);
-	void * data;
-	
-	TRACE_ENTRY("%p", arg);
-	CHECK_PARAMS_DO( arg, return NULL );
-	
-	/* Set the thread name */
-	fd_log_threadname ( "Expired req cb." );
-	
-	/* Log */
-	TRACE_DEBUG(INFO, "The expiration timer for a request has been reached, abording this attempt now & calling cb...");
-	
-	/* Retrieve callback in the message */
-	CHECK_FCT_DO( fd_msg_anscb_get( expired_req, &anscb, &data ), return NULL);
-	ASSERT(anscb);
-
-	/* Call it */
-	(*anscb)(data, &expired_req);
-	
-	/* If the callback did not dispose of the message, do it now */
-	if (expired_req) {
-		CHECK_FCT_DO( fd_msg_free(expired_req), /* ignore */ );
-	}
-	
-	/* Finish */
-	return NULL;
-}
-
-/* thread that handles messages expiring. The thread is started / stopped only when needed */
-static void * sr_expiry_th(void * arg) {
-	struct sr_list * srlist = arg;
-	struct msg * expired_req;
-	pthread_attr_t detached;
-	
-	TRACE_ENTRY("%p", arg);
-	CHECK_PARAMS_DO( arg, return NULL );
-	
-	/* Set the thread name */
-	{
-		char buf[48];
-		sprintf(buf, "ReqExp/%.*s", (int)sizeof(buf) - 8, ((struct fd_peer *)(srlist->exp.o))->p_hdr.info.pi_diamid);
-		fd_log_threadname ( buf );
-	}
-	
-	CHECK_POSIX_DO( pthread_attr_init(&detached), return NULL );
-	CHECK_POSIX_DO( pthread_attr_setdetachstate(&detached, PTHREAD_CREATE_DETACHED), return NULL );
-	
-	CHECK_POSIX_DO( pthread_mutex_lock(&srlist->mtx),  return NULL );
-	pthread_cleanup_push( fd_cleanup_mutex, &srlist->mtx );
-	
-	do {
-		struct timespec	now, *t;
-		struct sentreq * first;
-		pthread_t th;
-		
-		/* Check if there are expiring requests available */
-		if (FD_IS_LIST_EMPTY(&srlist->exp)) {
-			/* Just wait for a change or cancelation */
-			CHECK_POSIX_DO( pthread_cond_wait( &srlist->cnd, &srlist->mtx ), goto error );
-			/* Restart the loop on wakeup */
-			continue;
-		}
-		
-		/* Get the pointer to the request that expires first */
-		first = (struct sentreq *)(srlist->exp.next->o);
-		t = fd_msg_anscb_gettimeout( first->req );
-		ASSERT(t);
-		
-		/* Get the current time */
-		CHECK_SYS_DO(  clock_gettime(CLOCK_REALTIME, &now),  goto error  );
-
-		/* If first request is not expired, we just wait until it happens */
-		if ( TS_IS_INFERIOR( &now, t ) ) {
-			
-			CHECK_POSIX_DO2(  pthread_cond_timedwait( &srlist->cnd, &srlist->mtx, t ),  
-					ETIMEDOUT, /* ETIMEDOUT is a normal return value, continue */,
-					/* on other error, */ goto error );
-	
-			/* on wakeup, loop */
-			continue;
-		}
-		
-		/* Now, the first request in the list is expired; remove it and call the anscb for it in a new thread */
-		fd_list_unlink(&first->chain);
-		fd_list_unlink(&first->expire);
-		expired_req = first->req;
-		free(first);
-		
-		CHECK_POSIX_DO( pthread_create( &th, &detached, call_anscb_expire, expired_req ), goto error );
-
-		/* loop */
-	} while (1);
-error:	
-	; /* pthread_cleanup_pop sometimes expands as "} ..." and the label beofre this cause some compilers to complain... */
-	pthread_cleanup_pop( 1 );
-	return NULL;
-}
-
-
-/* Store a new sent request */
-int fd_p_sr_store(struct sr_list * srlist, struct msg **req, uint32_t *hbhloc, uint32_t hbh_restore)
-{
-	struct sentreq * sr;
-	struct fd_list * next;
-	int match;
-	struct timespec * ts;
-	
-	TRACE_ENTRY("%p %p %p %x", srlist, req, hbhloc, hbh_restore);
-	CHECK_PARAMS(srlist && req && *req && hbhloc);
-	
-	CHECK_MALLOC( sr = malloc(sizeof(struct sentreq)) );
-	memset(sr, 0, sizeof(struct sentreq));
-	fd_list_init(&sr->chain, hbhloc);
-	sr->req = *req;
-	sr->prevhbh = hbh_restore;
-	fd_list_init(&sr->expire, sr);
-	CHECK_SYS( clock_gettime(CLOCK_REALTIME, &sr->added_on) );
-	
-	/* Search the place in the list */
-	CHECK_POSIX( pthread_mutex_lock(&srlist->mtx) );
-	next = find_or_next(&srlist->srs, *hbhloc, &match);
-	if (match) {
-		TRACE_DEBUG(INFO, "A request with the same hop-by-hop Id was already sent: error");
-		free(sr);
-		CHECK_POSIX_DO( pthread_mutex_unlock(&srlist->mtx), /* ignore */ );
-		return EINVAL;
-	}
-	
-	/* Save in the list */
-	*req = NULL;
-	fd_list_insert_before(next, &sr->chain);
-	srl_dump("Saved new request, ", &srlist->srs);
-	
-	/* In case of request with a timeout, also store in the timeout list */
-	ts = fd_msg_anscb_gettimeout( sr->req );
-	if (ts) {
-		struct fd_list * li;
-		struct timespec * t;
-		
-		/* browse srlist->exp from the end */
-		for (li = srlist->exp.prev; li != &srlist->exp; li = li->prev) {
-			struct sentreq * s = (struct sentreq *)(li->o);
-			t = fd_msg_anscb_gettimeout( s->req );
-			ASSERT( t ); /* sanity */
-			if (TS_IS_INFERIOR(t, ts))
-				break;
-		}
-		
-		fd_list_insert_after(li, &sr->expire);
-	
-		/* if the thread does not exist yet, create it */
-		if (srlist->thr == (pthread_t)NULL) {
-			CHECK_POSIX_DO( pthread_create(&srlist->thr, NULL, sr_expiry_th, srlist), /* continue anyway */);
-		} else {
-			/* or, if added in first position, signal the condvar to update the sleep time of the thread */
-			if (li == &srlist->exp) {
-				CHECK_POSIX_DO( pthread_cond_signal(&srlist->cnd), /* continue anyway */);
-			}
-		}
-	}
-	
-	CHECK_POSIX( pthread_mutex_unlock(&srlist->mtx) );
-	return 0;
-}
-
-/* Fetch a request by hbh */
-int fd_p_sr_fetch(struct sr_list * srlist, uint32_t hbh, struct msg **req)
-{
-	struct sentreq * sr;
-	int match;
-	
-	TRACE_ENTRY("%p %x %p", srlist, hbh, req);
-	CHECK_PARAMS(srlist && req);
-	
-	/* Search the request in the list */
-	CHECK_POSIX( pthread_mutex_lock(&srlist->mtx) );
-	srl_dump("Fetching a request, ", &srlist->srs);
-	sr = (struct sentreq *)find_or_next(&srlist->srs, hbh, &match);
-	if (!match) {
-		TRACE_DEBUG(INFO, "There is no saved request with this hop-by-hop id (%x)", hbh);
-		*req = NULL;
-	} else {
-		/* Restore hop-by-hop id */
-		*((uint32_t *)sr->chain.o) = sr->prevhbh;
-		/* Unlink */
-		fd_list_unlink(&sr->chain);
-		fd_list_unlink(&sr->expire);
-		*req = sr->req;
-		free(sr);
-	}
-	CHECK_POSIX( pthread_mutex_unlock(&srlist->mtx) );
-	
-	/* do not stop the expire thread here, it might cause creating/destroying it very often otherwise */
-
-	/* Done */
-	return 0;
-}
-
-/* Failover requests (free or requeue routables) */
-void fd_p_sr_failover(struct sr_list * srlist)
-{
-	CHECK_POSIX_DO( pthread_mutex_lock(&srlist->mtx), /* continue anyway */ );
-	while (!FD_IS_LIST_EMPTY(&srlist->srs)) {
-		struct sentreq * sr = (struct sentreq *)(srlist->srs.next);
-		fd_list_unlink(&sr->chain);
-		fd_list_unlink(&sr->expire);
-		if (fd_msg_is_routable(sr->req)) {
-			struct msg_hdr * hdr = NULL;
-			
-			/* Set the 'T' flag */
-			CHECK_FCT_DO(fd_msg_hdr(sr->req, &hdr), /* continue */);
-			if (hdr)
-				hdr->msg_flags |= CMD_FLAG_RETRANSMIT;
-			
-			/* Requeue for sending to another peer */
-			CHECK_FCT_DO(fd_fifo_post(fd_g_outgoing, &sr->req),
-					CHECK_FCT_DO(fd_msg_free(sr->req), /* What can we do more? */));
-		} else {
-			/* Just free the request... */
-			CHECK_FCT_DO(fd_msg_free(sr->req), /* Ignore */);
-		}
-		free(sr);
-	}
-	/* The list of expiring requests must be empty now */
-	ASSERT( FD_IS_LIST_EMPTY(&srlist->exp) );
-	
-	CHECK_POSIX_DO( pthread_mutex_unlock(&srlist->mtx), /* continue anyway */ );
-	
-	/* Terminate the expiry thread (must be done when the lock can be taken) */
-	CHECK_FCT_DO( fd_thr_term(&srlist->thr), /* ignore error */ );
-}
-
Index: eeDiameter/peers.c
===================================================================
--- freeDiameter/peers.c	(revision 649)
+++ 	(revision )
@@ -1,546 +1,0 @@
-/*********************************************************************************************************
-* Software License Agreement (BSD License)                                                               *
-* Author: Sebastien Decugis <sdecugis@nict.go.jp>							 *
-*													 *
-* Copyright (c) 2010, 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 "fD.h"
-
-/* Global list of peers */
-struct fd_list   fd_g_peers = FD_LIST_INITIALIZER(fd_g_peers);
-pthread_rwlock_t fd_g_peers_rw = PTHREAD_RWLOCK_INITIALIZER;
-
-/* List of active peers */
-struct fd_list   fd_g_activ_peers = FD_LIST_INITIALIZER(fd_g_activ_peers);	/* peers linked by their p_actives oredered by p_diamid */
-pthread_rwlock_t fd_g_activ_peers_rw = PTHREAD_RWLOCK_INITIALIZER;
-
-/* List of validation callbacks (registered with fd_peer_validate_register) */
-static struct fd_list validators = FD_LIST_INITIALIZER(validators);	/* list items are simple fd_list with "o" pointing to the callback */
-static pthread_rwlock_t validators_rw = PTHREAD_RWLOCK_INITIALIZER;
-
-
-/* Alloc / reinit a peer structure. if *ptr is not NULL, it must already point to a valid struct fd_peer. */
-int fd_peer_alloc(struct fd_peer ** ptr)
-{
-	struct fd_peer *p;
-	
-	TRACE_ENTRY("%p", ptr);
-	CHECK_PARAMS(ptr);
-	
-	if (*ptr) {
-		p = *ptr;
-	} else {
-		CHECK_MALLOC( p = malloc(sizeof(struct fd_peer)) );
-		*ptr = p;
-	}
-	
-	/* Now initialize the content */
-	memset(p, 0, sizeof(struct fd_peer));
-	
-	fd_list_init(&p->p_hdr.chain, p);
-	
-	fd_list_init(&p->p_hdr.info.pi_endpoints, p);
-	fd_list_init(&p->p_hdr.info.runtime.pir_apps, p);
-	
-	p->p_eyec = EYEC_PEER;
-	fd_list_init(&p->p_actives, p);
-	fd_list_init(&p->p_expiry, p);
-	CHECK_FCT( fd_fifo_new(&p->p_tosend) );
-	p->p_hbh = lrand48();
-	
-	fd_list_init(&p->p_sr.srs, p);
-	fd_list_init(&p->p_sr.exp, p);
-	CHECK_POSIX( pthread_mutex_init(&p->p_sr.mtx, NULL) );
-	CHECK_POSIX( pthread_cond_init(&p->p_sr.cnd, NULL) );
-	
-	fd_list_init(&p->p_connparams, p);
-	
-	return 0;
-}
-
-/* Add a new peer entry */
-int fd_peer_add ( struct peer_info * info, char * orig_dbg, void (*cb)(struct peer_info *, void *), void * cb_data )
-{
-	struct fd_peer *p = NULL;
-	struct fd_list * li;
-	int ret = 0;
-	TRACE_ENTRY("%p %p %p %p", info, orig_dbg, cb, cb_data);
-	CHECK_PARAMS(info && info->pi_diamid);
-	
-	/* Create a structure to contain the new peer information */
-	CHECK_FCT( fd_peer_alloc(&p) );
-	
-	/* Copy the informations from the parameters received */
-	CHECK_MALLOC( p->p_hdr.info.pi_diamid = strdup(info->pi_diamid) );
-	
-	memcpy( &p->p_hdr.info.config, &info->config, sizeof(p->p_hdr.info.config) );
-	/* Duplicate the strings if provided */
-	if (info->config.pic_realm) {
-		CHECK_MALLOC( p->p_hdr.info.config.pic_realm = strdup(info->config.pic_realm) );
-	}
-	if (info->config.pic_priority) {
-		CHECK_MALLOC( p->p_hdr.info.config.pic_realm = strdup(info->config.pic_priority) );
-	}
-	
-	/* Move the list of endpoints into the peer */
-	if (info->pi_endpoints.next)
-		while (!FD_IS_LIST_EMPTY( &info->pi_endpoints ) ) {
-			li = info->pi_endpoints.next;
-			fd_list_unlink(li);
-			fd_list_insert_before(&p->p_hdr.info.pi_endpoints, li);
-		}
-	
-	/* The internal data */
-	if (orig_dbg) {
-		CHECK_MALLOC( p->p_dbgorig = strdup(orig_dbg) );
-	} else {
-		CHECK_MALLOC( p->p_dbgorig = strdup("unknown") );
-	}
-	p->p_cb = cb;
-	p->p_cb_data = cb_data;
-	
-	/* Ok, now check if we don't already have an entry with the same Diameter Id, and insert this one */
-	CHECK_POSIX( pthread_rwlock_wrlock(&fd_g_peers_rw) );
-	
-	for (li = fd_g_peers.next; li != &fd_g_peers; li = li->next) {
-		struct fd_peer * next = (struct fd_peer *)li;
-		int cmp = strcasecmp( p->p_hdr.info.pi_diamid, next->p_hdr.info.pi_diamid );
-		if (cmp > 0)
-			continue;
-		if (cmp == 0)
-			ret = EEXIST;
-		break;
-	}
-	
-	/* We can insert the new peer object */
-	if (! ret)
-		do {
-			/* Update expiry list */
-			CHECK_FCT_DO( ret = fd_p_expi_update( p ), break );
-
-			/* Insert the new element in the list */
-			fd_list_insert_before( li, &p->p_hdr.chain );
-		} while (0);
-
-	CHECK_POSIX( pthread_rwlock_unlock(&fd_g_peers_rw) );
-	if (ret) {
-		CHECK_FCT( fd_peer_free(&p) );
-	} else {
-		CHECK_FCT( fd_psm_begin(p) );
-	}
-	return ret;
-}
-
-/* Search for a peer */
-int fd_peer_getbyid( char * diamid, struct peer_hdr ** peer )
-{
-	struct fd_list * li;
-	
-	TRACE_ENTRY("%p %p", diamid, peer);
-	CHECK_PARAMS( diamid && peer );
-	
-	*peer = NULL;
-	
-	/* Search in the list */
-	CHECK_POSIX( pthread_rwlock_rdlock(&fd_g_peers_rw) );
-	for (li = fd_g_peers.next; li != &fd_g_peers; li = li->next) {
-		struct fd_peer * next = (struct fd_peer *)li;
-		int cmp = strcasecmp( diamid, next->p_hdr.info.pi_diamid );
-		if (cmp > 0)
-			continue;
-		if (cmp == 0)
-			*peer = &next->p_hdr;
-		break;
-	}
-	CHECK_POSIX( pthread_rwlock_unlock(&fd_g_peers_rw) );
-	
-	return 0;
-}
-
-
-#define free_null( _v ) 	\
-	if (_v) {		\
-		free(_v);	\
-		(_v) = NULL;	\
-	}
-	
-#define free_list( _l ) 						\
-	while (!FD_IS_LIST_EMPTY(_l)) {					\
-		struct fd_list * __li = ((struct fd_list *)(_l))->next;	\
-		fd_list_unlink(__li);					\
-		free(__li);						\
-	}
-
-/* Empty the lists of p_tosend and p_sentreq messages */
-void fd_peer_failover_msg(struct fd_peer * peer)
-{
-	struct msg *m;
-	TRACE_ENTRY("%p", peer);
-	CHECK_PARAMS_DO(CHECK_PEER(peer), return);
-	
-	/* Requeue all messages in the "out" queue */
-	while ( fd_fifo_tryget(peer->p_tosend, &m) == 0 ) {
-		CHECK_FCT_DO(fd_fifo_post(fd_g_outgoing, &m), 
-				/* fallback: destroy the message */
-				CHECK_FCT_DO(fd_msg_free(m), /* What can we do more? */));
-	}
-	
-	/* Requeue all routable sent requests */
-	fd_p_sr_failover(&peer->p_sr);
-	
-	/* Done */
-	return;
-}
-
-/* Destroy a structure once cleanups have been performed (fd_psm_abord, ...) */
-int fd_peer_free(struct fd_peer ** ptr)
-{
-	struct fd_peer *p;
-	
-	TRACE_ENTRY("%p", ptr);
-	CHECK_PARAMS(ptr);
-	p = *ptr;
-	*ptr = NULL;
-	CHECK_PARAMS(p);
-	
-	CHECK_PARAMS( FD_IS_LIST_EMPTY(&p->p_hdr.chain) );
-	
-	free_null(p->p_hdr.info.pi_diamid);
-	
-	free_null(p->p_hdr.info.config.pic_realm); 
-	free_null(p->p_hdr.info.config.pic_priority); 
-	
-	free_null(p->p_hdr.info.runtime.pir_realm);
-	free_null(p->p_hdr.info.runtime.pir_prodname);
-	free_list( &p->p_hdr.info.runtime.pir_apps );
-	
-	free_list( &p->p_hdr.info.pi_endpoints );
-	
-	free_null(p->p_dbgorig);
-	
-	fd_list_unlink(&p->p_expiry);
-	fd_list_unlink(&p->p_actives);
-	
-	CHECK_FCT_DO( fd_fifo_del(&p->p_tosend), /* continue */ );
-	CHECK_POSIX_DO( pthread_mutex_destroy(&p->p_sr.mtx), /* continue */);
-	CHECK_POSIX_DO( pthread_cond_destroy(&p->p_sr.cnd), /* continue */);
-	
-	/* If the callback is still around... */
-	if (p->p_cb)
-		(*p->p_cb)(NULL, p->p_cb_data);
-	
-	/* Free the structure */
-	free(p);
-	return 0;
-}
-
-/* Terminate peer module (destroy all peers, first gently, then violently) */
-int fd_peer_fini()
-{
-	struct fd_list * li;
-	struct fd_list purge = FD_LIST_INITIALIZER(purge); /* Store zombie peers here */
-	int list_empty;
-	struct timespec	wait_until, now;
-	
-	TRACE_ENTRY();
-	
-	CHECK_FCT_DO(fd_p_expi_fini(), /* continue */);
-	
-	TRACE_DEBUG(INFO, "Sending terminate signal to all peer connections");
-	
-	CHECK_FCT_DO( pthread_rwlock_wrlock(&fd_g_peers_rw), /* continue */ );
-	for (li = fd_g_peers.next; li != &fd_g_peers; li = li->next) {
-		struct fd_peer * peer = (struct fd_peer *)li;
-		
-		fd_cpu_flush_cache();
-		if (peer->p_hdr.info.runtime.pir_state != STATE_ZOMBIE) {
-			CHECK_FCT_DO( fd_psm_terminate(peer, "REBOOTING"), /* continue */ );
-		} else {
-			li = li->prev; /* to avoid breaking the loop */
-			fd_list_unlink(&peer->p_hdr.chain);
-			fd_list_insert_before(&purge, &peer->p_hdr.chain);
-		}
-	}
-	list_empty = FD_IS_LIST_EMPTY(&fd_g_peers);
-	CHECK_FCT_DO( pthread_rwlock_unlock(&fd_g_peers_rw), /* continue */ );
-	
-	if (!list_empty) {
-		CHECK_SYS(  clock_gettime(CLOCK_REALTIME, &now)  );
-		TRACE_DEBUG(INFO, "Waiting for connections shutdown... (%d sec max)", DPR_TIMEOUT + 1);
-		wait_until.tv_sec  = now.tv_sec + DPR_TIMEOUT + 1;
-		wait_until.tv_nsec = now.tv_nsec;
-	}
-	
-	while ((!list_empty) && (TS_IS_INFERIOR(&now, &wait_until))) {
-		
-		/* Allow the PSM(s) to execute */
-		sched_yield();
-		
-		/* Remove zombie peers */
-		CHECK_FCT_DO( pthread_rwlock_wrlock(&fd_g_peers_rw), /* continue */ );
-		for (li = fd_g_peers.next; li != &fd_g_peers; li = li->next) {
-			struct fd_peer * peer = (struct fd_peer *)li;
-			fd_cpu_flush_cache();
-			if (peer->p_hdr.info.runtime.pir_state == STATE_ZOMBIE) {
-				li = li->prev; /* to avoid breaking the loop */
-				fd_list_unlink(&peer->p_hdr.chain);
-				fd_list_insert_before(&purge, &peer->p_hdr.chain);
-			}
-		}
-		list_empty = FD_IS_LIST_EMPTY(&fd_g_peers);
-		CHECK_FCT_DO( pthread_rwlock_unlock(&fd_g_peers_rw), /* continue */ );
-		CHECK_SYS(  clock_gettime(CLOCK_REALTIME, &now)  );
-	}
-	
-	if (!list_empty) {
-		TRACE_DEBUG(INFO, "Forcing connections shutdown");
-		CHECK_FCT_DO( pthread_rwlock_wrlock(&fd_g_peers_rw), /* continue */ );
-		while (!FD_IS_LIST_EMPTY(&fd_g_peers)) {
-			struct fd_peer * peer = (struct fd_peer *)(fd_g_peers.next);
-			fd_psm_abord(peer);
-			fd_list_unlink(&peer->p_hdr.chain);
-			fd_list_insert_before(&purge, &peer->p_hdr.chain);
-		}
-		CHECK_FCT_DO( pthread_rwlock_unlock(&fd_g_peers_rw), /* continue */ );
-	}
-	
-	/* Free memory objects of all peers */
-	while (!FD_IS_LIST_EMPTY(&purge)) {
-		struct fd_peer * peer = (struct fd_peer *)(purge.next);
-		fd_list_unlink(&peer->p_hdr.chain);
-		fd_peer_free(&peer);
-	}
-	
-	/* Now empty the validators list */
-	CHECK_FCT_DO( pthread_rwlock_wrlock(&validators_rw), /* continue */ );
-	while (!FD_IS_LIST_EMPTY( &validators )) {
-		struct fd_list * v = validators.next;
-		fd_list_unlink(v);
-		free(v);
-	}
-	CHECK_FCT_DO( pthread_rwlock_unlock(&validators_rw), /* continue */ );
-	
-	return 0;
-}
-
-/* Dump info of one peer */
-void fd_peer_dump(struct fd_peer * peer, int details)
-{
-	if (peer->p_eyec != EYEC_PEER) {
-		fd_log_debug("  Invalid peer @ %p !\n", peer);
-		return;
-	}
-
-	fd_log_debug(">  %s\t%s", STATE_STR(peer->p_hdr.info.runtime.pir_state), peer->p_hdr.info.pi_diamid);
-	if (details > INFO) {
-		fd_log_debug("\t(rlm:%s)", peer->p_hdr.info.runtime.pir_realm ?: "(unknown)");
-		if (peer->p_hdr.info.runtime.pir_prodname)
-			fd_log_debug("\t['%s' %u]", peer->p_hdr.info.runtime.pir_prodname, peer->p_hdr.info.runtime.pir_firmrev);
-	}
-	fd_log_debug("\n");
-	if (details > FULL) {
-		/* Dump all info */
-		fd_log_debug("\tEntry origin : %s\n", peer->p_dbgorig?: "not set");
-		fd_log_debug("\tConfig flags : %s%s%s%s%s - %s%s%s\n", 
-				peer->p_hdr.info.config.pic_flags.pro3 == PI_P3_DEFAULT ? "" :
-					(peer->p_hdr.info.config.pic_flags.pro3 == PI_P3_IP ? "IP." : "IPv6."),
-				peer->p_hdr.info.config.pic_flags.pro4 == PI_P4_DEFAULT ? "" :
-					(peer->p_hdr.info.config.pic_flags.pro4 == PI_P4_TCP ? "TCP." : "SCTP."),
-				peer->p_hdr.info.config.pic_flags.alg ? "PrefTCP." : "",
-				peer->p_hdr.info.config.pic_flags.sec & PI_SEC_NONE ? "NoTLSok" :"",
-				peer->p_hdr.info.config.pic_flags.sec & PI_SEC_TLS_OLD ? "OldTLS" :"",
-				peer->p_hdr.info.config.pic_flags.exp ? "Expire." : "",
-				peer->p_hdr.info.config.pic_flags.persist ? "Persist." : ""
-				);
-		fd_log_debug("\tLifetime : %d sec\n", peer->p_hdr.info.config.pic_lft);
-	}
-}
-
-/* Dump the list of peers */
-void fd_peer_dump_list(int details)
-{
-	struct fd_list * li;
-	
-	fd_log_debug("Dumping list of peers :\n");
-	CHECK_FCT_DO( pthread_rwlock_rdlock(&fd_g_peers_rw), /* continue */ );
-	
-	for (li = fd_g_peers.next; li != &fd_g_peers; li = li->next) {
-		struct fd_peer * np = (struct fd_peer *)li;
-		fd_peer_dump(np, details);
-	}
-	
-	CHECK_FCT_DO( pthread_rwlock_unlock(&fd_g_peers_rw), /* continue */ );
-}
-
-/* Handle an incoming CER request on a new connection */
-int fd_peer_handle_newCER( struct msg ** cer, struct cnxctx ** cnx )
-{
-	struct msg * msg;
-	struct dict_object *avp_oh_model;
-	avp_code_t code = AC_ORIGIN_HOST;
-	struct avp *avp_oh;
-	struct avp_hdr * avp_hdr;
-	struct fd_list * li;
-	int found = 0;
-	int ret = 0;
-	struct fd_peer * peer;
-	struct cnx_incoming * ev_data;
-	
-	TRACE_ENTRY("%p %p", cer, cnx);
-	CHECK_PARAMS(cer && *cer && cnx && *cnx);
-	
-	msg = *cer; 
-	
-	/* Find the Diameter Identity of the remote peer in the message */
-	CHECK_FCT( fd_dict_search ( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_CODE, &code, &avp_oh_model, ENOENT) );
-	CHECK_FCT( fd_msg_search_avp ( msg, avp_oh_model, &avp_oh ) );
-	CHECK_FCT( fd_msg_avp_hdr ( avp_oh, &avp_hdr ) );
-	
-	/* Search if we already have this peer id in our list. We take directly the write lock so that we don't need to upgrade if it is a new peer.
-	 * There is space for a small optimization here if needed.
-	 */
-	CHECK_POSIX( pthread_rwlock_wrlock(&fd_g_peers_rw) );
-	
-	for (li = fd_g_peers.next; li != &fd_g_peers; li = li->next) {
-		peer = (struct fd_peer *)li;
-		/* It is probably unwise to use strcasecmp on UTF8 data... To be improved! */
-		int cmp = strncasecmp( (char *)avp_hdr->avp_value->os.data, peer->p_hdr.info.pi_diamid, avp_hdr->avp_value->os.len );
-		if (cmp > 0)
-			continue;
-		if (cmp == 0)
-			found = 1;
-		break;
-	}
-	
-	if (!found) {
-		/* Create a new peer entry for this new remote peer */
-		peer = NULL;
-		CHECK_FCT_DO( ret = fd_peer_alloc(&peer), goto out );
-		
-		/* Set the peer Diameter Id and the responder flag parameters */
-		CHECK_MALLOC_DO( peer->p_hdr.info.pi_diamid = malloc(avp_hdr->avp_value->os.len + 1), { ret = ENOMEM; goto out; } );
-		memcpy(peer->p_hdr.info.pi_diamid, avp_hdr->avp_value->os.data, avp_hdr->avp_value->os.len);
-		peer->p_hdr.info.pi_diamid[avp_hdr->avp_value->os.len] = '\0';
-		CHECK_MALLOC_DO( peer->p_dbgorig = strdup(fd_cnx_getid(*cnx)), { ret = ENOMEM; goto out; } );
-		peer->p_flags.pf_responder = 1;
-		peer->p_flags.pf_delete = 1;
-		
-		/* Set this peer to expire on inactivity */
-		peer->p_hdr.info.config.pic_flags.exp 	= PI_EXP_INACTIVE;
-		peer->p_hdr.info.config.pic_lft		= 3600;	/* 1 hour without any message 
-		-- RFC3539 states that this must not be inferior to BRINGDOWN_INTERVAL = 5 minutes */
-		
-		/* Insert the new peer in the list (the PSM will take care of setting the expiry after validation) */
-		fd_list_insert_before( li, &peer->p_hdr.chain );
-		
-		/* Start the PSM, which will receive the event bellow */
-		CHECK_FCT_DO( ret = fd_psm_begin(peer), goto out );
-	} else {
-		/* Check if the peer is in zombie state */
-		fd_cpu_flush_cache();
-		if (peer->p_hdr.info.runtime.pir_state == STATE_ZOMBIE) {
-			/* Re-activate the peer */
-			if (peer->p_hdr.info.config.pic_flags.exp)
-				peer->p_flags.pf_responder = 1;
-			peer->p_hdr.info.runtime.pir_state = STATE_NEW;
-			CHECK_FCT_DO( ret = fd_psm_begin(peer), goto out );
-		}
-	}
-		
-	/* Send the new connection event to the PSM */
-	CHECK_MALLOC_DO( ev_data = malloc(sizeof(struct cnx_incoming)), { ret = ENOMEM; goto out; } );
-	memset(ev_data, 0, sizeof(ev_data));
-	
-	ev_data->cer = msg;
-	ev_data->cnx = *cnx;
-	ev_data->validate = !found;
-	
-	CHECK_FCT_DO( ret = fd_event_send(peer->p_events, FDEVP_CNX_INCOMING, sizeof(ev_data), ev_data), goto out );
-	
-out:	
-	CHECK_POSIX( pthread_rwlock_unlock(&fd_g_peers_rw) );
-
-	if (ret == 0) {
-		/* Reset the "out" parameters, so that they are not cleanup on function return. */
-		*cer = NULL;
-		*cnx = NULL;
-	}
-	
-	return ret;
-}
-
-/* Save a callback to accept / reject incoming unknown peers */
-int fd_peer_validate_register ( int (*peer_validate)(struct peer_info * /* info */, int * /* auth */, int (**cb2)(struct peer_info *)) )
-{
-	struct fd_list * v;
-	
-	TRACE_ENTRY("%p", peer_validate);
-	CHECK_PARAMS(peer_validate);
-	
-	/* Alloc a new entry */
-	CHECK_MALLOC( v = malloc(sizeof(struct fd_list)) );
-	fd_list_init( v, peer_validate );
-	
-	/* Add at the beginning of the list */
-	CHECK_FCT( pthread_rwlock_wrlock(&validators_rw) );
-	fd_list_insert_after(&validators, v);
-	CHECK_FCT( pthread_rwlock_unlock(&validators_rw));
-	
-	/* Done! */
-	return 0;
-}
-
-/* Validate a peer by calling the callbacks in turn -- return 0 if the peer is validated, ! 0 in case of error (>0) or if the peer is rejected (-1) */
-int fd_peer_validate( struct fd_peer * peer )
-{
-	int ret = 0;
-	struct fd_list * v;
-	
-	CHECK_FCT( pthread_rwlock_rdlock(&validators_rw) );
-	for (v = validators.next; v != &validators; v = v->next) {
-		int auth = 0;
-		pthread_cleanup_push(fd_cleanup_rwlock, &validators_rw);
-		CHECK_FCT_DO( ret = ((int(*)(struct peer_info *, int *, int (**)(struct peer_info *)))(v->o)) (&peer->p_hdr.info, &auth, &peer->p_cb2), goto out );
-		pthread_cleanup_pop(0);
-		if (auth) {
-			ret = (auth > 0) ? 0 : -1;
-			goto out;
-		}
-		peer->p_cb2 = NULL;
-	}
-	
-	/* No callback has given a firm result, the default is to reject */
-	ret = -1;
-out:
-	CHECK_FCT( pthread_rwlock_unlock(&validators_rw));
-	return ret;
-}
Index: eeDiameter/queues.c
===================================================================
--- freeDiameter/queues.c	(revision 124)
+++ 	(revision )
@@ -1,81 +1,0 @@
-/*********************************************************************************************************
-* Software License Agreement (BSD License)                                                               *
-* Author: Sebastien Decugis <sdecugis@nict.go.jp>							 *
-*													 *
-* Copyright (c) 2009, WIDE Project and NICT								 *
-* All rights reserved.											 *
-* 													 *
-* Redistribution and use of this software in source and binary forms, with or without modification, are  *
-* permitted provided that the following conditions are met:						 *
-* 													 *
-* * Redistributions of source code must retain the above 						 *
-*   copyright notice, this list of conditions and the 							 *
-*   following disclaimer.										 *
-*    													 *
-* * Redistributions in binary form must reproduce the above 						 *
-*   copyright notice, this list of conditions and the 							 *
-*   following disclaimer in the documentation and/or other						 *
-*   materials provided with the distribution.								 *
-* 													 *
-* * Neither the name of the WIDE Project or NICT nor the 						 *
-*   names of its contributors may be used to endorse or 						 *
-*   promote products derived from this software without 						 *
-*   specific prior written permission of WIDE Project and 						 *
-*   NICT.												 *
-* 													 *
-* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED *
-* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
-* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR *
-* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 	 *
-* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 	 *
-* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR *
-* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF   *
-* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.								 *
-*********************************************************************************************************/
-
-#include "fD.h"
-
-/* The global message queues */
-struct fifo * fd_g_incoming = NULL;
-struct fifo * fd_g_outgoing = NULL;
-struct fifo * fd_g_local = NULL;
-
-/* Initialize the message queues. */
-int fd_queues_init(void)
-{
-	TRACE_ENTRY();
-	CHECK_FCT( fd_fifo_new ( &fd_g_incoming ) );
-	CHECK_FCT( fd_fifo_new ( &fd_g_outgoing ) );
-	CHECK_FCT( fd_fifo_new ( &fd_g_local ) );
-	return 0;
-}
-
-/* Destroy a queue after emptying it (and dumping the content) */
-int fd_queues_fini(struct fifo ** queue)
-{
-	struct msg * msg;
-	int ret = 0;
-	
-	TRACE_ENTRY("%p", queue);
-	
-	/* Note : the threads that post into this queue should already been stopped before this !!! */
-
-	/* Empty all contents */
-	while (1) {
-		/* Check if there is a message in the queue */
-		ret = fd_fifo_tryget(*queue, &msg);
-		if (ret == EWOULDBLOCK)
-			break;
-		CHECK_FCT(ret);
-		
-		/* We got one! */
-		fd_log_debug("The following message is lost because the daemon is stopping:\n");
-		fd_msg_dump_walk(NONE, msg);
-		fd_msg_free(msg);
-	}
-	
-	/* Now, delete the empty queue */
-	CHECK_FCT( fd_fifo_del ( queue ) );
-	
-	return 0;
-}
Index: eeDiameter/routing_dispatch.c
===================================================================
--- freeDiameter/routing_dispatch.c	(revision 649)
+++ 	(revision )
@@ -1,1233 +1,0 @@
-/*********************************************************************************************************
-* Software License Agreement (BSD License)                                                               *
-* Author: Sebastien Decugis <sdecugis@nict.go.jp>							 *
-*													 *
-* Copyright (c) 2010, 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 "fD.h"
-
-/********************************************************************************/
-/*              First part : handling the extensions callbacks                  */
-/********************************************************************************/
-
-/* Lists of the callbacks, and locks to protect them */
-static pthread_rwlock_t rt_fwd_lock = PTHREAD_RWLOCK_INITIALIZER;
-static struct fd_list 	rt_fwd_list = FD_LIST_INITIALIZER_O(rt_fwd_list, &rt_fwd_lock);
-
-static pthread_rwlock_t rt_out_lock = PTHREAD_RWLOCK_INITIALIZER;
-static struct fd_list 	rt_out_list = FD_LIST_INITIALIZER_O(rt_out_list, &rt_out_lock);
-
-/* Items in the lists are the same */
-struct rt_hdl {
-	struct fd_list	chain;	/* link in the rt_fwd_list or rt_out_list */
-	void *		cbdata;	/* the registered data */
-	union {
-		int	order;	/* This value is used to sort the list */
-		int 	dir;	/* It is the direction for FWD handlers */
-		int	prio;	/* and the priority for OUT handlers */
-	};
-	union {
-		int (*rt_fwd_cb)(void * cbdata, struct msg ** msg);
-		int (*rt_out_cb)(void * cbdata, struct msg * msg, struct fd_list * candidates);
-	};
-};	
-
-/* Add a new entry in the list */
-static int add_ordered(struct rt_hdl * new, struct fd_list * list)
-{
-	/* The list is ordered by prio parameter */
-	struct fd_list * li;
-	
-	CHECK_POSIX( pthread_rwlock_wrlock(list->o) );
-	
-	for (li = list->next; li != list; li = li->next) {
-		struct rt_hdl * h = (struct rt_hdl *) li;
-		if (new->order <= h->order)
-			break;
-	}
-	
-	fd_list_insert_before(li, &new->chain);
-	
-	CHECK_POSIX( pthread_rwlock_unlock(list->o) );
-	
-	return 0;
-}
-
-/* Register a new FWD callback */
-int fd_rt_fwd_register ( int (*rt_fwd_cb)(void * cbdata, struct msg ** msg), void * cbdata, enum fd_rt_fwd_dir dir, struct fd_rt_fwd_hdl ** handler )
-{
-	struct rt_hdl * new;
-	
-	TRACE_ENTRY("%p %p %d %p", rt_fwd_cb, cbdata, dir, handler);
-	CHECK_PARAMS( rt_fwd_cb );
-	CHECK_PARAMS( (dir >= RT_FWD_REQ) && ( dir <= RT_FWD_ANS) );
-	
-	/* Create a new container */
-	CHECK_MALLOC(new = malloc(sizeof(struct rt_hdl)));
-	memset(new, 0, sizeof(struct rt_hdl));
-	
-	/* Write the content */
-	fd_list_init(&new->chain, NULL);
-	new->cbdata 	= cbdata;
-	new->dir    	= dir;
-	new->rt_fwd_cb 	= rt_fwd_cb;
-	
-	/* Save this in the list */
-	CHECK_FCT( add_ordered(new, &rt_fwd_list) );
-	
-	/* Give it back to the extension if needed */
-	if (handler)
-		*handler = (void *)new;
-	
-	return 0;
-}
-
-/* Remove it */
-int fd_rt_fwd_unregister ( struct fd_rt_fwd_hdl * handler, void ** cbdata )
-{
-	struct rt_hdl * del;
-	TRACE_ENTRY( "%p %p", handler, cbdata);
-	CHECK_PARAMS( handler );
-	
-	del = (struct rt_hdl *)handler;
-	CHECK_PARAMS( del->chain.head == &rt_fwd_list );
-	
-	/* Unlink */
-	CHECK_POSIX( pthread_rwlock_wrlock(&rt_fwd_lock) );
-	fd_list_unlink(&del->chain);
-	CHECK_POSIX( pthread_rwlock_unlock(&rt_fwd_lock) );
-	
-	if (cbdata)
-		*cbdata = del->cbdata;
-	
-	free(del);
-	return 0;
-}
-
-/* Register a new OUT callback */
-int fd_rt_out_register ( int (*rt_out_cb)(void * cbdata, struct msg * msg, struct fd_list * candidates), void * cbdata, int priority, struct fd_rt_out_hdl ** handler )
-{
-	struct rt_hdl * new;
-	
-	TRACE_ENTRY("%p %p %d %p", rt_out_cb, cbdata, priority, handler);
-	CHECK_PARAMS( rt_out_cb );
-	
-	/* Create a new container */
-	CHECK_MALLOC(new = malloc(sizeof(struct rt_hdl)));
-	memset(new, 0, sizeof(struct rt_hdl));
-	
-	/* Write the content */
-	fd_list_init(&new->chain, NULL);
-	new->cbdata 	= cbdata;
-	new->prio    	= priority;
-	new->rt_out_cb 	= rt_out_cb;
-	
-	/* Save this in the list */
-	CHECK_FCT( add_ordered(new, &rt_out_list) );
-	
-	/* Give it back to the extension if needed */
-	if (handler)
-		*handler = (void *)new;
-	
-	return 0;
-}
-
-/* Remove it */
-int fd_rt_out_unregister ( struct fd_rt_out_hdl * handler, void ** cbdata )
-{
-	struct rt_hdl * del;
-	TRACE_ENTRY( "%p %p", handler, cbdata);
-	CHECK_PARAMS( handler );
-	
-	del = (struct rt_hdl *)handler;
-	CHECK_PARAMS( del->chain.head == &rt_out_list );
-	
-	/* Unlink */
-	CHECK_POSIX( pthread_rwlock_wrlock(&rt_out_lock) );
-	fd_list_unlink(&del->chain);
-	CHECK_POSIX( pthread_rwlock_unlock(&rt_out_lock) );
-	
-	if (cbdata)
-		*cbdata = del->cbdata;
-	
-	free(del);
-	return 0;
-}
-
-/********************************************************************************/
-/*                      Some default OUT routing callbacks                      */
-/********************************************************************************/
-
-/* Prevent sending to peers that do not support the message application */
-static int dont_send_if_no_common_app(void * cbdata, struct msg * msg, struct fd_list * candidates)
-{
-	struct fd_list * li;
-	struct msg_hdr * hdr;
-	
-	TRACE_ENTRY("%p %p %p", cbdata, msg, candidates);
-	CHECK_PARAMS(msg && candidates);
-	
-	CHECK_FCT( fd_msg_hdr(msg, &hdr) );
-	
-	/* For Base Diameter Protocol, every peer is supposed to support it, so skip */
-	if (hdr->msg_appl == 0)
-		return 0;
-	
-	/* Otherwise, check that the peers support the application */
-	for (li = candidates->next; li != candidates; li = li->next) {
-		struct rtd_candidate *c = (struct rtd_candidate *) li;
-		struct fd_peer * peer;
-		struct fd_app *found;
-		CHECK_FCT( fd_peer_getbyid( c->diamid, (void *)&peer ) );
-		if (peer && (peer->p_hdr.info.runtime.pir_relay == 0)) {
-			/* Check if the remote peer advertised the message's appli */
-			CHECK_FCT( fd_app_check(&peer->p_hdr.info.runtime.pir_apps, hdr->msg_appl, &found) );
-			if (!found)
-				c->score += FD_SCORE_NO_DELIVERY;
-		}
-	}
-
-	return 0;
-}
-
-/* Detect if the Destination-Host and Destination-Realm match the peer */
-static int score_destination_avp(void * cbdata, struct msg * msg, struct fd_list * candidates)
-{
-	struct fd_list * li;
-	struct avp * avp;
-	union avp_value *dh = NULL, *dr = NULL;
-	
-	TRACE_ENTRY("%p %p %p", cbdata, msg, candidates);
-	CHECK_PARAMS(msg && candidates);
-	
-	/* Search the Destination-Host and Destination-Realm AVPs -- we could also use fd_msg_search_avp here, but this one is slightly more efficient */
-	CHECK_FCT(  fd_msg_browse(msg, MSG_BRW_FIRST_CHILD, &avp, NULL) );
-	while (avp) {
-		struct avp_hdr * ahdr;
-		CHECK_FCT(  fd_msg_avp_hdr( avp, &ahdr ) );
-
-		if (! (ahdr->avp_flags & AVP_FLAG_VENDOR)) {
-			switch (ahdr->avp_code) {
-				case AC_DESTINATION_HOST:
-					/* Parse this AVP */
-					CHECK_FCT( fd_msg_parse_dict ( avp, fd_g_config->cnf_dict, NULL ) );
-					ASSERT( ahdr->avp_value );
-					dh = ahdr->avp_value;
-					break;
-
-				case AC_DESTINATION_REALM:
-					/* Parse this AVP */
-					CHECK_FCT( fd_msg_parse_dict ( avp, fd_g_config->cnf_dict, NULL ) );
-					ASSERT( ahdr->avp_value );
-					dr = ahdr->avp_value;
-					break;
-			}
-		}
-
-		if (dh && dr)
-			break;
-
-		/* Go to next AVP */
-		CHECK_FCT(  fd_msg_browse(avp, MSG_BRW_NEXT, &avp, NULL) );
-	}
-	
-	/* Now, check each candidate against these AVP values */
-	for (li = candidates->next; li != candidates; li = li->next) {
-		struct rtd_candidate *c = (struct rtd_candidate *) li;
-		struct fd_peer * peer;
-		CHECK_FCT( fd_peer_getbyid( c->diamid, (void *)&peer ) );
-		if (peer) {
-			if (dh 
-				&& (dh->os.len == strlen(peer->p_hdr.info.pi_diamid)) 
-				/* Here again we use strncasecmp on UTF8 data... This should probably be changed. */
-				&& (strncasecmp(peer->p_hdr.info.pi_diamid, (char *)dh->os.data, dh->os.len) == 0)) {
-				/* The candidate is the Destination-Host */
-				c->score += FD_SCORE_FINALDEST;
-			} else {
-				if (dr  && peer->p_hdr.info.runtime.pir_realm 
-					&& (dr->os.len == strlen(peer->p_hdr.info.runtime.pir_realm)) 
-					/* Yet another case where we use strncasecmp on UTF8 data... Hmmm :-( */
-					&& (strncasecmp(peer->p_hdr.info.runtime.pir_realm, (char *)dr->os.data, dr->os.len) == 0)) {
-					/* The candidate's realm matchs the Destination-Realm */
-					c->score += FD_SCORE_REALM;
-				}
-			}
-		}
-	}
-
-	return 0;
-}
-
-/********************************************************************************/
-/*                        Helper functions                                      */
-/********************************************************************************/
-
-/* Find (first) '!' and '@' positions in a UTF-8 encoded string (User-Name AVP value) */
-static void nai_get_indexes(union avp_value * un, int * excl_idx, int * at_idx)
-{
-	int i;
-	
-	TRACE_ENTRY("%p %p %p", un, excl_idx, at_idx);
-	CHECK_PARAMS_DO( un && excl_idx, return );
-	*excl_idx = 0;
-	
-	/* Search if there is a '!' before any '@' -- do we need to check it contains a '.' ? */
-	for (i = 0; i < un->os.len; i++) {
-		/* The '!' marks the decorated NAI */
-		if ( un->os.data[i] == (unsigned char) '!' ) {
-			if (!*excl_idx)
-				*excl_idx = i;
-			if (!at_idx)
-				return;
-		}
-		/* If we reach the realm part, we can stop */
-		if ( un->os.data[i] == (unsigned char) '@' ) {
-			if (at_idx)
-				*at_idx = i;
-			break;
-		}
-		/* Skip escaped characters */
-		if ( un->os.data[i] == (unsigned char) '\\' ) {
-			i++;
-			continue;
-		}
-		/* Skip UTF-8 characters spanning on several bytes */
-		if ( (un->os.data[i] & 0xF8) == 0xF0 ) { /* 11110zzz */
-			i += 3;
-			continue;
-		}
-		if ( (un->os.data[i] & 0xF0) == 0xE0 ) { /* 1110yyyy */
-			i += 2;
-			continue;
-		}
-		if ( (un->os.data[i] & 0xE0) == 0xC0 ) { /* 110yyyxx */
-			i += 1;
-			continue;
-		}
-	}
-	
-	return;
-}	
-
-/* Test if a User-Name AVP contains a Decorated NAI -- RFC4282, RFC5729 */
-static int is_decorated_NAI(union avp_value * un)
-{
-	int i;
-	TRACE_ENTRY("%p", un);
-	
-	/* If there was no User-Name, we return false */
-	if (un == NULL)
-		return 0;
-	
-	nai_get_indexes(un, &i, NULL);
-	
-	return i;
-}
-
-/* Create new User-Name and Destination-Realm values */
-static int process_decorated_NAI(union avp_value * un, union avp_value * dr)
-{
-	int at_idx = 0, sep_idx = 0;
-	unsigned char * old_un;
-	TRACE_ENTRY("%p %p", un, dr);
-	CHECK_PARAMS(un && dr);
-	
-	/* Save the decorated User-Name, for example 'homerealm.example.net!user@otherrealm.example.net' */
-	old_un = un->os.data;
-	
-	/* Search the positions of the first '!' and the '@' in the string */
-	nai_get_indexes(un, &sep_idx, &at_idx);
-	CHECK_PARAMS( (0 < sep_idx) && (sep_idx < at_idx) && (at_idx < un->os.len));
-	
-	/* Create the new User-Name value */
-	CHECK_MALLOC( un->os.data = malloc( at_idx ) );
-	memcpy( un->os.data, old_un + sep_idx + 1, at_idx - sep_idx ); /* user@ */
-	memcpy( un->os.data + at_idx - sep_idx, old_un, sep_idx ); /* homerealm.example.net */
-	
-	/* Create the new Destination-Realm value */
-	CHECK_MALLOC( dr->os.data = realloc(dr->os.data, sep_idx) );
-	memcpy( dr->os.data, old_un, sep_idx );
-	dr->os.len = sep_idx;
-	
-	TRACE_DEBUG(FULL, "Processed Decorated NAI : '%.*s' became '%.*s' (%.*s)",
-				un->os.len, old_un,
-				at_idx, un->os.data,
-				dr->os.len, dr->os.data);
-	
-	un->os.len = at_idx;
-	free(old_un);
-	
-	return 0;
-}
-
-/* Function to return an error to an incoming request */
-static int return_error(struct msg ** pmsg, char * error_code, char * error_message, struct avp * failedavp)
-{
-	struct fd_peer * peer;
-	int is_loc = 0;
-
-	/* Get the source of the message */
-	{
-		char * id;
-		CHECK_FCT( fd_msg_source_get( *pmsg, &id ) );
-		
-		if (id == NULL) {
-			is_loc = 1; /* The message was issued locally */
-		} else {
-		
-			/* Search the peer with this id */
-			CHECK_FCT( fd_peer_getbyid( id, (void *)&peer ) );
-
-			if (!peer) {
-				TRACE_DEBUG(INFO, "Unable to send error '%s' to deleted peer '%s' in reply to:", error_code, id);
-				fd_msg_dump_walk(INFO, *pmsg);
-				fd_msg_free(*pmsg);
-				*pmsg = NULL;
-				return 0;
-			}
-		}
-	}
-	
-	/* Create the error message */
-	CHECK_FCT( fd_msg_new_answer_from_req ( fd_g_config->cnf_dict, pmsg, MSGFL_ANSW_ERROR ) );
-
-	/* Set the error code */
-	CHECK_FCT( fd_msg_rescode_set(*pmsg, error_code, error_message, failedavp, 1 ) );
-
-	/* Send the answer */
-	if (is_loc) {
-		CHECK_FCT( fd_fifo_post(fd_g_incoming, pmsg) );
-	} else {
-		CHECK_FCT( fd_out_send(pmsg, NULL, peer, 0) );
-	}
-	
-	/* Done */
-	return 0;
-}
-
-
-/****************************************************************************/
-/*         Second part : threads moving messages in the daemon              */
-/****************************************************************************/
-
-/* These are the functions of each threads: dispatch & routing */
-/* The DISPATCH message processing */
-static int msg_dispatch(struct msg ** pmsg)
-{
-	struct msg_hdr * hdr;
-	int is_req = 0, ret;
-	struct session * sess;
-	enum disp_action action;
-	const char * ec = NULL;
-	const char * em = NULL;
-
-	/* Read the message header */
-	CHECK_FCT( fd_msg_hdr(*pmsg, &hdr) );
-	is_req = hdr->msg_flags & CMD_FLAG_REQUEST;
-	
-	/* Note: if the message is for local delivery, we should test for duplicate
-	  (draft-asveren-dime-dupcons-00). This may conflict with path validation decisions, no clear answer yet */
-
-	/* At this point, we need to understand the message content, so parse it */
-	CHECK_FCT_DO( ret = fd_msg_parse_or_error( pmsg ),
-		{
-			/* in case of error, the message is already dump'd */
-			if ((ret == EBADMSG) && (*pmsg != NULL)) {
-				/* msg now contains the answer message to send back */
-				CHECK_FCT( fd_fifo_post(fd_g_outgoing, pmsg) );
-			}
-			if (*pmsg) {	/* another error happen'd */
-				TRACE_DEBUG(INFO, "An unexpected error occurred (%s), discarding a message:", strerror(ret));
-				fd_msg_dump_walk(INFO, *pmsg);
-				CHECK_FCT_DO( fd_msg_free(*pmsg), /* continue */);
-				*pmsg = NULL;
-			}
-			/* We're done with this one */
-			return 0;
-		} );
-
-	/* First, if the original request was registered with a callback and we receive the answer, call it. */
-	if ( ! is_req ) {
-		struct msg * qry;
-		void (*anscb)(void *, struct msg **) = NULL;
-		void * data = NULL;
-
-		/* Retrieve the corresponding query */
-		CHECK_FCT( fd_msg_answ_getq( *pmsg, &qry ) );
-
-		/* Retrieve any registered handler */
-		CHECK_FCT( fd_msg_anscb_get( qry, &anscb, &data ) );
-
-		/* If a callback was registered, pass the message to it */
-		if (anscb != NULL) {
-
-			TRACE_DEBUG(FULL, "Calling callback registered when query was sent (%p, %p)", anscb, data);
-			(*anscb)(data, pmsg);
-			
-			/* If the message is processed, we're done */
-			if (*pmsg == NULL) {
-				return 0;
-			}
-		}
-	}
-	
-	/* Retrieve the session of the message */
-	CHECK_FCT( fd_msg_sess_get(fd_g_config->cnf_dict, *pmsg, &sess, NULL) );
-
-	/* Now, call any callback registered for the message */
-	CHECK_FCT( fd_msg_dispatch ( pmsg, sess, &action, &ec) );
-
-	/* Now, act depending on msg and action and ec */
-	if (*pmsg)
-		switch ( action ) {
-			case DISP_ACT_CONT:
-				/* No callback has handled the message, let's reply with a generic error */
-				em = "The message was not handled by any extension callback";
-				ec = "DIAMETER_COMMAND_UNSUPPORTED";
-			
-			case DISP_ACT_ERROR:
-				/* We have a problem with delivering the message */
-				if (ec == NULL) {
-					ec = "DIAMETER_UNABLE_TO_COMPLY";
-				}
-				
-				if (!is_req) {
-					TRACE_DEBUG(INFO, "Received an answer to a localy issued query, but no handler processed this answer!");
-					fd_msg_dump_walk(INFO, *pmsg);
-					fd_msg_free(*pmsg);
-					*pmsg = NULL;
-					break;
-				}
-				
-				/* Create an answer with the error code and message */
-				CHECK_FCT( fd_msg_new_answer_from_req ( fd_g_config->cnf_dict, pmsg, 0 ) );
-				CHECK_FCT( fd_msg_rescode_set(*pmsg, (char *)ec, (char *)em, NULL, 1 ) );
-				
-			case DISP_ACT_SEND:
-				/* Now, send the message */
-				CHECK_FCT( fd_fifo_post(fd_g_outgoing, pmsg) );
-		}
-	
-	/* We're done with dispatching this message */
-	return 0;
-}
-
-/* The ROUTING-IN message processing */
-static int msg_rt_in(struct msg ** pmsg)
-{
-	struct msg_hdr * hdr;
-	int is_req = 0;
-	int is_err = 0;
-	char * qry_src = NULL;
-
-	/* Read the message header */
-	CHECK_FCT( fd_msg_hdr(*pmsg, &hdr) );
-	is_req = hdr->msg_flags & CMD_FLAG_REQUEST;
-	is_err = hdr->msg_flags & CMD_FLAG_ERROR;
-
-	/* Handle incorrect bits */
-	if (is_req && is_err) {
-		CHECK_FCT( return_error( pmsg, "DIAMETER_INVALID_HDR_BITS", "R & E bits were set", NULL) );
-		return 0;
-	}
-	
-	/* If it is a request, we must analyze its content to decide what we do with it */
-	if (is_req) {
-		struct avp * avp, *un = NULL;
-		union avp_value * un_val = NULL, *dr_val = NULL;
-		enum status { UNKNOWN, YES, NO };
-		/* Are we Destination-Host? */
-		enum status is_dest_host = UNKNOWN;
-		/* Are we Destination-Realm? */
-		enum status is_dest_realm = UNKNOWN;
-		/* Do we support the application of the message? */
-		enum status is_local_app = UNKNOWN;
-
-		/* Check if we have local support for the message application */
-		if ( (hdr->msg_appl == 0) || (hdr->msg_appl == AI_RELAY) ) {
-			TRACE_DEBUG(INFO, "Received a routable message with application id 0, returning DIAMETER_APPLICATION_UNSUPPORTED");
-			CHECK_FCT( return_error( pmsg, "DIAMETER_APPLICATION_UNSUPPORTED", "Routable message with application id 0 or relay", NULL) );
-			return 0;
-		} else {
-			struct fd_app * app;
-			CHECK_FCT( fd_app_check(&fd_g_config->cnf_apps, hdr->msg_appl, &app) );
-			is_local_app = (app ? YES : NO);
-		}
-
-		/* Parse the message for Dest-Host and Dest-Realm */
-		CHECK_FCT(  fd_msg_browse(*pmsg, MSG_BRW_FIRST_CHILD, &avp, NULL)  );
-		while (avp) {
-			struct avp_hdr * ahdr;
-			struct fd_pei error_info;
-			int ret;
-			CHECK_FCT(  fd_msg_avp_hdr( avp, &ahdr )  );
-
-			if (! (ahdr->avp_flags & AVP_FLAG_VENDOR)) {
-				switch (ahdr->avp_code) {
-					case AC_DESTINATION_HOST:
-						/* Parse this AVP */
-						CHECK_FCT_DO( ret = fd_msg_parse_dict ( avp, fd_g_config->cnf_dict, &error_info ),
-							{
-								if (error_info.pei_errcode) {
-									CHECK_FCT( return_error( pmsg, error_info.pei_errcode, error_info.pei_message, error_info.pei_avp) );
-									return 0;
-								} else {
-									return ret;
-								}
-							} );
-						ASSERT( ahdr->avp_value );
-						/* Compare the Destination-Host AVP of the message with our identity */
-						if (ahdr->avp_value->os.len != fd_g_config->cnf_diamid_len) {
-							is_dest_host = NO;
-						} else {
-							is_dest_host = (strncasecmp(fd_g_config->cnf_diamid, (char *)ahdr->avp_value->os.data, fd_g_config->cnf_diamid_len) 
-										? NO : YES);
-						}
-						break;
-
-					case AC_DESTINATION_REALM:
-						/* Parse this AVP */
-						CHECK_FCT_DO( ret = fd_msg_parse_dict ( avp, fd_g_config->cnf_dict, &error_info ),
-							{
-								if (error_info.pei_errcode) {
-									CHECK_FCT( return_error( pmsg, error_info.pei_errcode, error_info.pei_message, error_info.pei_avp) );
-									return 0;
-								} else {
-									return ret;
-								}
-							} );
-						ASSERT( ahdr->avp_value );
-						dr_val = ahdr->avp_value;
-						/* Compare the Destination-Realm AVP of the message with our identity */
-						if (ahdr->avp_value->os.len != fd_g_config->cnf_diamrlm_len) {
-							is_dest_realm = NO;
-						} else {
-							is_dest_realm = (strncasecmp(fd_g_config->cnf_diamrlm, (char *)ahdr->avp_value->os.data, fd_g_config->cnf_diamrlm_len) 
-										? NO : YES);
-						}
-						break;
-
-					case AC_USER_NAME:
-						/* Parse this AVP */
-						CHECK_FCT_DO( ret = fd_msg_parse_dict ( avp, fd_g_config->cnf_dict, &error_info ),
-							{
-								if (error_info.pei_errcode) {
-									CHECK_FCT( return_error( pmsg, error_info.pei_errcode, error_info.pei_message, error_info.pei_avp) );
-									return 0;
-								} else {
-									return ret;
-								}
-							} );
-						ASSERT( ahdr->avp_value );
-						un = avp;
-						un_val = ahdr->avp_value;
-						break;
-				}
-			}
-
-			if ((is_dest_host != UNKNOWN) && (is_dest_realm != UNKNOWN) && un)
-				break;
-
-			/* Go to next AVP */
-			CHECK_FCT(  fd_msg_browse(avp, MSG_BRW_NEXT, &avp, NULL)  );
-		}
-
-		/* OK, now decide what we do with the request */
-
-		/* Handle the missing routing AVPs first */
-		if ( is_dest_realm == UNKNOWN ) {
-			CHECK_FCT( return_error( pmsg, "DIAMETER_COMMAND_UNSUPPORTED", "Non-routable message not supported (invalid bit ? missing Destination-Realm ?)", NULL) );
-			return 0;
-		}
-
-		/* If we are listed as Destination-Host */
-		if (is_dest_host == YES) {
-			if (is_local_app == YES) {
-				/* Ok, give the message to the dispatch thread */
-				CHECK_FCT( fd_fifo_post(fd_g_local, pmsg) );
-			} else {
-				/* We don't support the application, reply an error */
-				CHECK_FCT( return_error( pmsg, "DIAMETER_APPLICATION_UNSUPPORTED", NULL, NULL) );
-			}
-			return 0;
-		}
-
-		/* If the message is explicitely for someone else */
-		if ((is_dest_host == NO) || (is_dest_realm == NO)) {
-			if (fd_g_config->cnf_flags.no_fwd) {
-				CHECK_FCT( return_error( pmsg, "DIAMETER_UNABLE_TO_DELIVER", "This peer is not an agent", NULL) );
-				return 0;
-			}
-		} else {
-		/* Destination-Host was not set, and Destination-Realm is matching : we may handle or pass to a fellow peer */
-
-			/* test for decorated NAI  (RFC5729 section 4.4) */
-			if (is_decorated_NAI(un_val)) {
-				/* Handle the decorated NAI */
-				CHECK_FCT_DO( process_decorated_NAI(un_val, dr_val),
-					{
-						/* If the process failed, we assume it is because of the AVP format */
-						CHECK_FCT( return_error( pmsg, "DIAMETER_INVALID_AVP_VALUE", "Failed to process decorated NAI", un) );
-						return 0;
-					} );
-
-				/* We have transformed the AVP, now submit it again in the queue */
-				CHECK_FCT(fd_fifo_post(fd_g_incoming, pmsg) );
-				return 0;
-			}
-
-			if (is_local_app == YES) {
-				/* Handle localy since we are able to */
-				CHECK_FCT(fd_fifo_post(fd_g_local, pmsg) );
-				return 0;
-			}
-
-			if (fd_g_config->cnf_flags.no_fwd) {
-				/* We return an error */
-				CHECK_FCT( return_error( pmsg, "DIAMETER_APPLICATION_UNSUPPORTED", NULL, NULL) );
-				return 0;
-			}
-		}
-
-		/* From that point, for requests, we will call the registered callbacks, then forward to another peer */
-
-	} else {
-		/* The message is an answer */
-		struct msg * qry;
-
-		/* Retrieve the corresponding query and its origin */
-		CHECK_FCT( fd_msg_answ_getq( *pmsg, &qry ) );
-		CHECK_FCT( fd_msg_source_get( qry, &qry_src ) );
-
-		if ((!qry_src) && (!is_err)) {
-			/* The message is a normal answer to a request issued localy, we do not call the callbacks chain on it. */
-			CHECK_FCT(fd_fifo_post(fd_g_local, pmsg) );
-			return 0;
-		}
-
-		/* From that point, for answers, we will call the registered callbacks, then pass it to the dispatch module or forward it */
-	}
-
-	/* Call all registered callbacks for this message */
-	{
-		struct fd_list * li;
-
-		CHECK_FCT( pthread_rwlock_rdlock( &rt_fwd_lock ) );
-		pthread_cleanup_push( fd_cleanup_rwlock, &rt_fwd_lock );
-
-		/* requests: dir = 1 & 2 => in order; answers = 3 & 2 => in reverse order */
-		for (	li = (is_req ? rt_fwd_list.next : rt_fwd_list.prev) ; *pmsg && (li != &rt_fwd_list) ; li = (is_req ? li->next : li->prev) ) {
-			struct rt_hdl * rh = (struct rt_hdl *)li;
-
-			if (is_req && (rh->dir > RT_FWD_ALL))
-				break;
-			if ((!is_req) && (rh->dir < RT_FWD_ALL))
-				break;
-
-			/* Ok, call this cb */
-			TRACE_DEBUG(ANNOYING, "Calling next FWD callback on %p : %p", *pmsg, rh->rt_fwd_cb);
-			CHECK_FCT_DO( (*rh->rt_fwd_cb)(rh->cbdata, pmsg),
-				{
-					TRACE_DEBUG(INFO, "A FWD routing callback returned an error, message discarded.");
-					fd_msg_dump_walk(INFO, *pmsg);
-					fd_msg_free(*pmsg);
-					*pmsg = NULL;
-				} );
-		}
-
-		pthread_cleanup_pop(0);
-		CHECK_FCT( pthread_rwlock_unlock( &rt_fwd_lock ) );
-
-		/* If a callback has handled the message, we stop now */
-		if (!*pmsg)
-			return 0;
-	}
-
-	/* Now pass the message to the next step: either forward to another peer, or dispatch to local extensions */
-	if (is_req || qry_src) {
-		CHECK_FCT(fd_fifo_post(fd_g_outgoing, pmsg) );
-	} else {
-		CHECK_FCT(fd_fifo_post(fd_g_local, pmsg) );
-	}
-
-	/* We're done with this message */
-	return 0;
-}
-		
-
-/* The ROUTING-OUT message processing */
-static int msg_rt_out(struct msg ** pmsg)
-{
-	struct rt_data * rtd = NULL;
-	struct msg_hdr * hdr;
-	int is_req = 0;
-	int ret;
-	struct fd_list * li, *candidates;
-	struct avp * avp;
-	struct rtd_candidate * c;
-	
-	/* Read the message header */
-	CHECK_FCT( fd_msg_hdr(*pmsg, &hdr) );
-	is_req = hdr->msg_flags & CMD_FLAG_REQUEST;
-	
-	/* For answers, the routing is very easy */
-	if ( ! is_req ) {
-		struct msg * qry;
-		char * qry_src = NULL;
-		struct msg_hdr * qry_hdr;
-		struct fd_peer * peer = NULL;
-
-		/* Retrieve the corresponding query and its origin */
-		CHECK_FCT( fd_msg_answ_getq( *pmsg, &qry ) );
-		CHECK_FCT( fd_msg_source_get( qry, &qry_src ) );
-
-		ASSERT( qry_src ); /* if it is NULL, the message should have been in the LOCAL queue! */
-
-		/* Find the peer corresponding to this name */
-		CHECK_FCT( fd_peer_getbyid( qry_src, (void *) &peer ) );
-		fd_cpu_flush_cache();
-		if ((!peer) || (peer->p_hdr.info.runtime.pir_state != STATE_OPEN)) {
-			TRACE_DEBUG(INFO, "Unable to forward answer message to peer '%s', deleted or not in OPEN state.", qry_src);
-			fd_msg_dump_walk(INFO, *pmsg);
-			fd_msg_free(*pmsg);
-			*pmsg = NULL;
-			return 0;
-		}
-
-		/* We must restore the hop-by-hop id */
-		CHECK_FCT( fd_msg_hdr(qry, &qry_hdr) );
-		hdr->msg_hbhid = qry_hdr->msg_hbhid;
-
-		/* Push the message into this peer */
-		CHECK_FCT( fd_out_send(pmsg, NULL, peer, 0) );
-
-		/* We're done with this answer */
-		return 0;
-	}
-	
-	/* From that point, the message is a request */
-
-	/* Get the routing data out of the message if any (in case of re-transmit) */
-	CHECK_FCT( fd_msg_rt_get ( *pmsg, &rtd ) );
-
-	/* If there is no routing data already, let's create it */
-	if (rtd == NULL) {
-		CHECK_FCT( fd_rtd_init(&rtd) );
-
-		/* Add all peers currently in OPEN state */
-		CHECK_FCT( pthread_rwlock_rdlock(&fd_g_activ_peers_rw) );
-		for (li = fd_g_activ_peers.next; li != &fd_g_activ_peers; li = li->next) {
-			struct fd_peer * p = (struct fd_peer *)li->o;
-			CHECK_FCT_DO( ret = fd_rtd_candidate_add(rtd, p->p_hdr.info.pi_diamid, p->p_hdr.info.runtime.pir_realm), { CHECK_FCT_DO( pthread_rwlock_unlock(&fd_g_activ_peers_rw), ); return ret; } );
-		}
-		CHECK_FCT( pthread_rwlock_unlock(&fd_g_activ_peers_rw) );
-
-		/* Now let's remove all peers from the Route-Records */
-		CHECK_FCT(  fd_msg_browse(*pmsg, MSG_BRW_FIRST_CHILD, &avp, NULL)  );
-		while (avp) {
-			struct avp_hdr * ahdr;
-			struct fd_pei error_info;
-			CHECK_FCT(  fd_msg_avp_hdr( avp, &ahdr )  );
-
-			if ((ahdr->avp_code == AC_ROUTE_RECORD) && (! (ahdr->avp_flags & AVP_FLAG_VENDOR)) ) {
-				/* Parse this AVP */
-				CHECK_FCT_DO( ret = fd_msg_parse_dict ( avp, fd_g_config->cnf_dict, &error_info ),
-					{
-						if (error_info.pei_errcode) {
-							CHECK_FCT( return_error( pmsg, error_info.pei_errcode, error_info.pei_message, error_info.pei_avp) );
-							return 0;
-						} else {
-							return ret;
-						}
-					} );
-				ASSERT( ahdr->avp_value );
-				/* Remove this value from the list */
-				fd_rtd_candidate_del(rtd, (char *)ahdr->avp_value->os.data, ahdr->avp_value->os.len);
-			}
-
-			/* Go to next AVP */
-			CHECK_FCT(  fd_msg_browse(avp, MSG_BRW_NEXT, &avp, NULL)  );
-		}
-	}
-
-	/* Note: we reset the scores and pass the message to the callbacks, maybe we could re-use the saved scores when we have received an error ? */
-
-	/* Ok, we have our list in rtd now, let's (re)initialize the scores */
-	fd_rtd_candidate_extract(rtd, &candidates, FD_SCORE_INI);
-
-	/* Pass the list to registered callbacks (even if it is empty) */
-	{
-		CHECK_FCT( pthread_rwlock_rdlock( &rt_out_lock ) );
-		pthread_cleanup_push( fd_cleanup_rwlock, &rt_out_lock );
-
-		/* We call the cb by reverse priority order */
-		for (	li = rt_out_list.prev ; li != &rt_out_list ; li = li->prev ) {
-			struct rt_hdl * rh = (struct rt_hdl *)li;
-
-			TRACE_DEBUG(ANNOYING, "Calling next OUT callback on %p : %p (prio %d)", *pmsg, rh->rt_out_cb, rh->prio);
-			CHECK_FCT_DO( ret = (*rh->rt_out_cb)(rh->cbdata, *pmsg, candidates),
-				{
-					TRACE_DEBUG(INFO, "An OUT routing callback returned an error (%s) ! Message discarded.", strerror(ret));
-					fd_msg_dump_walk(INFO, *pmsg);
-					fd_msg_free(*pmsg);
-					*pmsg = NULL;
-					break;
-				} );
-		}
-
-		pthread_cleanup_pop(0);
-		CHECK_FCT( pthread_rwlock_unlock( &rt_out_lock ) );
-
-		/* If an error occurred, skip to the next message */
-		if (! *pmsg) {
-			if (rtd)
-				fd_rtd_free(&rtd);
-			return 0;
-		}
-	}
-	
-	/* Order the candidate peers by score attributed by the callbacks */
-	CHECK_FCT( fd_rtd_candidate_reorder(candidates) );
-
-	/* Save the routing information in the message */
-	CHECK_FCT( fd_msg_rt_associate ( *pmsg, &rtd ) );
-
-	/* Now try sending the message */
-	for (li = candidates->prev; li != candidates; li = li->prev) {
-		struct fd_peer * peer;
-
-		c = (struct rtd_candidate *) li;
-
-		/* Stop when we have reached the end of valid candidates */
-		if (c->score < 0)
-			break;
-
-		/* Search for the peer */
-		CHECK_FCT( fd_peer_getbyid( c->diamid, (void *)&peer ) );
-
-		fd_cpu_flush_cache();
-		if (peer && (peer->p_hdr.info.runtime.pir_state == STATE_OPEN)) {
-			/* Send to this one */
-			CHECK_FCT_DO( fd_out_send(pmsg, NULL, peer, 0), continue );
-			
-			/* If the sending was successful */
-			break;
-		}
-	}
-
-	/* If the message has not been sent, return an error */
-	if (*pmsg) {
-		TRACE_DEBUG(INFO, "Could not send the following message, replying with UNABLE_TO_DELIVER");
-		fd_msg_dump_walk(INFO, *pmsg);
-		return_error( pmsg, "DIAMETER_UNABLE_TO_DELIVER", "No suitable candidate to route the message to", NULL);
-	}
-
-	/* We're done with this message */
-	
-	return 0;
-}
-
-
-/********************************************************************************/
-/*                     Management of the threads                                */
-/********************************************************************************/
-
-/* Note: in the first version, we only create one thread of each kind.
- We could improve the scalability by using the threshold feature of the queues
- to create additional threads if a queue is filling up, or at least giving a configurable
- number of threads of each kind.
- */
-
-/* Control of the threads */
-static enum { RUN = 0, STOP = 1 } order_val = RUN;
-static pthread_mutex_t order_lock = PTHREAD_MUTEX_INITIALIZER;
-
-/* Threads report their status */
-enum thread_state { INITIAL = 0, RUNNING = 1, TERMINATED = 2 };
-static void cleanup_state(void * state_loc)
-{
-	if (state_loc)
-		*(enum thread_state *)state_loc = TERMINATED;
-}
-
-/* This is the common thread code (same for routing and dispatching) */
-static void * process_thr(void * arg, int (*action_cb)(struct msg ** pmsg), struct fifo * queue, char * action_name)
-{
-	TRACE_ENTRY("%p %p %p %p", arg, action_cb, queue, action_name);
-	
-	/* Set the thread name */
-	{
-		char buf[48];
-		snprintf(buf, sizeof(buf), "%s (%p)", action_name, arg);
-		fd_log_threadname ( buf );
-	}
-	
-	/* The thread reports its status when canceled */
-	CHECK_PARAMS_DO(arg, return NULL);
-	pthread_cleanup_push( cleanup_state, arg );
-	
-	/* Mark the thread running */
-	*(enum thread_state *)arg = RUNNING;
-	fd_cpu_flush_cache();
-	
-	do {
-		struct msg * msg;
-	
-		/* Test the current order */
-		{
-			int must_stop;
-			CHECK_POSIX_DO( pthread_mutex_lock(&order_lock), goto end ); /* we lock to flush the caches */
-			must_stop = (order_val == STOP);
-			CHECK_POSIX_DO( pthread_mutex_unlock(&order_lock), goto end );
-			if (must_stop)
-				goto end;
-			
-			pthread_testcancel();
-		}
-		
-		/* Ok, we are allowed to run */
-		
-		/* Get the next message from the queue */
-		{
-			int ret;
-			ret = fd_fifo_get ( queue, &msg );
-			if (ret == EPIPE)
-				/* The queue was destroyed, we are probably exiting */
-				goto end;
-			
-			/* check if another error occurred */
-			CHECK_FCT_DO( ret, goto fatal_error );
-		}
-		
-		if (TRACE_BOOL(FULL)) {
-			TRACE_DEBUG(FULL, "Picked next message");
-			fd_msg_dump_one(ANNOYING, msg);
-		}
-		
-		/* Now process the message */
-		CHECK_FCT_DO( (*action_cb)(&msg), goto fatal_error);
-
-		/* We're done with this message */
-	
-	} while (1);
-	
-fatal_error:
-	TRACE_DEBUG(INFO, "An unrecoverable error occurred, %s thread is terminating...", action_name);
-	CHECK_FCT_DO(fd_event_send(fd_g_config->cnf_main_ev, FDEV_TERMINATE, 0, NULL), );
-	
-end:	
-	; /* noop so that we get rid of "label at end of compund statement" warning */
-	/* Mark the thread as terminated */
-	pthread_cleanup_pop(1);
-	return NULL;
-}
-
-/* The dispatch thread */
-static void * dispatch_thr(void * arg)
-{
-	return process_thr(arg, msg_dispatch, fd_g_local, "Dispatch");
-}
-
-/* The (routing-in) thread -- see description in freeDiameter.h */
-static void * routing_in_thr(void * arg)
-{
-	return process_thr(arg, msg_rt_in, fd_g_incoming, "Routing-IN");
-}
-
-/* The (routing-out) thread -- see description in freeDiameter.h */
-static void * routing_out_thr(void * arg)
-{
-	return process_thr(arg, msg_rt_out, fd_g_outgoing, "Routing-OUT");
-}
-
-
-/********************************************************************************/
-/*                     The functions for the other files                        */
-/********************************************************************************/
-
-static pthread_t * dispatch = NULL;
-static enum thread_state * disp_state = NULL;
-
-/* Later: make this more dynamic */
-static pthread_t rt_out = (pthread_t)NULL;
-static enum thread_state out_state = INITIAL;
-
-static pthread_t rt_in  = (pthread_t)NULL;
-static enum thread_state in_state = INITIAL;
-
-/* Initialize the routing and dispatch threads */
-int fd_rtdisp_init(void)
-{
-	int i;
-	
-	/* Prepare the array for dispatch */
-	CHECK_MALLOC( dispatch = calloc(fd_g_config->cnf_dispthr, sizeof(pthread_t)) );
-	CHECK_MALLOC( disp_state = calloc(fd_g_config->cnf_dispthr, sizeof(enum thread_state)) );
-	
-	/* Create the threads */
-	for (i=0; i < fd_g_config->cnf_dispthr; i++) {
-		CHECK_POSIX( pthread_create( &dispatch[i], NULL, dispatch_thr, &disp_state[i] ) );
-	}
-	CHECK_POSIX( pthread_create( &rt_out, NULL, routing_out_thr, &out_state) );
-	CHECK_POSIX( pthread_create( &rt_in,  NULL, routing_in_thr,  &in_state) );
-	
-	/* Later: TODO("Set the thresholds for the queues to create more threads as needed"); */
-	
-	/* Register the built-in callbacks */
-	CHECK_FCT( fd_rt_out_register( dont_send_if_no_common_app, NULL, 10, NULL ) );
-	CHECK_FCT( fd_rt_out_register( score_destination_avp, NULL, 10, NULL ) );
-	return 0;
-}
-
-/* Ask the thread to terminate after next iteration */
-int fd_rtdisp_cleanstop(void)
-{
-	CHECK_POSIX( pthread_mutex_lock(&order_lock) );
-	order_val = STOP;
-	CHECK_POSIX( pthread_mutex_unlock(&order_lock) );
-
-	return 0;
-}
-
-static void stop_thread_delayed(enum thread_state *st, pthread_t * thr, char * th_name)
-{
-	TRACE_ENTRY("%p %p", st, thr);
-	CHECK_PARAMS_DO(st && thr, return);
-
-	/* Wait for a second for the thread to complete, by monitoring my_state */
-	fd_cpu_flush_cache();
-	if (*st != TERMINATED) {
-		TRACE_DEBUG(INFO, "Waiting for the %s thread to have a chance to terminate", th_name);
-		do {
-			struct timespec	 ts, ts_final;
-
-			CHECK_SYS_DO( clock_gettime(CLOCK_REALTIME, &ts), break );
-			
-			ts_final.tv_sec = ts.tv_sec + 1;
-			ts_final.tv_nsec = ts.tv_nsec;
-			
-			while (TS_IS_INFERIOR( &ts, &ts_final )) {
-				if (*st == TERMINATED)
-					break;
-				
-				usleep(100000);
-				CHECK_SYS_DO( clock_gettime(CLOCK_REALTIME, &ts), break );
-			}
-		} while (0);
-	}
-
-	/* Now stop the thread and reclaim its resources */
-	CHECK_FCT_DO( fd_thr_term(thr ), /* continue */);
-	
-}
-
-/* Stop the thread after up to one second of wait */
-int fd_rtdisp_fini(void)
-{
-	int i;
-	
-	/* Destroy the incoming queue */
-	CHECK_FCT_DO( fd_queues_fini(&fd_g_incoming), /* ignore */);
-	
-	/* Stop the routing IN thread */
-	stop_thread_delayed(&in_state, &rt_in, "IN routing");
-	
-	/* Destroy the outgoing queue */
-	CHECK_FCT_DO( fd_queues_fini(&fd_g_outgoing), /* ignore */);
-	
-	/* Stop the routing OUT thread */
-	stop_thread_delayed(&out_state, &rt_out, "OUT routing");
-	
-	/* Destroy the local queue */
-	CHECK_FCT_DO( fd_queues_fini(&fd_g_local), /* ignore */);
-	
-	/* Stop the Dispatch thread */
-	for (i=0; i < fd_g_config->cnf_dispthr; i++) {
-		stop_thread_delayed(&disp_state[i], &dispatch[i], "Dispatching");
-	}
-	
-	return 0;
-}
-
-/* Cleanup handlers */
-int fd_rtdisp_cleanup(void)
-{
-	/* Cleanup all remaining handlers */
-	while (!FD_IS_LIST_EMPTY(&rt_fwd_list)) {
-		CHECK_FCT_DO( fd_rt_fwd_unregister ( (void *)rt_fwd_list.next, NULL ), /* continue */ );
-	}
-	while (!FD_IS_LIST_EMPTY(&rt_out_list)) {
-		CHECK_FCT_DO( fd_rt_out_unregister ( (void *)rt_out_list.next, NULL ), /* continue */ );
-	}
-	
-	fd_disp_unregister_all(); /* destroy remaining handlers */
-
-	return 0;
-}
-
-
-/********************************************************************************/
-/*                     For extensiosn to register a new appl                    */
-/********************************************************************************/
-
-/* Add an application into the peer's supported apps */
-int fd_disp_app_support ( struct dict_object * app, struct dict_object * vendor, int auth, int acct )
-{
-	application_id_t aid = 0;
-	vendor_id_t	 vid = 0;
-	
-	TRACE_ENTRY("%p %p %d %d", app, vendor, auth, acct);
-	CHECK_PARAMS( app && (auth || acct) );
-	
-	{
-		enum dict_object_type type = 0;
-		struct dict_application_data data;
-		CHECK_FCT( fd_dict_gettype(app, &type) );
-		CHECK_PARAMS( type == DICT_APPLICATION );
-		CHECK_FCT( fd_dict_getval(app, &data) );
-		aid = data.application_id;
-	}
-
-	if (vendor) {
-		enum dict_object_type type = 0;
-		struct dict_vendor_data data;
-		CHECK_FCT( fd_dict_gettype(vendor, &type) );
-		CHECK_PARAMS( type == DICT_VENDOR );
-		CHECK_FCT( fd_dict_getval(vendor, &data) );
-		vid = data.vendor_id;
-	}
-	
-	return fd_app_merge(&fd_g_config->cnf_apps, aid, vid, auth, acct);
-}
-
-
-
Index: eeDiameter/sctp.c
===================================================================
--- freeDiameter/sctp.c	(revision 590)
+++ 	(revision )
@@ -1,1232 +1,0 @@
-/*********************************************************************************************************
-* Software License Agreement (BSD License)                                                               *
-* Author: Sebastien Decugis <sdecugis@nict.go.jp>							 *
-*													 *
-* Copyright (c) 2010, 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 "fD.h"
-#include "cnxctx.h"
-
-#include <netinet/sctp.h>
-#include <sys/uio.h>
-
-/* Size of buffer to receive ancilliary data. May need to be enlarged if more sockopt are set... */
-#ifndef CMSG_BUF_LEN
-#define CMSG_BUF_LEN	1024
-#endif /* CMSG_BUF_LEN */
-
-/* Level of SCTP-specific traces */
-#ifdef DEBUG_SCTP
-#define SCTP_LEVEL	FULL
-#else /* DEBUG_SCTP */
-#define SCTP_LEVEL	(FCTS + 1)
-#endif /* DEBUG_SCTP */
-
-/* Temper with the retransmission timers to try and improve disconnection detection response? Undef this to keep the defaults of SCTP stack */
-#ifndef USE_DEFAULT_SCTP_RTX_PARAMS	/* make this a configuration option if useful */
-#define ADJUST_RTX_PARAMS
-#endif /* USE_DEFAULT_SCTP_RTX_PARAMS */
-
-/* Pre-binding socket options -- # streams read in config */
-/* The code of this file is based on draft-ietf-tsvwg-sctpsocket, versions 17 to 21 */
-static int fd_setsockopt_prebind(int sk)
-{
-	socklen_t sz;
-	
-	TRACE_ENTRY( "%d", sk);
-	
-	CHECK_PARAMS( sk > 0 );
-	
-#ifdef DEBUG
-	{
-		int reuse = 1;
-		CHECK_SYS(  setsockopt(sk, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse))  );
-	}
-#endif /* DEBUG	*/
-	
-#ifdef ADJUST_RTX_PARAMS
-	/* Set the retransmit parameters */
-	#ifdef SCTP_RTOINFO
-	{
-		struct sctp_rtoinfo rtoinfo;
-		memset(&rtoinfo, 0, sizeof(rtoinfo));
-
-		if (TRACE_BOOL(SCTP_LEVEL)) {
-			sz = sizeof(rtoinfo);
-			/* Read socket defaults */
-			CHECK_SYS(  getsockopt(sk, IPPROTO_SCTP, SCTP_RTOINFO, &rtoinfo, &sz)  );
-			if (sz != sizeof(rtoinfo))
-			{
-				TRACE_DEBUG(INFO, "Invalid size of socket option: %d / %d", sz, (socklen_t)sizeof(rtoinfo));
-				return ENOTSUP;
-			}
-			fd_log_debug( "Def SCTP_RTOINFO : srto_initial : %u\n", rtoinfo.srto_initial);
-			fd_log_debug( "                   srto_min     : %u\n", rtoinfo.srto_min);
-			fd_log_debug( "                   srto_max     : %u\n", rtoinfo.srto_max);
-		}
-
-		/* rtoinfo.srto_initial: Estimate of the RTT before it can be measured; keep the default value */
-		rtoinfo.srto_max = 5000; /* Maximum retransmit timer (in ms), we want fast retransmission time. */
-		rtoinfo.srto_min = 1000; /* Value under which the RTO does not descend, we set this value to not conflict with srto_max */
-
-		/* Set the option to the socket */
-		CHECK_SYS(  setsockopt(sk, IPPROTO_SCTP, SCTP_RTOINFO, &rtoinfo, sizeof(rtoinfo))  );
-		
-		if (TRACE_BOOL(SCTP_LEVEL)) {
-			/* Check new values */
-			CHECK_SYS(  getsockopt(sk, IPPROTO_SCTP, SCTP_RTOINFO, &rtoinfo, &sz)  );
-			fd_log_debug( "New SCTP_RTOINFO : srto_initial : %u\n", rtoinfo.srto_initial);
-			fd_log_debug( "                   srto_max     : %u\n", rtoinfo.srto_max);
-			fd_log_debug( "                   srto_min     : %u\n", rtoinfo.srto_min);
-		}
-	}
-	#else /* SCTP_RTOINFO */
-	TRACE_DEBUG(SCTP_LEVEL, "Skipping SCTP_RTOINFO");
-	#endif /* SCTP_RTOINFO */
-	
-	/* Set the association parameters: max number of retransmits, ... */
-	#ifdef SCTP_ASSOCINFO
-	{
-		struct sctp_assocparams assoc;
-		memset(&assoc, 0, sizeof(assoc));
-
-		if (TRACE_BOOL(SCTP_LEVEL)) {
-			sz = sizeof(assoc);
-			/* Read socket defaults */
-			CHECK_SYS(  getsockopt(sk, IPPROTO_SCTP, SCTP_ASSOCINFO, &assoc, &sz)  );
-			if (sz != sizeof(assoc))
-			{
-				TRACE_DEBUG(INFO, "Invalid size of socket option: %d / %d", sz, (socklen_t)sizeof(assoc));
-				return ENOTSUP;
-			}
-			fd_log_debug( "Def SCTP_ASSOCINFO : sasoc_asocmaxrxt               : %hu\n", assoc.sasoc_asocmaxrxt);
-			fd_log_debug( "                     sasoc_number_peer_destinations : %hu\n", assoc.sasoc_number_peer_destinations);
-			fd_log_debug( "                     sasoc_peer_rwnd                : %u\n" , assoc.sasoc_peer_rwnd);
-			fd_log_debug( "                     sasoc_local_rwnd               : %u\n" , assoc.sasoc_local_rwnd);
-			fd_log_debug( "                     sasoc_cookie_life              : %u\n" , assoc.sasoc_cookie_life);
-		}
-
-		assoc.sasoc_asocmaxrxt = 4;	/* Maximum number of retransmission attempts: we want fast detection of errors */
-						/* Note that this must remain less than the sum of retransmission parameters of the different paths. */
-		
-		/* Set the option to the socket */
-		CHECK_SYS(  setsockopt(sk, IPPROTO_SCTP, SCTP_ASSOCINFO, &assoc, sizeof(assoc))  );
-		
-		if (TRACE_BOOL(SCTP_LEVEL)) {
-			/* Check new values */
-			CHECK_SYS(  getsockopt(sk, IPPROTO_SCTP, SCTP_ASSOCINFO, &assoc, &sz)  );
-			fd_log_debug( "New SCTP_ASSOCINFO : sasoc_asocmaxrxt               : %hu\n", assoc.sasoc_asocmaxrxt);
-			fd_log_debug( "                     sasoc_number_peer_destinations : %hu\n", assoc.sasoc_number_peer_destinations);
-			fd_log_debug( "                     sasoc_peer_rwnd                : %u\n" , assoc.sasoc_peer_rwnd);
-			fd_log_debug( "                     sasoc_local_rwnd               : %u\n" , assoc.sasoc_local_rwnd);
-			fd_log_debug( "                     sasoc_cookie_life              : %u\n" , assoc.sasoc_cookie_life);
-		}
-	}
-	#else /* SCTP_ASSOCINFO */
-	TRACE_DEBUG(SCTP_LEVEL, "Skipping SCTP_ASSOCINFO");
-	#endif /* SCTP_ASSOCINFO */
-#endif /* ADJUST_RTX_PARAMS */
-	
-	/* Set the INIT parameters, such as number of streams */
-	#ifdef SCTP_INITMSG
-	{
-		struct sctp_initmsg init;
-		memset(&init, 0, sizeof(init));
-		
-		if (TRACE_BOOL(SCTP_LEVEL)) {
-			sz = sizeof(init);
-
-			/* Read socket defaults */
-			CHECK_SYS(  getsockopt(sk, IPPROTO_SCTP, SCTP_INITMSG, &init, &sz)  );
-			if (sz != sizeof(init))
-			{
-				TRACE_DEBUG(INFO, "Invalid size of socket option: %d / %d", sz, (socklen_t)sizeof(init));
-				return ENOTSUP;
-			}
-			fd_log_debug( "Def SCTP_INITMSG : sinit_num_ostreams   : %hu\n", init.sinit_num_ostreams);
-			fd_log_debug( "                   sinit_max_instreams  : %hu\n", init.sinit_max_instreams);
-			fd_log_debug( "                   sinit_max_attempts   : %hu\n", init.sinit_max_attempts);
-			fd_log_debug( "                   sinit_max_init_timeo : %hu\n", init.sinit_max_init_timeo);
-		}
-
-		/* Set the init options -- need to receive SCTP_COMM_UP to confirm the requested parameters, but we don't care (best effort) */
-		init.sinit_num_ostreams	  = fd_g_config->cnf_sctp_str;	/* desired number of outgoing streams */
-		init.sinit_max_init_timeo = CNX_TIMEOUT * 1000;
-
-		/* Set the option to the socket */
-		CHECK_SYS(  setsockopt(sk, IPPROTO_SCTP, SCTP_INITMSG, &init, sizeof(init))  );
-		
-		if (TRACE_BOOL(SCTP_LEVEL)) {
-			/* Check new values */
-			CHECK_SYS(  getsockopt(sk, IPPROTO_SCTP, SCTP_INITMSG, &init, &sz)  );
-			fd_log_debug( "New SCTP_INITMSG : sinit_num_ostreams   : %hu\n", init.sinit_num_ostreams);
-			fd_log_debug( "                   sinit_max_instreams  : %hu\n", init.sinit_max_instreams);
-			fd_log_debug( "                   sinit_max_attempts   : %hu\n", init.sinit_max_attempts);
-			fd_log_debug( "                   sinit_max_init_timeo : %hu\n", init.sinit_max_init_timeo);
-		}
-	}
-	#else /* SCTP_INITMSG */
-	TRACE_DEBUG(SCTP_LEVEL, "Skipping SCTP_INITMSG");
-	#endif /* SCTP_INITMSG */
-	
-	/* The SO_LINGER option will be reset if we want to perform SCTP ABORT */
-	#ifdef SO_LINGER
-	{
-		struct linger linger;
-		memset(&linger, 0, sizeof(linger));
-		
-		if (TRACE_BOOL(SCTP_LEVEL)) {
-			sz = sizeof(linger);
-			/* Read socket defaults */
-			CHECK_SYS(  getsockopt(sk, SOL_SOCKET, SO_LINGER, &linger, &sz)  );
-			if (sz != sizeof(linger))
-			{
-				TRACE_DEBUG(INFO, "Invalid size of socket option: %d / %d", sz, (socklen_t)sizeof(linger));
-				return ENOTSUP;
-			}
-			fd_log_debug( "Def SO_LINGER : l_onoff  : %d\n", linger.l_onoff);
-			fd_log_debug( " 	       l_linger : %d\n", linger.l_linger);
-		}
-		
-		linger.l_onoff	= 0;	/* Do not activate the linger */
-		linger.l_linger = 0;	/* Ignored, but it would mean : Return immediately when closing (=> abort) (graceful shutdown in background) */
-		
-		/* Set the option */
-		CHECK_SYS(  setsockopt(sk, SOL_SOCKET, SO_LINGER, &linger, sizeof(linger))  );
-		
-		if (TRACE_BOOL(SCTP_LEVEL)) {
-			/* Check new values */
-			CHECK_SYS(  getsockopt(sk, SOL_SOCKET, SO_LINGER, &linger, &sz)  );
-			fd_log_debug( "New SO_LINGER : l_onoff  : %d\n", linger.l_onoff);
-			fd_log_debug( "		  l_linger : %d\n", linger.l_linger);
-		}
-	}
-	#else /* SO_LINGER */
-	TRACE_DEBUG(SCTP_LEVEL, "Skipping SO_LINGER");
-	#endif /* SO_LINGER */
-	
-	/* Set the NODELAY option (Nagle-like algorithm) */
-	#ifdef SCTP_NODELAY
-	{
-		int nodelay;
-		
-		if (TRACE_BOOL(SCTP_LEVEL)) {
-			sz = sizeof(nodelay);
-			/* Read socket defaults */
-			CHECK_SYS(  getsockopt(sk, IPPROTO_SCTP, SCTP_NODELAY, &nodelay, &sz)  );
-			if (sz != sizeof(nodelay))
-			{
-				TRACE_DEBUG(INFO, "Invalid size of socket option: %d / %d", sz, (socklen_t)sizeof(nodelay));
-				return ENOTSUP;
-			}
-			fd_log_debug( "Def SCTP_NODELAY value : %s\n", nodelay ? "true" : "false");
-		}
-
-		nodelay = 1;	/* We turn ON the Nagle algorithm (probably the default already), since we might have several messages to send through the same proxy (not the same session). */
-		
-		/* Set the option to the socket */
-		CHECK_SYS(  setsockopt(sk, IPPROTO_SCTP, SCTP_NODELAY, &nodelay, sizeof(nodelay))  );
-		
-		if (TRACE_BOOL(SCTP_LEVEL)) {
-			/* Check new values */
-			CHECK_SYS(  getsockopt(sk, IPPROTO_SCTP, SCTP_NODELAY, &nodelay, &sz)  );
-			fd_log_debug( "New SCTP_NODELAY value : %s\n", nodelay ? "true" : "false");
-		}
-	}
-	#else /* SCTP_NODELAY */
-	TRACE_DEBUG(SCTP_LEVEL, "Skipping SCTP_NODELAY");
-	#endif /* SCTP_NODELAY */
-	
-	/*
-	   SO_RCVBUF			size of receiver window
-	   SO_SNDBUF			size of pending data to send
-	   SCTP_AUTOCLOSE		for one-to-many only
-	   SCTP_PRIMARY_ADDR		use this address as primary locally
-	   SCTP_ADAPTATION_LAYER	set adaptation layer indication, we don't use this 
-	*/
-	
-	/* Set the SCTP_DISABLE_FRAGMENTS option, required for TLS */
-	#ifdef SCTP_DISABLE_FRAGMENTS
-	{
-		int nofrag;
-		
-		if (TRACE_BOOL(SCTP_LEVEL)) {
-			sz = sizeof(nofrag);
-			/* Read socket defaults */
-			CHECK_SYS(  getsockopt(sk, IPPROTO_SCTP, SCTP_DISABLE_FRAGMENTS, &nofrag, &sz)  );
-			if (sz != sizeof(nofrag))
-			{
-				TRACE_DEBUG(INFO, "Invalid size of socket option: %d / %d", sz, (socklen_t)sizeof(nofrag));
-				return ENOTSUP;
-			}
-			fd_log_debug( "Def SCTP_DISABLE_FRAGMENTS value : %s\n", nofrag ? "true" : "false");
-		}
-
-		nofrag = 0;	/* We turn ON the fragmentation, since Diameter messages & TLS messages can be quite large. */
-		
-		/* Set the option to the socket */
-		CHECK_SYS(  setsockopt(sk, IPPROTO_SCTP, SCTP_DISABLE_FRAGMENTS, &nofrag, sizeof(nofrag))  );
-		
-		if (TRACE_BOOL(SCTP_LEVEL)) {
-			/* Check new values */
-			CHECK_SYS(  getsockopt(sk, IPPROTO_SCTP, SCTP_DISABLE_FRAGMENTS, &nofrag, &sz)  );
-			fd_log_debug( "New SCTP_DISABLE_FRAGMENTS value : %s\n", nofrag ? "true" : "false");
-		}
-	}
-	#else /* SCTP_DISABLE_FRAGMENTS */
-	# error "TLS requires support of SCTP_DISABLE_FRAGMENTS"
-	#endif /* SCTP_DISABLE_FRAGMENTS */
-	
-	/* SCTP_PEER_ADDR_PARAMS	control heartbeat per peer address. We set it as a default for all addresses in the association; not sure if it works ... */
-	#ifdef SCTP_PEER_ADDR_PARAMS
-	{
-		struct sctp_paddrparams parms;
-		memset(&parms, 0, sizeof(parms));
-		
-		/* Some kernel versions need this to be set */
-		parms.spp_address.ss_family = AF_INET;
-		
-		if (TRACE_BOOL(SCTP_LEVEL)) {
-			sz = sizeof(parms);
-
-			/* Read socket defaults */
-			CHECK_SYS(  getsockopt(sk, IPPROTO_SCTP, SCTP_PEER_ADDR_PARAMS, &parms, &sz)  );
-			if (sz != sizeof(parms))
-			{
-				TRACE_DEBUG(INFO, "Invalid size of socket option: %d / %d", sz, (socklen_t)sizeof(parms));
-				return ENOTSUP;
-			}
-			fd_log_debug( "Def SCTP_PEER_ADDR_PARAMS : spp_hbinterval    : %u\n",  parms.spp_hbinterval);
-			fd_log_debug( "                            spp_pathmaxrxt    : %hu\n", parms.spp_pathmaxrxt);
-			fd_log_debug( "                            spp_pathmtu       : %u\n",  parms.spp_pathmtu);
-			fd_log_debug( "                            spp_flags         : %x\n",  parms.spp_flags);
-			// fd_log_debug( "                            spp_ipv6_flowlabel: %u\n",  parms.spp_ipv6_flowlabel);
-			// fd_log_debug( "                            spp_ipv4_tos      : %hhu\n",parms.spp_ipv4_tos);
-		}
-
-		parms.spp_flags = SPP_HB_ENABLE;	/* Enable heartbeat for the association */
-		#ifdef SPP_PMTUD_ENABLE
-		parms.spp_flags |= SPP_PMTUD_ENABLE;	/* also enable path MTU discovery mechanism */
-		#endif /* SPP_PMTUD_ENABLE */
-		
-#ifdef ADJUST_RTX_PARAMS
-		parms.spp_hbinterval = 6000;		/* Send an heartbeat every 6 seconds to quickly start retransmissions */
-		/* parms.spp_pathmaxrxt : max nbr of restransmissions on this address. There is a relationship with sasoc_asocmaxrxt, so we leave the default here */
-#endif /* ADJUST_RTX_PARAMS */
-
-		/* Set the option to the socket */
-		CHECK_SYS(  setsockopt(sk, IPPROTO_SCTP, SCTP_PEER_ADDR_PARAMS, &parms, sizeof(parms)) );
-		
-		if (TRACE_BOOL(SCTP_LEVEL)) {
-			/* Check new values */
-			CHECK_SYS(  getsockopt(sk, IPPROTO_SCTP, SCTP_PEER_ADDR_PARAMS, &parms, &sz)  );
-			fd_log_debug( "New SCTP_PEER_ADDR_PARAMS : spp_hbinterval    : %u\n",  parms.spp_hbinterval);
-			fd_log_debug( "                            spp_pathmaxrxt    : %hu\n", parms.spp_pathmaxrxt);
-			fd_log_debug( "                            spp_pathmtu       : %u\n",  parms.spp_pathmtu);
-			fd_log_debug( "                            spp_flags         : %x\n",  parms.spp_flags);
-			// fd_log_debug( "                            spp_ipv6_flowlabel: %u\n",  parms.spp_ipv6_flowlabel);
-			// fd_log_debug( "                            spp_ipv4_tos      : %hhu\n",parms.spp_ipv4_tos);
-		}
-	}
-	#else /* SCTP_PEER_ADDR_PARAMS */
-	TRACE_DEBUG(SCTP_LEVEL, "Skipping SCTP_PEER_ADDR_PARAMS");
-	#endif /* SCTP_PEER_ADDR_PARAMS */
-	
-	/*
-	   SCTP_DEFAULT_SEND_PARAM	parameters for the sendto() call, we don't use it.
-	*/
-
-	/* Subscribe to some notifications */
-	#ifdef SCTP_EVENTS
-	{
-		struct sctp_event_subscribe event;
-
-		memset(&event, 0, sizeof(event));
-		event.sctp_data_io_event	= 1;	/* to receive the stream ID in SCTP_SNDRCV ancilliary data on message reception */
-		event.sctp_association_event	= 0;	/* new or closed associations (mostly for one-to-many style sockets) */
-		event.sctp_address_event	= 1;	/* address changes */
-		event.sctp_send_failure_event	= 1;	/* delivery failures */
-		event.sctp_peer_error_event	= 1;	/* remote peer sends an error */
-		event.sctp_shutdown_event	= 1;	/* peer has sent a SHUTDOWN */
-		event.sctp_partial_delivery_event = 1;	/* a partial delivery is aborted, probably indicating the connection is being shutdown */
-		// event.sctp_adaptation_layer_event = 0;	/* adaptation layer notifications */
-		// event.sctp_authentication_event = 0;	/* when new key is made active */
-
-		/* Set the option to the socket */
-		CHECK_SYS(  setsockopt(sk, IPPROTO_SCTP, SCTP_EVENTS, &event, sizeof(event)) );
-		
-		if (TRACE_BOOL(SCTP_LEVEL)) {
-			sz = sizeof(event);
-			CHECK_SYS(  getsockopt(sk, IPPROTO_SCTP, SCTP_EVENTS, &event, &sz) );
-			if (sz != sizeof(event))
-			{
-				TRACE_DEBUG(INFO, "Invalid size of socket option: %d / %d", sz, (socklen_t)sizeof(event));
-				return ENOTSUP;
-			}
-
-			fd_log_debug( "SCTP_EVENTS : sctp_data_io_event          : %hhu\n", event.sctp_data_io_event);
-			fd_log_debug( "       	     sctp_association_event      : %hhu\n", event.sctp_association_event);
-			fd_log_debug( "       	     sctp_address_event	         : %hhu\n", event.sctp_address_event);
-			fd_log_debug( "       	     sctp_send_failure_event     : %hhu\n", event.sctp_send_failure_event);
-			fd_log_debug( "       	     sctp_peer_error_event       : %hhu\n", event.sctp_peer_error_event);
-			fd_log_debug( "       	     sctp_shutdown_event	 : %hhu\n", event.sctp_shutdown_event);
-			fd_log_debug( "       	     sctp_partial_delivery_event : %hhu\n", event.sctp_partial_delivery_event);
-			fd_log_debug( "       	     sctp_adaptation_layer_event : %hhu\n", event.sctp_adaptation_layer_event);
-			// fd_log_debug( "             sctp_authentication_event    : %hhu\n", event.sctp_authentication_event);
-		}
-	}
-	#else /* SCTP_EVENTS */
-	TRACE_DEBUG(SCTP_LEVEL, "Skipping SCTP_EVENTS");
-	#endif /* SCTP_EVENTS */
-	
-	/* Set the v4 mapped addresses option */
-	#ifdef SCTP_I_WANT_MAPPED_V4_ADDR
-	{
-		int v4mapped;
-		
-		if (TRACE_BOOL(SCTP_LEVEL)) {
-			sz = sizeof(v4mapped);
-			/* Read socket defaults */
-			CHECK_SYS(  getsockopt(sk, IPPROTO_SCTP, SCTP_I_WANT_MAPPED_V4_ADDR, &v4mapped, &sz)  );
-			if (sz != sizeof(v4mapped))
-			{
-				TRACE_DEBUG(INFO, "Invalid size of socket option: %d / %d", sz, (socklen_t)sizeof(v4mapped));
-				return ENOTSUP;
-			}
-			fd_log_debug( "Def SCTP_I_WANT_MAPPED_V4_ADDR value : %s\n", v4mapped ? "true" : "false");
-		}
-
-		#ifndef SCTP_USE_MAPPED_ADDRESSES
-		v4mapped = 0;	/* We don't want v4 mapped addresses */
-		#else /* SCTP_USE_MAPPED_ADDRESSES */
-		v4mapped = 1;	/* but we may have to, otherwise the bind fails in some environments */
-		#endif /* SCTP_USE_MAPPED_ADDRESSES */
-		
-		/* Set the option to the socket */
-		CHECK_SYS(  setsockopt(sk, IPPROTO_SCTP, SCTP_I_WANT_MAPPED_V4_ADDR, &v4mapped, sizeof(v4mapped))  );
-		
-		if (TRACE_BOOL(SCTP_LEVEL)) {
-			/* Check new values */
-			CHECK_SYS(  getsockopt(sk, IPPROTO_SCTP, SCTP_I_WANT_MAPPED_V4_ADDR, &v4mapped, &sz)  );
-			fd_log_debug( "New SCTP_I_WANT_MAPPED_V4_ADDR value : %s\n", v4mapped ? "true" : "false");
-		}
-	}
-	#else /* SCTP_I_WANT_MAPPED_V4_ADDR */
-	TRACE_DEBUG(SCTP_LEVEL, "Skipping SCTP_I_WANT_MAPPED_V4_ADDR");
-	#endif /* SCTP_I_WANT_MAPPED_V4_ADDR */
-	
-	/*
-	   SCTP_MAXSEG			max size of fragmented segments -- bound to PMTU
-	   SCTP_HMAC_IDENT		authentication algorithms
-	   SCTP_AUTH_ACTIVE_KEY		set the active key
-	   SCTP_DELAYED_SACK		control delayed acks
-	*/
-	
-	
-	/* Set the interleaving option */
-	#ifdef SCTP_FRAGMENT_INTERLEAVE
-	{
-		int interleave;
-		
-		if (TRACE_BOOL(SCTP_LEVEL)) {
-			sz = sizeof(interleave);
-			/* Read socket defaults */
-			CHECK_SYS(  getsockopt(sk, IPPROTO_SCTP, SCTP_FRAGMENT_INTERLEAVE, &interleave, &sz)  );
-			if (sz != sizeof(interleave))
-			{
-				TRACE_DEBUG(INFO, "Invalid size of socket option: %d / %d", sz, (socklen_t)sizeof(interleave));
-				return ENOTSUP;
-			}
-			fd_log_debug( "Def SCTP_FRAGMENT_INTERLEAVE value : %d\n", interleave);
-		}
-
-		#if 0
-		interleave = 2;	/* Allow partial delivery on several streams at the same time, since we are stream-aware in our security modules */
-		#else /* 0 */
-		interleave = 1;	/* hmmm actually, we are not yet capable of handling this, and we don t need it. */
-		#endif /* 0 */
-		
-		/* Set the option to the socket */
-		CHECK_SYS(  setsockopt(sk, IPPROTO_SCTP, SCTP_FRAGMENT_INTERLEAVE, &interleave, sizeof(interleave))  );
-		
-		if (TRACE_BOOL(SCTP_LEVEL)) {
-			/* Check new values */
-			CHECK_SYS(  getsockopt(sk, IPPROTO_SCTP, SCTP_FRAGMENT_INTERLEAVE, &interleave, &sz)  );
-			fd_log_debug( "New SCTP_FRAGMENT_INTERLEAVE value : %d\n", interleave);
-		}
-	}
-	#else /* SCTP_FRAGMENT_INTERLEAVE */
-	TRACE_DEBUG(SCTP_LEVEL, "Skipping SCTP_FRAGMENT_INTERLEAVE");
-	#endif /* SCTP_FRAGMENT_INTERLEAVE */
-	
-	/*
-	   SCTP_PARTIAL_DELIVERY_POINT	control partial delivery size
-	   SCTP_USE_EXT_RCVINFO		use extended receive info structure (information about the next message if available)
-	 */
-	/* SCTP_AUTO_ASCONF is set by the postbind function */
-	/*
-	   SCTP_MAX_BURST		number of packets that can be burst emitted
-	   SCTP_CONTEXT			save a context information along with the association.
-	 */
-	 
-	/* SCTP_EXPLICIT_EOR: we assume implicit EOR in freeDiameter, so let's ensure this is known by the stack */
-	#ifdef SCTP_EXPLICIT_EOR
-	{
-		int bool;
-		
-		if (TRACE_BOOL(SCTP_LEVEL)) {
-			sz = sizeof(bool);
-			/* Read socket defaults */
-			CHECK_SYS(  getsockopt(sk, IPPROTO_SCTP, SCTP_EXPLICIT_EOR, &bool, &sz)  );
-			if (sz != sizeof(bool))
-			{
-				TRACE_DEBUG(INFO, "Invalid size of socket option: %d / %d", sz, (socklen_t)sizeof(bool));
-				return ENOTSUP;
-			}
-			fd_log_debug( "Def SCTP_EXPLICIT_EOR value : %s\n", bool ? "true" : "false");
-		}
-
-		bool = 0;
-		
-		/* Set the option to the socket */
-		CHECK_SYS(  setsockopt(sk, IPPROTO_SCTP, SCTP_EXPLICIT_EOR, &bool, sizeof(bool))  );
-		
-		if (TRACE_BOOL(SCTP_LEVEL)) {
-			/* Check new values */
-			CHECK_SYS(  getsockopt(sk, IPPROTO_SCTP, SCTP_EXPLICIT_EOR, &bool, &sz)  );
-			fd_log_debug( "New SCTP_EXPLICIT_EOR value : %s\n", bool ? "true" : "false");
-		}
-	}
-	#else /* SCTP_EXPLICIT_EOR */
-	TRACE_DEBUG(SCTP_LEVEL, "Skipping SCTP_EXPLICIT_EOR");
-	#endif /* SCTP_EXPLICIT_EOR */
-	
-	/*
-	   SCTP_REUSE_PORT		share one listening port with several sockets
-	   SCTP_EVENT			same as EVENTS ?
-	*/
-	
-	/* In case of no_ip4, force the v6only option */
-	#ifdef IPV6_V6ONLY
-	if (fd_g_config->cnf_flags.no_ip4) {
-		int opt = 1;
-		CHECK_SYS(setsockopt(sk, IPPROTO_IPV6, IPV6_V6ONLY, &opt, sizeof(opt)));
-	}
-	#endif /* IPV6_V6ONLY */
-	
-	return 0;
-}
-
-
-/* Post-binding socket options */
-static int fd_setsockopt_postbind(int sk, int bound_to_default)
-{
-	TRACE_ENTRY( "%d %d", sk, bound_to_default);
-	
-	CHECK_PARAMS( (sk > 0) );
-	
-	/* Set the ASCONF option */
-	#ifdef SCTP_AUTO_ASCONF
-	if (bound_to_default) {
-		int asconf;
-		
-		if (TRACE_BOOL(SCTP_LEVEL)) {
-			socklen_t sz;
-
-			sz = sizeof(asconf);
-			/* Read socket defaults */
-			CHECK_SYS(  getsockopt(sk, IPPROTO_SCTP, SCTP_AUTO_ASCONF, &asconf, &sz)  );
-			if (sz != sizeof(asconf))
-			{
-				TRACE_DEBUG(INFO, "Invalid size of socket option: %d / %d", sz, (socklen_t)sizeof(asconf));
-				return ENOTSUP;
-			}
-			fd_log_debug( "Def SCTP_AUTO_ASCONF value : %s\n", asconf ? "true" : "false");
-		}
-
-		asconf = 1;	/* allow automatic use of added or removed addresses in the association (for bound-all sockets) */
-		
-		/* Set the option to the socket */
-		CHECK_SYS(  setsockopt(sk, IPPROTO_SCTP, SCTP_AUTO_ASCONF, &asconf, sizeof(asconf))  );
-		
-		if (TRACE_BOOL(SCTP_LEVEL)) {
-			socklen_t sz = sizeof(asconf);
-			/* Check new values */
-			CHECK_SYS(  getsockopt(sk, IPPROTO_SCTP, SCTP_AUTO_ASCONF, &asconf, &sz)  );
-			fd_log_debug( "New SCTP_AUTO_ASCONF value : %s\n", asconf ? "true" : "false");
-		}
-	}
-	#else /* SCTP_AUTO_ASCONF */
-	TRACE_DEBUG(SCTP_LEVEL, "Skipping SCTP_AUTO_ASCONF");
-	#endif /* SCTP_AUTO_ASCONF */
-	
-	return 0;
-}
-
-/* Add addresses from a list to an array, with filter on the flags */
-static int add_addresses_from_list_mask(uint8_t ** array, size_t * size, int * addr_count, int target_family, uint16_t port, struct fd_list * list, uint32_t mask, uint32_t val)
-{
-	struct fd_list * li;
-	int to_add4 = 0;
-	int to_add6 = 0;
-	union {
-		uint8_t *buf;
-		sSA4	*sin;
-		sSA6	*sin6;
-	} ptr;
-	size_t sz;
-	
-	/* First, count the number of addresses to add */
-	for (li = list->next; li != list; li = li->next) {
-		struct fd_endpoint * ep = (struct fd_endpoint *) li;
-		
-		/* Do the flag match ? */
-		if ((val & mask) != (ep->flags & mask))
-			continue;
-		
-		if (ep->sa.sa_family == AF_INET) {
-			to_add4 ++;
-		} else {
-			to_add6 ++;
-		}
-	}
-	
-	if ((to_add4 + to_add6) == 0)
-		return 0; /* nothing to do */
-	
-	/* The size to add */
-	if (target_family == AF_INET) {
-		sz = to_add4 * sizeof(sSA4);
-	} else {
-		#ifndef SCTP_USE_MAPPED_ADDRESSES
-			sz = (to_add4 * sizeof(sSA4)) + (to_add6 * sizeof(sSA6));
-		#else /* SCTP_USE_MAPPED_ADDRESSES */
-			sz = (to_add4 + to_add6) * sizeof(sSA6);
-		#endif /* SCTP_USE_MAPPED_ADDRESSES */
-	}
-	
-	/* Now, (re)alloc the array to store the new addresses */
-	CHECK_MALLOC( *array = realloc(*array, *size + sz) );
-	
-	/* Finally, add the addresses */
-	for (li = list->next; li != list; li = li->next) {
-		struct fd_endpoint * ep = (struct fd_endpoint *) li;
-		
-		/* Skip v6 addresses for v4 socket */
-		if ((target_family == AF_INET) && (ep->sa.sa_family == AF_INET6))
-			continue;
-		
-		/* Are the flags matching ? */
-		if ((val & mask) != (ep->flags & mask))
-			continue;
-		
-		/* Size of the new SA we are adding (array may contain a mix of sockaddr_in and sockaddr_in6) */
-		#ifndef SCTP_USE_MAPPED_ADDRESSES
-		if (ep->sa.sa_family == AF_INET6)
-		#else /* SCTP_USE_MAPPED_ADDRESSES */
-		if (target_family == AF_INET6) {
-		#endif /* SCTP_USE_MAPPED_ADDRESSES */
-			sz = sizeof(sSA6);
-		else
-			sz = sizeof(sSA4);
-		
-		/* Place where we add the new address */
-		ptr.buf = *array + *size; /* place of the new SA */
-		
-		/* Update other information */
-		*size += sz;
-		*addr_count += 1;
-		
-		/* And write the addr in the buffer */
-		if (sz == sizeof(sSA4)) {
-			memcpy(ptr.buf, &ep->sin, sz);
-			ptr.sin->sin_port = port;
-		} else {
-			if (ep->sa.sa_family == AF_INET) { /* We must map the address */ 
-				memset(ptr.buf, 0, sz);
-				ptr.sin6->sin6_family = AF_INET6;
-				IN6_ADDR_V4MAP( &ptr.sin6->sin6_addr.s6_addr, ep->sin.sin_addr.s_addr );
-			} else {
-				memcpy(ptr.sin6, &ep->sin6, sz);
-			}
-			ptr.sin6->sin6_port = port;
-		}
-	}
-	
-	return 0;
-}
-
-/* Create a socket server and bind it according to daemon s configuration */
-int fd_sctp_create_bind_server( int * sock, int family, struct fd_list * list, uint16_t port )
-{
-	int bind_default;
-	
-	TRACE_ENTRY("%p %i %p %hu", sock, family, list, port);
-	CHECK_PARAMS(sock);
-	
-	/* Create the socket */
-	CHECK_SYS( *sock = socket(family, SOCK_STREAM, IPPROTO_SCTP) );
-	
-	/* Set pre-binding socket options, including number of streams etc... */
-	CHECK_FCT( fd_setsockopt_prebind(*sock) );
-	
-	bind_default = (! list) || (FD_IS_LIST_EMPTY(list)) ;
-redo:
-	if ( bind_default ) {
-		/* Implicit endpoints : bind to default addresses */
-		union {
-			sSS  ss;
-			sSA  sa;
-			sSA4 sin;
-			sSA6 sin6;
-		} s;
-		
-		/* 0.0.0.0 and [::] are all zeros */
-		memset(&s, 0, sizeof(s));
-		
-		s.sa.sa_family = family;
-		
-		if (family == AF_INET)
-			s.sin.sin_port = htons(port);
-		else
-			s.sin6.sin6_port = htons(port);
-		
-		CHECK_SYS( bind(*sock, &s.sa, sSAlen(&s)) );
-		
-	} else {
-		/* Explicit endpoints to bind to from config */
-		
-		sSA * sar = NULL; /* array of addresses */
-		size_t sz = 0; /* size of the array */
-		int count = 0; /* number of sock addr in the array */
-		
-		/* Create the array of configured addresses */
-		CHECK_FCT( add_addresses_from_list_mask((void *)&sar, &sz, &count, family, htons(port), list, EP_FL_CONF, EP_FL_CONF) );
-		
-		if (!count) {
-			/* None of the addresses in the list came from configuration, we bind to default */
-			bind_default = 1;
-			goto redo;
-		}
-		
-		if (TRACE_BOOL(SCTP_LEVEL)) {
-			union {
-				sSA	*sa;
-				uint8_t *buf;
-			} ptr;
-			int i;
-			ptr.sa = sar;
-			fd_log_debug("Calling sctp_bindx with the following address array:\n");
-			for (i = 0; i < count; i++) {
-				TRACE_DEBUG_sSA(FULL, "    - ", ptr.sa, NI_NUMERICHOST | NI_NUMERICSERV, "" );
-				ptr.buf += (ptr.sa->sa_family == AF_INET) ? sizeof(sSA4) : sizeof(sSA6) ;
-			}
-		}
-		
-		/* Bind to this array */
-		CHECK_SYS(  sctp_bindx(*sock, sar, count, SCTP_BINDX_ADD_ADDR)  );
-		
-		/* We don't need sar anymore */
-		free(sar);
-	}
-	
-	/* Now, the server is bound, set remaining sockopt */
-	CHECK_FCT( fd_setsockopt_postbind(*sock, bind_default) );
-	
-	/* Debug: show all local listening addresses */
-	if (TRACE_BOOL(SCTP_LEVEL)) {
-		sSA *sar;
-		union {
-			sSA	*sa;
-			uint8_t *buf;
-		} ptr;
-		int sz;
-		
-		CHECK_SYS(  sz = sctp_getladdrs(*sock, 0, &sar)  );
-		
-		fd_log_debug("SCTP server bound on :\n");
-		for (ptr.sa = sar; sz-- > 0; ptr.buf += (ptr.sa->sa_family == AF_INET) ? sizeof(sSA4) : sizeof(sSA6)) {
-			TRACE_DEBUG_sSA(FULL, "    - ", ptr.sa, NI_NUMERICHOST | NI_NUMERICSERV, "" );
-		}
-		sctp_freeladdrs(sar);
-	}
-
-	return 0;
-}
-
-/* Allow clients connections on server sockets */
-int fd_sctp_listen( int sock )
-{
-	TRACE_ENTRY("%d", sock);
-	CHECK_SYS( listen(sock, 5) );
-	return 0;
-}
-
-/* Create a client socket and connect to remote server */
-int fd_sctp_client( int *sock, int no_ip6, uint16_t port, struct fd_list * list )
-{
-	int family;
-	union {
-		uint8_t *buf;
-		sSA	*sa;
-	} sar;
-	size_t size = 0;
-	int count = 0;
-	int ret;
-	
-	sar.buf = NULL;
-	
-	TRACE_ENTRY("%p %i %hu %p", sock, no_ip6, port, list);
-	CHECK_PARAMS( sock && list && (!FD_IS_LIST_EMPTY(list)) );
-	
-	if (no_ip6) {
-		family = AF_INET;
-	} else {
-		family = AF_INET6;
-	}
-	
-	/* Create the socket */
-	CHECK_SYS( *sock = socket(family, SOCK_STREAM, IPPROTO_SCTP) );
-	
-	/* Cleanup if we are cancelled */
-	pthread_cleanup_push(fd_cleanup_socket, sock);
-	
-	/* Set the socket options */
-	CHECK_FCT_DO( ret = fd_setsockopt_prebind(*sock), goto fail );
-	
-	/* Create the array of addresses, add first the configured addresses, then the discovered, then the other ones */
-	CHECK_FCT_DO( ret = add_addresses_from_list_mask(&sar.buf, &size, &count, family, htons(port), list, EP_FL_CONF,              EP_FL_CONF	), goto fail );
-	CHECK_FCT_DO( ret = add_addresses_from_list_mask(&sar.buf, &size, &count, family, htons(port), list, EP_FL_CONF | EP_FL_DISC, EP_FL_DISC	), goto fail );
-	CHECK_FCT_DO( ret = add_addresses_from_list_mask(&sar.buf, &size, &count, family, htons(port), list, EP_FL_CONF | EP_FL_DISC, 0		), goto fail );
-	
-	/* Try connecting */
-	if (TRACE_BOOL(FULL)) {
-		TRACE_DEBUG(FULL, "Attempting SCTP connection (%d addresses attempted) :", count);
-		/* Dump the SAs */
-		union {
-			uint8_t *buf;
-			sSA	*sa;
-			sSA4	*sin;
-			sSA6	*sin6;
-		} ptr;
-		int i;
-		ptr.buf = sar.buf;
-		for (i=0; i< count; i++) {
-			TRACE_DEBUG_sSA(FULL, "  - ", ptr.sa, NI_NUMERICHOST | NI_NUMERICSERV, "" );
-			ptr.buf += (ptr.sa->sa_family == AF_INET) ? sizeof(sSA4) : sizeof(sSA6);
-		}
-	}
-	
-#ifdef SCTP_CONNECTX_4_ARGS
-	ret = sctp_connectx(*sock, sar.sa, count, NULL);
-#else /* SCTP_CONNECTX_4_ARGS */
-	ret = sctp_connectx(*sock, sar.sa, count);
-#endif /* SCTP_CONNECTX_4_ARGS */
-	
-	if (ret < 0) {
-		int lvl;
-		switch (ret = errno) {
-			case ECONNREFUSED:
-			
-				/* "Normal" errors */
-				lvl = FULL;
-				break;
-			default:
-				lvl = INFO;
-		}
-		/* Some errors are expected, we log at different level */
-		TRACE_DEBUG( lvl, "sctp_connectx returned an error: %s", strerror(ret));
-		goto fail;
-	}
-	
-	free(sar.buf); sar.buf = NULL;
-	
-	/* Set the remaining sockopts */
-	CHECK_FCT_DO( ret = fd_setsockopt_postbind(*sock, 1), goto fail_deco );
-	
-	/* Done! */
-	pthread_cleanup_pop(0);
-	return 0;
-	
-fail_deco:
-	CHECK_SYS_DO( shutdown(*sock, SHUT_RDWR), /* continue */ );
-fail:
-	if (*sock > 0) {
-		CHECK_SYS_DO( close(*sock), /* continue */ );
-		*sock = -1;
-	}
-	free(sar.buf);
-	return ret;
-}
-
-/* Retrieve streams information from a connected association -- optionaly provide the primary address */
-int fd_sctp_get_str_info( int sock, uint16_t *in, uint16_t *out, sSS *primary )
-{
-	struct sctp_status status;
-	socklen_t sz = sizeof(status);
-	
-	TRACE_ENTRY("%d %p %p %p", sock, in, out, primary);
-	CHECK_PARAMS( (sock > 0) && in && out );
-	
-	/* Read the association parameters */
-	memset(&status, 0, sizeof(status));
-	CHECK_SYS(  getsockopt(sock, IPPROTO_SCTP, SCTP_STATUS, &status, &sz) );
-	if (sz != sizeof(status))
-	{
-		TRACE_DEBUG(INFO, "Invalid size of socket option: %d / %zd", sz, sizeof(status));
-		return ENOTSUP;
-	}
-	if (TRACE_BOOL(SCTP_LEVEL)) {
-		fd_log_debug( "SCTP_STATUS : sstat_state                  : %i\n" , status.sstat_state);
-		fd_log_debug( "              sstat_rwnd  	          : %u\n" , status.sstat_rwnd);
-		fd_log_debug( "		     sstat_unackdata	          : %hu\n", status.sstat_unackdata);
-		fd_log_debug( "		     sstat_penddata 	          : %hu\n", status.sstat_penddata);
-		fd_log_debug( "		     sstat_instrms  	          : %hu\n", status.sstat_instrms);
-		fd_log_debug( "		     sstat_outstrms 	          : %hu\n", status.sstat_outstrms);
-		fd_log_debug( "		     sstat_fragmentation_point    : %u\n" , status.sstat_fragmentation_point);
-		fd_log_debug( "		     sstat_primary.spinfo_address : ");
-		sSA_DUMP_NODE_SERV(&status.sstat_primary.spinfo_address, NI_NUMERICHOST | NI_NUMERICSERV );
-		fd_log_debug( "\n" );
-		fd_log_debug( "		     sstat_primary.spinfo_state   : %d\n" , status.sstat_primary.spinfo_state);
-		fd_log_debug( "		     sstat_primary.spinfo_cwnd    : %u\n" , status.sstat_primary.spinfo_cwnd);
-		fd_log_debug( "		     sstat_primary.spinfo_srtt    : %u\n" , status.sstat_primary.spinfo_srtt);
-		fd_log_debug( "		     sstat_primary.spinfo_rto     : %u\n" , status.sstat_primary.spinfo_rto);
-		fd_log_debug( "		     sstat_primary.spinfo_mtu     : %u\n" , status.sstat_primary.spinfo_mtu);
-	}
-	
-	*in = status.sstat_instrms;
-	*out = status.sstat_outstrms;
-	
-	if (primary)
-		memcpy(primary, &status.sstat_primary.spinfo_address, sizeof(sSS));
-	
-	return 0;
-}
-
-/* Get the list of remote endpoints of the socket */
-int fd_sctp_get_remote_ep(int sock, struct fd_list * list)
-{
-	union {
-		sSA	*sa;
-		uint8_t	*buf;
-	} ptr;
-	
-	sSA * data = NULL;
-	int count;
-	
-	TRACE_ENTRY("%d %p", sock, list);
-	CHECK_PARAMS(list);
-	
-	/* Read the list on the socket */
-	CHECK_SYS( count = sctp_getpaddrs(sock, 0, &data)  );
-	ptr.sa = data;
-	
-	while (count) {
-		socklen_t sl;
-		switch (ptr.sa->sa_family) {
-			case AF_INET:	sl = sizeof(sSA4); break;
-			case AF_INET6:	sl = sizeof(sSA6); break;
-			default:
-				TRACE_DEBUG(INFO, "Unknown address family returned in sctp_getpaddrs: %d, skip", ptr.sa->sa_family);
-				/* There is a bug in current Linux kernel: http://www.spinics.net/lists/linux-sctp/msg00760.html */
-				goto stop;
-		}
-				
-		CHECK_FCT( fd_ep_add_merge( list, ptr.sa, sl, EP_FL_LL ) );
-		ptr.buf += sl;
-		count --;
-	}
-stop:	
-	/* Free the list */
-	sctp_freepaddrs(data);
-	
-	/* Now get the primary address, the add function will take care of merging with existing entry */
-	{
-		 
-		struct sctp_status status;
-		socklen_t sz = sizeof(status);
-		int ret;
-		
-		memset(&status, 0, sizeof(status));
-		/* Attempt to use SCTP_STATUS message to retrieve the primary address */
-		CHECK_SYS_DO( ret = getsockopt(sock, IPPROTO_SCTP, SCTP_STATUS, &status, &sz), /* continue */);
-		if (sz != sizeof(status))
-			ret = -1;
-		sz = sizeof(sSS);
-		if (ret < 0)
-		{
-			/* Fallback to getsockname -- not recommended by draft-ietf-tsvwg-sctpsocket-19#section-7.4 */
-			CHECK_SYS(getpeername(sock, (sSA *)&status.sstat_primary.spinfo_address, &sz));
-		}
-			
-		CHECK_FCT( fd_ep_add_merge( list, (sSA *)&status.sstat_primary.spinfo_address, sz, EP_FL_PRIMARY ) );
-	}
-	
-	/* Done! */
-	return 0;
-}
-
-/* Send a buffer over a specified stream */
-int fd_sctp_sendstr(int sock, uint16_t strid, uint8_t * buf, size_t len, uint32_t * cc_status)
-{
-	struct msghdr mhdr;
-	struct iovec  iov;
-	struct cmsghdr 		*hdr;
-	struct sctp_sndrcvinfo	*sndrcv;
-	uint8_t anci[CMSG_SPACE(sizeof(struct sctp_sndrcvinfo))];
-	ssize_t ret;
-	int timedout = 0;
-	
-	TRACE_ENTRY("%d %hu %p %zd %p", sock, strid, buf, len, cc_status);
-	CHECK_PARAMS(cc_status);
-	
-	memset(&mhdr, 0, sizeof(mhdr));
-	memset(&iov,  0, sizeof(iov));
-	memset(&anci, 0, sizeof(anci));
-	
-	/* IO Vector: message data */
-	iov.iov_base = buf;
-	iov.iov_len  = len;
-	
-	/* Anciliary data: specify SCTP stream */
-	hdr = (struct cmsghdr *)anci;
-	sndrcv = (struct sctp_sndrcvinfo *)CMSG_DATA(hdr);
-	hdr->cmsg_len   = sizeof(anci);
-	hdr->cmsg_level = IPPROTO_SCTP;
-	hdr->cmsg_type  = SCTP_SNDRCV;
-	sndrcv->sinfo_stream = strid;
-	/* note : we could store other data also, for example in .sinfo_ppid for remote peer or in .sinfo_context for errors. */
-	
-	/* We don't use mhdr.msg_name here; it could be used to specify an address different from the primary */
-	
-	mhdr.msg_iov    = &iov;
-	mhdr.msg_iovlen = 1;
-	
-	mhdr.msg_control    = anci;
-	mhdr.msg_controllen = sizeof(anci);
-	
-	TRACE_DEBUG(FULL, "Sending %db data on stream %hu of socket %d", len, strid, sock);
-again:	
-	ret = sendmsg(sock, &mhdr, 0);
-	/* Handle special case of timeout */
-	if ((ret < 0) && (errno == EAGAIN)) {
-		if (!(*cc_status & CC_STATUS_CLOSING))
-			goto again; /* don't care, just ignore */
-		if (!timedout) {
-			timedout ++; /* allow for one timeout while closing */
-			goto again;
-		}
-	}
-	
-	CHECK_SYS( ret );
-	ASSERT( ret == len ); /* There should not be partial delivery with sendmsg... */
-	
-	return 0;
-}
-
-/* Receive the next data from the socket, or next notification */
-int fd_sctp_recvmeta(int sock, uint16_t * strid, uint8_t ** buf, size_t * len, int *event, uint32_t * cc_status)
-{
-	ssize_t 		 ret = 0;
-	struct msghdr 		 mhdr;
-	char   			 ancidata[ CMSG_BUF_LEN ];
-	struct iovec 		 iov;
-	uint8_t			*data = NULL;
-	size_t 			 bufsz = 0, datasize = 0;
-	size_t			 mempagesz = sysconf(_SC_PAGESIZE); /* We alloc buffer by memory pages for efficiency */
-	int 			 timedout = 0;
-	
-	TRACE_ENTRY("%d %p %p %p %p %p", sock, strid, buf, len, event, cc_status);
-	CHECK_PARAMS( (sock > 0) && buf && len && event && cc_status );
-	
-	/* Cleanup out parameters */
-	*buf = NULL;
-	*len = 0;
-	*event = 0;
-	
-	/* Prepare header for receiving message */
-	memset(&mhdr, 0, sizeof(mhdr));
-	mhdr.msg_iov    = &iov;
-	mhdr.msg_iovlen = 1;
-	mhdr.msg_control    = &ancidata;
-	mhdr.msg_controllen = sizeof(ancidata);
-	
-	/* We will loop while all data is not received. */
-incomplete:
-	if (datasize == bufsz) {
-		/* The buffer is full, enlarge it */
-		bufsz += mempagesz;
-		CHECK_MALLOC( data = realloc(data, bufsz) );
-	}
-	/* the new data will be received following the preceding */
-	memset(&iov,  0, sizeof(iov));
-	iov.iov_base = data + datasize ;
-	iov.iov_len  = bufsz - datasize;
-
-	/* Receive data from the socket */
-again:
-	pthread_cleanup_push(free, data);
-	ret = recvmsg(sock, &mhdr, 0);
-	pthread_cleanup_pop(0);
-	
-	/* First, handle timeouts (same as fd_cnx_s_recv) */
-	if ((ret < 0) && (errno == EAGAIN)) {
-		if (!(*cc_status & CC_STATUS_CLOSING))
-			goto again; /* don't care, just ignore */
-		if (!timedout) {
-			timedout ++; /* allow for one timeout while closing */
-			goto again;
-		}
-		/* fallback to normal handling */
-	}
-	
-	/* Handle errors */
-	if (ret <= 0) { /* Socket timedout, closed, or an error occurred */
-		CHECK_SYS_DO(ret, /* to log in case of error */);
-		free(data);
-		*event = FDEVP_CNX_ERROR;
-		return 0;
-	}
-	
-	/* Update the size of data we received */
-	datasize += ret;
-
-	/* SCTP provides an indication when we received a full record; loop if it is not the case */
-	if ( ! (mhdr.msg_flags & MSG_EOR) ) {
-		goto incomplete;
-	}
-	
-	/* Handle the case where the data received is a notification */
-	if (mhdr.msg_flags & MSG_NOTIFICATION) {
-		union sctp_notification * notif = (union sctp_notification *) data;
-		
-		TRACE_DEBUG(FULL, "Received %db data of notification on socket %d", datasize, sock);
-	
-		switch (notif->sn_header.sn_type) {
-			
-			case SCTP_ASSOC_CHANGE:
-				TRACE_DEBUG(FULL, "Received SCTP_ASSOC_CHANGE notification");
-				TRACE_DEBUG(SCTP_LEVEL, "    state : %hu", notif->sn_assoc_change.sac_state);
-				TRACE_DEBUG(SCTP_LEVEL, "    error : %hu", notif->sn_assoc_change.sac_error);
-				TRACE_DEBUG(SCTP_LEVEL, "    instr : %hu", notif->sn_assoc_change.sac_inbound_streams);
-				TRACE_DEBUG(SCTP_LEVEL, "   outstr : %hu", notif->sn_assoc_change.sac_outbound_streams);
-				
-				*event = FDEVP_CNX_EP_CHANGE;
-				break;
-	
-			case SCTP_PEER_ADDR_CHANGE:
-				TRACE_DEBUG(FULL, "Received SCTP_PEER_ADDR_CHANGE notification");
-				TRACE_DEBUG_sSA(SCTP_LEVEL, "    intf_change : ", &(notif->sn_paddr_change.spc_aaddr), NI_NUMERICHOST | NI_NUMERICSERV, "" );
-				TRACE_DEBUG(SCTP_LEVEL, "          state : %d", notif->sn_paddr_change.spc_state);
-				TRACE_DEBUG(SCTP_LEVEL, "          error : %d", notif->sn_paddr_change.spc_error);
-				
-				*event = FDEVP_CNX_EP_CHANGE;
-				break;
-	
-			case SCTP_SEND_FAILED:
-				TRACE_DEBUG(FULL, "Received SCTP_SEND_FAILED notification");
-				TRACE_DEBUG(SCTP_LEVEL, "    len : %hu", notif->sn_send_failed.ssf_length);
-				TRACE_DEBUG(SCTP_LEVEL, "    err : %d",  notif->sn_send_failed.ssf_error);
-				
-				*event = FDEVP_CNX_ERROR;
-				break;
-			
-			case SCTP_REMOTE_ERROR:
-				TRACE_DEBUG(FULL, "Received SCTP_REMOTE_ERROR notification");
-				TRACE_DEBUG(SCTP_LEVEL, "    err : %hu", ntohs(notif->sn_remote_error.sre_error));
-				TRACE_DEBUG(SCTP_LEVEL, "    len : %hu", ntohs(notif->sn_remote_error.sre_length));
-				
-				*event = FDEVP_CNX_ERROR;
-				break;
-	
-			case SCTP_SHUTDOWN_EVENT:
-				TRACE_DEBUG(FULL, "Received SCTP_SHUTDOWN_EVENT notification");
-				
-				*event = FDEVP_CNX_SHUTDOWN;
-				break;
-			
-			default:	
-				TRACE_DEBUG(FULL, "Received unknown notification %d, assume error", notif->sn_header.sn_type);
-				*event = FDEVP_CNX_ERROR;
-		}
-		
-		free(data);
-		return 0;
-	}
-	
-	/* From this point, we have received a message */
-	*event = FDEVP_CNX_MSG_RECV;
-	*buf = data;
-	*len = datasize;
-	
-	if (strid) {
-		struct cmsghdr 		*hdr;
-		struct sctp_sndrcvinfo	*sndrcv;
-		
-		/* Handle the anciliary data */
-		for (hdr = CMSG_FIRSTHDR(&mhdr); hdr; hdr = CMSG_NXTHDR(&mhdr, hdr)) {
-
-			/* We deal only with anciliary data at SCTP level */
-			if (hdr->cmsg_level != IPPROTO_SCTP) {
-				TRACE_DEBUG(FULL, "Received some anciliary data at level %d, skipped", hdr->cmsg_level);
-				continue;
-			}
-			
-			/* Also only interested in SCTP_SNDRCV message for the moment */
-			if (hdr->cmsg_type != SCTP_SNDRCV) {
-				TRACE_DEBUG(FULL, "Anciliary block IPPROTO_SCTP / %d, skipped", hdr->cmsg_type);
-				continue;
-			}
-			
-			sndrcv = (struct sctp_sndrcvinfo *) CMSG_DATA(hdr);
-			if (TRACE_BOOL(SCTP_LEVEL)) {
-				fd_log_debug( "Anciliary block IPPROTO_SCTP / SCTP_SNDRCV\n");
-				fd_log_debug( "    sinfo_stream    : %hu\n", sndrcv->sinfo_stream);
-				fd_log_debug( "    sinfo_ssn       : %hu\n", sndrcv->sinfo_ssn);
-				fd_log_debug( "    sinfo_flags     : %hu\n", sndrcv->sinfo_flags);
-				/* fd_log_debug( "    sinfo_pr_policy : %hu\n", sndrcv->sinfo_pr_policy); */
-				fd_log_debug( "    sinfo_ppid      : %u\n" , sndrcv->sinfo_ppid);
-				fd_log_debug( "    sinfo_context   : %u\n" , sndrcv->sinfo_context);
-				/* fd_log_debug( "    sinfo_pr_value  : %u\n" , sndrcv->sinfo_pr_value); */
-				fd_log_debug( "    sinfo_tsn       : %u\n" , sndrcv->sinfo_tsn);
-				fd_log_debug( "    sinfo_cumtsn    : %u\n" , sndrcv->sinfo_cumtsn);
-			}
-
-			*strid = sndrcv->sinfo_stream;
-		}
-		TRACE_DEBUG(FULL, "Received %db data on socket %d, stream %hu", datasize, sock, *strid);
-	} else {
-		TRACE_DEBUG(FULL, "Received %db data on socket %d (stream ignored)", datasize, sock);
-	}
-	
-	return 0;
-}
Index: eeDiameter/sctps.c
===================================================================
--- freeDiameter/sctps.c	(revision 543)
+++ 	(revision )
@@ -1,716 +1,0 @@
-/*********************************************************************************************************
-* Software License Agreement (BSD License)                                                               *
-* Author: Sebastien Decugis <sdecugis@nict.go.jp>							 *
-*													 *
-* Copyright (c) 2010, 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 code for TLS over multi-stream SCTP wrapper implementation (GnuTLS does not support this) */
-/* See http://aaa.koganei.wide.ad.jp/blogs/index.php/waaad/2008/08/18/tls-over-sctp for history */
-
-#include "fD.h"
-#include "cnxctx.h"
-
-#include <netinet/sctp.h>
-#include <sys/uio.h>
-
-/*
-
-Architecture of this wrapper:
- - we have several fifo queues (1 per stream pairs).
- GnuTLS is configured to use custom push / pull functions:
- - the pull function retrieves the data from the fifo queue corresponding to a stream #.
- - the push function sends the data on a certain stream.
- We also have a demux thread that reads the socket and store received data in the appropriate fifo
- 
- We have one gnutls_session per stream pair, and as many streams that read the gnutls records and save incoming data to the target queue.
- 
-This complexity is required because we cannot read a socket for a given stream only; we can only get the next message and find its stream.
-*/
-
-
-
-/*************************************************************/
-/*                      threads                              */
-/*************************************************************/
-
-/* Demux received data and store in the appropriate fifo */
-static void * demuxer(void * arg)
-{
-	struct cnxctx * conn = arg;
-	uint8_t * buf;
-	size_t    bufsz;
-	int	  event;
-	uint16_t  strid;
-	
-	TRACE_ENTRY("%p", arg);
-	CHECK_PARAMS_DO(conn && (conn->cc_socket > 0), goto out);
-	
-	/* Set the thread name */
-	{
-		char buf[48];
-		snprintf(buf, sizeof(buf), "Demuxer (%d:%s)", conn->cc_socket, conn->cc_remid);
-		fd_log_threadname ( buf );
-	}
-	
-	ASSERT( conn->cc_proto == IPPROTO_SCTP );
-	ASSERT( Target_Queue(conn) );
-	ASSERT( conn->cc_sctps_data.array );
-	
-	do {
-		fd_cpu_flush_cache();
-		CHECK_FCT_DO( fd_sctp_recvmeta(conn->cc_socket, &strid, &buf, &bufsz, &event, &conn->cc_status), goto fatal );
-		switch (event) {
-			case FDEVP_CNX_MSG_RECV:
-				/* Demux this message to the appropriate fifo, another thread will pull, gnutls process, and send to target queue */
-				if (strid < conn->cc_sctp_para.pairs) {
-					CHECK_FCT_DO(fd_event_send(conn->cc_sctps_data.array[strid].raw_recv, event, bufsz, buf), goto fatal );
-				} else {
-					TRACE_DEBUG(INFO, "Received packet (%d bytes) on out-of-range stream #%d from %s, discarded.", bufsz, strid, conn->cc_remid);
-					free(buf);
-				}
-				break;
-				
-			case FDEVP_CNX_EP_CHANGE:
-				/* Send this event to the target queue */
-				fd_cpu_flush_cache();
-				CHECK_FCT_DO( fd_event_send( Target_Queue(conn), event, bufsz, buf), goto fatal );
-				break;
-			
-			case FDEVP_CNX_ERROR:
-				fd_cnx_markerror(conn);
-				goto out;
-				
-			case FDEVP_CNX_SHUTDOWN:
-				/* Just ignore the notification for now, we will get another error later anyway */
-				continue;
-				
-			default:
-				goto fatal;
-		}
-		
-	} while (conn->cc_loop);
-	
-out:
-	/* Signal termination of the connection to all decipher threads */
-	for (strid = 0; strid < conn->cc_sctp_para.pairs; strid++) {
-		if (conn->cc_sctps_data.array[strid].raw_recv)
-			CHECK_FCT_DO(fd_event_send(conn->cc_sctps_data.array[strid].raw_recv, FDEVP_CNX_ERROR, 0, NULL), goto fatal );
-	}
-	TRACE_DEBUG(FULL, "Thread terminated");	
-	return NULL;
-	
-fatal:
-	/* An unrecoverable error occurred, stop the daemon */
-	CHECK_FCT_DO(fd_event_send(fd_g_config->cnf_main_ev, FDEV_TERMINATE, 0, NULL), );
-	goto out;
-}
-
-/* Decrypt the data received in this stream pair and store it in the target queue */
-static void * decipher(void * arg)
-{
-	struct sctps_ctx * ctx = arg;
-	struct cnxctx 	 *cnx;
-	
-	TRACE_ENTRY("%p", arg);
-	CHECK_PARAMS_DO(ctx && ctx->raw_recv && ctx->parent, goto error);
-	cnx = ctx->parent;
-	ASSERT( Target_Queue(cnx) );
-	
-	/* Set the thread name */
-	{
-		char buf[48];
-		snprintf(buf, sizeof(buf), "Decipher (%hu@%d:%s)", ctx->strid, cnx->cc_socket, cnx->cc_remid);
-		fd_log_threadname ( buf );
-	}
-	
-	/* The next function loops while there is no error */
-	CHECK_FCT_DO(fd_tls_rcvthr_core(cnx, ctx->strid ? ctx->session : cnx->cc_tls_para.session), /* continue */);
-error:
-	fd_cnx_markerror(cnx);
-	TRACE_DEBUG(FULL, "Thread terminated");	
-	return NULL;
-}
-
-/*************************************************************/
-/*                     push / pull                           */
-/*************************************************************/
-
-/* Send data over the connection, called by gnutls */
-static ssize_t sctps_push(gnutls_transport_ptr_t tr, const void * data, size_t len)
-{
-	struct sctps_ctx * ctx = (struct sctps_ctx *) tr;
-	
-	TRACE_ENTRY("%p %p %zd", tr, data, len);
-	CHECK_PARAMS_DO( tr && data, { errno = EINVAL; return -1; } );
-	
-	fd_cpu_flush_cache();
-	CHECK_FCT_DO( fd_sctp_sendstr(ctx->parent->cc_socket, ctx->strid, (uint8_t *)data, len, &ctx->parent->cc_status), /* errno is already set */ return -1 );
-	
-	return len;
-}
-
-/* Retrieve data received on a stream and already demultiplexed */
-static ssize_t sctps_pull(gnutls_transport_ptr_t tr, void * buf, size_t len)
-{
-	struct sctps_ctx * ctx = (struct sctps_ctx *) tr;
-	size_t pulled = 0;
-	int emptied;
-	
-	TRACE_ENTRY("%p %p %zd", tr, buf, len);
-	CHECK_PARAMS_DO( tr && buf, { errno = EINVAL; return -1; } );
-	
-	/* If we don't have data available now, pull new message from the fifo -- this is blocking (until the queue is destroyed) */
-	if (!ctx->partial.buf) {
-		int ev;
-		CHECK_FCT_DO( errno = fd_event_get(ctx->raw_recv, &ev, &ctx->partial.bufsz, (void *)&ctx->partial.buf), return -1 );
-		if (ev == FDEVP_CNX_ERROR) 
-			return 0; /* connection closed */
-	}
-		
-	pulled = ctx->partial.bufsz - ctx->partial.offset;
-	if (pulled <= len) {
-		emptied = 1;
-	} else {
-		/* limit to the capacity of destination buffer */
-		emptied = 0;
-		pulled = len;
-	}
-
-	/* Store the data in the destination buffer */
-	memcpy(buf, ctx->partial.buf + ctx->partial.offset, pulled);
-
-	/* Free the buffer if we read all its content, and reset the partial structure */
-	if (emptied) {
-		free(ctx->partial.buf);
-		memset(&ctx->partial, 0, sizeof(ctx->partial));
-	} else {
-		ctx->partial.offset += pulled;
-	}
-
-	/* We are done */
-	return pulled;
-}
-
-/* Set the parameters of a session to use the appropriate fifo and stream information */
-static void set_sess_transport(gnutls_session_t session, struct sctps_ctx *ctx)
-{
-	/* Set the transport pointer passed to push & pull callbacks */
-	GNUTLS_TRACE( gnutls_transport_set_ptr( session, (gnutls_transport_ptr_t) ctx ) );
-	
-	/* Reset the low water value, since we don't use sockets */
-	GNUTLS_TRACE( gnutls_transport_set_lowat( session, 0 ) );
-	
-	/* Set the push and pull callbacks */
-	GNUTLS_TRACE( gnutls_transport_set_pull_function(session, sctps_pull) );
-	GNUTLS_TRACE( gnutls_transport_set_push_function(session, sctps_push) );
-
-	return;
-}
-
-/*************************************************************/
-/*               Session resuming support                    */
-/*************************************************************/
-
-struct sr_store {
-	struct fd_list	 list;	/* list of sr_data, ordered by key.size then key.data */
-	pthread_rwlock_t lock;
-	struct cnxctx   *parent;
-	/* Add another list to chain in a global list to implement a garbage collector on sessions -- TODO if needed */
-};
-
-/* Saved master session data for resuming sessions */
-struct sr_data {
-	struct fd_list	chain;
-	gnutls_datum_t	key;
-	gnutls_datum_t 	data;
-};
-
-/* Initialize the store area for a connection */
-static int store_init(struct cnxctx * conn)
-{
-	TRACE_ENTRY("%p", conn);
-	CHECK_PARAMS( conn && !conn->cc_sctps_data.sess_store );
-	
-	CHECK_MALLOC( conn->cc_sctps_data.sess_store = malloc(sizeof(struct sr_store)) );
-	memset(conn->cc_sctps_data.sess_store, 0, sizeof(struct sr_store));
-	
-	fd_list_init(&conn->cc_sctps_data.sess_store->list, NULL);
-	CHECK_POSIX( pthread_rwlock_init(&conn->cc_sctps_data.sess_store->lock, NULL) );
-	conn->cc_sctps_data.sess_store->parent = conn;
-	
-	return 0;
-}
-
-/* Destroy the store area for a connection, and all its content */
-static void store_destroy(struct cnxctx * conn)
-{
-	/* Del all list entries */
-	TRACE_ENTRY("%p", conn);
-	CHECK_PARAMS_DO( conn, return );
-	
-	if (!conn->cc_sctps_data.sess_store)
-		return;
-	
-	CHECK_POSIX_DO( pthread_rwlock_destroy(&conn->cc_sctps_data.sess_store->lock), /* continue */ );
-	
-	while (!FD_IS_LIST_EMPTY(&conn->cc_sctps_data.sess_store->list)) {
-		struct sr_data * sr = (struct sr_data *) conn->cc_sctps_data.sess_store->list.next;
-		fd_list_unlink( &sr->chain );
-		free(sr->key.data);
-		free(sr->data.data);
-		free(sr);
-	}
-	
-	free(conn->cc_sctps_data.sess_store);
-	conn->cc_sctps_data.sess_store = NULL;
-	return;
-}
-
-/* Search the position (or next if not found) of a key in a store */
-static struct fd_list * find_or_next(struct sr_store * sto, gnutls_datum_t key, int * match)
-{
-	struct fd_list * ret;
-	*match = 0;
-	
-	for (ret = sto->list.next; ret != &sto->list; ret = ret->next) {
-		int cmp = 0;
-		struct sr_data * sr = (struct sr_data *)ret;
-		
-		if ( key.size < sr->key.size )
-			break;
-		
-		if ( key.size > sr->key.size )
-			continue;
-		
-		/* Key sizes are equal */
-		cmp = memcmp( key.data, sr->key.data, key.size );
-		
-		if (cmp > 0)
-			continue;
-		
-		if (cmp == 0)
-			*match = 1;
-		
-		break;
-	}
-	
-	return ret;
-}
-
-/* Callbacks for the TLS server side of the connection, called during gnutls_handshake */
-static int sr_store (void *dbf, gnutls_datum_t key, gnutls_datum_t data)
-{
-	struct sr_store * sto = (struct sr_store *)dbf;
-	struct fd_list * li;
-	struct sr_data * sr;
-	int match = 0;
-	int ret = 0;
-	
-	TRACE_DEBUG( GNUTLS_DBG_LEVEL, "GNUTLS Callback: %s", __PRETTY_FUNCTION__ );
-	CHECK_PARAMS_DO( sto && key.data && data.data, return -1 );
-	
-	CHECK_POSIX_DO( pthread_rwlock_wrlock(&sto->lock), return -1 );
-	TRACE_DEBUG_BUFFER(GNUTLS_DBG_LEVEL, "Session store [key ", key.data, key.size, "]");
-	
-	li = find_or_next(sto, key, &match);
-	if (match) {
-		sr = (struct sr_data *)li;
-		
-		/* Check the data is the same */
-		if ((data.size != sr->data.size) || memcmp(data.data, sr->data.data, data.size)) {
-			TRACE_DEBUG(INFO, "GnuTLS tried to store a session with same key and different data!");
-			TRACE_DEBUG_BUFFER(INFO, "Session store [key ", key.data, key.size, "]");
-			TRACE_DEBUG_BUFFER(INFO, "  -- old data [", sr->data.data, sr->data.size, "]");
-			TRACE_DEBUG_BUFFER(INFO, "  -- new data [", data.data, data.size, "]");
-			
-			ret = -1;
-		} else {
-			TRACE_DEBUG(GNUTLS_DBG_LEVEL, "GnuTLS tried to store a session with same key and same data, skipped.");
-		}
-		goto out;
-	}
-	
-	/* Create a new entry */
-	CHECK_MALLOC_DO( sr = malloc(sizeof(struct sr_data)), { ret = -1; goto out; } );
-	memset(sr, 0, sizeof(struct sr_data));
-
-	fd_list_init(&sr->chain, sr);
-
-	CHECK_MALLOC_DO( sr->key.data = malloc(key.size), { ret = -1; goto out; } );
-	sr->key.size = key.size;
-	memcpy(sr->key.data, key.data, key.size);
-
-	CHECK_MALLOC_DO( sr->data.data = malloc(data.size), { ret = -1; goto out; } );
-	sr->data.size = data.size;
-	memcpy(sr->data.data, data.data, data.size);
-	
-	/* Save this new entry in the list, we are done */
-	fd_list_insert_before(li, &sr->chain);
-
-out:	
-	CHECK_POSIX_DO( pthread_rwlock_unlock(&sto->lock), return -1 );
-	return ret;
-}
-
-static int sr_remove (void *dbf, gnutls_datum_t key)
-{
-	struct sr_store * sto = (struct sr_store *)dbf;
-	struct fd_list * li;
-	struct sr_data * sr;
-	int match = 0;
-	int ret = 0;
-	
-	TRACE_DEBUG( GNUTLS_DBG_LEVEL, "GNUTLS Callback: %s", __PRETTY_FUNCTION__ );
-	CHECK_PARAMS_DO( sto && key.data, return -1 );
-	
-	CHECK_POSIX_DO( pthread_rwlock_wrlock(&sto->lock), return -1 );
-	TRACE_DEBUG_BUFFER(GNUTLS_DBG_LEVEL, "Session delete [key ", key.data, key.size, "]");
-	
-	li = find_or_next(sto, key, &match);
-	if (match) {
-		sr = (struct sr_data *)li;
-		
-		/* Destroy this data */
-		fd_list_unlink(li);
-		free(sr->key.data);
-		free(sr->data.data);
-		free(sr);
-	} else {
-		/* It was not found */
-		ret = -1;
-	}
-	
-	CHECK_POSIX_DO( pthread_rwlock_unlock(&sto->lock), return -1 );
-	return ret;
-}
-
-static gnutls_datum_t sr_fetch (void *dbf, gnutls_datum_t key)
-{
-	struct sr_store * sto = (struct sr_store *)dbf;
-	struct fd_list * li;
-	struct sr_data * sr;
-	int match = 0;
-	gnutls_datum_t res = { NULL, 0 };
-	gnutls_datum_t error = { NULL, 0 };
-
-	TRACE_DEBUG( GNUTLS_DBG_LEVEL, "GNUTLS Callback: %s", __PRETTY_FUNCTION__ );
-	CHECK_PARAMS_DO( sto && key.data, return error );
-
-	CHECK_POSIX_DO( pthread_rwlock_rdlock(&sto->lock), return error );
-	TRACE_DEBUG_BUFFER(GNUTLS_DBG_LEVEL, "Session fetch [key ", key.data, key.size, "]");
-	
-	li = find_or_next(sto, key, &match);
-	if (match) {
-		sr = (struct sr_data *)li;
-		GNUTLS_TRACE( CHECK_MALLOC_DO(res.data = gnutls_malloc(sr->data.size), goto out ) );
-		res.size = sr->data.size;
-		memcpy(res.data, sr->data.data, res.size);
-	}
-out:	
-	TRACE_DEBUG(GNUTLS_DBG_LEVEL, "Fetched (%p, %d) from store %p", res.data, res.size, sto);
-	CHECK_POSIX_DO( pthread_rwlock_unlock(&sto->lock), return error);
-	return res;
-}
-
-/* Set the session pointer in a session object */
-static void set_resume_callbacks(gnutls_session_t session, struct cnxctx * conn)
-{
-	TRACE_ENTRY("%p", conn);
-	
-	GNUTLS_TRACE( gnutls_db_set_retrieve_function(session, sr_fetch));
-	GNUTLS_TRACE( gnutls_db_set_remove_function  (session, sr_remove));
-	GNUTLS_TRACE( gnutls_db_set_store_function   (session, sr_store));
-	GNUTLS_TRACE( gnutls_db_set_ptr              (session, conn->cc_sctps_data.sess_store));
-	
-	return;
-}
-
-/* The handshake is made in parallel in several threads to speed up */
-static void * handshake_resume_th(void * arg)
-{
-	struct sctps_ctx * ctx = (struct sctps_ctx *) arg;
-	int resumed;
-	
-	TRACE_ENTRY("%p", arg);
-	
-	/* Set the thread name */
-	{
-		char buf[48];
-		snprintf(buf, sizeof(buf), "Handshake resume (%hu@%d)", ctx->strid, ctx->parent->cc_socket);
-		fd_log_threadname ( buf );
-	}
-	
-	TRACE_DEBUG(FULL, "Starting TLS resumed handshake on stream %hu", ctx->strid);
-	CHECK_GNUTLS_DO( gnutls_handshake( ctx->session ), return NULL);
-			
-	GNUTLS_TRACE( resumed = gnutls_session_is_resumed(ctx->session) );
-	if (!resumed) {
-		/* Check the credentials here also */
-		CHECK_FCT_DO( fd_tls_verify_credentials(ctx->session, ctx->parent, 0), return NULL );
-	}
-	if (TRACE_BOOL(FULL)) {
-		if (resumed) {
-			fd_log_debug("Session was resumed successfully on stream %hu (conn: '%s')\n", ctx->strid, fd_cnx_getid(ctx->parent));
-		} else {
-			fd_log_debug("Session was NOT resumed on stream %hu  (full handshake + verif) (conn: '%s')\n", ctx->strid, fd_cnx_getid(ctx->parent));
-		}
-	}
-			
-	/* Finished, OK */
-	return arg;
-}
-
-
-/*************************************************************/
-/*                     Exported functions                    */
-/*************************************************************/
-
-/* Initialize the wrapper for the connection */
-int fd_sctps_init(struct cnxctx * conn)
-{
-	uint16_t i;
-	
-	TRACE_ENTRY("%p", conn);
-	CHECK_PARAMS( conn && (conn->cc_sctp_para.pairs > 1) && (!conn->cc_sctps_data.array) );
-	
-	/* First, alloc the array and initialize the non-TLS data */
-	CHECK_MALLOC( conn->cc_sctps_data.array = calloc(conn->cc_sctp_para.pairs, sizeof(struct sctps_ctx))  );
-	for (i = 0; i < conn->cc_sctp_para.pairs; i++) {
-		conn->cc_sctps_data.array[i].parent = conn;
-		conn->cc_sctps_data.array[i].strid  = i;
-		CHECK_FCT( fd_fifo_new(&conn->cc_sctps_data.array[i].raw_recv) );
-	}
-	
-	/* Set push/pull functions in the master session, using fifo in array[0] */
-	set_sess_transport(conn->cc_tls_para.session, &conn->cc_sctps_data.array[0]);
-	
-	/* For server side, we also initialize the resuming capabilities */
-	if (conn->cc_tls_para.mode == GNUTLS_SERVER) {
-		
-		/* Prepare the store for sessions data */
-		CHECK_FCT( store_init(conn) );
-		
-		/* Set the callbacks for resuming in the master session */
-		set_resume_callbacks(conn->cc_tls_para.session, conn);
-	}
-
-	/* Start the demux thread */
-	CHECK_POSIX( pthread_create( &conn->cc_rcvthr, NULL, demuxer, conn ) );
-	
-	return 0;
-}
-
-/* Handshake other streams, after full handshake on the master session */
-int fd_sctps_handshake_others(struct cnxctx * conn, char * priority, void * alt_creds)
-{
-	uint16_t i;
-	int errors = 0;
-	gnutls_datum_t 	master_data;
-	
-	TRACE_ENTRY("%p %p", conn, priority);
-	CHECK_PARAMS( conn && (conn->cc_sctp_para.pairs > 1) && conn->cc_sctps_data.array );
-
-	/* Server side: we set all the parameters, the resume callback will take care of resuming the session */
-	/* Client side: we duplicate the parameters of the master session, then set the transport pointer */
-	
-	/* For client side, retrieve the master session parameters */
-	if (conn->cc_tls_para.mode == GNUTLS_CLIENT) {
-		CHECK_GNUTLS_DO( gnutls_session_get_data2(conn->cc_tls_para.session, &master_data), return ENOMEM );
-		/* For debug: */
-		if (TRACE_BOOL(GNUTLS_DBG_LEVEL)) {
-			uint8_t  id[256];
-			size_t	 ids = sizeof(id);
-			CHECK_GNUTLS_DO( gnutls_session_get_id(conn->cc_tls_para.session, id, &ids), /* continue */ );
-			TRACE_DEBUG_BUFFER(GNUTLS_DBG_LEVEL, "Master session id: [", id, ids, "]");
-		}
-	}
-	
-	/* Initialize the session objects and start the handshake in a separate thread */
-	for (i = 1; i < conn->cc_sctp_para.pairs; i++) {
-		/* Set credentials and priority */
-		CHECK_FCT( fd_tls_prepare(&conn->cc_sctps_data.array[i].session, conn->cc_tls_para.mode, priority, alt_creds) );
-		
-		/* For the client, copy data from master session; for the server, set session resuming pointers */
-		if (conn->cc_tls_para.mode == GNUTLS_CLIENT) {
-			CHECK_GNUTLS_DO( gnutls_session_set_data(conn->cc_sctps_data.array[i].session, master_data.data, master_data.size), return ENOMEM );
-		} else {
-			set_resume_callbacks(conn->cc_sctps_data.array[i].session, conn);
-		}
-		
-		/* Set transport parameters */
-		set_sess_transport(conn->cc_sctps_data.array[i].session, &conn->cc_sctps_data.array[i]);
-		
-		/* Start the handshake thread */
-		CHECK_POSIX( pthread_create( &conn->cc_sctps_data.array[i].thr, NULL, handshake_resume_th, &conn->cc_sctps_data.array[i] ) );
-	}
-	
-	/* We can now release the memory of master session data if any */
-	if (conn->cc_tls_para.mode == GNUTLS_CLIENT) {
-		GNUTLS_TRACE( gnutls_free(master_data.data) );
-	}
-	
-	/* Now wait for all handshakes to finish */
-	for (i = 1; i < conn->cc_sctp_para.pairs; i++) {
-		void * ret;
-		CHECK_POSIX( pthread_join(conn->cc_sctps_data.array[i].thr, &ret) );
-		conn->cc_sctps_data.array[i].thr = (pthread_t) NULL;
-		if (ret == NULL) {
-			errors++; /* Handshake failed on this stream */
-		}
-	}
-	
-	if (errors) {
-		TRACE_DEBUG(INFO, "Handshake failed on %d/%hd stream pairs", errors, conn->cc_sctp_para.pairs);
-		fd_cnx_markerror(conn);
-		return ENOTCONN;
-	}
-	
-	return 0;
-}
-
-/* Receive messages from others ? all other stream pairs : the master pair */
-int fd_sctps_startthreads(struct cnxctx * conn, int others)
-{
-	uint16_t i;
-	
-	TRACE_ENTRY("%p", conn);
-	CHECK_PARAMS( conn && conn->cc_sctps_data.array );
-	
-	if (others) {
-		for (i = 1; i < conn->cc_sctp_para.pairs; i++) {
-
-			/* Start the decipher thread */
-			CHECK_POSIX( pthread_create( &conn->cc_sctps_data.array[i].thr, NULL, decipher, &conn->cc_sctps_data.array[i] ) );
-		}
-	} else {
-		CHECK_POSIX( pthread_create( &conn->cc_sctps_data.array[0].thr, NULL, decipher, &conn->cc_sctps_data.array[0] ) );
-	}
-	return 0;
-}
-
-/* Initiate a "bye" on all stream pairs */
-void fd_sctps_bye(struct cnxctx * conn)
-{
-	uint16_t i;
-	
-	CHECK_PARAMS_DO( conn && conn->cc_sctps_data.array, return );
-	
-	/* End all TLS sessions, in series (not as efficient as paralel, but simpler) */
-	for (i = 1; i < conn->cc_sctp_para.pairs; i++) {
-		fd_cpu_flush_cache();
-		if ( ! (conn->cc_status & CC_STATUS_ERROR)) {
-			CHECK_GNUTLS_DO( gnutls_bye(conn->cc_sctps_data.array[i].session, GNUTLS_SHUT_WR), fd_cnx_markerror(conn) );
-		}
-	}
-}
-
-/* After "bye" was sent on all streams, read from sessions until an error is received */
-void fd_sctps_waitthreadsterm(struct cnxctx * conn)
-{
-	uint16_t i;
-	
-	TRACE_ENTRY("%p", conn);
-	CHECK_PARAMS_DO( conn && conn->cc_sctps_data.array, return );
-	
-	for (i = 0; i < conn->cc_sctp_para.pairs; i++) {
-		if (conn->cc_sctps_data.array[i].thr != (pthread_t)NULL) {
-			CHECK_POSIX_DO( pthread_join(conn->cc_sctps_data.array[i].thr, NULL), /* continue */ );
-			conn->cc_sctps_data.array[i].thr = (pthread_t)NULL;
-		}
-	}
-	return;
-}
-
-/* Free gnutls resources of all sessions */
-void fd_sctps_gnutls_deinit_others(struct cnxctx * conn)
-{
-	uint16_t i;
-	
-	TRACE_ENTRY("%p", conn);
-	CHECK_PARAMS_DO( conn && conn->cc_sctps_data.array, return );
-	
-	for (i = 1; i < conn->cc_sctp_para.pairs; i++) {
-		if (conn->cc_sctps_data.array[i].session) {
-			GNUTLS_TRACE( gnutls_deinit(conn->cc_sctps_data.array[i].session) );
-			conn->cc_sctps_data.array[i].session = NULL;
-		}
-	}
-}
-
-
-/* Stop all receiver threads */
-void fd_sctps_stopthreads(struct cnxctx * conn)
-{
-	uint16_t i;
-	
-	TRACE_ENTRY("%p", conn);
-	CHECK_PARAMS_DO( conn && conn->cc_sctps_data.array, return );
-	
-	for (i = 0; i < conn->cc_sctp_para.pairs; i++) {
-		CHECK_FCT_DO( fd_thr_term(&conn->cc_sctps_data.array[i].thr), /* continue */ );
-	}
-	return;
-}
-
-/* Destroy a wrapper context */
-void fd_sctps_destroy(struct cnxctx * conn)
-{
-	uint16_t i;
-	
-	CHECK_PARAMS_DO( conn && conn->cc_sctps_data.array, return );
-	
-	/* Terminate all receiving threads in case we did not do it yet */
-	fd_sctps_stopthreads(conn);
-	
-	/* Now, stop the demux thread */
-	CHECK_FCT_DO( fd_thr_term(&conn->cc_rcvthr), /* continue */ );
-	
-	/* Free remaining data in the array */
-	for (i = 0; i < conn->cc_sctp_para.pairs; i++) {
-		if (conn->cc_sctps_data.array[i].raw_recv)
-			fd_event_destroy( &conn->cc_sctps_data.array[i].raw_recv, free );
-		free(conn->cc_sctps_data.array[i].partial.buf);
-		if (conn->cc_sctps_data.array[i].session) {
-			GNUTLS_TRACE( gnutls_deinit(conn->cc_sctps_data.array[i].session) );
-			conn->cc_sctps_data.array[i].session = NULL;
-		}
-	}
-	
-	/* Free the array itself now */
-	free(conn->cc_sctps_data.array);
-	conn->cc_sctps_data.array = NULL;
-	
-	/* Delete the store of sessions */
-	store_destroy(conn);
-	
-	return ;
-}
Index: eeDiameter/server.c
===================================================================
--- freeDiameter/server.c	(revision 590)
+++ 	(revision )
@@ -1,399 +1,0 @@
-/*********************************************************************************************************
-* Software License Agreement (BSD License)                                                               *
-* Author: Sebastien Decugis <sdecugis@nict.go.jp>							 *
-*													 *
-* Copyright (c) 2010, 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 "fD.h"
-
-/* Server (listening) part of the daemon */
-
-struct fd_list		FD_SERVERS = FD_LIST_INITIALIZER(FD_SERVERS);	/* The list of all server objects */
-/* We don't need to protect this list, it is only accessed from the main daemon thread. */
-
-/* Servers information */
-struct server {
-	struct fd_list	chain;		/* link in the FD_SERVERS list */
-
-	struct cnxctx *	conn;		/* server connection context (listening socket) */
-	int 		proto;		/* IPPROTO_TCP or IPPROTO_SCTP */
-	int 		secur;		/* TLS is started immediatly after connection ? */
-	
-	pthread_t	thr;		/* The thread listening for new connections */
-	int		status;		/* 0 : not created; 1 : running; 2 : terminated */
-	
-	struct fd_list	clients;	/* List of clients connected to this server, not yet identified */
-	pthread_mutex_t	clients_mtx;	/* Mutex to protect the list of clients */
-};
-
-/* Client information (connecting peer for which we don't have the CER yet) */
-struct client {
-	struct fd_list	 chain;	/* link in the server's list of clients */
-	struct cnxctx	*conn;	/* Parameters of the connection */
-	struct timespec	 ts;	/* Deadline for receiving CER (after INCNX_TIMEOUT) */
-	pthread_t	 thr; 	/* connection state machine */
-};
-
-
-/* Dump all servers information */
-void fd_servers_dump()
-{
-	struct fd_list * li, *cli;
-	
-	fd_log_debug("Dumping servers list :\n");
-	for (li = FD_SERVERS.next; li != &FD_SERVERS; li = li->next) {
-		struct server * s = (struct server *)li;
-		fd_cpu_flush_cache();
-		fd_log_debug("  Serv %p '%s': %s, %s, %s\n", 
-				s, fd_cnx_getid(s->conn), 
-				IPPROTO_NAME( s->proto ),
-				s->secur ? "Secur" : "NotSecur",
-				(s->status == 0) ? "Thread not created" :
-				((s->status == 1) ? "Thread running" :
-				((s->status == 2) ? "Thread terminated" :
-							  "Thread status unknown")));
-		/* Dump the client list of this server */
-		(void) pthread_mutex_lock(&s->clients_mtx);
-		for (cli = s->clients.next; cli != &s->clients; cli = cli->next) {
-			struct client * c = (struct client *)cli;
-			char bufts[128];
-			fd_log_debug("     Connected: '%s' (timeout: %s)\n",
-					fd_cnx_getid(c->conn),
-					fd_log_time(&c->ts, bufts, sizeof(bufts)));
-		}
-		(void) pthread_mutex_unlock(&s->clients_mtx);
-	}
-}
-
-
-/* The state machine to handle incoming connection before the remote peer is identified */
-static void * client_sm(void * arg)
-{
-	struct client * c = arg;
-	struct server * s = NULL;
-	uint8_t       * buf = NULL;
-	size_t 		bufsz;
-	struct msg    * msg = NULL;
-	struct msg_hdr *hdr = NULL;
-	
-	TRACE_ENTRY("%p", c);
-	
-	CHECK_PARAMS_DO(c && c->conn && c->chain.head, goto fatal_error );
-	
-	s = c->chain.head->o;
-	
-	/* Name the current thread */
-	fd_log_threadname ( fd_cnx_getid(c->conn) );
-	
-	/* Handshake if we are a secure server port, or start clear otherwise */
-	if (s->secur) {
-		int ret = fd_cnx_handshake(c->conn, GNUTLS_SERVER, NULL, NULL);
-		if (ret != 0) {
-			if (TRACE_BOOL(INFO)) {
-				fd_log_debug("TLS handshake failed for client '%s', connection aborted.\n", fd_cnx_getid(c->conn));
-			}
-			goto cleanup;
-		}
-	} else {
-		CHECK_FCT_DO( fd_cnx_start_clear(c->conn, 0), goto cleanup );
-	}
-	
-	/* Set the timeout to receive the first message */
-	CHECK_SYS_DO( clock_gettime(CLOCK_REALTIME, &c->ts), goto fatal_error );
-	c->ts.tv_sec += INCNX_TIMEOUT;
-	
-	/* Receive the first Diameter message on the connection -- cleanup in case of timeout */
-	CHECK_FCT_DO( fd_cnx_receive(c->conn, &c->ts, &buf, &bufsz), goto cleanup );
-	
-	TRACE_DEBUG(FULL, "Received %zdb from new client '%s'", bufsz, fd_cnx_getid(c->conn));
-	
-	/* Try parsing this message */
-	CHECK_FCT_DO( fd_msg_parse_buffer( &buf, bufsz, &msg ), /* Parsing failed */ goto cleanup );
-	
-	/* We expect a CER, it must parse with our dictionary and rules */
-	CHECK_FCT_DO( fd_msg_parse_rules( msg, fd_g_config->cnf_dict, NULL ), /* Parsing failed -- trace details ? */ goto cleanup );
-	
-	fd_msg_dump_walk(FULL, msg);
-	
-	/* Now check we received a CER */
-	CHECK_FCT_DO( fd_msg_hdr ( msg, &hdr ), goto fatal_error );
-	CHECK_PARAMS_DO( (hdr->msg_appl == 0) && (hdr->msg_flags & CMD_FLAG_REQUEST) && (hdr->msg_code == CC_CAPABILITIES_EXCHANGE),
-		{ fd_log_debug("Connection '%s', expecting CER, received something else, closing...\n", fd_cnx_getid(c->conn)); goto cleanup; } );
-	
-	/* Finally, pass the information to the peers module which will handle it next */
-	pthread_cleanup_push((void *)fd_cnx_destroy, c->conn);
-	pthread_cleanup_push((void *)fd_msg_free, msg);
-	CHECK_FCT_DO( fd_peer_handle_newCER( &msg, &c->conn ), goto cleanup );
-	pthread_cleanup_pop(0);
-	pthread_cleanup_pop(0);
-	
-	/* The end, we cleanup the client structure */
-cleanup:
-	/* Unlink the client structure */
-	CHECK_POSIX_DO( pthread_mutex_lock(&s->clients_mtx), goto fatal_error );
-	fd_list_unlink( &c->chain );
-	CHECK_POSIX_DO( pthread_mutex_unlock(&s->clients_mtx), goto fatal_error );
-	
-	/* Destroy the connection object if present */
-	if (c->conn)
-		fd_cnx_destroy(c->conn);
-	
-	/* Cleanup the received buffer if any */
-	free(buf);
-	
-	/* Cleanup the parsed message if any */
-	if (msg) {
-		CHECK_FCT_DO( fd_msg_free(msg), /* continue */);
-	}
-	
-	/* Detach the thread, cleanup the client structure */
-	pthread_detach(pthread_self());
-	free(c);
-	return NULL;
-	
-fatal_error:	/* This has effect to terminate the daemon */
-	CHECK_FCT_DO(fd_event_send(fd_g_config->cnf_main_ev, FDEV_TERMINATE, 0, NULL), );
-	return NULL;
-}
-
-/* The thread managing a server */
-static void * serv_th(void * arg)
-{
-	struct server *s = (struct server *)arg;
-	
-	CHECK_PARAMS_DO(s, goto error);
-	fd_log_threadname ( fd_cnx_getid(s->conn) );
-	s->status = 1;
-	fd_cpu_flush_cache();
-	
-	/* Accept incoming connections */
-	CHECK_FCT_DO( fd_cnx_serv_listen(s->conn), goto error );
-	
-	do {
-		struct client * c = NULL;
-		struct cnxctx * conn = NULL;
-		
-		/* Wait for a new client */
-		CHECK_MALLOC_DO( conn = fd_cnx_serv_accept(s->conn), goto error );
-		
-		TRACE_DEBUG(FULL, "New connection accepted");
-		
-		/* Create a client structure */
-		CHECK_MALLOC_DO( c = malloc(sizeof(struct client)), goto error );
-		memset(c, 0, sizeof(struct client));
-		fd_list_init(&c->chain, c);
-		c->conn = conn;
-		
-		/* Save the client in the list */
-		CHECK_POSIX_DO( pthread_mutex_lock( &s->clients_mtx ), goto error );
-		fd_list_insert_before(&s->clients, &c->chain);
-		CHECK_POSIX_DO( pthread_mutex_unlock( &s->clients_mtx ), goto error );
-
-		/* Start the client thread */
-		CHECK_POSIX_DO( pthread_create( &c->thr, NULL, client_sm, c ), goto error );
-		
-	} while (1);
-	
-error:	
-	if (s)
-		s->status = 2;
-	/* Send error signal to the daemon */
-	TRACE_DEBUG(INFO, "An error occurred in server module! Thread is terminating...");
-	CHECK_FCT_DO(fd_event_send(fd_g_config->cnf_main_ev, FDEV_TERMINATE, 0, NULL), );
-
-	return NULL;
-}
-
-
-/* Create a new server structure */
-static struct server * new_serv( int proto, int secur )
-{
-	struct server * new;
-	
-	/* New server structure */
-	CHECK_MALLOC_DO( new = malloc(sizeof(struct server)), return NULL );
-	
-	memset(new, 0, sizeof(struct server));
-	fd_list_init(&new->chain, new);
-	new->proto = proto;
-	new->secur = secur;
-	CHECK_POSIX_DO( pthread_mutex_init(&new->clients_mtx, NULL), return NULL );
-	fd_list_init(&new->clients, new);
-	
-	return new;
-}
-
-/* Start all the servers */
-int fd_servers_start()
-{
-	struct server * s;
-	
-	int empty_conf_ep = FD_IS_LIST_EMPTY(&fd_g_config->cnf_endpoints);
-	
-	/* SCTP */
-	if (!fd_g_config->cnf_flags.no_sctp) {
-#ifdef DISABLE_SCTP
-		ASSERT(0);
-#else /* DISABLE_SCTP */
-		
-		/* Create the server on unsecure port */
-		CHECK_MALLOC( s = new_serv(IPPROTO_SCTP, 0) );
-		CHECK_MALLOC( s->conn = fd_cnx_serv_sctp(fd_g_config->cnf_port, FD_IS_LIST_EMPTY(&fd_g_config->cnf_endpoints) ? NULL : &fd_g_config->cnf_endpoints) );
-		fd_list_insert_before( &FD_SERVERS, &s->chain );
-		CHECK_POSIX( pthread_create( &s->thr, NULL, serv_th, s ) );
-		
-		/* Create the server on secure port */
-		CHECK_MALLOC( s = new_serv(IPPROTO_SCTP, 1) );
-		CHECK_MALLOC( s->conn = fd_cnx_serv_sctp(fd_g_config->cnf_port_tls, FD_IS_LIST_EMPTY(&fd_g_config->cnf_endpoints) ? NULL : &fd_g_config->cnf_endpoints) );
-		fd_list_insert_before( &FD_SERVERS, &s->chain );
-		CHECK_POSIX( pthread_create( &s->thr, NULL, serv_th, s ) );
-		
-#endif /* DISABLE_SCTP */
-	}
-	
-	/* TCP */
-	if (!fd_g_config->cnf_flags.no_tcp) {
-		
-		if (empty_conf_ep) {
-			/* Bind TCP servers on [0.0.0.0] */
-			if (!fd_g_config->cnf_flags.no_ip4) {
-				
-				CHECK_MALLOC( s = new_serv(IPPROTO_TCP, 0) );
-				CHECK_MALLOC( s->conn = fd_cnx_serv_tcp(fd_g_config->cnf_port, AF_INET, NULL) );
-				fd_list_insert_before( &FD_SERVERS, &s->chain );
-				CHECK_POSIX( pthread_create( &s->thr, NULL, serv_th, s ) );
-
-				CHECK_MALLOC( s = new_serv(IPPROTO_TCP, 1) );
-				CHECK_MALLOC( s->conn = fd_cnx_serv_tcp(fd_g_config->cnf_port_tls, AF_INET, NULL) );
-				fd_list_insert_before( &FD_SERVERS, &s->chain );
-				CHECK_POSIX( pthread_create( &s->thr, NULL, serv_th, s ) );
-			}
-			/* Bind TCP servers on [::] */
-			if (!fd_g_config->cnf_flags.no_ip6) {
-				
-				CHECK_MALLOC( s = new_serv(IPPROTO_TCP, 0) );
-				CHECK_MALLOC( s->conn = fd_cnx_serv_tcp(fd_g_config->cnf_port, AF_INET6, NULL) );
-				fd_list_insert_before( &FD_SERVERS, &s->chain );
-				CHECK_POSIX( pthread_create( &s->thr, NULL, serv_th, s ) );
-
-				CHECK_MALLOC( s = new_serv(IPPROTO_TCP, 1) );
-				CHECK_MALLOC( s->conn = fd_cnx_serv_tcp(fd_g_config->cnf_port_tls, AF_INET6, NULL) );
-				fd_list_insert_before( &FD_SERVERS, &s->chain );
-				CHECK_POSIX( pthread_create( &s->thr, NULL, serv_th, s ) );
-			}
-		} else {
-			/* Create all endpoints -- check flags */
-			struct fd_list * li;
-			for (li = fd_g_config->cnf_endpoints.next; li != &fd_g_config->cnf_endpoints; li = li->next) {
-				struct fd_endpoint * ep = (struct fd_endpoint *)li;
-				sSA * sa = (sSA *) &ep->ss;
-				if (! (ep->flags & EP_FL_CONF))
-					continue;
-				if (fd_g_config->cnf_flags.no_ip4 && (sa->sa_family == AF_INET))
-					continue;
-				if (fd_g_config->cnf_flags.no_ip6 && (sa->sa_family == AF_INET6))
-					continue;
-				
-				CHECK_MALLOC( s = new_serv(IPPROTO_TCP, 0) );
-				CHECK_MALLOC( s->conn = fd_cnx_serv_tcp(fd_g_config->cnf_port, sa->sa_family, ep) );
-				fd_list_insert_before( &FD_SERVERS, &s->chain );
-				CHECK_POSIX( pthread_create( &s->thr, NULL, serv_th, s ) );
-
-				CHECK_MALLOC( s = new_serv(IPPROTO_TCP, 1) );
-				CHECK_MALLOC( s->conn = fd_cnx_serv_tcp(fd_g_config->cnf_port_tls, sa->sa_family, ep) );
-				fd_list_insert_before( &FD_SERVERS, &s->chain );
-				CHECK_POSIX( pthread_create( &s->thr, NULL, serv_th, s ) );
-			}
-		}
-	}
-	
-	/* Now, if we still have not got the list of local adresses, try to read it from the kernel directly */
-	if (FD_IS_LIST_EMPTY(&fd_g_config->cnf_endpoints)) {
-		CHECK_FCT(fd_cnx_get_local_eps(&fd_g_config->cnf_endpoints));
-		if (FD_IS_LIST_EMPTY(&fd_g_config->cnf_endpoints)) {
-			TRACE_DEBUG(INFO, "Unable to find the addresses of the local system. Please use \"ListenOn\" parameter in the configuration.");
-			return EINVAL;
-		}
-	}
-	if (TRACE_BOOL(FULL)){
-		fd_log_debug("  Local server address(es) :\n");
-		fd_ep_dump( 5, &fd_g_config->cnf_endpoints );
-	}
-	return 0;
-}
-
-/* Terminate all the servers */
-int fd_servers_stop()
-{
-	TRACE_ENTRY("");
-	
-	TRACE_DEBUG(INFO, "Shutting down server sockets...");
-	
-	/* Loop on all servers */
-	while (!FD_IS_LIST_EMPTY(&FD_SERVERS)) {
-		struct server * s = (struct server *)(FD_SERVERS.next);
-		
-		/* Lock client list now */
-		CHECK_FCT_DO( pthread_mutex_lock(&s->clients_mtx), /* continue anyway */);
-		
-		/* cancel thread */
-		CHECK_FCT_DO( fd_thr_term(&s->thr), /* continue */);
-		
-		/* destroy server connection context */
-		fd_cnx_destroy(s->conn);
-		
-		/* cancel and destroy all clients */
-		while (!FD_IS_LIST_EMPTY(&s->clients)) {
-			struct client * c = (struct client *)(s->clients.next);
-			
-			/* Destroy client's thread */
-			CHECK_FCT_DO( fd_thr_term(&c->thr), /* continue */);
-			
-			/* Destroy client's connection */
-			fd_cnx_destroy(c->conn);
-			
-			/* Unlink and free the client */
-			fd_list_unlink(&c->chain);
-			free(c);
-		}
-		/* Unlock & destroy */
-		CHECK_FCT_DO( pthread_mutex_unlock(&s->clients_mtx), /* continue anyway */);
-		CHECK_FCT_DO( pthread_mutex_destroy(&s->clients_mtx), /* continue */);
-		
-		/* Now destroy the server object */
-		fd_list_unlink(&s->chain);
-		free(s);
-	}
-	
-	/* We're done! */
-	return 0;
-}
Index: eeDiameter/tcp.c
===================================================================
--- freeDiameter/tcp.c	(revision 378)
+++ 	(revision )
@@ -1,183 +1,0 @@
-/*********************************************************************************************************
-* Software License Agreement (BSD License)                                                               *
-* Author: Sebastien Decugis <sdecugis@nict.go.jp>							 *
-*													 *
-* Copyright (c) 2010, 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 "fD.h"
-#include "cnxctx.h"
-
-#include <netinet/tcp.h>
-#include <netinet/ip6.h>
-#include <sys/socket.h>
-
-/* Set the socket options for TCP sockets, before bind is called */
-static int fd_tcp_setsockopt(int family, int sk)
-{
-	int ret = 0;
-	int opt;
-	
-	/* Clear the NODELAY option in case it was set, as requested by rfc3539#section-3.2 */
-	/* Note that this is supposed to be the default, so we could probably remove this call ... */
-	opt = 0;
-	ret = setsockopt(sk, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof(opt));
-	if (ret != 0) {
-		ret = errno;
-		TRACE_DEBUG(INFO, "Unable to set the socket TCP_NODELAY option: %s", strerror(ret));
-		return ret;
-	}
-	
-	/* Under Linux, we may also set the TCP_CONGESTION option to one of the following strings:
-	    - reno (default)
-	    - bic
-	    - cubic
-	    - highspeed
-	    - htcp
-	    - hybla
-	    - illinois
-	    - lp
-	    - scalable
-	    - vegas
-	    - veno
-	    - westwood
-	    - yeah
-	*/
-	
-	/* In case of v6 address, force the v6only option, we use a different socket for v4 */
-	#ifdef IPV6_V6ONLY
-	if (family == AF_INET6) {
-		opt = 1;
-		CHECK_SYS(setsockopt(sk, IPPROTO_IPV6, IPV6_V6ONLY, &opt, sizeof(opt)));
-	}
-	#endif /* IPV6_V6ONLY */
-	
-#ifdef DEBUG
-	{
-		opt = 1;
-		CHECK_SYS(  setsockopt(sk, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt))  );
-	}
-#endif /* DEBUG	*/
-	
-	
-	/* There are also others sockopt that can be set, but nothing useful for us AFAICT */
-	
-	return 0;
-}
-
-/* Create a socket server and bind it */
-int fd_tcp_create_bind_server( int * sock, sSA * sa, socklen_t salen )
-{
-	TRACE_ENTRY("%p %p %d", sock, sa, salen);
-	
-	CHECK_PARAMS(  sock && sa  );
-	
-	/* Create the socket */
-	CHECK_SYS(  *sock = socket(sa->sa_family, SOCK_STREAM, IPPROTO_TCP)  );
-
-	/* Set the socket options */
-	CHECK_FCT(  fd_tcp_setsockopt(sa->sa_family, *sock)  );
-	
-	/* Bind the socket */
-	CHECK_SYS(  bind( *sock, sa, salen )  );
-			
-	/* We're done */
-	return 0;
-}
-
-/* Allow clients connections on server sockets */
-int fd_tcp_listen( int sock )
-{
-	TRACE_ENTRY("%d", sock);
-	CHECK_SYS( listen(sock, 5) );
-	return 0;
-}
-
-/* Create a client socket and connect to remote server */
-int fd_tcp_client( int *sock, sSA * sa, socklen_t salen )
-{
-	int ret = 0;
-	int s;
-	
-	TRACE_ENTRY("%p %p %d", sock, sa, salen);
-	CHECK_PARAMS( sock && (*sock <= 0) && sa && salen );
-	
-	/* Create the socket */
-	CHECK_SYS(  s = socket(sa->sa_family, SOCK_STREAM, IPPROTO_TCP)  );
-	
-	/* Cleanup if we are cancelled */
-	pthread_cleanup_push(fd_cleanup_socket, &s);
-	
-	/* Set the socket options */
-	CHECK_FCT(  fd_tcp_setsockopt(sa->sa_family, s)  );
-	
-	TRACE_DEBUG_sSA(FULL, "Attempting TCP connection with peer: ", sa, NI_NUMERICHOST | NI_NUMERICSERV, "..." );
-	
-	/* Try connecting to the remote address */
-	ret = connect(s, sa, salen);
-	
-	pthread_cleanup_pop(0);
-	
-	if (ret < 0) {
-		int lvl;
-		switch (ret = errno) {
-			case ECONNREFUSED:
-			
-				/* "Normal" errors */
-				lvl = FULL;
-				break;
-			default:
-				lvl = INFO;
-		}
-		/* Some errors are expected, we log at different level */
-		TRACE_DEBUG( lvl, "connect returned an error: %s", strerror(ret));
-		CHECK_SYS_DO( close(s), /* continue */ );
-		*sock = -1;
-		return ret;
-	}
-	
-	/* Done! */
-	*sock = s;
-	return ret;
-}
-
-/* Get the remote name of a TCP socket */
-int fd_tcp_get_remote_ep(int sock, sSS * ss, socklen_t *sl)
-{
-	TRACE_ENTRY("%d %p %p", sock, ss, sl);
-	CHECK_PARAMS( ss && sl );
-	
-	*sl = sizeof(sSS);
-	CHECK_SYS(getpeername(sock, (sSA *)ss, sl));
-	
-	return 0;
-}
-
Index: eeDiameter/tests/CMakeLists.txt
===================================================================
--- freeDiameter/tests/CMakeLists.txt	(revision 585)
+++ 	(revision )
@@ -1,119 +1,0 @@
-# Test directory
-PROJECT("libfreeDiameter tests" C)
-
-# give the possibility to configure the timeout duration for the tests
-OPTION(TEST_TIMEOUT "Timeout for the tests, in seconds (default: 5)?")
-IF(TEST_TIMEOUT)
-	ADD_DEFINITIONS(-DTEST_TIMEOUT=${TEST_TIMEOUT})
-ENDIF(TEST_TIMEOUT)
-
-
-#############################
-# List the test cases
-SET(TEST_LIST
-	testsctp
-	testlist
-	testdict
-	testmesg
-	testfifo
-	testsess
-	testdisp
-	testcnx
-)
-
-#############################
-# Some parameters for the tests
-
-ADD_DEFINITIONS(-DTEST_DEBUG)
-ADD_DEFINITIONS(-DTRACE_LEVEL=NONE)
-
-INCLUDE_DIRECTORIES( ".." )
-INCLUDE_DIRECTORIES( "../../libfreeDiameter" )
-
-BISON_FILE(../fdd.y)
-FLEX_FILE(../fdd.l)
-
-SET(TEST_COMMON_SRC "")
-
-FOREACH( SRC_FILE ${FD_COMMON_SRC})
-   SET(TEST_COMMON_SRC ${TEST_COMMON_SRC} "../${SRC_FILE}")
-ENDFOREACH(SRC_FILE)
-
-FOREACH( SRC_FILE ${FD_COMMON_GEN_SRC})
-   SET(TEST_COMMON_SRC ${TEST_COMMON_SRC} "${CMAKE_CURRENT_BINARY_DIR}/../${SRC_FILE}")
-ENDFOREACH(SRC_FILE)
-
-FOREACH( SRC_FILE ${LFD_SRC})
-   SET(TEST_COMMON_SRC ${TEST_COMMON_SRC} "../../libfreeDiameter/${SRC_FILE}")
-ENDFOREACH(SRC_FILE)
-
-# Create an archive with the daemon common files (all but main)
-ADD_LIBRARY(fDcore STATIC ${TEST_COMMON_SRC})
-
-
-##############################
-# 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} fDcore ${FD_LIBS} ${LFD_LIBS} ${${TEST}_ADDITIONAL_LIB})
-   ADD_TEST(${TEST} ${EXECUTABLE_OUTPUT_PATH}/${TEST})
-ENDFOREACH( TEST )
-
-
-####
-## INSTALL section ##
-
-# we do not install the tests
Index: eeDiameter/tests/testappacct.c
===================================================================
--- freeDiameter/tests/testappacct.c	(revision 403)
+++ 	(revision )
@@ -1,266 +1,0 @@
-/*********************************************************************************************************
-* Software License Agreement (BSD License)                                                               *
-* Author: Sebastien Decugis <sdecugis@nict.go.jp>							 *
-*													 *
-* Copyright (c) 2010, 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>
-(
-  ts 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;
-}
-
-/* Main test routine */
-int main(int argc, char *argv[])
-{
-	extern PGconn *conn; /* in acct_db.c */
-	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;
-	char * sess_bkp;
-	
-	/* First, initialize the daemon modules */
-	INIT_FD();
-	fd_g_config->cnf_diamid = strdup("test.app.acct");
-	fd_g_config->cnf_diamid_len = strlen(fd_g_config->cnf_diamid);
-	fd_g_config->cnf_diamrlm = strdup("app.acct");
-	fd_g_config->cnf_diamrlm_len = strlen(fd_g_config->cnf_diamrlm);
-	
-	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) );
-	}
-	
-	/* 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;
-			char * s;
-			CHECK( 0, fd_sess_new( &sess, fd_g_config->cnf_diamid, NULL, 0) );
-			CHECK( 0, fd_sess_getsid(sess, &s) );
-			sess_bkp = strdup(s);
-
-			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 = (unsigned char *)sess_bkp;
-			avp_val.os.len = strlen(sess_bkp);
-			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", 1, 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;
-		char * s;
-		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) */
-		s = PQgetvalue(res, 0, 0);
-		CHECK( 0, strcmp(s, sess_bkp) );
-		
-		PQclear(res);
-	}  
-
-	/* That's all for the tests yet */
-	free(sess_bkp);
-	fd_ext_fini();
-	
-	PASSTEST();
-} 
-	
Index: eeDiameter/tests/testcnx.c
===================================================================
--- freeDiameter/tests/testcnx.c	(revision 421)
+++ 	(revision )
@@ -1,1607 +1,0 @@
-/*********************************************************************************************************
-* Software License Agreement (BSD License)                                                               *
-* Author: Sebastien Decugis <sdecugis@nict.go.jp>							 *
-*													 *
-* Copyright (c) 2010, 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";
-
-
-struct fd_list eps = FD_LIST_INITIALIZER(eps);
-
-struct connect_flags {
-	int	proto;
-	int	expect_failure; /* 0 or 1 */
-};
-
-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;
-}
-
-struct handshake_flags {
-	struct cnxctx * cnx;
-	gnutls_certificate_credentials_t	creds;
-	int ret;
-};
-
-void * handshake_thr(void * arg)
-{
-	struct handshake_flags * hf = arg;
-	fd_log_threadname ( "testcnx:handshake" );
-	hf->ret = fd_cnx_handshake(hf->cnx, GNUTLS_CLIENT, NULL, hf->creds);
-	return NULL;
-}
-	
-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) };
-	
-	struct cnxctx * listener;
-#ifndef DISABLE_SCTP
-	struct cnxctx * listener_sctp;
-#endif /* DISABLE_SCTP */
-	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 );
-	
-	/* 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_generate2( fd_g_config->cnf_sec_data.dh_cache, GNUTLS_DEFAULT_DHBITS), );
-	CHECK( GNUTLS_E_SUCCESS, ret );
-	
-	/* 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);
-	}
-	
-	/* 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 1
-		/* For debug: dump the object */
-		fd_log_debug("Dumping CER\n");
-		fd_msg_dump_walk(0, cer);
-		#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, 0, 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, 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, 0));
-		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);
-	}
-		
-#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, 0, 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, 0));
-		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, 0));
-		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, 0));
-		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, 0, 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, 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);
-		
-		/* And the supposed reply */
-		CHECK( 0, fd_cnx_send(client_side, cer_buf, cer_sz, 0));
-		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, 0, handshake_thr, &hf) );
-		CHECK( 0, fd_cnx_handshake(server_side, GNUTLS_SERVER, 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, 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);
-
-			CHECK( 0, fd_cnx_send(client_side, cer_buf, cer_sz, 0));
-			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, 0, 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));
-		
-		/* 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, 0, 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, 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);
-		
-		/* And the supposed reply */
-		CHECK( 0, fd_cnx_send(client_side, cer_buf, cer_sz, 0));
-		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, 0, handshake_thr, &hf) );
-		CHECK( 0, fd_cnx_handshake(server_side, GNUTLS_SERVER, 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, 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);
-
-			CHECK( 0, fd_cnx_send(client_side, cer_buf, cer_sz, 0));
-			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, 0, 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, 0, 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, 0, handshake_thr, &hf) );
-		CHECK( 0, fd_cnx_handshake(server_side, GNUTLS_SERVER, 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, 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);
-
-			CHECK( 0, fd_cnx_send(client_side, cer_buf, cer_sz, 0));
-			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, 0, 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 (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));
-		
-		/* 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, 0, 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, 0, handshake_thr, &hf) );
-		CHECK( 0, fd_cnx_handshake(server_side, GNUTLS_SERVER, 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, 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);
-
-			CHECK( 0, fd_cnx_send(client_side, cer_buf, cer_sz, 0));
-			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, 0, 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
-	{
-		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, 0, 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, 0, handshake_thr, &hf) );
-		CHECK( 0, fd_cnx_handshake(server_side, GNUTLS_SERVER, 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, 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);
-
-			CHECK( 0, fd_cnx_send(client_side, cer_buf, cer_sz, 0));
-			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, 0, 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, 0, 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, 0, handshake_thr, &hf) );
-		CHECK( 0, fd_cnx_handshake(server_side, GNUTLS_SERVER, 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, 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);
-
-			CHECK( 0, fd_cnx_send(client_side, cer_buf, cer_sz, 0));
-			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, 0, 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, 0, 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, 0, handshake_thr, &hf) );
-		CHECK( EINVAL, fd_cnx_handshake(server_side, GNUTLS_SERVER, NULL, NULL) );
-		CHECK( 0, pthread_join(thr, NULL) );
-		
-		/* Now close the connection */
-		CHECK( 0, pthread_create(&thr, 0, 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);
-	}
-	
-	/* Same in SCTP */
-#ifndef DISABLE_SCTP
-	{
-		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, 0, 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, 0, handshake_thr, &hf) );
-		CHECK( EINVAL, fd_cnx_handshake(server_side, GNUTLS_SERVER, NULL, NULL) );
-		CHECK( 0, pthread_join(thr, NULL) );
-		
-		/* Now close the connection */
-		CHECK( 0, pthread_create(&thr, 0, 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 */
-	
-	/* 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, 0, 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, 0, handshake_thr, &hf) );
-		CHECK( EINVAL, fd_cnx_handshake(server_side, GNUTLS_SERVER, NULL, NULL) );
-		CHECK( 0, pthread_join(thr, NULL) );
-		
-		/* Now close the connection */
-		CHECK( 0, pthread_create(&thr, 0, 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);
-	}
-	
-	/* 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, 0, 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, 0, handshake_thr, &hf) );
-		CHECK( 0, fd_cnx_handshake(server_side, GNUTLS_SERVER, NULL, NULL) );
-		CHECK( 0, pthread_join(thr, NULL) );
-		CHECK( 0, hf.ret );
-		
-		/* Now close the connection */
-		CHECK( 0, pthread_create(&thr, 0, 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, 0, 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, 0, handshake_thr, &hf) );
-		CHECK( EINVAL, fd_cnx_handshake(server_side, GNUTLS_SERVER, NULL, NULL) );
-		CHECK( 0, pthread_join(thr, NULL) );
-		
-		/* Now close the connection */
-		CHECK( 0, pthread_create(&thr, 0, 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);
-	}
-	
-	/* 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, 0, 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, 0, handshake_thr, &hf) );
-		CHECK( 0, fd_cnx_handshake(server_side, GNUTLS_SERVER, 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, 0));
-		CHECK( 0, fd_fifo_new(&myfifo) );
-		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);
-		fd_event_destroy(&myfifo, free);
-		
-		/* Now close the connection */
-		CHECK( 0, pthread_create(&thr, 0, 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
-	/* And re-test with a SCTP connection */
-	{
-		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, 0, 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, 0, handshake_thr, &hf) );
-		CHECK( 0, fd_cnx_handshake(server_side, GNUTLS_SERVER, 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, 0));
-		CHECK( 0, fd_fifo_new(&myfifo) );
-		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, 0, 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, 0, 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, 0, 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();
-} 
-	
Index: eeDiameter/tests/testdict.c
===================================================================
--- freeDiameter/tests/testdict.c	(revision 648)
+++ 	(revision )
@@ -1,135 +1,0 @@
-/*********************************************************************************************************
-* Software License Agreement (BSD License)                                                               *
-* Author: Sebastien Decugis <sdecugis@nict.go.jp>							 *
-*													 *
-* Copyright (c) 2010, 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 );
-	}
-
-	/* That's all for the tests yet */
-	PASSTEST();
-} 
-	
Index: eeDiameter/tests/testdisp.c
===================================================================
--- freeDiameter/tests/testdisp.c	(revision 648)
+++ 	(revision )
@@ -1,725 +1,0 @@
-/*********************************************************************************************************
-* Software License Agreement (BSD License)                                                               *
-* Author: Sebastien Decugis <sdecugis@nict.go.jp>							 *
-*													 *
-* Copyright (c) 2010, 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;
-	enum disp_action action;
-	struct disp_hdl * hdl[NB_CB];
-	struct disp_when when;
-	const char * ec;
-	
-	/* First, initialize the daemon modules */
-	INIT_FD();
-	
-	/* Create a dummy session, we don't use it anyway */
-	CHECK( 0, fd_sess_new( &sess, "test.disp", 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 ) );
-		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 ) );
-		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 ) );
-		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 ) );
-		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 ) );
-		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 ) );
-		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 ) );
-		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 ) );
-		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 ) );
-		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 ) );
-		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 ) );
-		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 ) );
-		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 ) );
-		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 ) );
-		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 ) );
-		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 ) );
-		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 ) );
-		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 ) );
-		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 ) );
-		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 ) );
-		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 ) );
-		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( 12345, fd_msg_dispatch ( &msg, sess, &action, &ec ) );
-		CHECK( 1, cbcalled[0] );
-		CHECK( 1, cbcalled[1] );
-		CHECK( 1, cbcalled[6] );
-		CHECK( 0, cbcalled[2] );
-		CHECK( 0, cbcalled[3] );
-		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_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 ) );
-		CHECK( 1, cbcalled[0] );
-		CHECK( 1, cbcalled[1] );
-		CHECK( 1, cbcalled[8] );
-		CHECK( 0, cbcalled[2] );
-		CHECK( 0, cbcalled[3] );
-		CHECK( NULL, 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_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 ) );
-		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 ) );
-		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 ) );
-		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 ) );
-		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 ) );
-		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 ) );
-		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 ) );
-		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_conf_dump();
-		#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 ) );
-		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();
-} 
-	
Index: eeDiameter/tests/testfifo.c
===================================================================
--- freeDiameter/tests/testfifo.c	(revision 648)
+++ 	(revision )
@@ -1,438 +1,0 @@
-/*********************************************************************************************************
-* Software License Agreement (BSD License)                                                               *
-* Author: Sebastien Decugis <sdecugis@nict.go.jp>							 *
-*													 *
-* Copyright (c) 2010, 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>
-
-/* 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;
-}
-
-
-/* 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;
-		int count;
-		struct msg * msg  = NULL;
-		
-		/* Create the queue */
-		CHECK( 0, fd_fifo_new(&queue) );
-		
-		/* Check the count is 0 */
-		CHECK( 0, fd_fifo_length(queue, &count) );
-		CHECK( 0, count);
-		
-		/* 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( 0, fd_fifo_length(queue, &count) );
-		CHECK( 3, count);
-		
-		/* Retrieve the first message using fd_fifo_get */
-		CHECK( 0, fd_fifo_get(queue, &msg) );
-		CHECK( msg1, msg);
-		CHECK( 0, fd_fifo_length(queue, &count) );
-		CHECK( 2, count);
-		
-		/* 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( 0, fd_fifo_length(queue, &count) );
-		CHECK( 1, count);
-		
-		/* Retrieve the third message using meq_tryget */
-		CHECK( 0, fd_fifo_tryget(queue, &msg) );
-		CHECK( msg3, msg);
-		CHECK( 0, fd_fifo_length(queue, &count) );
-		CHECK( 0, count);
-		
-		/* Check that another meq_tryget does not block */
-		CHECK( EWOULDBLOCK, fd_fifo_tryget(queue, &msg) );
-		CHECK( 0, fd_fifo_length(queue, &count) );
-		CHECK( 0, 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 			 count;
-		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 {
-			/* The local limit is bellow 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) );
-		
-		/* 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, &count) );
-		CHECK( 0, count);
-		
-		/* 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) );
-		
-		/* 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) );
-		
-		/* 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) );
-	}
-	
-	/* 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();
-} 
Index: eeDiameter/tests/testlist.c
===================================================================
--- freeDiameter/tests/testlist.c	(revision 3)
+++ 	(revision )
@@ -1,74 +1,0 @@
-/*********************************************************************************************************
-* Software License Agreement (BSD License)                                                               *
-* Author: Sebastien Decugis <sdecugis@nict.go.jp>							 *
-*													 *
-* Copyright (c) 2009, WIDE Project and NICT								 *
-* All rights reserved.											 *
-* 													 *
-* Redistribution and use of this software in source and binary forms, with or without modification, are  *
-* permitted provided that the following conditions are met:						 *
-* 													 *
-* * Redistributions of source code must retain the above 						 *
-*   copyright notice, this list of conditions and the 							 *
-*   following disclaimer.										 *
-*    													 *
-* * Redistributions in binary form must reproduce the above 						 *
-*   copyright notice, this list of conditions and the 							 *
-*   following disclaimer in the documentation and/or other						 *
-*   materials provided with the distribution.								 *
-* 													 *
-* * Neither the name of the WIDE Project or NICT nor the 						 *
-*   names of its contributors may be used to endorse or 						 *
-*   promote products derived from this software without 						 *
-*   specific prior written permission of WIDE Project and 						 *
-*   NICT.												 *
-* 													 *
-* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED *
-* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
-* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR *
-* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 	 *
-* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 	 *
-* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR *
-* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF   *
-* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.								 *
-*********************************************************************************************************/
-
-#include "tests.h"
-
-#define TEST_STR "This is my test string (with extra unused data)"
-#define TEST_STRLEN 22
-
-/* Main test routine */
-int main(int argc, char *argv[])
-{
-	/* First, initialize the daemon modules */
-	INIT_FD();
-	
-	/* Check the hash function */
-	{
-		char buf[30];
-		
-		uint32_t hash = fd_hash(TEST_STR, TEST_STRLEN); /* reference value */
-		
-		/* Check that a hash of a substring / surstring is different */
-		CHECK( 1, hash != fd_hash(TEST_STR, TEST_STRLEN - 1) ? 1 : 0 );
-		CHECK( 1, hash != fd_hash(TEST_STR, TEST_STRLEN + 1) ? 1 : 0 );
-		
-		/* Check alignment of the string is not important */
-		memcpy(buf + 4, TEST_STR, TEST_STRLEN);
-		CHECK( hash, fd_hash(buf + 4, TEST_STRLEN) );
-		
-		memcpy(buf + 3, TEST_STR, TEST_STRLEN);
-		CHECK( hash, fd_hash(buf + 3, TEST_STRLEN) );
-		
-		memcpy(buf + 2, TEST_STR, TEST_STRLEN);
-		CHECK( hash, fd_hash(buf + 2, TEST_STRLEN) );
-		
-		memcpy(buf + 1, TEST_STR, TEST_STRLEN);
-		CHECK( hash, fd_hash(buf + 1, TEST_STRLEN) );
-	}
-
-	/* That's all for the tests yet */
-	PASSTEST();
-} 
-	
Index: eeDiameter/tests/testmesg.c
===================================================================
--- freeDiameter/tests/testmesg.c	(revision 648)
+++ 	(revision )
@@ -1,1247 +1,0 @@
-/*********************************************************************************************************
-* Software License Agreement (BSD License)                                                               *
-* Author: Sebastien Decugis <sdecugis@nict.go.jp>							 *
-*													 *
-* Copyright (c) 2010, 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\n");
-		fd_msg_dump_walk( 0, acr );
-		#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\n");
-		fd_msg_dump_walk(0, pi);
-		fd_log_debug("Dumping dictionary model\n");
-		fd_dict_dump_object(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\n");
-		fd_msg_dump_walk(0, acr);
-		#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_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  * 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_dict_dump_object ( gavp );
-			#endif
-		}
-		#if 0
-		{
-			fd_dict_dump_object ( 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:\n");
-			fd_msg_dump_one(0, avpi);
-			#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:\n");
-			fd_msg_dump_one(0, avpi);
-			#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):\n");
-				fd_msg_dump_one(0, avpi);
-				#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):\n");
-				fd_msg_dump_one(0, avpi);
-				#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):\n");
-			fd_msg_dump_one(0, avpi);
-			#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...'\n");
-				fd_msg_dump_one(0, avpi);
-				#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):\n");
-				fd_msg_dump_one(0, avpi);
-				#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):\n");
-				fd_msg_dump_one(0, avpi);
-				#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'\n");
-				fd_msg_dump_one(0, avpch);
-				#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'\n");
-				fd_msg_dump_one(0, avpch);
-				#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_msg_dump_walk(0, msg);
-			#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_msg_dump_walk(0, msg);
-			#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_msg_dump_walk(0, msg);
-				#endif
-				
-				/* reset */
-				CHECK( 0, fd_msg_free ( msg ) );
-			}
-			
-			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_msg_dump_walk(0, msg);
-			#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 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_msg_dump_walk(0, msg);
-			#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_msg_dump_walk(0, msg);
-			#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();
-} 
-	
Index: eeDiameter/tests/tests.h
===================================================================
--- freeDiameter/tests/tests.h	(revision 481)
+++ 	(revision )
@@ -1,170 +1,0 @@
-/*********************************************************************************************************
-* Software License Agreement (BSD License)                                                               *
-* Author: Sebastien Decugis <sdecugis@nict.go.jp>							 *
-*													 *
-* Copyright (c) 2010, 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 "libfD.h"
-#include "fD.h"
-
-#include <pthread.h>
-#include <errno.h>
-#include <gcrypt.h>
-
-/* Test timeout duration, unless -n is passed on the command line */
-#ifndef TEST_TIMEOUT
-#define TEST_TIMEOUT	30	/* 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... ){				\
-	fprintf(stderr, ## message);			\
-	TRACE_DEBUG(INFO, "Test failed");		\
-	exit(FAIL);					\
-}
-
-/* Define the macro to pass a test */
-#define PASSTEST( ){					\
-	fprintf(stderr, "Test %s passed\n", __FILE__);	\
-	TRACE_DEBUG(INFO, "Test passed");		\
-	exit(PASS);					\
-}
-
-static int test_verbo = 0;
-static struct fd_config conf;
-struct fd_config * fd_g_config = &conf;
-
-/* gcrypt functions to support posix threads */
-GCRY_THREAD_OPTION_PTHREAD_IMPL;
-
-/* Define the standard check routines */
-#define CHECK( _val, _assert ){				\
-	if (test_verbo > 0) {				\
-		fprintf(stderr,				\
-			"%s:%-4d: CHECK( " #_assert " == "\
-				#_val " )\n",		\
-			__FILE__, 			\
-			__LINE__);			\
-	}{						\
-	__typeof__ (_val) __ret = (_assert);		\
-	if (__ret != (_val)) {				\
-		FAILTEST( "%s:%d: CHECK FAILED : %s == %lx != %lx\n",	\
-			__FILE__,			\
-			__LINE__,			\
-			#_assert,			\
-			(unsigned long)__ret,		\
-			(unsigned long)(_val));		\
-	}}						\
-}
-
-/* Minimum inits */
-#define INIT_FD() {								\
-	memset(fd_g_config, 0, sizeof(struct fd_config));			\
-	CHECK( 0, fd_lib_init(1) );						\
-	fd_log_threadname(basename(__FILE__));					\
-	(void) gcry_control (GCRYCTL_SET_THREAD_CBS, &gcry_threads_pthread);	\
-	(void) gcry_control (GCRYCTL_ENABLE_QUICK_RANDOM, 0);			\
-	CHECK( 0, gnutls_global_init());					\
-	CHECK( 0, fd_conf_init() );						\
-	CHECK( 0, fd_dict_base_protocol(fd_g_config->cnf_dict) );		\
-	CHECK( 0, fd_sess_start() );						\
-	parse_cmdline(argc, argv);						\
-}
-
-static void test_timeout(int signal)
-{
-	FAILTEST("The timeout (" _stringize(TEST_TIMEOUT) " sec) was reached. Use -n or change TEST_TIMEOUT if the test needs more time to execute.");
-}
-
-static inline void parse_cmdline(int argc, char * argv[]) {
-	int c;
-	int no_timeout = 0;
-	while ((c = getopt (argc, argv, "dqnf:F:")) != -1) {
-		switch (c) {
-			case 'd':	/* Increase verbosity of debug messages.  */
-				test_verbo++;
-				break;
-				
-			case 'q':	/* Decrease verbosity.  */
-				test_verbo--;
-				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;
-				
-			default:	/* bug: option not considered.  */
-				return;
-		}
-	}
-	fd_g_debug_lvl = (test_verbo > 0) ? (test_verbo - 1) : 0;
-	if (!no_timeout) {
-		alarm(TEST_TIMEOUT);
-		fd_sig_register(SIGALRM, "Test.harness", test_timeout);
-	}
-}
- 
-#endif /* _TESTS_H */
Index: eeDiameter/tests/testsctp.c
===================================================================
--- freeDiameter/tests/testsctp.c	(revision 485)
+++ 	(revision )
@@ -1,136 +1,0 @@
-/*********************************************************************************************************
-* Software License Agreement (BSD License)                                                               *
-* Author: Sebastien Decugis <sdecugis@nict.go.jp>							 *
-*													 *
-* Copyright (c) 2010, 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 */
-	int sock, srvsock, clisock;
-	char buf1[]="abcdef";
-	char *buf2;
-	size_t sz;
-	struct fd_list eps = FD_LIST_INITIALIZER(eps);
-	uint32_t status = 0;
-	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);
-	}
-	
-	/* 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( &clisock, 0, TEST_PORT, &eps ));
-	
-	/* Accept this connection */
-	srvsock = accept(sock, NULL, NULL);
-	
-	/* Send a first message */
-	CHECK( 0, fd_sctp_sendstr(srvsock, 1, (uint8_t *)buf1, sizeof(buf1), &status) );
-	CHECK( 0, status);
-	
-	/* Receive this message */
-redo1:
-	CHECK( 0, fd_sctp_recvmeta(clisock, &str, (uint8_t **)&buf2, &sz, &ev, &status) );
-	if (ev == FDEVP_CNX_EP_CHANGE)
-		goto redo1;
-	CHECK( FDEVP_CNX_MSG_RECV, ev);
-	CHECK( 0, status);
-	CHECK( 1, str);
-	CHECK( sizeof(buf1), sz );
-	CHECK( 0, memcmp(buf1, buf2, sz) );
-	free(buf2); buf2 = NULL;
-	
-	/* Send in the other direction */
-	CHECK( 0, fd_sctp_sendstr(clisock, 2, (uint8_t *)buf1, sizeof(buf1), &status) );
-	CHECK( 0, status);
-	
-	/* Receive this message */
-redo2:
-	CHECK( 0, fd_sctp_recvmeta(srvsock, &str, (uint8_t **)&buf2, &sz, &ev, &status) );
-	if (ev == FDEVP_CNX_EP_CHANGE)
-		goto redo2;
-	CHECK( FDEVP_CNX_MSG_RECV, ev);
-	CHECK( 0, status);
-	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 */
-} 
-	
Index: eeDiameter/tests/testsess.c
===================================================================
--- freeDiameter/tests/testsess.c	(revision 639)
+++ 	(revision )
@@ -1,369 +1,0 @@
-/*********************************************************************************************************
-* Software License Agreement (BSD License)                                                               *
-* Author: Sebastien Decugis <sdecugis@nict.go.jp>							 *
-*													 *
-* Copyright (c) 2010, WIDE Project and NICT								 *
-* All rights reserved.											 *
-* 													 *
-* Redistribution and use of this software in source and binary forms, with or without modification, are  *
-* permitted provided that the following conditions are met:						 *
-* 													 *
-* * Redistributions of source code must retain the above 						 *
-*   copyright notice, this list of conditions and the 							 *
-*   following disclaimer.										 *
-*    													 *
-* * Redistributions in binary form must reproduce the above 						 *
-*   copyright notice, this list of conditions and the 							 *
-*   following disclaimer in the documentation and/or other						 *
-*   materials provided with the distribution.								 *
-* 													 *
-* * Neither the name of the WIDE Project or NICT nor the 						 *
-*   names of its contributors may be used to endorse or 						 *
-*   promote products derived from this software without 						 *
-*   specific prior written permission of WIDE Project and 						 *
-*   NICT.												 *
-* 													 *
-* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED *
-* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
-* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR *
-* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 	 *
-* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 	 *
-* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR *
-* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF   *
-* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.								 *
-*********************************************************************************************************/
-
-#include "tests.h"
-
-#define TEST_DIAM_ID 	"testsess.myid"
-#define TEST_OPT	"suffix"
-#define TEST_SID	TEST_DIAM_ID ";1234;5678;" TEST_OPT
-
-#define TEST_EYEC	0x7e57e1ec
-struct mystate {
-	int	eyec;	/* TEST_EYEC */
-	char *  sid; 	/* the session with which the data was registered */
-	int  *  freed;	/* location where to write the freed status */
-	void *  opaque; /* if opaque was provided, this is the value we expect */
-};
-
-static void mycleanup( struct mystate * data, char * sid, void * opaque )
-{
-	/* sanity */
-	CHECK( 1, sid ? 1 : 0 );
-	CHECK( 1, data? 1 : 0 );
-	CHECK( TEST_EYEC, data->eyec );
-	CHECK( 0, strcmp(sid, data->sid) );
-	if (data->freed)
-		*(data->freed) += 1;
-	if (data->opaque) {
-		CHECK( 1, opaque == data->opaque ? 1 : 0 );  
-	}
-	/* Now, free the data */
-	free(data->sid);
-	free(data);
-}
-
-static __inline__ struct mystate * new_state(char * sid, int *freed) 
-{
-	struct mystate *new;
-	new = malloc(sizeof(struct mystate));
-	CHECK( 1, new ? 1 : 0 );
-	memset(new, 0, sizeof(struct mystate));
-	new->eyec = TEST_EYEC;
-	new->sid = strdup(sid);
-	CHECK( 1, new->sid ? 1 : 0 );
-	new->freed = freed;
-	return new;
-}
-
-void * g_opaque = (void *)"test";
-	
-
-/* Main test routine */
-int main(int argc, char *argv[])
-{
-	struct session_handler * hdl1, *hdl2;
-	struct session *sess1, *sess2, *sess3;
-	char *str1, *str2;
-	int new;
-	
-	/* First, initialize the daemon modules */
-	INIT_FD();
-	
-	/* Test functions related to handlers (simple situation) */
-	{
-		void * testptr = NULL;
-		CHECK( 0, fd_sess_handler_create ( &hdl1, mycleanup, NULL ) );
-		CHECK( 0, fd_sess_handler_create ( &hdl2, mycleanup, NULL ) );
-		CHECK( 0, fd_sess_handler_destroy( &hdl2, &testptr ) );
-		CHECK( 1, testptr == NULL ? 1 : 0 );
-		CHECK( 0, fd_sess_handler_create ( &hdl2, mycleanup, g_opaque ) );
-		#if 0
-		fd_sess_dump_hdl(0, hdl1);
-		fd_sess_dump_hdl(0, hdl2);
-		#endif
-	}
-	
-	/* Test Session Id generation (fd_sess_new) */
-	{
-		/* DiamId is provided, not opt */
-		CHECK( 0, fd_sess_new( &sess1, TEST_DIAM_ID, NULL, 0 ) );
-		CHECK( 0, fd_sess_new( &sess2, TEST_DIAM_ID, NULL, 0 ) );
-		#if 0
-		fd_sess_dump(0, sess1);
-		fd_sess_dump(0, sess2);
-		#endif
-		
-		/* Check both string start with the diameter Id, but are different */
-		CHECK( 0, fd_sess_getsid(sess1, &str1) );
-		CHECK( 0, strncmp(str1, TEST_DIAM_ID ";", strlen(TEST_DIAM_ID) + 1) );
-		CHECK( 0, fd_sess_getsid(sess2, &str2) );
-		CHECK( 0, strncmp(str2, TEST_DIAM_ID ";", strlen(TEST_DIAM_ID) + 1) );
-		CHECK( 1, strcmp(str1, str2) ? 1 : 0 );
-		CHECK( 0, fd_sess_destroy( &sess1 ) );
-		CHECK( 0, fd_sess_destroy( &sess2 ) );
-		
-		/* diamId and opt */
-		CHECK( 0, fd_sess_new( &sess1, TEST_DIAM_ID, TEST_OPT, 0 ) );
-		CHECK( 0, fd_sess_new( &sess2, TEST_DIAM_ID, TEST_OPT, strlen(TEST_OPT) - 1 ) );
-		#if 0
-		fd_sess_dump(0, sess1);
-		fd_sess_dump(0, sess2);
-		#endif
-		
-		CHECK( 0, fd_sess_getsid(sess1, &str1) );
-		CHECK( 0, strncmp(str1, TEST_DIAM_ID ";", strlen(TEST_DIAM_ID) + 1) );
-		CHECK( 0, strcmp(str1 + strlen(str1) - strlen(TEST_OPT) - 1, ";" TEST_OPT) );
-		
-		CHECK( 0, fd_sess_getsid(sess2, &str2) );
-		CHECK( 0, strncmp(str2, TEST_DIAM_ID ";", strlen(TEST_DIAM_ID) + 1) );
-		CHECK( 0, strncmp(str2 + strlen(str2) - strlen(TEST_OPT), ";" TEST_OPT, strlen(TEST_OPT)) );
-		
-		CHECK( 1, strcmp(str1, str2) ? 1 : 0 );
-		CHECK( 0, fd_sess_destroy( &sess1 ) );
-		CHECK( 0, fd_sess_destroy( &sess2 ) );
-		
-		/* Now, only opt is provided */
-		CHECK( 0, fd_sess_new( &sess1, NULL, TEST_SID, 0 ) );
-		CHECK( EALREADY, fd_sess_new( &sess2, NULL, TEST_SID, 0 ) );
-		CHECK( sess2, sess1 );
-		CHECK( EALREADY, fd_sess_new( &sess3, NULL, TEST_SID, strlen(TEST_SID) ) );
-		CHECK( sess3, sess1 );
-		CHECK( 0, fd_sess_new( &sess2, NULL, TEST_SID, strlen(TEST_SID) - 1 ) );
-		#if 0
-		fd_sess_dump(0, sess1);
-		fd_sess_dump(0, sess2);
-		#endif
-		CHECK( 0, fd_sess_getsid(sess1, &str1) );
-		CHECK( 0, fd_sess_getsid(sess2, &str2) );
-		CHECK( 0, strncmp( str1, str2, strlen(TEST_SID) - 1 ) );
-		CHECK( 0, strcmp( str1, TEST_SID ) );
-		
-		CHECK( 0, fd_sess_destroy( &sess2 ) );
-		CHECK( 0, fd_sess_destroy( &sess1 ) );
-	}
-		
-	/* Test fd_sess_fromsid */
-	{
-		CHECK( 0, fd_sess_fromsid( TEST_SID, strlen(TEST_SID), &sess1, &new ) );
-		CHECK( 1, new ? 1 : 0 );
-		
-		CHECK( 0, fd_sess_fromsid( TEST_SID, strlen(TEST_SID), &sess2, &new ) );
-		CHECK( 0, new );
-		CHECK( sess1, sess2 );
-		
-		CHECK( 0, fd_sess_fromsid( TEST_SID, strlen(TEST_SID), &sess3, NULL ) );
-		CHECK( sess1, sess3 );
-		
-		CHECK( 0, fd_sess_destroy( &sess1 ) );
-	}
-	
-	/* Test fd_sess_reclaim */
-	{
-		struct mystate *tms;
-		
-		CHECK( 0, fd_sess_fromsid( TEST_SID, strlen(TEST_SID), &sess1, &new ) );
-		CHECK( 1, new ? 1 : 0 );
-		
-		CHECK( 0, fd_sess_reclaim( &sess1 ) );
-		CHECK( NULL, sess1 );
-		
-		CHECK( 0, fd_sess_fromsid( TEST_SID, strlen(TEST_SID), &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, strlen(TEST_SID), &sess1, &new ) );
-		CHECK( 0, new );
-		
-		CHECK( 0, fd_sess_destroy( &sess1 ) );
-		
-		CHECK( 0, fd_sess_fromsid( TEST_SID, strlen(TEST_SID), &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, strlen(TEST_SID), &sess1, &new ) );
-		CHECK( 1, new ? 1 : 0 );
-		
-		CHECK( 0, clock_gettime(CLOCK_REALTIME, &timeout) );
-		CHECK( 0, fd_sess_settimeout( sess1, &timeout) );
-		timeout.tv_sec = 0;
-		timeout.tv_nsec= 50000000; /* 50 ms */
-		CHECK( 0, nanosleep(&timeout, NULL) );
-		
-		CHECK( 0, fd_sess_fromsid( TEST_SID, strlen(TEST_SID), &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, NULL, 0 ) );
-		
-		/* We don't really have away 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 mystate * ms[6], *tms;
-		int freed[6];
-		struct timespec timeout;
-		void * testptr = NULL;
-		
-		/* Create three sessions */
-		CHECK( 0, fd_sess_new( &sess1, TEST_DIAM_ID, NULL, 0 ) );
-		CHECK( 0, fd_sess_new( &sess2, TEST_DIAM_ID, NULL, 0 ) );
-		CHECK( 0, fd_sess_new( &sess3, TEST_DIAM_ID, NULL, 0 ) );
-		
-		/* Create 2 states */
-		CHECK( 0, fd_sess_getsid(sess1, &str1) );
-		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_sess_dump(0, sess1);
-		#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) );
-		ms[0] = new_state(str1, &freed[0]);
-		ms[1] = new_state(str1, &freed[1]);
-		CHECK( 0, fd_sess_getsid(sess2, &str1) );
-		ms[2] = new_state(str1, &freed[2]);
-		ms[3] = new_state(str1, &freed[3]);
-		CHECK( 0, fd_sess_getsid(sess3, &str1) );
-		ms[4] = new_state(str1, &freed[4]);
-		ms[5] = new_state(str1, &freed[5]);
-		ms[5]->opaque = g_opaque;
-		str2 = strdup(str1);
-		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_sess_dump(0, sess1);
-		fd_sess_dump(0, sess2);
-		fd_sess_dump(0, sess3);
-		#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_sess_dump(0, sess1);
-		fd_sess_dump(0, sess2);
-		#endif
-		
-		/* Create again session 3, check that no data is associated to it */
-		CHECK( 0, fd_sess_fromsid( str2, strlen(str2), &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_sess_dump(0, sess1);
-		fd_sess_dump(0, sess2);
-		#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( 0, fd_sess_getsid(sess1, &str1) );
-		mycleanup(tms, str1, NULL);
-	}
-	
-	
-	/* That's all for the tests yet */
-	PASSTEST();
-} 
-	
Index: freeDiameterd/CMakeLists.txt
===================================================================
--- freeDiameterd/CMakeLists.txt	(revision 658)
+++ freeDiameterd/CMakeLists.txt	(revision 658)
@@ -0,0 +1,19 @@
+# The subproject name
+Project("freeDiameter simple daemon" C)
+
+# Build the executable
+ADD_EXECUTABLE(freeDiameterd main.c)
+
+# The version
+SET_TARGET_PROPERTIES(freeDiameterd PROPERTIES 
+	VERSION ${FD_PROJECT_VERSION_MAJOR}.${FD_PROJECT_VERSION_MINOR}.${FD_PROJECT_VERSION_REV})
+
+# The link command
+TARGET_LINK_LIBRARIES(freeDiameterd libfdproto libfdcore)
+
+####
+## INSTALL section ##
+
+INSTALL(TARGETS freeDiameterd
+	RUNTIME DESTINATION ${INSTALL_DAEMON_SUFFIX}
+	COMPONENT freeDiameter-daemon)
Index: freeDiameterd/main.c
===================================================================
--- freeDiameterd/main.c	(revision 658)
+++ freeDiameterd/main.c	(revision 658)
@@ -0,0 +1,299 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@nict.go.jp>							 *
+*													 *
+* Copyright (c) 2010, 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 <freeDiameter/freeDiameter-host.h>
+#include <freeDiameter/libfdcore.h>
+
+#include <signal.h>
+#include <getopt.h>
+#include <locale.h>
+
+
+/* forward declarations */
+static int main_cmdline(int argc, char *argv[]);
+static void * catch_signals(void * arg);
+static pthread_t signals_thr;
+
+static char *conffile = NULL;
+static int gnutls_debug = 0;
+
+/* gnutls debug */
+static void fd_gnutls_debug(int level, const char * str) {
+	fd_log_debug(" [gnutls:%d] %s", level, str);
+}
+
+
+/* freeDiameter starting point */
+int main(int argc, char * argv[])
+{
+	int ret;
+	sigset_t sig_all;
+	
+	/* Block all signals from the current thread and all its future children */
+	sigfillset(&sig_all);
+	ret = pthread_sigmask(SIG_BLOCK, &sig_all, NULL);
+	ASSERT(ret == 0);
+	
+	/* Parse the command-line */
+	ret = main_cmdline(argc, argv);
+	if (ret != 0) {
+		return ret;
+	}
+	
+	/* Initialize the core library */
+	ret = fd_core_initialize();
+	if (ret != 0) {
+		fprintf(stderr, "An error occurred during freeDiameter core library initialization.\n");
+		return ret;
+	}
+	
+	/* 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);
+	}
+		
+	/* Allow SIGINT and SIGTERM from this point to terminate the application */
+	CHECK_POSIX( pthread_create(&signals_thr, NULL, catch_signals, NULL) );
+	
+	/* Parse the configuration file */
+	CHECK_FCT( fd_core_parseconf(conffile) );
+	
+	/* Start the servers */
+	CHECK_FCT( fd_core_start() );
+	
+	TRACE_DEBUG(INFO, FD_PROJECT_BINARY " daemon initialized.");
+	
+	/* Now, just wait for termination */
+	CHECK_FCT( fd_core_wait_shutdown_complete() );
+	
+	/* Just in case it was not the result of a signal, we cancel signals_thr */
+	fd_thr_term(&signals_thr);
+	
+	return 0;
+}
+
+
+/* Display package version */
+static void main_version_core(void)
+{
+	printf("%s, version %d.%d.%d"
+#ifdef HG_VERSION
+		" (r%s"
+# ifdef PACKAGE_HG_REVISION
+		"/%s"
+# endif /* PACKAGE_HG_VERSION */
+		")"
+#endif /* HG_VERSION */
+		" (libfdcore: %s)\n", 
+		FD_PROJECT_NAME, FD_PROJECT_VERSION_MAJOR, FD_PROJECT_VERSION_MINOR, FD_PROJECT_VERSION_REV
+#ifdef HG_VERSION
+		, HG_VERSION
+# ifdef PACKAGE_HG_REVISION
+		, PACKAGE_HG_REVISION
+# endif /* PACKAGE_HG_VERSION */
+#endif /* HG_VERSION */
+		, fd_core_version());
+}
+
+/* Display package version and general info */
+static void main_version(void)
+{
+	main_version_core();
+	printf( "%s\n", FD_PROJECT_COPYRIGHT);
+	printf( "\nSee " FD_PROJECT_NAME " homepage at http://www.freediameter.net/\n"
+		" for information, updates and bug reports on this software.\n");
+}
+
+/* Print command-line options */
+static void main_help( void )
+{
+	main_version_core();
+	printf(	"  This daemon is an implementation of the Diameter protocol\n"
+		"  used for Authentication, Authorization, and Accounting (AAA).\n");
+	printf("\nUsage:  " FD_PROJECT_BINARY " [OPTIONS]...\n");
+	printf( "  -h, --help             Print help and exit\n"
+  		"  -V, --version          Print version and exit\n"
+  		"  -c, --config=filename  Read configuration from this file instead of the \n"
+		"                           default location (" DEFAULT_CONF_PATH "/" FD_DEFAULT_CONF_FILENAME ").\n");
+ 	printf( "\nDebug:\n"
+  		"  These options are mostly useful for developers\n"
+  		"  -l, --dbglocale        Set the locale for error messages\n"
+  		"  -d, --debug            Increase verbosity of debug messages\n"
+  		"  -q, --quiet            Decrease verbosity then remove debug messages\n"
+  		"  --dbg_gnutls <int>     Enable GNU TLS debug at level <int>\n");
+}
+
+/* Parse the command-line */
+static int main_cmdline(int argc, char *argv[])
+{
+	int c;
+	int option_index = 0;
+	char * locale;
+	
+      	struct option long_options[] = {
+		{ "help",	no_argument, 		NULL, 'h' },
+		{ "version",	no_argument, 		NULL, 'V' },
+		{ "config",	required_argument, 	NULL, 'c' },
+		{ "debug",	no_argument, 		NULL, 'd' },
+		{ "quiet",	no_argument, 		NULL, 'q' },
+		{ "dbglocale",	optional_argument, 	NULL, 'l' },
+		{ "dbg_func",	required_argument, 	NULL, 'f' },
+		{ "dbg_file",	required_argument, 	NULL, 'F' },
+		{ "dbg_gnutls",	required_argument, 	NULL, 'g' },
+		{ NULL,		0, 			NULL, 0 }
+	};
+	
+	/* Loop on arguments */
+	while (1) {
+		c = getopt_long (argc, argv, "hVc:dql:", long_options, &option_index);
+		if (c == -1) 
+			break;	/* Exit from the loop.  */
+		
+		switch (c) {
+			case 'h':	/* Print help and exit.  */
+				main_help();
+				exit(0);
+
+			case 'V':	/* Print version and exit.  */
+				main_version();
+				exit(0);
+
+			case 'c':	/* Read configuration from this file instead of the default location..  */
+				if (optarg == NULL ) {
+					fprintf(stderr, "Missing argument with --config directive\n");
+					return EINVAL;
+				}
+				conffile = optarg;
+				break;
+
+			case 'l':	/* Change the locale.  */
+				locale = setlocale(LC_ALL, optarg?:"");
+				if (!locale) {
+					fprintf(stderr, "Unable to set locale (%s)\n", optarg);
+					return EINVAL;
+				}
+				break;
+
+			case 'd':	/* Increase verbosity of debug messages.  */
+				fd_g_debug_lvl++;
+				break;
+				
+			case 'f':	/* Full debug for the function with this name.  */
+				#ifdef DEBUG
+				fd_debug_one_function = optarg;
+				#else /* DEBUG */
+				fprintf(stderr, "Error: must compile with DEBUG support to use --dbg_func feature!\n");
+				return EINVAL;
+				#endif /* DEBUG */
+				break;
+				
+			case 'F':	/* Full debug for the file with this name.  */
+				#ifdef DEBUG
+				fd_debug_one_file = basename(optarg);
+				#else /* DEBUG */
+				fprintf(stderr, "Error: must compile with DEBUG support to use --dbg_file feature!\n");
+				return EINVAL;
+				#endif /* DEBUG */
+				break;
+				
+			case 'g':	/* Set a debug level and function for GNU TLS calls.  */
+				gnutls_debug = (int)atoi(optarg);
+				break;
+				
+			case 'q':	/* Decrease verbosity then remove debug messages.  */
+				fd_g_debug_lvl--;
+				break;
+
+			case '?':	/* Invalid option.  */
+				/* `getopt_long' already printed an error message.  */
+				fprintf(stderr, "getopt_long found an invalid character\n");
+				return EINVAL;
+
+			default:	/* bug: option not considered.  */
+				fprintf(stderr, "A command-line option is missing in parser: %c\n", c);
+				ASSERT(0);
+				return EINVAL;
+		}
+	}
+		
+	return 0;
+}
+
+/* Handle some signals */
+static void * catch_signals(void * arg)
+{
+	sigset_t ss;
+	fd_log_threadname ( "signals catcher" );
+	
+	sigemptyset(&ss);
+	
+	/* Signals that terminate the daemon */
+	sigaddset(&ss, SIGTERM);
+	sigaddset(&ss, SIGINT);
+	
+	/* Signals that send an event */
+	sigaddset(&ss, SIGUSR1);
+	sigaddset(&ss, SIGUSR2);
+	
+	/* Now loop on the reception of the signal */
+	while (1) {
+		int sig, *ps;
+		
+		/* Wait to receive the next signal */
+		CHECK_POSIX_DO( sigwait(&ss, &sig), break );
+		
+		TRACE_DEBUG(FULL, "Signal %d caught", sig);
+		
+		switch (sig) {
+			case SIGUSR1:
+			case SIGUSR2:
+				CHECK_MALLOC_DO( ps = malloc(sizeof(int)), goto out);
+				*ps = sig;
+				CHECK_FCT_DO( fd_event_send(fd_g_config->cnf_main_ev, FDEV_TRIGGER, sizeof(int), ps), goto out );
+				break;
+				
+			case SIGINT:
+			case SIGTERM:
+				CHECK_FCT_DO( fd_core_shutdown(), goto out );
+
+		}
+	}
+out:	
+	/* Better way to handle this ? */
+	ASSERT(0);
+	return NULL;
+}
Index: include/freeDiameter/CMakeLists.txt
===================================================================
--- include/freeDiameter/CMakeLists.txt	(revision 561)
+++ include/freeDiameter/CMakeLists.txt	(revision 658)
@@ -1,5 +1,5 @@
 #CMake configuration for freeDiameter include directory
 
-Project("freeDiameter include directory" C)
+Project("freeDiameter includes directory" C)
 
 ########################
@@ -68,6 +68,6 @@
 ENDIF (HAVE_CLOCK_GETTIME)
 
-# LFD_LIBS = libraries required by the libfreeDiameter.
-SET(LFD_LIBS ${CLOCK_GETTIME_LIBS} ${CMAKE_THREAD_LIBS_INIT})
+# LFDPROTO_LIBS = libraries required by the libfdproto.
+SET(LFDPROTO_LIBS ${CLOCK_GETTIME_LIBS} ${CMAKE_THREAD_LIBS_INIT})
 
 
@@ -98,6 +98,26 @@
 
 
-# FD_LIBS = libraries required by the daemon
-SET(FD_LIBS ${CMAKE_DL_LIBS} ${SCTP_LIBRARIES})
+# Require GNU TLS for building the library
+FIND_PACKAGE(GnuTLS REQUIRED)
+INCLUDE_DIRECTORIES(${GNUTLS_INCLUDE_DIR})
+
+find_path(GCRYPT_INCLUDE_DIR NAMES gcrypt.h)
+If ( NOT GCRYPT_INCLUDE_DIR )
+	MESSAGE(SEND_ERROR "Unable to find gcrypt.h, please install libgcrypt-dev or equivalent")
+Endif ( NOT GCRYPT_INCLUDE_DIR )
+MARK_AS_ADVANCED(GCRYPT_INCLUDE_DIR)
+INCLUDE_DIRECTORIES(${GCRYPT_INCLUDE_DIR})
+
+# Also we need libgcrypt to... display its version :(
+find_library(GCRYPT_LIBRARY 
+  NAMES gcrypt
+)
+If ( NOT GCRYPT_LIBRARY )
+	MESSAGE(SEND_ERROR "Unable to find libgcrypt, please install libgcrypt or equivalent")
+Endif ( NOT GCRYPT_LIBRARY )
+
+
+# LFDCORE_LIBS = libraries required by the libfdcore
+SET(LFDCORE_LIBS ${CMAKE_DL_LIBS} ${SCTP_LIBRARIES} ${GCRYPT_LIBRARY} ${GNUTLS_LIBRARIES})
 
 ##########################
@@ -111,6 +131,6 @@
 
 # Save some variables for the other directories
-SET(FD_LIBS  ${FD_LIBS}  PARENT_SCOPE)
-SET(LFD_LIBS ${LFD_LIBS} PARENT_SCOPE)
+SET(LFDPROTO_LIBS ${LFDPROTO_LIBS} PARENT_SCOPE)
+SET(LFDCORE_LIBS  ${LFDCORE_LIBS}  PARENT_SCOPE)
 SET(CMAKE_THREAD_LIBS_INIT ${CMAKE_THREAD_LIBS_INIT} PARENT_SCOPE)
 
@@ -119,5 +139,5 @@
 
 # The headers from this directory are required to develop new extensions for freeDiameter.
-INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/freeDiameter-host.h libfreeDiameter.h freeDiameter.h extension.h
+INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/freeDiameter-host.h libfdproto.h libfdcore.h extension.h
 	DESTINATION ${INSTALL_HEADERS_SUFFIX}
 	COMPONENT freeDiameter-dev)
Index: include/freeDiameter/extension.h
===================================================================
--- include/freeDiameter/extension.h	(revision 8)
+++ include/freeDiameter/extension.h	(revision 658)
@@ -3,5 +3,5 @@
 * Author: Sebastien Decugis <sdecugis@nict.go.jp>							 *
 *													 *
-* Copyright (c) 2009, WIDE Project and NICT								 *
+* Copyright (c) 2011, WIDE Project and NICT								 *
 * All rights reserved.											 *
 * 													 *
@@ -39,5 +39,5 @@
 /* Include definition of freeDiameter API */
 #include <freeDiameter/freeDiameter-host.h>
-#include <freeDiameter/freeDiameter.h>
+#include <freeDiameter/libfdcore.h>
 
 /* Macro that define the entry point of the extension */
Index: include/freeDiameter/freeDiameter-host.h.in
===================================================================
--- include/freeDiameter/freeDiameter-host.h.in	(revision 648)
+++ include/freeDiameter/freeDiameter-host.h.in	(revision 658)
@@ -75,3 +75,7 @@
 #cmakedefine DEFAULT_EXTENSIONS_PATH "@DEFAULT_EXTENSIONS_PATH@"
 
+#ifndef FD_DEFAULT_CONF_FILENAME
+#define FD_DEFAULT_CONF_FILENAME "freeDiameter.conf"
+#endif /* FD_DEFAULT_CONF_FILENAME */
+
 #endif /* FD_IS_CONFIG */
Index: clude/freeDiameter/freeDiameter.h
===================================================================
--- include/freeDiameter/freeDiameter.h	(revision 648)
+++ 	(revision )
@@ -1,758 +1,0 @@
-/*********************************************************************************************************
-* Software License Agreement (BSD License)                                                               *
-* Author: Sebastien Decugis <sdecugis@nict.go.jp>							 *
-*													 *
-* 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.								 *
-*********************************************************************************************************/
-
-#ifndef _FREEDIAMETER_H
-#define _FREEDIAMETER_H
-
-
-#include <freeDiameter/libfreeDiameter.h>
-#include <gnutls/gnutls.h>
-#include <gnutls/x509.h>
-
-/* GNUTLS version */
-#ifndef GNUTLS_VERSION
-#define GNUTLS_VERSION LIBGNUTLS_VERSION
-#endif /* GNUTLS_VERSION */
-
-/* GNUTLS calls debug level */
-#ifndef GNUTLS_DBG_LEVEL
-#define GNUTLS_DBG_LEVEL ANNOYING
-#endif /* GNUTLS_DBG_LEVEL */
-
-/* Check the return value of a GNUTLS function, log and propagate */
-#define CHECK_GNUTLS_DO( __call__, __fallback__ ) {						\
-	int __ret__;										\
-	TRACE_DEBUG(GNUTLS_DBG_LEVEL, "GNUTLS call: " #__call__ );				\
-	__ret__ = (__call__);									\
-	if (__ret__ < 0) {									\
-		TRACE_DEBUG(INFO, "Error in '" #__call__ "':\t%s", gnutls_strerror(__ret__));	\
-		__fallback__;									\
-	}											\
-}
-
-/* For GNUTLS routines that do not return a value */
-#define GNUTLS_TRACE( __call__) {					\
-	TRACE_DEBUG(GNUTLS_DBG_LEVEL, "GNUTLS call: " #__call__ );	\
-	(__call__);							\
-}
-
-/* Structure to hold the configuration of the freeDiameter daemon */
-#define	EYEC_CONFIG	0xC011F16
-struct fd_config {
-	int		 cnf_eyec;	/* Eye catcher: EYEC_CONFIG */
-	
-	char		*cnf_file;	/* Configuration file to parse, default is DEFAULT_CONF_FILE */
-	
-	char   		*cnf_diamid;	/* Diameter Identity of the local peer (FQDN -- UTF-8) */
-	size_t		 cnf_diamid_len;	/* length of the previous string */
-	char		*cnf_diamrlm;	/* Diameter realm of the local peer, default to realm part of diam_id */
-	size_t		 cnf_diamrlm_len;/* length of the previous string */
-	
-	unsigned int	 cnf_timer_tc;	/* The value in seconds of the default Tc timer */
-	unsigned int 	 cnf_timer_tw;	/* The value in seconds of the default Tw timer */
-	
-	uint16_t	 cnf_port;	/* the local port for legacy Diameter (default: 3868) in host byte order */
-	uint16_t	 cnf_port_tls;	/* the local port for Diameter/TLS (default: 3869) in host byte order */
-	uint16_t	 cnf_sctp_str;	/* default max number of streams for SCTP associations (def: 30) */
-	struct fd_list	 cnf_endpoints;	/* the local endpoints to bind the server to. list of struct fd_endpoint. default is empty (bind all) */
-	struct fd_list	 cnf_apps;	/* Applications locally supported (except relay, see flags). Use fd_disp_app_support to add one. list of struct fd_app. */
-	uint16_t	 cnf_dispthr;	/* Number of dispatch threads to create */
-	struct {
-		unsigned no_fwd : 1;	/* the peer does not relay messages (0xffffff app id) */
-		unsigned no_ip4 : 1;	/* disable IP */
-		unsigned no_ip6 : 1;	/* disable IPv6 */
-		unsigned no_tcp : 1;	/* disable use of TCP */
-		unsigned no_sctp: 1;	/* disable the use of SCTP */
-		unsigned pr_tcp	: 1;	/* prefer TCP over SCTP */
-		unsigned tls_alg: 1;	/* TLS algorithm for initiated cnx. 0: separate port. 1: inband-security (old) */
-	} 		 cnf_flags;
-	
-	struct {
-		/* Credentials parameters (backup) */
-		char *  			 cert_file;
-		char *				 key_file;
-		
-		char *  			 ca_file;
-		int				 ca_file_nr;
-		char *  			 crl_file;
-		
-		char *				 prio_string;
-		unsigned int 			 dh_bits;
-		char *				 dh_file;
-		
-		/* GNUTLS parameters */
-		gnutls_priority_t 		 prio_cache;
-		gnutls_dh_params_t 		 dh_cache;
-		
-		/* GNUTLS server credential(s) */
-		gnutls_certificate_credentials_t credentials;
-		
-	} 		 cnf_sec_data;
-	
-	uint32_t	 cnf_orstateid;	/* The value to use in Origin-State-Id, default to random value */
-	struct dictionary *cnf_dict;	/* pointer to the global dictionary */
-	struct fifo	  *cnf_main_ev;	/* events for the daemon's main (struct fd_event items) */
-};
-extern struct fd_config *fd_g_config; /* The pointer to access the global configuration, initalized in main */
-
-
-/***************************************/
-/*   Peers information                 */
-/***************************************/
-
-/* States of a peer */
-enum peer_state {
-	/* Stable states */
-	STATE_NEW = 0,		/* The peer has been just been created, PSM thread not started yet */
-	STATE_OPEN,		/* Connexion established */
-	
-	/* Peer state machine */
-	STATE_CLOSED,		/* No connection established, will re-attempt after TcTimer. */
-	STATE_CLOSING,		/* the connection is being shutdown (DPR/DPA in progress) */
-	STATE_WAITCNXACK,	/* Attempting to establish transport-level connection */
-	STATE_WAITCNXACK_ELEC,	/* Received a CER from this same peer on an incoming connection (other peer object), while we were waiting for cnx ack */
-	STATE_WAITCEA,		/* Connection established, CER sent, waiting for CEA */
-	/* STATE_WAITRETURNS_ELEC, */	/* This state is not stable and therefore deprecated:
-				   We have sent a CER on our initiated connection, and received a CER from the remote peer on another connection. Election.
-				   If we win the election, we must disconnect the initiated connection and send a CEA on the other => we go to OPEN state.
-				   If we lose, we disconnect the other connection (receiver) and fallback to WAITCEA state. */
-	STATE_OPEN_HANDSHAKE,	/* TLS Handshake and validation are in progress in open state -- we use it only for debug purpose, it is never displayed */
-	
-	/* Failover state machine */
-	STATE_SUSPECT,		/* A DWR was sent and not answered within TwTime. Failover in progress. */
-	STATE_REOPEN,		/* Connection has been re-established, waiting for 3 DWR/DWA exchanges before putting back to service */
-	
-	/* Error state */
-	STATE_ZOMBIE		/* The PSM thread is not running anymore; it must be re-started or peer should be deleted. */
-#define STATE_MAX STATE_ZOMBIE
-};
-/* The following macro is called in freeDiameter/p_psm.c */
-#define DECLARE_STATE_STR()		\
-const char *peer_state_str[] = { 	\
-	  "STATE_NEW"			\
-	, "STATE_OPEN"			\
-	, "STATE_CLOSED"		\
-	, "STATE_CLOSING"		\
-	, "STATE_WAITCNXACK"		\
-	, "STATE_WAITCNXACK_ELEC"	\
-	, "STATE_WAITCEA"		\
-	, "STATE_OPEN_HANDSHAKE"	\
-	, "STATE_SUSPECT"		\
-	, "STATE_REOPEN"		\
-	, "STATE_ZOMBIE"		\
-	};
-extern const char *peer_state_str[];
-#define STATE_STR(state) \
-	(((unsigned)(state)) <= STATE_MAX ? peer_state_str[((unsigned)(state)) ] : "<Invalid>")
-
-/* Constants for the peer_info structure bellow */
-#define PI_P3_DEFAULT	0	/* Use any available protocol */
-#define PI_P3_IP	1	/* Use only IP to connect to this peer */
-#define PI_P3_IPv6	2	/* resp, IPv6 */
-
-#define PI_P4_DEFAULT	0	/* Attempt any available protocol */
-#define PI_P4_TCP	1	/* Only use TCP */
-#define PI_P4_SCTP	2	/* Only use SCTP */
-
-#define PI_ALGPREF_SCTP	0	/* SCTP is  attempted first (default) */
-#define PI_ALGPREF_TCP	1	/* TCP is attempted first */
-
-#define PI_SEC_DEFAULT	0	/* New TLS security (handshake after connection, protecting also CER/CEA) */
-#define PI_SEC_NONE	1	/* Transparent security with this peer (IPsec) */
-#define PI_SEC_TLS_OLD	2	/* Old TLS security (use Inband-Security-Id AVP during CER/CEA) */
-				/* Set sec = 3 to authorize use of (Inband-Security-Id == NONE) with this peer, sec = 2 only authorizing TLS */
-
-#define PI_EXP_NONE	0	/* the peer entry does not expire */
-#define PI_EXP_INACTIVE	1	/* the peer entry expires (i.e. is deleted) after pi_lft seconds without activity */
-
-#define PI_PRST_NONE	0	/* the peer entry is deleted after disconnection / error */
-#define PI_PRST_ALWAYS	1	/* the peer entry is persistant (will be kept as ZOMBIE in case of error) */
-			
-/* Information about a remote peer */
-struct peer_info {
-	
-	char * 		pi_diamid;	/* UTF-8, \0 terminated. The Diameter Identity of the remote peer. */
-	
-	struct {
-		struct {
-			unsigned	pro3 :2;	/* PI_P3_* */
-			unsigned	pro4 :2;	/* PI_P4_* */
-			unsigned	alg :1;		/* PI_ALGPREF_* */
-			unsigned	sec :2;		/* PI_SEC_* */
-			unsigned	exp :1;		/* PI_EXP_* */
-			unsigned	persist :1;	/* PI_PRST_* */
-			
-		}		pic_flags;	/* Flags influencing the connection to the remote peer */
-		
-		char * 		pic_realm;	/* If configured, the daemon will match the received realm in CER/CEA matches this. */
-		uint16_t	pic_port; 	/* port to connect to. 0: default. */
-		
-		uint32_t 	pic_lft;	/* lifetime of this peer when inactive (see pic_flags.exp definition) */
-		int		pic_tctimer; 	/* use this value for TcTimer instead of global, if != 0 */
-		int		pic_twtimer; 	/* use this value for TwTimer instead of global, if != 0 */
-		
-		char *		pic_priority;	/* Priority string for GnuTLS if we don't use the default */
-		
-	} config;	/* Configured data (static for this peer entry) */
-	
-	struct {
-		
-		enum peer_state	pir_state;	/* Current state of the peer in the state machine. fd_cpu_flush_cache() might be useful before reading. */
-		
-		char * 		pir_realm;	/* The received realm in CER/CEA. */
-		
-		uint32_t	pir_vendorid;	/* Content of the Vendor-Id AVP, or 0 by default */
-		uint32_t	pir_orstate;	/* Origin-State-Id value */
-		char *		pir_prodname;	/* copy of UTF-8 Product-Name AVP (\0 terminated) */
-		uint32_t	pir_firmrev;	/* Content of the Firmware-Revision AVP */
-		int		pir_relay;	/* The remote peer advertized the relay application */
-		struct fd_list	pir_apps;	/* applications advertised by the remote peer, except relay (pi_flags.relay) */
-		int		pir_isi;	/* Inband-Security-Id advertised (PI_SEC_* bits) */
-		
-		uint32_t	pir_lastDC;	/* The last Disconnect-Cause value received */
-		
-		int		pir_proto;	/* The L4 protocol currently used with the peer (IPPROTO_TCP or IPPROTO_SCTP) */
-		const gnutls_datum_t 	*pir_cert_list; 	/* The (valid) credentials that the peer has presented, or NULL if TLS is not used */
-								/* This is inspired from http://www.gnu.org/software/gnutls/manual/gnutls.html#ex_003ax509_002dinfo 
-								   see there for example of using this data */
-		unsigned int 	pir_cert_list_size;		/* Number of certificates in the list */
-		
-	} runtime;	/* Data populated after connection, may change between 2 connections -- not used by fd_peer_add */
-	
-	struct fd_list	pi_endpoints;	/* Endpoint(s) of the remote peer (configured, discovered, or advertized). list of struct fd_endpoint. DNS resolved if empty. */
-};
-
-
-struct peer_hdr {
-	struct fd_list	 chain;	/* List of all the peers, ordered by their Diameter Id */
-	struct peer_info info;	/* The public data */
-	
-	/* This header is followed by more data in the private peer structure definition */
-};
-
-/* the global list of peers. 
-  Since we are not expecting so many connections, we don't use a hash, but it might be changed.
-  The list items are peer_hdr structures (actually, fd_peer, but the cast is OK) */
-extern struct fd_list fd_g_peers;
-extern pthread_rwlock_t fd_g_peers_rw; /* protect the list */
-
-/*
- * FUNCTION:	fd_peer_add
- *
- * PARAMETERS:
- *  info 	: Information to create the peer.
- *  orig_dbg	: A string indicating the origin of the peer information, for debug (ex: conf, redirect, ...)
- *  cb		: optional, a callback to call (once) when the peer connection is established or failed
- *  cb_data	: opaque data to pass to the callback.
- *
- * DESCRIPTION: 
- *  Add a peer to the list of peers to which the daemon must maintain a connexion.
- *
- *  The content of info parameter is copied, except for the list of endpoints if 
- * not empty, which is simply moved into the created object. It means that the list
- * items must have been malloc'd, so that they can be freed.
- *
- *  If cb is not null, the callback is called when the connection is in OPEN state or
- * when an error has occurred. The callback should use the pi_state information to 
- * determine which one it is. If the first parameter of the called callback is NULL, it 
- * means that the peer is being destroyed before attempt success / failure. 
- * cb is called to allow freeing cb_data in  * this case.
- *
- *  The orig_dbg string is only useful for easing debug, and can be left to NULL.
- *
- * RETURN VALUE:
- *  0      	: The peer is added.
- *  EINVAL 	: A parameter is invalid.
- *  EEXIST 	: A peer with the same Diameter-Id is already in the list.
- *  (other standard errors may be returned, too, with their standard meaning. Example:
- *    ENOMEM 	: Memory allocation for the new object element failed.)
- */
-int fd_peer_add ( struct peer_info * info, char * orig_dbg, void (*cb)(struct peer_info *, void *), void * cb_data );
-
-/*
- * FUNCTION:	fd_peer_getbyid
- *
- * PARAMETERS:
- *  diamid 	: A \0 terminated string.
- *  peer	: The peer is stored here if it exists.
- *
- * DESCRIPTION: 
- *   Search a peer by its Diameter-Id.
- *
- * RETURN VALUE:
- *  0   : *peer has been updated (to NULL if the peer is not found).
- * !0	: An error occurred.
- */
-int fd_peer_getbyid( char * diamid, struct peer_hdr ** peer );
-
-/*
- * FUNCTION:	fd_peer_validate_register
- *
- * PARAMETERS:
- *  peer_validate 	: Callback as defined bellow.
- *
- * DESCRIPTION: 
- *  Add a callback to authorize / reject incoming peer connections.
- * All registered callbacks are called until a callback sets auth = -1 or auth = 1.
- * If no callback returns a clear decision, the default behavior is applied (reject unknown connections)
- * The callbacks are called in FILO order of their registration.
- *
- * RETURN VALUE:
- *  0   : The callback is added.
- * !0	: An error occurred.
- */
-int fd_peer_validate_register ( int (*peer_validate)(struct peer_info * /* info */, int * /* auth */, int (**cb2)(struct peer_info *)) );
-/*
- * CALLBACK:	peer_validate
- *
- * PARAMETERS:
- *   info     : Structure containing information about the peer attempting the connection.
- *   auth     : Store there the result if the peer is accepted (1), rejected (-1), or unknown (0).
- *   cb2      : If != NULL and in case of PI_SEC_TLS_OLD, another callback to call after handshake (if auth = 1).
- *
- * DESCRIPTION: 
- *   This callback is called when a new connection is being established from an unknown peer,
- * after the CER is received. An extension must register such callback with peer_validate_register.
- *
- *   The callback can learn if the peer has sent Inband-Security-Id AVPs in runtime.pir_isi fields.
- * It can also learn if a handshake has already been performed in runtime.pir_cert_list field.
- * The callback must set the value of config.pic_flags.sec appropriately to allow a connection without TLS.
- *
- *   If the old TLS mechanism is used,
- * the extension may also need to check the credentials provided during the TLS
- * exchange (remote certificate). For this purpose, it may set the address of a new callback
- * to be called once the handshake is completed. This new callback receives the information
- * structure as parameter (with pir_cert_list set) and returns 0 if the credentials are correct,
- * or an error code otherwise. If the error code is received, the connection is closed and the 
- * peer is destroyed.
- * Note that freeDiameter already achieves some usual checks. The callback may be used to enforce
- * additional restrictions.
- *
- * RETURN VALUE:
- *  0      	: The authorization decision has been written in the location pointed by auth.
- *  !0 		: An error occurred.
- */
-
-/***************************************/
-/*   Sending a message on the network  */
-/***************************************/
-
-/*
- * FUNCTION:	fd_msg_send, fd_msg_send_timeout  
- *
- * PARAMETERS:
- *  pmsg 	: Location of the message to be sent on the network (set to NULL on function return to avoid double deletion).
- *  anscb	: A callback to be called when answer is received, if msg is a request (optional for fd_msg_send)
- *  anscb_data	: opaque data to be passed back to the anscb when it is called.
- *  timeout     : (only for fd_msg_send_timeout) sets the absolute time until when to wait for an answer. Past this time,
- *                the anscb is called with the request as parameter and the answer will be discarded when received.
- *
- * DESCRIPTION: 
- *   Sends a message on the network. (actually simply queues it in a global queue, to be picked by a daemon's thread)
- * For requests, the end-to-end id must be set (see fd_msg_get_eteid / MSGFL_ALLOC_ETEID).
- * For answers, the message must be created with function fd_msg_new_answ.
- *
- * The routing module will handle sending to the correct peer, usually based on the Destination-Realm / Destination-Host AVP.
- *
- * If the msg is a request, there are two ways of receiving the answer:
- *  - either having registered a callback in the dispatch module (see fd_disp_register)
- *  - or provide a callback as parameter here. If such callback is provided, it is called before the dispatch callbacks.
- *    The prototype for this callback function is:
- *     void anscb(void * data, struct msg ** answer)
- *	where:
- *		data   : opaque data that was registered along with the callback.
- *		answer : location of the pointer to the answer.
- *      note1: on function return, if *answer is not NULL, the message is passed to the dispatch module for regular callbacks.
- *	       otherwise, the callback must take care of freeing the message (fd_msg_free).
- *	note2: the opaque data is not freed by the daemon in any case, extensions should ensure clean handling in fd_ext_fini.
- * 
- * If no callback is registered to handle an answer, the message is discarded and an error is logged.
- *
- *  fd_msg_send_timeout is similar to fd_msg_send, except that it takes an additional argument "timeout" and can be called
- * only with requests as parameters, and an anscb callback.
- * If the matching answer or error is received before the timeout date passes, everything occurs as with fd_msg_send. Otherwise,
- * the request is removed from the queue (meaning the matching answer will be discarded upon reception) and passed to the answcb 
- * function. This function can easily distinguish between timeout case and answer case by checking if the message received is 
- * a request. Upon return, if the *msg parameter is not NULL, it is freed (not passed to other callbacks).
- *
- * RETURN VALUE:
- *  0      	: The message has been queued for sending (sending may fail asynchronously).
- *  EINVAL 	: A parameter is invalid (ex: anscb provided but message is not a request).
- *  ...
- */
-int fd_msg_send ( struct msg ** pmsg, void (*anscb)(void *, struct msg **), void * data );
-int fd_msg_send_timeout ( struct msg ** pmsg, void (*anscb)(void *, struct msg **), void * data, const struct timespec *timeout );
-
-/*
- * FUNCTION:	fd_msg_rescode_set
- *
- * PARAMETERS:
- *  msg		: A msg object -- it must be an answer.
- *  rescode	: The name of the returned error code (ex: "DIAMETER_INVALID_AVP")
- *  errormsg    : (optional) human-readable error message to put in Error-Message AVP
- *  optavp	: (optional) If provided, the content will be put inside a Failed-AVP
- *  type_id	: 0 => nothing; 1 => adds Origin-Host and Origin-Realm with local info. 2=> adds Error-Reporting-Host.
- *
- * DESCRIPTION: 
- *   This function adds a Result-Code AVP to a message, and optionally
- *  - sets the 'E' error flag in the header,
- *  - adds Error-Message, Error-Reporting-Host and Failed-AVP AVPs.
- *
- * RETURN VALUE:
- *  0      	: Operation complete.
- *  !0      	: an error occurred.
- */
-int fd_msg_rescode_set( struct msg * msg, char * rescode, char * errormsg, struct avp * optavp, int type_id );
-
-/* Add Origin-Host, Origin-Realm, (if osi) Origin-State-Id AVPS at the end of the message */
-int fd_msg_add_origin ( struct msg * msg, int osi ); 
-
-/* Parse a message against our dictionary, and in case of error log and eventually build the error reply (on return and EBADMSG, *msg == NULL or *msg is the error message ready to send) */
-int fd_msg_parse_or_error( struct msg ** msg );
-
-
-/***************************************/
-/*   Dispatch module, daemon's part    */
-/***************************************/
-
-/*
- * FUNCTION:	fd_disp_app_support
- *
- * PARAMETERS:
- *  app		: The dictionary object corresponding to the Application.
- *  vendor	: (Optional) the dictionary object of a Vendor to claim support in Vendor-Specific-Application-Id
- *  auth	: Support auth app part.
- *  acct	: Support acct app part.
- *
- * DESCRIPTION: 
- *   Registers an application to be advertized in CER/CEA exchanges.
- *  Messages with an application-id matching a registered value are passed to the dispatch module,
- * while other messages are simply relayed or an error is returned (if local node does not relay)
- *
- * RETURN VALUE:
- *  0      	: The application support is registered.
- *  EINVAL 	: A parameter is invalid.
- */
-int fd_disp_app_support ( struct dict_object * app, struct dict_object * vendor, int auth, int acct );
-
-/* Note: if we want to support capabilities updates, we'll have to add possibility to remove an app as well... */
-
-
-/***************************************/
-/*          Routing module             */
-/***************************************/
-
-/* This file contains the definitions of types and functions involved in the routing decisions in freeDiameter,
- * and that can be called by extensions. 
- *
- * Three different type of messages must be distinguished:
- *  - Messages received, and the peer is final recipient (IN messages)
- *  - Messages received, and the peer is not final recipient (FWD messages)
- *  - Message is locally generated (OUT messages)
- *
- * There are three global message queues (in queues.c) and also peers-specific queues (in struct fd_peer).
- *
- * (*) IN messages processing details:
- *   - the message is received from the remote peer, a FDEVP_CNX_MSG_RECV event is generated for the peer.
- *   - the PSM thread parses the buffer, does some verifications, handles non routable messages (fd_msg_is_routable)
- *   - routable messages are queued in the fd_g_incoming global queue.
- *   - a thread (routing-in) picks the message and takes the decision if it is handled locally or forwarded, 
- *       based on local capabilities (registered by extensions with fd_disp_app_support).
- *   - If the message is handled locally, it is queued in fd_g_local.
- *   - Another thread (dispatch.c) will handle this message and pass it to registered callbacks (see fd_disp_register in libfreeDiameter.h).
- *
- * (*) FWD messages details:
- *   - The process is the same as for IN messages, until the routing-in threads makes its decision that the message is not handled locally.
- *   - If the local peer does not relay message, an error DIAMETER_APPLICATION_UNSUPPORTED is returned.
- *   - All callbacks registered with fd_rt_fwd_register are called for the message (see bellow).
- *     - these callbacks will typically do proxying work. Note that adding the route-record is handled by the daemon.
- *   - Once all callbacks have been called, the message is queued in the global fd_g_outgoing queue.
- *   - The remaining processing is the same as for OUT messages, as described bellow.
- *
- * (*) OUT messages details:
- *   - The message are picked from fd_g_outgoing (they are queued there as result of forwarding process or call to fd_msg_send.)
- *   - The (routing-out) thread builds a list of possible destinations for the message, as follow:
- *      - create a list of all known peers in the "OPEN" state.
- *      - remove from that list all peers that are in a Route-Record AVP of the message, to avoid routing loops.
- *      - remove also all peers that have previously replied an error message for this message.
- *   - If the list is empty, create an error UNABLE_TO_DELIVER (note: should we trig dynamic discovery here???) and reply.
- *   - Otherwise, call all callbacks registered by function fd_rt_out_register, with the list of peers and the message.
- *   - Order the resulting list of peers by score (see bellow), and sent the message to the peer with highest (positive) score.
- *    - in case the peer is no longer in the "OPEN" state, send the message to the second peer in the list.
- *      - if no peer is in OPEN state anymore, restart the process of creating the list.
- *   - Once a peer has been selected, the message is queued into that peer's outgoing queue.
- *
- * The following functions allow an extension to register or remove a callback as described above.
- */
-
-/********** Forwarding callbacks: for Proxy operations ***********/
-
-/* Handle to registered callback */
-struct fd_rt_fwd_hdl;
-
-/* Message direction for the callback */
-enum fd_rt_fwd_dir {
-	RT_FWD_REQ = 1,	/* The callback will be called on forwarded requests only */
-	RT_FWD_ALL = 2,	/* The callback will be called on all forwarded messages (requests and answers )*/
-	RT_FWD_ANS = 3	/* The callback will be called on answers and errors only */
-};	
-
-/*
- * FUNCTION:	fd_rt_fwd_register
- *
- * PARAMETERS:
- *  rt_fwd_cb	  : The callback function to register (see prototype bellow).
- *  cbdata	  : Pointer to pass to the callback when it is called. The data is opaque to the daemon.
- *  dir           : One of the RT_FWD_* directions defined above.
- *  handler       : On success, a handler to the registered callback is stored here. 
- *		   This handler will be used to unregister the cb.
- *
- * DESCRIPTION: 
- *   Register a new callback for forwarded messages. See explanations above. 
- * Note that there is no guaranteed order for the callbacks calls.
- *
- * RETURN VALUE:
- *  0      	: The callback is registered.
- *  EINVAL 	: A parameter is invalid.
- *  ENOMEM	: Not enough memory to complete the operation
- */
-int fd_rt_fwd_register ( int (*rt_fwd_cb)(void * cbdata, struct msg ** msg), void * cbdata, enum fd_rt_fwd_dir dir, struct fd_rt_fwd_hdl ** handler );
-/*
- * CALLBACK:	rt_fwd_cb
- *
- * PARAMETERS:
- *  data	: pointer to some data that was passed when the callback was registered (optional).
- *  msg 	: The message that is being forwarded.
- *
- * DESCRIPTION: 
- *   This callback is called when a message is forwarded to another peer. It may for example add a Proxy-Info AVP.
- *  The callback may also choose to handle the message in a more complex form. In that case, it must set *msg = NULL
- *  and handle it differently. In such case, the forwarding thread will stop processing this message.
- *
- * RETURN VALUE:
- *  0      	: Operation complete.
- *  !0 		: An error occurred -- will result in daemon's termination.
- */
-
-/*
- * FUNCTION:	fd_rt_fwd_unregister
- *
- * PARAMETERS:
- *  handler     : The handler of the callback that must be unregistered.
- *  cbdata	: Will receive the data registered with the callback, that can be freed if needed.
- *
- * DESCRIPTION: 
- *   Removes a callback from the list of registered callbacks.
- *
- * RETURN VALUE:
- *  0      	: The callback is unregistered.
- *  EINVAL 	: A parameter is invalid.
- */
-int fd_rt_fwd_unregister ( struct fd_rt_fwd_hdl * handler, void ** cbdata );
-
-
-/********** Out callbacks: for next hop routing decision operations ***********/
-
-/* Handle to registered callback */
-struct fd_rt_out_hdl;
-
-enum fd_rt_out_score {
-	FD_SCORE_NO_DELIVERY	 = -70,	/* We should not send this message to this candidate */
-	FD_SCORE_INI		 =  -2, /* All candidates are initialized with this value */
-	FD_SCORE_LOAD_BALANCE	 =   1,	/* Use this to differentiate between several peers with the same score */
-	FD_SCORE_DEFAULT	 =   5,	/* The peer is a default route for all messages */
-	FD_SCORE_DEFAULT_REALM	 =  10,	/* The peer is a default route for this realm */
-	FD_SCORE_REALM		 =  15,	/* The peer belongs to Destination-Realm of the message */
-	FD_SCORE_REDIR_HOST	 =  25,	/* If there is a redirect rule with ALL_HOST for these message and peer */
-	FD_SCORE_REDIR_APP	 =  30,	/* If there is a redirect rule with ALL_APPLICATION for these message and peer */
-	FD_SCORE_REDIR_REALM	 =  35,	/* If there is a redirect rule with ALL_REALM for these message and peer */
-	FD_SCORE_REDIR_REALM_APP =  40,	/* If there is a redirect rule with REALM_AND_APPLICATION for these message and peer */
-	FD_SCORE_REDIR_USER	 =  45,	/* If there is a redirect rule with ALL_USER for these message and peer */
-	FD_SCORE_REDIR_SESSION	 =  50,	/* If there is a redirect rule with ALL_SESSION for these message and peer */
-	FD_SCORE_FINALDEST	 = 100	/* If the peer is the final recipient of the message (i.e. matching Destination-Host), it receives a big score. */
-};
-
-/*
- * FUNCTION:	fd_rt_out_register
- *
- * PARAMETERS:
- *  rt_out_cb	  : The callback function to register (see prototype bellow).
- *  cbdata	  : Pointer to pass to the callback when it is called. The data is opaque to the daemon.
- *  priority      : Order for calling this callback. The callbacks are called in reverse priority order (higher priority = called sooner).
- *  handler       : On success, a handler to the registered callback is stored here. 
- *		   This handler will be used to unregister the cb.
- *
- * DESCRIPTION: 
- *   Register a new callback to handle OUT routing decisions. See explanations above. 
- *
- * RETURN VALUE:
- *  0      	: The callback is registered.
- *  EINVAL 	: A parameter is invalid.
- *  ENOMEM	: Not enough memory to complete the operation
- */
-int fd_rt_out_register ( int (*rt_out_cb)(void * cbdata, struct msg * msg, struct fd_list * candidates), void * cbdata, int priority, struct fd_rt_out_hdl ** handler );
-/*
- * CALLBACK:	rt_out_cb
- *
- * PARAMETERS:
- *  cbdata	: pointer to some data that was registered with the callback.
- *  msg 	: The message that must be sent.
- *  list        : The list of peers to which the message may be sent to, as returned by fd_rtd_candidate_extract
- *
- * DESCRIPTION: 
- *   This callback must attribute a score (preferably from FD_SCORE_*) to each candidate peer in the list.
- *  Once all registered callbacks have been called, the message is sent to the candidate with the highest score.
- *  Note that each callback must *add* its locally-attributed score to the candidate current "score" parameter, not replace it!
- *  Note also that this callback must be re-entrant since it may be called by several threads at the same time 
- *  (for different messages)
- *
- * RETURN VALUE:
- *  0      	: Operation complete.
- *  !0 		: An error occurred.
- */
-
-/*
- * FUNCTION:	fd_rt_out_unregister
- *
- * PARAMETERS:
- *  handler     : The handler of the callback that must be unregistered.
- *  cbdata	: Will receive the data registered with the callback, that can be freed if needed.
- *
- * DESCRIPTION: 
- *   Removes a callback from the list of registered callbacks.
- *
- * RETURN VALUE:
- *  0      	: The callback is unregistered.
- *  EINVAL 	: A parameter is invalid.
- */
-int fd_rt_out_unregister ( struct fd_rt_out_hdl * handler, void ** cbdata );
-
-
-/***************************************/
-/*   Events helpers                    */
-/***************************************/
-
-struct fd_event {
-	int	 code; /* codespace depends on the queue */
-	size_t 	 size;
-	void    *data;
-};
-
-/* Daemon's codespace: 1000->1999 (1500->1999 defined in fD.h) */
-enum {
-	 FDEV_TERMINATE	= 1000	/* request to terminate */
-	,FDEV_DUMP_DICT		/* Dump the content of the dictionary */
-	,FDEV_DUMP_EXT		/* Dump state of extensions */
-	,FDEV_DUMP_SERV		/* Dump the server socket status */
-	,FDEV_DUMP_QUEUES	/* Dump the message queues */
-	,FDEV_DUMP_CONFIG	/* Dump the configuration */
-	,FDEV_DUMP_PEERS	/* Dump the list of peers */
-};
-
-int fd_event_send(struct fifo *queue, int code, size_t datasz, void * data);
-int fd_event_get(struct fifo *queue, int * code, size_t * datasz, void ** data);
-int fd_event_timedget(struct fifo *queue, struct timespec * timeout, int timeoutcode, int * code, size_t * datasz, void ** data);
-void fd_event_destroy(struct fifo **queue, void (*free_cb)(void * data));
-const char * fd_ev_str(int event);
-
-
-/* The following function does not really use events, but it may be used 
-by extensions that need to start an action when the framework is fully initialized. 
-This function will block until all initializations are performed in the daemon.
-It is meant to be used as follow by extensions:
- - in initialization callback, create a new thread.
- - this new thread calls this function.
- - when the function returns, the thread can start working and using all framework features.
-*/
-int fd_wait_initialization_complete(void);
-
-
-/***************************************/
-/*   Endpoints lists helpers           */
-/***************************************/
-
-struct fd_endpoint {
-	struct fd_list  chain;	/* link in cnf_endpoints list */
-	
-	union {
-		sSS		ss;	/* the socket information. List is always ordered by ss value (memcmp) -- see fd_ep_add_merge */
-		sSA4		sin;
-		sSA6		sin6;
-		sSA		sa;
-	}
-#ifdef SWIG /* nested anonymous unions are not supported yet */
-			s
-#endif /* SWIG */
-	;
-	
-#define	EP_FL_CONF	(1 << 0)	/* This endpoint is statically configured in a configuration file */
-#define	EP_FL_DISC	(1 << 1)	/* This endpoint was resolved from the Diameter Identity or other DNS query */
-#define	EP_FL_ADV	(1 << 2)	/* This endpoint was advertized in Diameter CER/CEA exchange */
-#define	EP_FL_LL	(1 << 3)	/* Lower layer mechanism provided this endpoint */
-#define	EP_FL_PRIMARY	(1 << 4)	/* This endpoint is primary in a multihomed SCTP association */
-#define	EP_ACCEPTALL	(1 << 15)	/* This flag allows bypassing the address filter in fd_ep_add_merge. */
-	uint32_t	flags;		/* Additional information about the endpoint */
-		
-	/* To add: a validity timestamp for DNS records ? How do we retrieve this lifetime from DNS ? */
-};
-
-int fd_ep_add_merge( struct fd_list * list, sSA * sa, socklen_t sl, uint32_t flags );
-int fd_ep_filter( struct fd_list * list, uint32_t flags );
-int fd_ep_filter_family( struct fd_list * list, int af );
-int fd_ep_filter_list( struct fd_list * list, struct fd_list * exclude_list );
-int fd_ep_clearflags( struct fd_list * list, uint32_t flags );
-void fd_ep_dump_one( char * prefix, struct fd_endpoint * ep, char * suffix );
-void fd_ep_dump( int indent, struct fd_list * eps );
-
-/***************************************/
-/*   Applications lists helpers        */
-/***************************************/
-
-struct fd_app {
-	struct fd_list	 chain;	/* link in cnf_apps list. List ordered by appid. */
-	struct {
-		unsigned auth   : 1;
-		unsigned acct   : 1;
-	}		 flags;
-	vendor_id_t	 vndid; /* if not 0, Vendor-Specific-App-Id AVP will be used */
-	application_id_t appid;	/* The identifier of the application */
-};
-	
-int fd_app_merge(struct fd_list * list, application_id_t aid, vendor_id_t vid, int auth, int acct);
-int fd_app_check(struct fd_list * list, application_id_t aid, struct fd_app **detail);
-int fd_app_check_common(struct fd_list * list1, struct fd_list * list2, int * common_found);
-int fd_app_empty(struct fd_list * list);
-
-#endif /* _FREEDIAMETER_H */
Index: include/freeDiameter/libfdcore.h
===================================================================
--- include/freeDiameter/libfdcore.h	(revision 658)
+++ include/freeDiameter/libfdcore.h	(revision 658)
@@ -0,0 +1,790 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@nict.go.jp>							 *
+*													 *
+* 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.								 *
+*********************************************************************************************************/
+
+#ifndef _LIBFDCORE_H
+#define _LIBFDCORE_H
+
+
+#include <freeDiameter/libfdproto.h>
+#include <gnutls/gnutls.h>
+#include <gnutls/x509.h>
+
+/* GNUTLS version */
+#ifndef GNUTLS_VERSION
+#define GNUTLS_VERSION LIBGNUTLS_VERSION
+#endif /* GNUTLS_VERSION */
+
+/* GNUTLS calls debug level */
+#ifndef GNUTLS_DBG_LEVEL
+#define GNUTLS_DBG_LEVEL ANNOYING
+#endif /* GNUTLS_DBG_LEVEL */
+
+/* Check the return value of a GNUTLS function, log and propagate */
+#define CHECK_GNUTLS_DO( __call__, __fallback__ ) {						\
+	int __ret__;										\
+	TRACE_DEBUG(GNUTLS_DBG_LEVEL, "GNUTLS call: " #__call__ );				\
+	__ret__ = (__call__);									\
+	if (__ret__ < 0) {									\
+		TRACE_DEBUG(INFO, "Error in '" #__call__ "':\t%s", gnutls_strerror(__ret__));	\
+		__fallback__;									\
+	}											\
+}
+
+/* For GNUTLS routines that do not return a value */
+#define GNUTLS_TRACE( __call__) {					\
+	TRACE_DEBUG(GNUTLS_DBG_LEVEL, "GNUTLS call: " #__call__ );	\
+	(__call__);							\
+}
+
+
+/*============================================================*/
+/*                      INITIALIZATION                        */
+/*============================================================*/
+
+
+/* Initialize the libfdcore internals. This also initializes libfdproto */
+int fd_core_initialize(void);
+
+/* Return a string describing the version of the library */
+const char *fd_core_version(void);
+
+/* Parse the freeDiameter.conf configuration file, load the extensions */
+int fd_core_parseconf(char * conffile);
+
+/* Start the server & client threads */
+int fd_core_start(void);
+
+/* Block until the framework has completed its initialization -- useful for extensions */
+int fd_core_waitstartcomplete(void);
+
+/* Initialize shutdown of the framework */
+int fd_core_shutdown(void);
+
+/* Wait for the shutdown to be complete -- this should always be called after fd_core_shutdown */
+int fd_core_wait_shutdown_complete(void);
+
+
+/*============================================================*/
+/*                          CONFIG                            */
+/*============================================================*/
+
+/* Structure to hold the configuration of the freeDiameter daemon */
+#define	EYEC_CONFIG	0xC011F16
+struct fd_config {
+	int		 cnf_eyec;	/* Eye catcher: EYEC_CONFIG */
+	
+	char		*cnf_file;	/* Configuration file to parse, default is DEFAULT_CONF_FILE */
+	
+	char   		*cnf_diamid;	/* Diameter Identity of the local peer (FQDN -- UTF-8) */
+	size_t		 cnf_diamid_len;	/* length of the previous string */
+	char		*cnf_diamrlm;	/* Diameter realm of the local peer, default to realm part of diam_id */
+	size_t		 cnf_diamrlm_len;/* length of the previous string */
+	
+	unsigned int	 cnf_timer_tc;	/* The value in seconds of the default Tc timer */
+	unsigned int 	 cnf_timer_tw;	/* The value in seconds of the default Tw timer */
+	
+	uint16_t	 cnf_port;	/* the local port for legacy Diameter (default: 3868) in host byte order */
+	uint16_t	 cnf_port_tls;	/* the local port for Diameter/TLS (default: 3869) in host byte order */
+	uint16_t	 cnf_sctp_str;	/* default max number of streams for SCTP associations (def: 30) */
+	struct fd_list	 cnf_endpoints;	/* the local endpoints to bind the server to. list of struct fd_endpoint. default is empty (bind all) */
+	struct fd_list	 cnf_apps;	/* Applications locally supported (except relay, see flags). Use fd_disp_app_support to add one. list of struct fd_app. */
+	uint16_t	 cnf_dispthr;	/* Number of dispatch threads to create */
+	struct {
+		unsigned no_fwd : 1;	/* the peer does not relay messages (0xffffff app id) */
+		unsigned no_ip4 : 1;	/* disable IP */
+		unsigned no_ip6 : 1;	/* disable IPv6 */
+		unsigned no_tcp : 1;	/* disable use of TCP */
+		unsigned no_sctp: 1;	/* disable the use of SCTP */
+		unsigned pr_tcp	: 1;	/* prefer TCP over SCTP */
+		unsigned tls_alg: 1;	/* TLS algorithm for initiated cnx. 0: separate port. 1: inband-security (old) */
+	} 		 cnf_flags;
+	
+	struct {
+		/* Credentials parameters (backup) */
+		char *  			 cert_file;
+		char *				 key_file;
+		
+		char *  			 ca_file;
+		int				 ca_file_nr;
+		char *  			 crl_file;
+		
+		char *				 prio_string;
+		unsigned int 			 dh_bits;
+		char *				 dh_file;
+		
+		/* GNUTLS parameters */
+		gnutls_priority_t 		 prio_cache;
+		gnutls_dh_params_t 		 dh_cache;
+		
+		/* GNUTLS server credential(s) */
+		gnutls_certificate_credentials_t credentials;
+		
+	} 		 cnf_sec_data;
+	
+	uint32_t	 cnf_orstateid;	/* The value to use in Origin-State-Id, default to random value */
+	struct dictionary *cnf_dict;	/* pointer to the global dictionary */
+	struct fifo	  *cnf_main_ev;	/* events for the daemon's main (struct fd_event items) */
+};
+extern struct fd_config *fd_g_config; /* The pointer to access the global configuration, initalized in main */
+
+
+
+/*============================================================*/
+/*                         PEERS                              */
+/*============================================================*/
+
+/* States of a peer */
+enum peer_state {
+	/* Stable states */
+	STATE_NEW = 0,		/* The peer has been just been created, PSM thread not started yet */
+	STATE_OPEN,		/* Connexion established */
+	
+	/* Peer state machine */
+	STATE_CLOSED,		/* No connection established, will re-attempt after TcTimer. */
+	STATE_CLOSING,		/* the connection is being shutdown (DPR/DPA in progress) */
+	STATE_WAITCNXACK,	/* Attempting to establish transport-level connection */
+	STATE_WAITCNXACK_ELEC,	/* Received a CER from this same peer on an incoming connection (other peer object), while we were waiting for cnx ack */
+	STATE_WAITCEA,		/* Connection established, CER sent, waiting for CEA */
+	/* STATE_WAITRETURNS_ELEC, */	/* This state is not stable and therefore deprecated:
+				   We have sent a CER on our initiated connection, and received a CER from the remote peer on another connection. Election.
+				   If we win the election, we must disconnect the initiated connection and send a CEA on the other => we go to OPEN state.
+				   If we lose, we disconnect the other connection (receiver) and fallback to WAITCEA state. */
+	STATE_OPEN_HANDSHAKE,	/* TLS Handshake and validation are in progress in open state -- we use it only for debug purpose, it is never displayed */
+	
+	/* Failover state machine */
+	STATE_SUSPECT,		/* A DWR was sent and not answered within TwTime. Failover in progress. */
+	STATE_REOPEN,		/* Connection has been re-established, waiting for 3 DWR/DWA exchanges before putting back to service */
+	
+	/* Error state */
+	STATE_ZOMBIE		/* The PSM thread is not running anymore; it must be re-started or peer should be deleted. */
+#define STATE_MAX STATE_ZOMBIE
+};
+/* The following macro is called in freeDiameter/p_psm.c */
+#define DECLARE_STATE_STR()		\
+const char *peer_state_str[] = { 	\
+	  "STATE_NEW"			\
+	, "STATE_OPEN"			\
+	, "STATE_CLOSED"		\
+	, "STATE_CLOSING"		\
+	, "STATE_WAITCNXACK"		\
+	, "STATE_WAITCNXACK_ELEC"	\
+	, "STATE_WAITCEA"		\
+	, "STATE_OPEN_HANDSHAKE"	\
+	, "STATE_SUSPECT"		\
+	, "STATE_REOPEN"		\
+	, "STATE_ZOMBIE"		\
+	};
+extern const char *peer_state_str[];
+#define STATE_STR(state) \
+	(((unsigned)(state)) <= STATE_MAX ? peer_state_str[((unsigned)(state)) ] : "<Invalid>")
+
+/* Constants for the peer_info structure bellow */
+#define PI_P3_DEFAULT	0	/* Use any available protocol */
+#define PI_P3_IP	1	/* Use only IP to connect to this peer */
+#define PI_P3_IPv6	2	/* resp, IPv6 */
+
+#define PI_P4_DEFAULT	0	/* Attempt any available protocol */
+#define PI_P4_TCP	1	/* Only use TCP */
+#define PI_P4_SCTP	2	/* Only use SCTP */
+
+#define PI_ALGPREF_SCTP	0	/* SCTP is  attempted first (default) */
+#define PI_ALGPREF_TCP	1	/* TCP is attempted first */
+
+#define PI_SEC_DEFAULT	0	/* New TLS security (handshake after connection, protecting also CER/CEA) */
+#define PI_SEC_NONE	1	/* Transparent security with this peer (IPsec) */
+#define PI_SEC_TLS_OLD	2	/* Old TLS security (use Inband-Security-Id AVP during CER/CEA) */
+				/* Set sec = 3 to authorize use of (Inband-Security-Id == NONE) with this peer, sec = 2 only authorizing TLS */
+
+#define PI_EXP_NONE	0	/* the peer entry does not expire */
+#define PI_EXP_INACTIVE	1	/* the peer entry expires (i.e. is deleted) after pi_lft seconds without activity */
+
+#define PI_PRST_NONE	0	/* the peer entry is deleted after disconnection / error */
+#define PI_PRST_ALWAYS	1	/* the peer entry is persistant (will be kept as ZOMBIE in case of error) */
+			
+/* Information about a remote peer */
+struct peer_info {
+	
+	char * 		pi_diamid;	/* UTF-8, \0 terminated. The Diameter Identity of the remote peer. */
+	
+	struct {
+		struct {
+			unsigned	pro3 :2;	/* PI_P3_* */
+			unsigned	pro4 :2;	/* PI_P4_* */
+			unsigned	alg :1;		/* PI_ALGPREF_* */
+			unsigned	sec :2;		/* PI_SEC_* */
+			unsigned	exp :1;		/* PI_EXP_* */
+			unsigned	persist :1;	/* PI_PRST_* */
+			
+		}		pic_flags;	/* Flags influencing the connection to the remote peer */
+		
+		char * 		pic_realm;	/* If configured, the daemon will match the received realm in CER/CEA matches this. */
+		uint16_t	pic_port; 	/* port to connect to. 0: default. */
+		
+		uint32_t 	pic_lft;	/* lifetime of this peer when inactive (see pic_flags.exp definition) */
+		int		pic_tctimer; 	/* use this value for TcTimer instead of global, if != 0 */
+		int		pic_twtimer; 	/* use this value for TwTimer instead of global, if != 0 */
+		
+		char *		pic_priority;	/* Priority string for GnuTLS if we don't use the default */
+		
+	} config;	/* Configured data (static for this peer entry) */
+	
+	struct {
+		
+		enum peer_state	pir_state;	/* Current state of the peer in the state machine. fd_cpu_flush_cache() might be useful before reading. */
+		
+		char * 		pir_realm;	/* The received realm in CER/CEA. */
+		
+		uint32_t	pir_vendorid;	/* Content of the Vendor-Id AVP, or 0 by default */
+		uint32_t	pir_orstate;	/* Origin-State-Id value */
+		char *		pir_prodname;	/* copy of UTF-8 Product-Name AVP (\0 terminated) */
+		uint32_t	pir_firmrev;	/* Content of the Firmware-Revision AVP */
+		int		pir_relay;	/* The remote peer advertized the relay application */
+		struct fd_list	pir_apps;	/* applications advertised by the remote peer, except relay (pi_flags.relay) */
+		int		pir_isi;	/* Inband-Security-Id advertised (PI_SEC_* bits) */
+		
+		uint32_t	pir_lastDC;	/* The last Disconnect-Cause value received */
+		
+		int		pir_proto;	/* The L4 protocol currently used with the peer (IPPROTO_TCP or IPPROTO_SCTP) */
+		const gnutls_datum_t 	*pir_cert_list; 	/* The (valid) credentials that the peer has presented, or NULL if TLS is not used */
+								/* This is inspired from http://www.gnu.org/software/gnutls/manual/gnutls.html#ex_003ax509_002dinfo 
+								   see there for example of using this data */
+		unsigned int 	pir_cert_list_size;		/* Number of certificates in the list */
+		
+	} runtime;	/* Data populated after connection, may change between 2 connections -- not used by fd_peer_add */
+	
+	struct fd_list	pi_endpoints;	/* Endpoint(s) of the remote peer (configured, discovered, or advertized). list of struct fd_endpoint. DNS resolved if empty. */
+};
+
+
+struct peer_hdr {
+	struct fd_list	 chain;	/* List of all the peers, ordered by their Diameter Id */
+	struct peer_info info;	/* The public data */
+	
+	/* This header is followed by more data in the private peer structure definition */
+};
+
+/* the global list of peers. 
+  Since we are not expecting so many connections, we don't use a hash, but it might be changed.
+  The list items are peer_hdr structures (actually, fd_peer, but the cast is OK) */
+extern struct fd_list fd_g_peers;
+extern pthread_rwlock_t fd_g_peers_rw; /* protect the list */
+
+/*
+ * FUNCTION:	fd_peer_add
+ *
+ * PARAMETERS:
+ *  info 	: Information to create the peer.
+ *  orig_dbg	: A string indicating the origin of the peer information, for debug (ex: conf, redirect, ...)
+ *  cb		: optional, a callback to call (once) when the peer connection is established or failed
+ *  cb_data	: opaque data to pass to the callback.
+ *
+ * DESCRIPTION: 
+ *  Add a peer to the list of peers to which the daemon must maintain a connexion.
+ *
+ *  The content of info parameter is copied, except for the list of endpoints if 
+ * not empty, which is simply moved into the created object. It means that the list
+ * items must have been malloc'd, so that they can be freed.
+ *
+ *  If cb is not null, the callback is called when the connection is in OPEN state or
+ * when an error has occurred. The callback should use the pi_state information to 
+ * determine which one it is. If the first parameter of the called callback is NULL, it 
+ * means that the peer is being destroyed before attempt success / failure. 
+ * cb is called to allow freeing cb_data in  * this case.
+ *
+ *  The orig_dbg string is only useful for easing debug, and can be left to NULL.
+ *
+ * RETURN VALUE:
+ *  0      	: The peer is added.
+ *  EINVAL 	: A parameter is invalid.
+ *  EEXIST 	: A peer with the same Diameter-Id is already in the list.
+ *  (other standard errors may be returned, too, with their standard meaning. Example:
+ *    ENOMEM 	: Memory allocation for the new object element failed.)
+ */
+int fd_peer_add ( struct peer_info * info, char * orig_dbg, void (*cb)(struct peer_info *, void *), void * cb_data );
+
+/*
+ * FUNCTION:	fd_peer_getbyid
+ *
+ * PARAMETERS:
+ *  diamid 	: A \0 terminated string.
+ *  peer	: The peer is stored here if it exists.
+ *
+ * DESCRIPTION: 
+ *   Search a peer by its Diameter-Id.
+ *
+ * RETURN VALUE:
+ *  0   : *peer has been updated (to NULL if the peer is not found).
+ * !0	: An error occurred.
+ */
+int fd_peer_getbyid( char * diamid, struct peer_hdr ** peer );
+
+/*
+ * FUNCTION:	fd_peer_validate_register
+ *
+ * PARAMETERS:
+ *  peer_validate 	: Callback as defined bellow.
+ *
+ * DESCRIPTION: 
+ *  Add a callback to authorize / reject incoming peer connections.
+ * All registered callbacks are called until a callback sets auth = -1 or auth = 1.
+ * If no callback returns a clear decision, the default behavior is applied (reject unknown connections)
+ * The callbacks are called in FILO order of their registration.
+ *
+ * RETURN VALUE:
+ *  0   : The callback is added.
+ * !0	: An error occurred.
+ */
+int fd_peer_validate_register ( int (*peer_validate)(struct peer_info * /* info */, int * /* auth */, int (**cb2)(struct peer_info *)) );
+/*
+ * CALLBACK:	peer_validate
+ *
+ * PARAMETERS:
+ *   info     : Structure containing information about the peer attempting the connection.
+ *   auth     : Store there the result if the peer is accepted (1), rejected (-1), or unknown (0).
+ *   cb2      : If != NULL and in case of PI_SEC_TLS_OLD, another callback to call after handshake (if auth = 1).
+ *
+ * DESCRIPTION: 
+ *   This callback is called when a new connection is being established from an unknown peer,
+ * after the CER is received. An extension must register such callback with peer_validate_register.
+ *
+ *   The callback can learn if the peer has sent Inband-Security-Id AVPs in runtime.pir_isi fields.
+ * It can also learn if a handshake has already been performed in runtime.pir_cert_list field.
+ * The callback must set the value of config.pic_flags.sec appropriately to allow a connection without TLS.
+ *
+ *   If the old TLS mechanism is used,
+ * the extension may also need to check the credentials provided during the TLS
+ * exchange (remote certificate). For this purpose, it may set the address of a new callback
+ * to be called once the handshake is completed. This new callback receives the information
+ * structure as parameter (with pir_cert_list set) and returns 0 if the credentials are correct,
+ * or an error code otherwise. If the error code is received, the connection is closed and the 
+ * peer is destroyed.
+ * Note that freeDiameter already achieves some usual checks. The callback may be used to enforce
+ * additional restrictions.
+ *
+ * RETURN VALUE:
+ *  0      	: The authorization decision has been written in the location pointed by auth.
+ *  !0 		: An error occurred.
+ */
+
+
+
+/*============================================================*/
+/*                         MESSAGES                           */
+/*============================================================*/
+
+/*
+ * FUNCTION:	fd_msg_send, fd_msg_send_timeout  
+ *
+ * PARAMETERS:
+ *  pmsg 	: Location of the message to be sent on the network (set to NULL on function return to avoid double deletion).
+ *  anscb	: A callback to be called when answer is received, if msg is a request (optional for fd_msg_send)
+ *  anscb_data	: opaque data to be passed back to the anscb when it is called.
+ *  timeout     : (only for fd_msg_send_timeout) sets the absolute time until when to wait for an answer. Past this time,
+ *                the anscb is called with the request as parameter and the answer will be discarded when received.
+ *
+ * DESCRIPTION: 
+ *   Sends a message on the network. (actually simply queues it in a global queue, to be picked by a daemon's thread)
+ * For requests, the end-to-end id must be set (see fd_msg_get_eteid / MSGFL_ALLOC_ETEID).
+ * For answers, the message must be created with function fd_msg_new_answ.
+ *
+ * The routing module will handle sending to the correct peer, usually based on the Destination-Realm / Destination-Host AVP.
+ *
+ * If the msg is a request, there are two ways of receiving the answer:
+ *  - either having registered a callback in the dispatch module (see fd_disp_register)
+ *  - or provide a callback as parameter here. If such callback is provided, it is called before the dispatch callbacks.
+ *    The prototype for this callback function is:
+ *     void anscb(void * data, struct msg ** answer)
+ *	where:
+ *		data   : opaque data that was registered along with the callback.
+ *		answer : location of the pointer to the answer.
+ *      note1: on function return, if *answer is not NULL, the message is passed to the dispatch module for regular callbacks.
+ *	       otherwise, the callback must take care of freeing the message (fd_msg_free).
+ *	note2: the opaque data is not freed by the daemon in any case, extensions should ensure clean handling in fd_ext_fini.
+ * 
+ * If no callback is registered to handle an answer, the message is discarded and an error is logged.
+ *
+ *  fd_msg_send_timeout is similar to fd_msg_send, except that it takes an additional argument "timeout" and can be called
+ * only with requests as parameters, and an anscb callback.
+ * If the matching answer or error is received before the timeout date passes, everything occurs as with fd_msg_send. Otherwise,
+ * the request is removed from the queue (meaning the matching answer will be discarded upon reception) and passed to the answcb 
+ * function. This function can easily distinguish between timeout case and answer case by checking if the message received is 
+ * a request. Upon return, if the *msg parameter is not NULL, it is freed (not passed to other callbacks).
+ *
+ * RETURN VALUE:
+ *  0      	: The message has been queued for sending (sending may fail asynchronously).
+ *  EINVAL 	: A parameter is invalid (ex: anscb provided but message is not a request).
+ *  ...
+ */
+int fd_msg_send ( struct msg ** pmsg, void (*anscb)(void *, struct msg **), void * data );
+int fd_msg_send_timeout ( struct msg ** pmsg, void (*anscb)(void *, struct msg **), void * data, const struct timespec *timeout );
+
+/*
+ * FUNCTION:	fd_msg_rescode_set
+ *
+ * PARAMETERS:
+ *  msg		: A msg object -- it must be an answer.
+ *  rescode	: The name of the returned error code (ex: "DIAMETER_INVALID_AVP")
+ *  errormsg    : (optional) human-readable error message to put in Error-Message AVP
+ *  optavp	: (optional) If provided, the content will be put inside a Failed-AVP
+ *  type_id	: 0 => nothing; 1 => adds Origin-Host and Origin-Realm with local info. 2=> adds Error-Reporting-Host.
+ *
+ * DESCRIPTION: 
+ *   This function adds a Result-Code AVP to a message, and optionally
+ *  - sets the 'E' error flag in the header,
+ *  - adds Error-Message, Error-Reporting-Host and Failed-AVP AVPs.
+ *
+ * RETURN VALUE:
+ *  0      	: Operation complete.
+ *  !0      	: an error occurred.
+ */
+int fd_msg_rescode_set( struct msg * msg, char * rescode, char * errormsg, struct avp * optavp, int type_id );
+
+/* Add Origin-Host, Origin-Realm, (if osi) Origin-State-Id AVPS at the end of the message */
+int fd_msg_add_origin ( struct msg * msg, int osi ); 
+
+/* Parse a message against our dictionary, and in case of error log and eventually build the error reply (on return and EBADMSG, *msg == NULL or *msg is the error message ready to send) */
+int fd_msg_parse_or_error( struct msg ** msg );
+
+
+
+
+/*============================================================*/
+/*                         DISPATCH                           */
+/*============================================================*/
+
+/*
+ * FUNCTION:	fd_disp_app_support
+ *
+ * PARAMETERS:
+ *  app		: The dictionary object corresponding to the Application.
+ *  vendor	: (Optional) the dictionary object of a Vendor to claim support in Vendor-Specific-Application-Id
+ *  auth	: Support auth app part.
+ *  acct	: Support acct app part.
+ *
+ * DESCRIPTION: 
+ *   Registers an application to be advertized in CER/CEA exchanges.
+ *  Messages with an application-id matching a registered value are passed to the dispatch module,
+ * while other messages are simply relayed or an error is returned (if local node does not relay)
+ *
+ * RETURN VALUE:
+ *  0      	: The application support is registered.
+ *  EINVAL 	: A parameter is invalid.
+ */
+int fd_disp_app_support ( struct dict_object * app, struct dict_object * vendor, int auth, int acct );
+
+/* Note: if we want to support capabilities updates, we'll have to add possibility to remove an app as well... */
+
+
+/*============================================================*/
+/*                         ROUTING                            */
+/*============================================================*/
+
+/* This file contains the definitions of types and functions involved in the routing decisions in freeDiameter,
+ * and that can be called by extensions. 
+ *
+ * Three different type of messages must be distinguished:
+ *  - Messages received, and the peer is final recipient (IN messages)
+ *  - Messages received, and the peer is not final recipient (FWD messages)
+ *  - Message is locally generated (OUT messages)
+ *
+ * There are three global message queues (in queues.c) and also peers-specific queues (in struct fd_peer).
+ *
+ * (*) IN messages processing details:
+ *   - the message is received from the remote peer, a FDEVP_CNX_MSG_RECV event is generated for the peer.
+ *   - the PSM thread parses the buffer, does some verifications, handles non routable messages (fd_msg_is_routable)
+ *   - routable messages are queued in the fd_g_incoming global queue.
+ *   - a thread (routing-in) picks the message and takes the decision if it is handled locally or forwarded, 
+ *       based on local capabilities (registered by extensions with fd_disp_app_support).
+ *   - If the message is handled locally, it is queued in fd_g_local.
+ *   - Another thread (dispatch.c) will handle this message and pass it to registered callbacks (see fd_disp_register in libfreeDiameter.h).
+ *
+ * (*) FWD messages details:
+ *   - The process is the same as for IN messages, until the routing-in threads makes its decision that the message is not handled locally.
+ *   - If the local peer does not relay message, an error DIAMETER_APPLICATION_UNSUPPORTED is returned.
+ *   - All callbacks registered with fd_rt_fwd_register are called for the message (see bellow).
+ *     - these callbacks will typically do proxying work. Note that adding the route-record is handled by the daemon.
+ *   - Once all callbacks have been called, the message is queued in the global fd_g_outgoing queue.
+ *   - The remaining processing is the same as for OUT messages, as described bellow.
+ *
+ * (*) OUT messages details:
+ *   - The message are picked from fd_g_outgoing (they are queued there as result of forwarding process or call to fd_msg_send.)
+ *   - The (routing-out) thread builds a list of possible destinations for the message, as follow:
+ *      - create a list of all known peers in the "OPEN" state.
+ *      - remove from that list all peers that are in a Route-Record AVP of the message, to avoid routing loops.
+ *      - remove also all peers that have previously replied an error message for this message.
+ *   - If the list is empty, create an error UNABLE_TO_DELIVER (note: should we trig dynamic discovery here???) and reply.
+ *   - Otherwise, call all callbacks registered by function fd_rt_out_register, with the list of peers and the message.
+ *   - Order the resulting list of peers by score (see bellow), and sent the message to the peer with highest (positive) score.
+ *    - in case the peer is no longer in the "OPEN" state, send the message to the second peer in the list.
+ *      - if no peer is in OPEN state anymore, restart the process of creating the list.
+ *   - Once a peer has been selected, the message is queued into that peer's outgoing queue.
+ *
+ * The following functions allow an extension to register or remove a callback as described above.
+ */
+
+/********** Forwarding callbacks: for Proxy operations ***********/
+
+/* Handle to registered callback */
+struct fd_rt_fwd_hdl;
+
+/* Message direction for the callback */
+enum fd_rt_fwd_dir {
+	RT_FWD_REQ = 1,	/* The callback will be called on forwarded requests only */
+	RT_FWD_ALL = 2,	/* The callback will be called on all forwarded messages (requests and answers )*/
+	RT_FWD_ANS = 3	/* The callback will be called on answers and errors only */
+};	
+
+/*
+ * FUNCTION:	fd_rt_fwd_register
+ *
+ * PARAMETERS:
+ *  rt_fwd_cb	  : The callback function to register (see prototype bellow).
+ *  cbdata	  : Pointer to pass to the callback when it is called. The data is opaque to the daemon.
+ *  dir           : One of the RT_FWD_* directions defined above.
+ *  handler       : On success, a handler to the registered callback is stored here. 
+ *		   This handler will be used to unregister the cb.
+ *
+ * DESCRIPTION: 
+ *   Register a new callback for forwarded messages. See explanations above. 
+ * Note that there is no guaranteed order for the callbacks calls.
+ *
+ * RETURN VALUE:
+ *  0      	: The callback is registered.
+ *  EINVAL 	: A parameter is invalid.
+ *  ENOMEM	: Not enough memory to complete the operation
+ */
+int fd_rt_fwd_register ( int (*rt_fwd_cb)(void * cbdata, struct msg ** msg), void * cbdata, enum fd_rt_fwd_dir dir, struct fd_rt_fwd_hdl ** handler );
+/*
+ * CALLBACK:	rt_fwd_cb
+ *
+ * PARAMETERS:
+ *  data	: pointer to some data that was passed when the callback was registered (optional).
+ *  msg 	: The message that is being forwarded.
+ *
+ * DESCRIPTION: 
+ *   This callback is called when a message is forwarded to another peer. It may for example add a Proxy-Info AVP.
+ *  The callback may also choose to handle the message in a more complex form. In that case, it must set *msg = NULL
+ *  and handle it differently. In such case, the forwarding thread will stop processing this message.
+ *
+ * RETURN VALUE:
+ *  0      	: Operation complete.
+ *  !0 		: An error occurred -- will result in daemon's termination.
+ */
+
+/*
+ * FUNCTION:	fd_rt_fwd_unregister
+ *
+ * PARAMETERS:
+ *  handler     : The handler of the callback that must be unregistered.
+ *  cbdata	: Will receive the data registered with the callback, that can be freed if needed.
+ *
+ * DESCRIPTION: 
+ *   Removes a callback from the list of registered callbacks.
+ *
+ * RETURN VALUE:
+ *  0      	: The callback is unregistered.
+ *  EINVAL 	: A parameter is invalid.
+ */
+int fd_rt_fwd_unregister ( struct fd_rt_fwd_hdl * handler, void ** cbdata );
+
+
+/********** Out callbacks: for next hop routing decision operations ***********/
+
+/* Handle to registered callback */
+struct fd_rt_out_hdl;
+
+enum fd_rt_out_score {
+	FD_SCORE_NO_DELIVERY	 = -70,	/* We should not send this message to this candidate */
+	FD_SCORE_INI		 =  -2, /* All candidates are initialized with this value */
+	FD_SCORE_LOAD_BALANCE	 =   1,	/* Use this to differentiate between several peers with the same score */
+	FD_SCORE_DEFAULT	 =   5,	/* The peer is a default route for all messages */
+	FD_SCORE_DEFAULT_REALM	 =  10,	/* The peer is a default route for this realm */
+	FD_SCORE_REALM		 =  15,	/* The peer belongs to Destination-Realm of the message */
+	FD_SCORE_REDIR_HOST	 =  25,	/* If there is a redirect rule with ALL_HOST for these message and peer */
+	FD_SCORE_REDIR_APP	 =  30,	/* If there is a redirect rule with ALL_APPLICATION for these message and peer */
+	FD_SCORE_REDIR_REALM	 =  35,	/* If there is a redirect rule with ALL_REALM for these message and peer */
+	FD_SCORE_REDIR_REALM_APP =  40,	/* If there is a redirect rule with REALM_AND_APPLICATION for these message and peer */
+	FD_SCORE_REDIR_USER	 =  45,	/* If there is a redirect rule with ALL_USER for these message and peer */
+	FD_SCORE_REDIR_SESSION	 =  50,	/* If there is a redirect rule with ALL_SESSION for these message and peer */
+	FD_SCORE_FINALDEST	 = 100	/* If the peer is the final recipient of the message (i.e. matching Destination-Host), it receives a big score. */
+};
+
+/*
+ * FUNCTION:	fd_rt_out_register
+ *
+ * PARAMETERS:
+ *  rt_out_cb	  : The callback function to register (see prototype bellow).
+ *  cbdata	  : Pointer to pass to the callback when it is called. The data is opaque to the daemon.
+ *  priority      : Order for calling this callback. The callbacks are called in reverse priority order (higher priority = called sooner).
+ *  handler       : On success, a handler to the registered callback is stored here. 
+ *		   This handler will be used to unregister the cb.
+ *
+ * DESCRIPTION: 
+ *   Register a new callback to handle OUT routing decisions. See explanations above. 
+ *
+ * RETURN VALUE:
+ *  0      	: The callback is registered.
+ *  EINVAL 	: A parameter is invalid.
+ *  ENOMEM	: Not enough memory to complete the operation
+ */
+int fd_rt_out_register ( int (*rt_out_cb)(void * cbdata, struct msg * msg, struct fd_list * candidates), void * cbdata, int priority, struct fd_rt_out_hdl ** handler );
+/*
+ * CALLBACK:	rt_out_cb
+ *
+ * PARAMETERS:
+ *  cbdata	: pointer to some data that was registered with the callback.
+ *  msg 	: The message that must be sent.
+ *  list        : The list of peers to which the message may be sent to, as returned by fd_rtd_candidate_extract
+ *
+ * DESCRIPTION: 
+ *   This callback must attribute a score (preferably from FD_SCORE_*) to each candidate peer in the list.
+ *  Once all registered callbacks have been called, the message is sent to the candidate with the highest score.
+ *  Note that each callback must *add* its locally-attributed score to the candidate current "score" parameter, not replace it!
+ *  Note also that this callback must be re-entrant since it may be called by several threads at the same time 
+ *  (for different messages)
+ *
+ * RETURN VALUE:
+ *  0      	: Operation complete.
+ *  !0 		: An error occurred.
+ */
+
+/*
+ * FUNCTION:	fd_rt_out_unregister
+ *
+ * PARAMETERS:
+ *  handler     : The handler of the callback that must be unregistered.
+ *  cbdata	: Will receive the data registered with the callback, that can be freed if needed.
+ *
+ * DESCRIPTION: 
+ *   Removes a callback from the list of registered callbacks.
+ *
+ * RETURN VALUE:
+ *  0      	: The callback is unregistered.
+ *  EINVAL 	: A parameter is invalid.
+ */
+int fd_rt_out_unregister ( struct fd_rt_out_hdl * handler, void ** cbdata );
+
+
+/*============================================================*/
+/*                         EVENTS                             */
+/*============================================================*/
+
+struct fd_event {
+	int	 code; /* codespace depends on the queue */
+	size_t 	 size;
+	void    *data;
+};
+
+/* Daemon's codespace: 1000->1999 (1500->1999 defined in fdcore-internal.h) */
+enum {
+	 FDEV_TERMINATE	= 1000	/* request to terminate */
+	,FDEV_DUMP_DICT		/* Dump the content of the dictionary */
+	,FDEV_DUMP_EXT		/* Dump state of extensions */
+	,FDEV_DUMP_SERV		/* Dump the server socket status */
+	,FDEV_DUMP_QUEUES	/* Dump the message queues */
+	,FDEV_DUMP_CONFIG	/* Dump the configuration */
+	,FDEV_DUMP_PEERS	/* Dump the list of peers */
+	,FDEV_TRIGGER		/* Trigger available for extensions. size is sizeof(int), data is int * */
+};
+
+int fd_event_send(struct fifo *queue, int code, size_t datasz, void * data);
+int fd_event_get(struct fifo *queue, int * code, size_t * datasz, void ** data);
+int fd_event_timedget(struct fifo *queue, struct timespec * timeout, int timeoutcode, int * code, size_t * datasz, void ** data);
+void fd_event_destroy(struct fifo **queue, void (*free_cb)(void * data));
+const char * fd_ev_str(int event);
+
+/* for extensions */
+int fd_event_trig_regcb(int trigger_val, const char * module, void (*cb)(void));
+void fd_event_trig_dump();
+
+
+/*============================================================*/
+/*                         ENDPOINTS                          */
+/*============================================================*/
+
+struct fd_endpoint {
+	struct fd_list  chain;	/* link in cnf_endpoints list */
+	
+	union {
+		sSS		ss;	/* the socket information. List is always ordered by ss value (memcmp) -- see fd_ep_add_merge */
+		sSA4		sin;
+		sSA6		sin6;
+		sSA		sa;
+	}
+#ifdef SWIG /* nested anonymous unions are not supported yet */
+			s
+#endif /* SWIG */
+	;
+	
+#define	EP_FL_CONF	(1 << 0)	/* This endpoint is statically configured in a configuration file */
+#define	EP_FL_DISC	(1 << 1)	/* This endpoint was resolved from the Diameter Identity or other DNS query */
+#define	EP_FL_ADV	(1 << 2)	/* This endpoint was advertized in Diameter CER/CEA exchange */
+#define	EP_FL_LL	(1 << 3)	/* Lower layer mechanism provided this endpoint */
+#define	EP_FL_PRIMARY	(1 << 4)	/* This endpoint is primary in a multihomed SCTP association */
+#define	EP_ACCEPTALL	(1 << 15)	/* This flag allows bypassing the address filter in fd_ep_add_merge. */
+	uint32_t	flags;		/* Additional information about the endpoint */
+		
+	/* To add: a validity timestamp for DNS records ? How do we retrieve this lifetime from DNS ? */
+};
+
+int fd_ep_add_merge( struct fd_list * list, sSA * sa, socklen_t sl, uint32_t flags );
+int fd_ep_filter( struct fd_list * list, uint32_t flags );
+int fd_ep_filter_family( struct fd_list * list, int af );
+int fd_ep_filter_list( struct fd_list * list, struct fd_list * exclude_list );
+int fd_ep_clearflags( struct fd_list * list, uint32_t flags );
+void fd_ep_dump_one( char * prefix, struct fd_endpoint * ep, char * suffix );
+void fd_ep_dump( int indent, struct fd_list * eps );
+
+
+/*============================================================*/
+/*                         APPLICATIONS IDs                   */
+/*============================================================*/
+
+struct fd_app {
+	struct fd_list	 chain;	/* link in cnf_apps list. List ordered by appid. */
+	struct {
+		unsigned auth   : 1;
+		unsigned acct   : 1;
+	}		 flags;
+	vendor_id_t	 vndid; /* if not 0, Vendor-Specific-App-Id AVP will be used */
+	application_id_t appid;	/* The identifier of the application */
+};
+	
+int fd_app_merge(struct fd_list * list, application_id_t aid, vendor_id_t vid, int auth, int acct);
+int fd_app_check(struct fd_list * list, application_id_t aid, struct fd_app **detail);
+int fd_app_check_common(struct fd_list * list1, struct fd_list * list2, int * common_found);
+int fd_app_empty(struct fd_list * list);
+
+#endif /* _LIBFDCORE_H */
Index: include/freeDiameter/libfdproto.h
===================================================================
--- include/freeDiameter/libfdproto.h	(revision 658)
+++ include/freeDiameter/libfdproto.h	(revision 658)
@@ -0,0 +1,2794 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@nict.go.jp>							 *
+*													 *
+* 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 S_OUT OF THE USE OF THIS SOFTWARE, EVEN IF   *
+* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.								 *
+*********************************************************************************************************/
+
+/* This file contains the definitions of functions and types used by the libfreeDiameter library.
+ *
+ * This library is meant to be used by both the freeDiameter daemon and its extensions.
+ * It provides the tools to manipulate Diameter messages and related data.
+ * This file should always be included as #include <freeDiameter/libfreeDiameter.h>
+ *
+ * If any change is made to this file, you must increment the FD_PROJECT_VERSION_API version.
+ *
+ * The file contains the following parts:
+ *	DEBUG
+ *	MACROS
+ *	THREADS
+ *	LISTS
+ *	DICTIONARY
+ *	SESSIONS
+ *	MESSAGES
+ *	DISPATCH
+ *	QUEUES
+ */
+
+#ifndef _LIBFDPROTO_H
+#define _LIBFDPROTO_H
+
+#ifndef FD_IS_CONFIG
+#error "You must include 'freeDiameter-host.h' before this file."
+#endif /* FD_IS_CONFIG */
+
+#include <pthread.h>
+#include <sched.h>
+#include <string.h>
+#include <assert.h>
+#include <errno.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#ifdef DEBUG
+#include <libgen.h>	/* for basename if --dbg_file is specified */
+#endif /* DEBUG */
+
+/*============================================================*/
+/*                          INIT                              */
+/*============================================================*/
+
+/* This function must be called first, before any call to another library function */
+int fd_libproto_init(void); /* note if you are using libfdcore, it handles this already */
+
+/* Call this one when the application terminates, to destroy internal threads */
+void fd_libproto_fini(void);
+
+
+/*============================================================*/
+/*                          DEBUG                             */
+/*============================================================*/
+
+
+/*
+ * FUNCTION:	fd_log_debug
+ *
+ * PARAMETERS:
+ *  format 	: Same format string as in the printf function
+ *  ...		: Same list as printf
+ *
+ * DESCRIPTION: 
+ *  Log internal information for use of developpers only.
+ * The format and arguments may contain UTF-8 encoded data. The
+ * output medium (file or console) is expected to support this encoding.
+ *
+ * This function assumes that a global mutex called "fd_log_lock" exists
+ * in the address space of the current process.
+ *
+ * RETURN VALUE:
+ *  None.
+ */
+void fd_log_debug ( const char * format, ... );
+extern pthread_mutex_t	fd_log_lock;
+extern char * fd_debug_one_function;
+extern char * fd_debug_one_file;
+
+/*
+ * FUNCTION:	fd_log_threadname
+ *
+ * PARAMETERS:
+ *  name 	: \0-terminated string containing a name to identify the current thread.
+ *
+ * DESCRIPTION: 
+ *  Name the current thread, useful for debugging multi-threaded problems.
+ *
+ * This function assumes that a global thread-specific key called "fd_log_thname" exists
+ * in the address space of the current process.
+ *
+ * RETURN VALUE:
+ *  None.
+ */
+void fd_log_threadname ( char * name );
+extern pthread_key_t	fd_log_thname;
+
+/*
+ * FUNCTION:	fd_log_time
+ *
+ * PARAMETERS:
+ *  ts	 	: The timestamp to log, or NULL for "now"
+ *  buf 	: An array where the time must be stored
+ *  len		: size of the buffer
+ *
+ * DESCRIPTION: 
+ *  Writes the timestamp (in human readable format) in a buffer. 
+ *
+ * RETURN VALUE:
+ *  pointer to buf.
+ */
+char * fd_log_time ( struct timespec * ts, char * buf, size_t len );
+
+
+/*============================================================*/
+/*                    DEBUG MACROS                            */
+/*============================================================*/
+
+#ifndef ASSERT
+#define ASSERT(x) assert(x)
+#endif /* ASSERT */
+
+/* levels definitions */
+#define NONE 0	/* Display no debug message */
+#define INFO 1	/* Display errors only */
+#define FULL 2  /* Display additional information to follow code execution */
+#define ANNOYING 4 /* Very verbose, for example in loops */
+#define FCTS 6  /* Display entry parameters of most functions */
+#define CALL 9  /* Display calls to most functions (with CHECK macros) */
+
+/* Default level is INFO */
+#ifndef TRACE_LEVEL 
+#define TRACE_LEVEL INFO
+#endif /* TRACE_LEVEL */
+
+/* The level of the file being compiled. */
+static int local_debug_level = TRACE_LEVEL;
+
+/* A global level, changed by configuration or cmd line for example. default is 0. */
+extern int fd_g_debug_lvl;
+
+/* Some portability code to get nice function name in __PRETTY_FUNCTION__ */
+#if __STDC_VERSION__ < 199901L
+# if __GNUC__ >= 2
+#  define __func__ __FUNCTION__
+# else /* __GNUC__ >= 2 */
+#  define __func__ "<unknown>"
+# endif /* __GNUC__ >= 2 */
+#endif /* __STDC_VERSION__ < 199901L */
+#ifndef __PRETTY_FUNCTION__
+#define __PRETTY_FUNCTION__ __func__
+#endif /* __PRETTY_FUNCTION__ */
+
+/* A version of __FILE__ without the full path */
+static char * file_bname = NULL;
+#define __STRIPPED_FILE__	(file_bname ?: (file_bname = basename((char *)__FILE__)))
+
+
+/* Boolean for tracing at a certain level */
+#ifdef DEBUG
+#define TRACE_BOOL(_level_) ( ((_level_) <= local_debug_level + fd_g_debug_lvl) 					\
+				|| (fd_debug_one_function && !strcmp(fd_debug_one_function, __PRETTY_FUNCTION__)) 	\
+				|| (fd_debug_one_file && !strcmp(fd_debug_one_file, __STRIPPED_FILE__) ) )
+#else /* DEBUG */
+#define TRACE_BOOL(_level_) ((_level_) <= local_debug_level + fd_g_debug_lvl)
+#endif /* DEBUG */
+
+
+/*************
+ The general debug macro, each call results in two lines of debug messages (change the macro for more compact output) 
+ *************/
+#ifdef DEBUG
+/* In DEBUG mode, we add (a lot of) meta-information along each trace. This makes multi-threading problems easier to debug. */
+#define TRACE_DEBUG(level,format,args... ) {											\
+	if ( TRACE_BOOL(level) ) {												\
+		char __buf[25];													\
+		const char * __thn = ((char *)pthread_getspecific(fd_log_thname) ?: "unnamed");					\
+		fd_log_debug("\t | tid:%-20s\t%s\tin %s@%s:%d\n"								\
+			  "\t%s|%*s" format "\n",  										\
+					__thn, fd_log_time(NULL, __buf, sizeof(__buf)), __PRETTY_FUNCTION__, __FILE__, __LINE__,\
+					(level < FULL)?"@":" ",level, "", ## args); 						\
+	}															\
+}
+#else /* DEBUG */
+/* Do not print thread, function, ... only the message itself in this case, unless the debug level is set > FULL. */
+#define TRACE_DEBUG(level,format,args... ) {												\
+	if ( TRACE_BOOL(level) ) {													\
+		if (fd_g_debug_lvl > FULL) {												\
+			char __buf[25];													\
+			const char * __thn = ((char *)pthread_getspecific(fd_log_thname) ?: "unnamed");					\
+			fd_log_debug("\t | tid:%-20s\t%s\tin %s@%s:%d\n"								\
+				  "\t%s|%*s" format "\n",  										\
+						__thn, fd_log_time(NULL, __buf, sizeof(__buf)), __PRETTY_FUNCTION__, __FILE__, __LINE__,\
+						(level < FULL)?"@":" ",level, "", ## args); 						\
+		} else {														\
+			fd_log_debug(format "\n", ## args); 										\
+		}															\
+	}																\
+}
+#endif /* DEBUG */
+
+/*************
+ Derivatives from this macro 
+ ************/
+/* Helper for function entry -- for very detailed trace of the execution */
+#define TRACE_ENTRY(_format,_args... ) \
+	TRACE_DEBUG(FCTS, "[enter] %s(" _format ") {" #_args "}", __PRETTY_FUNCTION__, ##_args );
+
+/* Helper for debugging by adding traces -- for debuging a specific location of the code */
+#define TRACE_HERE()	\
+	TRACE_DEBUG(NONE, " -- debug checkpoint %d -- ", fd_breakhere());
+int fd_breakhere(void);
+
+/* Helper for tracing the CHECK_* macros bellow -- very very verbose code execution! */
+#define TRACE_DEBUG_ALL( str ) 	\
+	TRACE_DEBUG(CALL, str );
+
+/* For development only, to keep track of TODO locations in the code */
+#ifndef ERRORS_ON_TODO
+#define TODO( _msg, _args... ) \
+	TRACE_DEBUG(NONE, "TODO: " _msg , ##_args);
+#else /* ERRORS_ON_TODO */
+#define TODO( _msg, _args... ) \
+	"TODO" = _msg ## _args; /* just a stupid compilation error to spot the todo */
+#endif /* ERRORS_ON_TODO */
+
+/* Trace a binary buffer content */
+#define TRACE_DEBUG_BUFFER(level, prefix, buf, bufsz, suffix ) {								\
+	if ( TRACE_BOOL(level) ) {												\
+		char __ts[25];													\
+		int __i;													\
+		size_t __sz = (size_t)(bufsz);											\
+		uint8_t * __buf = (uint8_t *)(buf);										\
+		char * __thn = ((char *)pthread_getspecific(fd_log_thname) ?: "unnamed");					\
+		fd_log_debug("\t | tid:%-20s\t%s\tin %s@%s:%d\n"								\
+			  "\t%s|%*s" prefix ,  											\
+					__thn, fd_log_time(NULL, __ts, sizeof(__ts)), __PRETTY_FUNCTION__, __FILE__, __LINE__,	\
+					(level < FULL)?"@":" ",level, ""); 							\
+		for (__i = 0; __i < __sz; __i++) {										\
+			fd_log_debug("%02.2hhx", __buf[__i]);									\
+		}														\
+		fd_log_debug(suffix "\n");											\
+	}															\
+}
+
+/* Some aliases to socket addresses structures */
+#define sSS	struct sockaddr_storage
+#define sSA	struct sockaddr
+#define sSA4	struct sockaddr_in
+#define sSA6	struct sockaddr_in6
+
+/* The sockaddr length of a sSS structure */
+#define sSAlen( _sa_ )	\
+	( (socklen_t) ( (((sSA *)_sa_)->sa_family == AF_INET) ? (sizeof(sSA4)) :		\
+				((((sSA *)_sa_)->sa_family == AF_INET6) ? (sizeof(sSA6)) :	\
+					0 ) ) )
+
+/* Dump one sockaddr Node information */
+#define sSA_DUMP_NODE( sa, flag ) {				\
+	sSA * __sa = (sSA *)(sa);				\
+	char __addrbuf[INET6_ADDRSTRLEN];			\
+	if (__sa) {						\
+	  int __rc = getnameinfo(__sa, 				\
+	  		sSAlen(__sa),				\
+			__addrbuf,				\
+			sizeof(__addrbuf),			\
+			NULL,					\
+			0,					\
+			flag);					\
+	  if (__rc)						\
+	  	fd_log_debug("%s", (char *)gai_strerror(__rc));	\
+	  else							\
+	  	fd_log_debug("%s", &__addrbuf[0]);		\
+	} else {						\
+		fd_log_debug("(NULL / ANY)");			\
+	}							\
+}
+/* Same but with the port (service) also */
+#define sSA_DUMP_NODE_SERV( sa, flag ) {				\
+	sSA * __sa = (sSA *)(sa);					\
+	char __addrbuf[INET6_ADDRSTRLEN];				\
+	char __servbuf[32];						\
+	if (__sa) {							\
+	  int __rc = getnameinfo(__sa, 					\
+	  		sSAlen(__sa),					\
+			__addrbuf,					\
+			sizeof(__addrbuf),				\
+			__servbuf,					\
+			sizeof(__servbuf),				\
+			flag);						\
+	  if (__rc)							\
+	  	fd_log_debug("%s", (char *)gai_strerror(__rc));		\
+	  else								\
+	  	fd_log_debug("[%s]:%s", &__addrbuf[0],&__servbuf[0]);	\
+	} else {							\
+		fd_log_debug("(NULL / ANY)");				\
+	}								\
+}
+
+/* Inside a debug trace */
+#define TRACE_DEBUG_sSA(level, prefix, sa, flags, suffix ) {										\
+	if ( TRACE_BOOL(level) ) {												\
+		char __buf[25];													\
+		char * __thn = ((char *)pthread_getspecific(fd_log_thname) ?: "unnamed");					\
+		fd_log_debug("\t | tid:%-20s\t%s\tin %s@%s:%d\n"								\
+			  "\t%s|%*s" prefix ,  											\
+					__thn, fd_log_time(NULL, __buf, sizeof(__buf)), __PRETTY_FUNCTION__, __FILE__, __LINE__,\
+					(level < FULL)?"@":" ",level, ""); 							\
+		sSA_DUMP_NODE_SERV( sa, flags );										\
+		fd_log_debug(suffix "\n");											\
+	}															\
+}
+
+/* Report an error */
+#define TRACE_DEBUG_ERROR(format,args... ) \
+	TRACE_DEBUG(NONE, format, ##args)
+
+/******************
+ Optimized code: remove all debugging code
+ **/
+#ifdef STRIP_DEBUG_CODE
+#undef TRACE_DEBUG
+#undef TRACE_BOOL
+#undef TRACE_DEBUG_sSA
+#undef TRACE_DEBUG_BUFFER
+#undef TRACE_DEBUG_ERROR
+#define TRACE_DEBUG(level,format,args... )
+#define TRACE_BOOL(_level_) (0)
+#define TRACE_DEBUG_BUFFER(level, prefix, buf, bufsz, suffix )
+#define TRACE_DEBUG_sSA(level, prefix, sa, flags, suffix )
+#define TRACE_DEBUG_ERROR(format,args... ) {	\
+	fd_log_debug(format "\n", ## args); 	\
+}
+#endif /* STRIP_DEBUG_CODE */
+
+
+/*============================================================*/
+/*                  ERROR CHECKING MACRO                      */
+/*============================================================*/
+
+/* Macros to check a return value and branch out in case of error.
+ * These macro should be used only when errors are improbable, not for expected errors.
+ */
+
+/* Check the return value of a system function and execute fallback in case of error */
+#define CHECK_SYS_DO( __call__, __fallback__  ) { 					\
+	int __ret__;									\
+	TRACE_DEBUG_ALL( "Check SYS: " #__call__ );					\
+	__ret__ = (__call__);								\
+	if (__ret__ < 0) {								\
+		int __err__ = errno;	/* We may handle EINTR here */			\
+		TRACE_DEBUG_ERROR("ERROR: in '" #__call__ "' :\t%s", strerror(__err__));\
+		__fallback__;								\
+	}										\
+}
+/* Check the return value of a system function, return error code on error */
+#define CHECK_SYS( __call__  ) { 							\
+	int __ret__;									\
+	TRACE_DEBUG_ALL( "Check SYS: " #__call__ );					\
+	__ret__ = (__call__);								\
+	if (__ret__ < 0) {								\
+		int __err__ = errno;	/* We may handle EINTR here */			\
+		TRACE_DEBUG_ERROR("ERROR: in '" #__call__ "' :\t%s", strerror(__err__));\
+		return __err__;								\
+	}										\
+}
+
+/* Check the return value of a POSIX function and execute fallback in case of error or special value */
+#define CHECK_POSIX_DO2( __call__, __speval__, __fallback1__, __fallback2__ ) {			\
+	int __ret__;										\
+	TRACE_DEBUG_ALL( "Check POSIX: " #__call__ );						\
+	__ret__ = (__call__);									\
+	if (__ret__ != 0) {									\
+		if (__ret__ == (__speval__)) {							\
+			__fallback1__;								\
+		} else {									\
+			TRACE_DEBUG_ERROR("ERROR: in '" #__call__ "':\t%s", strerror(__ret__));	\
+			__fallback2__;								\
+		}										\
+	}											\
+}
+
+/* Check the return value of a POSIX function and execute fallback in case of error */
+#define CHECK_POSIX_DO( __call__, __fallback__ ) 					\
+	CHECK_POSIX_DO2( (__call__), 0, , __fallback__ );
+
+/* Check the return value of a POSIX function and return it if error */
+#define CHECK_POSIX( __call__ ) { 							\
+	int __v__;									\
+	CHECK_POSIX_DO( __v__ = (__call__), return __v__ );				\
+}
+
+/* Check that a memory allocator did not return NULL, otherwise log an error and execute fallback */
+#define CHECK_MALLOC_DO( __call__, __fallback__ ) { 					\
+	void *  __ret__;								\
+	TRACE_DEBUG_ALL( "Check MALLOC: " #__call__ );					\
+	__ret__ = (void *)( __call__ );							\
+	if (__ret__ == NULL) {								\
+		int __err__ = errno;							\
+		TRACE_DEBUG_ERROR("ERROR: in '" #__call__ "':\t%s", strerror(__err__));	\
+		__fallback__;								\
+	}										\
+}
+
+/* Check that a memory allocator did not return NULL, otherwise return ENOMEM */
+#define CHECK_MALLOC( __call__ )							\
+	CHECK_MALLOC_DO( __call__, return ENOMEM );
+
+
+/* Check parameters at function entry, execute fallback on error */
+#define CHECK_PARAMS_DO( __bool__, __fallback__ )						\
+	TRACE_DEBUG_ALL( "Check PARAMS: " #__bool__ );						\
+	if ( ! (__bool__) ) {									\
+		TRACE_DEBUG_ERROR("Warning: Invalid parameter received in '" #__bool__ "'");	\
+		__fallback__;									\
+	}
+/* Check parameters at function entry, return EINVAL if the boolean is false (similar to assert) */
+#define CHECK_PARAMS( __bool__ )							\
+	CHECK_PARAMS_DO( __bool__, return EINVAL );
+
+/* Check the return value of an internal function, log and propagate */
+#define CHECK_FCT_DO( __call__, __fallback__ ) {					\
+	int __ret__;									\
+	TRACE_DEBUG_ALL( "Check FCT: " #__call__ );					\
+	__ret__ = (__call__);								\
+	if (__ret__ != 0) {								\
+		TRACE_DEBUG_ERROR("ERROR: in '" #__call__ "':\t%s", strerror(__ret__));	\
+		__fallback__;								\
+	}										\
+}
+/* Check the return value of a function call, return any error code */
+#define CHECK_FCT( __call__ ) {								\
+	int __v__;									\
+	CHECK_FCT_DO( __v__ = (__call__), return __v__ );				\
+}
+
+
+
+/*============================================================*/
+/*                  OTHER MACROS                              */
+/*============================================================*/
+
+
+/* helper macros (pre-processor hacks to allow macro arguments) */
+#define __tostr( arg )  #arg
+#define _stringize( arg ) __tostr( arg )
+#define __agr( arg1, arg2 ) arg1 ## arg2
+#define _aggregate( arg1, arg2 ) __agr( arg1, arg2 )
+
+
+/* A l4 protocol name (TCP / SCTP) */
+#ifdef DISABLE_SCTP
+#define IPPROTO_NAME( _proto )					\
+	(((_proto) == IPPROTO_TCP) ? "TCP" :			\
+			"Unknown")
+#else /* DISABLE_SCTP */
+#define IPPROTO_NAME( _proto )					\
+	( ((_proto) == IPPROTO_TCP) ? "TCP" :			\
+		(((_proto) == IPPROTO_SCTP) ? "SCTP" :		\
+			"Unknown"))
+#endif /* DISABLE_SCTP */
+
+/* Define the value of IP loopback address */
+#ifndef INADDR_LOOPBACK 
+#define INADDR_LOOPBACK	inet_addr("127.0.0.1")
+#endif /* INADDR_LOOPBACK */
+
+#ifndef INADDR_BROADCAST
+#define	INADDR_BROADCAST	((in_addr_t) 0xffffffff)
+#endif /* INADDR_BROADCAST */
+
+/* An IP equivalent to IN6_IS_ADDR_LOOPBACK */
+#ifndef IN_IS_ADDR_LOOPBACK
+#define IN_IS_ADDR_LOOPBACK(a) \
+  ((((long int) (a)->s_addr) & ntohl(0xff000000)) == ntohl(0x7f000000))
+#endif /* IN_IS_ADDR_LOOPBACK */
+
+/* An IP equivalent to IN6_IS_ADDR_UNSPECIFIED */
+#ifndef IN_IS_ADDR_UNSPECIFIED
+#define IN_IS_ADDR_UNSPECIFIED(a) \
+  (((long int) (a)->s_addr) == 0x00000000)
+#endif /* IN_IS_ADDR_UNSPECIFIED */
+
+/* create a V4MAPPED address */
+#define IN6_ADDR_V4MAP( a6, a4 ) {			\
+	((uint32_t *)(a6))[0] = 0;			\
+	((uint32_t *)(a6))[1] = 0;			\
+	((uint32_t *)(a6))[2] = htonl(0xffff);		\
+	((uint32_t *)(a6))[3] = (uint32_t)(a4);		\
+}
+
+/* Retrieve a v4 value from V4MAPPED address ( takes a s6_addr as param) */
+#define IN6_ADDR_V4UNMAP( a6 ) 				\
+	(((in_addr_t *)(a6))[3])
+
+
+/* We provide macros to convert 64 bit values to and from network byte-order, on systems where it is not already provided. */
+#ifndef HAVE_NTOHLL	/* Defined by the cmake step, if the ntohll symbol is defined on the system */
+# if HOST_BIG_ENDIAN
+    /* In big-endian systems, we don't have to change the values, since the order is the same as network */
+#   define ntohll(x) (x)
+#   define htonll(x) (x)
+# else /* HOST_BIG_ENDIAN */
+    /* For these systems, we must reverse the bytes. Use ntohl and htonl on sub-32 blocs, and inverse these blocs. */
+#   define ntohll(x) (typeof (x))( (((uint64_t)ntohl( (uint32_t)(x))) << 32 ) | ((uint64_t) ntohl( ((uint64_t)(x)) >> 32 ))) 
+#   define htonll(x) (typeof (x))( (((uint64_t)htonl( (uint32_t)(x))) << 32 ) | ((uint64_t) htonl( ((uint64_t)(x)) >> 32 ))) 
+# endif /* HOST_BIG_ENDIAN */
+#endif /* HAVE_NTOHLL */
+
+/* This macro will give the next multiple of 4 for an integer (used for padding sizes of AVP). */
+#define PAD4(_x) ((_x) + ( (4 - (_x)) & 3 ) )
+
+/* Useful to display any value as (safe) ASCII (will garbage UTF-8 output...) */
+#define ASCII(_c) ( ((_c < 32) || (_c > 127)) ? ( _c ? '?' : ' ' ) : _c )
+
+/* Compare timespec structures */
+#define TS_IS_INFERIOR( ts1, ts2 ) 		\
+	(    ((ts1)->tv_sec  < (ts2)->tv_sec ) 	\
+	  || (((ts1)->tv_sec  == (ts2)->tv_sec ) && ((ts1)->tv_nsec < (ts2)->tv_nsec) ))
+
+/* This gives a good size for buffered reads */
+#ifndef BUFSIZ
+#define BUFSIZ 96
+#endif /* BUFSIZ */
+
+
+
+/*============================================================*/
+/*                          THREADS                           */
+/*============================================================*/
+
+/* Terminate a thread */
+static __inline__ int fd_thr_term(pthread_t * th)
+{
+	void * th_ret = NULL;
+	
+	CHECK_PARAMS(th);
+	
+	/* Test if it was already terminated */
+	if (*th == (pthread_t)NULL)
+		return 0;
+	
+	/* Cancel the thread if it is still running - ignore error if it was already terminated */
+	(void) pthread_cancel(*th);
+	
+	/* Then join the thread */
+	CHECK_POSIX( pthread_join(*th, &th_ret) );
+	
+	if (th_ret == PTHREAD_CANCELED) {
+		TRACE_DEBUG(ANNOYING, "The thread %p was canceled", *th);
+	} else {
+		TRACE_DEBUG(CALL, "The thread %p returned %x", *th, th_ret);
+	}
+	
+	/* Clean the location */
+	*th = (pthread_t)NULL;
+	
+	return 0;
+}
+
+/* Force flushing the cache of a CPU before reading a shared memory area (use only for atomic reads such as int and void*) */
+extern pthread_mutex_t fd_cpu_mtx_dummy; /* only for the macro bellow, so that we have reasonably fresh pir_state value when needed */
+#define fd_cpu_flush_cache() {				\
+	(void)pthread_mutex_lock(&fd_cpu_mtx_dummy);	\
+	(void)pthread_mutex_unlock(&fd_cpu_mtx_dummy);	\
+}
+
+
+/*************
+ Cancelation cleanup handlers for common objects 
+ *************/
+static __inline__ void fd_cleanup_mutex( void * mutex )
+{
+	CHECK_POSIX_DO( pthread_mutex_unlock((pthread_mutex_t *)mutex), /* */);
+}
+		
+static __inline__ void fd_cleanup_rwlock( void * rwlock )
+{
+	CHECK_POSIX_DO( pthread_rwlock_unlock((pthread_rwlock_t *)rwlock), /* */);
+}
+
+static __inline__ void fd_cleanup_buffer( void * buffer )
+{
+	free(buffer);
+}
+static __inline__ void fd_cleanup_socket(void * sockptr)
+{
+	if (sockptr && (*(int *)sockptr > 0)) {
+		CHECK_SYS_DO( close(*(int *)sockptr), /* ignore */ );
+		*(int *)sockptr = -1;
+	}
+}
+
+
+/*============================================================*/
+/*                          LISTS                             */
+/*============================================================*/
+
+/* The following structure represents a chained list element  */
+struct fd_list {
+	struct fd_list 	*next; /* next element in the list */
+	struct fd_list 	*prev; /* previous element in the list */
+	struct fd_list 	*head; /* head of the list */
+	void		*o;    /* additional pointer, used for any purpose (ex: start of the parent object) */
+};
+
+/* Initialize a list element */
+#define FD_LIST_INITIALIZER( _list_name ) \
+	{ .next = & _list_name, .prev = & _list_name, .head = & _list_name, .o = NULL }
+#define FD_LIST_INITIALIZER_O( _list_name, _obj ) \
+	{ .next = & _list_name, .prev = & _list_name, .head = & _list_name, .o = _obj }
+void fd_list_init ( struct fd_list * list, void * obj );
+
+/* Return boolean, true if the list is empty */
+#define FD_IS_LIST_EMPTY( _list ) ((((struct fd_list *)(_list))->head == (_list)) && (((struct fd_list *)(_list))->next == (_list)))
+
+/* Insert an item in a list at known position */
+void fd_list_insert_after  ( struct fd_list * ref, struct fd_list * item );
+void fd_list_insert_before ( struct fd_list * ref, struct fd_list * item );
+
+/* Move all elements from a list at the end of another */
+void fd_list_move_end(struct fd_list * ref, struct fd_list * senti);
+
+/* Insert an item in an ordered list -- ordering function must be provided. If duplicate object found, EEXIST and it is returned in ref_duplicate */
+int fd_list_insert_ordered( struct fd_list * head, struct fd_list * item, int (*cmp_fct)(void *, void *), void ** ref_duplicate);
+
+/* Unlink an item from a list */
+void fd_list_unlink ( struct fd_list * item );
+
+
+
+/*============================================================*/
+/*                          HASH                              */
+/*============================================================*/
+
+/* Compute a hash value of a string (session id, diameter id, ...) */
+uint32_t fd_hash ( char * string, size_t len );
+
+
+
+/*============================================================*/
+/*                        DICTIONARY                          */
+/*============================================================*/
+
+/* Structure that contains the complete dictionary definitions */
+struct dictionary;
+
+/* Structure that contains a dictionary object */
+struct dict_object;
+
+/* Types of object in the dictionary. */
+enum dict_object_type {
+	DICT_VENDOR	= 1,	/* Vendor */
+	DICT_APPLICATION,	/* Diameter Application */
+	DICT_TYPE,		/* AVP data type */
+	DICT_ENUMVAL,		/* Named constant (value of an enumerated AVP type) */
+	DICT_AVP,		/* AVP */
+	DICT_COMMAND,		/* Diameter Command */
+	DICT_RULE		/* a Rule for AVP in command or grouped AVP */
+#define DICT_TYPE_MAX	DICT_RULE
+};
+	
+/* Initialize a dictionary */
+int fd_dict_init(struct dictionary ** dict);
+/* Destroy a dictionary */
+int fd_dict_fini(struct dictionary ** dict);
+
+/*
+ * FUNCTION:	fd_dict_new
+ *
+ * PARAMETERS:
+ *  dict	: Pointer to the dictionnary where the object is created
+ *  type 	: What kind of object must be created
+ *  data 	: pointer to the data for the object. 
+ *          	 type parameter is used to determine the type of data (see bellow for detail).
+ *  parent 	: a reference to a parent object, if needed.
+ *  ref 	: upon successful creation, reference to new object is stored here if !null.
+ *
+ * DESCRIPTION: 
+ *  Create a new object in the dictionary. 
+ *  See following object sections in this header file for more information on data and parent parameters format.
+ *
+ * RETURN VALUE:
+ *  0      	: The object is created in the dictionary.
+ *  EINVAL 	: A parameter is invalid.
+ *  EEXIST 	: This object is already defined in the dictionary (with conflicting data). 
+ *                If "ref" is not NULL, it points to the existing element on return.
+ *  (other standard errors may be returned, too, with their standard meaning. Example:
+ *    ENOMEM 	: Memory allocation for the new object element failed.)
+ */
+int fd_dict_new ( struct dictionary * dict, enum dict_object_type type, void * data, struct dict_object * parent, struct dict_object ** ref );
+
+/*
+ * FUNCTION: 	fd_dict_search
+ *
+ * PARAMETERS:
+ *  dict	: Pointer to the dictionnary where the object is searched
+ *  type 	: type of object that is being searched
+ *  criteria 	: how the object must be searched. See object-related sections bellow for more information.
+ *  what 	: depending on criteria, the data that must be searched.
+ *  result 	: On successful return, pointer to the object is stored here.
+ *  retval	: this value is returned if the object is not found and result is not NULL.
+ *
+ * DESCRIPTION: 
+ *   Perform a search in the dictionary. 
+ *   See the object-specific sections bellow to find how to look for each objects.
+ *   If the "result" parameter is NULL, the function is used to check if an object is in the dictionary.
+ *   Otherwise, a reference to the object is stored in result if found.
+ *   If result is not NULL and the object is not found, retval is returned (should be 0 or ENOENT usually)
+ *
+ * RETURN VALUE:
+ *  0      	: The object has been found in the dictionary, or *result is NULL.
+ *  EINVAL 	: A parameter is invalid.
+ *  ENOENT	: No matching object has been found, and result was NULL.
+ */
+int fd_dict_search ( struct dictionary * dict, enum dict_object_type type, int criteria, void * what, struct dict_object ** result, int retval );
+
+/* Special case: get the generic error command object */
+int fd_dict_get_error_cmd(struct dictionary * dict, struct dict_object ** obj);
+
+/*
+ * FUNCTION:	fd_dict_getval
+ *
+ * PARAMETERS:
+ *  object 	: Pointer to a dictionary object.
+ *  data 	: pointer to a structure to hold the data for the object.
+ *          	  The type is the same as "data" parameter in fd_dict_new function.
+ *
+ * DESCRIPTION: 
+ *  Retrieve content of a dictionary object.
+ *  See following object sections in this header file for more information on data and parent parameters format.
+ *
+ * RETURN VALUE:
+ *  0      	: The content of the object has been retrieved.
+ *  EINVAL 	: A parameter is invalid.
+ */
+int fd_dict_getval ( struct dict_object * object, void * val);
+int fd_dict_gettype ( struct dict_object * object, enum dict_object_type * type);
+int fd_dict_getdict ( struct dict_object * object, struct dictionary ** dict);
+
+/* Debug functions */
+void fd_dict_dump_object(struct dict_object * obj);
+void fd_dict_dump(struct dictionary * dict);
+
+/*
+ ***************************************************************************
+ *
+ * Vendor object 
+ *
+ * These types are used to manage vendors in the dictionary
+ *
+ ***************************************************************************
+ */
+
+/* Type to hold a Vendor ID: "SMI Network Management Private Enterprise Codes" (RFC3232) */
+typedef uint32_t	vendor_id_t;
+
+/* Type to hold data associated to a vendor */
+struct dict_vendor_data {
+	vendor_id_t	 vendor_id;	/* ID of a vendor */
+	char 		*vendor_name;	/* The name of this vendor */
+};
+
+/* The criteria for searching a vendor object in the dictionary */
+enum {
+	VENDOR_BY_ID = 10,	/* "what" points to a vendor_id_t */
+	VENDOR_BY_NAME,		/* "what" points to a string */
+	VENDOR_OF_APPLICATION	/* "what" points to a struct dict_object containing an application (see bellow) */
+};
+
+/***
+ *  API usage :
+
+Note: the value of "vendor_name" is copied when the object is created, and the string may be disposed afterwards.
+On the other side, when value is retrieved with dict_getval, the string is not copied and MUST NOT be freed. It will
+be freed automatically along with the object itself with call to dict_fini later.
+ 
+- fd_dict_new:
+ The "parent" parameter is not used for vendors. 
+ Sample code to create a vendor:
+ {
+	 int ret;
+	 struct dict_object * myvendor;
+	 struct dict_vendor_data myvendordata = { 23455, "my vendor name" };  -- just an example...
+	 ret = fd_dict_new ( dict, DICT_VENDOR, &myvendordata, NULL, &myvendor );
+ }
+
+- fd_dict_search:
+ Sample codes to look for a vendor object, by its id or name:
+ {
+	 int ret;
+	 struct dict_object * vendor_found;
+	 vendor_id_t vendorid = 23455;
+	 ret = fd_dict_search ( dict, DICT_VENDOR, VENDOR_BY_ID, &vendorid, &vendor_found, ENOENT);
+	 - or -
+	 ret = fd_dict_search ( dict, DICT_VENDOR, VENDOR_BY_NAME, "my vendor name", &vendor_found, ENOENT);
+ }
+ 
+ - fd_dict_getval:
+ Sample code to retrieve the data from a vendor object:
+ {
+	 int ret;
+	 struct dict_object * myvendor;
+	 struct dict_vendor_data myvendordata;
+	 ret = fd_dict_search ( dict, DICT_VENDOR, VENDOR_BY_NAME, "my vendor name", &myvendor, ENOENT);
+	 ret = fd_dict_getval ( myvendor, &myvendordata );
+	 printf("my vendor id: %d\n", myvendordata.vendor_id );
+ }
+		 
+*/
+		
+/* Special function: */
+uint32_t * fd_dict_get_vendorid_list(struct dictionary * dict);
+	 
+/*
+ ***************************************************************************
+ *
+ * Application object 
+ *
+ * These types are used to manage Diameter applications in the dictionary
+ *
+ ***************************************************************************
+ */
+
+/* Type to hold a Diameter application ID: IANA assigned value for this application. */
+typedef uint32_t	application_id_t;
+
+/* Type to hold data associated to an application */
+struct dict_application_data {
+	application_id_t	 application_id;	/* ID of the application */
+	char 			*application_name;	/* The name of this application */
+};
+
+/* The criteria for searching an application object in the dictionary */
+enum {
+	APPLICATION_BY_ID = 20,		/* "what" points to a application_id_t */
+	APPLICATION_BY_NAME,		/* "what" points to a string */
+	APPLICATION_OF_TYPE,		/* "what" points to a struct dict_object containing a type object (see bellow) */
+	APPLICATION_OF_COMMAND		/* "what" points to a struct dict_object containing a command (see bellow) */
+};
+
+/***
+ *  API usage :
+
+The "parent" parameter of dict_new may point to a vendor object to inform of what vendor defines the application.
+for standard-track applications, the "parent" parameter should be NULL.
+The vendor associated to an application is retrieved with VENDOR_OF_APPLICATION search criteria on vendors.
+
+- fd_dict_new:
+ Sample code for application creation:
+ {
+	 int ret;
+	 struct dict_object * vendor;
+	 struct dict_object * appl;
+	 struct dict_vendor_data vendor_data = {
+		 23455,
+		 "my vendor name"
+	 };
+	 struct dict_application_data app_data = {
+		 9789,
+		 "my vendor's application"
+	 };
+	
+	 ret = fd_dict_new ( dict, DICT_VENDOR, &vendor_data, NULL, &vendor );
+	 ret = fd_dict_new ( dict, DICT_APPLICATION, &app_data, vendor, &appl );
+ }
+
+- fd_dict_search:
+ Sample code to retrieve the vendor of an application
+ {
+	 int ret;
+	 struct dict_object * vendor, * appli;
+	 
+	 ret = fd_dict_search ( dict, DICT_APPLICATION, APPLICATION_BY_NAME, "my vendor's application", &appli, ENOENT);
+	 ret = fd_dict_search ( dict, DICT_VENDOR, VENDOR_OF_APPLICATION, appli, &vendor, ENOENT);
+ }
+ 
+ - fd_dict_getval:
+ Sample code to retrieve the data from an application object:
+ {
+	 int ret;
+	 struct dict_object * appli;
+	 struct dict_application_data appl_data;
+	 ret = fd_dict_search ( dict, DICT_APPLICATION, APPLICATION_BY_NAME, "my vendor's application", &appli, ENOENT);
+	 ret = fd_dict_getval ( appli, &appl_data );
+	 printf("my application id: %s\n", appl_data.application_id );
+ }
+
+*/
+
+/*
+ ***************************************************************************
+ *
+ * Type object 
+ *
+ * These types are used to manage AVP data types in the dictionary
+ *
+ ***************************************************************************
+ */
+
+/* Type to store any AVP value */ 
+union avp_value {
+	struct {
+		uint8_t *data;	/* bytes buffer */
+		size_t   len;	/* length of the data buffer */
+	}           os;		/* Storage for an octet string, data is alloc'd and must be freed */
+	int32_t     i32;	/* integer 32 */
+	int64_t     i64;	/* integer 64 */
+	uint32_t    u32;	/* unsigned 32 */
+	uint64_t    u64;	/* unsigned 64 */
+	float       f32;	/* float 32 */
+	double 	    f64;	/* float 64 */
+};
+
+/* These are the basic AVP types defined in RFC3588bis */
+enum dict_avp_basetype {
+	AVP_TYPE_GROUPED,
+	AVP_TYPE_OCTETSTRING,
+	AVP_TYPE_INTEGER32,
+	AVP_TYPE_INTEGER64,
+	AVP_TYPE_UNSIGNED32,
+	AVP_TYPE_UNSIGNED64,
+	AVP_TYPE_FLOAT32,
+	AVP_TYPE_FLOAT64
+#define AVP_TYPE_MAX AVP_TYPE_FLOAT64
+};
+
+/* Callbacks that can be associated with a derived type to easily interpret the AVP value. */
+/*
+ * CALLBACK:	dict_avpdata_interpret
+ *
+ * PARAMETERS:
+ *   val         : Pointer to the AVP value that must be interpreted.
+ *   interpreted : The result of interpretation is stored here. The format and meaning depends on each type.
+ *
+ * DESCRIPTION: 
+ *   This callback can be provided with a derived type in order to facilitate the interpretation of formated data.
+ *  For example, when an AVP of type "Address" is received, it can be used to convert the octetstring into a struct sockaddr.
+ *  This callback is not called directly, but through the message's API msg_avp_value_interpret function.
+ *
+ * RETURN VALUE:
+ *  0      	: Operation complete.
+ *  !0 		: An error occurred, the error code is returned.
+ */
+typedef int (*dict_avpdata_interpret) (union avp_value * value, void * interpreted);
+/*
+ * CALLBACK:	dict_avpdata_encode
+ *
+ * PARAMETERS:
+ *   data	: The formated data that must be stored in the AVP value.
+ *   val	: Pointer to the AVP value storage area where the data must be stored.
+ *
+ * DESCRIPTION: 
+ *   This callback can be provided with a derived type in order to facilitate the encoding of formated data.
+ *  For example, it can be used to convert a struct sockaddr in an AVP value of type Address.
+ *  This callback is not called directly, but through the message's API msg_avp_value_encode function.
+ *  If the callback is defined for an OctetString based type, the created string must be malloc'd. free will be called 
+ *  automatically later.
+ *
+ * RETURN VALUE:
+ *  0      	: Operation complete.
+ *  !0 		: An error occurred, the error code is returned.
+ */
+typedef int (*dict_avpdata_encode) (void * data, union avp_value * val);
+
+
+/* Type to hold data associated to a derived AVP data type */
+struct dict_type_data {
+	enum dict_avp_basetype	 type_base;	/* How the data of such AVP must be interpreted */
+	char 			*type_name;	/* The name of this type */
+	dict_avpdata_interpret	 type_interpret;/* cb to convert the AVP value in more comprehensive format (or NULL) */
+	dict_avpdata_encode	 type_encode;	/* cb to convert formatted data into an AVP value (or NULL) */
+	void			(*type_dump)(union avp_value * val);	/* cb called by fd_msg_dump_one for this type of data (if != NULL), to dump the AVP value in debug */
+};
+
+/* The criteria for searching a type object in the dictionary */
+enum {
+	TYPE_BY_NAME = 30,		/* "what" points to a string */
+	TYPE_OF_ENUMVAL,		/* "what" points to a struct dict_object containing an enumerated constant (DICT_ENUMVAL, see bellow). */
+	TYPE_OF_AVP			/* "what" points to a struct dict_object containing an AVP object. */
+};
+
+
+/***
+ *  API usage :
+
+- fd_dict_new:
+ The "parent" parameter may point to an application object, when a type is defined by a Diameter application. 
+ 
+ Sample code:
+ {
+	 int ret;
+	 struct dict_object * mytype;
+	 struct dict_type_data mytypedata = 
+		{ 
+		 AVP_TYPE_OCTETSTRING,
+		 "Address",
+		 NULL,
+		 NULL
+		};
+	 ret = fd_dict_new ( dict, DICT_TYPE, &mytypedata, NULL, &mytype );
+ }
+
+- fd_dict_search:
+ Sample code:
+ {
+	 int ret;
+	 struct dict_object * address_type;
+	 ret = fd_dict_search ( dict, DICT_TYPE, TYPE_BY_NAME, "Address", &address_type, ENOENT);
+ }
+ 
+*/
+	 
+/*
+ ***************************************************************************
+ *
+ * Enumerated values object 
+ *
+ * These types are used to manage named constants of some AVP,
+ * for enumerated types. freeDiameter allows constants for types others than Unsigned32
+ *
+ ***************************************************************************
+ */
+
+/* Type to hold data of named constants for AVP */
+struct dict_enumval_data {
+	char 		*enum_name;	/* The name of this constant */
+	union avp_value  enum_value;	/* Value of the constant. Union term depends on parent type's base type. */
+};
+
+/* The criteria for searching a constant in the dictionary */
+enum {
+	ENUMVAL_BY_STRUCT = 40,	/* "what" points to a struct dict_enumval_request as defined bellow */
+};
+
+struct dict_enumval_request {
+	/* Identifier of the parent type, one of the following must not be NULL */
+	struct dict_object	*type_obj;
+	char			*type_name;
+	
+	/* Search criteria for the constant */
+	struct dict_enumval_data search; /* search.enum_value is used only if search.enum_name == NULL */
+};
+
+/***
+ *  API usage :
+
+- fd_dict_new:
+ The "parent" parameter must point to a derived type object. 
+ Sample code to create a type "Boolean" with two constants "True" and "False":
+ {
+	 int ret;
+	 struct dict_object * type_boolean;
+	 struct dict_type_data type_boolean_data = 
+		{ 
+		 AVP_TYPE_INTEGER32,
+		 "Boolean",
+		 NULL,
+		 NULL
+		};
+	 struct dict_enumval_data boolean_false =
+	 	{
+		 .enum_name="False",
+		 .enum_value.i32 = 0
+	 	};
+	 struct dict_enumval_data boolean_true =
+	 	{
+		 .enum_name="True",
+		 .enum_value.i32 = -1
+	 	};
+	 ret = fd_dict_new ( dict, DICT_TYPE, &type_boolean_data, NULL, &type_boolean );
+	 ret = fd_dict_new ( dict, DICT_ENUMVAL, &boolean_false, type_boolean, NULL );
+	 ret = fd_dict_new ( dict, DICT_ENUMVAL, &boolean_true , type_boolean, NULL );
+	 
+ }
+
+- fd_dict_search:
+ Sample code to look for a constant name, by its value:
+ {
+	 int ret;
+	 struct dict_object * value_found;
+	 struct dict_enumval_request boolean_by_value =
+	 	{
+		 .type_name = "Boolean",
+		 .search.enum_name=NULL,
+		 .search.enum_value.i32 = -1
+	 	};
+	 
+	 ret = fd_dict_search ( dict, DICT_ENUMVAL, ENUMVAL_BY_STRUCT, &boolean_by_value, &value_found, ENOENT);
+ }
+ 
+ - fd_dict_getval:
+ Sample code to retrieve the data from a constant object:
+ {
+	 int ret;
+	 struct dict_object * value_found;
+	 struct dict_enumval_data boolean_data = NULL;
+	 struct dict_enumval_request boolean_by_value =
+	 	{
+		 .type_name = "Boolean",
+		 .search.enum_name=NULL,
+		 .search.enum_value.i32 = 0
+	 	};
+	 
+	 ret = fd_dict_search ( dict, DICT_ENUMVAL, ENUMVAL_BY_STRUCT, &boolean_by_value, &value_found, ENOENT);
+	 ret = fd_dict_getval ( value_found, &boolean_data );
+	 printf(" Boolean with value 0: %s", boolean_data.enum_name );
+ }
+*/
+	 
+/*
+ ***************************************************************************
+ *
+ * AVP object 
+ *
+ * These objects are used to manage AVP definitions in the dictionary
+ *
+ ***************************************************************************
+ */
+
+/* Type to hold an AVP code. For vendor 0, these codes are assigned by IANA. Otherwise, it is managed by the vendor */
+typedef uint32_t	avp_code_t;
+
+/* Values of AVP flags */
+#define	AVP_FLAG_VENDOR	  	0x80
+#define	AVP_FLAG_MANDATORY	0x40
+#define	AVP_FLAG_RESERVED3	0x20
+#define	AVP_FLAG_RESERVED4	0x10
+#define	AVP_FLAG_RESERVED5	0x08
+#define	AVP_FLAG_RESERVED6	0x04
+#define	AVP_FLAG_RESERVED7	0x02
+#define	AVP_FLAG_RESERVED8	0x01
+
+/* For dumping flags and values */
+#define DUMP_AVPFL_str	"%c%c"
+#define DUMP_AVPFL_val(_val) (_val & AVP_FLAG_VENDOR)?'V':'-' , (_val & AVP_FLAG_MANDATORY)?'M':'-'
+
+/* Type to hold data associated to an avp */
+struct dict_avp_data {
+	avp_code_t	 	 avp_code;	/* Code of the avp */
+	vendor_id_t	 	 avp_vendor;	/* Vendor of the AVP, or 0 */
+	char			*avp_name;	/* Name of this AVP */
+	uint8_t		 	 avp_flag_mask;	/* Mask of fixed AVP flags */
+	uint8_t		 	 avp_flag_val;	/* Values of the fixed flags */
+	enum dict_avp_basetype 	 avp_basetype;	/* Basic type of data found in the AVP */
+};
+
+/* The criteria for searching an avp object in the dictionary */
+enum {
+	AVP_BY_CODE = 50,	/* "what" points to an avp_code_t, vendor is always 0 */
+	AVP_BY_NAME,		/* "what" points to a string, vendor is always 0 */
+	AVP_BY_CODE_AND_VENDOR,	/* "what" points to a struct dict_avp_request (see bellow), where avp_vendor and avp_code are set */
+	AVP_BY_NAME_AND_VENDOR,	/* "what" points to a struct dict_avp_request (see bellow), where avp_vendor and avp_name are set */
+	AVP_BY_NAME_ALL_VENDORS /* "what" points to a string. Might be quite slow... */
+};
+
+/* Struct used for some researchs */
+struct dict_avp_request {
+	vendor_id_t	 avp_vendor;
+	avp_code_t	 avp_code;
+	char		*avp_name;
+};
+
+
+/***
+ *  API usage :
+
+If "parent" parameter is not NULL during AVP creation, it must point to a DICT_TYPE object. 
+The extended type is then attached to the AVP. In case where it is an enumerated type, the value of 
+AVP is automatically interpreted in debug messages, and in message checks.
+The derived type of an AVP can be retrieved with: dict_search ( DICT_TYPE, TYPE_OF_AVP, avp, ... )
+
+To create the rules (ABNF) for children of Grouped AVP, see the DICT_RULE related part.
+
+- fd_dict_new:
+ Sample code for AVP creation:
+ {
+	 int ret;
+	 struct dict_object * user_name_avp;
+	 struct dict_object * boolean_type;
+	 struct dict_object * sample_boolean_avp;
+	 struct dict_avp_data user_name_data = {
+		 1,					// code
+		 0,					// vendor
+		 "User-Name",				// name
+		 AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY,	// fixed mask: V and M values must always be defined as follow. other flags can be set or cleared
+		 AVP_FLAG_MANDATORY,			// the V flag must be cleared, the M flag must be set.
+		 AVP_TYPE_OCTETSTRING			// User-Name AVP contains OctetString data (further precision such as UTF8String can be given with a parent derived type)
+	 };
+	 struct dict_avp_data sample_boolean_data = {
+		 31337,
+		 23455,
+		 "Sample-Boolean",
+		 AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY,
+		 AVP_FLAG_VENDOR,
+		 AVP_TYPE_INTEGER32			// This MUST be the same as parent type's
+	 };
+	
+ 	 -- Create an AVP with a base type --
+	 ret = fd_dict_new ( dict, DICT_AVP, &user_name_data, NULL, &user_name_avp );
+	 
+	 -- Create an AVP with a derived type --
+	 ret = fd_dict_search ( dict, DICT_TYPE, TYPE_BY_NAME, "Boolean", &boolean_type, ENOENT);
+	 ret = fd_dict_new ( dict, DICT_AVP, &sample_boolean_data , boolean_type, &sample_boolean_avp );
+	 
+ }
+
+- fd_dict_search:
+ Sample code to look for an AVP
+ {
+	 int ret;
+	 struct dict_object * avp_username;
+	 struct dict_object * avp_sampleboolean;
+	 struct dict_avp_request avpvendorboolean =
+	 	{
+		 .avp_vendor = 23455,
+		 .avp_name   = "Sample-Boolean"
+	 	};
+	 
+	 ret = fd_dict_search ( dict, DICT_AVP, AVP_BY_NAME, "User-Name", &avp_username, ENOENT);
+	 
+	 ret = fd_dict_search ( dict, DICT_AVP, AVP_BY_NAME_AND_VENDOR, &avpvendorboolean, &avp_sampleboolean, ENOENT);
+	 
+ }
+ 
+ - fd_dict_getval:
+ Sample code to retrieve the data from an AVP object:
+ {
+	 int ret;
+	 struct dict_object * avp_username;
+	 struct dict_avp_data user_name_data;
+	 ret = fd_dict_search ( dict, DICT_AVP, AVP_BY_NAME, "User-Name", &avp_username, ENOENT);
+	 ret = fd_dict_getval ( avp_username, &user_name_data );
+	 printf("User-Name code: %d\n", user_name_data.avp_code );
+ }
+
+*/
+
+/*
+ ***************************************************************************
+ *
+ * Command object 
+ *
+ * These types are used to manage commands objects in the dictionary
+ *
+ ***************************************************************************
+ */
+
+/* Type to hold a Diameter command code: IANA assigned values. 0x0-0x7fffff=standard, 0x800000-0xfffffd=vendors, 0xfffffe-0xffffff=experimental */
+typedef uint32_t	command_code_t;
+
+/* Values of command flags */
+#define CMD_FLAG_REQUEST	0x80
+#define CMD_FLAG_PROXIABLE	0x40
+#define CMD_FLAG_ERROR		0x20
+#define CMD_FLAG_RETRANSMIT	0x10
+#define CMD_FLAG_RESERVED5	0x08
+#define CMD_FLAG_RESERVED6	0x04
+#define CMD_FLAG_RESERVED7	0x02
+#define CMD_FLAG_RESERVED8	0x01
+
+/* For dumping flags and values */
+#define DUMP_CMDFL_str	"%c%c%c%c"
+#define DUMP_CMDFL_val(_val) (_val & CMD_FLAG_REQUEST)?'R':'-' , (_val & CMD_FLAG_PROXIABLE)?'P':'-' , (_val & CMD_FLAG_ERROR)?'E':'-' , (_val & CMD_FLAG_RETRANSMIT)?'T':'-'
+
+/* Type to hold data associated to a command */
+struct dict_cmd_data {
+	command_code_t	 cmd_code;	/* code of the command */
+	char		*cmd_name;	/* Name of the command */
+	uint8_t		 cmd_flag_mask;	/* Mask of fixed-value flags */
+	uint8_t		 cmd_flag_val;	/* values of the fixed flags */
+};
+
+/* The criteria for searching an avp object in the dictionary */
+enum {
+	CMD_BY_NAME = 60,	/* "what" points to a string */
+	CMD_BY_CODE_R,		/* "what" points to a command_code_t. The "Request" command is returned. */
+	CMD_BY_CODE_A,		/* "what" points to a command_code_t. The "Answer" command is returned. */
+	CMD_ANSWER		/* "what" points to a struct dict_object of a request command. The corresponding "Answer" command is returned. */
+};
+
+
+/***
+ *  API usage :
+
+The "parent" parameter of dict_new may point to an application object to inform of what application defines the command.
+The application associated to a command is retrieved with APPLICATION_OF_COMMAND search criteria on applications.
+
+To create the rules for children of commands, see the DICT_RULE related part.
+
+Note that the "Request" and "Answer" commands are two independant objects. This allows to have different rules for each.
+
+- fd_dict_new:
+ Sample code for command creation:
+ {
+	 int ret;
+	 struct dict_object * cer;
+	 struct dict_object * cea;
+	 struct dict_cmd_data ce_data = {
+		 257,					// code
+		 "Capabilities-Exchange-Request",	// name
+		 CMD_FLAG_REQUEST,			// mask
+		 CMD_FLAG_REQUEST			// value. Only the "R" flag is constrained here, set.
+	 };
+	
+	 ret = fd_dict_new (dict,  DICT_COMMAND, &ce_data, NULL, &cer );
+	 
+	 ce_data.cmd_name = "Capabilities-Exchange-Answer";
+	 ce_data.cmd_flag_val = 0;			// Same constraint on "R" flag, but this time it must be cleared.
+
+	 ret = fd_dict_new ( dict, DICT_COMMAND, &ce_data, NULL, &cea );
+ }
+
+- fd_dict_search:
+ Sample code to look for a command
+ {
+	 int ret;
+	 struct dict_object * cer, * cea;
+	 command_code_t	code = 257;
+	 ret = fd_dict_search ( dict, DICT_COMMAND, CMD_BY_NAME, "Capabilities-Exchange-Request", &cer, ENOENT);
+	 ret = fd_dict_search ( dict, DICT_COMMAND, CMD_BY_CODE_R, &code, &cer, ENOENT);
+ }
+ 
+ - fd_dict_getval:
+ Sample code to retrieve the data from a command object:
+ {
+	 int ret;
+	 struct dict_object * cer;
+	 struct dict_object * cea;
+	 struct dict_cmd_data cea_data;
+	 ret = fd_dict_search ( dict, DICT_COMMAND, CMD_BY_NAME, "Capabilities-Exchange-Request", &cer, ENOENT);
+	 ret = fd_dict_search ( dict, DICT_COMMAND, CMD_ANSWER, cer, &cea, ENOENT);
+	 ret = fd_dict_getval ( cea, &cea_data );
+	 printf("Answer to CER: %s\n", cea_data.cmd_name );
+ }
+
+*/
+
+/*
+ ***************************************************************************
+ *
+ * Rule object
+ *
+ * These objects are used to manage rules in the dictionary (ABNF implementation)
+ * This is used for checking messages validity (more powerful than a DTD)
+ *
+ ***************************************************************************
+ */
+
+/* This defines the kind of rule that is defined */
+enum rule_position {
+	RULE_FIXED_HEAD = 1,	/* The AVP must be at the head of the group. The rule_order field is used to specify the position. */
+	RULE_REQUIRED,		/* The AVP must be present in the parent, but its position is not defined. */
+	RULE_OPTIONAL,		/* The AVP may be present in the message. Used to specify a max number of occurences for example */
+	RULE_FIXED_TAIL		/* The AVP must be at the end of the group. The rule_order field is used to specify the position. */
+};
+
+/* Content of a RULE object data */
+struct dict_rule_data {
+	struct dict_object	*rule_avp;	/* Pointer to the AVP object that is concerned by this rule */
+	enum rule_position	 rule_position;	/* The position in which the rule_avp must appear in the parent */
+	unsigned		 rule_order;	/* for RULE_FIXED_* rules, the place. 1,2,3.. for HEAD rules; ...,3,2,1 for TAIL rules. */
+	int	 		 rule_min;	/* Minimum number of occurences. -1 means "default": 0 for optional rules, 1 for other rules */
+	int			 rule_max;	/* Maximum number of occurences. -1 means no maximum. 0 means the AVP is forbidden. */
+};
+
+/* The criteria for searching a rule in the dictionary */
+enum {
+	RULE_BY_AVP_AND_PARENT = 70	/* "what" points to a struct dict_rule_request -- see bellow. This is used to query "what is the rule for this AVP in this group?" */
+};
+
+/* Structure for querying the dictionary about a rule */
+struct dict_rule_request {
+	struct dict_object	*rule_parent;	/* The grouped avp or command to which the rule apply */
+	struct dict_object	*rule_avp;	/* The AVP concerned by this rule */
+};
+
+
+/***
+ *  API usage :
+
+The "parent" parameter can not be NULL. It points to the object (grouped avp or command) to which this rule apply (i.e. for which the ABNF is defined).
+
+- fd_dict_new:
+ Sample code for rule creation. Let's create the Proxy-Info grouped AVP for example.
+ {
+	int ret;
+	struct dict_object * proxy_info_avp;
+	struct dict_object * proxy_host_avp;
+	struct dict_object * proxy_state_avp;
+	struct dict_object * diameteridentity_type;
+	struct dict_rule_data rule_data;
+	struct dict_type_data di_type_data = { AVP_TYPE_OCTETSTRING, "DiameterIdentity", NULL, NULL };
+	struct dict_avp_data proxy_info_data = { 284, 0, "Proxy-Info", AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, AVP_FLAG_MANDATORY, AVP_TYPE_GROUPED };
+	struct dict_avp_data proxy_host_data = { 280, 0, "Proxy-Host", AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, AVP_FLAG_MANDATORY, AVP_TYPE_OCTETSTRING };
+	struct dict_avp_data proxy_state_data = { 33, 0, "Proxy-State",AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, AVP_FLAG_MANDATORY, AVP_TYPE_OCTETSTRING };
+	
+	-- Create the parent AVP
+	ret = fd_dict_new ( dict, DICT_AVP, &proxy_info_data, NULL, &proxy_info_avp );
+	
+	-- Create the first child AVP.
+	ret = fd_dict_new ( dict, DICT_TYPE, &di_type_data, NULL, &diameteridentity_type );
+	ret = fd_dict_new ( dict, DICT_AVP, &proxy_host_data, diameteridentity_type, &proxy_host_avp );
+	
+	-- Create the other child AVP
+	ret = fd_dict_new ( dict, DICT_AVP, &proxy_state_data, NULL, &proxy_state_avp );
+	
+	-- Now we can create the rules. Both children AVP are mandatory.
+	rule_data.rule_position = RULE_REQUIRED;
+	rule_data.rule_min = -1;
+	rule_data.rule_max = -1;
+	
+	rule_data.rule_avp = proxy_host_avp;
+	ret = fd_dict_new ( dict, DICT_RULE, &rule_data, proxy_info_avp, NULL );
+	
+	rule_data.rule_avp = proxy_state_avp;
+	ret = fd_dict_new ( dict, DICT_RULE, &rule_data, proxy_info_avp, NULL );
+}
+
+- fd_dict_search and fd_dict_getval are similar to previous examples.
+
+*/
+		
+/* Define some hard-coded values */
+/* Application */
+#define AI_RELAY			0xffffffff
+
+/* Commands Codes */
+#define CC_CAPABILITIES_EXCHANGE	257
+#define CC_RE_AUTH			258
+#define CC_ACCOUNTING			271
+#define CC_ABORT_SESSION		274
+#define CC_SESSION_TERMINATION		275
+#define CC_DEVICE_WATCHDOG		280
+#define CC_DISCONNECT_PEER		282
+
+/* AVPs (Vendor 0) */
+#define AC_USER_NAME			1
+#define AC_PROXY_STATE			33
+#define AC_HOST_IP_ADDRESS		257
+#define AC_AUTH_APPLICATION_ID		258
+#define AC_ACCT_APPLICATION_ID		259
+#define AC_VENDOR_SPECIFIC_APPLICATION_ID 260
+#define AC_REDIRECT_HOST_USAGE		261
+#define AC_REDIRECT_MAX_CACHE_TIME	262
+#define AC_SESSION_ID 			263
+#define AC_ORIGIN_HOST			264
+#define AC_SUPPORTED_VENDOR_ID		265
+#define AC_VENDOR_ID			266
+#define AC_FIRMWARE_REVISION		267
+#define AC_RESULT_CODE			268
+#define AC_PRODUCT_NAME			269
+#define AC_DISCONNECT_CAUSE		273
+#define ACV_DC_REBOOTING			0
+#define ACV_DC_BUSY				1
+#define ACV_DC_NOT_FRIEND			2
+#define AC_ORIGIN_STATE_ID		278
+#define AC_FAILED_AVP			279
+#define AC_PROXY_HOST			280
+#define AC_ERROR_MESSAGE		281
+#define AC_ROUTE_RECORD			282
+#define AC_DESTINATION_REALM		283
+#define AC_PROXY_INFO			284
+#define AC_REDIRECT_HOST		292
+#define AC_DESTINATION_HOST		293
+#define AC_ERROR_REPORTING_HOST		294
+#define AC_ORIGIN_REALM			296
+#define AC_INBAND_SECURITY_ID		299
+#define ACV_ISI_NO_INBAND_SECURITY		0
+#define ACV_ISI_TLS				1
+
+/* Error codes */
+#define ER_DIAMETER_SUCCESS			2001
+#define ER_DIAMETER_REALM_NOT_SERVED		3003
+#define ER_DIAMETER_TOO_BUSY			3004
+#define ER_DIAMETER_REDIRECT_INDICATION		3006
+#define ER_ELECTION_LOST			4003
+
+
+/*============================================================*/
+/*                         SESSIONS                           */
+/*============================================================*/
+
+/* Modules that want to associate a state with a Session-Id must first register a handler of this type */
+struct session_handler;
+
+/* This opaque structure represents a session associated with a Session-Id */
+struct session;
+
+/* The state information that a module associate with a session -- each module defines its own data format */
+typedef void session_state;
+
+/* The following function must be called to activate the session expiry mechanism */
+int fd_sess_start(void);
+
+/*
+ * FUNCTION:	fd_sess_handler_create
+ *
+ * PARAMETERS:
+ *  handler	: location where the new handler must be stored.
+ *  cleanup	: a callback function that must be called when the session with associated data is destroyed.
+ *  opaque      : A pointer that is passed to the cleanup callback -- the content is never examined by the framework.
+ *
+ * DESCRIPTION: 
+ *  Create a new session handler. This is needed by a module to associate a state with a session object.
+ * The cleanup handler is called when the session timeout expires, or fd_sess_destroy is called. It must free
+ * the state associated with the session, and eventually trig other actions (send a STR, ...).
+ *
+ * RETURN VALUE:
+ *  0      	: The new handler has been created.
+ *  EINVAL 	: A parameter is invalid.
+ *  ENOMEM	: Not enough memory to complete the operation
+ */
+int fd_sess_handler_create_internal ( struct session_handler ** handler, void (*cleanup)(session_state * state, char * sid, void * opaque), void * opaque );
+/* Macro to avoid casting everywhere */
+#define fd_sess_handler_create( _handler, _cleanup, _opaque ) \
+	fd_sess_handler_create_internal( (_handler), (void (*)(session_state *, char *, void *))(_cleanup), (void *)(_opaque) )
+
+	
+/*
+ * FUNCTION:	fd_sess_handler_destroy
+ *
+ * PARAMETERS:
+ *  handler	: location of an handler created by fd_sess_handler_create.
+ *  opaque      : the opaque pointer registered with the callback is restored here (if ! NULL).
+ *
+ * DESCRIPTION: 
+ *  This destroys a session handler (typically called when an application is shutting down).
+ * If sessions states are registered with this handler, the cleanup callback is called on them.
+ *
+ * RETURN VALUE:
+ *  0      	: The handler was destroyed.
+ *  EINVAL 	: A parameter is invalid.
+ *  ENOMEM	: Not enough memory to complete the operation
+ */
+int fd_sess_handler_destroy ( struct session_handler ** handler, void **opaque );
+
+
+
+/*
+ * FUNCTION:	fd_sess_new
+ *
+ * PARAMETERS:
+ *  session	  : The location where the session object will be created upon success.
+ *  diamId	  : \0-terminated string containing a Diameter Identity.
+ *  opt           : Additional string. Usage is described bellow.
+ *  optlen	  : if opt is \0-terminated, this can be 0. Otherwise, the length of opt.
+ *
+ * DESCRIPTION: 
+ *   Create a new session object. The Session-Id string associated with this session is generated as follow:
+ *  If diamId parameter is provided, the string is created according to the RFC: <diamId>;<high32>;<low32>[;opt] where
+ *    diamId is a Diameter Identity.
+ *    high32 and low32 are the parts of a monotonic 64 bits counter initialized to (time, 0) at startup.
+ *    opt is an optional string that can be concatenated to the identifier.
+ *  If diamId is NULL, the string is exactly the content of opt.
+ *
+ * RETURN VALUE:
+ *  0      	: The session is created.
+ *  EINVAL 	: A parameter is invalid.
+ *  EALREADY	: A session with the same name already exists (returned in *session)
+ *  ENOMEM	: Not enough memory to complete the operation
+ */
+int fd_sess_new ( struct session ** session, char * diamId, char * opt, size_t optlen );
+
+/*
+ * FUNCTION:	fd_sess_fromsid
+ *
+ * PARAMETERS:
+ *  sid	  	: pointer to a string containing a Session-Id (UTF-8).
+ *  len		: length of the sid string (which does not need to be '\0'-terminated)
+ *  session	: On success, pointer to the session object created / retrieved.
+ *  isnew	: if not NULL, set to 1 on return if the session object has been created, 0 if it was simply retrieved.
+ *
+ * DESCRIPTION: 
+ *   Retrieve a session object from a Session-Id string. In case no session object was previously existing with this 
+ *  id, a new object is silently created (equivalent to fd_sess_new with flag SESSION_NEW_FULL).
+ *
+ * RETURN VALUE:
+ *  0      	: The session parameter has been updated.
+ *  EINVAL 	: A parameter is invalid.
+ *  ENOMEM	: Not enough memory to complete the operation
+ */
+int fd_sess_fromsid ( char * sid, size_t len, struct session ** session, int * isnew);
+
+/*
+ * FUNCTION:	fd_sess_getsid
+ *
+ * PARAMETERS:
+ *  session	: Pointer to a session object.
+ *  sid	  	: On success, the location of a (\0-terminated) string is stored here.
+ *
+ * DESCRIPTION: 
+ *   Retrieve the session identifier (Session-Id) corresponding to a session object.
+ *  The returned sid is an UTF-8 string terminated by \0, suitable for calls to strlen and strcpy.
+ *  It may be used for example to set the value of an AVP.
+ *  Note that the sid string is not copied, just its reference... do not free it!
+ *
+ * RETURN VALUE:
+ *  0      	: The sid parameter has been updated.
+ *  EINVAL 	: A parameter is invalid.
+ */
+int fd_sess_getsid ( struct session * session, char ** sid );
+
+/*
+ * FUNCTION:	fd_sess_settimeout
+ *
+ * PARAMETERS:
+ *  session	: The session for which to set the timeout.
+ *  timeout	: The date when the session times out.
+ *
+ * DESCRIPTION: 
+ *   Set the lifetime for a given session object. This function may be 
+ * called several times on the same object to update the timeout value.
+ *   When the timeout date is reached, the cleanup handler of each 
+ * module that registered data with this session is called, then the 
+ * session is cleared.
+ *
+ *   There is a possible race condition between cleanup of the session
+ * and use of its data; applications should ensure that they are not 
+ * using data from a session that is about to expire / expired.
+ *
+ * RETURN VALUE:
+ *  0      	: The session timeout has been updated.
+ *  EINVAL 	: A parameter is invalid.
+ */
+int fd_sess_settimeout( struct session * session, const struct timespec * timeout );
+
+/*
+ * FUNCTION:	fd_sess_destroy
+ *
+ * PARAMETERS:
+ *  session	: Pointer to a session object.
+ *
+ * DESCRIPTION: 
+ *   Destroys a session an all associated data, if any.
+ * Equivalent to a session timeout expired, but the effect is immediate.
+ *
+ * RETURN VALUE:
+ *  0      	: The session no longer exists.
+ *  EINVAL 	: A parameter is invalid.
+ */
+int fd_sess_destroy ( struct session ** session );
+
+/*
+ * FUNCTION:	fd_sess_reclaim
+ *
+ * PARAMETERS:
+ *  session	: Pointer to a session object.
+ *
+ * DESCRIPTION: 
+ *   Destroys the resources of a session, only if no session_state is associated with it.
+ *
+ * RETURN VALUE:
+ *  0      	: The session no longer exists.
+ *  EINVAL 	: A parameter is invalid.
+ */
+int fd_sess_reclaim ( struct session ** session );
+
+
+
+
+/*
+ * FUNCTION:	fd_sess_state_store
+ *
+ * PARAMETERS:
+ *  handler	: The handler with which the state is registered.
+ *  session	: The session object with which the state is registered.
+ *  state	: An application state (opaque data) to store with the session.
+ *
+ * DESCRIPTION: 
+ *  Stores an application state with a session. This state can later be retrieved
+ * with fd_sess_state_retrieve, or implicitly in the cleanup handler when the session
+ * is destroyed.
+ *
+ * RETURN VALUE:
+ *  0      	: The state has been stored.
+ *  EINVAL 	: A parameter is invalid.
+ *  EALREADY	: Data was already associated with this session and client.
+ *  ENOMEM	: Not enough memory to complete the operation
+ */
+int fd_sess_state_store_internal ( struct session_handler * handler, struct session * session, session_state ** state );
+#define fd_sess_state_store( _handler, _session, _state ) \
+	fd_sess_state_store_internal( (_handler), (_session), (void *)(_state) )
+
+/*
+ * FUNCTION:	fd_sess_state_retrieve
+ *
+ * PARAMETERS:
+ *  handler	: The handler with which the state was registered.
+ *  session	: The session object with which the state was registered.
+ *  state	: Location where the state must be saved if it is found.
+ *
+ * DESCRIPTION: 
+ *  Retrieves a state saved by fd_sess_state_store.
+ * After this function has been called, the state is no longer associated with 
+ * the session. A new call to fd_sess_state_store must be performed in order to
+ * store again the data with the session.
+ *
+ * RETURN VALUE:
+ *  0      	: *state is updated (NULL or points to the state if it was found).
+ *  EINVAL 	: A parameter is invalid.
+ */
+int fd_sess_state_retrieve_internal ( struct session_handler * handler, struct session * session, session_state ** state ); 
+#define fd_sess_state_retrieve( _handler, _session, _state ) \
+	fd_sess_state_retrieve_internal( (_handler), (_session), (void *)(_state) )
+
+
+/* For debug */
+void fd_sess_dump(int level, struct session * session);
+void fd_sess_dump_hdl(int level, struct session_handler * handler);
+
+/*============================================================*/
+/*                         ROUTING                            */
+/*============================================================*/
+
+/* The following functions are helpers for the routing module. 
+  The routing data is stored in the message itself. */
+
+/* Structure that contains the routing data for a message */
+struct rt_data;
+
+/* Following functions are helpers to create the routing data of a message */
+int  fd_rtd_init(struct rt_data ** rtd);
+void fd_rtd_free(struct rt_data ** rtd);
+
+/* Add a peer to the candidates list */
+int  fd_rtd_candidate_add(struct rt_data * rtd, char * peerid, char * realm);
+
+/* Remove a peer from the candidates (if it is found) */
+void fd_rtd_candidate_del(struct rt_data * rtd, char * peerid, size_t sz /* if !0, peerid does not need to be \0 terminated */);
+
+/* Extract the list of valid candidates, and initialize their scores to 0 */
+void fd_rtd_candidate_extract(struct rt_data * rtd, struct fd_list ** candidates, int ini_score);
+
+/* If a peer returned a protocol error for this message, save it so that we don't try to send it there again */
+int  fd_rtd_error_add(struct rt_data * rtd, char * sentto, uint8_t * origin, size_t originsz, uint32_t rcode);
+
+/* The extracted list items have the following structure: */
+struct rtd_candidate {
+	struct fd_list	chain;	/* link in the list returned by the previous fct */
+	char *		diamid;	/* the diameter Id of the peer */
+	char *		realm;	/* the diameter realm of the peer (if known) */
+	int		score;	/* the current routing score for this peer, see fd_rt_out_register definition for details */
+};
+
+/* Reorder the list of peers */
+int  fd_rtd_candidate_reorder(struct fd_list * candidates);
+
+/* Note : it is fine for a callback to add a new entry in the candidates list after the list has been extracted. The diamid must then be malloc'd. */
+/* Beware that this could lead to routing loops */
+
+/*============================================================*/
+/*                         MESSAGES                           */
+/*============================================================*/
+
+/* The following types are opaque */
+struct	msg;	/* A message: command with children AVPs (possibly grand children) */
+struct	avp;	/* AVP object */
+
+/* Some details about chaining:
+ *
+ *  A message is made of a header ( msg ) and 0 or more AVPs ( avp ).
+ * The structure is a kind of tree, where some AVPs (grouped AVPs) can contain other AVPs.
+ * Exemple:
+ * msg
+ *  |-avp
+ *  |-gavp
+ *  |   |-avp
+ *  |   |-avp
+ *  |   \-avp
+ *  |-avp
+ *  \-avp
+ *
+ */
+
+/* The following type is used to point to either a msg or an AVP */
+typedef void msg_or_avp;
+
+/* The Diameter protocol version */
+#define DIAMETER_VERSION	1
+
+/* In the two following types, some fields are marked (READONLY). 
+ * This means that the content of these fields will be overwritten by the daemon so modifying it is useless.
+ */
+
+/* The following structure represents the header of a message. All data is in host byte order. */
+struct msg_hdr {
+	uint8_t		 msg_version;		/* (READONLY) Version of Diameter: must be DIAMETER_VERSION. */
+	uint32_t	 msg_length;		/* (READONLY)(3 bytes) indicates the length of the message */
+	uint8_t		 msg_flags;		/* Message flags: CMD_FLAG_* */
+	command_code_t	 msg_code;		/* (3 bytes) the command-code. See dictionary-api.h for more detail */
+	application_id_t msg_appl;		/* The application issuing this message */
+	uint32_t	 msg_hbhid;		/* The Hop-by-Hop identifier of the message */
+	uint32_t	 msg_eteid;		/* The End-to-End identifier of the message */
+};
+
+/* The following structure represents the visible content of an AVP. All data is in host byte order. */
+struct avp_hdr {
+	avp_code_t	 avp_code;		/* the AVP Code */
+	uint8_t		 avp_flags;		/* AVP_FLAG_* flags */
+	uint32_t	 avp_len;		/* (READONLY)(Only 3 bytes are used) the length of the AVP as described in the RFC */
+	vendor_id_t	 avp_vendor;		/* Only used if AVP_FLAG_VENDOR is present */
+	union avp_value *avp_value;		/* pointer to the value of the AVP. NULL means that the value is not set / not understood.
+						   One should not directly change this value. Use the msg_avp_setvalue function instead.
+						   The content of the pointed structure can be changed directly, with this restriction:
+						     if the AVP is an OctetString, and you change the value of the pointer avp_value->os.data, then
+						     you must call free() on the previous value, and the new one must be free()-able.
+						 */
+};
+
+/* The following enum is used to browse inside message hierarchy (msg, gavp, avp) */
+enum msg_brw_dir {
+	MSG_BRW_NEXT = 1,	/* Get the next element at the same level, or NULL if this is the last element. */
+	MSG_BRW_PREV,		/* Get the previous element at the same level, or NULL if this is the first element. */
+	MSG_BRW_FIRST_CHILD,	/* Get the first child AVP of this element, if any. */
+	MSG_BRW_LAST_CHILD,	/* Get the last child AVP of this element, if any. */
+	MSG_BRW_PARENT,		/* Get the parent element of this element, if any. Only the msg_t object has no parent. */
+	MSG_BRW_WALK		/* This is equivalent to FIRST_CHILD or NEXT or PARENT->next, first that is not NULL. Use this to walk inside all AVPs. */
+};
+
+/* Some flags used in the functions bellow */
+#define AVPFL_SET_BLANK_VALUE	0x01	/* When creating an AVP, initialize its value to a blank area */
+#define AVPFL_MAX		AVPFL_SET_BLANK_VALUE	/* The biggest valid flag value */
+	
+#define MSGFL_ALLOC_ETEID	0x01	/* When creating a message, a new end-to-end ID is allocated and set in the message */
+#define MSGFL_ANSW_ERROR	0x02	/* When creating an answer message, set the 'E' bit and use the generic error ABNF instead of command-specific ABNF */
+#define MSGFL_ANSW_NOSID	0x04	/* When creating an answer message, do not add the Session-Id even if present in request */
+#define MSGFL_MAX		MSGFL_ANSW_NOSID	/* The biggest valid flag value */
+
+/**************************************************/
+/*   Message creation, manipulation, disposal     */
+/**************************************************/
+/*
+ * FUNCTION:	fd_msg_avp_new
+ *
+ * PARAMETERS:
+ *  model 	: Pointer to a DICT_AVP dictionary object describing the avp to create, or NULL.
+ *  flags	: Flags to use in creation (AVPFL_*).
+ *  avp 	: Upon success, pointer to the new avp is stored here.
+ *
+ * DESCRIPTION: 
+ *   Create a new AVP instance.
+ *
+ * RETURN VALUE:
+ *  0      	: The AVP is created.
+ *  EINVAL 	: A parameter is invalid.
+ *  (other standard errors may be returned, too, with their standard meaning. Example:
+ *    ENOMEM 	: Memory allocation for the new avp failed.)
+ */
+int fd_msg_avp_new ( struct dict_object * model, int flags, struct avp ** avp );
+
+/*
+ * FUNCTION:	fd_msg_new
+ *
+ * PARAMETERS:
+ *  model 	: Pointer to a DICT_COMMAND dictionary object describing the message to create, or NULL.
+ *  flags	: combination of MSGFL_* flags.
+ *  msg 	: Upon success, pointer to the new message is stored here.
+ *
+ * DESCRIPTION: 
+ *   Create a new empty Diameter message. 
+ *
+ * RETURN VALUE:
+ *  0      	: The message is created.
+ *  EINVAL 	: A parameter is invalid.
+ *  (other standard errors may be returned, too, with their standard meaning. Example:
+ *    ENOMEM 	: Memory allocation for the new message failed.)
+ */
+int fd_msg_new ( struct dict_object * model, int flags, struct msg ** msg );
+
+/*
+ * FUNCTION:	msg_new_answer_from_req
+ *
+ * PARAMETERS:
+ *  dict	: Pointer to the dictionary containing the model of the query.
+ *  msg		: The location of the query on function call. Updated by the location of answer message on return.
+ *  flag        : Pass MSGFL_ANSW_ERROR to indicate if the answer is an error message (will set the 'E' bit)
+ *
+ * DESCRIPTION: 
+ *   This function creates the empty answer message corresponding to a request.
+ *  The header is set properly (R flag, ccode, appid, hbhid, eteid)
+ *  The Session-Id AVP is copied if present.
+ *  The calling code should usually call fd_msg_rescode_set function on the answer.
+ *  Upon return, the original query may be retrieved by calling fd_msg_answ_getq on the message.
+ *
+ * RETURN VALUE:
+ *  0      	: Operation complete.
+ *  !0      	: an error occurred.
+ */
+int fd_msg_new_answer_from_req ( struct dictionary * dict, struct msg ** msg, int flag );
+
+/*
+ * FUNCTION:	fd_msg_browse
+ *
+ * PARAMETERS:
+ *  reference 	: Pointer to a struct msg or struct avp.
+ *  dir         : Direction for browsing
+ *  found       : If not NULL, updated with the element that has been found, if any, or NULL if no element was found / an error occurred.
+ *  depth	: If not NULL, points to an integer representing the "depth" of this object in the tree. This is a relative value, updated on return.
+ *
+ * DESCRIPTION: 
+ *   Explore the content of a message object (hierarchy). If "found" is null, only error checking is performed.
+ *  If "depth" is provided, it is updated as follow on successful function return:
+ *   - not modified for MSG_BRW_NEXT and MSG_BRW_PREV.
+ *   - *depth = *depth + 1 for MSG_BRW_FIRST_CHILD and MSG_BRW_LAST_CHILD.
+ *   - *depth = *depth - 1 for MSG_BRW_PARENT.
+ *   - *depth = *depth + X for MSG_BRW_WALK, with X between 1 (returned the 1st child) and -N (returned the Nth parent's next).
+ *
+ * RETURN VALUE:
+ *  0      	: found has been updated (if non NULL).
+ *  EINVAL 	: A parameter is invalid.
+ *  ENOENT	: No element has been found where requested, and "found" was NULL (otherwise, *found is set to NULL and 0 is returned). 
+ */
+int fd_msg_browse_internal ( msg_or_avp * reference, enum msg_brw_dir dir, msg_or_avp ** found, int * depth );
+/* Macro to avoid having to cast the third parameter everywhere */
+#define fd_msg_browse( ref, dir, found, depth )	\
+	fd_msg_browse_internal( (ref), (dir), (void *)(found), (depth) )
+
+
+/*
+ * FUNCTION:	fd_msg_avp_add
+ *
+ * PARAMETERS:
+ *  reference 	: Pointer to a valid msg or avp.
+ *  dir         : location where the new AVP should be inserted, relative to the reference. MSG_BRW_PARENT and MSG_BRW_WALK are not valid.
+ *  avp         : pointer to the AVP object that must be inserted.
+ *
+ * DESCRIPTION: 
+ *   Adds an AVP into an object that can contain it: grouped AVP or message.
+ * Note that the added AVP will be freed at the same time as the object it is added to, 
+ * so it should not be freed after the call to this function.
+ *
+ * RETURN VALUE:
+ *  0      	: The AVP has been added.
+ *  EINVAL 	: A parameter is invalid.
+ */
+int fd_msg_avp_add ( msg_or_avp * reference, enum msg_brw_dir dir, struct avp *avp);
+
+/*
+ * FUNCTION:	fd_msg_search_avp
+ *
+ * PARAMETERS:
+ *  msg 	: The message structure in which to search the AVP.
+ *  what 	: The dictionary model of the AVP to search.
+ *  avp		: location where the AVP reference is stored if found.
+ *
+ * DESCRIPTION: 
+ *   Search the first top-level AVP of a given model inside a message.
+ * Note: only the first instance of the AVP is returned by this function.
+ * Note: only top-level AVPs are searched, not inside grouped AVPs.
+ * Use msg_browse if you need more advanced research features.
+ *
+ * RETURN VALUE:
+ *  0      	: The AVP has been found.
+ *  EINVAL 	: A parameter is invalid.
+ *  ENOENT	: No AVP has been found, and "avp" was NULL (otherwise, *avp is set to NULL and 0 returned).
+ */
+int fd_msg_search_avp ( struct msg * msg, struct dict_object * what, struct avp ** avp );
+
+/*
+ * FUNCTION:	fd_msg_free
+ *
+ * PARAMETERS:
+ *  object      : pointer to the message or AVP object that must be unlinked and freed.
+ *
+ * DESCRIPTION: 
+ *   Unlink and free a message or AVP object and its children.
+ *  If the object is an AVP linked into a message, the AVP is removed before being freed.
+ *
+ * RETURN VALUE:
+ *  0      	: The message has been freed.
+ *  EINVAL 	: A parameter is invalid.
+ */
+int fd_msg_free ( msg_or_avp * object );
+
+/***************************************/
+/*   Dump functions                    */
+/***************************************/
+/*
+ * FUNCTION:	fd_msg_dump_*
+ *
+ * PARAMETERS:
+ *  level	: the log level (INFO, FULL, ...) at which the object is dumped
+ *  obj		: A msg or avp object.
+ *
+ * DESCRIPTION: 
+ *   These functions dump the content of a message to the debug log
+ * either recursively or only the object itself.
+ *
+ * RETURN VALUE:
+ *   -
+ */
+void fd_msg_dump_walk ( int level, msg_or_avp *obj );
+void fd_msg_dump_one  ( int level, msg_or_avp *obj );
+
+
+/*********************************************/
+/*   Message metadata management functions   */
+/*********************************************/
+/*
+ * FUNCTION:	fd_msg_model
+ *
+ * PARAMETERS:
+ *  reference 	: Pointer to a valid msg or avp.
+ *  model       : on success, pointer to the dictionary model of this command or AVP. NULL if the model is unknown.
+ *
+ * DESCRIPTION: 
+ *   Retrieve the dictionary object describing this message or avp. If the object is unknown or the fd_msg_parse_dict has not been called,
+ *  *model is set to NULL.
+ *
+ * RETURN VALUE:
+ *  0      	: The model has been set.
+ *  EINVAL 	: A parameter is invalid.
+ */
+int fd_msg_model ( msg_or_avp * reference, struct dict_object ** model );
+
+/*
+ * FUNCTION:	fd_msg_hdr
+ *
+ * PARAMETERS:
+ *  msg 	: Pointer to a valid message object.
+ *  pdata 	: Upon success, pointer to the msg_hdr structure of this message. The fields may be modified.
+ *
+ * DESCRIPTION: 
+ *   Retrieve location of modifiable section of a message. 
+ *
+ * RETURN VALUE:
+ *  0      	: The location has been written.
+ *  EINVAL 	: A parameter is invalid.
+ */
+int fd_msg_hdr ( struct msg *msg, struct msg_hdr ** pdata );
+
+/*
+ * FUNCTION:	fd_msg_avp_hdr
+ *
+ * PARAMETERS:
+ *  avp 	: Pointer to a valid avp object.
+ *  pdata 	: Upon success, pointer to the avp_hdr structure of this avp. The fields may be modified.
+ *
+ * DESCRIPTION: 
+ *   Retrieve location of modifiable data of an avp. 
+ *
+ * RETURN VALUE:
+ *  0      	: The location has been written.
+ *  EINVAL 	: A parameter is invalid.
+ */
+int fd_msg_avp_hdr ( struct avp *avp, struct avp_hdr ** pdata );
+
+/*
+ * FUNCTION:	fd_msg_answ_associate, fd_msg_answ_getq, fd_msg_answ_detach
+ *
+ * PARAMETERS:
+ *  answer	: the received answer message
+ *  query	: the corresponding query that had been sent
+ *
+ * DESCRIPTION:
+ *  fd_msg_answ_associate associates a query msg with the received answer. 
+ * Query is retrieved with fd_msg_answ_getq.
+ * If answer message is freed, the query is also freed.
+ * If the msg_answ_detach function is called, the association is removed.
+ * This is meant to be called from the daemon only.
+ *
+ * RETURN VALUE:
+ *  0 	  : ok
+ *  EINVAL: a parameter is invalid
+ */
+int fd_msg_answ_associate( struct msg * answer, struct msg * query );
+int fd_msg_answ_getq     ( struct msg * answer, struct msg ** query );
+int fd_msg_answ_detach   ( struct msg * answer );
+
+/*
+ * FUNCTION:	fd_msg_anscb_associate, fd_msg_anscb_get
+ *
+ * PARAMETERS:
+ *  msg		: the answer message
+ *  anscb	: the callback to associate with the message
+ *  data	: the data to pass to the callback
+ *  timeout     : (optional, use NULL if no timeout) a timeout associated with calling the cb.
+ *
+ * DESCRIPTION:
+ *  Associate or retrieve a callback with an answer message.
+ * This is meant to be called from the daemon only.
+ *
+ * RETURN VALUE:
+ *  0 	  : ok
+ *  EINVAL: a parameter is invalid
+ */
+int fd_msg_anscb_associate( struct msg * msg, void ( *anscb)(void *, struct msg **), void  * data, const struct timespec *timeout );
+int fd_msg_anscb_get      ( struct msg * msg, void (**anscb)(void *, struct msg **), void ** data );
+struct timespec *fd_msg_anscb_gettimeout( struct msg * msg ); /* returns NULL or a valid non-0 timespec */
+
+/*
+ * FUNCTION:	fd_msg_rt_associate, fd_msg_rt_get
+ *
+ * PARAMETERS:
+ *  msg		: the query message to be sent
+ *  list	: the ordered list of possible next-peers
+ *
+ * DESCRIPTION:
+ *  Associate a routing list with a query, and retrieve it.
+ * If the message is freed, the list is also freed.
+ *
+ * RETURN VALUE:
+ *  0 	  : ok
+ *  EINVAL: a parameter is invalid
+ */
+int fd_msg_rt_associate( struct msg * msg, struct rt_data ** rtd );
+int fd_msg_rt_get      ( struct msg * msg, struct rt_data ** rtd );
+
+/*
+ * FUNCTION:	fd_msg_is_routable
+ *
+ * PARAMETERS:
+ *  msg		: A msg object.
+ *
+ * DESCRIPTION: 
+ *   This function returns a boolean telling if a given message is routable in the Diameter network, 
+ *  or if it is a local link message only (ex: CER/CEA, DWR/DWA, ...).
+ *
+ * RETURN VALUE:
+ *  0      	: The message is not routable / an error occurred.
+ *  1      	: The message is routable.
+ */
+int fd_msg_is_routable ( struct msg * msg );
+
+/*
+ * FUNCTION:	fd_msg_source_(g/s)et
+ *
+ * PARAMETERS:
+ *  msg		: A msg object.
+ *  diamid	: The diameter id of the peer from which this message was received.
+ *  add_rr	: if true, a Route-Record AVP is added to the message with content diamid. In that case, dict must be supplied.
+ *  dict	: a dictionary with definition of Route-Record AVP (if add_rr is true)
+ *
+ * DESCRIPTION: 
+ *   Store or retrieve the diameted id of the peer from which this message was received.
+ * Will be used for example by the routing module to add the Route-Record AVP in forwarded requests,
+ * or to direct answers to the appropriate peer.
+ *
+ * RETURN VALUE:
+ *  0      	: Operation complete.
+ *  !0      	: an error occurred.
+ */
+int fd_msg_source_set( struct msg * msg, char * diamid, int add_rr, struct dictionary * dict );
+int fd_msg_source_get( struct msg * msg, char ** diamid );
+
+/*
+ * FUNCTION:	fd_msg_eteid_get
+ *
+ * PARAMETERS:
+ *  -
+ *
+ * DESCRIPTION: 
+ *   Get a new unique end-to-end id value for the local peer.
+ *
+ * RETURN VALUE:
+ *  The new assigned value. No error code is defined.
+ */
+uint32_t fd_msg_eteid_get ( void );
+
+
+/*
+ * FUNCTION:	fd_msg_sess_get
+ *
+ * PARAMETERS:
+ *  dict	: the dictionary that contains the Session-Id AVP definition
+ *  msg		: A valid message.
+ *  session	: Location to store the session pointer when retrieved.
+ *  isnew	: Indicates if the session has been created.
+ *
+ * DESCRIPTION:
+ *  This function retrieves or creates the session object corresponding to a message.
+ * If the message does not contain a Session-Id AVP, *session == NULL on return.
+ * Note that the Session-Id AVP must never be modified after created in a message.
+ *
+ * RETURN VALUE:
+ *  0 : success
+ * !0 : standard error code.
+ */
+int fd_msg_sess_get(struct dictionary * dict, struct msg * msg, struct session ** session, int * isnew);
+
+/***************************************/
+/*   Manage AVP values                 */
+/***************************************/
+
+/*
+ * FUNCTION:	fd_msg_avp_setvalue
+ *
+ * PARAMETERS:
+ *  avp 	: Pointer to a valid avp object with a NULL avp_value pointer. The model must be known.
+ *  value 	: pointer to an avp_value. The content will be COPIED into the internal storage area. 
+ *		 If data type is an octetstring, the data is also copied.
+ * 		 If value is a NULL pointer, the previous data is erased and value is unset in the AVP.
+ *
+ * DESCRIPTION: 
+ *   Initialize the avp_value field of an AVP header.
+ *
+ * RETURN VALUE:
+ *  0      	: The avp_value pointer has been set.
+ *  EINVAL 	: A parameter is invalid.
+ */
+int fd_msg_avp_setvalue ( struct avp *avp, union avp_value *value );
+
+/*
+ * FUNCTION:	fd_msg_avp_value_encode
+ *
+ * PARAMETERS:
+ *  avp 	: Pointer to a valid avp object with a NULL avp_value. The model must be known.
+ *  data 	: Pointer to the data that must be encoded as AVP value and stored in the AVP.
+ *		 This is only valid for AVPs of derived type for which type_data_encode callback is set. (ex: Address type)
+ *
+ * DESCRIPTION: 
+ *   Initialize the avp_value field of an AVP object from formatted data, using the AVP's type "type_data_encode" callback.
+ *
+ * RETURN VALUE:
+ *  0      	: The avp_value has been set.
+ *  EINVAL 	: A parameter is invalid.
+ *  ENOTSUP 	: There is no appropriate callback registered with this AVP's type.
+ */
+int fd_msg_avp_value_encode ( void *data, struct avp *avp );
+/*
+ * FUNCTION:	fd_msg_avp_value_interpret
+ *
+ * PARAMETERS:
+ *  avp 	: Pointer to a valid avp object with a non-NULL avp_value value.
+ *  data 	: Upon success, formatted interpretation of the AVP value is stored here.
+ *
+ * DESCRIPTION: 
+ *   Interpret the content of an AVP of Derived type and store the result in data pointer. The structure
+ * of the data pointer is dependent on the AVP type. This function calls the "type_data_interpret" callback 
+ * of the type.
+ *
+ * RETURN VALUE:
+ *  0      	: The avp_value has been set.
+ *  EINVAL 	: A parameter is invalid.
+ *  ENOTSUP 	: There is no appropriate callback registered with this AVP's type.
+ */
+int fd_msg_avp_value_interpret ( struct avp *avp, void *data );
+
+
+/***************************************/
+/*   Message parsing functions         */
+/***************************************/
+
+/*
+ * FUNCTION:	fd_msg_bufferize
+ *
+ * PARAMETERS:
+ *  msg		: A valid msg object. All AVPs must have a value set. 
+ *  buffer 	: Upon success, this points to a buffer (malloc'd) containing the message ready for network transmission (or security transformations). 
+ *		 The buffer may be freed after use.
+ *  len		: if not NULL, the size of the buffer is written here. In any case, this size is updated in the msg header.
+ *
+ * DESCRIPTION: 
+ *   Renders a message in memory as a buffer that can be sent over the network to the next peer.
+ *
+ * RETURN VALUE:
+ *  0      	: The location has been written.
+ *  EINVAL 	: The buffer does not contain a valid Diameter message.
+ *  ENOMEM	: Unable to allocate enough memory to create the buffer object.
+ */
+int fd_msg_bufferize ( struct msg * msg, unsigned char ** buffer, size_t * len );
+
+/*
+ * FUNCTION:	fd_msg_parse_buffer
+ *
+ * PARAMETERS:
+ *  buffer 	: Pointer to a buffer containing a message received from the network. 
+ *  buflen	: the size in bytes of the buffer.
+ *  msg		: Upon success, this points to a valid msg object. No AVP value is resolved in this object, nor grouped AVP.
+ *
+ * DESCRIPTION: 
+ *   This function parses a buffer an creates a msg object to represent the structure of the message.
+ *  Since no dictionary lookup is performed, the values of the AVPs are not interpreted. To interpret the values,
+ *  the returned message object must be passed to fd_msg_parse_dict function.
+ *  The buffer pointer is saved inside the message and will be freed when not needed anymore.
+ *
+ * RETURN VALUE:
+ *  0      	: The location has been written.
+ *  ENOMEM	: Unable to allocate enough memory to create the msg object.
+ *  EBADMSG	: The buffer does not contain a valid Diameter message (or is truncated).
+ *  EINVAL 	: A parameter is invalid.
+ */
+int fd_msg_parse_buffer ( unsigned char ** buffer, size_t buflen, struct msg ** msg );
+
+/* Parsing Error Information structure */
+struct fd_pei {
+	char *		pei_errcode;	/* name of the error code to use */
+	struct avp *	pei_avp;	/* pointer to invalid or missing AVP (to be freed) */
+	char *		pei_message;	/* Overwrite default message if needed */
+	int		pei_protoerr; 	/* do we set the 'E' bit in the error message ? */
+};
+
+/*
+ * FUNCTION:	fd_msg_parse_dict
+ *
+ * PARAMETERS:
+ *  object	: A msg or AVP object as returned by fd_msg_parse_buffer.
+ *  dict	: the dictionary containing the objects definitions to use for resolving all AVPs.
+ *  error_info	: If not NULL, will contain the detail about error upon return. May be used to generate an error reply.
+ *
+ * DESCRIPTION: 
+ *   This function looks up for the command and each children AVP definitions in the dictionary.
+ *  If the dictionary definition is found, avp_model is set and the value of the AVP is interpreted accordingly and:
+ *   - for grouped AVPs, the children AVP are created and interpreted also.
+ *   - for numerical AVPs, the value is converted to host byte order and saved in the avp_value field.
+ *   - for octetstring AVPs, the string is copied into a new buffer and its address is saved in avp_value. 
+ *  If the dictionary definition is not found, avp_model is set to NULL and
+ *  the content of the AVP is saved as an octetstring in an internal structure. avp_value is NULL.
+ *  As a result, after this function has been called, there is no more dependency of the msg object to the message buffer, that is freed.
+ *
+ * RETURN VALUE:
+ *  0      	: The message has been fully parsed as described.
+ *  EINVAL 	: The msg parameter is invalid for this operation.
+ *  ENOMEM	: Unable to allocate enough memory to complete the operation.
+ *  ENOTSUP	: No dictionary definition for the command or one of the mandatory AVP was found.
+ */
+int fd_msg_parse_dict ( msg_or_avp * object, struct dictionary * dict, struct fd_pei * error_info );
+
+/*
+ * FUNCTION:	fd_msg_parse_rules
+ *
+ * PARAMETERS:
+ *  object	: A msg or grouped avp object that must be verified.
+ *  dict	: The dictionary containing the rules definitions.
+ *  error_info	: If not NULL, the first problem information will be saved here.
+ *
+ * DESCRIPTION: 
+ *   Check that the children of the object do not conflict with the dictionary rules (ABNF compliance).
+ *
+ * RETURN VALUE:
+ *  0      	: The message has been fully parsed and complies to the defined rules.
+ *  EBADMSG	: A conflict was detected, or a mandatory AVP is unknown in the dictionary.
+ *  EINVAL 	: The msg or avp object is invalid for this operation.
+ *  ENOMEM	: Unable to allocate enough memory to complete the operation.
+ */
+int fd_msg_parse_rules ( msg_or_avp * object, struct dictionary * dict, struct fd_pei * error_info);
+
+
+
+/*
+ * FUNCTION:	fd_msg_update_length
+ *
+ * PARAMETERS:
+ *  object 	: Pointer to a valid msg or avp. 
+ *
+ * DESCRIPTION: 
+ *   Update the length field of the object passed as parameter.
+ * As a side effect, all children objects are also updated. Therefore, all avp_value fields of
+ * the children AVPs must be set, or an error will occur.
+ *
+ * RETURN VALUE:
+ *  0      	: The size has been recomputed.
+ *  EINVAL 	: A parameter is invalid.
+ */
+int fd_msg_update_length ( msg_or_avp * object );
+
+
+/*============================================================*/
+/*                         DISPATCH                           */
+/*============================================================*/
+
+/* Dispatch module (passing incoming messages to extensions registered callbacks)
+ * is split between the library and the daemon.
+ *
+ * The library provides the support for associating dispatch callbacks with
+ * dictionary objects.
+ *
+ * The daemon is responsible for calling the callbacks for a message when appropriate.
+ *
+ *
+ * The dispatch module has two main roles:
+ *  - help determine if a message can be handled locally (during the routing step)
+ *        This decision involves only the application-id of the message.
+ *  - pass the message to the callback(s) that will handle it (during the dispatch step)
+ *
+ * The first role is handled by the daemon.
+ *
+ * About the second, these are the possibilities for registering a dispatch callback:
+ *
+ * -> For All messages.
+ *  This callback is called for all messages that are handled locally. This should be used only
+ *  for debug purpose.
+ *
+ * -> by AVP value (constants only).
+ *  This callback will be called when a message is received and contains an AVP with a specified enumerated value.
+ *
+ * -> by AVP.
+ *  This callback will be called when the received message contains a certain AVP.
+ *
+ * -> by command-code.
+ *  This callback will be called when the message is a specific command (and 'R' flag).
+ *
+ * -> by application.
+ *  This callback will be called when the message has a specific application-id.
+ *
+ * ( by vendor: would this be useful? it may be added later)
+ */
+enum disp_how {
+	DISP_HOW_ANY = 1,		/* Any message. This should be only used for debug. */
+	DISP_HOW_APPID,			/* Any message with the specified application-id */
+	DISP_HOW_CC,			/* Messages of the specified command-code (request or answer). App id may be specified. */
+	DISP_HOW_AVP,			/* Messages containing a specific AVP. Command-code and App id may be specified. */
+	DISP_HOW_AVP_ENUMVAL		/* Messages containing a specific AVP with a specific enumerated value. Command-code and App id may be specified. */
+};
+/*
+ * Several criteria may be selected at the same time, for example command-code AND application id.
+ *
+ * If several callbacks are registered for the same object, they are called in the order they were registered.
+ * The order in which the callbacks are called is:
+ *  DISP_HOW_ANY
+ *  DISP_HOW_AVP_ENUMVAL & DISP_HOW_AVP
+ *  DISP_HOW_CC
+ *  DISP_HOW_APPID
+ */
+
+/* When a callback is registered, a "when" argument is passed in addition to the disp_how value,
+ * to specify which values the criteria must match. */
+struct disp_when {
+	struct dict_object *	app;
+	struct dict_object *	command;
+	struct dict_object *	avp;
+	struct dict_object *	value;
+};
+
+/* Note that all the dictionary objects should really belong to the same dictionary!
+ *
+ * Here is the details on this "when" argument, depending on the disp_how value.
+ *
+ * DISP_HOW_ANY.
+ *  In this case, "when" must be NULL.
+ *
+ * DISP_HOW_APPID.
+ *  Only the "app_id" field must be set, other fields are ignored. It points to a dictionary object of type DICT_APPLICATION.
+ *
+ * DISP_HOW_CC.
+ *  The "command" field must be defined and point to a dictionary object of type DICT_COMMAND.
+ *  The "app_id" may be also set. In the case it is set, it restricts the callback to be called only with this command-code and app id.
+ *  The other fields are ignored.
+ *
+ * DISP_HOW_AVP.
+ *  The "avp" field of the structure must be set and point to a dictionary object of type DICT_AVP.
+ *  The "app_id" field may be set to restrict the messages matching to a specific app id.
+ *  The "command" field may also be set to a valid DICT_COMMAND object.
+ *  The content of the "value" field is ignored.
+ *
+ * DISP_HOW_AVP_ENUMVAL.
+ *  All fields have the same constraints and meaning as in DISP_REG_AVP. In addition, the "value" field must be set
+ *  and points to a valid DICT_ENUMVAL object. 
+ *
+ * Here is a sumary of the fields: ( M : must be set; m : may be set; 0 : ignored )
+ *  field:     app_id    command     avp    value
+ * APPID :       M          0         0       0
+ * CC    :       m          M         0       0
+ * AVP   :       m          m         M       0
+ * ENUMVA:       m          m         M       M
+ */
+
+enum disp_action {
+	DISP_ACT_CONT,	/* The next handler should be called, unless *msg == NULL. */
+	DISP_ACT_SEND,	/* The updated message must be sent. No further callback is called. */
+	DISP_ACT_ERROR	/* An error must be created and sent as a reply -- not valid for callbacks, only for fd_msg_dispatch. */
+};
+/* The callbacks that are registered have the following prototype:
+ *  	int dispatch_callback( struct msg ** msg, struct avp * avp, struct session * session, enum disp_action * action );
+ *
+ * CALLBACK:	dispatch_callback
+ *
+ * PARAMETERS:
+ *  msg 	: the received message on function entry. may be updated to answer on return (see description)
+ *  avp 	: for callbacks registered with DISP_HOW_AVP or DISP_HOW_AVP_ENUMVAL, direct link to the triggering AVP.
+ *  session	: if the message contains a Session-Id AVP, the corresponding session object, NULL otherwise.
+ *  opaque      : An opaque pointer that is registered along the session handler.
+ *  action	: upon return, this tells the daemon what to do next.
+ *
+ * DESCRIPTION: 
+ *   Called when a received message matchs the condition for which the callback was registered.
+ * This callback may do any kind of processing on the message, including:
+ *  - create an answer for a request.
+ *  - proxy a request or message, add / remove the Proxy-Info AVP, then forward the message.
+ *  - update a routing table or start a connection with a new peer, then forward the message.
+ *  - ...
+ * 
+ * When *action == DISP_ACT_SEND on callback return, the msg pointed by *msg is passed to the routing module for sending.
+ * When *action == DISP_ACT_CONT, the next registered callback is called.
+ *  When the last callback gives also DISP_ACT_CONT action value, a default handler is called. It's behavior is as follow:
+ *   - if the message is an answer, it is discarded.
+ *   - if the message is a request, it is passed again to the routing stack, and marked as non-local handling.
+ *
+ * RETURN VALUE:
+ *  0      	: The callback executed successfully and updated *action appropriately.
+ *  !0		: standard errors. In case of error, the message is discarded.
+ */
+
+/* This structure represents a handler for a registered callback, allowing its de-registration */
+struct disp_hdl;
+
+/*
+ * FUNCTION:	fd_disp_register
+ *
+ * PARAMETERS:
+ *  cb 		  : The callback function to register (see dispatch_callback description above).
+ *  how	  	  : How the callback must be registered.
+ *  when          : Values that must match, depending on the how argument.
+ *  opaque        : A pointer that is passed back to the handler. The content is not interpreted by the framework.
+ *  handle        : On success, a handler to the registered callback is stored here if not NULL. 
+ *		   This handler can be used to unregister the cb.
+ *
+ * DESCRIPTION: 
+ *   Register a new callback to handle messages delivered locally.
+ *
+ * RETURN VALUE:
+ *  0      	: The callback is registered.
+ *  EINVAL 	: A parameter is invalid.
+ *  ENOMEM	: Not enough memory to complete the operation
+ */
+int fd_disp_register ( int (*cb)( struct msg **, struct avp *, struct session *, void *, enum disp_action *), 
+			enum disp_how how, struct disp_when * when, void * opaque, struct disp_hdl ** handle );
+
+/*
+ * FUNCTION:	fd_disp_unregister
+ *
+ * PARAMETERS:
+ *  handle       : Location of the handle of the callback that must be unregistered.
+ *  opaque       : If not NULL, the opaque data that was registered is restored here.
+ *
+ * DESCRIPTION: 
+ *   Removes a callback previously registered by fd_disp_register.
+ *
+ * RETURN VALUE:
+ *  0      	: The callback is unregistered.
+ *  EINVAL 	: A parameter is invalid.
+ */
+int fd_disp_unregister ( struct disp_hdl ** handle, void ** opaque );
+
+/* Destroy all handlers */
+void fd_disp_unregister_all ( void );
+
+/*
+ * FUNCTION:	fd_msg_dispatch
+ *
+ * PARAMETERS:
+ *  msg 	: A msg object that have already been fd_msg_parse_dict.
+ *  session	: The session corresponding to this object, if any.
+ *  action	: Upon return, the action that must be taken on the message
+ *
+ * DESCRIPTION: 
+ *   Call all handlers registered for a given message.
+ *  The session must have already been resolved on entry.
+ *  The msg pointed may be updated during this process.
+ *  Upon return, the action parameter points to what must be done next.
+ *
+ * RETURN VALUE:
+ *  0      	: Success.
+ *  EINVAL 	: A parameter is invalid.
+ *  (other errors)
+ */
+int fd_msg_dispatch ( struct msg ** msg, struct session * session, enum disp_action *action, const char ** error_code );
+
+
+
+/*============================================================*/
+/*                     QUEUES                                 */
+/*============================================================*/
+
+/* Management of FIFO queues of elements */
+
+/* A queue is an opaque object */
+struct fifo;
+
+/*
+ * FUNCTION:	fd_fifo_new
+ *
+ * PARAMETERS:
+ *  queue	: Upon success, a pointer to the new queue is saved here.
+ *
+ * DESCRIPTION: 
+ *  Create a new empty queue.
+ *
+ * RETURN VALUE :
+ *  0		: The queue has been initialized successfully.
+ *  EINVAL 	: The parameter is invalid.
+ *  ENOMEM	: Not enough memory to complete the creation.  
+ */
+int fd_fifo_new ( struct fifo ** queue );
+
+/*
+ * FUNCTION:	fd_fifo_del
+ *
+ * PARAMETERS:
+ *  queue	: Pointer to an empty queue to delete.
+ *
+ * DESCRIPTION: 
+ *  Destroys a queue. This is only possible if no thread is waiting for an element,
+ * and the queue is empty.
+ *
+ * RETURN VALUE:
+ *  0		: The queue has been destroyed successfully.
+ *  EINVAL 	: The parameter is invalid.
+ */
+int fd_fifo_del ( struct fifo  ** queue );
+
+/*
+ * FUNCTION:	fd_fifo_move
+ *
+ * PARAMETERS:
+ *  oldq	: Location of a FIFO that is to be emptied.
+ *  newq	: A FIFO that will receive the old data.
+ *  loc_update	: if non NULL, a place to store the pointer to new FIFO atomically with the move.
+ *
+ * DESCRIPTION: 
+ *  Empties a queue and move its content to another one atomically.
+ *
+ * RETURN VALUE:
+ *  0		: The queue has been destroyed successfully.
+ *  EINVAL 	: A parameter is invalid.
+ */
+int fd_fifo_move ( struct fifo * oldq, struct fifo * newq, struct fifo ** loc_update );
+
+/*
+ * FUNCTION:	fd_fifo_length
+ *
+ * PARAMETERS:
+ *  queue	: The queue from which to retrieve the number of elements.
+ *  length	: Upon success, the current number of elements in the queue is stored here.
+ *
+ * DESCRIPTION: 
+ *  Retrieve the number of elements in a queue.
+ *
+ * RETURN VALUE:
+ *  0		: The length of the queue has been written.
+ *  EINVAL 	: A parameter is invalid.
+ */
+int fd_fifo_length ( struct fifo * queue, int * length );
+int fd_fifo_length_noerr ( struct fifo * queue ); /* no error checking version */
+
+/*
+ * FUNCTION:	fd_fifo_setthrhd
+ *
+ * PARAMETERS:
+ *  queue	: The queue for which the thresholds are being set.
+ *  data	: An opaque pointer that is passed to h_cb and l_cb callbacks.
+ *  high        : The high-level threshold. If the number of elements in the queue increase to this value, h_cb is called.
+ *  h_cb        : if not NULL, a callback to call when the queue lengh is bigger than "high".
+ *  low         : The low-level threshold. Must be < high.
+ *  l_cb        : If the number of elements decrease to low, this callback is called.
+ *
+ * DESCRIPTION: 
+ *  This function allows to adjust the number of producer / consumer threads of a queue.
+ * If the consumer are slower than the producers, the number of elements in the queue increase.
+ * By setting a "high" value, we allow a callback to be called when this number is too high.
+ * The typical use would be to create an additional consumer thread in this callback.
+ * If the queue continues to grow, the callback will be called again when the length is 2 * high, then 3*high, ... N * high
+ * (the callback itself should implement a limit on the number of consumers that can be created)
+ * When the queue starts to decrease, and the number of elements go under ((N - 1) * high + low, the l_cb callback is called
+ * and would typially stop one of the consumer threads. If the queue continues to reduce, l_cb is again called at (N-2)*high + low,
+ * and so on.
+ *
+ * Since there is no destructor for the data pointer, if cleanup operations are required, they should be performed in
+ * l_cb when the length of the queue is becoming < low.
+ *
+ * Note that the callbacks are called synchronously, during fd_fifo_post or fd_fifo_get. Their operation should be quick.
+ *
+ * RETURN VALUE:
+ *  0		: The thresholds have been set
+ *  EINVAL 	: A parameter is invalid.
+ */
+int fd_fifo_setthrhd ( struct fifo * queue, void * data, uint16_t high, void (*h_cb)(struct fifo *, void **), uint16_t low, void (*l_cb)(struct fifo *, void **) );
+
+/*
+ * FUNCTION:	fd_fifo_post
+ *
+ * PARAMETERS:
+ *  queue	: The queue in which the element must be posted.
+ *  item	: The element that is put in the queue.
+ *
+ * DESCRIPTION: 
+ *  An element is added in a queue. Elements are retrieved from the queue in FIFO order
+ *  with the fd_fifo_get, fd_fifo_tryget, or fd_fifo_timedget functions.
+ *
+ * RETURN VALUE:
+ *  0		: The element is queued.
+ *  EINVAL 	: A parameter is invalid.
+ *  ENOMEM 	: Not enough memory to complete the operation.
+ */
+int fd_fifo_post_int ( struct fifo * queue, void ** item );
+#define fd_fifo_post(queue, item) \
+	fd_fifo_post_int((queue), (void *)(item))
+
+/*
+ * FUNCTION:	fd_fifo_get
+ *
+ * PARAMETERS:
+ *  queue	: The queue from which the first element must be retrieved.
+ *  item	: On return, the first element of the queue is stored here.
+ *
+ * DESCRIPTION: 
+ *  This function retrieves the first element from a queue. If the queue is empty, the function will block the 
+ * thread until a new element is posted to the queue, or until the thread is canceled (in which case the 
+ * function does not return).
+ *
+ * RETURN VALUE:
+ *  0		: A new element has been retrieved.
+ *  EINVAL 	: A parameter is invalid.
+ */
+int fd_fifo_get_int ( struct fifo * queue, void ** item );
+#define fd_fifo_get(queue, item) \
+	fd_fifo_get_int((queue), (void *)(item))
+
+/*
+ * FUNCTION:	fd_fifo_tryget
+ *
+ * PARAMETERS:
+ *  queue	: The queue from which the element must be retrieved.
+ *  item	: On return, the first element of the queue is stored here.
+ *
+ * DESCRIPTION: 
+ *  This function is similar to fd_fifo_get, except that it will not block if 
+ * the queue is empty, but return EWOULDBLOCK instead.
+ *
+ * RETURN VALUE:
+ *  0		: A new element has been retrieved.
+ *  EINVAL 	: A parameter is invalid.
+ *  EWOULDBLOCK : The queue was empty.
+ */
+int fd_fifo_tryget_int ( struct fifo * queue, void ** item );
+#define fd_fifo_tryget(queue, item) \
+	fd_fifo_tryget_int((queue), (void *)(item))
+
+/*
+ * FUNCTION:	fd_fifo_timedget
+ *
+ * PARAMETERS:
+ *  queue	: The queue from which the element must be retrieved.
+ *  item	: On return, the element is stored here.
+ *  abstime	: the absolute time until which we allow waiting for an item.
+ *
+ * DESCRIPTION: 
+ *  This function is similar to fd_fifo_get, except that it will block if the queue is empty 
+ * only until the absolute time abstime (see pthread_cond_timedwait for + info).
+ * If the queue is still empty when the time expires, the function returns ETIMEDOUT
+ *
+ * RETURN VALUE:
+ *  0		: A new item has been retrieved.
+ *  EINVAL 	: A parameter is invalid.
+ *  ETIMEDOUT   : The time out has passed and no item has been received.
+ */
+int fd_fifo_timedget_int ( struct fifo * queue, void ** item, const struct timespec *abstime );
+#define fd_fifo_timedget(queue, item, abstime) \
+	fd_fifo_timedget_int((queue), (void *)(item), (abstime))
+
+/* Dump a fifo list and optionally its inner elements -- beware of deadlocks! */
+void fd_fifo_dump(int level, char * name, struct fifo * queue, void (*dump_item)(int level, void * item));
+
+#endif /* _LIBFDPROTO_H */
Index: clude/freeDiameter/libfreeDiameter.h
===================================================================
--- include/freeDiameter/libfreeDiameter.h	(revision 653)
+++ 	(revision )
@@ -1,2814 +1,0 @@
-/*********************************************************************************************************
-* Software License Agreement (BSD License)                                                               *
-* Author: Sebastien Decugis <sdecugis@nict.go.jp>							 *
-*													 *
-* 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 S_OUT OF THE USE OF THIS SOFTWARE, EVEN IF   *
-* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.								 *
-*********************************************************************************************************/
-
-/* This file contains the definitions of functions and types used by the libfreeDiameter library.
- *
- * This library is meant to be used by both the freeDiameter daemon and its extensions.
- * It provides the tools to manipulate Diameter messages and related data.
- * This file should always be included as #include <freeDiameter/libfreeDiameter.h>
- *
- * If any change is made to this file, you must increment the FD_PROJECT_VERSION_API version.
- *
- * The file contains the following parts:
- *	DEBUG
- *	MACROS
- *	THREADS
- *	LISTS
- *	DICTIONARY
- *	SESSIONS
- *	MESSAGES
- *	DISPATCH
- *	QUEUES
- */
-
-#ifndef _LIBFREEDIAMETER_H
-#define _LIBFREEDIAMETER_H
-
-#ifndef FD_IS_CONFIG
-#error "You must include 'freeDiameter-host.h' before this file."
-#endif /* FD_IS_CONFIG */
-
-#include <pthread.h>
-#include <sched.h>
-#include <string.h>
-#include <assert.h>
-#include <errno.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-#include <sys/socket.h>
-#include <netdb.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-
-#ifdef DEBUG
-#include <libgen.h>	/* for basename if --dbg_file is specified */
-#endif /* DEBUG */
-
-/*============================================================*/
-/*                          INIT                              */
-/*============================================================*/
-
-/* This function must be called first, before any call to another library function */
-/* If the parameter is not 0, the support for signals (fd_sig_register) is enabled, otherwise it is disabled */
-/* The function must be called while the application is single-threaded to enable support for signals */
-int fd_lib_init(int support_signals);
-
-/* Call this one when the application terminates, to destroy internal threads */
-void fd_lib_fini(void);
-
-
-/*============================================================*/
-/*                          DEBUG                             */
-/*============================================================*/
-
-
-/*
- * FUNCTION:	fd_log_debug
- *
- * PARAMETERS:
- *  format 	: Same format string as in the printf function
- *  ...		: Same list as printf
- *
- * DESCRIPTION: 
- *  Log internal information for use of developpers only.
- * The format and arguments may contain UTF-8 encoded data. The
- * output medium (file or console) is expected to support this encoding.
- *
- * This function assumes that a global mutex called "fd_log_lock" exists
- * in the address space of the current process.
- *
- * RETURN VALUE:
- *  None.
- */
-void fd_log_debug ( const char * format, ... );
-extern pthread_mutex_t	fd_log_lock;
-extern char * fd_debug_one_function;
-extern char * fd_debug_one_file;
-
-/*
- * FUNCTION:	fd_log_threadname
- *
- * PARAMETERS:
- *  name 	: \0-terminated string containing a name to identify the current thread.
- *
- * DESCRIPTION: 
- *  Name the current thread, useful for debugging multi-threaded problems.
- *
- * This function assumes that a global thread-specific key called "fd_log_thname" exists
- * in the address space of the current process.
- *
- * RETURN VALUE:
- *  None.
- */
-void fd_log_threadname ( char * name );
-extern pthread_key_t	fd_log_thname;
-
-/*
- * FUNCTION:	fd_log_time
- *
- * PARAMETERS:
- *  ts	 	: The timestamp to log, or NULL for "now"
- *  buf 	: An array where the time must be stored
- *  len		: size of the buffer
- *
- * DESCRIPTION: 
- *  Writes the timestamp (in human readable format) in a buffer. 
- *
- * RETURN VALUE:
- *  pointer to buf.
- */
-char * fd_log_time ( struct timespec * ts, char * buf, size_t len );
-
-
-/*============================================================*/
-/*                    DEBUG MACROS                            */
-/*============================================================*/
-
-#ifndef ASSERT
-#define ASSERT(x) assert(x)
-#endif /* ASSERT */
-
-/* levels definitions */
-#define NONE 0	/* Display no debug message */
-#define INFO 1	/* Display errors only */
-#define FULL 2  /* Display additional information to follow code execution */
-#define ANNOYING 4 /* Very verbose, for example in loops */
-#define FCTS 6  /* Display entry parameters of most functions */
-#define CALL 9  /* Display calls to most functions (with CHECK macros) */
-
-/* Default level is INFO */
-#ifndef TRACE_LEVEL 
-#define TRACE_LEVEL INFO
-#endif /* TRACE_LEVEL */
-
-/* The level of the file being compiled. */
-static int local_debug_level = TRACE_LEVEL;
-
-/* A global level, changed by configuration or cmd line for example. default is 0. */
-extern int fd_g_debug_lvl;
-
-/* Some portability code to get nice function name in __PRETTY_FUNCTION__ */
-#if __STDC_VERSION__ < 199901L
-# if __GNUC__ >= 2
-#  define __func__ __FUNCTION__
-# else /* __GNUC__ >= 2 */
-#  define __func__ "<unknown>"
-# endif /* __GNUC__ >= 2 */
-#endif /* __STDC_VERSION__ < 199901L */
-#ifndef __PRETTY_FUNCTION__
-#define __PRETTY_FUNCTION__ __func__
-#endif /* __PRETTY_FUNCTION__ */
-
-/* A version of __FILE__ without the full path */
-static char * file_bname = NULL;
-#define __STRIPPED_FILE__	(file_bname ?: (file_bname = basename((char *)__FILE__)))
-
-
-/* Boolean for tracing at a certain level */
-#ifdef DEBUG
-#define TRACE_BOOL(_level_) ( ((_level_) <= local_debug_level + fd_g_debug_lvl) 					\
-				|| (fd_debug_one_function && !strcmp(fd_debug_one_function, __PRETTY_FUNCTION__)) 	\
-				|| (fd_debug_one_file && !strcmp(fd_debug_one_file, __STRIPPED_FILE__) ) )
-#else /* DEBUG */
-#define TRACE_BOOL(_level_) ((_level_) <= local_debug_level + fd_g_debug_lvl)
-#endif /* DEBUG */
-
-
-/*************
- The general debug macro, each call results in two lines of debug messages (change the macro for more compact output) 
- *************/
-#ifdef DEBUG
-/* In DEBUG mode, we add (a lot of) meta-information along each trace. This makes multi-threading problems easier to debug. */
-#define TRACE_DEBUG(level,format,args... ) {											\
-	if ( TRACE_BOOL(level) ) {												\
-		char __buf[25];													\
-		const char * __thn = ((char *)pthread_getspecific(fd_log_thname) ?: "unnamed");					\
-		fd_log_debug("\t | tid:%-20s\t%s\tin %s@%s:%d\n"								\
-			  "\t%s|%*s" format "\n",  										\
-					__thn, fd_log_time(NULL, __buf, sizeof(__buf)), __PRETTY_FUNCTION__, __FILE__, __LINE__,\
-					(level < FULL)?"@":" ",level, "", ## args); 						\
-	}															\
-}
-#else /* DEBUG */
-/* Do not print thread, function, ... only the message itself in this case, unless the debug level is set > FULL. */
-#define TRACE_DEBUG(level,format,args... ) {												\
-	if ( TRACE_BOOL(level) ) {													\
-		if (fd_g_debug_lvl > FULL) {												\
-			char __buf[25];													\
-			const char * __thn = ((char *)pthread_getspecific(fd_log_thname) ?: "unnamed");					\
-			fd_log_debug("\t | tid:%-20s\t%s\tin %s@%s:%d\n"								\
-				  "\t%s|%*s" format "\n",  										\
-						__thn, fd_log_time(NULL, __buf, sizeof(__buf)), __PRETTY_FUNCTION__, __FILE__, __LINE__,\
-						(level < FULL)?"@":" ",level, "", ## args); 						\
-		} else {														\
-			fd_log_debug(format "\n", ## args); 										\
-		}															\
-	}																\
-}
-#endif /* DEBUG */
-
-/*************
- Derivatives from this macro 
- ************/
-/* Helper for function entry -- for very detailed trace of the execution */
-#define TRACE_ENTRY(_format,_args... ) \
-	TRACE_DEBUG(FCTS, "[enter] %s(" _format ") {" #_args "}", __PRETTY_FUNCTION__, ##_args );
-
-/* Helper for debugging by adding traces -- for debuging a specific location of the code */
-#define TRACE_HERE()	\
-	TRACE_DEBUG(NONE, " -- debug checkpoint %d -- ", fd_breakhere());
-int fd_breakhere(void);
-
-/* Helper for tracing the CHECK_* macros bellow -- very very verbose code execution! */
-#define TRACE_DEBUG_ALL( str ) 	\
-	TRACE_DEBUG(CALL, str );
-
-/* For development only, to keep track of TODO locations in the code */
-#ifndef ERRORS_ON_TODO
-#define TODO( _msg, _args... ) \
-	TRACE_DEBUG(NONE, "TODO: " _msg , ##_args);
-#else /* ERRORS_ON_TODO */
-#define TODO( _msg, _args... ) \
-	"TODO" = _msg ## _args; /* just a stupid compilation error to spot the todo */
-#endif /* ERRORS_ON_TODO */
-
-/* Trace a binary buffer content */
-#define TRACE_DEBUG_BUFFER(level, prefix, buf, bufsz, suffix ) {								\
-	if ( TRACE_BOOL(level) ) {												\
-		char __ts[25];													\
-		int __i;													\
-		size_t __sz = (size_t)(bufsz);											\
-		uint8_t * __buf = (uint8_t *)(buf);										\
-		char * __thn = ((char *)pthread_getspecific(fd_log_thname) ?: "unnamed");					\
-		fd_log_debug("\t | tid:%-20s\t%s\tin %s@%s:%d\n"								\
-			  "\t%s|%*s" prefix ,  											\
-					__thn, fd_log_time(NULL, __ts, sizeof(__ts)), __PRETTY_FUNCTION__, __FILE__, __LINE__,	\
-					(level < FULL)?"@":" ",level, ""); 							\
-		for (__i = 0; __i < __sz; __i++) {										\
-			fd_log_debug("%02.2hhx", __buf[__i]);									\
-		}														\
-		fd_log_debug(suffix "\n");											\
-	}															\
-}
-
-/* Some aliases to socket addresses structures */
-#define sSS	struct sockaddr_storage
-#define sSA	struct sockaddr
-#define sSA4	struct sockaddr_in
-#define sSA6	struct sockaddr_in6
-
-/* The sockaddr length of a sSS structure */
-#define sSAlen( _sa_ )	\
-	( (socklen_t) ( (((sSA *)_sa_)->sa_family == AF_INET) ? (sizeof(sSA4)) :		\
-				((((sSA *)_sa_)->sa_family == AF_INET6) ? (sizeof(sSA6)) :	\
-					0 ) ) )
-
-/* Dump one sockaddr Node information */
-#define sSA_DUMP_NODE( sa, flag ) {				\
-	sSA * __sa = (sSA *)(sa);				\
-	char __addrbuf[INET6_ADDRSTRLEN];			\
-	if (__sa) {						\
-	  int __rc = getnameinfo(__sa, 				\
-	  		sSAlen(__sa),				\
-			__addrbuf,				\
-			sizeof(__addrbuf),			\
-			NULL,					\
-			0,					\
-			flag);					\
-	  if (__rc)						\
-	  	fd_log_debug("%s", (char *)gai_strerror(__rc));	\
-	  else							\
-	  	fd_log_debug("%s", &__addrbuf[0]);		\
-	} else {						\
-		fd_log_debug("(NULL / ANY)");			\
-	}							\
-}
-/* Same but with the port (service) also */
-#define sSA_DUMP_NODE_SERV( sa, flag ) {				\
-	sSA * __sa = (sSA *)(sa);					\
-	char __addrbuf[INET6_ADDRSTRLEN];				\
-	char __servbuf[32];						\
-	if (__sa) {							\
-	  int __rc = getnameinfo(__sa, 					\
-	  		sSAlen(__sa),					\
-			__addrbuf,					\
-			sizeof(__addrbuf),				\
-			__servbuf,					\
-			sizeof(__servbuf),				\
-			flag);						\
-	  if (__rc)							\
-	  	fd_log_debug("%s", (char *)gai_strerror(__rc));		\
-	  else								\
-	  	fd_log_debug("[%s]:%s", &__addrbuf[0],&__servbuf[0]);	\
-	} else {							\
-		fd_log_debug("(NULL / ANY)");				\
-	}								\
-}
-
-/* Inside a debug trace */
-#define TRACE_DEBUG_sSA(level, prefix, sa, flags, suffix ) {										\
-	if ( TRACE_BOOL(level) ) {												\
-		char __buf[25];													\
-		char * __thn = ((char *)pthread_getspecific(fd_log_thname) ?: "unnamed");					\
-		fd_log_debug("\t | tid:%-20s\t%s\tin %s@%s:%d\n"								\
-			  "\t%s|%*s" prefix ,  											\
-					__thn, fd_log_time(NULL, __buf, sizeof(__buf)), __PRETTY_FUNCTION__, __FILE__, __LINE__,\
-					(level < FULL)?"@":" ",level, ""); 							\
-		sSA_DUMP_NODE_SERV( sa, flags );										\
-		fd_log_debug(suffix "\n");											\
-	}															\
-}
-
-/* Report an error */
-#define TRACE_DEBUG_ERROR(format,args... ) \
-	TRACE_DEBUG(NONE, format, ##args)
-
-/******************
- Optimized code: remove all debugging code
- **/
-#ifdef STRIP_DEBUG_CODE
-#undef TRACE_DEBUG
-#undef TRACE_BOOL
-#undef TRACE_DEBUG_sSA
-#undef TRACE_DEBUG_BUFFER
-#undef TRACE_DEBUG_ERROR
-#define TRACE_DEBUG(level,format,args... )
-#define TRACE_BOOL(_level_) (0)
-#define TRACE_DEBUG_BUFFER(level, prefix, buf, bufsz, suffix )
-#define TRACE_DEBUG_sSA(level, prefix, sa, flags, suffix )
-#define TRACE_DEBUG_ERROR(format,args... ) {	\
-	fd_log_debug(format "\n", ## args); 	\
-}
-#endif /* STRIP_DEBUG_CODE */
-
-
-/*============================================================*/
-/*                  ERROR CHECKING MACRO                      */
-/*============================================================*/
-
-/* Macros to check a return value and branch out in case of error.
- * These macro should be used only when errors are improbable, not for expected errors.
- */
-
-/* Check the return value of a system function and execute fallback in case of error */
-#define CHECK_SYS_DO( __call__, __fallback__  ) { 					\
-	int __ret__;									\
-	TRACE_DEBUG_ALL( "Check SYS: " #__call__ );					\
-	__ret__ = (__call__);								\
-	if (__ret__ < 0) {								\
-		int __err__ = errno;	/* We may handle EINTR here */			\
-		TRACE_DEBUG_ERROR("ERROR: in '" #__call__ "' :\t%s", strerror(__err__));\
-		__fallback__;								\
-	}										\
-}
-/* Check the return value of a system function, return error code on error */
-#define CHECK_SYS( __call__  ) { 							\
-	int __ret__;									\
-	TRACE_DEBUG_ALL( "Check SYS: " #__call__ );					\
-	__ret__ = (__call__);								\
-	if (__ret__ < 0) {								\
-		int __err__ = errno;	/* We may handle EINTR here */			\
-		TRACE_DEBUG_ERROR("ERROR: in '" #__call__ "' :\t%s", strerror(__err__));\
-		return __err__;								\
-	}										\
-}
-
-/* Check the return value of a POSIX function and execute fallback in case of error or special value */
-#define CHECK_POSIX_DO2( __call__, __speval__, __fallback1__, __fallback2__ ) {			\
-	int __ret__;										\
-	TRACE_DEBUG_ALL( "Check POSIX: " #__call__ );						\
-	__ret__ = (__call__);									\
-	if (__ret__ != 0) {									\
-		if (__ret__ == (__speval__)) {							\
-			__fallback1__;								\
-		} else {									\
-			TRACE_DEBUG_ERROR("ERROR: in '" #__call__ "':\t%s", strerror(__ret__));	\
-			__fallback2__;								\
-		}										\
-	}											\
-}
-
-/* Check the return value of a POSIX function and execute fallback in case of error */
-#define CHECK_POSIX_DO( __call__, __fallback__ ) 					\
-	CHECK_POSIX_DO2( (__call__), 0, , __fallback__ );
-
-/* Check the return value of a POSIX function and return it if error */
-#define CHECK_POSIX( __call__ ) { 							\
-	int __v__;									\
-	CHECK_POSIX_DO( __v__ = (__call__), return __v__ );				\
-}
-
-/* Check that a memory allocator did not return NULL, otherwise log an error and execute fallback */
-#define CHECK_MALLOC_DO( __call__, __fallback__ ) { 					\
-	void *  __ret__;								\
-	TRACE_DEBUG_ALL( "Check MALLOC: " #__call__ );					\
-	__ret__ = (void *)( __call__ );							\
-	if (__ret__ == NULL) {								\
-		int __err__ = errno;							\
-		TRACE_DEBUG_ERROR("ERROR: in '" #__call__ "':\t%s", strerror(__err__));	\
-		__fallback__;								\
-	}										\
-}
-
-/* Check that a memory allocator did not return NULL, otherwise return ENOMEM */
-#define CHECK_MALLOC( __call__ )							\
-	CHECK_MALLOC_DO( __call__, return ENOMEM );
-
-
-/* Check parameters at function entry, execute fallback on error */
-#define CHECK_PARAMS_DO( __bool__, __fallback__ )						\
-	TRACE_DEBUG_ALL( "Check PARAMS: " #__bool__ );						\
-	if ( ! (__bool__) ) {									\
-		TRACE_DEBUG_ERROR("Warning: Invalid parameter received in '" #__bool__ "'");	\
-		__fallback__;									\
-	}
-/* Check parameters at function entry, return EINVAL if the boolean is false (similar to assert) */
-#define CHECK_PARAMS( __bool__ )							\
-	CHECK_PARAMS_DO( __bool__, return EINVAL );
-
-/* Check the return value of an internal function, log and propagate */
-#define CHECK_FCT_DO( __call__, __fallback__ ) {					\
-	int __ret__;									\
-	TRACE_DEBUG_ALL( "Check FCT: " #__call__ );					\
-	__ret__ = (__call__);								\
-	if (__ret__ != 0) {								\
-		TRACE_DEBUG_ERROR("ERROR: in '" #__call__ "':\t%s", strerror(__ret__));	\
-		__fallback__;								\
-	}										\
-}
-/* Check the return value of a function call, return any error code */
-#define CHECK_FCT( __call__ ) {								\
-	int __v__;									\
-	CHECK_FCT_DO( __v__ = (__call__), return __v__ );				\
-}
-
-
-
-/*============================================================*/
-/*                  OTHER MACROS                              */
-/*============================================================*/
-
-
-/* helper macros (pre-processor hacks to allow macro arguments) */
-#define __tostr( arg )  #arg
-#define _stringize( arg ) __tostr( arg )
-#define __agr( arg1, arg2 ) arg1 ## arg2
-#define _aggregate( arg1, arg2 ) __agr( arg1, arg2 )
-
-
-/* A l4 protocol name (TCP / SCTP) */
-#ifdef DISABLE_SCTP
-#define IPPROTO_NAME( _proto )					\
-	(((_proto) == IPPROTO_TCP) ? "TCP" :			\
-			"Unknown")
-#else /* DISABLE_SCTP */
-#define IPPROTO_NAME( _proto )					\
-	( ((_proto) == IPPROTO_TCP) ? "TCP" :			\
-		(((_proto) == IPPROTO_SCTP) ? "SCTP" :		\
-			"Unknown"))
-#endif /* DISABLE_SCTP */
-
-/* Define the value of IP loopback address */
-#ifndef INADDR_LOOPBACK 
-#define INADDR_LOOPBACK	inet_addr("127.0.0.1")
-#endif /* INADDR_LOOPBACK */
-
-#ifndef INADDR_BROADCAST
-#define	INADDR_BROADCAST	((in_addr_t) 0xffffffff)
-#endif /* INADDR_BROADCAST */
-
-/* An IP equivalent to IN6_IS_ADDR_LOOPBACK */
-#ifndef IN_IS_ADDR_LOOPBACK
-#define IN_IS_ADDR_LOOPBACK(a) \
-  ((((long int) (a)->s_addr) & ntohl(0xff000000)) == ntohl(0x7f000000))
-#endif /* IN_IS_ADDR_LOOPBACK */
-
-/* An IP equivalent to IN6_IS_ADDR_UNSPECIFIED */
-#ifndef IN_IS_ADDR_UNSPECIFIED
-#define IN_IS_ADDR_UNSPECIFIED(a) \
-  (((long int) (a)->s_addr) == 0x00000000)
-#endif /* IN_IS_ADDR_UNSPECIFIED */
-
-/* create a V4MAPPED address */
-#define IN6_ADDR_V4MAP( a6, a4 ) {			\
-	((uint32_t *)(a6))[0] = 0;			\
-	((uint32_t *)(a6))[1] = 0;			\
-	((uint32_t *)(a6))[2] = htonl(0xffff);		\
-	((uint32_t *)(a6))[3] = (uint32_t)(a4);		\
-}
-
-/* Retrieve a v4 value from V4MAPPED address ( takes a s6_addr as param) */
-#define IN6_ADDR_V4UNMAP( a6 ) 				\
-	(((in_addr_t *)(a6))[3])
-
-
-/* We provide macros to convert 64 bit values to and from network byte-order, on systems where it is not already provided. */
-#ifndef HAVE_NTOHLL	/* Defined by the cmake step, if the ntohll symbol is defined on the system */
-# if HOST_BIG_ENDIAN
-    /* In big-endian systems, we don't have to change the values, since the order is the same as network */
-#   define ntohll(x) (x)
-#   define htonll(x) (x)
-# else /* HOST_BIG_ENDIAN */
-    /* For these systems, we must reverse the bytes. Use ntohl and htonl on sub-32 blocs, and inverse these blocs. */
-#   define ntohll(x) (typeof (x))( (((uint64_t)ntohl( (uint32_t)(x))) << 32 ) | ((uint64_t) ntohl( ((uint64_t)(x)) >> 32 ))) 
-#   define htonll(x) (typeof (x))( (((uint64_t)htonl( (uint32_t)(x))) << 32 ) | ((uint64_t) htonl( ((uint64_t)(x)) >> 32 ))) 
-# endif /* HOST_BIG_ENDIAN */
-#endif /* HAVE_NTOHLL */
-
-/* This macro will give the next multiple of 4 for an integer (used for padding sizes of AVP). */
-#define PAD4(_x) ((_x) + ( (4 - (_x)) & 3 ) )
-
-/* Useful to display any value as (safe) ASCII (will garbage UTF-8 output...) */
-#define ASCII(_c) ( ((_c < 32) || (_c > 127)) ? ( _c ? '?' : ' ' ) : _c )
-
-/* Compare timespec structures */
-#define TS_IS_INFERIOR( ts1, ts2 ) 		\
-	(    ((ts1)->tv_sec  < (ts2)->tv_sec ) 	\
-	  || (((ts1)->tv_sec  == (ts2)->tv_sec ) && ((ts1)->tv_nsec < (ts2)->tv_nsec) ))
-
-/* This gives a good size for buffered reads */
-#ifndef BUFSIZ
-#define BUFSIZ 96
-#endif /* BUFSIZ */
-
-
-
-/*============================================================*/
-/*                          THREADS                           */
-/*============================================================*/
-
-/* Terminate a thread */
-static __inline__ int fd_thr_term(pthread_t * th)
-{
-	void * th_ret = NULL;
-	
-	CHECK_PARAMS(th);
-	
-	/* Test if it was already terminated */
-	if (*th == (pthread_t)NULL)
-		return 0;
-	
-	/* Cancel the thread if it is still running - ignore error if it was already terminated */
-	(void) pthread_cancel(*th);
-	
-	/* Then join the thread */
-	CHECK_POSIX( pthread_join(*th, &th_ret) );
-	
-	if (th_ret == PTHREAD_CANCELED) {
-		TRACE_DEBUG(ANNOYING, "The thread %p was canceled", *th);
-	} else {
-		TRACE_DEBUG(CALL, "The thread %p returned %x", *th, th_ret);
-	}
-	
-	/* Clean the location */
-	*th = (pthread_t)NULL;
-	
-	return 0;
-}
-
-/* Force flushing the cache of a CPU before reading a shared memory area (use only for atomic reads such as int and void*) */
-extern pthread_mutex_t fd_cpu_mtx_dummy; /* only for the macro bellow, so that we have reasonably fresh pir_state value when needed */
-#define fd_cpu_flush_cache() {				\
-	(void)pthread_mutex_lock(&fd_cpu_mtx_dummy);	\
-	(void)pthread_mutex_unlock(&fd_cpu_mtx_dummy);	\
-}
-
-
-/*************
- Cancelation cleanup handlers for common objects 
- *************/
-static __inline__ void fd_cleanup_mutex( void * mutex )
-{
-	CHECK_POSIX_DO( pthread_mutex_unlock((pthread_mutex_t *)mutex), /* */);
-}
-		
-static __inline__ void fd_cleanup_rwlock( void * rwlock )
-{
-	CHECK_POSIX_DO( pthread_rwlock_unlock((pthread_rwlock_t *)rwlock), /* */);
-}
-
-static __inline__ void fd_cleanup_buffer( void * buffer )
-{
-	free(buffer);
-}
-static __inline__ void fd_cleanup_socket(void * sockptr)
-{
-	if (sockptr && (*(int *)sockptr > 0)) {
-		CHECK_SYS_DO( close(*(int *)sockptr), /* ignore */ );
-		*(int *)sockptr = -1;
-	}
-}
-
-/*============================================================*/
-/*                          SIGNALS                           */
-/*============================================================*/
-
-/* Register a new callback to be called on reception of a given signal (it receives the signal as parameter) */
-/* EALREADY will be returned if there is already a callback registered on this signal */
-/* NOTE: the signal handler will be called from a new detached thread */
-int fd_sig_register(int signal, char * modname, void (*callback)(int signal));
-
-/* Remove the handler for a given signal */
-int fd_sig_unregister(int signal);
-
-/* Dump list of handlers */
-void fd_sig_dump(int level, int indent);
-
-/* Name of signals */
-const char * fd_sig_abbrev(int signal);
-
-
-/*============================================================*/
-/*                          LISTS                             */
-/*============================================================*/
-
-/* The following structure represents a chained list element  */
-struct fd_list {
-	struct fd_list 	*next; /* next element in the list */
-	struct fd_list 	*prev; /* previous element in the list */
-	struct fd_list 	*head; /* head of the list */
-	void		*o;    /* additional pointer, used for any purpose (ex: start of the parent object) */
-};
-
-/* Initialize a list element */
-#define FD_LIST_INITIALIZER( _list_name ) \
-	{ .next = & _list_name, .prev = & _list_name, .head = & _list_name, .o = NULL }
-#define FD_LIST_INITIALIZER_O( _list_name, _obj ) \
-	{ .next = & _list_name, .prev = & _list_name, .head = & _list_name, .o = _obj }
-void fd_list_init ( struct fd_list * list, void * obj );
-
-/* Return boolean, true if the list is empty */
-#define FD_IS_LIST_EMPTY( _list ) ((((struct fd_list *)(_list))->head == (_list)) && (((struct fd_list *)(_list))->next == (_list)))
-
-/* Insert an item in a list at known position */
-void fd_list_insert_after  ( struct fd_list * ref, struct fd_list * item );
-void fd_list_insert_before ( struct fd_list * ref, struct fd_list * item );
-
-/* Move all elements from a list at the end of another */
-void fd_list_move_end(struct fd_list * ref, struct fd_list * senti);
-
-/* Insert an item in an ordered list -- ordering function must be provided. If duplicate object found, EEXIST and it is returned in ref_duplicate */
-int fd_list_insert_ordered( struct fd_list * head, struct fd_list * item, int (*cmp_fct)(void *, void *), void ** ref_duplicate);
-
-/* Unlink an item from a list */
-void fd_list_unlink ( struct fd_list * item );
-
-
-
-/*============================================================*/
-/*                          HASH                              */
-/*============================================================*/
-
-/* Compute a hash value of a string (session id, diameter id, ...) */
-uint32_t fd_hash ( char * string, size_t len );
-
-
-
-/*============================================================*/
-/*                        DICTIONARY                          */
-/*============================================================*/
-
-/* Structure that contains the complete dictionary definitions */
-struct dictionary;
-
-/* Structure that contains a dictionary object */
-struct dict_object;
-
-/* Types of object in the dictionary. */
-enum dict_object_type {
-	DICT_VENDOR	= 1,	/* Vendor */
-	DICT_APPLICATION,	/* Diameter Application */
-	DICT_TYPE,		/* AVP data type */
-	DICT_ENUMVAL,		/* Named constant (value of an enumerated AVP type) */
-	DICT_AVP,		/* AVP */
-	DICT_COMMAND,		/* Diameter Command */
-	DICT_RULE		/* a Rule for AVP in command or grouped AVP */
-#define DICT_TYPE_MAX	DICT_RULE
-};
-	
-/* Initialize a dictionary */
-int fd_dict_init(struct dictionary ** dict);
-/* Destroy a dictionary */
-int fd_dict_fini(struct dictionary ** dict);
-
-/*
- * FUNCTION:	fd_dict_new
- *
- * PARAMETERS:
- *  dict	: Pointer to the dictionnary where the object is created
- *  type 	: What kind of object must be created
- *  data 	: pointer to the data for the object. 
- *          	 type parameter is used to determine the type of data (see bellow for detail).
- *  parent 	: a reference to a parent object, if needed.
- *  ref 	: upon successful creation, reference to new object is stored here if !null.
- *
- * DESCRIPTION: 
- *  Create a new object in the dictionary. 
- *  See following object sections in this header file for more information on data and parent parameters format.
- *
- * RETURN VALUE:
- *  0      	: The object is created in the dictionary.
- *  EINVAL 	: A parameter is invalid.
- *  EEXIST 	: This object is already defined in the dictionary (with conflicting data). 
- *                If "ref" is not NULL, it points to the existing element on return.
- *  (other standard errors may be returned, too, with their standard meaning. Example:
- *    ENOMEM 	: Memory allocation for the new object element failed.)
- */
-int fd_dict_new ( struct dictionary * dict, enum dict_object_type type, void * data, struct dict_object * parent, struct dict_object ** ref );
-
-/*
- * FUNCTION: 	fd_dict_search
- *
- * PARAMETERS:
- *  dict	: Pointer to the dictionnary where the object is searched
- *  type 	: type of object that is being searched
- *  criteria 	: how the object must be searched. See object-related sections bellow for more information.
- *  what 	: depending on criteria, the data that must be searched.
- *  result 	: On successful return, pointer to the object is stored here.
- *  retval	: this value is returned if the object is not found and result is not NULL.
- *
- * DESCRIPTION: 
- *   Perform a search in the dictionary. 
- *   See the object-specific sections bellow to find how to look for each objects.
- *   If the "result" parameter is NULL, the function is used to check if an object is in the dictionary.
- *   Otherwise, a reference to the object is stored in result if found.
- *   If result is not NULL and the object is not found, retval is returned (should be 0 or ENOENT usually)
- *
- * RETURN VALUE:
- *  0      	: The object has been found in the dictionary, or *result is NULL.
- *  EINVAL 	: A parameter is invalid.
- *  ENOENT	: No matching object has been found, and result was NULL.
- */
-int fd_dict_search ( struct dictionary * dict, enum dict_object_type type, int criteria, void * what, struct dict_object ** result, int retval );
-
-/* Special case: get the generic error command object */
-int fd_dict_get_error_cmd(struct dictionary * dict, struct dict_object ** obj);
-
-/*
- * FUNCTION:	fd_dict_getval
- *
- * PARAMETERS:
- *  object 	: Pointer to a dictionary object.
- *  data 	: pointer to a structure to hold the data for the object.
- *          	  The type is the same as "data" parameter in fd_dict_new function.
- *
- * DESCRIPTION: 
- *  Retrieve content of a dictionary object.
- *  See following object sections in this header file for more information on data and parent parameters format.
- *
- * RETURN VALUE:
- *  0      	: The content of the object has been retrieved.
- *  EINVAL 	: A parameter is invalid.
- */
-int fd_dict_getval ( struct dict_object * object, void * val);
-int fd_dict_gettype ( struct dict_object * object, enum dict_object_type * type);
-int fd_dict_getdict ( struct dict_object * object, struct dictionary ** dict);
-
-/* Debug functions */
-void fd_dict_dump_object(struct dict_object * obj);
-void fd_dict_dump(struct dictionary * dict);
-
-/*
- ***************************************************************************
- *
- * Vendor object 
- *
- * These types are used to manage vendors in the dictionary
- *
- ***************************************************************************
- */
-
-/* Type to hold a Vendor ID: "SMI Network Management Private Enterprise Codes" (RFC3232) */
-typedef uint32_t	vendor_id_t;
-
-/* Type to hold data associated to a vendor */
-struct dict_vendor_data {
-	vendor_id_t	 vendor_id;	/* ID of a vendor */
-	char 		*vendor_name;	/* The name of this vendor */
-};
-
-/* The criteria for searching a vendor object in the dictionary */
-enum {
-	VENDOR_BY_ID = 10,	/* "what" points to a vendor_id_t */
-	VENDOR_BY_NAME,		/* "what" points to a string */
-	VENDOR_OF_APPLICATION	/* "what" points to a struct dict_object containing an application (see bellow) */
-};
-
-/***
- *  API usage :
-
-Note: the value of "vendor_name" is copied when the object is created, and the string may be disposed afterwards.
-On the other side, when value is retrieved with dict_getval, the string is not copied and MUST NOT be freed. It will
-be freed automatically along with the object itself with call to dict_fini later.
- 
-- fd_dict_new:
- The "parent" parameter is not used for vendors. 
- Sample code to create a vendor:
- {
-	 int ret;
-	 struct dict_object * myvendor;
-	 struct dict_vendor_data myvendordata = { 23455, "my vendor name" };  -- just an example...
-	 ret = fd_dict_new ( dict, DICT_VENDOR, &myvendordata, NULL, &myvendor );
- }
-
-- fd_dict_search:
- Sample codes to look for a vendor object, by its id or name:
- {
-	 int ret;
-	 struct dict_object * vendor_found;
-	 vendor_id_t vendorid = 23455;
-	 ret = fd_dict_search ( dict, DICT_VENDOR, VENDOR_BY_ID, &vendorid, &vendor_found, ENOENT);
-	 - or -
-	 ret = fd_dict_search ( dict, DICT_VENDOR, VENDOR_BY_NAME, "my vendor name", &vendor_found, ENOENT);
- }
- 
- - fd_dict_getval:
- Sample code to retrieve the data from a vendor object:
- {
-	 int ret;
-	 struct dict_object * myvendor;
-	 struct dict_vendor_data myvendordata;
-	 ret = fd_dict_search ( dict, DICT_VENDOR, VENDOR_BY_NAME, "my vendor name", &myvendor, ENOENT);
-	 ret = fd_dict_getval ( myvendor, &myvendordata );
-	 printf("my vendor id: %d\n", myvendordata.vendor_id );
- }
-		 
-*/
-		
-/* Special function: */
-uint32_t * fd_dict_get_vendorid_list(struct dictionary * dict);
-	 
-/*
- ***************************************************************************
- *
- * Application object 
- *
- * These types are used to manage Diameter applications in the dictionary
- *
- ***************************************************************************
- */
-
-/* Type to hold a Diameter application ID: IANA assigned value for this application. */
-typedef uint32_t	application_id_t;
-
-/* Type to hold data associated to an application */
-struct dict_application_data {
-	application_id_t	 application_id;	/* ID of the application */
-	char 			*application_name;	/* The name of this application */
-};
-
-/* The criteria for searching an application object in the dictionary */
-enum {
-	APPLICATION_BY_ID = 20,		/* "what" points to a application_id_t */
-	APPLICATION_BY_NAME,		/* "what" points to a string */
-	APPLICATION_OF_TYPE,		/* "what" points to a struct dict_object containing a type object (see bellow) */
-	APPLICATION_OF_COMMAND		/* "what" points to a struct dict_object containing a command (see bellow) */
-};
-
-/***
- *  API usage :
-
-The "parent" parameter of dict_new may point to a vendor object to inform of what vendor defines the application.
-for standard-track applications, the "parent" parameter should be NULL.
-The vendor associated to an application is retrieved with VENDOR_OF_APPLICATION search criteria on vendors.
-
-- fd_dict_new:
- Sample code for application creation:
- {
-	 int ret;
-	 struct dict_object * vendor;
-	 struct dict_object * appl;
-	 struct dict_vendor_data vendor_data = {
-		 23455,
-		 "my vendor name"
-	 };
-	 struct dict_application_data app_data = {
-		 9789,
-		 "my vendor's application"
-	 };
-	
-	 ret = fd_dict_new ( dict, DICT_VENDOR, &vendor_data, NULL, &vendor );
-	 ret = fd_dict_new ( dict, DICT_APPLICATION, &app_data, vendor, &appl );
- }
-
-- fd_dict_search:
- Sample code to retrieve the vendor of an application
- {
-	 int ret;
-	 struct dict_object * vendor, * appli;
-	 
-	 ret = fd_dict_search ( dict, DICT_APPLICATION, APPLICATION_BY_NAME, "my vendor's application", &appli, ENOENT);
-	 ret = fd_dict_search ( dict, DICT_VENDOR, VENDOR_OF_APPLICATION, appli, &vendor, ENOENT);
- }
- 
- - fd_dict_getval:
- Sample code to retrieve the data from an application object:
- {
-	 int ret;
-	 struct dict_object * appli;
-	 struct dict_application_data appl_data;
-	 ret = fd_dict_search ( dict, DICT_APPLICATION, APPLICATION_BY_NAME, "my vendor's application", &appli, ENOENT);
-	 ret = fd_dict_getval ( appli, &appl_data );
-	 printf("my application id: %s\n", appl_data.application_id );
- }
-
-*/
-
-/*
- ***************************************************************************
- *
- * Type object 
- *
- * These types are used to manage AVP data types in the dictionary
- *
- ***************************************************************************
- */
-
-/* Type to store any AVP value */ 
-union avp_value {
-	struct {
-		uint8_t *data;	/* bytes buffer */
-		size_t   len;	/* length of the data buffer */
-	}           os;		/* Storage for an octet string, data is alloc'd and must be freed */
-	int32_t     i32;	/* integer 32 */
-	int64_t     i64;	/* integer 64 */
-	uint32_t    u32;	/* unsigned 32 */
-	uint64_t    u64;	/* unsigned 64 */
-	float       f32;	/* float 32 */
-	double 	    f64;	/* float 64 */
-};
-
-/* These are the basic AVP types defined in RFC3588bis */
-enum dict_avp_basetype {
-	AVP_TYPE_GROUPED,
-	AVP_TYPE_OCTETSTRING,
-	AVP_TYPE_INTEGER32,
-	AVP_TYPE_INTEGER64,
-	AVP_TYPE_UNSIGNED32,
-	AVP_TYPE_UNSIGNED64,
-	AVP_TYPE_FLOAT32,
-	AVP_TYPE_FLOAT64
-#define AVP_TYPE_MAX AVP_TYPE_FLOAT64
-};
-
-/* Callbacks that can be associated with a derived type to easily interpret the AVP value. */
-/*
- * CALLBACK:	dict_avpdata_interpret
- *
- * PARAMETERS:
- *   val         : Pointer to the AVP value that must be interpreted.
- *   interpreted : The result of interpretation is stored here. The format and meaning depends on each type.
- *
- * DESCRIPTION: 
- *   This callback can be provided with a derived type in order to facilitate the interpretation of formated data.
- *  For example, when an AVP of type "Address" is received, it can be used to convert the octetstring into a struct sockaddr.
- *  This callback is not called directly, but through the message's API msg_avp_value_interpret function.
- *
- * RETURN VALUE:
- *  0      	: Operation complete.
- *  !0 		: An error occurred, the error code is returned.
- */
-typedef int (*dict_avpdata_interpret) (union avp_value * value, void * interpreted);
-/*
- * CALLBACK:	dict_avpdata_encode
- *
- * PARAMETERS:
- *   data	: The formated data that must be stored in the AVP value.
- *   val	: Pointer to the AVP value storage area where the data must be stored.
- *
- * DESCRIPTION: 
- *   This callback can be provided with a derived type in order to facilitate the encoding of formated data.
- *  For example, it can be used to convert a struct sockaddr in an AVP value of type Address.
- *  This callback is not called directly, but through the message's API msg_avp_value_encode function.
- *  If the callback is defined for an OctetString based type, the created string must be malloc'd. free will be called 
- *  automatically later.
- *
- * RETURN VALUE:
- *  0      	: Operation complete.
- *  !0 		: An error occurred, the error code is returned.
- */
-typedef int (*dict_avpdata_encode) (void * data, union avp_value * val);
-
-
-/* Type to hold data associated to a derived AVP data type */
-struct dict_type_data {
-	enum dict_avp_basetype	 type_base;	/* How the data of such AVP must be interpreted */
-	char 			*type_name;	/* The name of this type */
-	dict_avpdata_interpret	 type_interpret;/* cb to convert the AVP value in more comprehensive format (or NULL) */
-	dict_avpdata_encode	 type_encode;	/* cb to convert formatted data into an AVP value (or NULL) */
-	void			(*type_dump)(union avp_value * val);	/* cb called by fd_msg_dump_one for this type of data (if != NULL), to dump the AVP value in debug */
-};
-
-/* The criteria for searching a type object in the dictionary */
-enum {
-	TYPE_BY_NAME = 30,		/* "what" points to a string */
-	TYPE_OF_ENUMVAL,		/* "what" points to a struct dict_object containing an enumerated constant (DICT_ENUMVAL, see bellow). */
-	TYPE_OF_AVP			/* "what" points to a struct dict_object containing an AVP object. */
-};
-
-
-/***
- *  API usage :
-
-- fd_dict_new:
- The "parent" parameter may point to an application object, when a type is defined by a Diameter application. 
- 
- Sample code:
- {
-	 int ret;
-	 struct dict_object * mytype;
-	 struct dict_type_data mytypedata = 
-		{ 
-		 AVP_TYPE_OCTETSTRING,
-		 "Address",
-		 NULL,
-		 NULL
-		};
-	 ret = fd_dict_new ( dict, DICT_TYPE, &mytypedata, NULL, &mytype );
- }
-
-- fd_dict_search:
- Sample code:
- {
-	 int ret;
-	 struct dict_object * address_type;
-	 ret = fd_dict_search ( dict, DICT_TYPE, TYPE_BY_NAME, "Address", &address_type, ENOENT);
- }
- 
-*/
-	 
-/*
- ***************************************************************************
- *
- * Enumerated values object 
- *
- * These types are used to manage named constants of some AVP,
- * for enumerated types. freeDiameter allows constants for types others than Unsigned32
- *
- ***************************************************************************
- */
-
-/* Type to hold data of named constants for AVP */
-struct dict_enumval_data {
-	char 		*enum_name;	/* The name of this constant */
-	union avp_value  enum_value;	/* Value of the constant. Union term depends on parent type's base type. */
-};
-
-/* The criteria for searching a constant in the dictionary */
-enum {
-	ENUMVAL_BY_STRUCT = 40,	/* "what" points to a struct dict_enumval_request as defined bellow */
-};
-
-struct dict_enumval_request {
-	/* Identifier of the parent type, one of the following must not be NULL */
-	struct dict_object	*type_obj;
-	char			*type_name;
-	
-	/* Search criteria for the constant */
-	struct dict_enumval_data search; /* search.enum_value is used only if search.enum_name == NULL */
-};
-
-/***
- *  API usage :
-
-- fd_dict_new:
- The "parent" parameter must point to a derived type object. 
- Sample code to create a type "Boolean" with two constants "True" and "False":
- {
-	 int ret;
-	 struct dict_object * type_boolean;
-	 struct dict_type_data type_boolean_data = 
-		{ 
-		 AVP_TYPE_INTEGER32,
-		 "Boolean",
-		 NULL,
-		 NULL
-		};
-	 struct dict_enumval_data boolean_false =
-	 	{
-		 .enum_name="False",
-		 .enum_value.i32 = 0
-	 	};
-	 struct dict_enumval_data boolean_true =
-	 	{
-		 .enum_name="True",
-		 .enum_value.i32 = -1
-	 	};
-	 ret = fd_dict_new ( dict, DICT_TYPE, &type_boolean_data, NULL, &type_boolean );
-	 ret = fd_dict_new ( dict, DICT_ENUMVAL, &boolean_false, type_boolean, NULL );
-	 ret = fd_dict_new ( dict, DICT_ENUMVAL, &boolean_true , type_boolean, NULL );
-	 
- }
-
-- fd_dict_search:
- Sample code to look for a constant name, by its value:
- {
-	 int ret;
-	 struct dict_object * value_found;
-	 struct dict_enumval_request boolean_by_value =
-	 	{
-		 .type_name = "Boolean",
-		 .search.enum_name=NULL,
-		 .search.enum_value.i32 = -1
-	 	};
-	 
-	 ret = fd_dict_search ( dict, DICT_ENUMVAL, ENUMVAL_BY_STRUCT, &boolean_by_value, &value_found, ENOENT);
- }
- 
- - fd_dict_getval:
- Sample code to retrieve the data from a constant object:
- {
-	 int ret;
-	 struct dict_object * value_found;
-	 struct dict_enumval_data boolean_data = NULL;
-	 struct dict_enumval_request boolean_by_value =
-	 	{
-		 .type_name = "Boolean",
-		 .search.enum_name=NULL,
-		 .search.enum_value.i32 = 0
-	 	};
-	 
-	 ret = fd_dict_search ( dict, DICT_ENUMVAL, ENUMVAL_BY_STRUCT, &boolean_by_value, &value_found, ENOENT);
-	 ret = fd_dict_getval ( value_found, &boolean_data );
-	 printf(" Boolean with value 0: %s", boolean_data.enum_name );
- }
-*/
-	 
-/*
- ***************************************************************************
- *
- * AVP object 
- *
- * These objects are used to manage AVP definitions in the dictionary
- *
- ***************************************************************************
- */
-
-/* Type to hold an AVP code. For vendor 0, these codes are assigned by IANA. Otherwise, it is managed by the vendor */
-typedef uint32_t	avp_code_t;
-
-/* Values of AVP flags */
-#define	AVP_FLAG_VENDOR	  	0x80
-#define	AVP_FLAG_MANDATORY	0x40
-#define	AVP_FLAG_RESERVED3	0x20
-#define	AVP_FLAG_RESERVED4	0x10
-#define	AVP_FLAG_RESERVED5	0x08
-#define	AVP_FLAG_RESERVED6	0x04
-#define	AVP_FLAG_RESERVED7	0x02
-#define	AVP_FLAG_RESERVED8	0x01
-
-/* For dumping flags and values */
-#define DUMP_AVPFL_str	"%c%c"
-#define DUMP_AVPFL_val(_val) (_val & AVP_FLAG_VENDOR)?'V':'-' , (_val & AVP_FLAG_MANDATORY)?'M':'-'
-
-/* Type to hold data associated to an avp */
-struct dict_avp_data {
-	avp_code_t	 	 avp_code;	/* Code of the avp */
-	vendor_id_t	 	 avp_vendor;	/* Vendor of the AVP, or 0 */
-	char			*avp_name;	/* Name of this AVP */
-	uint8_t		 	 avp_flag_mask;	/* Mask of fixed AVP flags */
-	uint8_t		 	 avp_flag_val;	/* Values of the fixed flags */
-	enum dict_avp_basetype 	 avp_basetype;	/* Basic type of data found in the AVP */
-};
-
-/* The criteria for searching an avp object in the dictionary */
-enum {
-	AVP_BY_CODE = 50,	/* "what" points to an avp_code_t, vendor is always 0 */
-	AVP_BY_NAME,		/* "what" points to a string, vendor is always 0 */
-	AVP_BY_CODE_AND_VENDOR,	/* "what" points to a struct dict_avp_request (see bellow), where avp_vendor and avp_code are set */
-	AVP_BY_NAME_AND_VENDOR,	/* "what" points to a struct dict_avp_request (see bellow), where avp_vendor and avp_name are set */
-	AVP_BY_NAME_ALL_VENDORS /* "what" points to a string. Might be quite slow... */
-};
-
-/* Struct used for some researchs */
-struct dict_avp_request {
-	vendor_id_t	 avp_vendor;
-	avp_code_t	 avp_code;
-	char		*avp_name;
-};
-
-
-/***
- *  API usage :
-
-If "parent" parameter is not NULL during AVP creation, it must point to a DICT_TYPE object. 
-The extended type is then attached to the AVP. In case where it is an enumerated type, the value of 
-AVP is automatically interpreted in debug messages, and in message checks.
-The derived type of an AVP can be retrieved with: dict_search ( DICT_TYPE, TYPE_OF_AVP, avp, ... )
-
-To create the rules (ABNF) for children of Grouped AVP, see the DICT_RULE related part.
-
-- fd_dict_new:
- Sample code for AVP creation:
- {
-	 int ret;
-	 struct dict_object * user_name_avp;
-	 struct dict_object * boolean_type;
-	 struct dict_object * sample_boolean_avp;
-	 struct dict_avp_data user_name_data = {
-		 1,					// code
-		 0,					// vendor
-		 "User-Name",				// name
-		 AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY,	// fixed mask: V and M values must always be defined as follow. other flags can be set or cleared
-		 AVP_FLAG_MANDATORY,			// the V flag must be cleared, the M flag must be set.
-		 AVP_TYPE_OCTETSTRING			// User-Name AVP contains OctetString data (further precision such as UTF8String can be given with a parent derived type)
-	 };
-	 struct dict_avp_data sample_boolean_data = {
-		 31337,
-		 23455,
-		 "Sample-Boolean",
-		 AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY,
-		 AVP_FLAG_VENDOR,
-		 AVP_TYPE_INTEGER32			// This MUST be the same as parent type's
-	 };
-	
- 	 -- Create an AVP with a base type --
-	 ret = fd_dict_new ( dict, DICT_AVP, &user_name_data, NULL, &user_name_avp );
-	 
-	 -- Create an AVP with a derived type --
-	 ret = fd_dict_search ( dict, DICT_TYPE, TYPE_BY_NAME, "Boolean", &boolean_type, ENOENT);
-	 ret = fd_dict_new ( dict, DICT_AVP, &sample_boolean_data , boolean_type, &sample_boolean_avp );
-	 
- }
-
-- fd_dict_search:
- Sample code to look for an AVP
- {
-	 int ret;
-	 struct dict_object * avp_username;
-	 struct dict_object * avp_sampleboolean;
-	 struct dict_avp_request avpvendorboolean =
-	 	{
-		 .avp_vendor = 23455,
-		 .avp_name   = "Sample-Boolean"
-	 	};
-	 
-	 ret = fd_dict_search ( dict, DICT_AVP, AVP_BY_NAME, "User-Name", &avp_username, ENOENT);
-	 
-	 ret = fd_dict_search ( dict, DICT_AVP, AVP_BY_NAME_AND_VENDOR, &avpvendorboolean, &avp_sampleboolean, ENOENT);
-	 
- }
- 
- - fd_dict_getval:
- Sample code to retrieve the data from an AVP object:
- {
-	 int ret;
-	 struct dict_object * avp_username;
-	 struct dict_avp_data user_name_data;
-	 ret = fd_dict_search ( dict, DICT_AVP, AVP_BY_NAME, "User-Name", &avp_username, ENOENT);
-	 ret = fd_dict_getval ( avp_username, &user_name_data );
-	 printf("User-Name code: %d\n", user_name_data.avp_code );
- }
-
-*/
-
-/*
- ***************************************************************************
- *
- * Command object 
- *
- * These types are used to manage commands objects in the dictionary
- *
- ***************************************************************************
- */
-
-/* Type to hold a Diameter command code: IANA assigned values. 0x0-0x7fffff=standard, 0x800000-0xfffffd=vendors, 0xfffffe-0xffffff=experimental */
-typedef uint32_t	command_code_t;
-
-/* Values of command flags */
-#define CMD_FLAG_REQUEST	0x80
-#define CMD_FLAG_PROXIABLE	0x40
-#define CMD_FLAG_ERROR		0x20
-#define CMD_FLAG_RETRANSMIT	0x10
-#define CMD_FLAG_RESERVED5	0x08
-#define CMD_FLAG_RESERVED6	0x04
-#define CMD_FLAG_RESERVED7	0x02
-#define CMD_FLAG_RESERVED8	0x01
-
-/* For dumping flags and values */
-#define DUMP_CMDFL_str	"%c%c%c%c"
-#define DUMP_CMDFL_val(_val) (_val & CMD_FLAG_REQUEST)?'R':'-' , (_val & CMD_FLAG_PROXIABLE)?'P':'-' , (_val & CMD_FLAG_ERROR)?'E':'-' , (_val & CMD_FLAG_RETRANSMIT)?'T':'-'
-
-/* Type to hold data associated to a command */
-struct dict_cmd_data {
-	command_code_t	 cmd_code;	/* code of the command */
-	char		*cmd_name;	/* Name of the command */
-	uint8_t		 cmd_flag_mask;	/* Mask of fixed-value flags */
-	uint8_t		 cmd_flag_val;	/* values of the fixed flags */
-};
-
-/* The criteria for searching an avp object in the dictionary */
-enum {
-	CMD_BY_NAME = 60,	/* "what" points to a string */
-	CMD_BY_CODE_R,		/* "what" points to a command_code_t. The "Request" command is returned. */
-	CMD_BY_CODE_A,		/* "what" points to a command_code_t. The "Answer" command is returned. */
-	CMD_ANSWER		/* "what" points to a struct dict_object of a request command. The corresponding "Answer" command is returned. */
-};
-
-
-/***
- *  API usage :
-
-The "parent" parameter of dict_new may point to an application object to inform of what application defines the command.
-The application associated to a command is retrieved with APPLICATION_OF_COMMAND search criteria on applications.
-
-To create the rules for children of commands, see the DICT_RULE related part.
-
-Note that the "Request" and "Answer" commands are two independant objects. This allows to have different rules for each.
-
-- fd_dict_new:
- Sample code for command creation:
- {
-	 int ret;
-	 struct dict_object * cer;
-	 struct dict_object * cea;
-	 struct dict_cmd_data ce_data = {
-		 257,					// code
-		 "Capabilities-Exchange-Request",	// name
-		 CMD_FLAG_REQUEST,			// mask
-		 CMD_FLAG_REQUEST			// value. Only the "R" flag is constrained here, set.
-	 };
-	
-	 ret = fd_dict_new (dict,  DICT_COMMAND, &ce_data, NULL, &cer );
-	 
-	 ce_data.cmd_name = "Capabilities-Exchange-Answer";
-	 ce_data.cmd_flag_val = 0;			// Same constraint on "R" flag, but this time it must be cleared.
-
-	 ret = fd_dict_new ( dict, DICT_COMMAND, &ce_data, NULL, &cea );
- }
-
-- fd_dict_search:
- Sample code to look for a command
- {
-	 int ret;
-	 struct dict_object * cer, * cea;
-	 command_code_t	code = 257;
-	 ret = fd_dict_search ( dict, DICT_COMMAND, CMD_BY_NAME, "Capabilities-Exchange-Request", &cer, ENOENT);
-	 ret = fd_dict_search ( dict, DICT_COMMAND, CMD_BY_CODE_R, &code, &cer, ENOENT);
- }
- 
- - fd_dict_getval:
- Sample code to retrieve the data from a command object:
- {
-	 int ret;
-	 struct dict_object * cer;
-	 struct dict_object * cea;
-	 struct dict_cmd_data cea_data;
-	 ret = fd_dict_search ( dict, DICT_COMMAND, CMD_BY_NAME, "Capabilities-Exchange-Request", &cer, ENOENT);
-	 ret = fd_dict_search ( dict, DICT_COMMAND, CMD_ANSWER, cer, &cea, ENOENT);
-	 ret = fd_dict_getval ( cea, &cea_data );
-	 printf("Answer to CER: %s\n", cea_data.cmd_name );
- }
-
-*/
-
-/*
- ***************************************************************************
- *
- * Rule object
- *
- * These objects are used to manage rules in the dictionary (ABNF implementation)
- * This is used for checking messages validity (more powerful than a DTD)
- *
- ***************************************************************************
- */
-
-/* This defines the kind of rule that is defined */
-enum rule_position {
-	RULE_FIXED_HEAD = 1,	/* The AVP must be at the head of the group. The rule_order field is used to specify the position. */
-	RULE_REQUIRED,		/* The AVP must be present in the parent, but its position is not defined. */
-	RULE_OPTIONAL,		/* The AVP may be present in the message. Used to specify a max number of occurences for example */
-	RULE_FIXED_TAIL		/* The AVP must be at the end of the group. The rule_order field is used to specify the position. */
-};
-
-/* Content of a RULE object data */
-struct dict_rule_data {
-	struct dict_object	*rule_avp;	/* Pointer to the AVP object that is concerned by this rule */
-	enum rule_position	 rule_position;	/* The position in which the rule_avp must appear in the parent */
-	unsigned		 rule_order;	/* for RULE_FIXED_* rules, the place. 1,2,3.. for HEAD rules; ...,3,2,1 for TAIL rules. */
-	int	 		 rule_min;	/* Minimum number of occurences. -1 means "default": 0 for optional rules, 1 for other rules */
-	int			 rule_max;	/* Maximum number of occurences. -1 means no maximum. 0 means the AVP is forbidden. */
-};
-
-/* The criteria for searching a rule in the dictionary */
-enum {
-	RULE_BY_AVP_AND_PARENT = 70	/* "what" points to a struct dict_rule_request -- see bellow. This is used to query "what is the rule for this AVP in this group?" */
-};
-
-/* Structure for querying the dictionary about a rule */
-struct dict_rule_request {
-	struct dict_object	*rule_parent;	/* The grouped avp or command to which the rule apply */
-	struct dict_object	*rule_avp;	/* The AVP concerned by this rule */
-};
-
-
-/***
- *  API usage :
-
-The "parent" parameter can not be NULL. It points to the object (grouped avp or command) to which this rule apply (i.e. for which the ABNF is defined).
-
-- fd_dict_new:
- Sample code for rule creation. Let's create the Proxy-Info grouped AVP for example.
- {
-	int ret;
-	struct dict_object * proxy_info_avp;
-	struct dict_object * proxy_host_avp;
-	struct dict_object * proxy_state_avp;
-	struct dict_object * diameteridentity_type;
-	struct dict_rule_data rule_data;
-	struct dict_type_data di_type_data = { AVP_TYPE_OCTETSTRING, "DiameterIdentity", NULL, NULL };
-	struct dict_avp_data proxy_info_data = { 284, 0, "Proxy-Info", AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, AVP_FLAG_MANDATORY, AVP_TYPE_GROUPED };
-	struct dict_avp_data proxy_host_data = { 280, 0, "Proxy-Host", AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, AVP_FLAG_MANDATORY, AVP_TYPE_OCTETSTRING };
-	struct dict_avp_data proxy_state_data = { 33, 0, "Proxy-State",AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, AVP_FLAG_MANDATORY, AVP_TYPE_OCTETSTRING };
-	
-	-- Create the parent AVP
-	ret = fd_dict_new ( dict, DICT_AVP, &proxy_info_data, NULL, &proxy_info_avp );
-	
-	-- Create the first child AVP.
-	ret = fd_dict_new ( dict, DICT_TYPE, &di_type_data, NULL, &diameteridentity_type );
-	ret = fd_dict_new ( dict, DICT_AVP, &proxy_host_data, diameteridentity_type, &proxy_host_avp );
-	
-	-- Create the other child AVP
-	ret = fd_dict_new ( dict, DICT_AVP, &proxy_state_data, NULL, &proxy_state_avp );
-	
-	-- Now we can create the rules. Both children AVP are mandatory.
-	rule_data.rule_position = RULE_REQUIRED;
-	rule_data.rule_min = -1;
-	rule_data.rule_max = -1;
-	
-	rule_data.rule_avp = proxy_host_avp;
-	ret = fd_dict_new ( dict, DICT_RULE, &rule_data, proxy_info_avp, NULL );
-	
-	rule_data.rule_avp = proxy_state_avp;
-	ret = fd_dict_new ( dict, DICT_RULE, &rule_data, proxy_info_avp, NULL );
-}
-
-- fd_dict_search and fd_dict_getval are similar to previous examples.
-
-*/
-		
-/* Define some hard-coded values */
-/* Application */
-#define AI_RELAY			0xffffffff
-
-/* Commands Codes */
-#define CC_CAPABILITIES_EXCHANGE	257
-#define CC_RE_AUTH			258
-#define CC_ACCOUNTING			271
-#define CC_ABORT_SESSION		274
-#define CC_SESSION_TERMINATION		275
-#define CC_DEVICE_WATCHDOG		280
-#define CC_DISCONNECT_PEER		282
-
-/* AVPs (Vendor 0) */
-#define AC_USER_NAME			1
-#define AC_PROXY_STATE			33
-#define AC_HOST_IP_ADDRESS		257
-#define AC_AUTH_APPLICATION_ID		258
-#define AC_ACCT_APPLICATION_ID		259
-#define AC_VENDOR_SPECIFIC_APPLICATION_ID 260
-#define AC_REDIRECT_HOST_USAGE		261
-#define AC_REDIRECT_MAX_CACHE_TIME	262
-#define AC_SESSION_ID 			263
-#define AC_ORIGIN_HOST			264
-#define AC_SUPPORTED_VENDOR_ID		265
-#define AC_VENDOR_ID			266
-#define AC_FIRMWARE_REVISION		267
-#define AC_RESULT_CODE			268
-#define AC_PRODUCT_NAME			269
-#define AC_DISCONNECT_CAUSE		273
-#define ACV_DC_REBOOTING			0
-#define ACV_DC_BUSY				1
-#define ACV_DC_NOT_FRIEND			2
-#define AC_ORIGIN_STATE_ID		278
-#define AC_FAILED_AVP			279
-#define AC_PROXY_HOST			280
-#define AC_ERROR_MESSAGE		281
-#define AC_ROUTE_RECORD			282
-#define AC_DESTINATION_REALM		283
-#define AC_PROXY_INFO			284
-#define AC_REDIRECT_HOST		292
-#define AC_DESTINATION_HOST		293
-#define AC_ERROR_REPORTING_HOST		294
-#define AC_ORIGIN_REALM			296
-#define AC_INBAND_SECURITY_ID		299
-#define ACV_ISI_NO_INBAND_SECURITY		0
-#define ACV_ISI_TLS				1
-
-/* Error codes */
-#define ER_DIAMETER_SUCCESS			2001
-#define ER_DIAMETER_REALM_NOT_SERVED		3003
-#define ER_DIAMETER_TOO_BUSY			3004
-#define ER_DIAMETER_REDIRECT_INDICATION		3006
-#define ER_ELECTION_LOST			4003
-
-
-/*============================================================*/
-/*                         SESSIONS                           */
-/*============================================================*/
-
-/* Modules that want to associate a state with a Session-Id must first register a handler of this type */
-struct session_handler;
-
-/* This opaque structure represents a session associated with a Session-Id */
-struct session;
-
-/* The state information that a module associate with a session -- each module defines its own data format */
-typedef void session_state;
-
-/* The following function must be called to activate the session expiry mechanism */
-int fd_sess_start(void);
-
-/*
- * FUNCTION:	fd_sess_handler_create
- *
- * PARAMETERS:
- *  handler	: location where the new handler must be stored.
- *  cleanup	: a callback function that must be called when the session with associated data is destroyed.
- *  opaque      : A pointer that is passed to the cleanup callback -- the content is never examined by the framework.
- *
- * DESCRIPTION: 
- *  Create a new session handler. This is needed by a module to associate a state with a session object.
- * The cleanup handler is called when the session timeout expires, or fd_sess_destroy is called. It must free
- * the state associated with the session, and eventually trig other actions (send a STR, ...).
- *
- * RETURN VALUE:
- *  0      	: The new handler has been created.
- *  EINVAL 	: A parameter is invalid.
- *  ENOMEM	: Not enough memory to complete the operation
- */
-int fd_sess_handler_create_internal ( struct session_handler ** handler, void (*cleanup)(session_state * state, char * sid, void * opaque), void * opaque );
-/* Macro to avoid casting everywhere */
-#define fd_sess_handler_create( _handler, _cleanup, _opaque ) \
-	fd_sess_handler_create_internal( (_handler), (void (*)(session_state *, char *, void *))(_cleanup), (void *)(_opaque) )
-
-	
-/*
- * FUNCTION:	fd_sess_handler_destroy
- *
- * PARAMETERS:
- *  handler	: location of an handler created by fd_sess_handler_create.
- *  opaque      : the opaque pointer registered with the callback is restored here (if ! NULL).
- *
- * DESCRIPTION: 
- *  This destroys a session handler (typically called when an application is shutting down).
- * If sessions states are registered with this handler, the cleanup callback is called on them.
- *
- * RETURN VALUE:
- *  0      	: The handler was destroyed.
- *  EINVAL 	: A parameter is invalid.
- *  ENOMEM	: Not enough memory to complete the operation
- */
-int fd_sess_handler_destroy ( struct session_handler ** handler, void **opaque );
-
-
-
-/*
- * FUNCTION:	fd_sess_new
- *
- * PARAMETERS:
- *  session	  : The location where the session object will be created upon success.
- *  diamId	  : \0-terminated string containing a Diameter Identity.
- *  opt           : Additional string. Usage is described bellow.
- *  optlen	  : if opt is \0-terminated, this can be 0. Otherwise, the length of opt.
- *
- * DESCRIPTION: 
- *   Create a new session object. The Session-Id string associated with this session is generated as follow:
- *  If diamId parameter is provided, the string is created according to the RFC: <diamId>;<high32>;<low32>[;opt] where
- *    diamId is a Diameter Identity.
- *    high32 and low32 are the parts of a monotonic 64 bits counter initialized to (time, 0) at startup.
- *    opt is an optional string that can be concatenated to the identifier.
- *  If diamId is NULL, the string is exactly the content of opt.
- *
- * RETURN VALUE:
- *  0      	: The session is created.
- *  EINVAL 	: A parameter is invalid.
- *  EALREADY	: A session with the same name already exists (returned in *session)
- *  ENOMEM	: Not enough memory to complete the operation
- */
-int fd_sess_new ( struct session ** session, char * diamId, char * opt, size_t optlen );
-
-/*
- * FUNCTION:	fd_sess_fromsid
- *
- * PARAMETERS:
- *  sid	  	: pointer to a string containing a Session-Id (UTF-8).
- *  len		: length of the sid string (which does not need to be '\0'-terminated)
- *  session	: On success, pointer to the session object created / retrieved.
- *  isnew	: if not NULL, set to 1 on return if the session object has been created, 0 if it was simply retrieved.
- *
- * DESCRIPTION: 
- *   Retrieve a session object from a Session-Id string. In case no session object was previously existing with this 
- *  id, a new object is silently created (equivalent to fd_sess_new with flag SESSION_NEW_FULL).
- *
- * RETURN VALUE:
- *  0      	: The session parameter has been updated.
- *  EINVAL 	: A parameter is invalid.
- *  ENOMEM	: Not enough memory to complete the operation
- */
-int fd_sess_fromsid ( char * sid, size_t len, struct session ** session, int * isnew);
-
-/*
- * FUNCTION:	fd_sess_getsid
- *
- * PARAMETERS:
- *  session	: Pointer to a session object.
- *  sid	  	: On success, the location of a (\0-terminated) string is stored here.
- *
- * DESCRIPTION: 
- *   Retrieve the session identifier (Session-Id) corresponding to a session object.
- *  The returned sid is an UTF-8 string terminated by \0, suitable for calls to strlen and strcpy.
- *  It may be used for example to set the value of an AVP.
- *  Note that the sid string is not copied, just its reference... do not free it!
- *
- * RETURN VALUE:
- *  0      	: The sid parameter has been updated.
- *  EINVAL 	: A parameter is invalid.
- */
-int fd_sess_getsid ( struct session * session, char ** sid );
-
-/*
- * FUNCTION:	fd_sess_settimeout
- *
- * PARAMETERS:
- *  session	: The session for which to set the timeout.
- *  timeout	: The date when the session times out.
- *
- * DESCRIPTION: 
- *   Set the lifetime for a given session object. This function may be 
- * called several times on the same object to update the timeout value.
- *   When the timeout date is reached, the cleanup handler of each 
- * module that registered data with this session is called, then the 
- * session is cleared.
- *
- *   There is a possible race condition between cleanup of the session
- * and use of its data; applications should ensure that they are not 
- * using data from a session that is about to expire / expired.
- *
- * RETURN VALUE:
- *  0      	: The session timeout has been updated.
- *  EINVAL 	: A parameter is invalid.
- */
-int fd_sess_settimeout( struct session * session, const struct timespec * timeout );
-
-/*
- * FUNCTION:	fd_sess_destroy
- *
- * PARAMETERS:
- *  session	: Pointer to a session object.
- *
- * DESCRIPTION: 
- *   Destroys a session an all associated data, if any.
- * Equivalent to a session timeout expired, but the effect is immediate.
- *
- * RETURN VALUE:
- *  0      	: The session no longer exists.
- *  EINVAL 	: A parameter is invalid.
- */
-int fd_sess_destroy ( struct session ** session );
-
-/*
- * FUNCTION:	fd_sess_reclaim
- *
- * PARAMETERS:
- *  session	: Pointer to a session object.
- *
- * DESCRIPTION: 
- *   Destroys the resources of a session, only if no session_state is associated with it.
- *
- * RETURN VALUE:
- *  0      	: The session no longer exists.
- *  EINVAL 	: A parameter is invalid.
- */
-int fd_sess_reclaim ( struct session ** session );
-
-
-
-
-/*
- * FUNCTION:	fd_sess_state_store
- *
- * PARAMETERS:
- *  handler	: The handler with which the state is registered.
- *  session	: The session object with which the state is registered.
- *  state	: An application state (opaque data) to store with the session.
- *
- * DESCRIPTION: 
- *  Stores an application state with a session. This state can later be retrieved
- * with fd_sess_state_retrieve, or implicitly in the cleanup handler when the session
- * is destroyed.
- *
- * RETURN VALUE:
- *  0      	: The state has been stored.
- *  EINVAL 	: A parameter is invalid.
- *  EALREADY	: Data was already associated with this session and client.
- *  ENOMEM	: Not enough memory to complete the operation
- */
-int fd_sess_state_store_internal ( struct session_handler * handler, struct session * session, session_state ** state );
-#define fd_sess_state_store( _handler, _session, _state ) \
-	fd_sess_state_store_internal( (_handler), (_session), (void *)(_state) )
-
-/*
- * FUNCTION:	fd_sess_state_retrieve
- *
- * PARAMETERS:
- *  handler	: The handler with which the state was registered.
- *  session	: The session object with which the state was registered.
- *  state	: Location where the state must be saved if it is found.
- *
- * DESCRIPTION: 
- *  Retrieves a state saved by fd_sess_state_store.
- * After this function has been called, the state is no longer associated with 
- * the session. A new call to fd_sess_state_store must be performed in order to
- * store again the data with the session.
- *
- * RETURN VALUE:
- *  0      	: *state is updated (NULL or points to the state if it was found).
- *  EINVAL 	: A parameter is invalid.
- */
-int fd_sess_state_retrieve_internal ( struct session_handler * handler, struct session * session, session_state ** state ); 
-#define fd_sess_state_retrieve( _handler, _session, _state ) \
-	fd_sess_state_retrieve_internal( (_handler), (_session), (void *)(_state) )
-
-
-/* For debug */
-void fd_sess_dump(int level, struct session * session);
-void fd_sess_dump_hdl(int level, struct session_handler * handler);
-
-/*============================================================*/
-/*                         ROUTING                            */
-/*============================================================*/
-
-/* The following functions are helpers for the routing module. 
-  The routing data is stored in the message itself. */
-
-/* Structure that contains the routing data for a message */
-struct rt_data;
-
-/* Following functions are helpers to create the routing data of a message */
-int  fd_rtd_init(struct rt_data ** rtd);
-void fd_rtd_free(struct rt_data ** rtd);
-
-/* Add a peer to the candidates list */
-int  fd_rtd_candidate_add(struct rt_data * rtd, char * peerid, char * realm);
-
-/* Remove a peer from the candidates (if it is found) */
-void fd_rtd_candidate_del(struct rt_data * rtd, char * peerid, size_t sz /* if !0, peerid does not need to be \0 terminated */);
-
-/* Extract the list of valid candidates, and initialize their scores to 0 */
-void fd_rtd_candidate_extract(struct rt_data * rtd, struct fd_list ** candidates, int ini_score);
-
-/* If a peer returned a protocol error for this message, save it so that we don't try to send it there again */
-int  fd_rtd_error_add(struct rt_data * rtd, char * sentto, uint8_t * origin, size_t originsz, uint32_t rcode);
-
-/* The extracted list items have the following structure: */
-struct rtd_candidate {
-	struct fd_list	chain;	/* link in the list returned by the previous fct */
-	char *		diamid;	/* the diameter Id of the peer */
-	char *		realm;	/* the diameter realm of the peer (if known) */
-	int		score;	/* the current routing score for this peer, see fd_rt_out_register definition for details */
-};
-
-/* Reorder the list of peers */
-int  fd_rtd_candidate_reorder(struct fd_list * candidates);
-
-/* Note : it is fine for a callback to add a new entry in the candidates list after the list has been extracted. The diamid must then be malloc'd. */
-/* Beware that this could lead to routing loops */
-
-/*============================================================*/
-/*                         MESSAGES                           */
-/*============================================================*/
-
-/* The following types are opaque */
-struct	msg;	/* A message: command with children AVPs (possibly grand children) */
-struct	avp;	/* AVP object */
-
-/* Some details about chaining:
- *
- *  A message is made of a header ( msg ) and 0 or more AVPs ( avp ).
- * The structure is a kind of tree, where some AVPs (grouped AVPs) can contain other AVPs.
- * Exemple:
- * msg
- *  |-avp
- *  |-gavp
- *  |   |-avp
- *  |   |-avp
- *  |   \-avp
- *  |-avp
- *  \-avp
- *
- */
-
-/* The following type is used to point to either a msg or an AVP */
-typedef void msg_or_avp;
-
-/* The Diameter protocol version */
-#define DIAMETER_VERSION	1
-
-/* In the two following types, some fields are marked (READONLY). 
- * This means that the content of these fields will be overwritten by the daemon so modifying it is useless.
- */
-
-/* The following structure represents the header of a message. All data is in host byte order. */
-struct msg_hdr {
-	uint8_t		 msg_version;		/* (READONLY) Version of Diameter: must be DIAMETER_VERSION. */
-	uint32_t	 msg_length;		/* (READONLY)(3 bytes) indicates the length of the message */
-	uint8_t		 msg_flags;		/* Message flags: CMD_FLAG_* */
-	command_code_t	 msg_code;		/* (3 bytes) the command-code. See dictionary-api.h for more detail */
-	application_id_t msg_appl;		/* The application issuing this message */
-	uint32_t	 msg_hbhid;		/* The Hop-by-Hop identifier of the message */
-	uint32_t	 msg_eteid;		/* The End-to-End identifier of the message */
-};
-
-/* The following structure represents the visible content of an AVP. All data is in host byte order. */
-struct avp_hdr {
-	avp_code_t	 avp_code;		/* the AVP Code */
-	uint8_t		 avp_flags;		/* AVP_FLAG_* flags */
-	uint32_t	 avp_len;		/* (READONLY)(Only 3 bytes are used) the length of the AVP as described in the RFC */
-	vendor_id_t	 avp_vendor;		/* Only used if AVP_FLAG_VENDOR is present */
-	union avp_value *avp_value;		/* pointer to the value of the AVP. NULL means that the value is not set / not understood.
-						   One should not directly change this value. Use the msg_avp_setvalue function instead.
-						   The content of the pointed structure can be changed directly, with this restriction:
-						     if the AVP is an OctetString, and you change the value of the pointer avp_value->os.data, then
-						     you must call free() on the previous value, and the new one must be free()-able.
-						 */
-};
-
-/* The following enum is used to browse inside message hierarchy (msg, gavp, avp) */
-enum msg_brw_dir {
-	MSG_BRW_NEXT = 1,	/* Get the next element at the same level, or NULL if this is the last element. */
-	MSG_BRW_PREV,		/* Get the previous element at the same level, or NULL if this is the first element. */
-	MSG_BRW_FIRST_CHILD,	/* Get the first child AVP of this element, if any. */
-	MSG_BRW_LAST_CHILD,	/* Get the last child AVP of this element, if any. */
-	MSG_BRW_PARENT,		/* Get the parent element of this element, if any. Only the msg_t object has no parent. */
-	MSG_BRW_WALK		/* This is equivalent to FIRST_CHILD or NEXT or PARENT->next, first that is not NULL. Use this to walk inside all AVPs. */
-};
-
-/* Some flags used in the functions bellow */
-#define AVPFL_SET_BLANK_VALUE	0x01	/* When creating an AVP, initialize its value to a blank area */
-#define AVPFL_MAX		AVPFL_SET_BLANK_VALUE	/* The biggest valid flag value */
-	
-#define MSGFL_ALLOC_ETEID	0x01	/* When creating a message, a new end-to-end ID is allocated and set in the message */
-#define MSGFL_ANSW_ERROR	0x02	/* When creating an answer message, set the 'E' bit and use the generic error ABNF instead of command-specific ABNF */
-#define MSGFL_ANSW_NOSID	0x04	/* When creating an answer message, do not add the Session-Id even if present in request */
-#define MSGFL_MAX		MSGFL_ANSW_NOSID	/* The biggest valid flag value */
-
-/**************************************************/
-/*   Message creation, manipulation, disposal     */
-/**************************************************/
-/*
- * FUNCTION:	fd_msg_avp_new
- *
- * PARAMETERS:
- *  model 	: Pointer to a DICT_AVP dictionary object describing the avp to create, or NULL.
- *  flags	: Flags to use in creation (AVPFL_*).
- *  avp 	: Upon success, pointer to the new avp is stored here.
- *
- * DESCRIPTION: 
- *   Create a new AVP instance.
- *
- * RETURN VALUE:
- *  0      	: The AVP is created.
- *  EINVAL 	: A parameter is invalid.
- *  (other standard errors may be returned, too, with their standard meaning. Example:
- *    ENOMEM 	: Memory allocation for the new avp failed.)
- */
-int fd_msg_avp_new ( struct dict_object * model, int flags, struct avp ** avp );
-
-/*
- * FUNCTION:	fd_msg_new
- *
- * PARAMETERS:
- *  model 	: Pointer to a DICT_COMMAND dictionary object describing the message to create, or NULL.
- *  flags	: combination of MSGFL_* flags.
- *  msg 	: Upon success, pointer to the new message is stored here.
- *
- * DESCRIPTION: 
- *   Create a new empty Diameter message. 
- *
- * RETURN VALUE:
- *  0      	: The message is created.
- *  EINVAL 	: A parameter is invalid.
- *  (other standard errors may be returned, too, with their standard meaning. Example:
- *    ENOMEM 	: Memory allocation for the new message failed.)
- */
-int fd_msg_new ( struct dict_object * model, int flags, struct msg ** msg );
-
-/*
- * FUNCTION:	msg_new_answer_from_req
- *
- * PARAMETERS:
- *  dict	: Pointer to the dictionary containing the model of the query.
- *  msg		: The location of the query on function call. Updated by the location of answer message on return.
- *  flag        : Pass MSGFL_ANSW_ERROR to indicate if the answer is an error message (will set the 'E' bit)
- *
- * DESCRIPTION: 
- *   This function creates the empty answer message corresponding to a request.
- *  The header is set properly (R flag, ccode, appid, hbhid, eteid)
- *  The Session-Id AVP is copied if present.
- *  The calling code should usually call fd_msg_rescode_set function on the answer.
- *  Upon return, the original query may be retrieved by calling fd_msg_answ_getq on the message.
- *
- * RETURN VALUE:
- *  0      	: Operation complete.
- *  !0      	: an error occurred.
- */
-int fd_msg_new_answer_from_req ( struct dictionary * dict, struct msg ** msg, int flag );
-
-/*
- * FUNCTION:	fd_msg_browse
- *
- * PARAMETERS:
- *  reference 	: Pointer to a struct msg or struct avp.
- *  dir         : Direction for browsing
- *  found       : If not NULL, updated with the element that has been found, if any, or NULL if no element was found / an error occurred.
- *  depth	: If not NULL, points to an integer representing the "depth" of this object in the tree. This is a relative value, updated on return.
- *
- * DESCRIPTION: 
- *   Explore the content of a message object (hierarchy). If "found" is null, only error checking is performed.
- *  If "depth" is provided, it is updated as follow on successful function return:
- *   - not modified for MSG_BRW_NEXT and MSG_BRW_PREV.
- *   - *depth = *depth + 1 for MSG_BRW_FIRST_CHILD and MSG_BRW_LAST_CHILD.
- *   - *depth = *depth - 1 for MSG_BRW_PARENT.
- *   - *depth = *depth + X for MSG_BRW_WALK, with X between 1 (returned the 1st child) and -N (returned the Nth parent's next).
- *
- * RETURN VALUE:
- *  0      	: found has been updated (if non NULL).
- *  EINVAL 	: A parameter is invalid.
- *  ENOENT	: No element has been found where requested, and "found" was NULL (otherwise, *found is set to NULL and 0 is returned). 
- */
-int fd_msg_browse_internal ( msg_or_avp * reference, enum msg_brw_dir dir, msg_or_avp ** found, int * depth );
-/* Macro to avoid having to cast the third parameter everywhere */
-#define fd_msg_browse( ref, dir, found, depth )	\
-	fd_msg_browse_internal( (ref), (dir), (void *)(found), (depth) )
-
-
-/*
- * FUNCTION:	fd_msg_avp_add
- *
- * PARAMETERS:
- *  reference 	: Pointer to a valid msg or avp.
- *  dir         : location where the new AVP should be inserted, relative to the reference. MSG_BRW_PARENT and MSG_BRW_WALK are not valid.
- *  avp         : pointer to the AVP object that must be inserted.
- *
- * DESCRIPTION: 
- *   Adds an AVP into an object that can contain it: grouped AVP or message.
- * Note that the added AVP will be freed at the same time as the object it is added to, 
- * so it should not be freed after the call to this function.
- *
- * RETURN VALUE:
- *  0      	: The AVP has been added.
- *  EINVAL 	: A parameter is invalid.
- */
-int fd_msg_avp_add ( msg_or_avp * reference, enum msg_brw_dir dir, struct avp *avp);
-
-/*
- * FUNCTION:	fd_msg_search_avp
- *
- * PARAMETERS:
- *  msg 	: The message structure in which to search the AVP.
- *  what 	: The dictionary model of the AVP to search.
- *  avp		: location where the AVP reference is stored if found.
- *
- * DESCRIPTION: 
- *   Search the first top-level AVP of a given model inside a message.
- * Note: only the first instance of the AVP is returned by this function.
- * Note: only top-level AVPs are searched, not inside grouped AVPs.
- * Use msg_browse if you need more advanced research features.
- *
- * RETURN VALUE:
- *  0      	: The AVP has been found.
- *  EINVAL 	: A parameter is invalid.
- *  ENOENT	: No AVP has been found, and "avp" was NULL (otherwise, *avp is set to NULL and 0 returned).
- */
-int fd_msg_search_avp ( struct msg * msg, struct dict_object * what, struct avp ** avp );
-
-/*
- * FUNCTION:	fd_msg_free
- *
- * PARAMETERS:
- *  object      : pointer to the message or AVP object that must be unlinked and freed.
- *
- * DESCRIPTION: 
- *   Unlink and free a message or AVP object and its children.
- *  If the object is an AVP linked into a message, the AVP is removed before being freed.
- *
- * RETURN VALUE:
- *  0      	: The message has been freed.
- *  EINVAL 	: A parameter is invalid.
- */
-int fd_msg_free ( msg_or_avp * object );
-
-/***************************************/
-/*   Dump functions                    */
-/***************************************/
-/*
- * FUNCTION:	fd_msg_dump_*
- *
- * PARAMETERS:
- *  level	: the log level (INFO, FULL, ...) at which the object is dumped
- *  obj		: A msg or avp object.
- *
- * DESCRIPTION: 
- *   These functions dump the content of a message to the debug log
- * either recursively or only the object itself.
- *
- * RETURN VALUE:
- *   -
- */
-void fd_msg_dump_walk ( int level, msg_or_avp *obj );
-void fd_msg_dump_one  ( int level, msg_or_avp *obj );
-
-
-/*********************************************/
-/*   Message metadata management functions   */
-/*********************************************/
-/*
- * FUNCTION:	fd_msg_model
- *
- * PARAMETERS:
- *  reference 	: Pointer to a valid msg or avp.
- *  model       : on success, pointer to the dictionary model of this command or AVP. NULL if the model is unknown.
- *
- * DESCRIPTION: 
- *   Retrieve the dictionary object describing this message or avp. If the object is unknown or the fd_msg_parse_dict has not been called,
- *  *model is set to NULL.
- *
- * RETURN VALUE:
- *  0      	: The model has been set.
- *  EINVAL 	: A parameter is invalid.
- */
-int fd_msg_model ( msg_or_avp * reference, struct dict_object ** model );
-
-/*
- * FUNCTION:	fd_msg_hdr
- *
- * PARAMETERS:
- *  msg 	: Pointer to a valid message object.
- *  pdata 	: Upon success, pointer to the msg_hdr structure of this message. The fields may be modified.
- *
- * DESCRIPTION: 
- *   Retrieve location of modifiable section of a message. 
- *
- * RETURN VALUE:
- *  0      	: The location has been written.
- *  EINVAL 	: A parameter is invalid.
- */
-int fd_msg_hdr ( struct msg *msg, struct msg_hdr ** pdata );
-
-/*
- * FUNCTION:	fd_msg_avp_hdr
- *
- * PARAMETERS:
- *  avp 	: Pointer to a valid avp object.
- *  pdata 	: Upon success, pointer to the avp_hdr structure of this avp. The fields may be modified.
- *
- * DESCRIPTION: 
- *   Retrieve location of modifiable data of an avp. 
- *
- * RETURN VALUE:
- *  0      	: The location has been written.
- *  EINVAL 	: A parameter is invalid.
- */
-int fd_msg_avp_hdr ( struct avp *avp, struct avp_hdr ** pdata );
-
-/*
- * FUNCTION:	fd_msg_answ_associate, fd_msg_answ_getq, fd_msg_answ_detach
- *
- * PARAMETERS:
- *  answer	: the received answer message
- *  query	: the corresponding query that had been sent
- *
- * DESCRIPTION:
- *  fd_msg_answ_associate associates a query msg with the received answer. 
- * Query is retrieved with fd_msg_answ_getq.
- * If answer message is freed, the query is also freed.
- * If the msg_answ_detach function is called, the association is removed.
- * This is meant to be called from the daemon only.
- *
- * RETURN VALUE:
- *  0 	  : ok
- *  EINVAL: a parameter is invalid
- */
-int fd_msg_answ_associate( struct msg * answer, struct msg * query );
-int fd_msg_answ_getq     ( struct msg * answer, struct msg ** query );
-int fd_msg_answ_detach   ( struct msg * answer );
-
-/*
- * FUNCTION:	fd_msg_anscb_associate, fd_msg_anscb_get
- *
- * PARAMETERS:
- *  msg		: the answer message
- *  anscb	: the callback to associate with the message
- *  data	: the data to pass to the callback
- *  timeout     : (optional, use NULL if no timeout) a timeout associated with calling the cb.
- *
- * DESCRIPTION:
- *  Associate or retrieve a callback with an answer message.
- * This is meant to be called from the daemon only.
- *
- * RETURN VALUE:
- *  0 	  : ok
- *  EINVAL: a parameter is invalid
- */
-int fd_msg_anscb_associate( struct msg * msg, void ( *anscb)(void *, struct msg **), void  * data, const struct timespec *timeout );
-int fd_msg_anscb_get      ( struct msg * msg, void (**anscb)(void *, struct msg **), void ** data );
-struct timespec *fd_msg_anscb_gettimeout( struct msg * msg ); /* returns NULL or a valid non-0 timespec */
-
-/*
- * FUNCTION:	fd_msg_rt_associate, fd_msg_rt_get
- *
- * PARAMETERS:
- *  msg		: the query message to be sent
- *  list	: the ordered list of possible next-peers
- *
- * DESCRIPTION:
- *  Associate a routing list with a query, and retrieve it.
- * If the message is freed, the list is also freed.
- *
- * RETURN VALUE:
- *  0 	  : ok
- *  EINVAL: a parameter is invalid
- */
-int fd_msg_rt_associate( struct msg * msg, struct rt_data ** rtd );
-int fd_msg_rt_get      ( struct msg * msg, struct rt_data ** rtd );
-
-/*
- * FUNCTION:	fd_msg_is_routable
- *
- * PARAMETERS:
- *  msg		: A msg object.
- *
- * DESCRIPTION: 
- *   This function returns a boolean telling if a given message is routable in the Diameter network, 
- *  or if it is a local link message only (ex: CER/CEA, DWR/DWA, ...).
- *
- * RETURN VALUE:
- *  0      	: The message is not routable / an error occurred.
- *  1      	: The message is routable.
- */
-int fd_msg_is_routable ( struct msg * msg );
-
-/*
- * FUNCTION:	fd_msg_source_(g/s)et
- *
- * PARAMETERS:
- *  msg		: A msg object.
- *  diamid	: The diameter id of the peer from which this message was received.
- *  add_rr	: if true, a Route-Record AVP is added to the message with content diamid. In that case, dict must be supplied.
- *  dict	: a dictionary with definition of Route-Record AVP (if add_rr is true)
- *
- * DESCRIPTION: 
- *   Store or retrieve the diameted id of the peer from which this message was received.
- * Will be used for example by the routing module to add the Route-Record AVP in forwarded requests,
- * or to direct answers to the appropriate peer.
- *
- * RETURN VALUE:
- *  0      	: Operation complete.
- *  !0      	: an error occurred.
- */
-int fd_msg_source_set( struct msg * msg, char * diamid, int add_rr, struct dictionary * dict );
-int fd_msg_source_get( struct msg * msg, char ** diamid );
-
-/*
- * FUNCTION:	fd_msg_eteid_get
- *
- * PARAMETERS:
- *  -
- *
- * DESCRIPTION: 
- *   Get a new unique end-to-end id value for the local peer.
- *
- * RETURN VALUE:
- *  The new assigned value. No error code is defined.
- */
-uint32_t fd_msg_eteid_get ( void );
-
-
-/*
- * FUNCTION:	fd_msg_sess_get
- *
- * PARAMETERS:
- *  dict	: the dictionary that contains the Session-Id AVP definition
- *  msg		: A valid message.
- *  session	: Location to store the session pointer when retrieved.
- *  isnew	: Indicates if the session has been created.
- *
- * DESCRIPTION:
- *  This function retrieves or creates the session object corresponding to a message.
- * If the message does not contain a Session-Id AVP, *session == NULL on return.
- * Note that the Session-Id AVP must never be modified after created in a message.
- *
- * RETURN VALUE:
- *  0 : success
- * !0 : standard error code.
- */
-int fd_msg_sess_get(struct dictionary * dict, struct msg * msg, struct session ** session, int * isnew);
-
-/***************************************/
-/*   Manage AVP values                 */
-/***************************************/
-
-/*
- * FUNCTION:	fd_msg_avp_setvalue
- *
- * PARAMETERS:
- *  avp 	: Pointer to a valid avp object with a NULL avp_value pointer. The model must be known.
- *  value 	: pointer to an avp_value. The content will be COPIED into the internal storage area. 
- *		 If data type is an octetstring, the data is also copied.
- * 		 If value is a NULL pointer, the previous data is erased and value is unset in the AVP.
- *
- * DESCRIPTION: 
- *   Initialize the avp_value field of an AVP header.
- *
- * RETURN VALUE:
- *  0      	: The avp_value pointer has been set.
- *  EINVAL 	: A parameter is invalid.
- */
-int fd_msg_avp_setvalue ( struct avp *avp, union avp_value *value );
-
-/*
- * FUNCTION:	fd_msg_avp_value_encode
- *
- * PARAMETERS:
- *  avp 	: Pointer to a valid avp object with a NULL avp_value. The model must be known.
- *  data 	: Pointer to the data that must be encoded as AVP value and stored in the AVP.
- *		 This is only valid for AVPs of derived type for which type_data_encode callback is set. (ex: Address type)
- *
- * DESCRIPTION: 
- *   Initialize the avp_value field of an AVP object from formatted data, using the AVP's type "type_data_encode" callback.
- *
- * RETURN VALUE:
- *  0      	: The avp_value has been set.
- *  EINVAL 	: A parameter is invalid.
- *  ENOTSUP 	: There is no appropriate callback registered with this AVP's type.
- */
-int fd_msg_avp_value_encode ( void *data, struct avp *avp );
-/*
- * FUNCTION:	fd_msg_avp_value_interpret
- *
- * PARAMETERS:
- *  avp 	: Pointer to a valid avp object with a non-NULL avp_value value.
- *  data 	: Upon success, formatted interpretation of the AVP value is stored here.
- *
- * DESCRIPTION: 
- *   Interpret the content of an AVP of Derived type and store the result in data pointer. The structure
- * of the data pointer is dependent on the AVP type. This function calls the "type_data_interpret" callback 
- * of the type.
- *
- * RETURN VALUE:
- *  0      	: The avp_value has been set.
- *  EINVAL 	: A parameter is invalid.
- *  ENOTSUP 	: There is no appropriate callback registered with this AVP's type.
- */
-int fd_msg_avp_value_interpret ( struct avp *avp, void *data );
-
-
-/***************************************/
-/*   Message parsing functions         */
-/***************************************/
-
-/*
- * FUNCTION:	fd_msg_bufferize
- *
- * PARAMETERS:
- *  msg		: A valid msg object. All AVPs must have a value set. 
- *  buffer 	: Upon success, this points to a buffer (malloc'd) containing the message ready for network transmission (or security transformations). 
- *		 The buffer may be freed after use.
- *  len		: if not NULL, the size of the buffer is written here. In any case, this size is updated in the msg header.
- *
- * DESCRIPTION: 
- *   Renders a message in memory as a buffer that can be sent over the network to the next peer.
- *
- * RETURN VALUE:
- *  0      	: The location has been written.
- *  EINVAL 	: The buffer does not contain a valid Diameter message.
- *  ENOMEM	: Unable to allocate enough memory to create the buffer object.
- */
-int fd_msg_bufferize ( struct msg * msg, unsigned char ** buffer, size_t * len );
-
-/*
- * FUNCTION:	fd_msg_parse_buffer
- *
- * PARAMETERS:
- *  buffer 	: Pointer to a buffer containing a message received from the network. 
- *  buflen	: the size in bytes of the buffer.
- *  msg		: Upon success, this points to a valid msg object. No AVP value is resolved in this object, nor grouped AVP.
- *
- * DESCRIPTION: 
- *   This function parses a buffer an creates a msg object to represent the structure of the message.
- *  Since no dictionary lookup is performed, the values of the AVPs are not interpreted. To interpret the values,
- *  the returned message object must be passed to fd_msg_parse_dict function.
- *  The buffer pointer is saved inside the message and will be freed when not needed anymore.
- *
- * RETURN VALUE:
- *  0      	: The location has been written.
- *  ENOMEM	: Unable to allocate enough memory to create the msg object.
- *  EBADMSG	: The buffer does not contain a valid Diameter message (or is truncated).
- *  EINVAL 	: A parameter is invalid.
- */
-int fd_msg_parse_buffer ( unsigned char ** buffer, size_t buflen, struct msg ** msg );
-
-/* Parsing Error Information structure */
-struct fd_pei {
-	char *		pei_errcode;	/* name of the error code to use */
-	struct avp *	pei_avp;	/* pointer to invalid or missing AVP (to be freed) */
-	char *		pei_message;	/* Overwrite default message if needed */
-	int		pei_protoerr; 	/* do we set the 'E' bit in the error message ? */
-};
-
-/*
- * FUNCTION:	fd_msg_parse_dict
- *
- * PARAMETERS:
- *  object	: A msg or AVP object as returned by fd_msg_parse_buffer.
- *  dict	: the dictionary containing the objects definitions to use for resolving all AVPs.
- *  error_info	: If not NULL, will contain the detail about error upon return. May be used to generate an error reply.
- *
- * DESCRIPTION: 
- *   This function looks up for the command and each children AVP definitions in the dictionary.
- *  If the dictionary definition is found, avp_model is set and the value of the AVP is interpreted accordingly and:
- *   - for grouped AVPs, the children AVP are created and interpreted also.
- *   - for numerical AVPs, the value is converted to host byte order and saved in the avp_value field.
- *   - for octetstring AVPs, the string is copied into a new buffer and its address is saved in avp_value. 
- *  If the dictionary definition is not found, avp_model is set to NULL and
- *  the content of the AVP is saved as an octetstring in an internal structure. avp_value is NULL.
- *  As a result, after this function has been called, there is no more dependency of the msg object to the message buffer, that is freed.
- *
- * RETURN VALUE:
- *  0      	: The message has been fully parsed as described.
- *  EINVAL 	: The msg parameter is invalid for this operation.
- *  ENOMEM	: Unable to allocate enough memory to complete the operation.
- *  ENOTSUP	: No dictionary definition for the command or one of the mandatory AVP was found.
- */
-int fd_msg_parse_dict ( msg_or_avp * object, struct dictionary * dict, struct fd_pei * error_info );
-
-/*
- * FUNCTION:	fd_msg_parse_rules
- *
- * PARAMETERS:
- *  object	: A msg or grouped avp object that must be verified.
- *  dict	: The dictionary containing the rules definitions.
- *  error_info	: If not NULL, the first problem information will be saved here.
- *
- * DESCRIPTION: 
- *   Check that the children of the object do not conflict with the dictionary rules (ABNF compliance).
- *
- * RETURN VALUE:
- *  0      	: The message has been fully parsed and complies to the defined rules.
- *  EBADMSG	: A conflict was detected, or a mandatory AVP is unknown in the dictionary.
- *  EINVAL 	: The msg or avp object is invalid for this operation.
- *  ENOMEM	: Unable to allocate enough memory to complete the operation.
- */
-int fd_msg_parse_rules ( msg_or_avp * object, struct dictionary * dict, struct fd_pei * error_info);
-
-
-
-/*
- * FUNCTION:	fd_msg_update_length
- *
- * PARAMETERS:
- *  object 	: Pointer to a valid msg or avp. 
- *
- * DESCRIPTION: 
- *   Update the length field of the object passed as parameter.
- * As a side effect, all children objects are also updated. Therefore, all avp_value fields of
- * the children AVPs must be set, or an error will occur.
- *
- * RETURN VALUE:
- *  0      	: The size has been recomputed.
- *  EINVAL 	: A parameter is invalid.
- */
-int fd_msg_update_length ( msg_or_avp * object );
-
-
-/*============================================================*/
-/*                         DISPATCH                           */
-/*============================================================*/
-
-/* Dispatch module (passing incoming messages to extensions registered callbacks)
- * is split between the library and the daemon.
- *
- * The library provides the support for associating dispatch callbacks with
- * dictionary objects.
- *
- * The daemon is responsible for calling the callbacks for a message when appropriate.
- *
- *
- * The dispatch module has two main roles:
- *  - help determine if a message can be handled locally (during the routing step)
- *        This decision involves only the application-id of the message.
- *  - pass the message to the callback(s) that will handle it (during the dispatch step)
- *
- * The first role is handled by the daemon.
- *
- * About the second, these are the possibilities for registering a dispatch callback:
- *
- * -> For All messages.
- *  This callback is called for all messages that are handled locally. This should be used only
- *  for debug purpose.
- *
- * -> by AVP value (constants only).
- *  This callback will be called when a message is received and contains an AVP with a specified enumerated value.
- *
- * -> by AVP.
- *  This callback will be called when the received message contains a certain AVP.
- *
- * -> by command-code.
- *  This callback will be called when the message is a specific command (and 'R' flag).
- *
- * -> by application.
- *  This callback will be called when the message has a specific application-id.
- *
- * ( by vendor: would this be useful? it may be added later)
- */
-enum disp_how {
-	DISP_HOW_ANY = 1,		/* Any message. This should be only used for debug. */
-	DISP_HOW_APPID,			/* Any message with the specified application-id */
-	DISP_HOW_CC,			/* Messages of the specified command-code (request or answer). App id may be specified. */
-	DISP_HOW_AVP,			/* Messages containing a specific AVP. Command-code and App id may be specified. */
-	DISP_HOW_AVP_ENUMVAL		/* Messages containing a specific AVP with a specific enumerated value. Command-code and App id may be specified. */
-};
-/*
- * Several criteria may be selected at the same time, for example command-code AND application id.
- *
- * If several callbacks are registered for the same object, they are called in the order they were registered.
- * The order in which the callbacks are called is:
- *  DISP_HOW_ANY
- *  DISP_HOW_AVP_ENUMVAL & DISP_HOW_AVP
- *  DISP_HOW_CC
- *  DISP_HOW_APPID
- */
-
-/* When a callback is registered, a "when" argument is passed in addition to the disp_how value,
- * to specify which values the criteria must match. */
-struct disp_when {
-	struct dict_object *	app;
-	struct dict_object *	command;
-	struct dict_object *	avp;
-	struct dict_object *	value;
-};
-
-/* Note that all the dictionary objects should really belong to the same dictionary!
- *
- * Here is the details on this "when" argument, depending on the disp_how value.
- *
- * DISP_HOW_ANY.
- *  In this case, "when" must be NULL.
- *
- * DISP_HOW_APPID.
- *  Only the "app_id" field must be set, other fields are ignored. It points to a dictionary object of type DICT_APPLICATION.
- *
- * DISP_HOW_CC.
- *  The "command" field must be defined and point to a dictionary object of type DICT_COMMAND.
- *  The "app_id" may be also set. In the case it is set, it restricts the callback to be called only with this command-code and app id.
- *  The other fields are ignored.
- *
- * DISP_HOW_AVP.
- *  The "avp" field of the structure must be set and point to a dictionary object of type DICT_AVP.
- *  The "app_id" field may be set to restrict the messages matching to a specific app id.
- *  The "command" field may also be set to a valid DICT_COMMAND object.
- *  The content of the "value" field is ignored.
- *
- * DISP_HOW_AVP_ENUMVAL.
- *  All fields have the same constraints and meaning as in DISP_REG_AVP. In addition, the "value" field must be set
- *  and points to a valid DICT_ENUMVAL object. 
- *
- * Here is a sumary of the fields: ( M : must be set; m : may be set; 0 : ignored )
- *  field:     app_id    command     avp    value
- * APPID :       M          0         0       0
- * CC    :       m          M         0       0
- * AVP   :       m          m         M       0
- * ENUMVA:       m          m         M       M
- */
-
-enum disp_action {
-	DISP_ACT_CONT,	/* The next handler should be called, unless *msg == NULL. */
-	DISP_ACT_SEND,	/* The updated message must be sent. No further callback is called. */
-	DISP_ACT_ERROR	/* An error must be created and sent as a reply -- not valid for callbacks, only for fd_msg_dispatch. */
-};
-/* The callbacks that are registered have the following prototype:
- *  	int dispatch_callback( struct msg ** msg, struct avp * avp, struct session * session, enum disp_action * action );
- *
- * CALLBACK:	dispatch_callback
- *
- * PARAMETERS:
- *  msg 	: the received message on function entry. may be updated to answer on return (see description)
- *  avp 	: for callbacks registered with DISP_HOW_AVP or DISP_HOW_AVP_ENUMVAL, direct link to the triggering AVP.
- *  session	: if the message contains a Session-Id AVP, the corresponding session object, NULL otherwise.
- *  opaque      : An opaque pointer that is registered along the session handler.
- *  action	: upon return, this tells the daemon what to do next.
- *
- * DESCRIPTION: 
- *   Called when a received message matchs the condition for which the callback was registered.
- * This callback may do any kind of processing on the message, including:
- *  - create an answer for a request.
- *  - proxy a request or message, add / remove the Proxy-Info AVP, then forward the message.
- *  - update a routing table or start a connection with a new peer, then forward the message.
- *  - ...
- * 
- * When *action == DISP_ACT_SEND on callback return, the msg pointed by *msg is passed to the routing module for sending.
- * When *action == DISP_ACT_CONT, the next registered callback is called.
- *  When the last callback gives also DISP_ACT_CONT action value, a default handler is called. It's behavior is as follow:
- *   - if the message is an answer, it is discarded.
- *   - if the message is a request, it is passed again to the routing stack, and marked as non-local handling.
- *
- * RETURN VALUE:
- *  0      	: The callback executed successfully and updated *action appropriately.
- *  !0		: standard errors. In case of error, the message is discarded.
- */
-
-/* This structure represents a handler for a registered callback, allowing its de-registration */
-struct disp_hdl;
-
-/*
- * FUNCTION:	fd_disp_register
- *
- * PARAMETERS:
- *  cb 		  : The callback function to register (see dispatch_callback description above).
- *  how	  	  : How the callback must be registered.
- *  when          : Values that must match, depending on the how argument.
- *  opaque        : A pointer that is passed back to the handler. The content is not interpreted by the framework.
- *  handle        : On success, a handler to the registered callback is stored here if not NULL. 
- *		   This handler can be used to unregister the cb.
- *
- * DESCRIPTION: 
- *   Register a new callback to handle messages delivered locally.
- *
- * RETURN VALUE:
- *  0      	: The callback is registered.
- *  EINVAL 	: A parameter is invalid.
- *  ENOMEM	: Not enough memory to complete the operation
- */
-int fd_disp_register ( int (*cb)( struct msg **, struct avp *, struct session *, void *, enum disp_action *), 
-			enum disp_how how, struct disp_when * when, void * opaque, struct disp_hdl ** handle );
-
-/*
- * FUNCTION:	fd_disp_unregister
- *
- * PARAMETERS:
- *  handle       : Location of the handle of the callback that must be unregistered.
- *  opaque       : If not NULL, the opaque data that was registered is restored here.
- *
- * DESCRIPTION: 
- *   Removes a callback previously registered by fd_disp_register.
- *
- * RETURN VALUE:
- *  0      	: The callback is unregistered.
- *  EINVAL 	: A parameter is invalid.
- */
-int fd_disp_unregister ( struct disp_hdl ** handle, void ** opaque );
-
-/* Destroy all handlers */
-void fd_disp_unregister_all ( void );
-
-/*
- * FUNCTION:	fd_msg_dispatch
- *
- * PARAMETERS:
- *  msg 	: A msg object that have already been fd_msg_parse_dict.
- *  session	: The session corresponding to this object, if any.
- *  action	: Upon return, the action that must be taken on the message
- *
- * DESCRIPTION: 
- *   Call all handlers registered for a given message.
- *  The session must have already been resolved on entry.
- *  The msg pointed may be updated during this process.
- *  Upon return, the action parameter points to what must be done next.
- *
- * RETURN VALUE:
- *  0      	: Success.
- *  EINVAL 	: A parameter is invalid.
- *  (other errors)
- */
-int fd_msg_dispatch ( struct msg ** msg, struct session * session, enum disp_action *action, const char ** error_code );
-
-
-
-/*============================================================*/
-/*                     QUEUES                                 */
-/*============================================================*/
-
-/* Management of FIFO queues of elements */
-
-/* A queue is an opaque object */
-struct fifo;
-
-/*
- * FUNCTION:	fd_fifo_new
- *
- * PARAMETERS:
- *  queue	: Upon success, a pointer to the new queue is saved here.
- *
- * DESCRIPTION: 
- *  Create a new empty queue.
- *
- * RETURN VALUE :
- *  0		: The queue has been initialized successfully.
- *  EINVAL 	: The parameter is invalid.
- *  ENOMEM	: Not enough memory to complete the creation.  
- */
-int fd_fifo_new ( struct fifo ** queue );
-
-/*
- * FUNCTION:	fd_fifo_del
- *
- * PARAMETERS:
- *  queue	: Pointer to an empty queue to delete.
- *
- * DESCRIPTION: 
- *  Destroys a queue. This is only possible if no thread is waiting for an element,
- * and the queue is empty.
- *
- * RETURN VALUE:
- *  0		: The queue has been destroyed successfully.
- *  EINVAL 	: The parameter is invalid.
- */
-int fd_fifo_del ( struct fifo  ** queue );
-
-/*
- * FUNCTION:	fd_fifo_move
- *
- * PARAMETERS:
- *  oldq	: Location of a FIFO that is to be emptied.
- *  newq	: A FIFO that will receive the old data.
- *  loc_update	: if non NULL, a place to store the pointer to new FIFO atomically with the move.
- *
- * DESCRIPTION: 
- *  Empties a queue and move its content to another one atomically.
- *
- * RETURN VALUE:
- *  0		: The queue has been destroyed successfully.
- *  EINVAL 	: A parameter is invalid.
- */
-int fd_fifo_move ( struct fifo * oldq, struct fifo * newq, struct fifo ** loc_update );
-
-/*
- * FUNCTION:	fd_fifo_length
- *
- * PARAMETERS:
- *  queue	: The queue from which to retrieve the number of elements.
- *  length	: Upon success, the current number of elements in the queue is stored here.
- *
- * DESCRIPTION: 
- *  Retrieve the number of elements in a queue.
- *
- * RETURN VALUE:
- *  0		: The length of the queue has been written.
- *  EINVAL 	: A parameter is invalid.
- */
-int fd_fifo_length ( struct fifo * queue, int * length );
-int fd_fifo_length_noerr ( struct fifo * queue ); /* no error checking version */
-
-/*
- * FUNCTION:	fd_fifo_setthrhd
- *
- * PARAMETERS:
- *  queue	: The queue for which the thresholds are being set.
- *  data	: An opaque pointer that is passed to h_cb and l_cb callbacks.
- *  high        : The high-level threshold. If the number of elements in the queue increase to this value, h_cb is called.
- *  h_cb        : if not NULL, a callback to call when the queue lengh is bigger than "high".
- *  low         : The low-level threshold. Must be < high.
- *  l_cb        : If the number of elements decrease to low, this callback is called.
- *
- * DESCRIPTION: 
- *  This function allows to adjust the number of producer / consumer threads of a queue.
- * If the consumer are slower than the producers, the number of elements in the queue increase.
- * By setting a "high" value, we allow a callback to be called when this number is too high.
- * The typical use would be to create an additional consumer thread in this callback.
- * If the queue continues to grow, the callback will be called again when the length is 2 * high, then 3*high, ... N * high
- * (the callback itself should implement a limit on the number of consumers that can be created)
- * When the queue starts to decrease, and the number of elements go under ((N - 1) * high + low, the l_cb callback is called
- * and would typially stop one of the consumer threads. If the queue continues to reduce, l_cb is again called at (N-2)*high + low,
- * and so on.
- *
- * Since there is no destructor for the data pointer, if cleanup operations are required, they should be performed in
- * l_cb when the length of the queue is becoming < low.
- *
- * Note that the callbacks are called synchronously, during fd_fifo_post or fd_fifo_get. Their operation should be quick.
- *
- * RETURN VALUE:
- *  0		: The thresholds have been set
- *  EINVAL 	: A parameter is invalid.
- */
-int fd_fifo_setthrhd ( struct fifo * queue, void * data, uint16_t high, void (*h_cb)(struct fifo *, void **), uint16_t low, void (*l_cb)(struct fifo *, void **) );
-
-/*
- * FUNCTION:	fd_fifo_post
- *
- * PARAMETERS:
- *  queue	: The queue in which the element must be posted.
- *  item	: The element that is put in the queue.
- *
- * DESCRIPTION: 
- *  An element is added in a queue. Elements are retrieved from the queue in FIFO order
- *  with the fd_fifo_get, fd_fifo_tryget, or fd_fifo_timedget functions.
- *
- * RETURN VALUE:
- *  0		: The element is queued.
- *  EINVAL 	: A parameter is invalid.
- *  ENOMEM 	: Not enough memory to complete the operation.
- */
-int fd_fifo_post_int ( struct fifo * queue, void ** item );
-#define fd_fifo_post(queue, item) \
-	fd_fifo_post_int((queue), (void *)(item))
-
-/*
- * FUNCTION:	fd_fifo_get
- *
- * PARAMETERS:
- *  queue	: The queue from which the first element must be retrieved.
- *  item	: On return, the first element of the queue is stored here.
- *
- * DESCRIPTION: 
- *  This function retrieves the first element from a queue. If the queue is empty, the function will block the 
- * thread until a new element is posted to the queue, or until the thread is canceled (in which case the 
- * function does not return).
- *
- * RETURN VALUE:
- *  0		: A new element has been retrieved.
- *  EINVAL 	: A parameter is invalid.
- */
-int fd_fifo_get_int ( struct fifo * queue, void ** item );
-#define fd_fifo_get(queue, item) \
-	fd_fifo_get_int((queue), (void *)(item))
-
-/*
- * FUNCTION:	fd_fifo_tryget
- *
- * PARAMETERS:
- *  queue	: The queue from which the element must be retrieved.
- *  item	: On return, the first element of the queue is stored here.
- *
- * DESCRIPTION: 
- *  This function is similar to fd_fifo_get, except that it will not block if 
- * the queue is empty, but return EWOULDBLOCK instead.
- *
- * RETURN VALUE:
- *  0		: A new element has been retrieved.
- *  EINVAL 	: A parameter is invalid.
- *  EWOULDBLOCK : The queue was empty.
- */
-int fd_fifo_tryget_int ( struct fifo * queue, void ** item );
-#define fd_fifo_tryget(queue, item) \
-	fd_fifo_tryget_int((queue), (void *)(item))
-
-/*
- * FUNCTION:	fd_fifo_timedget
- *
- * PARAMETERS:
- *  queue	: The queue from which the element must be retrieved.
- *  item	: On return, the element is stored here.
- *  abstime	: the absolute time until which we allow waiting for an item.
- *
- * DESCRIPTION: 
- *  This function is similar to fd_fifo_get, except that it will block if the queue is empty 
- * only until the absolute time abstime (see pthread_cond_timedwait for + info).
- * If the queue is still empty when the time expires, the function returns ETIMEDOUT
- *
- * RETURN VALUE:
- *  0		: A new item has been retrieved.
- *  EINVAL 	: A parameter is invalid.
- *  ETIMEDOUT   : The time out has passed and no item has been received.
- */
-int fd_fifo_timedget_int ( struct fifo * queue, void ** item, const struct timespec *abstime );
-#define fd_fifo_timedget(queue, item, abstime) \
-	fd_fifo_timedget_int((queue), (void *)(item), (abstime))
-
-/* Dump a fifo list and optionally its inner elements -- beware of deadlocks! */
-void fd_fifo_dump(int level, char * name, struct fifo * queue, void (*dump_item)(int level, void * item));
-
-#endif /* _LIBFREEDIAMETER_H */
Index: libfdcore/CMakeLists.txt
===================================================================
--- libfdcore/CMakeLists.txt	(revision 658)
+++ libfdcore/CMakeLists.txt	(revision 658)
@@ -0,0 +1,71 @@
+# The subproject name
+Project("freeDiameter core library" C)
+
+# Configuration parser
+BISON_FILE(fdd.y)
+FLEX_FILE(fdd.l)
+SET_SOURCE_FILES_PROPERTIES(lex.fdd.c fdd.tab.c PROPERTIES COMPILE_FLAGS "-I ${CMAKE_CURRENT_SOURCE_DIR}")
+
+# List of source files
+SET(FDCORE_SRC
+	fdcore-internal.h
+	apps.c
+	cnxctx.h
+	config.c
+	core.c
+	cnxctx.c
+	endpoints.c
+	events.c
+	extensions.c
+	dict_base_proto.c
+	messages.c
+	queues.c
+	peers.c
+	p_ce.c
+	p_cnx.c
+	p_dw.c
+	p_dp.c
+	p_expiry.c
+	p_out.c
+	p_psm.c
+	p_sr.c
+	routing_dispatch.c
+	server.c
+	tcp.c
+	)
+
+IF(NOT DISABLE_SCTP)
+	SET(FDCORE_SRC ${FDCORE_SRC} sctp.c sctps.c)
+ENDIF(NOT DISABLE_SCTP)
+
+SET(FDCORE_GEN_SRC
+		lex.fdd.c
+		fdd.tab.c
+		fdd.tab.h
+	)
+	
+# Save the list of files for the tests 
+SET(FDCORE_SRC ${FDCORE_SRC} PARENT_SCOPE)
+SET(FDCORE_GEN_SRC ${FDCORE_GEN_SRC} PARENT_SCOPE)
+
+
+# Build the executable
+ADD_LIBRARY(libfdcore SHARED ${FDCORE_SRC} ${FDCORE_GEN_SRC})
+
+# Avoid the liblib name, and set the version
+SET_TARGET_PROPERTIES(libfdcore PROPERTIES 
+	OUTPUT_NAME "fdcore"
+	SOVERSION ${FD_PROJECT_VERSION_API}
+	VERSION ${FD_PROJECT_VERSION_MAJOR}.${FD_PROJECT_VERSION_MINOR}.${FD_PROJECT_VERSION_REV})
+
+# The library itself needs other libraries 
+LINK_DIRECTORIES(${CURRENT_BINARY_DIR}/../libfdproto)
+TARGET_LINK_LIBRARIES(libfdcore libfdproto ${LFDCORE_LIBS})
+
+
+####
+## INSTALL section ##
+
+INSTALL(TARGETS libfdcore
+	LIBRARY DESTINATION ${INSTALL_LIBRARY_SUFFIX}
+	COMPONENT freeDiameter-common)
Index: libfdcore/apps.c
===================================================================
--- libfdcore/apps.c	(revision 658)
+++ libfdcore/apps.c	(revision 658)
@@ -0,0 +1,152 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@nict.go.jp>							 *
+*													 *
+* Copyright (c) 2010, 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 "fdcore-internal.h"
+
+/* Merge information into a list of apps */
+int fd_app_merge(struct fd_list * list, application_id_t aid, vendor_id_t vid, int auth, int acct)
+{
+	struct fd_list * li;
+	int skip = 0;
+	
+	/* List is ordered by appid. Avoid duplicates */
+	for (li = list; li->next != list; li = li->next) {
+		struct fd_app * na = (struct fd_app *)(li->next);
+		if (na->appid < aid)
+			continue;
+		
+		if (na->appid > aid)
+			break;
+		
+		/* Otherwise, we merge with existing entry -- ignore vendor id in this case */
+		skip = 1;
+		
+		if (auth)
+			na->flags.auth = 1;
+		if (acct)
+			na->flags.acct = 1;
+		break;
+	}
+	
+	if (!skip) {			
+		struct fd_app  * new = NULL;
+
+		CHECK_MALLOC( new = malloc(sizeof(struct fd_app)) );
+		memset(new, 0, sizeof(struct fd_app));
+		fd_list_init(&new->chain, NULL);
+		new->flags.auth = (auth ? 1 : 0);
+		new->flags.acct = (acct ? 1 : 0);
+		new->vndid = vid;
+		new->appid = aid;
+		fd_list_insert_after(li, &new->chain);
+	}
+	
+	return 0;
+}
+
+/* Check if a given application id is in a list */
+int fd_app_check(struct fd_list * list, application_id_t aid, struct fd_app **detail)
+{
+	struct fd_list * li;
+	
+	TRACE_ENTRY("%p %d %p", list, aid, detail);
+	CHECK_PARAMS(list && detail);
+	
+	*detail = NULL;
+	
+	/* Search in the list */
+	for (li = list->next; li != list; li = li->next) {
+		struct fd_app * a = (struct fd_app *)li;
+		if (a->appid < aid)
+			continue;
+		
+		if (a->appid == aid)
+			*detail = a;
+		break;
+	}
+	
+	return 0;
+}
+
+/* Check if two lists have at least one common application */
+int fd_app_check_common(struct fd_list * list1, struct fd_list * list2, int * common_found)
+{
+	struct fd_list * li1, *li2;
+	
+	TRACE_ENTRY("%p %p %p", list1, list2, common_found);
+	CHECK_PARAMS( list1 && list2 && common_found );
+	
+	/* Both lists are ordered, so advance both pointers at the same time */
+	for (li1 = list1->next, li2 = list2->next;  (li1 != list1) && (li2 != list2); ) {
+		struct fd_app * a1 = (struct fd_app *)li1, *a2 = (struct fd_app *)li2;
+		if (a1->appid < a2->appid) {
+			li1 = li1->next;
+			continue;
+		}
+		if (a1->appid > a2->appid) {
+			li2 = li2->next;
+			continue;
+		}
+		/* They are equal, compare the applications */
+		if ((a1->flags.auth && a2->flags.auth) || (a1->flags.acct && a2->flags.acct)) {
+			/* found! */
+			*common_found = 1;
+			return 0;
+		}
+		
+		/* This application is not common, advance both lists */
+		li1 = li1->next;
+		li2 = li2->next;
+	}
+	
+	/* We did not find a common app */
+	*common_found = 0;
+	return 0;
+}
+
+/* Remove the apps from a list */
+int fd_app_empty(struct fd_list * list)
+{
+	TRACE_ENTRY("%p", list);
+	CHECK_PARAMS( list );
+	
+	while (!FD_IS_LIST_EMPTY(list)) {
+		struct fd_list * li = list->next;
+		fd_list_unlink(li);
+		free(li);
+	}
+	
+	return 0;
+}
Index: libfdcore/cnxctx.c
===================================================================
--- libfdcore/cnxctx.c	(revision 658)
+++ libfdcore/cnxctx.c	(revision 658)
@@ -0,0 +1,1583 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@nict.go.jp>							 *
+*													 *
+* Copyright (c) 2010, 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 "fdcore-internal.h"
+#include "cnxctx.h"
+
+#include <net/if.h>
+#include <ifaddrs.h> /* for getifaddrs */
+
+/* The maximum size of Diameter message we accept to receive (<= 2^24) to avoid too big mallocs in case of trashed headers */
+#ifndef DIAMETER_MSG_SIZE_MAX
+#define DIAMETER_MSG_SIZE_MAX	65535	/* in bytes */
+#endif /* DIAMETER_MSG_SIZE_MAX */
+
+
+/* Connections contexts (cnxctx) in freeDiameter are wrappers around the sockets and TLS operations .
+ * They are used to hide the details of the processing to the higher layers of the daemon.
+ * They are always oriented on connections (TCP or SCTP), connectionless modes (UDP or SCTP) are not supported.
+ */
+
+/* Note: this file could be moved to libfreeDiameter instead, but since it uses gnuTLS we prefer to keep it in the daemon */
+
+/* Lifetime of a cnxctx object:
+ * 1) Creation
+ *    a) a server socket:
+ *       - create the object with fd_cnx_serv_tcp or fd_cnx_serv_sctp
+ *       - start listening incoming connections: fd_cnx_serv_listen
+ *       - accept new clients with fd_cnx_serv_accept.
+ *    b) a client socket:
+ *       - connect to a remote server with fd_cnx_cli_connect
+ *
+ * 2) Initialization
+ *    - if TLS is started first, call fd_cnx_handshake
+ *    - otherwise to receive clear messages, call fd_cnx_start_clear. fd_cnx_handshake can be called later.
+ *
+ * 3) Usage
+ *    - fd_cnx_receive, fd_cnx_send : exchange messages on this connection (send is synchronous, receive is not, but blocking).
+ *    - fd_cnx_recv_setaltfifo : when a message is received, the event is sent to an external fifo list. fd_cnx_receive does not work when the alt_fifo is set.
+ *    - fd_cnx_getid : retrieve a descriptive string for the connection (for debug)
+ *    - fd_cnx_getremoteid : identification of the remote peer (IP address or fqdn)
+ *    - fd_cnx_getcred : get the remote peer TLS credentials, after handshake
+ *
+ * 4) End
+ *    - fd_cnx_destroy
+ */
+
+/*******************************************/
+/*     Creation of a connection object     */
+/*******************************************/
+
+/* Initialize a context structure */
+static struct cnxctx * fd_cnx_init(int full)
+{
+	struct cnxctx * conn = NULL;
+
+	TRACE_ENTRY("%d", full);
+
+	CHECK_MALLOC_DO( conn = malloc(sizeof(struct cnxctx)), return NULL );
+	memset(conn, 0, sizeof(struct cnxctx));
+
+	if (full) {
+		CHECK_FCT_DO( fd_fifo_new ( &conn->cc_incoming ), return NULL );
+	}
+
+	return conn;
+}
+
+/* Create and bind a server socket to the given endpoint and port */
+struct cnxctx * fd_cnx_serv_tcp(uint16_t port, int family, struct fd_endpoint * ep)
+{
+	struct cnxctx * cnx = NULL;
+	sSS dummy;
+	sSA * sa = (sSA *) &dummy;
+
+	TRACE_ENTRY("%hu %d %p", port, family, ep);
+
+	CHECK_PARAMS_DO( port, return NULL );
+	CHECK_PARAMS_DO( ep || family, return NULL );
+	CHECK_PARAMS_DO( (! family) || (family == AF_INET) || (family == AF_INET6), return NULL );
+	CHECK_PARAMS_DO( (! ep) || (!family) || (ep->ss.ss_family == family), return NULL );
+
+	/* The connection object */
+	CHECK_MALLOC_DO( cnx = fd_cnx_init(0), return NULL );
+
+	/* Prepare the socket address information */
+	if (ep) {
+		memcpy(sa, &ep->ss, sizeof(sSS));
+	} else {
+		memset(&dummy, 0, sizeof(dummy));
+		sa->sa_family = family;
+	}
+	if (sa->sa_family == AF_INET) {
+		((sSA4 *)sa)->sin_port = htons(port);
+		cnx->cc_family = AF_INET;
+	} else {
+		((sSA6 *)sa)->sin6_port = htons(port);
+		cnx->cc_family = AF_INET6;
+	}
+
+	/* Create the socket */
+	CHECK_FCT_DO( fd_tcp_create_bind_server( &cnx->cc_socket, sa, sSAlen(sa) ), goto error );
+
+	/* Generate the name for the connection object */
+	{
+		char addrbuf[INET6_ADDRSTRLEN];
+		int  rc;
+		rc = getnameinfo(sa, sSAlen(sa), addrbuf, sizeof(addrbuf), NULL, 0, NI_NUMERICHOST);
+		if (rc)
+			snprintf(addrbuf, sizeof(addrbuf), "[err:%s]", gai_strerror(rc));
+		snprintf(cnx->cc_id, sizeof(cnx->cc_id), "TCP srv [%s]:%hu (%d)", addrbuf, port, cnx->cc_socket);
+	}
+
+	cnx->cc_proto = IPPROTO_TCP;
+
+	return cnx;
+
+error:
+	fd_cnx_destroy(cnx);
+	return NULL;
+}
+
+/* Same function for SCTP, with a list of local endpoints to bind to */
+struct cnxctx * fd_cnx_serv_sctp(uint16_t port, struct fd_list * ep_list)
+{
+#ifdef DISABLE_SCTP
+	TRACE_DEBUG(INFO, "This function should never been called when SCTP is disabled...");
+	ASSERT(0);
+	CHECK_FCT_DO( ENOTSUP, );
+	return NULL;
+#else /* DISABLE_SCTP */
+	struct cnxctx * cnx = NULL;
+
+	TRACE_ENTRY("%hu %p", port, ep_list);
+
+	CHECK_PARAMS_DO( port, return NULL );
+
+	/* The connection object */
+	CHECK_MALLOC_DO( cnx = fd_cnx_init(0), return NULL );
+
+	if (fd_g_config->cnf_flags.no_ip6) {
+		cnx->cc_family = AF_INET;
+	} else {
+		cnx->cc_family = AF_INET6; /* can create socket for both IP and IPv6 */
+	}
+	
+	/* Create the socket */
+	CHECK_FCT_DO( fd_sctp_create_bind_server( &cnx->cc_socket, cnx->cc_family, ep_list, port ), goto error );
+
+	/* Generate the name for the connection object */
+	snprintf(cnx->cc_id, sizeof(cnx->cc_id), "SCTP srv :%hu (%d)", port, cnx->cc_socket);
+
+	cnx->cc_proto = IPPROTO_SCTP;
+
+	return cnx;
+
+error:
+	fd_cnx_destroy(cnx);
+	return NULL;
+#endif /* DISABLE_SCTP */
+}
+
+/* Allow clients to connect on the server socket */
+int fd_cnx_serv_listen(struct cnxctx * conn)
+{
+	CHECK_PARAMS( conn );
+
+	switch (conn->cc_proto) {
+		case IPPROTO_TCP:
+			CHECK_FCT(fd_tcp_listen(conn->cc_socket));
+			break;
+
+#ifndef DISABLE_SCTP
+		case IPPROTO_SCTP:
+			CHECK_FCT(fd_sctp_listen(conn->cc_socket));
+			break;
+#endif /* DISABLE_SCTP */
+
+		default:
+			CHECK_PARAMS(0);
+	}
+
+	return 0;
+}
+
+/* Accept a client (blocking until a new client connects) -- cancelable */
+struct cnxctx * fd_cnx_serv_accept(struct cnxctx * serv)
+{
+	struct cnxctx * cli = NULL;
+	sSS ss;
+	socklen_t ss_len = sizeof(ss);
+	int cli_sock = 0;
+
+	TRACE_ENTRY("%p", serv);
+	CHECK_PARAMS_DO(serv, return NULL);
+	
+	/* Accept the new connection -- this is blocking until new client enters or until cancellation */
+	CHECK_SYS_DO( cli_sock = accept(serv->cc_socket, (sSA *)&ss, &ss_len), return NULL );
+	
+	if (TRACE_BOOL(INFO)) {
+		fd_log_debug("%s : accepted new client [", fd_cnx_getid(serv));
+		sSA_DUMP_NODE( &ss, NI_NUMERICHOST );
+		fd_log_debug("].\n");
+	}
+	
+	CHECK_MALLOC_DO( cli = fd_cnx_init(1), { shutdown(cli_sock, SHUT_RDWR); close(cli_sock); return NULL; } );
+	cli->cc_socket = cli_sock;
+	cli->cc_family = serv->cc_family;
+	cli->cc_proto = serv->cc_proto;
+	
+	/* Set the timeout */
+	fd_cnx_s_setto(cli->cc_socket);
+	
+	/* Generate the name for the connection object */
+	{
+		char addrbuf[INET6_ADDRSTRLEN];
+		char portbuf[10];
+		int  rc;
+		
+		/* Numeric values for debug */
+		rc = getnameinfo((sSA *)&ss, sSAlen(&ss), addrbuf, sizeof(addrbuf), portbuf, sizeof(portbuf), NI_NUMERICHOST | NI_NUMERICSERV);
+		if (rc) {
+			snprintf(addrbuf, sizeof(addrbuf), "[err:%s]", gai_strerror(rc));
+			portbuf[0] = '\0';
+		}
+		
+		snprintf(cli->cc_id, sizeof(cli->cc_id), "{%s} (%d) <- [%s]:%s (%d)", 
+				IPPROTO_NAME(cli->cc_proto), serv->cc_socket, 
+				addrbuf, portbuf, cli->cc_socket);
+		
+		/* Name for log messages */
+		rc = getnameinfo((sSA *)&ss, sSAlen(&ss), cli->cc_remid, sizeof(cli->cc_remid), NULL, 0, 0);
+		if (rc)
+			snprintf(cli->cc_remid, sizeof(cli->cc_remid), "[err:%s]", gai_strerror(rc));
+	}
+
+#ifndef DISABLE_SCTP
+	/* SCTP-specific handlings */
+	if (cli->cc_proto == IPPROTO_SCTP) {
+		/* Retrieve the number of streams */
+		CHECK_FCT_DO( fd_sctp_get_str_info( cli->cc_socket, &cli->cc_sctp_para.str_in, &cli->cc_sctp_para.str_out, NULL ), {fd_cnx_destroy(cli); return NULL;} );
+		if (cli->cc_sctp_para.str_out < cli->cc_sctp_para.str_in)
+			cli->cc_sctp_para.pairs = cli->cc_sctp_para.str_out;
+		else
+			cli->cc_sctp_para.pairs = cli->cc_sctp_para.str_in;
+		
+		TRACE_DEBUG(FULL,"%s : client '%s' (SCTP:%d, %d/%d streams)", fd_cnx_getid(serv), fd_cnx_getid(cli), cli->cc_socket, cli->cc_sctp_para.str_in, cli->cc_sctp_para.str_out);
+	}
+#endif /* DISABLE_SCTP */
+
+	return cli;
+}
+
+/* Client side: connect to a remote server -- cancelable */
+struct cnxctx * fd_cnx_cli_connect_tcp(sSA * sa /* contains the port already */, socklen_t addrlen)
+{
+	int sock = 0;
+	struct cnxctx * cnx = NULL;
+	
+	TRACE_ENTRY("%p %d", sa, addrlen);
+	CHECK_PARAMS_DO( sa && addrlen, return NULL );
+	
+	/* Create the socket and connect, which can take some time and/or fail */
+	{
+		int ret = fd_tcp_client( &sock, sa, addrlen );
+		if (ret != 0) {
+			int lvl;
+			switch (ret) {
+				case ECONNREFUSED:
+
+					/* "Normal" errors */
+					lvl = FULL;
+					break;
+				default:
+					lvl = INFO;
+			}
+			/* Some errors are expected, we log at different level */
+			TRACE_DEBUG( lvl, "fd_tcp_client returned an error: %s", strerror(ret));
+			return NULL;
+		}
+	}
+	
+	if (TRACE_BOOL(INFO)) {
+		fd_log_debug("Connection established to server '");
+		sSA_DUMP_NODE_SERV( sa, NI_NUMERICSERV);
+		fd_log_debug("' (TCP:%d).\n", sock);
+	}
+	
+	/* Once the socket is created successfuly, prepare the remaining of the cnx */
+	CHECK_MALLOC_DO( cnx = fd_cnx_init(1), { shutdown(sock, SHUT_RDWR); close(sock); return NULL; } );
+	
+	cnx->cc_socket = sock;
+	cnx->cc_family = sa->sa_family;
+	cnx->cc_proto  = IPPROTO_TCP;
+	
+	/* Set the timeout */
+	fd_cnx_s_setto(cnx->cc_socket);
+	
+	/* Generate the names for the object */
+	{
+		char addrbuf[INET6_ADDRSTRLEN];
+		char portbuf[10];
+		int  rc;
+		
+		/* Numeric values for debug */
+		rc = getnameinfo(sa, addrlen, addrbuf, sizeof(addrbuf), portbuf, sizeof(portbuf), NI_NUMERICHOST | NI_NUMERICSERV);
+		if (rc) {
+			snprintf(addrbuf, sizeof(addrbuf), "[err:%s]", gai_strerror(rc));
+			portbuf[0] = '\0';
+		}
+		
+		snprintf(cnx->cc_id, sizeof(cnx->cc_id), "{TCP} -> [%s]:%s (%d)", addrbuf, portbuf, cnx->cc_socket);
+		
+		/* Name for log messages */
+		rc = getnameinfo(sa, addrlen, cnx->cc_remid, sizeof(cnx->cc_remid), NULL, 0, 0);
+		if (rc)
+			snprintf(cnx->cc_remid, sizeof(cnx->cc_remid), "[err:%s]", gai_strerror(rc));
+	}
+	
+	return cnx;
+}
+
+/* Same for SCTP, accepts a list of remote addresses to connect to (see sctp_connectx for how they are used) */
+struct cnxctx * fd_cnx_cli_connect_sctp(int no_ip6, uint16_t port, struct fd_list * list)
+{
+#ifdef DISABLE_SCTP
+	TRACE_DEBUG(INFO, "This function should never been called when SCTP is disabled...");
+	ASSERT(0);
+	CHECK_FCT_DO( ENOTSUP, );
+	return NULL;
+#else /* DISABLE_SCTP */
+	int sock = 0;
+	struct cnxctx * cnx = NULL;
+	sSS primary;
+	
+	TRACE_ENTRY("%p", list);
+	CHECK_PARAMS_DO( list && !FD_IS_LIST_EMPTY(list), return NULL );
+	
+	{
+		int ret = fd_sctp_client( &sock, no_ip6, port, list );
+		if (ret != 0) {
+			int lvl;
+			switch (ret) {
+				case ECONNREFUSED:
+
+					/* "Normal" errors */
+					lvl = FULL;
+					break;
+				default:
+					lvl = INFO;
+			}
+			/* Some errors are expected, we log at different level */
+			TRACE_DEBUG( lvl, "fd_sctp_client returned an error: %s", strerror(ret));
+			return NULL;
+		}
+	}
+	
+	/* Once the socket is created successfuly, prepare the remaining of the cnx */
+	CHECK_MALLOC_DO( cnx = fd_cnx_init(1), { shutdown(sock, SHUT_RDWR); close(sock); return NULL; } );
+	
+	cnx->cc_socket = sock;
+	cnx->cc_family = no_ip6 ? AF_INET : AF_INET6;
+	cnx->cc_proto  = IPPROTO_SCTP;
+	
+	/* Set the timeout */
+	fd_cnx_s_setto(cnx->cc_socket);
+	
+	/* Retrieve the number of streams and primary address */
+	CHECK_FCT_DO( fd_sctp_get_str_info( sock, &cnx->cc_sctp_para.str_in, &cnx->cc_sctp_para.str_out, &primary ), goto error );
+	if (cnx->cc_sctp_para.str_out < cnx->cc_sctp_para.str_in)
+		cnx->cc_sctp_para.pairs = cnx->cc_sctp_para.str_out;
+	else
+		cnx->cc_sctp_para.pairs = cnx->cc_sctp_para.str_in;
+	
+	if (TRACE_BOOL(INFO)) {
+		fd_log_debug("Connection established to server '");
+		sSA_DUMP_NODE_SERV( &primary, NI_NUMERICSERV);
+		fd_log_debug("' (SCTP:%d, %d/%d streams).\n", sock, cnx->cc_sctp_para.str_in, cnx->cc_sctp_para.str_out);
+	}
+	
+	/* Generate the names for the object */
+	{
+		char addrbuf[INET6_ADDRSTRLEN];
+		char portbuf[10];
+		int  rc;
+		
+		/* Numeric values for debug */
+		rc = getnameinfo((sSA *)&primary, sSAlen(&primary), addrbuf, sizeof(addrbuf), portbuf, sizeof(portbuf), NI_NUMERICHOST | NI_NUMERICSERV);
+		if (rc) {
+			snprintf(addrbuf, sizeof(addrbuf), "[err:%s]", gai_strerror(rc));
+			portbuf[0] = '\0';
+		}
+		
+		snprintf(cnx->cc_id, sizeof(cnx->cc_id), "{SCTP} -> [%s]:%s (%d)", addrbuf, portbuf, cnx->cc_socket);
+		
+		/* Name for log messages */
+		rc = getnameinfo((sSA *)&primary, sSAlen(&primary), cnx->cc_remid, sizeof(cnx->cc_remid), NULL, 0, 0);
+		if (rc)
+			snprintf(cnx->cc_remid, sizeof(cnx->cc_remid), "[err:%s]", gai_strerror(rc));
+	}
+	
+	return cnx;
+
+error:
+	fd_cnx_destroy(cnx);
+	return NULL;
+#endif /* DISABLE_SCTP */
+}
+
+/* Return a string describing the connection, for debug */
+char * fd_cnx_getid(struct cnxctx * conn)
+{
+	CHECK_PARAMS_DO( conn, return "" );
+	return conn->cc_id;
+}
+
+/* Return the protocol of a connection */
+int fd_cnx_getproto(struct cnxctx * conn)
+{
+	CHECK_PARAMS_DO( conn, return 0 );
+	return conn->cc_proto;
+}
+
+/* Set the hostname to check during handshake */
+void fd_cnx_sethostname(struct cnxctx * conn, char * hn)
+{
+	CHECK_PARAMS_DO( conn, return );
+	conn->cc_tls_para.cn = hn;
+}
+
+/* Return the TLS state of a connection */
+int fd_cnx_getTLS(struct cnxctx * conn)
+{
+	CHECK_PARAMS_DO( conn, return 0 );
+	fd_cpu_flush_cache();
+	return conn->cc_status & CC_STATUS_TLS;
+}
+
+/* Get the list of endpoints (IP addresses) of the local and remote peers on this connection */
+int fd_cnx_getremoteeps(struct cnxctx * conn, struct fd_list * eps)
+{
+	TRACE_ENTRY("%p %p %p", conn, eps);
+	CHECK_PARAMS(conn && eps);
+	
+	/* Check we have a full connection object, not a listening socket (with no remote) */
+	CHECK_PARAMS( conn->cc_incoming );
+
+	/* Retrieve the peer endpoint(s) of the connection */
+	switch (conn->cc_proto) {
+		case IPPROTO_TCP: {
+			sSS ss;
+			socklen_t sl;
+			CHECK_FCT(fd_tcp_get_remote_ep(conn->cc_socket, &ss, &sl));
+			CHECK_FCT(fd_ep_add_merge( eps, (sSA *)&ss, sl, EP_FL_LL | EP_FL_PRIMARY ));
+		}
+		break;
+
+		#ifndef DISABLE_SCTP
+		case IPPROTO_SCTP: {
+			CHECK_FCT(fd_sctp_get_remote_ep(conn->cc_socket, eps));
+		}
+		break;
+		#endif /* DISABLE_SCTP */
+
+		default:
+			CHECK_PARAMS(0);
+	}
+
+	return 0;
+}
+
+/* Get a string describing the remote peer address (ip address or fqdn) */
+char * fd_cnx_getremoteid(struct cnxctx * conn)
+{
+	CHECK_PARAMS_DO( conn, return "" );
+	return conn->cc_remid;
+}
+
+/* Retrieve a list of all IP addresses of the local system from the kernel, using  */
+int fd_cnx_get_local_eps(struct fd_list * list)
+{
+	struct ifaddrs *iflist, *cur;
+	CHECK_SYS(getifaddrs(&iflist));
+	
+	for (cur = iflist; cur != NULL; cur = cur->ifa_next) {
+		if (cur->ifa_flags & IFF_LOOPBACK)
+			continue;
+		
+		if (fd_g_config->cnf_flags.no_ip4 && (cur->ifa_addr->sa_family == AF_INET))
+			continue;
+		
+		if (fd_g_config->cnf_flags.no_ip6 && (cur->ifa_addr->sa_family == AF_INET6))
+			continue;
+		
+		CHECK_FCT(fd_ep_add_merge( list, cur->ifa_addr, sSAlen(cur->ifa_addr), EP_FL_LL ));
+	}
+	
+	freeifaddrs(iflist);
+	
+	return 0;
+}
+
+
+/**************************************/
+/*     Use of a connection object     */
+/**************************************/
+
+/* An error occurred on the socket */
+void fd_cnx_markerror(struct cnxctx * conn)
+{
+	TRACE_ENTRY("%p", conn);
+	CHECK_PARAMS_DO( conn, goto fatal );
+	
+	TRACE_DEBUG(FULL, "Error flag set for socket %d (%s / %s)", conn->cc_socket, conn->cc_remid, conn->cc_id);
+	
+	/* Mark the error */
+	fd_cpu_flush_cache();
+	conn->cc_status |= CC_STATUS_ERROR;
+	
+	/* Report the error if not reported yet, and not closing */
+	if ((!(conn->cc_status & CC_STATUS_CLOSING )) && (!(conn->cc_status & CC_STATUS_SIGNALED )))  {
+		TRACE_DEBUG(FULL, "Sending FDEVP_CNX_ERROR event");
+		CHECK_FCT_DO( fd_event_send( Target_Queue(conn), FDEVP_CNX_ERROR, 0, NULL), goto fatal);
+		conn->cc_status |= CC_STATUS_SIGNALED;
+	}
+	fd_cpu_flush_cache();
+	return;
+fatal:
+	/* An unrecoverable error occurred, stop the daemon */
+	CHECK_FCT_DO(fd_event_send(fd_g_config->cnf_main_ev, FDEV_TERMINATE, 0, NULL), );	
+}
+
+/* Set the timeout option on the socket */
+void fd_cnx_s_setto(int sock) 
+{
+	struct timeval tv;
+	
+	/* Set a timeout on the socket so that in any case we are not stuck waiting for something */
+	memset(&tv, 0, sizeof(tv));
+	tv.tv_sec = 3;	/* allow 3 seconds timeout for TLS session cleanup */
+	CHECK_SYS_DO( setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)), /* best effort only */ );
+	CHECK_SYS_DO( setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)), /* Also timeout for sending, to avoid waiting forever */ );
+}
+
+/* A recv-like function, taking a cnxctx object instead of socket as entry. We use it to quickly react to timeouts without traversing GNUTLS wrapper each time */
+ssize_t fd_cnx_s_recv(struct cnxctx * conn, void *buffer, size_t length)
+{
+	ssize_t ret = 0;
+	int timedout = 0;
+again:
+	ret = recv(conn->cc_socket, buffer, length, 0);
+	/* Handle special case of timeout */
+	if ((ret < 0) && (errno == EAGAIN)) {
+		fd_cpu_flush_cache();
+		if (! (conn->cc_status & CC_STATUS_CLOSING))
+			goto again; /* don't care, just ignore */
+		if (!timedout) {
+			timedout ++; /* allow for one timeout while closing */
+			goto again;
+		}
+	}
+	
+	CHECK_SYS_DO(ret, /* continue */);
+	
+	/* Mark the error */
+	if (ret <= 0)
+		fd_cnx_markerror(conn);
+	
+	return ret;
+}
+
+/* Send */
+static ssize_t fd_cnx_s_send(struct cnxctx * conn, void *buffer, size_t length)
+{
+	ssize_t ret = 0;
+	int timedout = 0;
+again:
+	ret = send(conn->cc_socket, buffer, length, 0);
+	/* Handle special case of timeout */
+	if ((ret < 0) && (errno == EAGAIN)) {
+		fd_cpu_flush_cache();
+		if (! (conn->cc_status & CC_STATUS_CLOSING))
+			goto again; /* don't care, just ignore */
+		if (!timedout) {
+			timedout ++; /* allow for one timeout while closing */
+			goto again;
+		}
+		CHECK_SYS_DO(ret, /* continue */);
+	}
+	
+	/* Mark the error */
+	if (ret <= 0)
+		fd_cnx_markerror(conn);
+	
+	return ret;
+}
+
+/* Receiver thread (TCP & noTLS) : incoming message is directly saved into the target queue */
+static void * rcvthr_notls_tcp(void * arg)
+{
+	struct cnxctx * conn = arg;
+	
+	TRACE_ENTRY("%p", arg);
+	CHECK_PARAMS_DO(conn && (conn->cc_socket > 0), goto out);
+	
+	/* Set the thread name */
+	{
+		char buf[48];
+		snprintf(buf, sizeof(buf), "Receiver (%d) TCP/noTLS)", conn->cc_socket);
+		fd_log_threadname ( buf );
+	}
+	
+	ASSERT( conn->cc_proto == IPPROTO_TCP );
+	ASSERT( ! (conn->cc_status & CC_STATUS_TLS) );
+	ASSERT( Target_Queue(conn) );
+	
+	/* Receive from a TCP connection: we have to rebuild the message boundaries */
+	do {
+		uint8_t header[4];
+		uint8_t * newmsg;
+		size_t  length;
+		ssize_t ret = 0;
+		size_t	received = 0;
+
+		do {
+			ret = fd_cnx_s_recv(conn, &header[received], sizeof(header) - received);
+			if (ret <= 0) {
+				goto out; /* Stop the thread, the event was already sent */
+			}
+
+			received += ret;
+		} while (received < sizeof(header));
+
+		length = ((size_t)header[1] << 16) + ((size_t)header[2] << 8) + (size_t)header[3];
+
+		/* Check the received word is a valid begining of a Diameter message */
+		if ((header[0] != DIAMETER_VERSION)	/* defined in <libfreeDiameter.h> */
+		   || (length > DIAMETER_MSG_SIZE_MAX)) { /* to avoid too big mallocs */
+			/* The message is suspect */
+			TRACE_DEBUG(INFO, "Received suspect header [ver: %d, size: %zd], assume disconnection", (int)header[0], length);
+			fd_cnx_markerror(conn);
+			goto out; /* Stop the thread, the recipient of the event will cleanup */
+		}
+
+		/* Ok, now we can really receive the data */
+		CHECK_MALLOC_DO(  newmsg = malloc( length ), goto fatal );
+		memcpy(newmsg, header, sizeof(header));
+
+		while (received < length) {
+			pthread_cleanup_push(free, newmsg); /* In case we are canceled, clean the partialy built buffer */
+			ret = fd_cnx_s_recv(conn, newmsg + received, length - received);
+			pthread_cleanup_pop(0);
+
+			if (ret <= 0) {
+				free(newmsg);
+				goto out;
+			}
+			received += ret;
+		}
+		
+		/* We have received a complete message, pass it to the daemon */
+		fd_cpu_flush_cache();
+		CHECK_FCT_DO( fd_event_send( Target_Queue(conn), FDEVP_CNX_MSG_RECV, length, newmsg), /* continue or destroy everything? */);
+		
+	} while (conn->cc_loop);
+	
+out:
+	TRACE_DEBUG(FULL, "Thread terminated");	
+	return NULL;
+	
+fatal:
+	/* An unrecoverable error occurred, stop the daemon */
+	CHECK_FCT_DO(fd_event_send(fd_g_config->cnf_main_ev, FDEV_TERMINATE, 0, NULL), );
+	goto out;
+}
+
+#ifndef DISABLE_SCTP
+/* Receiver thread (SCTP & noTLS) : incoming message is directly saved into cc_incoming, no need to care for the stream ID */
+static void * rcvthr_notls_sctp(void * arg)
+{
+	struct cnxctx * conn = arg;
+	uint8_t * buf;
+	size_t    bufsz;
+	int	  event;
+	
+	TRACE_ENTRY("%p", arg);
+	CHECK_PARAMS_DO(conn && (conn->cc_socket > 0), goto fatal);
+	
+	/* Set the thread name */
+	{
+		char buf[48];
+		snprintf(buf, sizeof(buf), "Receiver (%d) SCTP/noTLS)", conn->cc_socket);
+		fd_log_threadname ( buf );
+	}
+	
+	ASSERT( conn->cc_proto == IPPROTO_SCTP );
+	ASSERT( ! (conn->cc_status & CC_STATUS_TLS) );
+	ASSERT( Target_Queue(conn) );
+	
+	do {
+		fd_cpu_flush_cache();
+		CHECK_FCT_DO( fd_sctp_recvmeta(conn->cc_socket, NULL, &buf, &bufsz, &event, &conn->cc_status), goto fatal );
+		if (event == FDEVP_CNX_ERROR) {
+			fd_cnx_markerror(conn);
+			goto out;
+		}
+		
+		if (event == FDEVP_CNX_SHUTDOWN) {
+			/* Just ignore the notification for now, we will get another error later anyway */
+			continue;
+		}
+		
+		fd_cpu_flush_cache();
+		CHECK_FCT_DO( fd_event_send( Target_Queue(conn), event, bufsz, buf), goto fatal );
+		
+	} while (conn->cc_loop || (event != FDEVP_CNX_MSG_RECV));
+	
+out:
+	TRACE_DEBUG(FULL, "Thread terminated");	
+	return NULL;
+
+fatal:
+	/* An unrecoverable error occurred, stop the daemon */
+	CHECK_FCT_DO(fd_event_send(fd_g_config->cnf_main_ev, FDEV_TERMINATE, 0, NULL), );
+	goto out;
+}
+#endif /* DISABLE_SCTP */
+
+/* Start receving messages in clear (no TLS) on the connection */
+int fd_cnx_start_clear(struct cnxctx * conn, int loop)
+{
+	TRACE_ENTRY("%p %i", conn, loop);
+	
+	CHECK_PARAMS( conn && Target_Queue(conn) && (!(conn->cc_status & CC_STATUS_TLS)) && (!conn->cc_loop));
+	
+	/* Release resources in case of a previous call was already made */
+	CHECK_FCT_DO( fd_thr_term(&conn->cc_rcvthr), /* continue */);
+	
+	/* Save the loop request */
+	conn->cc_loop = loop;
+	
+	switch (conn->cc_proto) {
+		case IPPROTO_TCP:
+			/* Start the tcp_notls thread */
+			CHECK_POSIX( pthread_create( &conn->cc_rcvthr, NULL, rcvthr_notls_tcp, conn ) );
+			break;
+#ifndef DISABLE_SCTP
+		case IPPROTO_SCTP:
+			/* Start the tcp_notls thread */
+			CHECK_POSIX( pthread_create( &conn->cc_rcvthr, NULL, rcvthr_notls_sctp, conn ) );
+			break;
+#endif /* DISABLE_SCTP */
+		default:
+			TRACE_DEBUG(INFO, "Unknown protocol: %d", conn->cc_proto);
+			ASSERT(0);
+			return ENOTSUP;
+	}
+			
+	return 0;
+}
+
+
+
+
+/* Returns 0 on error, received data size otherwise (always >= 0) */
+static ssize_t fd_tls_recv_handle_error(struct cnxctx * conn, gnutls_session_t session, void * data, size_t sz)
+{
+	ssize_t ret;
+again:	
+	CHECK_GNUTLS_DO( ret = gnutls_record_recv(session, data, sz),
+		{
+			switch (ret) {
+				case GNUTLS_E_REHANDSHAKE: 
+					fd_cpu_flush_cache();
+					if (!(conn->cc_status & CC_STATUS_CLOSING))
+						CHECK_GNUTLS_DO( ret = gnutls_handshake(session),
+							{
+								if (TRACE_BOOL(INFO)) {
+									fd_log_debug("TLS re-handshake failed on socket %d (%s) : %s\n", conn->cc_socket, conn->cc_id, gnutls_strerror(ret));
+								}
+								goto end;
+							} );
+
+				case GNUTLS_E_AGAIN:
+				case GNUTLS_E_INTERRUPTED:
+					fd_cpu_flush_cache();
+					if (!(conn->cc_status & CC_STATUS_CLOSING))
+						goto again;
+					TRACE_DEBUG(FULL, "Connection is closing, so abord gnutls_record_recv now.");
+					break;
+
+				case GNUTLS_E_UNEXPECTED_PACKET_LENGTH:
+					/* The connection is closed */
+					TRACE_DEBUG(FULL, "Got 0 size while reading the socket, probably connection closed...");
+					break;
+				
+				default:
+					TRACE_DEBUG(INFO, "This GNU TLS error is not handled, assume unrecoverable error");
+			}
+		} );
+		
+	if (ret == 0)
+		CHECK_GNUTLS_DO( gnutls_bye(session, GNUTLS_SHUT_RDWR),  );
+	
+end:	
+	if (ret <= 0)
+		fd_cnx_markerror(conn);
+	return ret;
+}
+
+/* Wrapper around gnutls_record_send to handle some error codes */
+static ssize_t fd_tls_send_handle_error(struct cnxctx * conn, gnutls_session_t session, void * data, size_t sz)
+{
+	ssize_t ret;
+again:	
+	CHECK_GNUTLS_DO( ret = gnutls_record_send(session, data, sz),
+		{
+			switch (ret) {
+				case GNUTLS_E_REHANDSHAKE: 
+					fd_cpu_flush_cache();
+					if (!(conn->cc_status & CC_STATUS_CLOSING))
+						CHECK_GNUTLS_DO( ret = gnutls_handshake(session),
+							{
+								if (TRACE_BOOL(INFO)) {
+									fd_log_debug("TLS re-handshake failed on socket %d (%s) : %s\n", conn->cc_socket, conn->cc_id, gnutls_strerror(ret));
+								}
+								goto end;
+							} );
+
+				case GNUTLS_E_AGAIN:
+				case GNUTLS_E_INTERRUPTED:
+					fd_cpu_flush_cache();
+					if (!(conn->cc_status & CC_STATUS_CLOSING))
+						goto again;
+					TRACE_DEBUG(INFO, "Connection is closing, so abord gnutls_record_send now.");
+					break;
+
+				default:
+					TRACE_DEBUG(INFO, "This TLS error is not handled, assume unrecoverable error");
+			}
+		} );
+end:	
+	if (ret <= 0)
+		fd_cnx_markerror(conn);
+		
+	return ret;
+}
+
+
+/* The function that receives TLS data and re-builds a Diameter message -- it exits only on error or cancelation */
+int fd_tls_rcvthr_core(struct cnxctx * conn, gnutls_session_t session)
+{
+	/* No guarantee that GnuTLS preserves the message boundaries, so we re-build it as in TCP */
+	do {
+		uint8_t header[4];
+		uint8_t * newmsg;
+		size_t  length;
+		ssize_t ret = 0;
+		size_t	received = 0;
+
+		do {
+			ret = fd_tls_recv_handle_error(conn, session, &header[received], sizeof(header) - received);
+			if (ret <= 0) {
+				/* The connection is closed */
+				goto out;
+			}
+			received += ret;
+		} while (received < sizeof(header));
+
+		length = ((size_t)header[1] << 16) + ((size_t)header[2] << 8) + (size_t)header[3];
+
+		/* Check the received word is a valid beginning of a Diameter message */
+		if ((header[0] != DIAMETER_VERSION)	/* defined in <libfreeDiameter.h> */
+		   || (length > DIAMETER_MSG_SIZE_MAX)) { /* to avoid too big mallocs */
+			/* The message is suspect */
+			TRACE_DEBUG(INFO, "Received suspect header [ver: %d, size: %zd], assume disconnection", (int)header[0], length);
+			fd_cnx_markerror(conn);
+			goto out;
+		}
+
+		/* Ok, now we can really receive the data */
+		CHECK_MALLOC(  newmsg = malloc( length ) );
+		memcpy(newmsg, header, sizeof(header));
+
+		while (received < length) {
+			pthread_cleanup_push(free, newmsg); /* In case we are canceled, clean the partialy built buffer */
+			ret = fd_tls_recv_handle_error(conn, session, newmsg + received, length - received);
+			pthread_cleanup_pop(0);
+
+			if (ret <= 0) {
+				free(newmsg);
+				goto out;
+			}
+			received += ret;
+		}
+		
+		/* We have received a complete message, pass it to the daemon */
+		fd_cpu_flush_cache();
+		CHECK_FCT_DO( ret = fd_event_send( Target_Queue(conn), FDEVP_CNX_MSG_RECV, length, newmsg), 
+			{ 
+				free(newmsg); 
+				CHECK_FCT_DO(fd_event_send(fd_g_config->cnf_main_ev, FDEV_TERMINATE, 0, NULL), );
+				return ret; 
+			} );
+		
+	} while (1);
+	
+out:
+	return ENOTCONN;
+}
+
+/* Receiver thread (TLS & 1 stream SCTP or TCP)  */
+static void * rcvthr_tls_single(void * arg)
+{
+	struct cnxctx * conn = arg;
+	
+	TRACE_ENTRY("%p", arg);
+	CHECK_PARAMS_DO(conn && (conn->cc_socket > 0), return NULL );
+	
+	/* Set the thread name */
+	{
+		char buf[48];
+		snprintf(buf, sizeof(buf), "Receiver (%d) TLS/single stream", conn->cc_socket);
+		fd_log_threadname ( buf );
+	}
+	
+	ASSERT( conn->cc_status & CC_STATUS_TLS );
+	ASSERT( Target_Queue(conn) );
+
+	/* The next function only returns when there is an error on the socket */	
+	CHECK_FCT_DO(fd_tls_rcvthr_core(conn, conn->cc_tls_para.session), /* continue */);
+
+	TRACE_DEBUG(FULL, "Thread terminated");	
+	return NULL;
+}
+
+/* Prepare a gnutls session object for handshake */
+int fd_tls_prepare(gnutls_session_t * session, int mode, char * priority, void * alt_creds)
+{
+	/* Create the session context */
+	CHECK_GNUTLS_DO( gnutls_init (session, mode), return ENOMEM );
+
+	/* Set the algorithm suite */
+	if (priority) {
+		const char * errorpos;
+		CHECK_GNUTLS_DO( gnutls_priority_set_direct( *session, priority, &errorpos ), 
+			{ TRACE_DEBUG(INFO, "Error in priority string '%s' at position: '%s'\n", priority, errorpos); return EINVAL; } );
+	} else {
+		CHECK_GNUTLS_DO( gnutls_priority_set( *session, fd_g_config->cnf_sec_data.prio_cache ), return EINVAL );
+	}
+
+	/* Set the credentials of this side of the connection */
+	CHECK_GNUTLS_DO( gnutls_credentials_set (*session, GNUTLS_CRD_CERTIFICATE, alt_creds ?: fd_g_config->cnf_sec_data.credentials), return EINVAL );
+
+	/* Request the remote credentials as well */
+	if (mode == GNUTLS_SERVER) {
+		gnutls_certificate_server_set_request (*session, GNUTLS_CERT_REQUIRE);
+	}
+	
+	return 0;
+}
+
+/* Verify remote credentials after successful handshake (return 0 if OK, EINVAL otherwise) */
+int fd_tls_verify_credentials(gnutls_session_t session, struct cnxctx * conn, int verbose)
+{
+	int i, ret = 0;
+	unsigned int gtret;
+	const gnutls_datum_t *cert_list;
+	unsigned int cert_list_size;
+	gnutls_x509_crt_t cert;
+	time_t now;
+	
+	TRACE_ENTRY("%p %d", conn, verbose);
+	CHECK_PARAMS(conn);
+	
+	/* Trace the session information -- http://www.gnu.org/software/gnutls/manual/gnutls.html#Obtaining-session-information */
+	if (verbose && TRACE_BOOL(FULL)) {
+		const char *tmp;
+		gnutls_kx_algorithm_t kx;
+  		gnutls_credentials_type_t cred;
+		
+		fd_log_debug("TLS Session information for connection '%s':\n", conn->cc_id);
+
+		/* print the key exchange's algorithm name */
+		GNUTLS_TRACE( kx = gnutls_kx_get (session) );
+		GNUTLS_TRACE( tmp = gnutls_kx_get_name (kx) );
+		fd_log_debug("\t - Key Exchange: %s\n", tmp);
+
+		/* Check the authentication type used and switch
+		* to the appropriate. */
+		GNUTLS_TRACE( cred = gnutls_auth_get_type (session) );
+		switch (cred)
+		{
+			case GNUTLS_CRD_IA:
+				fd_log_debug("\t - TLS/IA session\n");
+				break;
+
+			case GNUTLS_CRD_PSK:
+				/* This returns NULL in server side. */
+				if (gnutls_psk_client_get_hint (session) != NULL)
+					fd_log_debug("\t - PSK authentication. PSK hint '%s'\n",
+						gnutls_psk_client_get_hint (session));
+				/* This returns NULL in client side. */
+				if (gnutls_psk_server_get_username (session) != NULL)
+					fd_log_debug("\t - PSK authentication. Connected as '%s'\n",
+						gnutls_psk_server_get_username (session));
+				break;
+
+			case GNUTLS_CRD_ANON:	/* anonymous authentication */
+				fd_log_debug("\t - Anonymous DH using prime of %d bits\n",
+					gnutls_dh_get_prime_bits (session));
+				break;
+
+			case GNUTLS_CRD_CERTIFICATE:	/* certificate authentication */
+				/* Check if we have been using ephemeral Diffie-Hellman. */
+				if (kx == GNUTLS_KX_DHE_RSA || kx == GNUTLS_KX_DHE_DSS) {
+					fd_log_debug("\t - Ephemeral DH using prime of %d bits\n",
+						gnutls_dh_get_prime_bits (session));
+				}
+				break;
+#ifdef ENABLE_SRP				
+			case GNUTLS_CRD_SRP:
+				fd_log_debug("\t - SRP session with username %s\n",
+					gnutls_srp_server_get_username (session));
+				break;
+#endif /* ENABLE_SRP */
+
+			default:
+				fd_log_debug("\t - Different type of credentials for the session (%d).\n", cred);
+				break;
+
+		}
+
+		/* print the protocol's name (ie TLS 1.0) */
+		tmp = gnutls_protocol_get_name (gnutls_protocol_get_version (session));
+		fd_log_debug("\t - Protocol: %s\n", tmp);
+
+		/* print the certificate type of the peer. ie X.509 */
+		tmp = gnutls_certificate_type_get_name (gnutls_certificate_type_get (session));
+		fd_log_debug("\t - Certificate Type: %s\n", tmp);
+
+		/* print the compression algorithm (if any) */
+		tmp = gnutls_compression_get_name (gnutls_compression_get (session));
+		fd_log_debug("\t - Compression: %s\n", tmp);
+
+		/* print the name of the cipher used. ie 3DES. */
+		tmp = gnutls_cipher_get_name (gnutls_cipher_get (session));
+		fd_log_debug("\t - Cipher: %s\n", tmp);
+
+		/* Print the MAC algorithms name. ie SHA1 */
+		tmp = gnutls_mac_get_name (gnutls_mac_get (session));
+		fd_log_debug("\t - MAC: %s\n", tmp);
+	}
+	
+	/* First, use built-in verification */
+	CHECK_GNUTLS_DO( gnutls_certificate_verify_peers2 (session, &gtret), return EINVAL );
+	if (gtret) {
+		if (TRACE_BOOL(INFO)) {
+			fd_log_debug("TLS: Remote certificate invalid on socket %d (Remote: '%s')(Connection: '%s') :\n", conn->cc_socket, conn->cc_remid, conn->cc_id);
+			if (gtret & GNUTLS_CERT_INVALID)
+				fd_log_debug(" - The certificate is not trusted (unknown CA? expired?)\n");
+			if (gtret & GNUTLS_CERT_REVOKED)
+				fd_log_debug(" - The certificate has been revoked.\n");
+			if (gtret & GNUTLS_CERT_SIGNER_NOT_FOUND)
+				fd_log_debug(" - The certificate hasn't got a known issuer.\n");
+			if (gtret & GNUTLS_CERT_SIGNER_NOT_CA)
+				fd_log_debug(" - The certificate signer is not a CA, or uses version 1, or 3 without basic constraints.\n");
+			if (gtret & GNUTLS_CERT_INSECURE_ALGORITHM)
+				fd_log_debug(" - The certificate signature uses a weak algorithm.\n");
+		}
+		return EINVAL;
+	}
+	
+	/* Code from http://www.gnu.org/software/gnutls/manual/gnutls.html#Verifying-peer_0027s-certificate */
+	if (gnutls_certificate_type_get (session) != GNUTLS_CRT_X509)
+		return EINVAL;
+	
+	GNUTLS_TRACE( cert_list = gnutls_certificate_get_peers (session, &cert_list_size) );
+	if (cert_list == NULL)
+		return EINVAL;
+	
+	now = time(NULL);
+	
+	if (verbose && TRACE_BOOL(FULL)) {
+		char serial[40];
+		char dn[128];
+		size_t size;
+		unsigned int algo, bits;
+		time_t expiration_time, activation_time;
+		
+		fd_log_debug("TLS Certificate information for connection '%s' (%d certs provided):\n", conn->cc_id, cert_list_size);
+		for (i = 0; i < cert_list_size; i++)
+		{
+
+			CHECK_GNUTLS_DO( gnutls_x509_crt_init (&cert), return EINVAL);
+			CHECK_GNUTLS_DO( gnutls_x509_crt_import (cert, &cert_list[i], GNUTLS_X509_FMT_DER), return EINVAL);
+		
+			fd_log_debug(" Certificate %d info:\n", i);
+
+			GNUTLS_TRACE( expiration_time = gnutls_x509_crt_get_expiration_time (cert) );
+			GNUTLS_TRACE( activation_time = gnutls_x509_crt_get_activation_time (cert) );
+
+			fd_log_debug("\t - Certificate is valid since: %s", ctime (&activation_time));
+			fd_log_debug("\t - Certificate expires: %s", ctime (&expiration_time));
+
+			/* Print the serial number of the certificate. */
+			size = sizeof (serial);
+			gnutls_x509_crt_get_serial (cert, serial, &size);
+			
+			fd_log_debug("\t - Certificate serial number: ");
+			{
+				int j;
+				for (j = 0; j < size; j++) {
+					fd_log_debug("%02.2hhx", serial[j]);
+				}
+			}
+			fd_log_debug("\n");
+
+			/* Extract some of the public key algorithm's parameters */
+			GNUTLS_TRACE( algo = gnutls_x509_crt_get_pk_algorithm (cert, &bits) );
+			fd_log_debug("\t - Certificate public key: %s\n",
+			      gnutls_pk_algorithm_get_name (algo));
+
+			/* Print the version of the X.509 certificate. */
+			fd_log_debug("\t - Certificate version: #%d\n",
+			      gnutls_x509_crt_get_version (cert));
+
+			size = sizeof (dn);
+			GNUTLS_TRACE( gnutls_x509_crt_get_dn (cert, dn, &size) );
+			fd_log_debug("\t - DN: %s\n", dn);
+
+			size = sizeof (dn);
+			GNUTLS_TRACE( gnutls_x509_crt_get_issuer_dn (cert, dn, &size) );
+			fd_log_debug("\t - Issuer's DN: %s\n", dn);
+
+			GNUTLS_TRACE( gnutls_x509_crt_deinit (cert) );
+		}
+	}
+
+	/* Check validity of all the certificates */
+	for (i = 0; i < cert_list_size; i++)
+	{
+		time_t deadline;
+		
+		CHECK_GNUTLS_DO( gnutls_x509_crt_init (&cert), return EINVAL);
+		CHECK_GNUTLS_DO( gnutls_x509_crt_import (cert, &cert_list[i], GNUTLS_X509_FMT_DER), return EINVAL);
+		
+		GNUTLS_TRACE( deadline = gnutls_x509_crt_get_expiration_time(cert) );
+		if ((deadline != (time_t)-1) && (deadline < now)) {
+			if (TRACE_BOOL(INFO)) {
+				fd_log_debug("TLS: Remote certificate invalid on socket %d (Remote: '%s')(Connection: '%s') :\n", conn->cc_socket, conn->cc_remid, conn->cc_id);
+				fd_log_debug(" - The certificate %d in the chain is expired\n", i);
+			}
+			ret = EINVAL;
+		}
+		
+		GNUTLS_TRACE( deadline = gnutls_x509_crt_get_activation_time(cert) );
+		if ((deadline != (time_t)-1) && (deadline > now)) {
+			if (TRACE_BOOL(INFO)) {
+				fd_log_debug("TLS: Remote certificate invalid on socket %d (Remote: '%s')(Connection: '%s') :\n", conn->cc_socket, conn->cc_remid, conn->cc_id);
+				fd_log_debug(" - The certificate %d in the chain is not yet activated\n", i);
+			}
+			ret = EINVAL;
+		}
+		
+		if ((i == 0) && (conn->cc_tls_para.cn)) {
+			if (!gnutls_x509_crt_check_hostname (cert, conn->cc_tls_para.cn)) {
+				if (TRACE_BOOL(INFO)) {
+					fd_log_debug("TLS: Remote certificate invalid on socket %d (Remote: '%s')(Connection: '%s') :\n", conn->cc_socket, conn->cc_remid, conn->cc_id);
+					fd_log_debug(" - The certificate hostname does not match '%s'\n", conn->cc_tls_para.cn);
+				}
+				ret = EINVAL;
+			}
+		}
+		
+		GNUTLS_TRACE( gnutls_x509_crt_deinit (cert) );
+	}
+
+	return ret;
+}
+
+/* TLS handshake a connection; no need to have called start_clear before. Reception is active if handhsake is successful */
+int fd_cnx_handshake(struct cnxctx * conn, int mode, char * priority, void * alt_creds)
+{
+	TRACE_ENTRY( "%p %d %p %p", conn, mode, priority, alt_creds);
+	CHECK_PARAMS( conn && (!(conn->cc_status & CC_STATUS_TLS)) && ( (mode == GNUTLS_CLIENT) || (mode == GNUTLS_SERVER) ) && (!conn->cc_loop) );
+
+	/* Save the mode */
+	conn->cc_tls_para.mode = mode;
+	
+	/* Cancel receiving thread if any -- it should already be terminated anyway, we just release the resources */
+	CHECK_FCT_DO( fd_thr_term(&conn->cc_rcvthr), /* continue */);
+	
+	/* Once TLS handshake is done, we don't stop after the first message */
+	conn->cc_loop = 1;
+	
+	/* Prepare the master session credentials and priority */
+	CHECK_FCT( fd_tls_prepare(&conn->cc_tls_para.session, mode, priority, alt_creds) );
+
+	/* Special case: multi-stream TLS is not natively managed in GNU TLS, we use a wrapper library */
+	if (conn->cc_sctp_para.pairs > 1) {
+#ifdef DISABLE_SCTP
+		ASSERT(0);
+		CHECK_FCT( ENOTSUP );
+#else /* DISABLE_SCTP */
+		/* Initialize the wrapper, start the demux thread */
+		CHECK_FCT( fd_sctps_init(conn) );
+#endif /* DISABLE_SCTP */
+	} else {
+		/* Set the transport pointer passed to push & pull callbacks */
+		GNUTLS_TRACE( gnutls_transport_set_ptr( conn->cc_tls_para.session, (gnutls_transport_ptr_t) conn ) );
+
+		/* Set the push and pull callbacks */
+		GNUTLS_TRACE( gnutls_transport_set_pull_function(conn->cc_tls_para.session, (void *)fd_cnx_s_recv) );
+		GNUTLS_TRACE( gnutls_transport_set_push_function(conn->cc_tls_para.session, (void *)fd_cnx_s_send) );
+	}
+
+	/* Mark the connection as protected from here, so that the gnutls credentials will be freed */
+	fd_cpu_flush_cache();
+	conn->cc_status |= CC_STATUS_TLS;
+
+	/* Handshake master session */
+	{
+		int ret;
+		
+		/* When gnutls 2.10.1 is around, we should use gnutls_certificate_set_verify_function and fd_tls_verify_credentials, so that handshake fails directly. */
+		
+		CHECK_GNUTLS_DO( ret = gnutls_handshake(conn->cc_tls_para.session),
+			{
+				if (TRACE_BOOL(INFO)) {
+					fd_log_debug("TLS Handshake failed on socket %d (%s) : %s\n", conn->cc_socket, conn->cc_id, gnutls_strerror(ret));
+				}
+				fd_cnx_markerror(conn);
+				return EINVAL;
+			} );
+
+		/* Now verify the remote credentials are valid -- only simple tests here */
+		CHECK_FCT_DO( fd_tls_verify_credentials(conn->cc_tls_para.session, conn, 1), 
+			{  
+				CHECK_GNUTLS_DO( gnutls_bye(conn->cc_tls_para.session, GNUTLS_SHUT_RDWR),  );
+				fd_cnx_markerror(conn);
+				return EINVAL;
+			});
+	}
+
+	/* Multi-stream TLS: handshake other streams as well */
+	if (conn->cc_sctp_para.pairs > 1) {
+#ifndef DISABLE_SCTP
+		/* Start reading the messages from the master session. That way, if the remote peer closed, we are not stuck inside handshake */
+		CHECK_FCT(fd_sctps_startthreads(conn, 0));
+		
+		/* Resume all additional sessions from the master one. */
+		CHECK_FCT(fd_sctps_handshake_others(conn, priority, alt_creds));
+
+		/* Start decrypting the messages from all threads and queuing them in target queue */
+		CHECK_FCT(fd_sctps_startthreads(conn, 1));
+#endif /* DISABLE_SCTP */
+	} else {
+		/* Start decrypting the data */
+		CHECK_POSIX( pthread_create( &conn->cc_rcvthr, NULL, rcvthr_tls_single, conn ) );
+	}
+	
+	return 0;
+}
+
+/* Retrieve TLS credentials of the remote peer, after handshake */
+int fd_cnx_getcred(struct cnxctx * conn, const gnutls_datum_t **cert_list, unsigned int *cert_list_size)
+{
+	TRACE_ENTRY("%p %p %p", conn, cert_list, cert_list_size);
+	CHECK_PARAMS( conn && (conn->cc_status & CC_STATUS_TLS) && cert_list && cert_list_size );
+	
+	/* This function only works for X.509 certificates. */
+	CHECK_PARAMS( gnutls_certificate_type_get (conn->cc_tls_para.session) == GNUTLS_CRT_X509 );
+	
+	GNUTLS_TRACE( *cert_list = gnutls_certificate_get_peers (conn->cc_tls_para.session, cert_list_size) );
+	if (*cert_list == NULL) {
+		TRACE_DEBUG(INFO, "No certificate was provided by remote peer / an error occurred.");
+		return EINVAL;
+	}
+
+	TRACE_DEBUG( FULL, "Saved certificate chain (%d certificates) in peer structure.", *cert_list_size);
+	
+	return 0;
+}
+
+/* Receive next message. if timeout is not NULL, wait only until timeout. This function only pulls from a queue, mgr thread is filling that queue aynchrounously. */
+/* if the altfifo has been set on this conn object, this function must not be called */
+int fd_cnx_receive(struct cnxctx * conn, struct timespec * timeout, unsigned char **buf, size_t * len)
+{
+	int    ev;
+	size_t ev_sz;
+	void * ev_data;
+	
+	TRACE_ENTRY("%p %p %p %p", conn, timeout, buf, len);
+	CHECK_PARAMS(conn && (conn->cc_socket > 0) && buf && len);
+	CHECK_PARAMS(conn->cc_rcvthr != (pthread_t)NULL);
+	CHECK_PARAMS(conn->cc_alt == NULL);
+
+	/* Now, pull the first event */
+get_next:
+	if (timeout) {
+		CHECK_FCT( fd_event_timedget(conn->cc_incoming, timeout, FDEVP_PSM_TIMEOUT, &ev, &ev_sz, &ev_data) );
+	} else {
+		CHECK_FCT( fd_event_get(conn->cc_incoming, &ev, &ev_sz, &ev_data) );
+	}
+	
+	switch (ev) {
+		case FDEVP_CNX_MSG_RECV:
+			/* We got one */
+			*len = ev_sz;
+			*buf = ev_data;
+			return 0;
+			
+		case FDEVP_PSM_TIMEOUT:
+			TRACE_DEBUG(FULL, "Timeout event received");
+			return ETIMEDOUT;
+			
+		case FDEVP_CNX_EP_CHANGE:
+			/* We ignore this event */
+			goto get_next;
+			
+		case FDEVP_CNX_ERROR:
+			TRACE_DEBUG(FULL, "Received ERROR event on the connection");
+			return ENOTCONN;
+	}
+	
+	TRACE_DEBUG(INFO, "Received unexpected event %d (%s)", ev, fd_pev_str(ev));
+	return EINVAL;
+}
+
+/* Set an alternate FIFO list to send FDEVP_CNX_* events to */
+int fd_cnx_recv_setaltfifo(struct cnxctx * conn, struct fifo * alt_fifo)
+{
+	TRACE_ENTRY( "%p %p", conn, alt_fifo );
+	CHECK_PARAMS( conn && alt_fifo && conn->cc_incoming );
+	
+	/* The magic function does it all */
+	CHECK_FCT( fd_fifo_move( conn->cc_incoming, alt_fifo, &conn->cc_alt ) );
+	
+	return 0;
+}
+
+/* Send function when no multi-stream is involved, or sending on stream #0 (send() always use stream 0)*/
+static int send_simple(struct cnxctx * conn, unsigned char * buf, size_t len)
+{
+	ssize_t ret;
+	size_t sent = 0;
+	TRACE_ENTRY("%p %p %zd", conn, buf, len);
+	do {
+		fd_cpu_flush_cache();
+		if (conn->cc_status & CC_STATUS_TLS) {
+			CHECK_GNUTLS_DO( ret = fd_tls_send_handle_error(conn, conn->cc_tls_para.session, buf + sent, len - sent),  );
+		} else {
+			/* Maybe better to replace this call with sendmsg for atomic sending? */
+			CHECK_SYS_DO( ret = fd_cnx_s_send(conn, buf + sent, len - sent), );
+		}
+		if (ret <= 0)
+			return ENOTCONN;
+		
+		sent += ret;
+	} while ( sent < len );
+	return 0;
+}
+
+/* Send a message -- this is synchronous -- and we assume it's never called by several threads at the same time, so we don't protect. */
+int fd_cnx_send(struct cnxctx * conn, unsigned char * buf, size_t len, uint32_t flags)
+{
+	TRACE_ENTRY("%p %p %zd %x", conn, buf, len, flags);
+	
+	CHECK_PARAMS(conn && (conn->cc_socket > 0) && (! (conn->cc_status & CC_STATUS_ERROR)) && buf && len);
+
+	TRACE_DEBUG(FULL, "Sending %zdb %sdata on connection %s", len, (conn->cc_status & CC_STATUS_TLS) ? "TLS-protected ":"", conn->cc_id);
+	
+	switch (conn->cc_proto) {
+		case IPPROTO_TCP:
+			CHECK_FCT( send_simple(conn, buf, len) );
+			break;
+		
+#ifndef DISABLE_SCTP
+		case IPPROTO_SCTP: {
+			if (flags & FD_CNX_BROADCAST) {
+				/* Send the buffer over all other streams */
+				uint16_t str;
+				fd_cpu_flush_cache();
+				if (conn->cc_status & CC_STATUS_TLS) {
+					for ( str=1; str < conn->cc_sctp_para.pairs; str++) {
+						ssize_t ret;
+						size_t sent = 0;
+						do {
+							CHECK_GNUTLS_DO( ret = fd_tls_send_handle_error(conn, conn->cc_sctps_data.array[str].session, buf + sent, len - sent), );
+							if (ret <= 0)
+								return ENOTCONN;
+
+							sent += ret;
+						} while ( sent < len );
+					}
+				} else {
+					for ( str=1; str < conn->cc_sctp_para.str_out; str++) {
+						CHECK_FCT_DO( fd_sctp_sendstr(conn->cc_socket, str, buf, len, &conn->cc_status), { fd_cnx_markerror(conn); return ENOTCONN; } );
+					}
+				}
+				
+				/* Set the ORDERED flag also so that it is sent over stream 0 as well */
+				flags &= FD_CNX_ORDERED;
+			}
+			
+			if (flags & FD_CNX_ORDERED) {
+				/* We send over stream #0 */
+				CHECK_FCT( send_simple(conn, buf, len) );
+			} else {
+				/* Default case : no flag specified */
+			
+				int another_str = 0; /* do we send over stream #0 ? */
+				
+				if ((conn->cc_sctp_para.str_out > 1) && ((! (conn->cc_status & CC_STATUS_TLS)) || (conn->cc_sctp_para.pairs > 1)))  {
+					/* Update the id of the stream we will send this message over */
+					conn->cc_sctp_para.next += 1;
+					conn->cc_sctp_para.next %= ((conn->cc_status & CC_STATUS_TLS) ? conn->cc_sctp_para.pairs : conn->cc_sctp_para.str_out);
+					another_str = (conn->cc_sctp_para.next ? 1 : 0);
+				}
+
+				if ( ! another_str ) {
+					CHECK_FCT( send_simple(conn, buf, len) );
+				} else {
+					if (!(conn->cc_status & CC_STATUS_TLS)) {
+						CHECK_FCT_DO( fd_sctp_sendstr(conn->cc_socket, conn->cc_sctp_para.next, buf, len, &conn->cc_status), { fd_cnx_markerror(conn); return ENOTCONN; } );
+					} else {
+						/* push the record to the appropriate session */
+						ssize_t ret;
+						size_t sent = 0;
+						ASSERT(conn->cc_sctps_data.array != NULL);
+						do {
+							CHECK_GNUTLS_DO( ret = fd_tls_send_handle_error(conn, conn->cc_sctps_data.array[conn->cc_sctp_para.next].session, buf + sent, len - sent), );
+							if (ret <= 0)
+								return ENOTCONN;
+
+							sent += ret;
+						} while ( sent < len );
+					}
+				}
+			}
+		}
+		break;
+#endif /* DISABLE_SCTP */
+	
+		default:
+			TRACE_DEBUG(INFO, "Unknwon protocol: %d", conn->cc_proto);
+			ASSERT(0);
+			return ENOTSUP;	/* or EINVAL... */
+	}
+	
+	return 0;
+}
+
+
+/**************************************/
+/*     Destruction of connection      */
+/**************************************/
+
+/* Destroy a conn structure, and shutdown the socket */
+void fd_cnx_destroy(struct cnxctx * conn)
+{
+	TRACE_ENTRY("%p", conn);
+	
+	CHECK_PARAMS_DO(conn, return);
+	
+	fd_cpu_flush_cache();
+	conn->cc_status |= CC_STATUS_CLOSING;
+	
+	/* Initiate shutdown of the TLS session(s): call gnutls_bye(WR), then read until error */
+	if (conn->cc_status & CC_STATUS_TLS) {
+#ifndef DISABLE_SCTP
+		if (conn->cc_sctp_para.pairs > 1) {
+			if (! (conn->cc_status & CC_STATUS_ERROR )) {
+				/* Bye on master session */
+				CHECK_GNUTLS_DO( gnutls_bye(conn->cc_tls_para.session, GNUTLS_SHUT_WR), fd_cnx_markerror(conn) );
+			}
+
+			if (! (conn->cc_status & CC_STATUS_ERROR ) ) {
+				/* and other stream pairs */
+				fd_sctps_bye(conn);
+			}
+
+			if (! (conn->cc_status & CC_STATUS_ERROR ) ) {
+				/* Now wait for all decipher threads to terminate */
+				fd_sctps_waitthreadsterm(conn);
+			} else {
+				/* Abord the threads, the connection is dead already */
+				fd_sctps_stopthreads(conn);
+			}
+
+			/* Deinit gnutls resources */
+			fd_sctps_gnutls_deinit_others(conn);
+			if (conn->cc_tls_para.session) {
+				GNUTLS_TRACE( gnutls_deinit(conn->cc_tls_para.session) );
+				conn->cc_tls_para.session = NULL;
+			}
+			
+			/* Destroy the wrapper (also stops the demux thread) */
+			fd_sctps_destroy(conn);
+
+		} else {
+#endif /* DISABLE_SCTP */
+		/* We are not using the sctps wrapper layer */
+			if (! (conn->cc_status & CC_STATUS_ERROR ) ) {
+				/* Master session */
+				CHECK_GNUTLS_DO( gnutls_bye(conn->cc_tls_para.session, GNUTLS_SHUT_WR), fd_cnx_markerror(conn) );
+			}
+
+			if (! (conn->cc_status & CC_STATUS_ERROR ) ) {
+				/* In this case, just wait for thread rcvthr_tls_single to terminate */
+				if (conn->cc_rcvthr != (pthread_t)NULL) {
+					CHECK_POSIX_DO(  pthread_join(conn->cc_rcvthr, NULL), /* continue */  );
+					conn->cc_rcvthr = (pthread_t)NULL;
+				}
+			} else {
+				/* Cancel the receiver thread in case it did not already terminate */
+				CHECK_FCT_DO( fd_thr_term(&conn->cc_rcvthr), /* continue */ );
+			}
+			
+			/* Free the resources of the TLS session */
+			if (conn->cc_tls_para.session) {
+				GNUTLS_TRACE( gnutls_deinit(conn->cc_tls_para.session) );
+				conn->cc_tls_para.session = NULL;
+			}
+		
+#ifndef DISABLE_SCTP
+		}
+#endif /* DISABLE_SCTP */
+	}
+	
+	/* Terminate the thread in case it is not done yet -- is there any such case left ?*/
+	CHECK_FCT_DO( fd_thr_term(&conn->cc_rcvthr), /* continue */ );
+		
+	/* Shut the connection down */
+	if (conn->cc_socket > 0) {
+		shutdown(conn->cc_socket, SHUT_RDWR);
+		close(conn->cc_socket);
+		conn->cc_socket = -1;
+	}
+	
+	/* Empty and destroy FIFO list */
+	if (conn->cc_incoming) {
+		fd_event_destroy( &conn->cc_incoming, free );
+	}
+	
+	/* Free the object */
+	free(conn);
+	
+	/* Done! */
+	return;
+}
Index: libfdcore/cnxctx.h
===================================================================
--- libfdcore/cnxctx.h	(revision 658)
+++ libfdcore/cnxctx.h	(revision 658)
@@ -0,0 +1,141 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@nict.go.jp>							 *
+*													 *
+* Copyright (c) 2010, 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 definitions for internal use in the connection context files */
+
+#ifndef _CNXCTX_H
+#define _CNXCTX_H
+
+/* The connection context structure */
+struct cnxctx {
+	char		cc_id[60];	/* The name of this connection */
+	char		cc_remid[60];	/* Id of remote peer */
+	
+	int 		cc_socket;	/* The socket object of the connection -- <=0 if no socket is created */
+
+	int 		cc_family;	/* AF_INET or AF_INET6 (mixed) */
+	int 		cc_proto;	/* IPPROTO_TCP or IPPROTO_SCTP */
+	uint32_t	cc_status;	/* True if the object is being destroyed: we don't send events anymore */
+	#define 	CC_STATUS_CLOSING	1
+	#define 	CC_STATUS_ERROR		2
+	#define 	CC_STATUS_SIGNALED	4
+	#define 	CC_STATUS_TLS		8
+
+	pthread_t	cc_rcvthr;	/* thread for receiving messages on the connection */
+	int		cc_loop;	/* tell the thread if it loops or stops after the first message is received */
+	
+	struct fifo *	cc_incoming;	/* FIFO queue of events received on the connection, FDEVP_CNX_* */
+	struct fifo *	cc_alt;		/* alternate fifo to send FDEVP_CNX_* events to. */
+	#define Target_Queue(cnx)	((cnx)->cc_alt ?: (cnx)->cc_incoming)
+
+	/* If cc_tls == true */
+	struct {
+		char 				*cn;		/* If not NULL, remote certif will be checked to match this Common Name */
+		int				 mode; 		/* GNUTLS_CLIENT / GNUTLS_SERVER */
+		gnutls_session_t 		 session;	/* Session object (stream #0 in case of SCTP) */
+	}		cc_tls_para;
+
+	/* If cc_proto == SCTP */
+	struct	{
+		uint16_t str_out;	/* Out streams */
+		uint16_t str_in;	/* In streams */
+		uint16_t pairs;		/* max number of pairs ( = min(in, out)) */
+		uint16_t next;		/* # of stream the next message will be sent to */
+	} 		cc_sctp_para;
+
+	/* If both conditions */
+	struct {
+		struct sctps_ctx *array; /* an array of cc_sctp_para.pairs elements -- the #0 is special (session is outside)*/
+		struct sr_store	 *sess_store; /* Session data of the master session, to resume the children sessions */
+	} 		cc_sctps_data;
+};
+
+void fd_cnx_markerror(struct cnxctx * conn);
+
+/* Socket */
+ssize_t fd_cnx_s_recv(struct cnxctx * conn, void *buffer, size_t length);
+void fd_cnx_s_setto(int sock);
+
+/* TLS */
+int fd_tls_rcvthr_core(struct cnxctx * conn, gnutls_session_t session);
+int fd_tls_prepare(gnutls_session_t * session, int mode, char * priority, void * alt_creds);
+int fd_tls_verify_credentials(gnutls_session_t session, struct cnxctx * conn, int verbose);
+
+/* TCP */
+int fd_tcp_create_bind_server( int * sock, sSA * sa, socklen_t salen );
+int fd_tcp_listen( int sock );
+int fd_tcp_client( int *sock, sSA * sa, socklen_t salen );
+int fd_tcp_get_local_ep(int sock, sSS * ss, socklen_t *sl);
+int fd_tcp_get_remote_ep(int sock, sSS * ss, socklen_t *sl);
+
+#ifndef DISABLE_SCTP
+/* SCTP */
+int fd_sctp_create_bind_server( int * sock, int family, struct fd_list * list, uint16_t port );
+int fd_sctp_listen( int sock );
+int fd_sctp_client( int *sock, int no_ip6, uint16_t port, struct fd_list * list );
+int fd_sctp_get_local_ep(int sock,  struct fd_list * list);
+int fd_sctp_get_remote_ep(int sock, struct fd_list * list);
+int fd_sctp_get_str_info( int sock, uint16_t *in, uint16_t *out, sSS *primary );
+int fd_sctp_sendstr(int sock, uint16_t strid, uint8_t * buf, size_t len, uint32_t * cc_closing);
+int fd_sctp_recvmeta(int sock, uint16_t * strid, uint8_t ** buf, size_t * len, int *event, uint32_t * cc_closing);
+
+/* TLS over SCTP (multi-stream) */
+struct sctps_ctx {
+	struct cnxctx 	*parent; 	/* for info such as socket, conn name, event list */
+	uint16_t	 strid;		/* Stream # of this session */
+	struct fifo	*raw_recv;	/* Raw data received on this stream, for demux */
+	struct {
+		uint8_t *buf;
+		size_t   bufsz;
+		size_t   offset;
+	} 		 partial;	/* If the pull function did not read the full content of first message in raw, it stores it here for next read call. */
+	pthread_t	 thr;		/* Thread to decrypt raw data in this pair of streams */
+	gnutls_session_t session;	/* TLS context using this pair of streams -- except if strid == 0, in that case session is outside the array */
+};
+
+int fd_sctps_init(struct cnxctx * conn);
+int fd_sctps_handshake_others(struct cnxctx * conn, char * priority, void * alt_creds);
+int fd_sctps_startthreads(struct cnxctx * conn, int others);
+void fd_sctps_bye(struct cnxctx * conn);
+void fd_sctps_waitthreadsterm(struct cnxctx * conn);
+void fd_sctps_gnutls_deinit_others(struct cnxctx * conn);
+void fd_sctps_stopthreads(struct cnxctx * conn);
+void fd_sctps_destroy(struct cnxctx * conn);
+
+#endif /* DISABLE_SCTP */
+
+
+#endif /* _CNXCTX_H */
+
Index: libfdcore/config.c
===================================================================
--- libfdcore/config.c	(revision 658)
+++ libfdcore/config.c	(revision 658)
@@ -0,0 +1,488 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@nict.go.jp>							 *
+*													 *
+* Copyright (c) 2010, 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 "fdcore-internal.h"
+#include <sys/stat.h>
+
+/* Configuration management */
+
+#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 */
+
+/* Initialize the fd_g_config structure to default values -- it should already have been initialized to all-0 */
+int fd_conf_init()
+{
+	TRACE_ENTRY();
+	
+	fd_g_config->cnf_eyec = EYEC_CONFIG;
+	
+	fd_g_config->cnf_timer_tc = 30;
+	fd_g_config->cnf_timer_tw = 30;
+	
+	fd_g_config->cnf_port     = 3868;
+	fd_g_config->cnf_port_tls = 3869;
+	fd_g_config->cnf_sctp_str = 30;
+	fd_g_config->cnf_dispthr  = 4;
+	fd_list_init(&fd_g_config->cnf_endpoints, NULL);
+	fd_list_init(&fd_g_config->cnf_apps, NULL);
+	#ifdef DISABLE_SCTP
+	fd_g_config->cnf_flags.no_sctp = 1;
+	#endif /* DISABLE_SCTP */
+	
+	fd_g_config->cnf_orstateid = (uint32_t) time(NULL);
+	
+	CHECK_FCT( fd_dict_init(&fd_g_config->cnf_dict) );
+	CHECK_FCT( fd_fifo_new(&fd_g_config->cnf_main_ev) );
+	
+	/* TLS parameters */
+	CHECK_GNUTLS_DO( gnutls_certificate_allocate_credentials (&fd_g_config->cnf_sec_data.credentials), return ENOMEM );
+	CHECK_GNUTLS_DO( gnutls_dh_params_init (&fd_g_config->cnf_sec_data.dh_cache), return ENOMEM );
+
+	return 0;
+}
+
+void fd_conf_dump()
+{
+	if (!TRACE_BOOL(INFO))
+		return;
+	
+	fd_log_debug("-- Configuration :\n");
+	fd_log_debug("  Debug trace level ...... : %+d\n", fd_g_debug_lvl);
+	fd_log_debug("  Configuration file ..... : %s\n", fd_g_config->cnf_file);
+	fd_log_debug("  Diameter Identity ...... : %s (l:%Zi)\n", fd_g_config->cnf_diamid, fd_g_config->cnf_diamid_len);
+	fd_log_debug("  Diameter Realm ......... : %s (l:%Zi)\n", fd_g_config->cnf_diamrlm, fd_g_config->cnf_diamrlm_len);
+	fd_log_debug("  Tc Timer ............... : %u\n", fd_g_config->cnf_timer_tc);
+	fd_log_debug("  Tw Timer ............... : %u\n", fd_g_config->cnf_timer_tw);
+	fd_log_debug("  Local port ............. : %hu\n", fd_g_config->cnf_port);
+	fd_log_debug("  Local secure port ...... : %hu\n", fd_g_config->cnf_port_tls);
+	fd_log_debug("  Number of SCTP streams . : %hu\n", fd_g_config->cnf_sctp_str);
+	fd_log_debug("  Number of server threads : %hu\n", fd_g_config->cnf_dispthr);
+	if (FD_IS_LIST_EMPTY(&fd_g_config->cnf_endpoints)) {
+		fd_log_debug("  Local endpoints ........ : Default (use all available)\n");
+	} else {
+		fd_log_debug("  Local endpoints ........ : \n");
+		fd_ep_dump( 29, &fd_g_config->cnf_endpoints );
+	}
+	if (FD_IS_LIST_EMPTY(&fd_g_config->cnf_apps)) {
+		fd_log_debug("  Local applications ..... : (none)\n");
+	} else {
+		struct fd_list * li = fd_g_config->cnf_apps.next;
+		fd_log_debug("  Local applications ..... : ");
+		while (li != &fd_g_config->cnf_apps) {
+			struct fd_app * app = (struct fd_app *)li;
+			if (li != fd_g_config->cnf_apps.next) fd_log_debug("                             ");
+			fd_log_debug("App: %u\t%s%s\tVnd: %u\n", 
+					app->appid,
+					app->flags.auth ? "Au" : "--",
+					app->flags.acct ? "Ac" : "--",
+					app->vndid);
+			li = li->next;
+		}
+	}
+	
+	fd_log_debug("  Flags : - IP ........... : %s\n", fd_g_config->cnf_flags.no_ip4 ? "DISABLED" : "Enabled");
+	fd_log_debug("          - IPv6 ......... : %s\n", fd_g_config->cnf_flags.no_ip6 ? "DISABLED" : "Enabled");
+	fd_log_debug("          - Relay app .... : %s\n", fd_g_config->cnf_flags.no_fwd ? "DISABLED" : "Enabled");
+	fd_log_debug("          - TCP .......... : %s\n", fd_g_config->cnf_flags.no_tcp ? "DISABLED" : "Enabled");
+	#ifdef DISABLE_SCTP
+	fd_log_debug("          - SCTP ......... : DISABLED (at compilation)\n");
+	#else /* DISABLE_SCTP */
+	fd_log_debug("          - SCTP ......... : %s\n", fd_g_config->cnf_flags.no_sctp ? "DISABLED" : "Enabled");
+	#endif /* DISABLE_SCTP */
+	fd_log_debug("          - Pref. proto .. : %s\n", fd_g_config->cnf_flags.pr_tcp ? "TCP" : "SCTP");
+	fd_log_debug("          - TLS method ... : %s\n", fd_g_config->cnf_flags.tls_alg ? "INBAND" : "Separate port");
+	
+	fd_log_debug("  TLS :   - Certificate .. : %s\n", fd_g_config->cnf_sec_data.cert_file ?: "(NONE)");
+	fd_log_debug("          - Private key .. : %s\n", fd_g_config->cnf_sec_data.key_file ?: "(NONE)");
+	fd_log_debug("          - CA (trust) ... : %s (%d certs)\n", fd_g_config->cnf_sec_data.ca_file ?: "(none)", fd_g_config->cnf_sec_data.ca_file_nr);
+	fd_log_debug("          - CRL .......... : %s\n", fd_g_config->cnf_sec_data.crl_file ?: "(none)");
+	fd_log_debug("          - Priority ..... : %s\n", fd_g_config->cnf_sec_data.prio_string ?: "(default: '" GNUTLS_DEFAULT_PRIORITY "')");
+	if (fd_g_config->cnf_sec_data.dh_file)
+		fd_log_debug("          - DH file ...... : %s\n", fd_g_config->cnf_sec_data.dh_file);
+	else
+		fd_log_debug("          - DH bits ...... : %d\n", fd_g_config->cnf_sec_data.dh_bits ?: GNUTLS_DEFAULT_DHBITS);
+	
+	fd_log_debug("  Origin-State-Id ........ : %u\n", fd_g_config->cnf_orstateid);
+}
+
+/* Parse the configuration file (using the yacc parser) */
+int fd_conf_parse()
+{
+	extern FILE * fddin;
+	
+	/* Attempt to find the configuration file */
+	if (!fd_g_config->cnf_file)
+		fd_g_config->cnf_file = FD_DEFAULT_CONF_FILENAME;
+	
+	fddin = fopen(fd_g_config->cnf_file, "r");
+	if ((fddin == NULL) && (*fd_g_config->cnf_file != '/')) {
+		/* We got a relative path, attempt to add the default directory prefix */
+		char * bkp = fd_g_config->cnf_file;
+		CHECK_MALLOC( fd_g_config->cnf_file = malloc(strlen(bkp) + strlen(DEFAULT_CONF_PATH) + 2) ); /* we will not free it, but not important */
+		sprintf( fd_g_config->cnf_file, DEFAULT_CONF_PATH "/%s", bkp );
+		fddin = fopen(fd_g_config->cnf_file, "r");
+	}
+	if (fddin == NULL) {
+		int ret = errno;
+		fprintf(stderr, "Unable to open configuration file %s for reading: %s\n", fd_g_config->cnf_file, strerror(ret));
+		return ret;
+	}
+	
+	/* call yacc parser */
+	TRACE_DEBUG (FULL, "Parsing configuration file: %s", fd_g_config->cnf_file);
+	CHECK_FCT(  fddparse(fd_g_config)  );
+	
+	/* close the file */
+	fclose(fddin);
+	
+	/* Check that TLS private key was given */
+	if (! fd_g_config->cnf_sec_data.key_file) {
+		fprintf(stderr, "Missing private key configuration for TLS. Please provide the TLS_cred configuration directive.\n");
+		return EINVAL;
+	}
+	
+	/* Resolve hostname if not provided */
+	if (fd_g_config->cnf_diamid == NULL) {
+#ifndef HOST_NAME_MAX
+#define HOST_NAME_MAX 1024
+#endif /* HOST_NAME_MAX */
+		char buf[HOST_NAME_MAX + 1];
+		struct addrinfo hints, *info;
+		int ret;
+		
+		/* local host name */
+		CHECK_SYS(gethostname(buf, sizeof(buf)));
+		
+		/* get FQDN */
+		memset(&hints, 0, sizeof hints);
+		hints.ai_flags = AI_CANONNAME;
+
+		ret = getaddrinfo(buf, NULL, &hints, &info);
+		if (ret != 0) {
+			fprintf(stderr, "Error resolving local FQDN :\n"
+					" '%s' : %s\n"
+					"Please provide Identity in configuration file.\n",
+					buf, gai_strerror(ret));
+			return EINVAL;
+		}
+		CHECK_MALLOC( fd_g_config->cnf_diamid = strdup(info->ai_canonname) );
+		freeaddrinfo(info);
+	}
+	
+	/* cache the length of the diameter id for the session module */
+	fd_g_config->cnf_diamid_len = strlen(fd_g_config->cnf_diamid);
+	
+	/* Handle the realm part */
+	if (fd_g_config->cnf_diamrlm == NULL) {
+		char * start = NULL;
+		
+		/* Check the diameter identity is a fqdn */
+		start = strchr(fd_g_config->cnf_diamid, '.');
+		if ((start == NULL) || (start[1] == '\0')) {
+			fprintf(stderr, "Unable to extract realm from the Identity '%s'.\n"
+					"Please fix your Identity setting or provide Realm.\n",
+					fd_g_config->cnf_diamid);
+			return EINVAL;
+		}		
+		
+		CHECK_MALLOC( fd_g_config->cnf_diamrlm = strdup( start + 1 )  ); 
+	}
+	fd_g_config->cnf_diamrlm_len = strlen(fd_g_config->cnf_diamrlm);
+	
+	/* Validate some flags */
+	if (fd_g_config->cnf_flags.no_ip4 && fd_g_config->cnf_flags.no_ip6) {
+		fprintf(stderr, "IP and IPv6 cannot be disabled at the same time.\n");
+		return EINVAL;
+	}
+	if (fd_g_config->cnf_flags.no_tcp && fd_g_config->cnf_flags.no_sctp) {
+		fprintf(stderr, "TCP and SCTP cannot be disabled at the same time.\n");
+		return EINVAL;
+	}
+	
+	/* Validate local endpoints */
+	if ((!FD_IS_LIST_EMPTY(&fd_g_config->cnf_endpoints)) && (fd_g_config->cnf_flags.no_ip4 || fd_g_config->cnf_flags.no_ip6)) {
+		struct fd_list * li;
+		for ( li = fd_g_config->cnf_endpoints.next; li != &fd_g_config->cnf_endpoints; li = li->next) {
+			struct fd_endpoint * ep = (struct fd_endpoint *)li;
+			if ( (fd_g_config->cnf_flags.no_ip4 && (ep->sa.sa_family == AF_INET))
+			   ||(fd_g_config->cnf_flags.no_ip6 && (ep->sa.sa_family == AF_INET6)) ) {
+				li = li->prev;
+				fd_list_unlink(&ep->chain);
+				if (TRACE_BOOL(INFO)) {
+					fd_log_debug("Info: Removing local address conflicting with the flags no_IP / no_IP6 : ");
+					sSA_DUMP_NODE( &ep->sa, NI_NUMERICHOST );
+					fd_log_debug("\n");
+				}
+				free(ep);
+			}
+		}
+	}
+	
+	/* Configure TLS default parameters */
+	if (! fd_g_config->cnf_sec_data.prio_string) {
+		const char * err_pos = NULL;
+		CHECK_GNUTLS_DO( gnutls_priority_init( 
+					&fd_g_config->cnf_sec_data.prio_cache,
+					GNUTLS_DEFAULT_PRIORITY,
+					&err_pos),
+				 { TRACE_DEBUG(INFO, "Error in priority string at position : %s", err_pos); return EINVAL; } );
+	}
+	
+	/* Verify that our certificate is valid -- otherwise remote peers will reject it */
+	{
+		int ret = 0, i;
+		
+		gnutls_datum_t certfile;
+		size_t alloc = 0;
+		
+		gnutls_x509_crt_t * certs = NULL;
+		unsigned int cert_max = 0;
+		
+		gnutls_x509_crt_t * CA_list;
+		int CA_list_length;
+		
+		gnutls_x509_crl_t * CRL_list;
+		int CRL_list_length;
+		
+		unsigned int verify;
+		time_t now;
+		
+		memset(&certfile, 0, sizeof(certfile));
+		
+		/* Read the certificate file */
+		FILE *stream = fopen (fd_g_config->cnf_sec_data.cert_file, "rb");
+		if (!stream) {
+			int err = errno;
+			TRACE_DEBUG(INFO, "An error occurred while opening '%s': %s\n", fd_g_config->cnf_sec_data.cert_file, strerror(err));
+			return err; 
+		}
+		do {
+			uint8_t * realloced = NULL;
+			size_t read = 0;
+			
+			if (alloc < certfile.size + BUFSIZ + 1) {
+				alloc += alloc / 2 + BUFSIZ + 1;
+				CHECK_MALLOC_DO( realloced = realloc(certfile.data, alloc),
+					{
+						free(certfile.data);
+						return ENOMEM;
+					} )
+				certfile.data = realloced;
+			}
+			
+			read = fread( certfile.data + certfile.size, 1, alloc - certfile.size - 1, stream );
+			certfile.size += read;
+			
+			if (ferror(stream)) {
+				int err = errno;
+				TRACE_DEBUG(INFO, "An error occurred while reading '%s': %s\n", fd_g_config->cnf_sec_data.cert_file, strerror(err));
+				return err; 
+			}
+		} while (!feof(stream));
+		certfile.data[certfile.size] = '\0';
+		fclose(stream);
+		
+		/* Import the certificate(s) */
+		GNUTLS_TRACE( ret = gnutls_x509_crt_list_import(NULL, &cert_max, &certfile, GNUTLS_X509_FMT_PEM, GNUTLS_X509_CRT_LIST_IMPORT_FAIL_IF_EXCEED) );
+		if (ret != GNUTLS_E_SHORT_MEMORY_BUFFER) {
+			CHECK_GNUTLS_DO(ret, return EINVAL);
+		}
+		
+		CHECK_MALLOC( certs = calloc(cert_max, sizeof(gnutls_x509_crt_t)) );
+		CHECK_GNUTLS_DO( gnutls_x509_crt_list_import(certs, &cert_max, &certfile, GNUTLS_X509_FMT_PEM, 0),
+			{
+				TRACE_DEBUG(INFO, "Failed to import the data from file '%s'", fd_g_config->cnf_sec_data.cert_file);
+				free(certfile.data);
+				return EINVAL;
+			} );
+		free(certfile.data);
+		
+		ASSERT(cert_max >= 1);
+		
+		/* Now, verify the list against the local CA and CRL */
+		GNUTLS_TRACE( gnutls_certificate_get_x509_cas (fd_g_config->cnf_sec_data.credentials, &CA_list, (unsigned int *) &CA_list_length) );
+		GNUTLS_TRACE( gnutls_certificate_get_x509_crls (fd_g_config->cnf_sec_data.credentials, &CRL_list, (unsigned int *) &CRL_list_length) );
+		CHECK_GNUTLS_DO( gnutls_x509_crt_list_verify(certs, cert_max, CA_list, CA_list_length, CRL_list, CRL_list_length, 0, &verify),
+			{
+				TRACE_DEBUG(INFO, "Failed to verify the local certificate '%s' against local credentials. Please check your certificate is valid.", fd_g_config->cnf_sec_data.cert_file);
+				return EINVAL;
+			} );
+		if (verify) {
+			fd_log_debug("TLS: Local certificate chain '%s' is invalid :\n", fd_g_config->cnf_sec_data.cert_file);
+			if (verify & GNUTLS_CERT_INVALID)
+				fd_log_debug(" - The certificate is not trusted (unknown CA? expired?)\n");
+			if (verify & GNUTLS_CERT_REVOKED)
+				fd_log_debug(" - The certificate has been revoked.\n");
+			if (verify & GNUTLS_CERT_SIGNER_NOT_FOUND)
+				fd_log_debug(" - The certificate hasn't got a known issuer.\n");
+			if (verify & GNUTLS_CERT_SIGNER_NOT_CA)
+				fd_log_debug(" - The certificate signer is not a CA, or uses version 1, or 3 without basic constraints.\n");
+			if (verify & GNUTLS_CERT_INSECURE_ALGORITHM)
+				fd_log_debug(" - The certificate signature uses a weak algorithm.\n");
+			return EINVAL;
+		}
+	
+		/* Check the local Identity is valid with the certificate */
+		if (!gnutls_x509_crt_check_hostname (certs[0], fd_g_config->cnf_diamid)) {
+			fd_log_debug("TLS: Local certificate '%s' is invalid :\n", fd_g_config->cnf_sec_data.cert_file);
+			fd_log_debug(" - The certificate hostname does not match '%s'\n", fd_g_config->cnf_diamid);
+			return EINVAL;
+		}
+		
+		/* Check validity of all the certificates in the chain */
+		now = time(NULL);
+		for (i = 0; i < cert_max; i++)
+		{
+			time_t deadline;
+
+			GNUTLS_TRACE( deadline = gnutls_x509_crt_get_expiration_time(certs[i]) );
+			if ((deadline != (time_t)-1) && (deadline < now)) {
+				fd_log_debug("TLS: Local certificate chain '%s' is invalid :\n", fd_g_config->cnf_sec_data.cert_file);
+				fd_log_debug(" - The certificate %d in the chain is expired\n", i);
+				return EINVAL;
+			}
+
+			GNUTLS_TRACE( deadline = gnutls_x509_crt_get_activation_time(certs[i]) );
+			if ((deadline != (time_t)-1) && (deadline > now)) {
+				fd_log_debug("TLS: Local certificate chain '%s' is invalid :\n", fd_g_config->cnf_sec_data.cert_file);
+				fd_log_debug(" - The certificate %d in the chain is not yet activated\n", i);
+				return EINVAL;
+			}
+		}
+		
+		/* Everything checked OK, free the certificate list */
+		for (i = 0; i < cert_max; i++)
+		{
+			GNUTLS_TRACE( gnutls_x509_crt_deinit (certs[i]) );
+		}
+		free(certs);
+	}
+	
+	
+	/* gnutls_certificate_set_verify_limits -- so far the default values are fine... */
+	
+	/* DH */
+	if (fd_g_config->cnf_sec_data.dh_file) {
+		gnutls_datum_t dhparams = { NULL, 0 };
+		size_t alloc = 0;
+		FILE *stream = fopen (fd_g_config->cnf_sec_data.dh_file, "rb");
+		if (!stream) {
+			int err = errno;
+			TRACE_DEBUG(INFO, "An error occurred while opening '%s': %s\n", fd_g_config->cnf_sec_data.dh_file, strerror(err));
+			return err; 
+		}
+		do {
+			uint8_t * realloced = NULL;
+			size_t read = 0;
+			
+			if (alloc < dhparams.size + BUFSIZ + 1) {
+				alloc += alloc / 2 + BUFSIZ + 1;
+				CHECK_MALLOC_DO( realloced = realloc(dhparams.data, alloc),
+					{
+						free(dhparams.data);
+						return ENOMEM;
+					} )
+				dhparams.data = realloced;
+			}
+			
+			read = fread( dhparams.data + dhparams.size, 1, alloc - dhparams.size - 1, stream );
+			dhparams.size += read;
+			
+			if (ferror(stream)) {
+				int err = errno;
+				TRACE_DEBUG(INFO, "An error occurred while reading '%s': %s\n", fd_g_config->cnf_sec_data.dh_file, strerror(err));
+				return err; 
+			}
+		} while (!feof(stream));
+		dhparams.data[dhparams.size] = '\0';
+		fclose(stream);
+		CHECK_GNUTLS_DO( gnutls_dh_params_import_pkcs3( 
+					fd_g_config->cnf_sec_data.dh_cache,
+					&dhparams,
+					GNUTLS_X509_FMT_PEM),
+					 { TRACE_DEBUG(INFO, "Error in DH bits value : %d", fd_g_config->cnf_sec_data.dh_bits ?: GNUTLS_DEFAULT_DHBITS); return EINVAL; } );
+		free(dhparams.data);
+		
+	} else {
+		TRACE_DEBUG(INFO, "Generating fresh Diffie-Hellman parameters of size %d (this takes some time)... ", fd_g_config->cnf_sec_data.dh_bits ?: GNUTLS_DEFAULT_DHBITS);
+		CHECK_GNUTLS_DO( gnutls_dh_params_generate2( 
+					fd_g_config->cnf_sec_data.dh_cache,
+					fd_g_config->cnf_sec_data.dh_bits ?: GNUTLS_DEFAULT_DHBITS),
+					 { TRACE_DEBUG(INFO, "Error in DH bits value : %d", fd_g_config->cnf_sec_data.dh_bits ?: GNUTLS_DEFAULT_DHBITS); return EINVAL; } );
+	}			
+	
+	return 0;
+}
+
+
+/* Destroy contents of fd_g_config structure */
+int fd_conf_deinit()
+{
+	TRACE_ENTRY();
+	
+	/* Free the TLS parameters */
+	gnutls_priority_deinit(fd_g_config->cnf_sec_data.prio_cache);
+	gnutls_dh_params_deinit(fd_g_config->cnf_sec_data.dh_cache);
+	gnutls_certificate_free_credentials(fd_g_config->cnf_sec_data.credentials);
+	
+	free(fd_g_config->cnf_sec_data.cert_file); fd_g_config->cnf_sec_data.cert_file = NULL;
+	free(fd_g_config->cnf_sec_data.key_file); fd_g_config->cnf_sec_data.key_file = NULL;
+	free(fd_g_config->cnf_sec_data.ca_file); fd_g_config->cnf_sec_data.ca_file = NULL;
+	free(fd_g_config->cnf_sec_data.crl_file); fd_g_config->cnf_sec_data.crl_file = NULL;
+	free(fd_g_config->cnf_sec_data.prio_string); fd_g_config->cnf_sec_data.prio_string = NULL;
+	free(fd_g_config->cnf_sec_data.dh_file); fd_g_config->cnf_sec_data.dh_file = NULL;
+	
+	/* Destroy dictionary */
+	CHECK_FCT_DO( fd_dict_fini(&fd_g_config->cnf_dict), );
+	
+	/* Destroy the main event queue */
+	CHECK_FCT_DO( fd_fifo_del(&fd_g_config->cnf_main_ev), );
+	
+	/* Destroy the local endpoints and applications */
+	CHECK_FCT_DO(fd_ep_filter(&fd_g_config->cnf_endpoints, 0 ), );
+	CHECK_FCT_DO(fd_app_empty(&fd_g_config->cnf_apps ), );
+	
+	/* Destroy the local identity */	
+	free(fd_g_config->cnf_diamid); fd_g_config->cnf_diamid = NULL;
+	free(fd_g_config->cnf_diamrlm); fd_g_config->cnf_diamrlm = NULL;
+	
+	return 0;
+}
+
+
Index: libfdcore/core.c
===================================================================
--- libfdcore/core.c	(revision 658)
+++ libfdcore/core.c	(revision 658)
@@ -0,0 +1,296 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@nict.go.jp>							 *
+*													 *
+* Copyright (c) 2009, WIDE Project and NICT								 *
+* All rights reserved.											 *
+* 													 *
+* Redistribution and use of this software in source and binary forms, with or without modification, are  *
+* permitted provided that the following conditions are met:						 *
+* 													 *
+* * Redistributions of source code must retain the above 						 *
+*   copyright notice, this list of conditions and the 							 *
+*   following disclaimer.										 *
+*    													 *
+* * Redistributions in binary form must reproduce the above 						 *
+*   copyright notice, this list of conditions and the 							 *
+*   following disclaimer in the documentation and/or other						 *
+*   materials provided with the distribution.								 *
+* 													 *
+* * Neither the name of the WIDE Project or NICT nor the 						 *
+*   names of its contributors may be used to endorse or 						 *
+*   promote products derived from this software without 						 *
+*   specific prior written permission of WIDE Project and 						 *
+*   NICT.												 *
+* 													 *
+* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED *
+* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
+* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR *
+* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 	 *
+* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 	 *
+* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR *
+* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF   *
+* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.								 *
+*********************************************************************************************************/
+
+#include "fdcore-internal.h"
+
+#include <gcrypt.h>
+
+/* The static configuration structure */
+static struct fd_config conf;
+struct fd_config * fd_g_config = &conf;
+
+/* gcrypt functions to support posix threads */
+GCRY_THREAD_OPTION_PTHREAD_IMPL;
+
+/* Signal extensions when the framework is completly initialized */
+static int             is_ready = 0;
+static pthread_mutex_t is_ready_mtx = PTHREAD_MUTEX_INITIALIZER;
+static pthread_cond_t  is_ready_cnd = PTHREAD_COND_INITIALIZER;
+
+static int signal_framework_ready(void)
+{
+	TRACE_ENTRY("");
+	CHECK_POSIX( pthread_mutex_lock( &is_ready_mtx ) );
+	is_ready = 1;
+	CHECK_POSIX( pthread_cond_broadcast( &is_ready_cnd ) );
+	CHECK_POSIX( pthread_mutex_unlock( &is_ready_mtx ) );
+	return 0;
+}
+
+/* Thread that process incoming events on the main queue -- and terminates the framework when requested */
+static pthread_t core_runner = (pthread_t)NULL;
+
+static void * core_runner_thread(void * arg) 
+{
+	fd_log_threadname("Core Runner");
+	
+	/* Handle events incoming on the main event queue */
+	while (1) {
+		int code; size_t sz; void * data;
+		CHECK_FCT_DO(  fd_event_get(fd_g_config->cnf_main_ev, &code, &sz, &data),  break  );
+		switch (code) {
+			case FDEV_DUMP_DICT:
+				fd_dict_dump(fd_g_config->cnf_dict);
+				break;
+			
+			case FDEV_DUMP_EXT:
+				fd_ext_dump();
+				break;
+			
+			case FDEV_DUMP_SERV:
+				fd_servers_dump();
+				break;
+			
+			case FDEV_DUMP_QUEUES:
+				fd_fifo_dump(0, "Incoming messages", fd_g_incoming, fd_msg_dump_walk);
+				fd_fifo_dump(0, "Outgoing messages", fd_g_outgoing, fd_msg_dump_walk);
+				fd_fifo_dump(0, "Local messages",    fd_g_local,    fd_msg_dump_walk);
+				break;
+			
+			case FDEV_DUMP_CONFIG:
+				fd_conf_dump();
+				break;
+			
+			case FDEV_DUMP_PEERS:
+				fd_peer_dump_list(FULL);
+				break;
+			
+			case FDEV_TRIGGER:
+				{
+					int tv, *p;
+					CHECK_PARAMS_DO( sz == sizeof(int), 
+						{
+							TRACE_DEBUG(NONE, "Internal error: got FDEV_TRIGGER without trigger value!");
+							ASSERT(0);
+							goto end;
+						} );
+					p = data;
+					tv = *p;
+					free(p);
+					CHECK_FCT_DO( fd_event_trig_call_cb(tv), goto end );
+				}
+				break;
+			
+			case FDEV_TERMINATE:
+				goto end;
+			
+			default:
+				TRACE_DEBUG(INFO, "Unexpected event in the main event queue (%d), ignored.\n", code);
+		}
+	}
+	
+end:
+	TRACE_DEBUG(INFO, FD_PROJECT_BINARY " framework is stopping...");
+	
+	/* cleanups */
+	CHECK_FCT_DO( fd_servers_stop(), /* Stop accepting new connections */ );
+	CHECK_FCT_DO( fd_rtdisp_cleanstop(), /* Stop dispatch thread(s) after a clean loop if possible */ );
+	CHECK_FCT_DO( fd_peer_fini(), /* Stop all connections */ );
+	CHECK_FCT_DO( fd_rtdisp_fini(), /* Stop routing threads and destroy routing queues */ );
+	
+	CHECK_FCT_DO( fd_ext_term(), /* Cleanup all extensions */ );
+	CHECK_FCT_DO( fd_rtdisp_cleanup(), /* destroy remaining handlers */ );
+	
+	GNUTLS_TRACE( gnutls_global_deinit() );
+	
+	CHECK_FCT_DO( fd_conf_deinit(), );
+	
+	CHECK_FCT_DO( fd_event_trig_fini(), );
+	
+	fd_log_debug(FD_PROJECT_BINARY " framework is terminated.\n");
+	
+	fd_libproto_fini();
+	
+	return NULL;
+}
+
+/*********************************/
+/* Public interfaces */
+
+/* Return a string describing the version of the library */
+const char *fd_core_version(void)
+{
+	return _stringize(FD_PROJECT_VERSION_MAJOR) "." _stringize(FD_PROJECT_VERSION_MINOR) "." _stringize(FD_PROJECT_VERSION_REV);
+}
+
+/* Initialize the libfdcore internals. This also initializes libfdproto */
+int fd_core_initialize(void)
+{
+	int ret;
+	
+	memset(fd_g_config, 0, sizeof(struct fd_config));
+	
+	/* Initialize the library -- must come first since it initializes the debug facility */
+	ret = fd_libproto_init();
+	if (ret != 0) {
+		fprintf(stderr, "Unable to initialize libfdproto: %s\n", strerror(ret));
+		return ret;
+	}
+	
+	TRACE_DEBUG(INFO, "libfdproto initialized.");
+	
+	/* Name this thread */
+	fd_log_threadname("Main");
+	
+	/* Initialize gcrypt and gnutls */
+	GNUTLS_TRACE( (void) gcry_control (GCRYCTL_SET_THREAD_CBS, &gcry_threads_pthread) );
+	GNUTLS_TRACE( (void) gcry_control (GCRYCTL_ENABLE_QUICK_RANDOM, 0) );
+	CHECK_GNUTLS_DO( gnutls_global_init(), return EINVAL );
+	if ( ! gnutls_check_version(GNUTLS_VERSION) ) {
+		fprintf(stderr, "The GNUTLS library is too old; found '%s', need '" GNUTLS_VERSION "'\n", gnutls_check_version(NULL));
+		return EINVAL;
+	} else {
+		TRACE_DEBUG(INFO, "libgnutls '%s', libgcrypt '%s', initialized.", gnutls_check_version(NULL), gcry_check_version(NULL) );
+	}
+	
+	/* Initialize the config with default values */
+	CHECK_FCT( fd_conf_init() );
+
+	/* Add definitions of the base protocol */
+	CHECK_FCT( fd_dict_base_protocol(fd_g_config->cnf_dict) );
+	
+	/* Initialize some modules */
+	CHECK_FCT( fd_queues_init() );
+	CHECK_FCT( fd_msg_init()    );
+	CHECK_FCT( fd_sess_start()  );
+	CHECK_FCT( fd_p_expi_init() );
+	
+	/* Next thing is to parse the config, leave this for a different function */
+	return 0;
+}
+
+/* Parse the freeDiameter.conf configuration file, load the extensions */
+int fd_core_parseconf(char * conffile)
+{
+	TRACE_ENTRY("%p", conffile);
+	
+	/* Conf file */
+	if (conffile)
+		fd_g_config->cnf_file = conffile; /* otherwise, we use the default name */
+	
+	CHECK_FCT( fd_conf_parse() );
+	
+	/* The following module use data from the configuration */
+	CHECK_FCT( fd_rtdisp_init() );
+	
+	/* Now, load all dynamic extensions */
+	CHECK_FCT(  fd_ext_load()  );
+	
+	/* Display configuration */
+	fd_conf_dump();
+	
+	/* Display registered triggers for FDEV_TRIGGER */
+	fd_event_trig_dump();
+	
+	return 0;
+}
+
+/* For threads that would need to wait complete start of the framework (ex: in extensions) */
+int fd_core_waitstartcomplete(void)
+{
+	TRACE_ENTRY("");
+	
+	CHECK_POSIX( pthread_mutex_lock( &is_ready_mtx ) );
+	pthread_cleanup_push( fd_cleanup_mutex, &is_ready_mtx );
+	
+	while (!is_ready) {
+		CHECK_POSIX( pthread_cond_wait( &is_ready_cnd, &is_ready_mtx ) );
+	}
+	
+	pthread_cleanup_pop( 0 );
+	CHECK_POSIX( pthread_mutex_unlock( &is_ready_mtx ) );
+	
+	return 0;
+}
+
+/* Start the server & client threads */
+int fd_core_start(void)
+{
+	/* Start server threads */ 
+	CHECK_FCT( fd_servers_start() );
+	
+	/* Start the peer state machines */
+	CHECK_FCT( fd_psm_start() );
+	
+	/* Start the core runner thread that handles main events (until shutdown) */
+	CHECK_POSIX( pthread_create(&core_runner, NULL, core_runner_thread, NULL) );
+
+	/* Unlock threads waiting into fd_core_waitstartcomplete */
+	CHECK_FCT( signal_framework_ready() );
+	
+	/* Ok, everything is running now... */
+	return 0;
+}
+
+
+/* Initialize shutdown of the framework. This is not blocking. */
+int fd_core_shutdown(void)
+{
+	/* Signal the framework to terminate */
+	CHECK_FCT( fd_event_send(fd_g_config->cnf_main_ev, FDEV_TERMINATE, 0, NULL) );
+	
+	return 0;
+}
+
+
+/* Wait for the shutdown to be complete -- this must always be called after fd_core_shutdown to relaim some resources. */
+int fd_core_wait_shutdown_complete(void)
+{
+	int ret;
+	void * th_ret = NULL;
+	
+	/* Just wait for core_runner_thread to complete and return gracefully */
+	ret = pthread_join(core_runner, &th_ret);
+	if (ret != 0) {
+		fprintf(stderr, "Unable to wait for main framework thread termination: %s\n", strerror(ret));
+		return ret;
+	}
+	
+	return 0;
+}
+
+
+
+
Index: libfdcore/dict_base_proto.c
===================================================================
--- libfdcore/dict_base_proto.c	(revision 658)
+++ libfdcore/dict_base_proto.c	(revision 658)
@@ -0,0 +1,3569 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@nict.go.jp>							 *
+*													 *
+* Copyright (c) 2010, 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.								 *
+*********************************************************************************************************/
+
+/* Diameter Base protocol definitions.
+ */
+
+#include "fdcore-internal.h"
+
+#include <netinet/in.h>
+#include <sys/socket.h>
+
+/* The pointer for the global dictionary (initialized from main) */
+struct dictionary * fd_g_dict = NULL;
+
+/* The functions to encode and interpret the derived types defined in the base protocol */
+
+/* Address AVP <-> struct sockaddr_storage */
+static int Address_encode(void * data, union avp_value * avp_value)
+{
+	sSS * ss = (sSS *) data;
+	uint16_t AddressType = 0;
+	size_t	size = 0;
+	unsigned char * buf = NULL;
+	
+	TRACE_ENTRY("%p %p", data, avp_value);
+	CHECK_PARAMS( data && avp_value  );
+	
+	switch (ss->ss_family) {
+		case AF_INET:
+			{
+				/* We are encoding an IP address */
+				sSA4 * sin = (sSA4 *)ss;
+				
+				AddressType = 1;/* see http://www.iana.org/assignments/address-family-numbers/ */
+				size = 6;	/* 2 for AddressType + 4 for data */
+				
+				CHECK_MALLOC(  buf = malloc(size)  );
+				
+				/* may not work because of alignment: *(uint32_t *)(buf+2) = htonl(sin->sin_addr.s_addr); */
+				memcpy(buf + 2, &sin->sin_addr.s_addr, 4);
+			}
+			break;
+				
+		case AF_INET6:
+			{
+				/* We are encoding an IPv6 address */
+				sSA6 * sin6 = (sSA6 *)ss;
+				
+				AddressType = 2;/* see http://www.iana.org/assignments/address-family-numbers/ */
+				size = 18;	/* 2 for AddressType + 16 for data */
+				
+				CHECK_MALLOC(  buf = malloc(size)  );
+				
+				/* The order is already good here */
+				memcpy(buf + 2, &sin6->sin6_addr.s6_addr, 16);
+			}
+			break;
+				
+		default:
+			CHECK_PARAMS( AddressType = 0 );
+	}
+	
+	*(uint16_t *)buf = htons(AddressType);
+
+	avp_value->os.len = size;
+	avp_value->os.data = buf;
+	
+	return 0;
+}
+
+static int Address_interpret(union avp_value * avp_value, void * interpreted)
+{
+	uint16_t AddressType = 0;
+	unsigned char * buf;
+	
+	TRACE_ENTRY("%p %p", avp_value, interpreted);
+	
+	CHECK_PARAMS( avp_value && interpreted && (avp_value->os.len >= 2)  );
+	
+	AddressType = ntohs(*(uint16_t *)avp_value->os.data);
+	buf = &avp_value->os.data[2];
+	
+	switch (AddressType) {
+		case 1 /* IP */:
+			{
+				sSA4 * sin = (sSA4 *)interpreted;
+				
+				CHECK_PARAMS(  avp_value->os.len == 6  );
+				
+				sin->sin_family = AF_INET;
+				/* sin->sin_addr.s_addr = ntohl( * (uint32_t *) buf); -- may not work because of bad alignment */
+				memcpy(&sin->sin_addr.s_addr, buf, 4);
+			}
+			break;
+				
+		case 2 /* IP6 */:
+			{
+				sSA6 * sin6 = (sSA6 *)interpreted;
+				
+				CHECK_PARAMS(  avp_value->os.len == 18  );
+				
+				sin6->sin6_family = AF_INET6;
+				memcpy(&sin6->sin6_addr.s6_addr, buf, 16);
+			}
+			break;
+				
+		default:
+			CHECK_PARAMS( AddressType = 0 );
+	}
+	
+	return 0;
+}
+
+/* Dump the content of an Address AVP */
+static void Address_dump(union avp_value * avp_value)
+{
+	union {
+		sSA	sa;
+		sSS	ss;
+		sSA4	sin;
+		sSA6	sin6;
+	} s;
+	uint16_t fam;
+	
+	memset(&s, 0, sizeof(s));
+	
+	/* The first two octets represent the address family, http://www.iana.org/assignments/address-family-numbers/ */
+	if (avp_value->os.len < 2) {
+		fd_log_debug("[invalid length: %d]", avp_value->os.len);
+		return;
+	}
+	
+	/* Following octets are the address in network byte order already */
+	fam = avp_value->os.data[0] << 8 | avp_value->os.data[1];
+	switch (fam) {
+		case 1:
+			/* IP */
+			s.sa.sa_family = AF_INET;
+			if (avp_value->os.len != 6) {
+				fd_log_debug("[invalid IP length: %d]", avp_value->os.len);
+				return;
+			}
+			memcpy(&s.sin.sin_addr.s_addr, avp_value->os.data + 2, 4);
+			break;
+		case 2:
+			/* IP6 */
+			s.sa.sa_family = AF_INET6;
+			if (avp_value->os.len != 18) {
+				fd_log_debug("[invalid IP6 length: %d]", avp_value->os.len);
+				return;
+			}
+			memcpy(&s.sin6.sin6_addr.s6_addr, avp_value->os.data + 2, 16);
+			break;
+		default:
+			fd_log_debug("[unsupported family: 0x%hx]", fam);
+			return;
+	}
+
+	sSA_DUMP_NODE(&s.sa, NI_NUMERICHOST);
+}
+
+static void UTF8String_dump(union avp_value * avp_value)
+{
+	size_t len = avp_value->os.len;
+	if (len > 42)
+		len = 42; /* avoid very long strings */
+	fd_log_debug("%.*s", len, avp_value->os.data);
+}
+
+
+
+
+#define CHECK_dict_new( _type, _data, _parent, _ref )				\
+	CHECK_FCT(  fd_dict_new( dict, (_type), (_data), (_parent), (_ref))  );
+
+#define CHECK_dict_search( _type, _criteria, _what, _result )					\
+	CHECK_FCT(  fd_dict_search( dict, (_type), (_criteria), (_what), (_result), ENOENT)  );
+
+struct local_rules_definition {
+	char 			*avp_name;
+	enum rule_position	position;
+	int 			min;
+	int			max;
+};
+
+#define RULE_ORDER( _position ) ((((_position) == RULE_FIXED_HEAD) || ((_position) == RULE_FIXED_TAIL)) ? 1 : 0 )
+
+#define PARSE_loc_rules( _rulearray, _parent) {						\
+	int __ar;									\
+	for (__ar=0; __ar < sizeof(_rulearray) / sizeof((_rulearray)[0]); __ar++) {	\
+		struct dict_rule_data __data = { NULL, 					\
+			(_rulearray)[__ar].position,					\
+			0, 								\
+			(_rulearray)[__ar].min,						\
+			(_rulearray)[__ar].max};					\
+		__data.rule_order = RULE_ORDER(__data.rule_position);			\
+		CHECK_FCT(  fd_dict_search( 						\
+			dict,								\
+			DICT_AVP, 							\
+			AVP_BY_NAME, 							\
+			(_rulearray)[__ar].avp_name, 					\
+			&__data.rule_avp, 0 ) );					\
+		if ( !__data.rule_avp ) {						\
+			TRACE_DEBUG(INFO, "AVP Not found: '%s'", (_rulearray)[__ar].avp_name );	\
+			return ENOENT;							\
+		}									\
+		CHECK_FCT_DO( fd_dict_new( dict, DICT_RULE, &__data, _parent, NULL),	\
+			{								\
+				TRACE_DEBUG(INFO, "Error on rule with AVP '%s'",	\
+					 (_rulearray)[__ar].avp_name );			\
+				return EINVAL;						\
+			} );								\
+	}										\
+}
+
+int fd_dict_base_protocol(struct dictionary * dict)
+{
+	TRACE_ENTRY("%p", dict);
+	CHECK_PARAMS(dict);
+	
+	/* Vendors section */
+	{
+		/* The base RFC has no vendor information */
+		;
+	}
+	
+	/* Applications section */
+	{
+		/* base accounting application */
+		{
+			struct dict_application_data data = {          3, "Diameter Base Accounting" 	};
+			CHECK_dict_new( DICT_APPLICATION, &data, NULL, NULL);
+		}
+		
+		/* relay application */
+		{
+			struct dict_application_data data  = { 0xffffffff, "Relay" 				};
+			#if AI_RELAY != 0xffffffff
+			#error "AI_RELAY definition mismatch"
+			#endif
+			CHECK_dict_new( DICT_APPLICATION, &data , NULL, NULL);
+		}
+	}
+	
+	/* Derived AVP types section */
+	{
+		/* Address */
+		{
+			/*
+				The Address format is derived from the OctetString AVP Base
+				Format.  It is a discriminated union, representing, for example a
+				32-bit (IPv4) [RFC791] or 128-bit (IPv6) [RFC4291] address, most
+				significant octet first.  The first two octets of the Address AVP
+				represents the AddressType, which contains an Address Family
+				defined in [IANAADFAM].  The AddressType is used to discriminate
+				the content and format of the remaining octets.
+			*/
+			struct dict_type_data data = { AVP_TYPE_OCTETSTRING,	"Address"		, Address_interpret	, Address_encode,	Address_dump	};
+			CHECK_dict_new( DICT_TYPE, &data , NULL, NULL);
+		}
+		
+		/* Time */
+		{
+			/*
+				The Time format is derived from the OctetString AVP Base Format.
+				The string MUST contain four octets, in the same format as the
+				first four bytes are in the NTP timestamp format.  The NTP
+				Timestamp format is defined in chapter 3 of [RFC4330].
+
+				This represents the number of seconds since 0h on 1 January 1900
+				with respect to the Coordinated Universal Time (UTC).
+
+				On 6h 28m 16s UTC, 7 February 2036 the time value will overflow.
+				SNTP [RFC4330] describes a procedure to extend the time to 2104.
+				This procedure MUST be supported by all DIAMETER nodes.
+			*/
+			struct dict_type_data data = { AVP_TYPE_OCTETSTRING,	"Time"			, NULL			, NULL		, NULL		};
+			CHECK_dict_new( DICT_TYPE, &data , NULL, NULL);
+		}
+		
+		/* UTF8String */
+		{
+			/*
+				The UTF8String format is derived from the OctetString AVP Base
+				Format.  This is a human readable string represented using the
+				ISO/IEC IS 10646-1 character set, encoded as an OctetString using
+				the UTF-8 [RFC3629] transformation format described in RFC 3629.
+
+				Since additional code points are added by amendments to the 10646
+				standard from time to time, implementations MUST be prepared to
+				encounter any code point from 0x00000001 to 0x7fffffff.  Byte
+				sequences that do not correspond to the valid encoding of a code
+				point into UTF-8 charset or are outside this range are prohibited.
+
+				The use of control codes SHOULD be avoided.  When it is necessary
+				to represent a new line, the control code sequence CR LF SHOULD be
+				used.
+
+				The use of leading or trailing white space SHOULD be avoided.
+
+				For code points not directly supported by user interface hardware
+				or software, an alternative means of entry and display, such as
+				hexadecimal, MAY be provided.
+
+				For information encoded in 7-bit US-ASCII, the UTF-8 charset is
+				identical to the US-ASCII charset.
+
+				UTF-8 may require multiple bytes to represent a single character /
+				code point; thus the length of an UTF8String in octets may be
+				different from the number of characters encoded.
+
+				Note that the AVP Length field of an UTF8String is measured in
+				octets, not characters.
+			*/
+			struct dict_type_data data = { AVP_TYPE_OCTETSTRING,	"UTF8String"		, NULL			, NULL	, UTF8String_dump	};
+			CHECK_dict_new( DICT_TYPE, &data , NULL, NULL);
+		}
+		
+		/* DiameterIdentity */
+		{
+			/*
+				The DiameterIdentity format is derived from the OctetString AVP
+				Base Format.
+
+                				DiameterIdentity  = FQDN
+
+
+				DiameterIdentity value is used to uniquely identify a Diameter
+				node for purposes of duplicate connection and routing loop
+				detection.
+
+				The contents of the string MUST be the FQDN of the Diameter node.
+				If multiple Diameter nodes run on the same host, each Diameter
+				node MUST be assigned a unique DiameterIdentity.  If a Diameter
+
+				node can be identified by several FQDNs, a single FQDN should be
+				picked at startup, and used as the only DiameterIdentity for that
+				node, whatever the connection it is sent on.  Note that in this
+				document, DiameterIdentity is in ASCII form in order to be
+				compatible with existing DNS infrastructure.  See Appendix D for
+				interactions between the Diameter protocol and Internationalized
+				Domain Name (IDNs).
+			*/
+			struct dict_type_data data = { AVP_TYPE_OCTETSTRING,	"DiameterIdentity"	, NULL			, NULL		, UTF8String_dump	};
+			CHECK_dict_new( DICT_TYPE, &data , NULL, NULL);
+		}
+		
+		/* DiameterURI */
+		{
+			/*
+				The DiameterURI MUST follow the Uniform Resource Identifiers (URI)
+				syntax [RFC3986] rules specified below:
+
+				 "aaa://" FQDN [ port ] [ transport ] [ protocol ]
+
+                				 ; No transport security
+
+				 "aaas://" FQDN [ port ] [ transport ] [ protocol ]
+
+                				 ; Transport security used
+
+				 FQDN               = Fully Qualified Host Name
+
+				 port               = ":" 1*DIGIT
+
+                				 ; One of the ports used to listen for
+                				 ; incoming connections.
+                				 ; If absent,
+                				 ; the default Diameter port (3868) is
+                				 ; assumed.
+
+				 transport          = ";transport=" transport-protocol
+
+                				 ; One of the transports used to listen
+                				 ; for incoming connections.  If absent,
+                				 ; the default SCTP [RFC2960] protocol is
+                				 ; assumed. UDP MUST NOT be used when
+                				 ; the aaa-protocol field is set to
+                				 ; diameter.
+
+				transport-protocol = ( "tcp" / "sctp" / "udp" )
+
+				protocol           = ";protocol=" aaa-protocol
+
+                				 ; If absent, the default AAA protocol
+                				 ; is diameter.
+
+				aaa-protocol       = ( "diameter" / "radius" / "tacacs+" )
+
+				The following are examples of valid Diameter host identities:
+
+				aaa://host.example.com;transport=tcp
+				aaa://host.example.com:6666;transport=tcp
+				aaa://host.example.com;protocol=diameter
+				aaa://host.example.com:6666;protocol=diameter
+				aaa://host.example.com:6666;transport=tcp;protocol=diameter
+				aaa://host.example.com:1813;transport=udp;protocol=radius
+			*/
+			struct dict_type_data data = { AVP_TYPE_OCTETSTRING,	"DiameterURI"		, NULL			, NULL		, UTF8String_dump	};
+			CHECK_dict_new( DICT_TYPE, &data , NULL, NULL);
+		}
+		
+		/* Enumerated */
+		{
+			/*
+				Enumerated is derived from the Integer32 AVP Base Format.  The
+				definition contains a list of valid values and their
+				interpretation and is described in the Diameter application
+				introducing the AVP.
+			*/
+			
+			/* We don't use a generic "Enumerated" type in freeDiameter. Instead, we define
+			 * types of the form "Enumerated(<avpname>)" where <avpname> is replaced 
+			 * by the name of the AVP to which the type applies.
+			 *  Example: Enumerated(Disconnect-Cause)
+			 */
+			;
+		}
+		
+		/* IPFilterRule */
+		{
+			/*
+				The IPFilterRule format is derived from the OctetString AVP Base
+				Format and uses the ASCII charset.  The rule syntax is a modified
+				subset of ipfw(8) from FreeBSD.  Packets may be filtered based on
+				the following information that is associated with it:
+
+				    Direction                          (in or out)
+				    Source and destination IP address  (possibly masked)
+				    Protocol
+				    Source and destination port        (lists or ranges)
+				    TCP flags
+				    IP fragment flag
+				    IP options
+				    ICMP types
+
+				Rules for the appropriate direction are evaluated in order, with
+				the first matched rule terminating the evaluation.  Each packet is
+				evaluated once.  If no rule matches, the packet is dropped if the
+				last rule evaluated was a permit, and passed if the last rule was
+				a deny.
+
+				IPFilterRule filters MUST follow the format:
+				
+				    action dir proto from src to dst [options]
+				
+			(...skipped loooong explanation...)
+				
+				There is one kind of packet that the access device MUST always
+				discard, that is an IP fragment with a fragment offset of one.
+				This is a valid packet, but it only has one use, to try to
+				circumvent firewalls.
+
+				An access device that is unable to interpret or apply a deny rule
+				MUST terminate the session.  An access device that is unable to
+				interpret or apply a permit rule MAY apply a more restrictive
+				rule.  An access device MAY apply deny rules of its own before the
+				supplied rules, for example to protect the access device owner's
+				infrastructure.
+			*/
+			struct dict_type_data data = { AVP_TYPE_OCTETSTRING,	"IPFilterRule"		, NULL			, NULL		, UTF8String_dump	};
+			CHECK_dict_new( DICT_TYPE, &data , NULL, NULL);
+		}
+	}
+	
+	/* AVP section */
+	{
+		struct dict_object * Address_type;
+		struct dict_object * UTF8String_type;
+		struct dict_object * DiameterIdentity_type;
+		struct dict_object * DiameterURI_type;
+		struct dict_object * Time_type;
+		
+		CHECK_dict_search( DICT_TYPE, TYPE_BY_NAME, "Address", &Address_type);
+		CHECK_dict_search( DICT_TYPE, TYPE_BY_NAME, "UTF8String", &UTF8String_type);
+		CHECK_dict_search( DICT_TYPE, TYPE_BY_NAME, "DiameterIdentity", &DiameterIdentity_type);
+		CHECK_dict_search( DICT_TYPE, TYPE_BY_NAME, "DiameterURI", &DiameterURI_type);
+		CHECK_dict_search( DICT_TYPE, TYPE_BY_NAME, "Time", &Time_type);
+		
+		/* Vendor-Id */
+		{
+			/*
+				The Vendor-Id AVP (AVP Code 266) is of type Unsigned32 and contains
+				the IANA "SMI Network Management Private Enterprise Codes" [RFC3232]
+				value assigned to the vendor of the Diameter device.  It is
+				envisioned that the combination of the Vendor-Id, Product-Name
+				(Section 5.3.7) and the Firmware-Revision (Section 5.3.4) AVPs may
+				provide useful debugging information.
+
+				A Vendor-Id value of zero in the CER or CEA messages is reserved and
+				indicates that this field is ignored.
+			*/
+			struct dict_avp_data data = { 
+					266, 					/* Code */
+					#if AC_VENDOR_ID != 266
+					#error "AC_VENDOR_ID definition mismatch"
+					#endif
+					0, 					/* Vendor */
+					"Vendor-Id", 				/* Name */
+					AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, 	/* Fixed flags */
+					AVP_FLAG_MANDATORY, 			/* Fixed flag values */
+					AVP_TYPE_UNSIGNED32 			/* base type of data */
+					};
+			CHECK_dict_new( DICT_AVP, &data, NULL, NULL);
+		}
+		
+		/* Firmware-Revision */
+		{
+			/*
+				The Firmware-Revision AVP (AVP Code 267) is of type Unsigned32 and is
+				used to inform a Diameter peer of the firmware revision of the
+				issuing device.
+
+				For devices that do not have a firmware revision (general purpose
+				computers running Diameter software modules, for instance), the
+				revision of the Diameter software module may be reported instead.
+			*/
+			struct dict_avp_data data = { 
+					267, 					/* Code */
+					#if AC_FIRMWARE_REVISION != 267
+					#error "AC_FIRMWARE_REVISION definition mismatch"
+					#endif
+					0, 					/* Vendor */
+					"Firmware-Revision", 			/* Name */
+					AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, 	/* Fixed flags */
+					0,		 			/* Fixed flag values */
+					AVP_TYPE_UNSIGNED32 			/* base type of data */
+					};
+			CHECK_dict_new( DICT_AVP, &data , NULL, NULL);
+		}
+		
+		/* Host-IP-Address */
+		{
+			/*
+				The Host-IP-Address AVP (AVP Code 257) is of type Address and is used
+				to inform a Diameter peer of the sender's IP address.  All source
+				addresses that a Diameter node expects to use with SCTP [RFC2960]
+				MUST be advertised in the CER and CEA messages by including a
+				Host-IP- Address AVP for each address.  This AVP MUST ONLY be used in
+				the CER and CEA messages.
+			*/
+			struct dict_avp_data data = { 
+					257, 					/* Code */
+					#if AC_HOST_IP_ADDRESS != 257
+					#error "AC_HOST_IP_ADDRESS definition mismatch"
+					#endif
+					0, 					/* Vendor */
+					"Host-IP-Address", 			/* Name */
+					AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, 	/* Fixed flags */
+					AVP_FLAG_MANDATORY,			/* Fixed flag values */
+					AVP_TYPE_OCTETSTRING 			/* base type of data */
+					};
+			CHECK_dict_new( DICT_AVP, &data , Address_type, NULL);
+		}
+		
+		/* Supported-Vendor-Id */
+		{
+			/*
+				The Supported-Vendor-Id AVP (AVP Code 265) is of type Unsigned32 and
+				contains the IANA "SMI Network Management Private Enterprise Codes"
+				[RFC3232] value assigned to a vendor other than the device vendor but
+				including the application vendor.  This is used in the CER and CEA
+				messages in order to inform the peer that the sender supports (a
+				subset of) the vendor-specific AVPs defined by the vendor identified
+				in this AVP.  The value of this AVP SHOULD NOT be set to zero.
+				Multiple instances of this AVP containing the same value SHOULD NOT
+				be sent.
+			*/
+			struct dict_avp_data data = { 
+					265, 					/* Code */
+					#if AC_SUPPORTED_VENDOR_ID != 265
+					#error "AC_SUPPORTED_VENDOR_ID definition mismatch"
+					#endif
+					0, 					/* Vendor */
+					"Supported-Vendor-Id", 			/* Name */
+					AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, 	/* Fixed flags */
+					AVP_FLAG_MANDATORY,			/* Fixed flag values */
+					AVP_TYPE_UNSIGNED32 			/* base type of data */
+					};
+			CHECK_dict_new( DICT_AVP, &data , NULL, NULL);
+		}
+		
+		/* Product-Name */
+		{
+			/*
+				The Product-Name AVP (AVP Code 269) is of type UTF8String, and
+				contains the vendor assigned name for the product.  The Product-Name
+				AVP SHOULD remain constant across firmware revisions for the same
+				product.
+			*/
+			struct dict_avp_data data = { 
+					269, 					/* Code */
+					#if AC_PRODUCT_NAME != 269
+					#error "AC_PRODUCT_NAME definition mismatch"
+					#endif
+					0, 					/* Vendor */
+					"Product-Name", 			/* Name */
+					AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, 	/* Fixed flags */
+					0,					/* Fixed flag values */
+					AVP_TYPE_OCTETSTRING 			/* base type of data */
+					};
+			CHECK_dict_new( DICT_AVP, &data , UTF8String_type, NULL);
+		}
+		
+		/* Disconnect-Cause */
+		{
+			/*
+				The Disconnect-Cause AVP (AVP Code 273) is of type Enumerated.  A
+				Diameter node MUST include this AVP in the Disconnect-Peer-Request
+				message to inform the peer of the reason for its intention to
+				shutdown the transport connection.  The following values are
+				supported:
+
+				REBOOTING                         0
+				A scheduled reboot is imminent. Receiver of DPR with above result
+				code MAY attempt reconnection.
+
+				BUSY                              1
+				The peer's internal resources are constrained, and it has
+				determined that the transport connection needs to be closed.
+				Receiver of DPR with above result code SHOULD NOT attempt
+				reconnection.
+
+				DO_NOT_WANT_TO_TALK_TO_YOU        2
+				The peer has determined that it does not see a need for the
+				transport connection to exist, since it does not expect any
+				messages to be exchanged in the near future. Receiver of DPR
+				with above result code SHOULD NOT attempt reconnection.
+			*/
+			struct dict_object 	* 	type;
+			struct dict_type_data 		tdata = { AVP_TYPE_INTEGER32,	"Enumerated(Disconnect-Cause)"	, NULL, NULL, NULL };
+			struct dict_enumval_data 	t_0 = { "REBOOTING", 			{ .i32 = 0 }};
+			struct dict_enumval_data 	t_1 = { "BUSY", 			{ .i32 = 1 }};
+			struct dict_enumval_data 	t_2 = { "DO_NOT_WANT_TO_TALK_TO_YOU", 	{ .i32 = 2 }};
+			struct dict_avp_data 		data = { 
+					273, 					/* Code */
+					#if AC_DISCONNECT_CAUSE != 273
+					#error "AC_DISCONNECT_CAUSE definition mismatch"
+					#endif
+					0, 					/* Vendor */
+					"Disconnect-Cause", 			/* Name */
+					AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, 	/* Fixed flags */
+					AVP_FLAG_MANDATORY,			/* Fixed flag values */
+					AVP_TYPE_INTEGER32 			/* base type of data */
+					};
+			/* Create the Enumerated type, and then the AVP */
+			CHECK_dict_new( DICT_TYPE, &tdata , NULL, &type);
+			CHECK_dict_new( DICT_ENUMVAL, &t_0 , type, NULL);
+			CHECK_dict_new( DICT_ENUMVAL, &t_1 , type, NULL);
+			CHECK_dict_new( DICT_ENUMVAL, &t_2 , type, NULL);
+			CHECK_dict_new( DICT_AVP, &data , type, NULL);
+		}
+		
+		/* Origin-Host */
+		{
+			/*
+				The Origin-Host AVP (AVP Code 264) is of type DiameterIdentity, and
+				MUST be present in all Diameter messages.  This AVP identifies the
+				endpoint that originated the Diameter message.  Relay agents MUST NOT
+				modify this AVP.
+
+				The value of the Origin-Host AVP is guaranteed to be unique within a
+				single host.
+
+				Note that the Origin-Host AVP may resolve to more than one address as
+				the Diameter peer may support more than one address.
+
+				This AVP SHOULD be placed as close to the Diameter header as
+				possible.
+			*/
+			struct dict_avp_data data = { 
+					264, 					/* Code */
+					#if AC_ORIGIN_HOST != 264
+					#error "AC_ORIGIN_HOST definition mismatch"
+					#endif
+					0, 					/* Vendor */
+					"Origin-Host", 				/* Name */
+					AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, 	/* Fixed flags */
+					AVP_FLAG_MANDATORY,			/* Fixed flag values */
+					AVP_TYPE_OCTETSTRING 			/* base type of data */
+					};
+			CHECK_dict_new( DICT_AVP, &data , DiameterIdentity_type, NULL);
+		}
+		
+		/* Origin-Realm */
+		{
+			/*
+				The Origin-Realm AVP (AVP Code 296) is of type DiameterIdentity.
+				This AVP contains the Realm of the originator of any Diameter message
+				and MUST be present in all messages.
+
+				This AVP SHOULD be placed as close to the Diameter header as
+				possible.
+			*/
+			struct dict_avp_data data = { 
+					296, 					/* Code */
+					#if AC_ORIGIN_REALM != 296
+					#error "AC_ORIGIN_REALM definition mismatch"
+					#endif
+					0, 					/* Vendor */
+					"Origin-Realm", 			/* Name */
+					AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, 	/* Fixed flags */
+					AVP_FLAG_MANDATORY,			/* Fixed flag values */
+					AVP_TYPE_OCTETSTRING 			/* base type of data */
+					};
+			CHECK_dict_new( DICT_AVP, &data , DiameterIdentity_type, NULL);
+		}
+		
+		/* Destination-Host */
+		{
+			/*
+				The Destination-Host AVP (AVP Code 293) is of type DiameterIdentity.
+				This AVP MUST be present in all unsolicited agent initiated messages,
+				MAY be present in request messages, and MUST NOT be present in Answer
+				messages.
+
+				The absence of the Destination-Host AVP will cause a message to be
+				sent to any Diameter server supporting the application within the
+				realm specified in Destination-Realm AVP.
+
+				This AVP SHOULD be placed as close to the Diameter header as
+				possible.
+			*/
+			struct dict_avp_data data = { 
+					293, 					/* Code */
+					#if AC_DESTINATION_HOST != 293
+					#error "AC_DESTINATION_HOST definition mismatch"
+					#endif
+					0, 					/* Vendor */
+					"Destination-Host", 			/* Name */
+					AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, 	/* Fixed flags */
+					AVP_FLAG_MANDATORY,			/* Fixed flag values */
+					AVP_TYPE_OCTETSTRING 			/* base type of data */
+					};
+			CHECK_dict_new( DICT_AVP, &data , DiameterIdentity_type, NULL);
+		}
+		
+		/* Destination-Realm */
+		{
+			/*
+				The Destination-Realm AVP (AVP Code 283) is of type DiameterIdentity,
+				and contains the realm the message is to be routed to.  The
+				Destination-Realm AVP MUST NOT be present in Answer messages.
+				Diameter Clients insert the realm portion of the User-Name AVP.
+				Diameter servers initiating a request message use the value of the
+				Origin-Realm AVP from a previous message received from the intended
+				target host (unless it is known a priori).  When present, the
+				Destination-Realm AVP is used to perform message routing decisions.
+
+				Request messages whose ABNF does not list the Destination-Realm AVP
+				as a mandatory AVP are inherently non-routable messages.
+
+				This AVP SHOULD be placed as close to the Diameter header as
+				possible.
+			*/
+			struct dict_avp_data data = { 
+					283, 					/* Code */
+					#if AC_DESTINATION_REALM != 283
+					#error "AC_DESTINATION_REALM definition mismatch"
+					#endif
+					0, 					/* Vendor */
+					"Destination-Realm", 			/* Name */
+					AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, 	/* Fixed flags */
+					AVP_FLAG_MANDATORY,			/* Fixed flag values */
+					AVP_TYPE_OCTETSTRING 			/* base type of data */
+					};
+			CHECK_dict_new( DICT_AVP, &data , DiameterIdentity_type, NULL);
+		}
+		
+		/* Route-Record */
+		{
+			/*
+				The Route-Record AVP (AVP Code 282) is of type DiameterIdentity.  The
+				identity added in this AVP MUST be the same as the one received in
+				the Origin-Host of the Capabilities Exchange message.
+			*/
+			struct dict_avp_data data = { 
+					282, 					/* Code */
+					#if AC_ROUTE_RECORD != 282
+					#error "AC_ROUTE_RECORD definition mismatch"
+					#endif
+					0, 					/* Vendor */
+					"Route-Record", 			/* Name */
+					AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, 	/* Fixed flags */
+					AVP_FLAG_MANDATORY,			/* Fixed flag values */
+					AVP_TYPE_OCTETSTRING 			/* base type of data */
+					};
+			CHECK_dict_new( DICT_AVP, &data , DiameterIdentity_type, NULL);
+		}
+		
+		/* Proxy-Host */
+		{
+			/*
+				The Proxy-Host AVP (AVP Code 280) is of type DiameterIdentity.  This
+				AVP contains the identity of the host that added the Proxy-Info AVP.
+			*/
+			struct dict_avp_data adata = { 
+					280, 					/* Code */
+					#if AC_PROXY_HOST != 280
+					#error "AC_PROXY_HOST definition mismatch"
+					#endif
+					0, 					/* Vendor */
+					"Proxy-Host", 				/* Name */
+					AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, 	/* Fixed flags */
+					AVP_FLAG_MANDATORY,			/* Fixed flag values */
+					AVP_TYPE_OCTETSTRING 			/* base type of data */
+					};
+			CHECK_dict_new( DICT_AVP, &adata , DiameterIdentity_type, NULL);
+		}
+		
+		/* Proxy-State */
+		{
+			/*
+				The Proxy-State AVP (AVP Code 33) is of type OctetString, and
+				contains state local information, and MUST be treated as opaque data.
+			*/
+			struct dict_avp_data adata = { 
+					33, 					/* Code */
+					#if AC_PROXY_STATE != 33
+					#error "AC_PROXY_STATE definition mismatch"
+					#endif
+					0, 					/* Vendor */
+					"Proxy-State", 				/* Name */
+					AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, 	/* Fixed flags */
+					AVP_FLAG_MANDATORY,			/* Fixed flag values */
+					AVP_TYPE_OCTETSTRING 			/* base type of data */
+					};
+			CHECK_dict_new( DICT_AVP, &adata , NULL, NULL);
+		}
+			
+		/* Proxy-Info */
+		{
+			/*
+				The Proxy-Info AVP (AVP Code 284) is of type Grouped.  The Grouped
+				Data field has the following ABNF grammar:
+
+				 Proxy-Info ::= < AVP Header: 284 >
+                				{ Proxy-Host }
+                				{ Proxy-State }
+        				      * [ AVP ]
+			*/
+			struct dict_object * avp;
+			struct dict_avp_data data = { 
+					284, 					/* Code */
+					#if AC_PROXY_INFO != 284
+					#error "AC_PROXY_INFO definition mismatch"
+					#endif
+					0, 					/* Vendor */
+					"Proxy-Info", 				/* Name */
+					AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, 	/* Fixed flags */
+					AVP_FLAG_MANDATORY,			/* Fixed flag values */
+					AVP_TYPE_GROUPED 			/* base type of data */
+					};
+			struct local_rules_definition rules[] = 
+						{ 	 {  "Proxy-Host", 			RULE_REQUIRED, -1, 1 }
+							,{  "Proxy-State",			RULE_REQUIRED, -1, 1 }
+						};
+			
+			CHECK_dict_new( DICT_AVP, &data , NULL, &avp);
+			PARSE_loc_rules( rules, avp );
+		}
+		
+		/* Auth-Application-Id */
+		{
+			/*
+				The Auth-Application-Id AVP (AVP Code 258) is of type Unsigned32 and
+				is used in order to advertise support of the Authentication and
+				Authorization portion of an application (see Section 2.4).  If
+				present in a message other than CER and CEA, the value of the Auth-
+				Application-Id AVP MUST match the Application Id present in the
+				Diameter message header.
+			*/
+			struct dict_avp_data data = { 
+					258, 					/* Code */
+					#if AC_AUTH_APPLICATION_ID != 258
+					#error "AC_AUTH_APPLICATION_ID definition mismatch"
+					#endif
+					0, 					/* Vendor */
+					"Auth-Application-Id", 			/* Name */
+					AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, 	/* Fixed flags */
+					AVP_FLAG_MANDATORY,			/* Fixed flag values */
+					AVP_TYPE_UNSIGNED32 			/* base type of data */
+					};
+			CHECK_dict_new( DICT_AVP, &data , NULL, NULL);
+		}
+		
+		/* Acct-Application-Id */
+		{
+			/*
+				The Acct-Application-Id AVP (AVP Code 259) is of type Unsigned32 and
+				is used in order to advertise support of the Accounting portion of an
+				application (see Section 2.4).  If present in a message other than
+				CER and CEA, the value of the Acct-Application-Id AVP MUST match the
+				Application Id present in the Diameter message header.
+			*/
+			struct dict_avp_data data = { 
+					259, 					/* Code */
+					#if AC_ACCT_APPLICATION_ID != 259
+					#error "AC_ACCT_APPLICATION_ID definition mismatch"
+					#endif
+					0, 					/* Vendor */
+					"Acct-Application-Id", 			/* Name */
+					AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, 	/* Fixed flags */
+					AVP_FLAG_MANDATORY,			/* Fixed flag values */
+					AVP_TYPE_UNSIGNED32 			/* base type of data */
+					};
+			CHECK_dict_new( DICT_AVP, &data , NULL, NULL);
+		}
+		
+		/* Inband-Security-Id */
+		{
+			/*
+				The Inband-Security-Id AVP (AVP Code 299) is of type Unsigned32 and
+				is used in order to advertise support of the Security portion of the
+				application.
+
+				Currently, the following values are supported, but there is ample
+				room to add new security Ids.
+
+
+				NO_INBAND_SECURITY 0
+
+				This peer does not support TLS.  This is the default value, if the
+				AVP is omitted.
+
+				TLS 1
+
+				This node supports TLS security, as defined by [RFC4346].
+			*/
+			
+			/* Although the RFC does not specify an "Enumerated" type here, we go forward and create one.
+			 * This is the reason for the "*" in the type name
+			 */
+			
+			struct dict_object 	* 	type;
+			struct dict_type_data	 	tdata = { AVP_TYPE_UNSIGNED32,	"Enumerated*(Inband-Security-Id)"	, NULL, NULL, NULL };
+			struct dict_enumval_data 	t_0 = { "NO_INBAND_SECURITY", 		{ .u32 = ACV_ISI_NO_INBAND_SECURITY }};
+			struct dict_enumval_data 	t_1 = { "TLS", 			{ .u32 = ACV_ISI_TLS }};
+			struct dict_avp_data 		data = { 
+					299, 					/* Code */
+					#if AC_INBAND_SECURITY_ID != 299
+					#error "AC_INBAND_SECURITY_ID definition mismatch"
+					#endif
+					0, 					/* Vendor */
+					"Inband-Security-Id", 			/* Name */
+					AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, 	/* Fixed flags */
+					AVP_FLAG_MANDATORY,			/* Fixed flag values */
+					AVP_TYPE_UNSIGNED32 			/* base type of data */
+					};
+			/* Create the Enumerated type, and then the AVP */
+			CHECK_dict_new( DICT_TYPE, &tdata , NULL, &type);
+			CHECK_dict_new( DICT_ENUMVAL, &t_0 , type, NULL);
+			CHECK_dict_new( DICT_ENUMVAL, &t_1 , type, NULL);
+			CHECK_dict_new( DICT_AVP, &data , type, NULL);
+		}
+		
+		/* Vendor-Specific-Application-Id */
+		{
+			/*
+				The Vendor-Specific-Application-Id AVP (AVP Code 260) is of type
+				Grouped and is used to advertise support of a vendor-specific
+				Diameter Application.  Exactly one instance of either Auth-
+				Application-Id or Acct-Application-Id AVP MUST be present.  The
+				Application Id carried by either Auth-Application-Id or Acct-
+				Application-Id AVP MUST comply with vendor specific Application Id
+				assignment described in Sec 11.3.  It MUST also match the Application
+				Id present in the diameter header except when used in a CER or CEA
+				messages.
+
+				The Vendor-Id AVP is an informational AVP pertaining to the vendor
+				who may have authorship of the vendor-specific Diameter application.
+				It MUST NOT be used as a means of defining a completely separate
+				vendor-specific Application Id space.
+
+				This AVP MUST also be present as the first AVP in all experimental
+				commands defined in the vendor-specific application.
+
+				This AVP SHOULD be placed as close to the Diameter header as
+				possible.
+
+				AVP Format
+
+				<Vendor-Specific-Application-Id> ::= < AVP Header: 260 >
+                                				   { Vendor-Id }
+                                				   [ Auth-Application-Id ]
+                                				   [ Acct-Application-Id ]
+
+				A Vendor-Specific-Application-Id AVP MUST contain exactly one of
+				either Auth-Application-Id or Acct-Application-Id.  If a Vendor-
+				Specific-Application-Id is received without any of these two AVPs,
+				then the recipient SHOULD issue an answer with a Result-Code set to
+				DIAMETER_MISSING_AVP.  The answer SHOULD also include a Failed-AVP
+				which MUST contain an example of an Auth-Application-Id AVP and an
+				Acct-Application-Id AVP.
+
+				If a Vendor-Specific-Application-Id is received that contains both
+				Auth-Application-Id and Acct-Application-Id, then the recipient
+				SHOULD issue an answer with Result-Code set to
+				DIAMETER_AVP_OCCURS_TOO_MANY_TIMES.  The answer SHOULD also include a
+				Failed-AVP which MUST contain the received Auth-Application-Id AVP
+				and Acct-Application-Id AVP.
+			*/
+			struct dict_object 	* avp;
+			struct dict_avp_data	  data = { 
+					260, 					/* Code */
+					#if AC_VENDOR_SPECIFIC_APPLICATION_ID != 260
+					#error "AC_VENDOR_SPECIFIC_APPLICATION_ID definition mismatch"
+					#endif
+					0, 					/* Vendor */
+					"Vendor-Specific-Application-Id", 	/* Name */
+					AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, 	/* Fixed flags */
+					AVP_FLAG_MANDATORY,			/* Fixed flag values */
+					AVP_TYPE_GROUPED 			/* base type of data */
+					};
+					
+			struct local_rules_definition rules[] = 
+						{ 	 {  "Vendor-Id", 			RULE_REQUIRED, -1, 1 }
+							,{  "Auth-Application-Id",		RULE_OPTIONAL, -1, 1 }
+							,{  "Acct-Application-Id",		RULE_OPTIONAL, -1, 1 }
+						};
+			
+			/* Create the grouped AVP */
+			CHECK_dict_new( DICT_AVP, &data , NULL, &avp);
+			PARSE_loc_rules( rules, avp );
+			
+		}
+		
+		/* Redirect-Host */
+		{
+			/*
+				One or more of instances of this AVP MUST be present if the answer
+				message's 'E' bit is set and the Result-Code AVP is set to
+				DIAMETER_REDIRECT_INDICATION.
+
+				Upon receiving the above, the receiving Diameter node SHOULD forward
+				the request directly to one of the hosts identified in these AVPs.
+				The server contained in the selected Redirect-Host AVP SHOULD be used
+				for all messages pertaining to this session.
+			*/
+			struct dict_avp_data data = { 
+					292, 					/* Code */
+					#if AC_REDIRECT_HOST != 292
+					#error "AC_REDIRECT_HOST definition mismatch"
+					#endif
+					0, 					/* Vendor */
+					"Redirect-Host", 			/* Name */
+					AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, 	/* Fixed flags */
+					AVP_FLAG_MANDATORY,			/* Fixed flag values */
+					AVP_TYPE_OCTETSTRING 			/* base type of data */
+					};
+			CHECK_dict_new( DICT_AVP, &data , DiameterURI_type, NULL);
+		}
+		
+		/* Redirect-Host-Usage */
+		{
+			/*
+				The Redirect-Host-Usage AVP (AVP Code 261) is of type Enumerated.
+				This AVP MAY be present in answer messages whose 'E' bit is set and
+				the Result-Code AVP is set to DIAMETER_REDIRECT_INDICATION.
+
+				When present, this AVP dictates how the routing entry resulting from
+				the Redirect-Host is to be used.  The following values are supported:
+
+
+				DONT_CACHE 0
+
+				The host specified in the Redirect-Host AVP should not be cached.
+				This is the default value.
+
+
+				ALL_SESSION 1
+
+				All messages within the same session, as defined by the same value
+				of the Session-ID AVP MAY be sent to the host specified in the
+				Redirect-Host AVP.
+
+
+				ALL_REALM 2
+
+				All messages destined for the realm requested MAY be sent to the
+				host specified in the Redirect-Host AVP.
+
+
+				REALM_AND_APPLICATION 3
+
+				All messages for the application requested to the realm specified
+				MAY be sent to the host specified in the Redirect-Host AVP.
+
+				ALL_APPLICATION 4
+
+				All messages for the application requested MAY be sent to the host
+				specified in the Redirect-Host AVP.
+
+
+				ALL_HOST 5
+
+				All messages that would be sent to the host that generated the
+				Redirect-Host MAY be sent to the host specified in the Redirect-
+				Host AVP.
+
+
+				ALL_USER 6
+
+				All messages for the user requested MAY be sent to the host
+				specified in the Redirect-Host AVP.
+
+
+				When multiple cached routes are created by redirect indications and
+				they differ only in redirect usage and peers to forward requests to
+				(see Section 6.1.8), a precedence rule MUST be applied to the
+				redirect usage values of the cached routes during normal routing to
+				resolve contentions that may occur.  The precedence rule is the order
+				that dictate which redirect usage should be considered before any
+				other as they appear.  The order is as follows:
+
+
+				1.  ALL_SESSION
+
+				2.  ALL_USER
+
+				3.  REALM_AND_APPLICATION
+
+				4.  ALL_REALM
+
+				5.  ALL_APPLICATION
+
+				6.  ALL_HOST
+			*/
+			struct dict_object 	* 	type;
+			struct dict_type_data	 	tdata = { AVP_TYPE_INTEGER32,	"Enumerated(Redirect-Host-Usage)"	, NULL, NULL, NULL };
+			struct dict_enumval_data 	t_0 = { "DONT_CACHE", 			{ .i32 = 0 }};
+			struct dict_enumval_data 	t_1 = { "ALL_SESSION", 			{ .i32 = 1 }};
+			struct dict_enumval_data 	t_2 = { "ALL_REALM", 			{ .i32 = 2 }};
+			struct dict_enumval_data 	t_3 = { "REALM_AND_APPLICATION", 	{ .i32 = 3 }};
+			struct dict_enumval_data 	t_4 = { "ALL_APPLICATION", 		{ .i32 = 4 }};
+			struct dict_enumval_data 	t_5 = { "ALL_HOST", 			{ .i32 = 5 }};
+			struct dict_enumval_data 	t_6 = { "ALL_USER", 			{ .i32 = 6 }};
+			struct dict_avp_data 		data = { 
+					261, 					/* Code */
+					#if AC_REDIRECT_HOST_USAGE != 261
+					#error "AC_REDIRECT_HOST_USAGE definition mismatch"
+					#endif
+					0, 					/* Vendor */
+					"Redirect-Host-Usage", 			/* Name */
+					AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, 	/* Fixed flags */
+					AVP_FLAG_MANDATORY,			/* Fixed flag values */
+					AVP_TYPE_INTEGER32 			/* base type of data */
+					};
+			/* Create the Enumerated type, and then the AVP */
+			CHECK_dict_new( DICT_TYPE, &tdata , NULL, &type);
+			CHECK_dict_new( DICT_ENUMVAL, &t_0 , type, NULL);
+			CHECK_dict_new( DICT_ENUMVAL, &t_1 , type, NULL);
+			CHECK_dict_new( DICT_ENUMVAL, &t_2 , type, NULL);
+			CHECK_dict_new( DICT_ENUMVAL, &t_3 , type, NULL);
+			CHECK_dict_new( DICT_ENUMVAL, &t_4 , type, NULL);
+			CHECK_dict_new( DICT_ENUMVAL, &t_5 , type, NULL);
+			CHECK_dict_new( DICT_ENUMVAL, &t_6 , type, NULL);
+			CHECK_dict_new( DICT_AVP, &data , type, NULL);
+		}
+		
+		/* Redirect-Max-Cache-Time */
+		{
+			/*
+				The Redirect-Max-Cache-Time AVP (AVP Code 262) is of type Unsigned32.
+				This AVP MUST be present in answer messages whose 'E' bit is set, the
+				Result-Code AVP is set to DIAMETER_REDIRECT_INDICATION and the
+				Redirect-Host-Usage AVP set to a non-zero value.
+
+				This AVP contains the maximum number of seconds the peer and route
+				table entries, created as a result of the Redirect-Host, will be
+				cached.  Note that once a host created due to a redirect indication
+				is no longer reachable, any associated peer and routing table entries
+				MUST be deleted.
+			*/
+			struct dict_avp_data data = { 
+					262, 					/* Code */
+					#if AC_REDIRECT_MAX_CACHE_TIME != 262
+					#error "AC_REDIRECT_MAX_CACHE_TIME definition mismatch"
+					#endif
+					0, 					/* Vendor */
+					"Redirect-Max-Cache-Time", 		/* Name */
+					AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, 	/* Fixed flags */
+					AVP_FLAG_MANDATORY,			/* Fixed flag values */
+					AVP_TYPE_UNSIGNED32 			/* base type of data */
+					};
+			CHECK_dict_new( DICT_AVP, &data , NULL, NULL);
+		}
+		
+		/* Result-Code */
+		{
+			/*
+				The Result-Code AVP (AVP Code 268) is of type Unsigned32 and
+				indicates whether a particular request was completed successfully or
+				whether an error occurred.  All Diameter answer messages defined in
+				IETF applications MUST include one Result-Code AVP.  A non-successful
+				Result-Code AVP (one containing a non 2xxx value other than
+				DIAMETER_REDIRECT_INDICATION) MUST include the Error-Reporting-Host
+				AVP if the host setting the Result-Code AVP is different from the
+				identity encoded in the Origin-Host AVP.
+
+				The Result-Code data field contains an IANA-managed 32-bit address
+				space representing errors (see Section 11.4).  Diameter provides the
+				following classes of errors, all identified by the thousands digit in
+				the decimal notation:
+
+				o  1xxx (Informational)
+
+				o  2xxx (Success)
+
+				o  3xxx (Protocol Errors)
+
+				o  4xxx (Transient Failures)
+
+				o  5xxx (Permanent Failure)
+
+				A non-recognized class (one whose first digit is not defined in this
+				section) MUST be handled as a permanent failure.
+			*/
+			
+			/* Although the RFC does not specify an "Enumerated" type here, we go forward and create one.
+			 * This is the reason for the "*" in the type name
+			 */
+			struct dict_object * 	type;
+			struct dict_type_data 	tdata = { AVP_TYPE_UNSIGNED32,	"Enumerated*(Result-Code)"	, NULL, NULL, NULL };
+			struct dict_avp_data 	data = { 
+					268, 					/* Code */
+					#if AC_RESULT_CODE != 268
+					#error "AC_RESULT_CODE definition mismatch"
+					#endif
+					0, 					/* Vendor */
+					"Result-Code", 				/* Name */
+					AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, 	/* Fixed flags */
+					AVP_FLAG_MANDATORY,			/* Fixed flag values */
+					AVP_TYPE_UNSIGNED32 			/* base type of data */
+					};
+			/* Create the Enumerated type, and then the AVP */
+			CHECK_dict_new( DICT_TYPE, &tdata , NULL, &type);
+			CHECK_dict_new( DICT_AVP, &data , type, NULL);
+			
+			/* Informational */
+			{
+				/* 1001 */
+				{
+					/*
+						This informational error is returned by a Diameter server to
+						inform the access device that the authentication mechanism being
+						used requires multiple round trips, and a subsequent request needs
+						to be issued in order for access to be granted.
+					*/
+					struct dict_enumval_data 	error_code = { "DIAMETER_MULTI_ROUND_AUTH", 	{ .u32 = 1001 }};
+					CHECK_dict_new( DICT_ENUMVAL, &error_code , type, NULL);
+				}
+			}
+			/* Success */
+			{
+				/* 2001 */
+				{
+					/*
+						The Request was successfully completed.
+					*/
+					struct dict_enumval_data 	error_code = { "DIAMETER_SUCCESS", 		{ .u32 = 2001 }};
+					#if ER_DIAMETER_SUCCESS != 2001
+					#error "ER_DIAMETER_SUCCESS definition mismatch"
+					#endif
+					CHECK_dict_new( DICT_ENUMVAL, &error_code , type, NULL);
+				}
+				/* 2002 */
+				{
+					/*
+						When returned, the request was successfully completed, but
+						additional processing is required by the application in order to
+						provide service to the user.
+					*/
+					struct dict_enumval_data 	error_code = { "DIAMETER_LIMITED_SUCCESS", 	{ .u32 = 2002 }};
+					CHECK_dict_new( DICT_ENUMVAL, &error_code , type, NULL);
+				}
+			}
+			/* Protocol Errors */
+			{
+				/* 3001 */
+				{
+					/*
+						The Request contained a Command-Code that the receiver did not
+						recognize or support.  This MUST be used when a Diameter node
+						receives an experimental command that it does not understand.
+					*/
+					/* (old): it has been changed to 5019 in 3588bis */
+					struct dict_enumval_data 	error_code = { "DIAMETER_COMMAND_UNSUPPORTED(old)", 	{ .u32 = 3001 }};
+					CHECK_dict_new( DICT_ENUMVAL, &error_code , type, NULL);
+				}
+				/* 3002 */
+				{
+					/*
+						This error is given when Diameter can not deliver the message to
+						the destination, either because no host within the realm
+						supporting the required application was available to process the
+						request, or because Destination-Host AVP was given without the
+						associated Destination-Realm AVP.
+					*/
+					struct dict_enumval_data 	error_code = { "DIAMETER_UNABLE_TO_DELIVER", 	{ .u32 = 3002 }};
+					CHECK_dict_new( DICT_ENUMVAL, &error_code , type, NULL);
+				}
+				/* 3003 */
+				{
+					/*
+						The intended realm of the request is not recognized.
+					*/
+					struct dict_enumval_data 	error_code = { "DIAMETER_REALM_NOT_SERVED", 	{ .u32 = 3003 }};
+					#if ER_DIAMETER_REALM_NOT_SERVED != 3003
+					#error "ER_DIAMETER_REALM_NOT_SERVED definition mismatch"
+					#endif
+					CHECK_dict_new( DICT_ENUMVAL, &error_code , type, NULL);
+				}
+				/* 3004 */
+				{
+					/*
+						When returned, a Diameter node SHOULD attempt to send the message
+						to an alternate peer.  This error MUST only be used when a
+						specific server is requested, and it cannot provide the requested
+						service.
+					*/
+					struct dict_enumval_data 	error_code = { "DIAMETER_TOO_BUSY", 		{ .u32 = 3004 }};
+					#if ER_DIAMETER_TOO_BUSY != 3004
+					#error "ER_DIAMETER_TOO_BUSY definition mismatch"
+					#endif
+					CHECK_dict_new( DICT_ENUMVAL, &error_code , type, NULL);
+				}
+				/* 3005 */
+				{
+					/*
+						An agent detected a loop while trying to get the message to the
+						intended recipient.  The message MAY be sent to an alternate peer,
+						if one is available, but the peer reporting the error has
+						identified a configuration problem.
+					*/
+					struct dict_enumval_data 	error_code = { "DIAMETER_LOOP_DETECTED", 	{ .u32 = 3005 }};
+					CHECK_dict_new( DICT_ENUMVAL, &error_code , type, NULL);
+				}
+				/* 3006 */
+				{
+					/*
+						A redirect agent has determined that the request could not be
+						satisfied locally and the initiator of the request should direct
+						the request directly to the server, whose contact information has
+						been added to the response.  When set, the Redirect-Host AVP MUST
+						be present.
+					*/
+					struct dict_enumval_data 	error_code = { "DIAMETER_REDIRECT_INDICATION", 	{ .u32 = 3006 }};
+					#if ER_DIAMETER_REDIRECT_INDICATION != 3006
+					#error "ER_DIAMETER_REDIRECT_INDICATION definition mismatch"
+					#endif
+					CHECK_dict_new( DICT_ENUMVAL, &error_code , type, NULL);
+				}
+				/* 3007 */
+				{
+					/*
+						A request was sent for an application that is not supported.
+					*/
+					struct dict_enumval_data 	error_code = { "DIAMETER_APPLICATION_UNSUPPORTED",	{ .u32 = 3007 }};
+					CHECK_dict_new( DICT_ENUMVAL, &error_code , type, NULL);
+				}
+				/* 3008 */
+				{
+					/*
+						A request was received whose bits in the Diameter header were
+						either set to an invalid combination, or to a value that is
+						inconsistent with the command code's definition.
+					*/
+					/* (old): it has been changed in 3588bis */
+					struct dict_enumval_data 	error_code = { "DIAMETER_INVALID_HDR_BITS(old)", 	{ .u32 = 3008 }};
+					CHECK_dict_new( DICT_ENUMVAL, &error_code , type, NULL);
+				}
+				/* 3009 */
+				{
+					/*
+						A request was received that included an AVP whose flag bits are
+						set to an unrecognized value, or that is inconsistent with the
+						AVP's definition.
+					*/
+					/* (old): it has been changed in 3588bis */
+					struct dict_enumval_data 	error_code = { "DIAMETER_INVALID_AVP_BITS(old)", 	{ .u32 = 3009 }};
+					CHECK_dict_new( DICT_ENUMVAL, &error_code , type, NULL);
+				}
+				/* 3010 */
+				{
+					/*
+						 A CER was received from an unknown peer.
+					*/
+					/* (old): it has been changed in 3588bis */
+					struct dict_enumval_data 	error_code = { "DIAMETER_UNKNOWN_PEER(old)", 	{ .u32 = 3010 }};
+					CHECK_dict_new( DICT_ENUMVAL, &error_code , type, NULL);
+				}
+				/* 3011 */
+				{
+					/*
+						This error is returned when a reserved bit in the Diameter header
+						is set to one (1) or the bits in the Diameter header defined in
+						Sec 3 are set incorrectly.
+					*/
+					struct dict_enumval_data 	error_code = { "DIAMETER_INVALID_BIT_IN_HEADER",	{ .u32 = 3011 }};
+					CHECK_dict_new( DICT_ENUMVAL, &error_code , type, NULL);
+				}
+				/* 3012 */
+				{
+					/*
+						This error is returned when a request is received with an invalid
+						message length.
+					*/
+					struct dict_enumval_data 	error_code = { "DIAMETER_INVALID_MESSAGE_LENGTH",	{ .u32 = 3012 }};
+					CHECK_dict_new( DICT_ENUMVAL, &error_code , type, NULL);
+				}
+			}
+			/* Transient Failures */
+			{
+				/* 4001 */
+				{
+					/*
+						The authentication process for the user failed, most likely due to
+						an invalid password used by the user.  Further attempts MUST only
+						be tried after prompting the user for a new password.
+					*/
+					struct dict_enumval_data 	error_code = { "DIAMETER_AUTHENTICATION_REJECTED", 	{ .u32 = 4001 }};
+					CHECK_dict_new( DICT_ENUMVAL, &error_code , type, NULL);
+				}
+				/* 4002 */
+				{
+					/*
+						A Diameter node received the accounting request but was unable to
+						commit it to stable storage due to a temporary lack of space.
+					*/
+					struct dict_enumval_data 	error_code = { "DIAMETER_OUT_OF_SPACE", 		{ .u32 = 4002 }};
+					CHECK_dict_new( DICT_ENUMVAL, &error_code , type, NULL);
+				}
+				/* 4003 */
+				{
+					/*
+						The peer has determined that it has lost the election process and
+						has therefore disconnected the transport connection.
+					*/
+					struct dict_enumval_data 	error_code = { "ELECTION_LOST", 			{ .u32 = 4003 }};
+					CHECK_dict_new( DICT_ENUMVAL, &error_code , type, NULL);
+				}
+			}
+			/* Permanent Failures */
+			{
+				/* 5001 */
+				{
+					/*
+						The peer received a message that contained an AVP that is not
+						recognized or supported and was marked with the Mandatory bit.  A
+						Diameter message with this error MUST contain one or more Failed-
+						AVP AVP containing the AVPs that caused the failure.
+					*/
+					struct dict_enumval_data 	error_code = { "DIAMETER_AVP_UNSUPPORTED", 	{ .u32 = 5001 }};
+					CHECK_dict_new( DICT_ENUMVAL, &error_code , type, NULL);
+				}
+				/* 5002 */
+				{
+					/*
+						The request contained an unknown Session-Id.
+					*/
+					struct dict_enumval_data 	error_code = { "DIAMETER_UNKNOWN_SESSION_ID", 	{ .u32 = 5002 }};
+					CHECK_dict_new( DICT_ENUMVAL, &error_code , type, NULL);
+				}
+				/* 5003 */
+				{
+					/*
+						A request was received for which the user could not be authorized.
+						This error could occur if the service requested is not permitted
+						to the user.
+					*/
+					struct dict_enumval_data 	error_code = { "DIAMETER_AUTHORIZATION_REJECTED",{ .u32 = 5003 }};
+					CHECK_dict_new( DICT_ENUMVAL, &error_code , type, NULL);
+				}
+				/* 5004 */
+				{
+					/*
+						The request contained an AVP with an invalid value in its data
+						portion.  A Diameter message indicating this error MUST include
+						the offending AVPs within a Failed-AVP AVP.
+					*/
+					struct dict_enumval_data 	error_code = { "DIAMETER_INVALID_AVP_VALUE",	{ .u32 = 5004 }};
+					CHECK_dict_new( DICT_ENUMVAL, &error_code , type, NULL);
+				}
+				/* 5005 */
+				{
+					/*
+						The request did not contain an AVP that is required by the Command
+						Code definition.  If this value is sent in the Result-Code AVP, a
+						Failed-AVP AVP SHOULD be included in the message.  The Failed-AVP
+						AVP MUST contain an example of the missing AVP complete with the
+						Vendor-Id if applicable.  The value field of the missing AVP
+						should be of correct minimum length and contain zeroes.
+					*/
+					struct dict_enumval_data 	error_code = { "DIAMETER_MISSING_AVP",		{ .u32 = 5005 }};
+					CHECK_dict_new( DICT_ENUMVAL, &error_code , type, NULL);
+				}
+				/* 5006 */
+				{
+					/*
+						A request was received that cannot be authorized because the user
+						has already expended allowed resources.  An example of this error
+						condition is a user that is restricted to one dial-up PPP port,
+						attempts to establish a second PPP connection.
+					*/
+					struct dict_enumval_data 	error_code = { "DIAMETER_RESOURCES_EXCEEDED",	{ .u32 = 5006 }};
+					CHECK_dict_new( DICT_ENUMVAL, &error_code , type, NULL);
+				}
+				/* 5007 */
+				{
+					/*
+						The Home Diameter server has detected AVPs in the request that
+						contradicted each other, and is not willing to provide service to
+						the user.  The Failed-AVP AVPs MUST be present which contains the
+						AVPs that contradicted each other.
+					*/
+					struct dict_enumval_data 	error_code = { "DIAMETER_CONTRADICTING_AVPS",	{ .u32 = 5007 }};
+					CHECK_dict_new( DICT_ENUMVAL, &error_code , type, NULL);
+				}
+				/* 5008 */
+				{
+					/*
+						A message was received with an AVP that MUST NOT be present.  The
+						Failed-AVP AVP MUST be included and contain a copy of the
+						offending AVP.
+					*/
+					struct dict_enumval_data 	error_code = { "DIAMETER_AVP_NOT_ALLOWED",	{ .u32 = 5008 }};
+					CHECK_dict_new( DICT_ENUMVAL, &error_code , type, NULL);
+				}
+				/* 5009 */
+				{
+					/*
+						A message was received that included an AVP that appeared more
+						often than permitted in the message definition.  The Failed-AVP
+						AVP MUST be included and contain a copy of the first instance of
+						the offending AVP that exceeded the maximum number of occurrences
+					*/
+					struct dict_enumval_data 	error_code = { "DIAMETER_AVP_OCCURS_TOO_MANY_TIMES",{ .u32 = 5009 }};
+					CHECK_dict_new( DICT_ENUMVAL, &error_code , type, NULL);
+				}
+				/* 5010 */
+				{
+					/*
+						This error is returned by a Diameter node that is not acting as a
+						relay when it receives a CER which advertises a set of
+						applications that it does not support.
+					*/
+					struct dict_enumval_data 	error_code = { "DIAMETER_NO_COMMON_APPLICATION",{ .u32 = 5010 }};
+					CHECK_dict_new( DICT_ENUMVAL, &error_code , type, NULL);
+				}
+				/* 5011 */
+				{
+					/*
+						This error is returned when a request was received, whose version
+						number is unsupported.
+					*/
+					struct dict_enumval_data 	error_code = { "DIAMETER_UNSUPPORTED_VERSION",	{ .u32 = 5011 }};
+					CHECK_dict_new( DICT_ENUMVAL, &error_code , type, NULL);
+				}
+				/* 5012 */
+				{
+					/*
+						This error is returned when a request is rejected for unspecified
+						reasons.
+					*/
+					struct dict_enumval_data 	error_code = { "DIAMETER_UNABLE_TO_COMPLY",	{ .u32 = 5012 }};
+					CHECK_dict_new( DICT_ENUMVAL, &error_code , type, NULL);
+				}
+				/* 5013 */
+				{
+					/*
+						This error is returned when an unrecognized bit in the Diameter
+						header is set to one (1).
+					*/
+					/* (old): it has been changed in 3588bis */
+					struct dict_enumval_data 	error_code = { "DIAMETER_INVALID_BIT_IN_HEADER(old)", 	{ .u32 = 5013 }};
+					CHECK_dict_new( DICT_ENUMVAL, &error_code , type, NULL);
+				}
+				/* 5014 */
+				{
+					/*
+						The request contained an AVP with an invalid length.  A Diameter
+						message indicating this error MUST include the offending AVPs
+						within a Failed-AVP AVP.  In cases where the erroneous avp length
+						value exceeds the message length or is less than the minimum AVP
+						header length, it is sufficient to include the offending AVP
+						header and a zero filled payload of the minimum required length
+						for the payloads data type.  If the AVP is a grouped AVP, the
+						grouped AVP header with an empty payload would be sufficient to
+						indicate the offending AVP.  In the case where the offending AVP
+						header cannot be fully decoded when avp length is less than the
+						minimum AVP header length, it is sufficient to include an
+						offending AVP header that is formulated by padding the incomplete
+						AVP header with zero up to the minimum AVP header length.
+					*/
+					struct dict_enumval_data 	error_code = { "DIAMETER_INVALID_AVP_LENGTH",	{ .u32 = 5014 }};
+					CHECK_dict_new( DICT_ENUMVAL, &error_code , type, NULL);
+				}
+				/* 5015 */
+				{
+					/*
+						This error is returned when a request is received with an invalid
+						message length.
+					*/
+					/* (old): it has been changed in 3588bis */
+					struct dict_enumval_data 	error_code = { "DIAMETER_INVALID_MESSAGE_LENGTH(old)", 	{ .u32 = 5015 }};
+					CHECK_dict_new( DICT_ENUMVAL, &error_code , type, NULL);
+				}
+				/* 5016 */
+				{
+					/*
+						The request contained an AVP with which is not allowed to have the
+						given value in the AVP Flags field.  A Diameter message indicating
+						this error MUST include the offending AVPs within a Failed-AVP
+						AVP.
+					*/
+					/* (old): it has been changed in 3588bis */
+					struct dict_enumval_data 	error_code = { "DIAMETER_INVALID_AVP_BIT_COMBO(old)", 	{ .u32 = 5016 }};
+					CHECK_dict_new( DICT_ENUMVAL, &error_code , type, NULL);
+				}
+				/* 5017 */
+				{
+					/*
+						This error is returned when a CER message is received, and there
+						are no common security mechanisms supported between the peers.  A
+						Capabilities-Exchange-Answer (CEA) MUST be returned with the
+						Result-Code AVP set to DIAMETER_NO_COMMON_SECURITY.
+					*/
+					struct dict_enumval_data 	error_code = { "DIAMETER_NO_COMMON_SECURITY",	{ .u32 = 5017 }};
+					CHECK_dict_new( DICT_ENUMVAL, &error_code , type, NULL);
+				}
+				/* 5018 */
+				{
+					/*
+						A CER was received from an unknown peer.
+					*/
+					struct dict_enumval_data 	error_code = { "DIAMETER_UNKNOWN_PEER",		{ .u32 = 5018 }};
+					CHECK_dict_new( DICT_ENUMVAL, &error_code , type, NULL);
+				}
+				/* 5019 */
+				{
+					/*
+						The Request contained a Command-Code that the receiver did not
+						recognize or support.  This MUST be used when a Diameter node
+						receives an experimental command that it does not understand.
+					*/
+					struct dict_enumval_data 	error_code = { "DIAMETER_COMMAND_UNSUPPORTED",	{ .u32 = 5019 }};
+					CHECK_dict_new( DICT_ENUMVAL, &error_code , type, NULL);
+				}
+				/* 5020 */
+				{
+					/*
+						A request was received whose bits in the Diameter header were
+						either set to an invalid combination, or to a value that is
+						inconsistent with the command code's definition.
+					*/
+					struct dict_enumval_data 	error_code = { "DIAMETER_INVALID_HDR_BITS",	{ .u32 = 5020 }};
+					CHECK_dict_new( DICT_ENUMVAL, &error_code , type, NULL);
+				}
+				/* 5021 */
+				{
+					/*
+						A request was received that included an AVP whose flag bits are
+						set to an unrecognized value, or that is inconsistent with the
+						AVP's definition.
+					*/
+					struct dict_enumval_data 	error_code = { "DIAMETER_INVALID_AVP_BITS",	{ .u32 = 5021 }};
+					CHECK_dict_new( DICT_ENUMVAL, &error_code , type, NULL);
+				}
+			}
+		}
+		
+		/* Error-Message */
+		{
+			/*
+				The Error-Message AVP (AVP Code 281) is of type UTF8String.  It MAY
+				accompany a Result-Code AVP as a human readable error message.  The
+				Error-Message AVP is not intended to be useful in real-time, and
+				SHOULD NOT be expected to be parsed by network entities.
+			*/
+			struct dict_avp_data data = { 
+					281, 					/* Code */
+					#if AC_ERROR_MESSAGE != 281
+					#error "AC_ERROR_MESSAGE definition mismatch"
+					#endif
+					0, 					/* Vendor */
+					"Error-Message", 			/* Name */
+					AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, 	/* Fixed flags */
+					0,					/* Fixed flag values */
+					AVP_TYPE_OCTETSTRING 			/* base type of data */
+					};
+			CHECK_dict_new( DICT_AVP, &data , UTF8String_type, NULL);
+		}
+		
+		/* Error-Reporting-Host */
+		{
+			/*
+				The Error-Reporting-Host AVP (AVP Code 294) is of type
+				DiameterIdentity.  This AVP contains the identity of the Diameter
+				host that sent the Result-Code AVP to a value other than 2001
+				(Success), only if the host setting the Result-Code is different from
+				the one encoded in the Origin-Host AVP.  This AVP is intended to be
+				used for troubleshooting purposes, and MUST be set when the Result-
+				Code AVP indicates a failure.
+			*/
+			struct dict_avp_data data = { 
+					294, 					/* Code */
+					#if AC_ERROR_REPORTING_HOST != 294
+					#error "AC_ERROR_REPORTING_HOST definition mismatch"
+					#endif
+					0, 					/* Vendor */
+					"Error-Reporting-Host", 		/* Name */
+					AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, 	/* Fixed flags */
+					0,					/* Fixed flag values */
+					AVP_TYPE_OCTETSTRING 			/* base type of data */
+					};
+			CHECK_dict_new( DICT_AVP, &data , DiameterIdentity_type, NULL);
+		}
+		
+		/* Failed-AVP */
+		{
+			/*
+				The Failed-AVP AVP (AVP Code 279) is of type Grouped and provides
+				debugging information in cases where a request is rejected or not
+				fully processed due to erroneous information in a specific AVP.  The
+				value of the Result-Code AVP will provide information on the reason
+				for the Failed-AVP AVP.  A Diameter message SHOULD contain only one
+				Failed-AVP that corresponds to the error indicated by the Result-Code
+				AVP.  For practical purposes, this Failed-AVP would typically refer
+				to the first AVP processing error that a Diameter node encounters.
+
+				The possible reasons for this AVP are the presence of an improperly
+				constructed AVP, an unsupported or unrecognized AVP, an invalid AVP
+				value, the omission of a required AVP, the presence of an explicitly
+				excluded AVP (see tables in Section 10), or the presence of two or
+				more occurrences of an AVP which is restricted to 0, 1, or 0-1
+				occurrences.
+
+				A Diameter message SHOULD contain one Failed-AVP AVP, containing the
+				entire AVP that could not be processed successfully.  If the failure
+				reason is omission of a required AVP, an AVP with the missing AVP
+				code, the missing vendor id, and a zero filled payload of the minimum
+				required length for the omitted AVP will be added.  If the failure
+				reason is an invalid AVP length where the reported length is less
+				than the minimum AVP header length or greater than the reported
+				message length, a copy of the offending AVP header and a zero filled
+				payload of the minimum required length SHOULD be added.
+
+				In the case where the offending AVP is embedded within a grouped AVP,
+				the Failed-AVP MAY contain the grouped AVP which in turn contains the
+				single offending AVP.  The same method MAY be employed if the grouped
+				AVP itself is embedded in yet another grouped AVP and so on.  In this
+				case, the Failed-AVP MAY contain the grouped AVP heirarchy up to the
+				single offending AVP.  This enables the recipient to detect the
+				location of the offending AVP when embedded in a group.
+
+				AVP Format
+
+				 <Failed-AVP> ::= < AVP Header: 279 >
+        				       1* {AVP}
+			*/
+			struct dict_avp_data data = { 
+					279, 					/* Code */
+					#if AC_FAILED_AVP != 279
+					#error "AC_FAILED_AVP definition mismatch"
+					#endif
+					0, 					/* Vendor */
+					"Failed-AVP", 				/* Name */
+					AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, 	/* Fixed flags */
+					AVP_FLAG_MANDATORY,			/* Fixed flag values */
+					AVP_TYPE_GROUPED 			/* base type of data */
+					};
+			CHECK_dict_new( DICT_AVP, &data , NULL, NULL);
+		}
+		
+		/* Experimental-Result */
+		{
+			/*
+				The Experimental-Result AVP (AVP Code 297) is of type Grouped, and
+				indicates whether a particular vendor-specific request was completed
+				successfully or whether an error occurred.  Its Data field has the
+				following ABNF grammar:
+
+				AVP Format
+
+				 Experimental-Result ::= < AVP Header: 297 >
+                        				 { Vendor-Id }
+                        				 { Experimental-Result-Code }
+
+				The Vendor-Id AVP (see Section 5.3.3) in this grouped AVP identifies
+				the vendor responsible for the assignment of the result code which
+				follows.  All Diameter answer messages defined in vendor-specific
+				applications MUST include either one Result-Code AVP or one
+				Experimental-Result AVP.
+			*/
+			struct dict_avp_data data = { 
+					297, 					/* Code */
+					0, 					/* Vendor */
+					"Experimental-Result", 			/* Name */
+					AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, 	/* Fixed flags */
+					AVP_FLAG_MANDATORY,			/* Fixed flag values */
+					AVP_TYPE_GROUPED 			/* base type of data */
+					};
+			CHECK_dict_new( DICT_AVP, &data , NULL, NULL);
+		}
+		
+		/* Experimental-Result-Code */
+		{
+			/*
+				The Experimental-Result-Code AVP (AVP Code 298) is of type Unsigned32
+				and contains a vendor-assigned value representing the result of
+				processing the request.
+
+				It is recommended that vendor-specific result codes follow the same
+				conventions given for the Result-Code AVP regarding the different
+				types of result codes and the handling of errors (for non 2xxx
+				values).
+			*/
+			/* Although the RFC does not specify an "Enumerated" type here, we go forward and create one.
+			 * This is the reason for the "*" in the type name. Vendors will have to define their values.
+			 */
+			struct dict_object * 	type;
+			struct dict_type_data 	tdata = { AVP_TYPE_UNSIGNED32,	"Enumerated*(Experimental-Result-Code)"	, NULL, NULL, NULL };
+			struct dict_avp_data 	data = { 
+					298, 					/* Code */
+					0, 					/* Vendor */
+					"Experimental-Result-Code", 		/* Name */
+					AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, 	/* Fixed flags */
+					AVP_FLAG_MANDATORY,			/* Fixed flag values */
+					AVP_TYPE_UNSIGNED32 			/* base type of data */
+					};
+			
+			CHECK_dict_new( DICT_TYPE, &tdata , NULL, &type);
+			CHECK_dict_new( DICT_AVP, &data , type, NULL);
+		}
+		
+		/* Auth-Request-Type */
+		{
+			/*
+				The Auth-Request-Type AVP (AVP Code 274) is of type Enumerated and is
+				included in application-specific auth requests to inform the peers
+				whether a user is to be authenticated only, authorized only or both.
+				Note any value other than both MAY cause RADIUS interoperability
+				issues.  The following values are defined:
+
+
+				AUTHENTICATE_ONLY 1
+
+				The request being sent is for authentication only, and MUST
+				contain the relevant application specific authentication AVPs that
+				are needed by the Diameter server to authenticate the user.
+
+
+				AUTHORIZE_ONLY 2
+
+				The request being sent is for authorization only, and MUST contain
+				the application specific authorization AVPs that are necessary to
+				identify the service being requested/offered.
+
+
+				AUTHORIZE_AUTHENTICATE 3
+
+				The request contains a request for both authentication and
+				authorization.  The request MUST include both the relevant
+				application specific authentication information, and authorization
+				information necessary to identify the service being requested/
+				offered.
+			*/
+			struct dict_object 	* 	type;
+			struct dict_type_data 		tdata = { AVP_TYPE_INTEGER32,	"Enumerated(Auth-Request-Type)"	, NULL, NULL, NULL };
+			struct dict_enumval_data 	t_1 = { "AUTHENTICATE_ONLY", 		{ .i32 = 1 }};
+			struct dict_enumval_data 	t_2 = { "AUTHORIZE_ONLY", 		{ .i32 = 2 }};
+			struct dict_enumval_data 	t_3 = { "AUTHORIZE_AUTHENTICATE", 	{ .i32 = 3 }};
+			struct dict_avp_data 	data = { 
+					274, 					/* Code */
+					0, 					/* Vendor */
+					"Auth-Request-Type", 			/* Name */
+					AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, 	/* Fixed flags */
+					AVP_FLAG_MANDATORY,			/* Fixed flag values */
+					AVP_TYPE_INTEGER32 			/* base type of data */
+					};
+			/* Create the Enumerated type, and then the AVP */
+			CHECK_dict_new( DICT_TYPE, &tdata , NULL, &type);
+			CHECK_dict_new( DICT_ENUMVAL, &t_1 , type, NULL);
+			CHECK_dict_new( DICT_ENUMVAL, &t_2 , type, NULL);
+			CHECK_dict_new( DICT_ENUMVAL, &t_3 , type, NULL);
+			CHECK_dict_new( DICT_AVP, &data , type, NULL);
+		}
+		
+		/* Session-Id */
+		{
+			/*
+				The Session-Id AVP (AVP Code 263) is of type UTF8String and is used
+				to identify a specific session (see Section 8).  All messages
+				pertaining to a specific session MUST include only one Session-Id AVP
+				and the same value MUST be used throughout the life of a session.
+				When present, the Session-Id SHOULD appear immediately following the
+				Diameter Header (see Section 3).
+
+				The Session-Id MUST be globally and eternally unique, as it is meant
+				to uniquely identify a user session without reference to any other
+				information, and may be needed to correlate historical authentication
+				information with accounting information.  The Session-Id includes a
+				mandatory portion and an implementation-defined portion; a
+				recommended format for the implementation-defined portion is outlined
+				below.
+				
+				(skipped, see RFC for detail)
+			*/
+			struct dict_avp_data data = { 
+					263, 					/* Code */
+					#if AC_SESSION_ID != 263
+					#error "AC_SESSION_ID definition mismatch"
+					#endif
+					0, 					/* Vendor */
+					"Session-Id", 				/* Name */
+					AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, 	/* Fixed flags */
+					AVP_FLAG_MANDATORY,			/* Fixed flag values */
+					AVP_TYPE_OCTETSTRING 			/* base type of data */
+					};
+			CHECK_dict_new( DICT_AVP, &data , UTF8String_type, NULL);
+		}
+		
+		/* Authorization-Lifetime */
+		{
+			/*
+				The Authorization-Lifetime AVP (AVP Code 291) is of type Unsigned32
+				and contains the maximum number of seconds of service to be provided
+				to the user before the user is to be re-authenticated and/or re-
+				authorized.  Great care should be taken when the Authorization-
+				Lifetime value is determined, since a low, non-zero, value could
+				create significant Diameter traffic, which could congest both the
+				network and the agents.
+
+				A value of zero (0) means that immediate re-auth is necessary by the
+				access device.  This is typically used in cases where multiple
+				authentication methods are used, and a successful auth response with
+				this AVP set to zero is used to signal that the next authentication
+				method is to be immediately initiated.  The absence of this AVP, or a
+				value of all ones (meaning all bits in the 32 bit field are set to
+				one) means no re-auth is expected.
+
+				If both this AVP and the Session-Timeout AVP are present in a
+				message, the value of the latter MUST NOT be smaller than the
+				Authorization-Lifetime AVP.
+
+				An Authorization-Lifetime AVP MAY be present in re-authorization
+				messages, and contains the number of seconds the user is authorized
+				to receive service from the time the re-auth answer message is
+				received by the access device.
+
+				This AVP MAY be provided by the client as a hint of the maximum
+				lifetime that it is willing to accept.  However, the server MAY
+				return a value that is equal to, or smaller, than the one provided by
+				the client.
+			*/
+			struct dict_avp_data data = { 
+					291, 					/* Code */
+					0, 					/* Vendor */
+					"Authorization-Lifetime", 		/* Name */
+					AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, 	/* Fixed flags */
+					AVP_FLAG_MANDATORY,			/* Fixed flag values */
+					AVP_TYPE_UNSIGNED32 			/* base type of data */
+					};
+			CHECK_dict_new( DICT_AVP, &data , NULL, NULL);
+		}
+		
+		/* Auth-Grace-Period */
+		{
+			/*
+				The Auth-Grace-Period AVP (AVP Code 276) is of type Unsigned32 and
+				contains the number of seconds the Diameter server will wait
+				following the expiration of the Authorization-Lifetime AVP before
+				cleaning up resources for the session.
+			*/
+			struct dict_avp_data data = { 
+					276, 					/* Code */
+					0, 					/* Vendor */
+					"Auth-Grace-Period", 			/* Name */
+					AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, 	/* Fixed flags */
+					AVP_FLAG_MANDATORY,			/* Fixed flag values */
+					AVP_TYPE_UNSIGNED32 			/* base type of data */
+					};
+			CHECK_dict_new( DICT_AVP, &data , NULL, NULL);
+		}
+		
+		/* Auth-Session-State */
+		{
+			/*
+				The Auth-Session-State AVP (AVP Code 277) is of type Enumerated and
+				specifies whether state is maintained for a particular session.  The
+				client MAY include this AVP in requests as a hint to the server, but
+				the value in the server's answer message is binding.  The following
+				values are supported:
+
+
+				STATE_MAINTAINED 0
+
+				This value is used to specify that session state is being
+				maintained, and the access device MUST issue a session termination
+				message when service to the user is terminated.  This is the
+				default value.
+
+
+				NO_STATE_MAINTAINED 1
+
+				This value is used to specify that no session termination messages
+				will be sent by the access device upon expiration of the
+				Authorization-Lifetime.
+			*/
+			struct dict_object 	* 	type;
+			struct dict_type_data	 	tdata = { AVP_TYPE_INTEGER32,	"Enumerated(Auth-Session-State)"	, NULL, NULL, NULL };
+			struct dict_enumval_data 	t_0 = { "STATE_MAINTAINED", 		{ .i32 = 0 }};
+			struct dict_enumval_data 	t_1 = { "NO_STATE_MAINTAINED", 		{ .i32 = 1 }};
+			struct dict_avp_data	 	data = { 
+					277, 					/* Code */
+					0, 					/* Vendor */
+					"Auth-Session-State", 			/* Name */
+					AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, 	/* Fixed flags */
+					AVP_FLAG_MANDATORY,			/* Fixed flag values */
+					AVP_TYPE_INTEGER32 			/* base type of data */
+					};
+			/* Create the Enumerated type, and then the AVP */
+			CHECK_dict_new( DICT_TYPE, &tdata , NULL, &type);
+			CHECK_dict_new( DICT_ENUMVAL, &t_0 , type, NULL);
+			CHECK_dict_new( DICT_ENUMVAL, &t_1 , type, NULL);
+			CHECK_dict_new( DICT_AVP, &data , type, NULL);
+		}
+		
+		/* Re-Auth-Request-Type */
+		{
+			/*
+				The Re-Auth-Request-Type AVP (AVP Code 285) is of type Enumerated and
+				is included in application-specific auth answers to inform the client
+				of the action expected upon expiration of the Authorization-Lifetime.
+				If the answer message contains an Authorization-Lifetime AVP with a
+				positive value, the Re-Auth-Request-Type AVP MUST be present in an
+				answer message.  The following values are defined:
+
+
+				AUTHORIZE_ONLY 0
+
+				An authorization only re-auth is expected upon expiration of the
+				Authorization-Lifetime.  This is the default value if the AVP is
+				not present in answer messages that include the Authorization-
+				Lifetime.
+
+
+				AUTHORIZE_AUTHENTICATE 1
+
+				An authentication and authorization re-auth is expected upon
+				expiration of the Authorization-Lifetime.
+			*/
+			struct dict_object 	* 	type;
+			struct dict_type_data	 	tdata = { AVP_TYPE_INTEGER32,	"Enumerated(Re-Auth-Request-Type)"	, NULL, NULL, NULL };
+			struct dict_enumval_data 	t_0 = { "AUTHORIZE_ONLY", 		{ .i32 = 0 }};
+			struct dict_enumval_data 	t_1 = { "AUTHORIZE_AUTHENTICATE",	{ .i32 = 1 }};
+			struct dict_avp_data	 	data = { 
+					285, 					/* Code */
+					0, 					/* Vendor */
+					"Re-Auth-Request-Type",			/* Name */
+					AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, 	/* Fixed flags */
+					AVP_FLAG_MANDATORY,			/* Fixed flag values */
+					AVP_TYPE_INTEGER32 			/* base type of data */
+					};
+			/* Create the Enumerated type, and then the AVP */
+			CHECK_dict_new( DICT_TYPE, &tdata , NULL, &type);
+			CHECK_dict_new( DICT_ENUMVAL, &t_0 , type, NULL);
+			CHECK_dict_new( DICT_ENUMVAL, &t_1 , type, NULL);
+			CHECK_dict_new( DICT_AVP, &data , type, NULL);
+		}
+		
+		/* Session-Timeout */
+		{
+			/*
+				The Session-Timeout AVP (AVP Code 27) [RFC2865] is of type Unsigned32
+				and contains the maximum number of seconds of service to be provided
+				to the user before termination of the session.  When both the
+				Session-Timeout and the Authorization-Lifetime AVPs are present in an
+				answer message, the former MUST be equal to or greater than the value
+				of the latter.
+
+				A session that terminates on an access device due to the expiration
+				of the Session-Timeout MUST cause an STR to be issued, unless both
+				the access device and the home server had previously agreed that no
+				session termination messages would be sent (see Section 8.11).
+
+				A Session-Timeout AVP MAY be present in a re-authorization answer
+				message, and contains the remaining number of seconds from the
+				beginning of the re-auth.
+
+				A value of zero, or the absence of this AVP, means that this session
+				has an unlimited number of seconds before termination.
+
+				This AVP MAY be provided by the client as a hint of the maximum
+				timeout that it is willing to accept.  However, the server MAY return
+				a value that is equal to, or smaller, than the one provided by the
+				client.
+			*/
+			struct dict_avp_data data = { 
+					27, 					/* Code */
+					0, 					/* Vendor */
+					"Session-Timeout", 			/* Name */
+					AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, 	/* Fixed flags */
+					AVP_FLAG_MANDATORY,			/* Fixed flag values */
+					AVP_TYPE_UNSIGNED32 			/* base type of data */
+					};
+			CHECK_dict_new( DICT_AVP, &data , NULL, NULL);
+		}
+		
+		/* User-Name */
+		{
+			/*
+				The User-Name AVP (AVP Code 1) [RFC2865] is of type UTF8String, which
+				contains the User-Name, in a format consistent with the NAI
+				specification [RFC4282].
+			*/
+			struct dict_avp_data data = { 
+					1, 					/* Code */
+					0, 					/* Vendor */
+					"User-Name", 				/* Name */
+					AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, 	/* Fixed flags */
+					AVP_FLAG_MANDATORY,			/* Fixed flag values */
+					AVP_TYPE_OCTETSTRING 			/* base type of data */
+					};
+			CHECK_dict_new( DICT_AVP, &data , UTF8String_type, NULL);
+		}
+		
+		/* Termination-Cause */
+		{
+			/*
+				The Termination-Cause AVP (AVP Code 295) is of type Enumerated, and
+				is used to indicate the reason why a session was terminated on the
+				access device.  The following values are defined:
+
+
+				DIAMETER_LOGOUT 1
+
+				The user initiated a disconnect
+
+
+				DIAMETER_SERVICE_NOT_PROVIDED 2
+
+				This value is used when the user disconnected prior to the receipt
+				of the authorization answer message.
+
+
+				DIAMETER_BAD_ANSWER 3
+
+				This value indicates that the authorization answer received by the
+				access device was not processed successfully.
+
+
+				DIAMETER_ADMINISTRATIVE 4
+
+				The user was not granted access, or was disconnected, due to
+				administrative reasons, such as the receipt of a Abort-Session-
+				Request message.
+
+
+				DIAMETER_LINK_BROKEN 5
+
+				The communication to the user was abruptly disconnected.
+
+
+				DIAMETER_AUTH_EXPIRED 6
+
+				The user's access was terminated since its authorized session time
+				has expired.
+
+
+				DIAMETER_USER_MOVED 7
+
+				The user is receiving services from another access device.
+
+
+				DIAMETER_SESSION_TIMEOUT 8
+
+				The user's session has timed out, and service has been terminated.
+			*/
+			struct dict_object 	* 	type;
+			struct dict_type_data	 	tdata = { AVP_TYPE_INTEGER32,	"Enumerated(Termination-Cause)"	, NULL, NULL, NULL };
+			struct dict_enumval_data 	t_1 = { "DIAMETER_LOGOUT",			{ .i32 = 1 }};
+			struct dict_enumval_data 	t_2 = { "DIAMETER_SERVICE_NOT_PROVIDED", 	{ .i32 = 2 }};
+			struct dict_enumval_data 	t_3 = { "DIAMETER_BAD_ANSWER",			{ .i32 = 3 }};
+			struct dict_enumval_data 	t_4 = { "DIAMETER_ADMINISTRATIVE", 		{ .i32 = 4 }};
+			struct dict_enumval_data 	t_5 = { "DIAMETER_LINK_BROKEN",			{ .i32 = 5 }};
+			struct dict_enumval_data 	t_6 = { "DIAMETER_AUTH_EXPIRED", 		{ .i32 = 6 }};
+			struct dict_enumval_data 	t_7 = { "DIAMETER_USER_MOVED",			{ .i32 = 7 }};
+			struct dict_enumval_data 	t_8 = { "DIAMETER_SESSION_TIMEOUT", 		{ .i32 = 8 }};
+			struct dict_avp_data 	data = { 
+					295, 					/* Code */
+					0, 					/* Vendor */
+					"Termination-Cause",			/* Name */
+					AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, 	/* Fixed flags */
+					AVP_FLAG_MANDATORY,			/* Fixed flag values */
+					AVP_TYPE_INTEGER32 			/* base type of data */
+					};
+			/* Create the Enumerated type, and then the AVP */
+			CHECK_dict_new( DICT_TYPE, &tdata , NULL, &type);
+			CHECK_dict_new( DICT_ENUMVAL, &t_1 , type, NULL);
+			CHECK_dict_new( DICT_ENUMVAL, &t_2 , type, NULL);
+			CHECK_dict_new( DICT_ENUMVAL, &t_3 , type, NULL);
+			CHECK_dict_new( DICT_ENUMVAL, &t_4 , type, NULL);
+			CHECK_dict_new( DICT_ENUMVAL, &t_5 , type, NULL);
+			CHECK_dict_new( DICT_ENUMVAL, &t_6 , type, NULL);
+			CHECK_dict_new( DICT_ENUMVAL, &t_7 , type, NULL);
+			CHECK_dict_new( DICT_ENUMVAL, &t_8 , type, NULL);
+			CHECK_dict_new( DICT_AVP, &data , type, NULL);
+		}
+		
+		/* Origin-State-Id */
+		{
+			/*
+				The Origin-State-Id AVP (AVP Code 278), of type Unsigned32, is a
+				monotonically increasing value that is advanced whenever a Diameter
+				entity restarts with loss of previous state, for example upon reboot.
+				Origin-State-Id MAY be included in any Diameter message, including
+				CER.
+
+				A Diameter entity issuing this AVP MUST create a higher value for
+				this AVP each time its state is reset.  A Diameter entity MAY set
+				Origin-State-Id to the time of startup, or it MAY use an incrementing
+				counter retained in non-volatile memory across restarts.
+
+				The Origin-State-Id, if present, MUST reflect the state of the entity
+				indicated by Origin-Host.  If a proxy modifies Origin-Host, it MUST
+				either remove Origin-State-Id or modify it appropriately as well.
+				Typically, Origin-State-Id is used by an access device that always
+				starts up with no active sessions; that is, any session active prior
+				to restart will have been lost.  By including Origin-State-Id in a
+				message, it allows other Diameter entities to infer that sessions
+				associated with a lower Origin-State-Id are no longer active.  If an
+				access device does not intend for such inferences to be made, it MUST
+				either not include Origin-State-Id in any message, or set its value
+				to 0.
+			*/
+			struct dict_avp_data data = { 
+					278, 					/* Code */
+					0, 					/* Vendor */
+					"Origin-State-Id", 			/* Name */
+					AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, 	/* Fixed flags */
+					AVP_FLAG_MANDATORY,			/* Fixed flag values */
+					AVP_TYPE_UNSIGNED32 			/* base type of data */
+					};
+			CHECK_dict_new( DICT_AVP, &data , NULL, NULL);
+		}
+		
+		/* Session-Binding */
+		{
+			/*
+				The Session-Binding AVP (AVP Code 270) is of type Unsigned32, and MAY
+				be present in application-specific authorization answer messages.  If
+				present, this AVP MAY inform the Diameter client that all future
+				application-specific re-auth messages for this session MUST be sent
+				to the same authorization server.  This AVP MAY also specify that a
+				Session-Termination-Request message for this session MUST be sent to
+				the same authorizing server.
+
+				This field is a bit mask, and the following bits have been defined:
+
+
+				RE_AUTH 1
+
+				When set, future re-auth messages for this session MUST NOT
+				include the Destination-Host AVP.  When cleared, the default
+				value, the Destination-Host AVP MUST be present in all re-auth
+				messages for this session.
+
+
+				STR 2
+
+				When set, the STR message for this session MUST NOT include the
+				Destination-Host AVP.  When cleared, the default value, the
+				Destination-Host AVP MUST be present in the STR message for this
+				session.
+
+
+				ACCOUNTING 4
+
+				When set, all accounting messages for this session MUST NOT
+				include the Destination-Host AVP.  When cleared, the default
+				value, the Destination-Host AVP, if known, MUST be present in all
+				accounting messages for this session.
+			*/
+			
+			/* Although the RFC does not specify an "Enumerated" type here, we go forward and create one.
+			 * This is the reason for the "*" in the type name
+			 * The actual values of the AVP will not always be defined here, but at least it can be used in some cases.
+			 *  ... maybe the code will be changed later to support bitfields AVP ...
+			 */
+			
+			struct dict_object 	* 	type;
+			struct dict_type_data	 	tdata = { AVP_TYPE_UNSIGNED32,	"Enumerated*(Session-Binding)"	, NULL, NULL, NULL };
+			struct dict_enumval_data 	t_1 = { "RE_AUTH", 		{ .u32 = 1 }};
+			struct dict_enumval_data 	t_2 = { "STR", 			{ .u32 = 2 }};
+			struct dict_enumval_data 	t_4 = { "ACCOUNTING", 		{ .u32 = 4 }};
+			struct dict_avp_data 	data = { 
+					270, 					/* Code */
+					0, 					/* Vendor */
+					"Session-Binding", 			/* Name */
+					AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, 	/* Fixed flags */
+					AVP_FLAG_MANDATORY,			/* Fixed flag values */
+					AVP_TYPE_UNSIGNED32 			/* base type of data */
+					};
+			/* Create the Enumerated type, and then the AVP */
+			CHECK_dict_new( DICT_TYPE, &tdata , NULL, &type);
+			CHECK_dict_new( DICT_ENUMVAL, &t_1 , type, NULL);
+			CHECK_dict_new( DICT_ENUMVAL, &t_2 , type, NULL);
+			CHECK_dict_new( DICT_ENUMVAL, &t_4 , type, NULL);
+			CHECK_dict_new( DICT_AVP, &data , type, NULL);
+		}
+		
+		/* Session-Server-Failover */
+		{
+			/*
+				The Session-Server-Failover AVP (AVP Code 271) is of type Enumerated,
+				and MAY be present in application-specific authorization answer
+				messages that either do not include the Session-Binding AVP or
+				include the Session-Binding AVP with any of the bits set to a zero
+				value.  If present, this AVP MAY inform the Diameter client that if a
+				re-auth or STR message fails due to a delivery problem, the Diameter
+				client SHOULD issue a subsequent message without the Destination-Host
+				AVP.  When absent, the default value is REFUSE_SERVICE.
+
+				The following values are supported:
+
+
+				REFUSE_SERVICE 0
+
+				If either the re-auth or the STR message delivery fails, terminate
+				service with the user, and do not attempt any subsequent attempts.
+
+
+				TRY_AGAIN 1
+
+				If either the re-auth or the STR message delivery fails, resend
+				the failed message without the Destination-Host AVP present.
+
+
+				ALLOW_SERVICE 2
+
+				If re-auth message delivery fails, assume that re-authorization
+				succeeded.  If STR message delivery fails, terminate the session.
+
+
+				TRY_AGAIN_ALLOW_SERVICE 3
+
+				If either the re-auth or the STR message delivery fails, resend
+				the failed message without the Destination-Host AVP present.  If
+				the second delivery fails for re-auth, assume re-authorization
+				succeeded.  If the second delivery fails for STR, terminate the
+				session.
+			*/
+			struct dict_object  	* 	type;
+			struct dict_type_data	 	tdata = { AVP_TYPE_INTEGER32,	"Enumerated(Session-Server-Failover)"	, NULL, NULL, NULL };
+			struct dict_enumval_data 	t_0 = { "REFUSE_SERVICE", 		{ .i32 = 0 }};
+			struct dict_enumval_data 	t_1 = { "TRY_AGAIN",			{ .i32 = 1 }};
+			struct dict_enumval_data 	t_2 = { "ALLOW_SERVICE", 		{ .i32 = 2 }};
+			struct dict_enumval_data 	t_3 = { "TRY_AGAIN_ALLOW_SERVICE",	{ .i32 = 3 }};
+			struct dict_avp_data	 	data = { 
+					271, 					/* Code */
+					0, 					/* Vendor */
+					"Session-Server-Failover",		/* Name */
+					AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, 	/* Fixed flags */
+					AVP_FLAG_MANDATORY,			/* Fixed flag values */
+					AVP_TYPE_INTEGER32 			/* base type of data */
+					};
+			/* Create the Enumerated type, and then the AVP */
+			CHECK_dict_new( DICT_TYPE, &tdata , NULL, &type);
+			CHECK_dict_new( DICT_ENUMVAL, &t_0 , type, NULL);
+			CHECK_dict_new( DICT_ENUMVAL, &t_1 , type, NULL);
+			CHECK_dict_new( DICT_ENUMVAL, &t_2 , type, NULL);
+			CHECK_dict_new( DICT_ENUMVAL, &t_3 , type, NULL);
+			CHECK_dict_new( DICT_AVP, &data , type, NULL);
+		}
+		
+		/* Multi-Round-Time-Out */
+		{
+			/*
+				The Multi-Round-Time-Out AVP (AVP Code 272) is of type Unsigned32,
+				and SHOULD be present in application-specific authorization answer
+				messages whose Result-Code AVP is set to DIAMETER_MULTI_ROUND_AUTH.
+				This AVP contains the maximum number of seconds that the access
+				device MUST provide the user in responding to an authentication
+				request.
+			*/
+			struct dict_avp_data data = { 
+					272, 					/* Code */
+					0, 					/* Vendor */
+					"Multi-Round-Time-Out", 			/* Name */
+					AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, 	/* Fixed flags */
+					AVP_FLAG_MANDATORY,			/* Fixed flag values */
+					AVP_TYPE_UNSIGNED32 			/* base type of data */
+					};
+			CHECK_dict_new( DICT_AVP, &data , NULL, NULL);
+		}
+		
+		/* Class */
+		{
+			/*
+				The Class AVP (AVP Code 25) is of type OctetString and is used to by
+				Diameter servers to return state information to the access device.
+				When one or more Class AVPs are present in application-specific
+				authorization answer messages, they MUST be present in subsequent re-
+				authorization, session termination and accounting messages.  Class
+				AVPs found in a re-authorization answer message override the ones
+				found in any previous authorization answer message.  Diameter server
+				implementations SHOULD NOT return Class AVPs that require more than
+				4096 bytes of storage on the Diameter client.  A Diameter client that
+				receives Class AVPs whose size exceeds local available storage MUST
+				terminate the session.
+			*/
+			struct dict_avp_data data = { 
+					25, 					/* Code */
+					0, 					/* Vendor */
+					"Class", 				/* Name */
+					AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, 	/* Fixed flags */
+					AVP_FLAG_MANDATORY,			/* Fixed flag values */
+					AVP_TYPE_OCTETSTRING 			/* base type of data */
+					};
+			CHECK_dict_new( DICT_AVP, &data , NULL, NULL);
+		}
+		
+		/* Event-Timestamp */
+		{
+			/*
+				The Event-Timestamp (AVP Code 55) is of type Time, and MAY be
+				included in an Accounting-Request and Accounting-Answer messages to
+				record the time that the reported event occurred, in seconds since
+				January 1, 1900 00:00 UTC.
+			*/
+			struct dict_avp_data data = { 
+					55, 					/* Code */
+					0, 					/* Vendor */
+					"Event-Timestamp", 			/* Name */
+					AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, 	/* Fixed flags */
+					AVP_FLAG_MANDATORY,			/* Fixed flag values */
+					AVP_TYPE_OCTETSTRING 			/* base type of data */
+					};
+			CHECK_dict_new( DICT_AVP, &data , Time_type, NULL);
+		}
+
+				
+		/* Accounting-Record-Type */
+		{
+			/*
+				The Accounting-Record-Type AVP (AVP Code 480) is of type Enumerated
+				and contains the type of accounting record being sent.  The following
+				values are currently defined for the Accounting-Record-Type AVP:
+
+
+				EVENT_RECORD 1
+
+				An Accounting Event Record is used to indicate that a one-time
+				event has occurred (meaning that the start and end of the event
+				are simultaneous).  This record contains all information relevant
+				to the service, and is the only record of the service.
+
+
+				START_RECORD 2
+
+				An Accounting Start, Interim, and Stop Records are used to
+				indicate that a service of a measurable length has been given.  An
+				Accounting Start Record is used to initiate an accounting session,
+				and contains accounting information that is relevant to the
+				initiation of the session.
+
+
+				INTERIM_RECORD 3
+
+				An Interim Accounting Record contains cumulative accounting
+				information for an existing accounting session.  Interim
+				Accounting Records SHOULD be sent every time a re-authentication
+				or re-authorization occurs.  Further, additional interim record
+				triggers MAY be defined by application-specific Diameter
+				applications.  The selection of whether to use INTERIM_RECORD
+				records is done by the Acct-Interim-Interval AVP.
+
+
+				STOP_RECORD 4
+
+				An Accounting Stop Record is sent to terminate an accounting
+				session and contains cumulative accounting information relevant to
+				the existing session.
+			*/
+			struct dict_object 	* 	type;
+			struct dict_type_data	  	tdata = { AVP_TYPE_INTEGER32,	"Enumerated(Accounting-Record-Type)"	, NULL, NULL, NULL };
+			struct dict_enumval_data 	t_1 = { "EVENT_RECORD",			{ .i32 = 1 }};
+			struct dict_enumval_data 	t_2 = { "START_RECORD", 		{ .i32 = 2 }};
+			struct dict_enumval_data 	t_3 = { "INTERIM_RECORD",		{ .i32 = 3 }};
+			struct dict_enumval_data 	t_4 = { "STOP_RECORD", 			{ .i32 = 4 }};
+			struct dict_avp_data 	data = { 
+					480, 					/* Code */
+					0, 					/* Vendor */
+					"Accounting-Record-Type",		/* Name */
+					AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, 	/* Fixed flags */
+					AVP_FLAG_MANDATORY,			/* Fixed flag values */
+					AVP_TYPE_INTEGER32 			/* base type of data */
+					};
+			/* Create the Enumerated type, and then the AVP */
+			CHECK_dict_new( DICT_TYPE, &tdata , NULL, &type);
+			CHECK_dict_new( DICT_ENUMVAL, &t_1 , type, NULL);
+			CHECK_dict_new( DICT_ENUMVAL, &t_2 , type, NULL);
+			CHECK_dict_new( DICT_ENUMVAL, &t_3 , type, NULL);
+			CHECK_dict_new( DICT_ENUMVAL, &t_4 , type, NULL);
+			CHECK_dict_new( DICT_AVP, &data , type, NULL);
+		}
+		
+		/* Acct-Interim-Interval */
+		{
+			/*
+				The Acct-Interim-Interval AVP (AVP Code 85) is of type Unsigned32 and
+				is sent from the Diameter home authorization server to the Diameter
+				client.  The client uses information in this AVP to decide how and
+				when to produce accounting records.  With different values in this
+				AVP, service sessions can result in one, two, or two+N accounting
+				records, based on the needs of the home-organization.  The following
+				accounting record production behavior is directed by the inclusion of
+				this AVP:
+
+
+				1.  The omission of the Acct-Interim-Interval AVP or its inclusion
+				with Value field set to 0 means that EVENT_RECORD, START_RECORD,
+				and STOP_RECORD are produced, as appropriate for the service.
+
+
+				2.  The inclusion of the AVP with Value field set to a non-zero value
+				means that INTERIM_RECORD records MUST be produced between the
+				START_RECORD and STOP_RECORD records.  The Value field of this
+				AVP is the nominal interval between these records in seconds.
+
+				The Diameter node that originates the accounting information,
+				known as the client, MUST produce the first INTERIM_RECORD record
+				roughly at the time when this nominal interval has elapsed from
+				the START_RECORD, the next one again as the interval has elapsed
+				once more, and so on until the session ends and a STOP_RECORD
+				record is produced.
+
+				The client MUST ensure that the interim record production times
+				are randomized so that large accounting message storms are not
+				created either among records or around a common service start
+				time.
+			*/
+			struct dict_avp_data data = { 
+					85, 					/* Code */
+					0, 					/* Vendor */
+					"Acct-Interim-Interval", 		/* Name */
+					AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, 	/* Fixed flags */
+					AVP_FLAG_MANDATORY,			/* Fixed flag values */
+					AVP_TYPE_UNSIGNED32 			/* base type of data */
+					};
+			CHECK_dict_new( DICT_AVP, &data , NULL, NULL);
+		}
+		
+		/* Accounting-Record-Number */
+		{
+			/*
+				The Accounting-Record-Number AVP (AVP Code 485) is of type Unsigned32
+				and identifies this record within one session.  As Session-Id AVPs
+				are globally unique, the combination of Session-Id and Accounting-
+				Record-Number AVPs is also globally unique, and can be used in
+				matching accounting records with confirmations.  An easy way to
+				produce unique numbers is to set the value to 0 for records of type
+				EVENT_RECORD and START_RECORD, and set the value to 1 for the first
+				INTERIM_RECORD, 2 for the second, and so on until the value for
+				STOP_RECORD is one more than for the last INTERIM_RECORD.
+			*/
+			struct dict_avp_data data = { 
+					485, 					/* Code */
+					0, 					/* Vendor */
+					"Accounting-Record-Number", 		/* Name */
+					AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, 	/* Fixed flags */
+					AVP_FLAG_MANDATORY,			/* Fixed flag values */
+					AVP_TYPE_UNSIGNED32 			/* base type of data */
+					};
+			CHECK_dict_new( DICT_AVP, &data , NULL, NULL);
+		}
+		
+		/* Acct-Session-Id */
+		{
+			/*
+				The Acct-Session-Id AVP (AVP Code 44) is of type OctetString is only
+				used when RADIUS/Diameter translation occurs.  This AVP contains the
+				contents of the RADIUS Acct-Session-Id attribute.
+			*/
+			struct dict_avp_data data = { 
+					44, 					/* Code */
+					0, 					/* Vendor */
+					"Acct-Session-Id", 			/* Name */
+					AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, 	/* Fixed flags */
+					AVP_FLAG_MANDATORY,			/* Fixed flag values */
+					AVP_TYPE_OCTETSTRING 			/* base type of data */
+					};
+			CHECK_dict_new( DICT_AVP, &data , NULL, NULL);
+		}
+		
+		/* Acct-Multi-Session-Id */
+		{
+			/*
+				The Acct-Multi-Session-Id AVP (AVP Code 50) is of type UTF8String,
+				following the format specified in Section 8.8.  The Acct-Multi-
+				Session-Id AVP is used to link together multiple related accounting
+				sessions, where each session would have a unique Session-Id, but the
+				same Acct-Multi-Session-Id AVP.  This AVP MAY be returned by the
+				Diameter server in an authorization answer, and MUST be used in all
+				accounting messages for the given session.
+			*/
+			struct dict_avp_data data = { 
+					50, 					/* Code */
+					0, 					/* Vendor */
+					"Acct-Multi-Session-Id", 		/* Name */
+					AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, 	/* Fixed flags */
+					AVP_FLAG_MANDATORY,			/* Fixed flag values */
+					AVP_TYPE_OCTETSTRING 			/* base type of data */
+					};
+			CHECK_dict_new( DICT_AVP, &data , UTF8String_type, NULL);
+		}
+		
+		/* Accounting-Sub-Session-Id */
+		{
+			/*
+				The Accounting-Sub-Session-Id AVP (AVP Code 287) is of type
+				Unsigned64 and contains the accounting sub-session identifier.  The
+				combination of the Session-Id and this AVP MUST be unique per sub-
+				session, and the value of this AVP MUST be monotonically increased by
+				one for all new sub-sessions.  The absence of this AVP implies no
+				sub-sessions are in use, with the exception of an Accounting-Request
+				whose Accounting-Record-Type is set to STOP_RECORD.  A STOP_RECORD
+				message with no Accounting-Sub-Session-Id AVP present will signal the
+				termination of all sub-sessions for a given Session-Id.
+			*/
+			struct dict_avp_data data = { 
+					287, 					/* Code */
+					0, 					/* Vendor */
+					"Accounting-Sub-Session-Id", 		/* Name */
+					AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, 	/* Fixed flags */
+					AVP_FLAG_MANDATORY,			/* Fixed flag values */
+					AVP_TYPE_UNSIGNED64 			/* base type of data */
+					};
+			CHECK_dict_new( DICT_AVP, &data , NULL, NULL);
+		}
+		
+		/* Accounting-Realtime-Required */
+		{
+			/*
+				The Accounting-Realtime-Required AVP (AVP Code 483) is of type
+				Enumerated and is sent from the Diameter home authorization server to
+				the Diameter client or in the Accounting-Answer from the accounting
+				server.  The client uses information in this AVP to decide what to do
+				if the sending of accounting records to the accounting server has
+				been temporarily prevented due to, for instance, a network problem.
+
+
+				DELIVER_AND_GRANT 1
+
+				The AVP with Value field set to DELIVER_AND_GRANT means that the
+				service MUST only be granted as long as there is a connection to
+				an accounting server.  Note that the set of alternative accounting
+				servers are treated as one server in this sense.  Having to move
+				the accounting record stream to a backup server is not a reason to
+				discontinue the service to the user.
+
+
+				GRANT_AND_STORE 2
+
+				The AVP with Value field set to GRANT_AND_STORE means that service
+				SHOULD be granted if there is a connection, or as long as records
+				can still be stored as described in Section 9.4.
+
+				This is the default behavior if the AVP isn't included in the
+				reply from the authorization server.
+
+
+				GRANT_AND_LOSE 3
+
+				The AVP with Value field set to GRANT_AND_LOSE means that service
+				SHOULD be granted even if the records can not be delivered or
+				stored.
+			*/
+			struct dict_object  	* 	type;
+			struct dict_type_data	 	tdata = { AVP_TYPE_INTEGER32,	"Enumerated(Accounting-Realtime-Required)"	, NULL, NULL, NULL };
+			struct dict_enumval_data 	t_1 = { "DELIVER_AND_GRANT",		{ .i32 = 1 }};
+			struct dict_enumval_data 	t_2 = { "GRANT_AND_STORE", 		{ .i32 = 2 }};
+			struct dict_enumval_data 	t_3 = { "GRANT_AND_LOSE",		{ .i32 = 3 }};
+			struct dict_avp_data 		data = { 
+					483, 					/* Code */
+					0, 					/* Vendor */
+					"Accounting-Realtime-Required",		/* Name */
+					AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, 	/* Fixed flags */
+					AVP_FLAG_MANDATORY,			/* Fixed flag values */
+					AVP_TYPE_INTEGER32 			/* base type of data */
+					};
+			/* Create the Enumerated type, and then the AVP */
+			CHECK_dict_new( DICT_TYPE, &tdata , NULL, &type);
+			CHECK_dict_new( DICT_ENUMVAL, &t_1 , type, NULL);
+			CHECK_dict_new( DICT_ENUMVAL, &t_2 , type, NULL);
+			CHECK_dict_new( DICT_ENUMVAL, &t_3 , type, NULL);
+			CHECK_dict_new( DICT_AVP, &data , type, NULL);
+		}
+		
+	}
+	
+	/* Commands section */
+	{
+		/* To avoid defining global variables for all the AVP that we use here, we do search the dictionary in each sub-block.
+		 * This is far from optimal, but the code is clearer like this, and the time it requires at execution is not noticeable.
+		 */
+		
+		/* Generic message syntax when the 'E' bit is set */
+		{
+			/*
+				The 'E' (Error Bit) in the Diameter header is set when the request
+				caused a protocol-related error (see Section 7.1.3).  A message with
+				the 'E' bit MUST NOT be sent as a response to an answer message.
+				Note that a message with the 'E' bit set is still subjected to the
+				processing rules defined in Section 6.2.  When set, the answer
+				message will not conform to the ABNF specification for the command,
+				and will instead conform to the following ABNF:
+
+				Message Format
+
+				<answer-message> ::= < Diameter Header: code, ERR [PXY] >
+                				0*1< Session-Id >
+                				   { Origin-Host }
+                				   { Origin-Realm }
+                				   { Result-Code }
+                				   [ Origin-State-Id ]
+                				   [ Error-Message ]
+                				   [ Error-Reporting-Host ]
+                				   [ Failed-AVP ]
+                				 * [ Proxy-Info ]
+                				 * [ AVP ]
+
+				Note that the code used in the header is the same than the one found
+				in the request message, but with the 'R' bit cleared and the 'E' bit
+				set.  The 'P' bit in the header is set to the same value as the one
+				found in the request message.
+			*/
+			struct dict_object * cmd_error;
+			struct local_rules_definition rules[] = 
+						{ 	 {  "Session-Id", 			RULE_FIXED_HEAD,0, 1 }
+							,{  "Origin-Host",			RULE_REQUIRED, -1, 1 }
+							,{  "Origin-Realm",			RULE_REQUIRED, -1, 1 }
+							,{  "Result-Code",			RULE_REQUIRED, -1, 1 }
+							,{  "Origin-State-Id",			RULE_OPTIONAL, -1, 1 }
+							,{  "Error-Message",			RULE_OPTIONAL, -1, 1 }
+							,{  "Error-Reporting-Host",		RULE_OPTIONAL, -1, 1 }
+							,{  "Failed-AVP",			RULE_OPTIONAL, -1, 1 }
+							,{  "Proxy-Info",			RULE_OPTIONAL, -1,-1 }
+						};
+			CHECK_FCT( fd_dict_get_error_cmd(dict, &cmd_error) );
+			PARSE_loc_rules( rules, cmd_error );
+		}		
+		
+		/* Capabilities-Exchange-Request */
+		{
+			/*
+				The Capabilities-Exchange-Request (CER), indicated by the Command-
+				Code set to 257 and the Command Flags' 'R' bit set, is sent to
+				exchange local capabilities.  Upon detection of a transport failure,
+				this message MUST NOT be sent to an alternate peer.
+
+				When Diameter is run over SCTP [RFC2960], which allows for
+				connections to span multiple interfaces and multiple IP addresses,
+				the Capabilities-Exchange-Request message MUST contain one Host-IP-
+				Address AVP for each potential IP address that MAY be locally used
+				when transmitting Diameter messages.
+
+				Message Format
+
+				 <CER> ::= < Diameter Header: 257, REQ >
+        				   { Origin-Host }
+        				   { Origin-Realm }
+        				1* { Host-IP-Address }
+        				   { Vendor-Id }
+        				   { Product-Name }
+        				   [ Origin-State-Id ]
+        				 * [ Supported-Vendor-Id ]
+        				 * [ Auth-Application-Id ]
+        				 * [ Inband-Security-Id ]
+        				 * [ Acct-Application-Id ]
+        				 * [ Vendor-Specific-Application-Id ]
+        				   [ Firmware-Revision ]
+        				 * [ AVP ]
+			*/
+			struct dict_object * cmd;
+			struct dict_cmd_data data = { 
+					257, 					/* Code */
+					#if CC_CAPABILITIES_EXCHANGE != 257
+					#error "CC_CAPABILITIES_EXCHANGE definition mismatch"
+					#endif
+					"Capabilities-Exchange-Request", 	/* Name */
+					CMD_FLAG_REQUEST | CMD_FLAG_PROXIABLE | CMD_FLAG_RETRANSMIT | CMD_FLAG_ERROR, 	/* Fixed flags */
+					CMD_FLAG_REQUEST 			/* Fixed flag values */
+					};
+			struct local_rules_definition rules[] = 
+						{ 	 {  "Origin-Host", 			RULE_REQUIRED, -1, 1 }
+							,{  "Origin-Realm",			RULE_REQUIRED, -1, 1 }
+							,{  "Host-IP-Address",			RULE_REQUIRED, -1,-1 }
+							,{  "Vendor-Id",			RULE_REQUIRED, -1, 1 }
+							,{  "Product-Name",			RULE_REQUIRED, -1, 1 }
+							,{  "Origin-State-Id",			RULE_OPTIONAL, -1, 1 }
+							,{  "Supported-Vendor-Id",		RULE_OPTIONAL, -1,-1 }
+							,{  "Auth-Application-Id",		RULE_OPTIONAL, -1,-1 }
+							,{  "Inband-Security-Id",		RULE_OPTIONAL, -1,-1 }
+							,{  "Acct-Application-Id",		RULE_OPTIONAL, -1,-1 }
+							,{  "Vendor-Specific-Application-Id",	RULE_OPTIONAL, -1,-1 }
+							,{  "Firmware-Revision",		RULE_OPTIONAL, -1, 1 }
+						};
+			
+			CHECK_dict_new( DICT_COMMAND, &data , NULL, &cmd);
+			PARSE_loc_rules( rules, cmd );
+		}
+		
+		/* Capabilities-Exchange-Answer */
+		{
+			/*
+				The Capabilities-Exchange-Answer (CEA), indicated by the Command-Code
+				set to 257 and the Command Flags' 'R' bit cleared, is sent in
+				response to a CER message.
+
+				When Diameter is run over SCTP [RFC2960], which allows connections to
+				span multiple interfaces, hence, multiple IP addresses, the
+				Capabilities-Exchange-Answer message MUST contain one Host-IP-Address
+				AVP for each potential IP address that MAY be locally used when
+				transmitting Diameter messages.
+
+				Message Format
+
+				 <CEA> ::= < Diameter Header: 257 >
+        				   { Result-Code }
+        				   { Origin-Host }
+        				   { Origin-Realm }
+        				1* { Host-IP-Address }
+        				   { Vendor-Id }
+        				   { Product-Name }
+        				   [ Origin-State-Id ]
+        				   [ Error-Message ]
+        				   [ Failed-AVP ]
+        				 * [ Supported-Vendor-Id ]
+        				 * [ Auth-Application-Id ]
+        				 * [ Inband-Security-Id ]
+        				 * [ Acct-Application-Id ]
+        				 * [ Vendor-Specific-Application-Id ]
+        				   [ Firmware-Revision ]
+        				 * [ AVP ]
+			*/
+			struct dict_object * cmd;
+			struct dict_cmd_data data = { 
+					257, 					/* Code */
+					#if CC_CAPABILITIES_EXCHANGE != 257
+					#error "CC_CAPABILITIES_EXCHANGE definition mismatch"
+					#endif
+					"Capabilities-Exchange-Answer", 	/* Name */
+					CMD_FLAG_REQUEST | CMD_FLAG_PROXIABLE | CMD_FLAG_RETRANSMIT, 	/* Fixed flags */
+					0 					/* Fixed flag values */
+					};
+			struct local_rules_definition rules[] = 
+						{ 	 {  "Result-Code", 			RULE_REQUIRED, -1, 1 }
+						 	,{  "Origin-Host", 			RULE_REQUIRED, -1, 1 }
+							,{  "Origin-Realm",			RULE_REQUIRED, -1, 1 }
+							,{  "Host-IP-Address",			RULE_REQUIRED, -1,-1 }
+							,{  "Vendor-Id",			RULE_REQUIRED, -1, 1 }
+							,{  "Product-Name",			RULE_REQUIRED, -1, 1 }
+							,{  "Origin-State-Id",			RULE_OPTIONAL, -1, 1 }
+							,{  "Error-Message",			RULE_OPTIONAL, -1, 1 }
+							,{  "Failed-AVP",			RULE_OPTIONAL, -1, 1 }
+							,{  "Supported-Vendor-Id",		RULE_OPTIONAL, -1,-1 }
+							,{  "Auth-Application-Id",		RULE_OPTIONAL, -1,-1 }
+							,{  "Inband-Security-Id",		RULE_OPTIONAL, -1,-1 }
+							,{  "Acct-Application-Id",		RULE_OPTIONAL, -1,-1 }
+							,{  "Vendor-Specific-Application-Id",	RULE_OPTIONAL, -1,-1 }
+							,{  "Firmware-Revision",		RULE_OPTIONAL, -1, 1 }
+						};
+			
+			CHECK_dict_new( DICT_COMMAND, &data , NULL, &cmd);
+			PARSE_loc_rules( rules, cmd );
+		}
+		
+		/* Disconnect-Peer-Request */
+		{
+			/*
+				The Disconnect-Peer-Request (DPR), indicated by the Command-Code set
+				to 282 and the Command Flags' 'R' bit set, is sent to a peer to
+				inform its intentions to shutdown the transport connection.  Upon
+				detection of a transport failure, this message MUST NOT be sent to an
+				alternate peer.
+
+				Message Format
+
+				 <DPR>  ::= < Diameter Header: 282, REQ >
+        				    { Origin-Host }
+        				    { Origin-Realm }
+        				    { Disconnect-Cause }
+			*/
+			struct dict_object * cmd;
+			struct dict_cmd_data data = { 
+					282, 					/* Code */
+					#if CC_DISCONNECT_PEER != 282
+					#error "CC_DISCONNECT_PEER definition mismatch"
+					#endif
+					"Disconnect-Peer-Request", 		/* Name */
+					CMD_FLAG_REQUEST | CMD_FLAG_PROXIABLE | CMD_FLAG_RETRANSMIT | CMD_FLAG_ERROR, 	/* Fixed flags */
+					CMD_FLAG_REQUEST 			/* Fixed flag values */
+					};
+			struct local_rules_definition rules[] = 
+						{ 	 {  "Origin-Host", 			RULE_REQUIRED, -1, 1 }
+							,{  "Origin-Realm",			RULE_REQUIRED, -1, 1 }
+							,{  "Disconnect-Cause",			RULE_REQUIRED, -1, 1 }
+						};
+			
+			CHECK_dict_new( DICT_COMMAND, &data , NULL, &cmd);
+			PARSE_loc_rules( rules, cmd );
+		}
+		
+		/* Disconnect-Peer-Answer */
+		{
+			/*
+				The Disconnect-Peer-Answer (DPA), indicated by the Command-Code set
+				to 282 and the Command Flags' 'R' bit cleared, is sent as a response
+				to the Disconnect-Peer-Request message.  Upon receipt of this
+				message, the transport connection is shutdown.
+
+				Message Format
+
+				 <DPA>  ::= < Diameter Header: 282 >
+        				    { Result-Code }
+        				    { Origin-Host }
+        				    { Origin-Realm }
+        				    [ Error-Message ]
+        				    [ Failed-AVP ]
+			*/
+			struct dict_object * cmd;
+			struct dict_cmd_data data = { 
+					282, 					/* Code */
+					#if CC_DISCONNECT_PEER != 282
+					#error "CC_DISCONNECT_PEER definition mismatch"
+					#endif
+					"Disconnect-Peer-Answer", 		/* Name */
+					CMD_FLAG_REQUEST | CMD_FLAG_PROXIABLE | CMD_FLAG_RETRANSMIT, 	/* Fixed flags */
+					0 					/* Fixed flag values */
+					};
+			struct local_rules_definition rules[] = 
+						{ 	 {  "Result-Code", 			RULE_REQUIRED, -1, 1 }
+						 	,{  "Origin-Host", 			RULE_REQUIRED, -1, 1 }
+							,{  "Origin-Realm",			RULE_REQUIRED, -1, 1 }
+							,{  "Error-Message",			RULE_OPTIONAL, -1, 1 }
+							,{  "Failed-AVP",			RULE_OPTIONAL, -1, 1 }
+						};
+			
+			CHECK_dict_new( DICT_COMMAND, &data , NULL, &cmd);
+			PARSE_loc_rules( rules, cmd );
+		}
+
+		/* Device-Watchdog-Request */
+		{
+			/*
+				The Device-Watchdog-Request (DWR), indicated by the Command-Code set
+				to 280 and the Command Flags' 'R' bit set, is sent to a peer when no
+				traffic has been exchanged between two peers (see Section 5.5.3).
+				Upon detection of a transport failure, this message MUST NOT be sent
+				to an alternate peer.
+
+				Message Format
+
+				 <DWR>  ::= < Diameter Header: 280, REQ >
+        				    { Origin-Host }
+        				    { Origin-Realm }
+        				    [ Origin-State-Id ]
+			*/
+			struct dict_object * cmd;
+			struct dict_cmd_data data = { 
+					280, 					/* Code */
+					#if CC_DEVICE_WATCHDOG != 280
+					#error "CC_DEVICE_WATCHDOG definition mismatch"
+					#endif
+					"Device-Watchdog-Request", 		/* Name */
+					CMD_FLAG_REQUEST | CMD_FLAG_PROXIABLE | CMD_FLAG_RETRANSMIT | CMD_FLAG_ERROR, 	/* Fixed flags */
+					CMD_FLAG_REQUEST 			/* Fixed flag values */
+					};
+			struct local_rules_definition rules[] = 
+						{ 	 {  "Origin-Host", 			RULE_REQUIRED, -1, 1 }
+							,{  "Origin-Realm",			RULE_REQUIRED, -1, 1 }
+							,{  "Origin-State-Id",			RULE_OPTIONAL, -1, 1 }
+						};
+			
+			CHECK_dict_new( DICT_COMMAND, &data , NULL, &cmd);
+			PARSE_loc_rules( rules, cmd );
+		}
+		
+		/* Device-Watchdog-Answer */
+		{
+			/*
+				The Device-Watchdog-Answer (DWA), indicated by the Command-Code set
+				to 280 and the Command Flags' 'R' bit cleared, is sent as a response
+				to the Device-Watchdog-Request message.
+
+				Message Format
+
+				 <DWA>  ::= < Diameter Header: 280 >
+        				    { Result-Code }
+        				    { Origin-Host }
+        				    { Origin-Realm }
+        				    [ Error-Message ]
+        				    [ Failed-AVP ]
+        				    [ Origin-State-Id ]
+			*/
+			struct dict_object * cmd;
+			struct dict_cmd_data data = { 
+					280, 					/* Code */
+					#if CC_DEVICE_WATCHDOG != 280
+					#error "CC_DEVICE_WATCHDOG definition mismatch"
+					#endif
+					"Device-Watchdog-Answer", 		/* Name */
+					CMD_FLAG_REQUEST | CMD_FLAG_PROXIABLE | CMD_FLAG_RETRANSMIT, 	/* Fixed flags */
+					0 					/* Fixed flag values */
+					};
+			struct local_rules_definition rules[] = 
+						{ 	 {  "Result-Code", 			RULE_REQUIRED, -1, 1 }
+						 	,{  "Origin-Host", 			RULE_REQUIRED, -1, 1 }
+							,{  "Origin-Realm",			RULE_REQUIRED, -1, 1 }
+							,{  "Error-Message",			RULE_OPTIONAL, -1, 1 }
+							,{  "Failed-AVP",			RULE_OPTIONAL, -1, 1 }
+							,{  "Origin-State-Id",			RULE_OPTIONAL, -1, 1 }
+						};
+			
+			CHECK_dict_new( DICT_COMMAND, &data , NULL, &cmd);
+			PARSE_loc_rules( rules, cmd );
+		}
+		
+		/* Re-Auth-Request */
+		{
+			/*
+				The Re-Auth-Request (RAR), indicated by the Command-Code set to 258
+				and the message flags' 'R' bit set, may be sent by any server to the
+				access device that is providing session service, to request that the
+				user be re-authenticated and/or re-authorized.
+
+
+				Message Format
+
+				 <RAR>  ::= < Diameter Header: 258, REQ, PXY >
+        				    < Session-Id >
+        				    { Origin-Host }
+        				    { Origin-Realm }
+        				    { Destination-Realm }
+        				    { Destination-Host }
+        				    { Auth-Application-Id }
+        				    { Re-Auth-Request-Type }
+        				    [ User-Name ]
+        				    [ Origin-State-Id ]
+        				  * [ Proxy-Info ]
+        				  * [ Route-Record ]
+        				  * [ AVP ]
+			*/
+			struct dict_object * cmd;
+			struct dict_cmd_data data = { 
+					258, 					/* Code */
+					#if CC_RE_AUTH != 258
+					#error "CC_RE_AUTH definition mismatch"
+					#endif
+					"Re-Auth-Request", 			/* Name */
+					CMD_FLAG_REQUEST | CMD_FLAG_PROXIABLE | CMD_FLAG_ERROR,	/* Fixed flags */
+					CMD_FLAG_REQUEST | CMD_FLAG_PROXIABLE	/* Fixed flag values */
+					};
+			struct local_rules_definition rules[] = 
+						{ 	 {  "Session-Id", 			RULE_FIXED_HEAD, -1, 1 }
+						 	,{  "Origin-Host", 			RULE_REQUIRED,   -1, 1 }
+							,{  "Origin-Realm",			RULE_REQUIRED,   -1, 1 }
+							,{  "Destination-Realm",		RULE_REQUIRED,   -1, 1 }
+						 	,{  "Destination-Host", 		RULE_REQUIRED,   -1, 1 }
+						 	,{  "Auth-Application-Id", 		RULE_REQUIRED,   -1, 1 }
+						 	,{  "Re-Auth-Request-Type", 		RULE_REQUIRED,   -1, 1 }
+							,{  "User-Name",			RULE_OPTIONAL,   -1, 1 }
+							,{  "Origin-State-Id",			RULE_OPTIONAL,   -1, 1 }
+							,{  "Proxy-Info",			RULE_OPTIONAL,   -1,-1 }
+							,{  "Route-Record",			RULE_OPTIONAL,   -1,-1 }
+						};
+			
+			CHECK_dict_new( DICT_COMMAND, &data , NULL, &cmd);
+			PARSE_loc_rules( rules, cmd );
+		}
+		
+		/* Re-Auth-Answer */
+		{
+			/*
+				The Re-Auth-Answer (RAA), indicated by the Command-Code set to 258
+				and the message flags' 'R' bit clear, is sent in response to the RAR.
+				The Result-Code AVP MUST be present, and indicates the disposition of
+				the request.
+
+				A successful RAA message MUST be followed by an application-specific
+				authentication and/or authorization message.
+
+
+				Message Format
+
+				 <RAA>  ::= < Diameter Header: 258, PXY >
+        				    < Session-Id >
+        				    { Result-Code }
+        				    { Origin-Host }
+        				    { Origin-Realm }
+        				    [ User-Name ]
+        				    [ Origin-State-Id ]
+        				    [ Error-Message ]
+        				    [ Error-Reporting-Host ]
+        				    [ Failed-AVP ]
+        				  * [ Redirect-Host ]
+        				    [ Redirect-Host-Usage ]
+        				    [ Redirect-Max-Cache-Time ]
+        				  * [ Proxy-Info ]
+        				  * [ AVP ]
+			*/
+			struct dict_object * cmd;
+			struct dict_cmd_data data = { 
+					258, 					/* Code */
+					#if CC_RE_AUTH != 258
+					#error "CC_RE_AUTH definition mismatch"
+					#endif
+					"Re-Auth-Answer", 			/* Name */
+					CMD_FLAG_REQUEST | CMD_FLAG_PROXIABLE,	/* Fixed flags */
+							   CMD_FLAG_PROXIABLE	/* Fixed flag values */
+					};
+			struct local_rules_definition rules[] = 
+						{ 	 {  "Session-Id", 			RULE_FIXED_HEAD, -1, 1 }
+ 							,{  "Result-Code", 			RULE_REQUIRED,   -1, 1 }
+						 	,{  "Origin-Host", 			RULE_REQUIRED,   -1, 1 }
+							,{  "Origin-Realm",			RULE_REQUIRED,   -1, 1 }
+							,{  "User-Name",			RULE_OPTIONAL,   -1, 1 }
+							,{  "Origin-State-Id",			RULE_OPTIONAL,   -1, 1 }
+							,{  "Error-Message",			RULE_OPTIONAL,   -1, 1 }
+							,{  "Error-Reporting-Host",		RULE_OPTIONAL,   -1, 1 }
+							,{  "Failed-AVP",			RULE_OPTIONAL,   -1, 1 }
+							,{  "Redirect-Host",			RULE_OPTIONAL,   -1,-1 }
+							,{  "Redirect-Host-Usage",		RULE_OPTIONAL,   -1, 1 }
+							,{  "Redirect-Max-Cache-Time",		RULE_OPTIONAL,   -1, 1 }
+							,{  "Proxy-Info",			RULE_OPTIONAL,   -1,-1 }
+						};
+			
+			CHECK_dict_new( DICT_COMMAND, &data , NULL, &cmd);
+			PARSE_loc_rules( rules, cmd );
+		}
+		
+		/* Session-Termination-Request */
+		{
+			/*
+				The Session-Termination-Request (STR), indicated by the Command-Code
+				set to 275 and the Command Flags' 'R' bit set, is sent by the access
+				device to inform the Diameter Server that an authenticated and/or
+				authorized session is being terminated.
+
+
+        				   Message Format
+
+				 <STR> ::= < Diameter Header: 275, REQ, PXY >
+        				   < Session-Id >
+        				   { Origin-Host }
+        				   { Origin-Realm }
+        				   { Destination-Realm }
+        				   { Auth-Application-Id }
+        				   { Termination-Cause }
+        				   [ User-Name ]
+        				   [ Destination-Host ]
+        				 * [ Class ]
+        				   [ Origin-State-Id ]
+        				 * [ Proxy-Info ]
+        				 * [ Route-Record ]
+        				 * [ AVP ]
+			*/
+			struct dict_object * cmd;
+			struct dict_cmd_data data = { 
+					275, 					/* Code */
+					#if CC_SESSION_TERMINATION != 275
+					#error "CC_SESSION_TERMINATION definition mismatch"
+					#endif
+					"Session-Termination-Request", 		/* Name */
+					CMD_FLAG_REQUEST | CMD_FLAG_PROXIABLE | CMD_FLAG_ERROR,	/* Fixed flags */
+					CMD_FLAG_REQUEST | CMD_FLAG_PROXIABLE	/* Fixed flag values */
+					};
+			struct local_rules_definition rules[] = 
+						{ 	 {  "Session-Id", 			RULE_FIXED_HEAD, -1, 1 }
+						 	,{  "Origin-Host", 			RULE_REQUIRED,   -1, 1 }
+							,{  "Origin-Realm",			RULE_REQUIRED,   -1, 1 }
+							,{  "Destination-Realm",		RULE_REQUIRED,   -1, 1 }
+						 	,{  "Auth-Application-Id", 		RULE_REQUIRED,   -1, 1 }
+						 	,{  "Termination-Cause", 		RULE_REQUIRED,   -1, 1 }
+							,{  "User-Name",			RULE_OPTIONAL,   -1, 1 }
+							,{  "Destination-Host",			RULE_OPTIONAL,   -1, 1 }
+							,{  "Class",				RULE_OPTIONAL,   -1,-1 }
+							,{  "Origin-State-Id",			RULE_OPTIONAL,   -1, 1 }
+							,{  "Proxy-Info",			RULE_OPTIONAL,   -1,-1 }
+							,{  "Route-Record",			RULE_OPTIONAL,   -1,-1 }
+						};
+			
+			CHECK_dict_new( DICT_COMMAND, &data , NULL, &cmd);
+			PARSE_loc_rules( rules, cmd );
+		}
+		
+		/* Session-Termination-Answer */
+		{
+			/*
+				The Session-Termination-Answer (STA), indicated by the Command-Code
+				set to 275 and the message flags' 'R' bit clear, is sent by the
+				Diameter Server to acknowledge the notification that the session has
+				been terminated.  The Result-Code AVP MUST be present, and MAY
+				contain an indication that an error occurred while servicing the STR.
+
+				Upon sending or receipt of the STA, the Diameter Server MUST release
+				all resources for the session indicated by the Session-Id AVP.  Any
+				intermediate server in the Proxy-Chain MAY also release any
+				resources, if necessary.
+
+        				    Message Format
+
+				 <STA>  ::= < Diameter Header: 275, PXY >
+        				    < Session-Id >
+        				    { Result-Code }
+        				    { Origin-Host }
+        				    { Origin-Realm }
+        				    [ User-Name ]
+        				  * [ Class ]
+        				    [ Error-Message ]
+        				    [ Error-Reporting-Host ]
+        				    [ Failed-AVP ]
+        				    [ Origin-State-Id ]
+        				  * [ Redirect-Host ]
+        				    [ Redirect-Host-Usage ]
+        				    [ Redirect-Max-Cache-Time ]
+        				  * [ Proxy-Info ]
+        				  * [ AVP ]
+			*/
+			struct dict_object * cmd;
+			struct dict_cmd_data data = { 
+					275, 					/* Code */
+					#if CC_SESSION_TERMINATION != 275
+					#error "CC_SESSION_TERMINATION definition mismatch"
+					#endif
+					"Session-Termination-Answer", 		/* Name */
+					CMD_FLAG_REQUEST | CMD_FLAG_PROXIABLE,	/* Fixed flags */
+							   CMD_FLAG_PROXIABLE	/* Fixed flag values */
+					};
+			struct local_rules_definition rules[] = 
+						{ 	 {  "Session-Id", 			RULE_FIXED_HEAD, -1, 1 }
+ 							,{  "Result-Code", 			RULE_REQUIRED,   -1, 1 }
+						 	,{  "Origin-Host", 			RULE_REQUIRED,   -1, 1 }
+							,{  "Origin-Realm",			RULE_REQUIRED,   -1, 1 }
+							,{  "User-Name",			RULE_OPTIONAL,   -1, 1 }
+							,{  "Class",				RULE_OPTIONAL,   -1,-1 }
+							,{  "Error-Message",			RULE_OPTIONAL,   -1, 1 }
+							,{  "Error-Reporting-Host",		RULE_OPTIONAL,   -1, 1 }
+							,{  "Failed-AVP",			RULE_OPTIONAL,   -1, 1 }
+							,{  "Origin-State-Id",			RULE_OPTIONAL,   -1, 1 }
+							,{  "Redirect-Host",			RULE_OPTIONAL,   -1,-1 }
+							,{  "Redirect-Host-Usage",		RULE_OPTIONAL,   -1, 1 }
+							,{  "Redirect-Max-Cache-Time",		RULE_OPTIONAL,   -1, 1 }
+							,{  "Proxy-Info",			RULE_OPTIONAL,   -1,-1 }
+						};
+			
+			CHECK_dict_new( DICT_COMMAND, &data , NULL, &cmd);
+			PARSE_loc_rules( rules, cmd );
+		}
+		
+		/* Abort-Session-Request */
+		{
+			/*
+				The Abort-Session-Request (ASR), indicated by the Command-Code set to
+				274 and the message flags' 'R' bit set, may be sent by any server to
+				the access device that is providing session service, to request that
+				the session identified by the Session-Id be stopped.
+
+
+        				    Message Format
+
+				 <ASR>  ::= < Diameter Header: 274, REQ, PXY >
+        				    < Session-Id >
+        				    { Origin-Host }
+        				    { Origin-Realm }
+        				    { Destination-Realm }
+        				    { Destination-Host }
+        				    { Auth-Application-Id }
+        				    [ User-Name ]
+        				    [ Origin-State-Id ]
+        				  * [ Proxy-Info ]
+        				  * [ Route-Record ]
+        				  * [ AVP ]
+			*/
+			struct dict_object * cmd;
+			struct dict_cmd_data data = { 
+					274, 					/* Code */
+					#if CC_ABORT_SESSION != 274
+					#error "CC_ABORT_SESSION definition mismatch"
+					#endif
+					"Abort-Session-Request", 		/* Name */
+					CMD_FLAG_REQUEST | CMD_FLAG_PROXIABLE | CMD_FLAG_ERROR,	/* Fixed flags */
+					CMD_FLAG_REQUEST | CMD_FLAG_PROXIABLE	/* Fixed flag values */
+					};
+			struct local_rules_definition rules[] = 
+						{ 	 {  "Session-Id", 			RULE_FIXED_HEAD, -1, 1 }
+						 	,{  "Origin-Host", 			RULE_REQUIRED,   -1, 1 }
+							,{  "Origin-Realm",			RULE_REQUIRED,   -1, 1 }
+							,{  "Destination-Realm",		RULE_REQUIRED,   -1, 1 }
+							,{  "Destination-Host",			RULE_REQUIRED,   -1, 1 }
+						 	,{  "Auth-Application-Id", 		RULE_REQUIRED,   -1, 1 }
+							,{  "User-Name",			RULE_OPTIONAL,   -1, 1 }
+							,{  "Origin-State-Id",			RULE_OPTIONAL,   -1, 1 }
+							,{  "Proxy-Info",			RULE_OPTIONAL,   -1,-1 }
+							,{  "Route-Record",			RULE_OPTIONAL,   -1,-1 }
+						};
+			
+			CHECK_dict_new( DICT_COMMAND, &data , NULL, &cmd);
+			PARSE_loc_rules( rules, cmd );
+		}
+		
+		/* Abort-Session-Answer */
+		{
+			/*
+				The Abort-Session-Answer (ASA), indicated by the Command-Code set to
+				274 and the message flags' 'R' bit clear, is sent in response to the
+				ASR.  The Result-Code AVP MUST be present, and indicates the
+				disposition of the request.
+
+				If the session identified by Session-Id in the ASR was successfully
+				terminated, Result-Code is set to DIAMETER_SUCCESS.  If the session
+				is not currently active, Result-Code is set to
+				DIAMETER_UNKNOWN_SESSION_ID.  If the access device does not stop the
+				session for any other reason, Result-Code is set to
+				DIAMETER_UNABLE_TO_COMPLY.
+
+        				    Message Format
+
+				 <ASA>  ::= < Diameter Header: 274, PXY >
+        				    < Session-Id >
+        				    { Result-Code }
+        				    { Origin-Host }
+        				    { Origin-Realm }
+        				    [ User-Name ]
+        				    [ Origin-State-Id ]
+        				    [ Error-Message ]
+        				    [ Error-Reporting-Host ]
+        				    [ Failed-AVP ]
+        				  * [ Redirect-Host ]
+        				    [ Redirect-Host-Usage ]
+        				    [ Redirect-Max-Cache-Time ]
+        				  * [ Proxy-Info ]
+        				  * [ AVP ]
+			*/
+			struct dict_object * cmd;
+			struct dict_cmd_data data = { 
+					274, 					/* Code */
+					#if CC_ABORT_SESSION != 274
+					#error "CC_ABORT_SESSION definition mismatch"
+					#endif
+					"Abort-Session-Answer", 		/* Name */
+					CMD_FLAG_REQUEST | CMD_FLAG_PROXIABLE,	/* Fixed flags */
+							   CMD_FLAG_PROXIABLE	/* Fixed flag values */
+					};
+			struct local_rules_definition rules[] = 
+						{ 	 {  "Session-Id", 			RULE_FIXED_HEAD, -1, 1 }
+ 							,{  "Result-Code", 			RULE_REQUIRED,   -1, 1 }
+						 	,{  "Origin-Host", 			RULE_REQUIRED,   -1, 1 }
+							,{  "Origin-Realm",			RULE_REQUIRED,   -1, 1 }
+							,{  "User-Name",			RULE_OPTIONAL,   -1, 1 }
+							,{  "Origin-State-Id",			RULE_OPTIONAL,   -1, 1 }
+							,{  "Error-Message",			RULE_OPTIONAL,   -1, 1 }
+							,{  "Error-Reporting-Host",		RULE_OPTIONAL,   -1, 1 }
+							,{  "Failed-AVP",			RULE_OPTIONAL,   -1, 1 }
+							,{  "Redirect-Host",			RULE_OPTIONAL,   -1,-1 }
+							,{  "Redirect-Host-Usage",		RULE_OPTIONAL,   -1, 1 }
+							,{  "Redirect-Max-Cache-Time",		RULE_OPTIONAL,   -1, 1 }
+							,{  "Proxy-Info",			RULE_OPTIONAL,   -1,-1 }
+						};
+			
+			CHECK_dict_new( DICT_COMMAND, &data , NULL, &cmd);
+			PARSE_loc_rules( rules, cmd );
+		}
+		
+		/* Accounting-Request */
+		{
+			/*
+				The Accounting-Request (ACR) command, indicated by the Command-Code
+				field set to 271 and the Command Flags' 'R' bit set, is sent by a
+				Diameter node, acting as a client, in order to exchange accounting
+				information with a peer.
+
+				One of Acct-Application-Id and Vendor-Specific-Application-Id AVPs
+				MUST be present.  If the Vendor-Specific-Application-Id grouped AVP
+				is present, it MUST include an Acct-Application-Id AVP.
+
+				The AVP listed below SHOULD include service specific accounting AVPs,
+				as described in Section 9.3.
+
+
+				Message Format
+
+				 <ACR> ::= < Diameter Header: 271, REQ, PXY >
+        				   < Session-Id >
+        				   { Origin-Host }
+        				   { Origin-Realm }
+        				   { Destination-Realm }
+        				   { Accounting-Record-Type }
+        				   { Accounting-Record-Number }
+        				   [ Acct-Application-Id ]
+        				   [ Vendor-Specific-Application-Id ]
+        				   [ User-Name ]
+        				   [ Destination-Host ]
+        				   [ Accounting-Sub-Session-Id ]
+        				   [ Acct-Session-Id ]
+        				   [ Acct-Multi-Session-Id ]
+        				   [ Acct-Interim-Interval ]
+        				   [ Accounting-Realtime-Required ]
+        				   [ Origin-State-Id ]
+        				   [ Event-Timestamp ]
+        				 * [ Proxy-Info ]
+        				 * [ Route-Record ]
+        				 * [ AVP ]
+			*/
+			struct dict_object * cmd;
+			struct dict_cmd_data data = { 
+					271, 					/* Code */
+					#if CC_ACCOUNTING != 271
+					#error "CC_ACCOUNTING definition mismatch"
+					#endif
+					"Accounting-Request", 			/* Name */
+					CMD_FLAG_REQUEST | CMD_FLAG_PROXIABLE | CMD_FLAG_ERROR,	/* Fixed flags */
+					CMD_FLAG_REQUEST | CMD_FLAG_PROXIABLE	/* Fixed flag values */
+					};
+			struct local_rules_definition rules[] = 
+						{ 	 {  "Session-Id", 			RULE_FIXED_HEAD, -1, 1 }
+						 	,{  "Origin-Host", 			RULE_REQUIRED,   -1, 1 }
+							,{  "Origin-Realm",			RULE_REQUIRED,   -1, 1 }
+							,{  "Destination-Realm",		RULE_REQUIRED,   -1, 1 }
+							,{  "Accounting-Record-Type",		RULE_REQUIRED,   -1, 1 }
+							,{  "Accounting-Record-Number",		RULE_REQUIRED,   -1, 1 }
+							,{  "Acct-Application-Id",		RULE_OPTIONAL,   -1, 1 }
+							,{  "Vendor-Specific-Application-Id",	RULE_OPTIONAL,   -1, 1 }
+							,{  "User-Name",			RULE_OPTIONAL,   -1, 1 }
+							,{  "Destination-Host",			RULE_OPTIONAL,   -1, 1 }
+							,{  "Accounting-Sub-Session-Id",	RULE_OPTIONAL,   -1, 1 }
+							,{  "Acct-Session-Id",			RULE_OPTIONAL,   -1, 1 }
+							,{  "Acct-Multi-Session-Id",		RULE_OPTIONAL,   -1, 1 }
+							,{  "Acct-Interim-Interval",		RULE_OPTIONAL,   -1, 1 }
+							,{  "Accounting-Realtime-Required",	RULE_OPTIONAL,   -1, 1 }
+							,{  "Origin-State-Id",			RULE_OPTIONAL,   -1, 1 }
+							,{  "Event-Timestamp",			RULE_OPTIONAL,   -1, 1 }
+							,{  "Proxy-Info",			RULE_OPTIONAL,   -1,-1 }
+							,{  "Route-Record",			RULE_OPTIONAL,   -1,-1 }
+						};
+			
+			CHECK_dict_new( DICT_COMMAND, &data , NULL, &cmd);
+			PARSE_loc_rules( rules, cmd );
+		}
+		
+		/* Accounting-Answer */
+		{
+			/*
+				The Accounting-Answer (ACA) command, indicated by the Command-Code
+				field set to 271 and the Command Flags' 'R' bit cleared, is used to
+				acknowledge an Accounting-Request command.  The Accounting-Answer
+				command contains the same Session-Id as the corresponding request.
+
+				Only the target Diameter Server, known as the home Diameter Server,
+				SHOULD respond with the Accounting-Answer command.
+
+				One of Acct-Application-Id and Vendor-Specific-Application-Id AVPs
+				MUST be present.  If the Vendor-Specific-Application-Id grouped AVP
+				is present, it MUST contain an Acct-Application-Id AVP.
+
+				The AVP listed below SHOULD include service specific accounting AVPs,
+				as described in Section 9.3.
+
+
+				Message Format
+
+				 <ACA> ::= < Diameter Header: 271, PXY >
+        				   < Session-Id >
+        				   { Result-Code }
+        				   { Origin-Host }
+        				   { Origin-Realm }
+        				   { Accounting-Record-Type }
+        				   { Accounting-Record-Number }
+        				   [ Acct-Application-Id ]
+        				   [ Vendor-Specific-Application-Id ]
+        				   [ User-Name ]
+        				   [ Accounting-Sub-Session-Id ]
+        				   [ Acct-Session-Id ]
+        				   [ Acct-Multi-Session-Id ]
+        				   [ Error-Message ]
+        				   [ Error-Reporting-Host ]
+        				   [ Failed-AVP ]
+        				   [ Acct-Interim-Interval ]
+        				   [ Accounting-Realtime-Required ]
+        				   [ Origin-State-Id ]
+        				   [ Event-Timestamp ]
+        				 * [ Proxy-Info ]
+        				 * [ AVP ]
+			*/
+			struct dict_object * cmd;
+			struct dict_cmd_data data = { 
+					271, 					/* Code */
+					#if CC_ACCOUNTING != 271
+					#error "CC_ACCOUNTING definition mismatch"
+					#endif
+					"Accounting-Answer", 			/* Name */
+					CMD_FLAG_REQUEST | CMD_FLAG_PROXIABLE,	/* Fixed flags */
+							   CMD_FLAG_PROXIABLE	/* Fixed flag values */
+					};
+			struct local_rules_definition rules[] = 
+						{ 	 {  "Session-Id", 			RULE_FIXED_HEAD, -1, 1 }
+ 							,{  "Result-Code", 			RULE_REQUIRED,   -1, 1 }
+						 	,{  "Origin-Host", 			RULE_REQUIRED,   -1, 1 }
+							,{  "Origin-Realm",			RULE_REQUIRED,   -1, 1 }
+							,{  "Accounting-Record-Type",		RULE_REQUIRED,   -1, 1 }
+							,{  "Accounting-Record-Number",		RULE_REQUIRED,   -1, 1 }
+							,{  "Acct-Application-Id",		RULE_OPTIONAL,   -1, 1 }
+							,{  "Vendor-Specific-Application-Id",	RULE_OPTIONAL,   -1, 1 }
+							,{  "User-Name",			RULE_OPTIONAL,   -1, 1 }
+							,{  "Accounting-Sub-Session-Id",	RULE_OPTIONAL,   -1, 1 }
+							,{  "Acct-Session-Id",			RULE_OPTIONAL,   -1, 1 }
+							,{  "Acct-Multi-Session-Id",		RULE_OPTIONAL,   -1, 1 }
+							,{  "Error-Message",			RULE_OPTIONAL,   -1, 1 }
+							,{  "Error-Reporting-Host",		RULE_OPTIONAL,   -1, 1 }
+							,{  "Failed-AVP",			RULE_OPTIONAL,   -1, 1 }
+							,{  "Acct-Interim-Interval",		RULE_OPTIONAL,   -1, 1 }
+							,{  "Accounting-Realtime-Required",	RULE_OPTIONAL,   -1, 1 }
+							,{  "Origin-State-Id",			RULE_OPTIONAL,   -1, 1 }
+							,{  "Event-Timestamp",			RULE_OPTIONAL,   -1, 1 }
+							,{  "Proxy-Info",			RULE_OPTIONAL,   -1,-1 }
+						};
+			
+			CHECK_dict_new( DICT_COMMAND, &data , NULL, &cmd);
+			PARSE_loc_rules( rules, cmd );
+		}
+	}
+
+	return 0;
+}
Index: libfdcore/endpoints.c
===================================================================
--- libfdcore/endpoints.c	(revision 658)
+++ libfdcore/endpoints.c	(revision 658)
@@ -0,0 +1,361 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@nict.go.jp>							 *
+*													 *
+* Copyright (c) 2010, 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 "fdcore-internal.h"
+
+
+/* Add an endpoint information in a list */
+int fd_ep_add_merge( struct fd_list * list, sSA * sa, socklen_t sl, uint32_t flags )
+{
+	struct fd_endpoint * ep;
+	struct fd_list * li;
+	union {
+		sSA * sa;
+		sSA4 *sin;
+		sSA6 *sin6;
+	} ptr;
+	in_port_t * port;
+	int cmp = -1;
+	
+	TRACE_ENTRY("%p %p %u %x", list, sa, sl, flags);
+	CHECK_PARAMS( list && sa && (sl <= sizeof(sSS)) );
+	
+	if (list->next == NULL) {
+		/* the list is not initialized yet, do it */
+		fd_list_init(list, NULL);
+	}
+	
+	if (TRACE_BOOL(ANNOYING + 1)) {
+		TRACE_DEBUG(ANNOYING, "  DEBUG:fd_ep_add_merge  Current list:");
+		fd_ep_dump( 4, list );
+		TRACE_DEBUG(ANNOYING, "  DEBUG:fd_ep_add_merge  Adding:");
+		fd_log_debug("    ");
+		sSA_DUMP_NODE_SERV( sa, NI_NUMERICHOST | NI_NUMERICSERV );
+		fd_log_debug(" {%s%s%s%s}\n", 
+				(flags & EP_FL_CONF) 	? "C" : "-",
+				(flags & EP_FL_DISC)	    ? "D" : "-",
+				(flags & EP_FL_ADV)	    ? "A" : "-",
+				(flags & EP_FL_LL)	    ? "L" : "-",
+				(flags & EP_FL_PRIMARY)     ? "P" : "-");
+	}
+	ptr.sa = sa;
+	
+	/* Filter out a bunch of invalid addresses */
+	switch (sa->sa_family) {
+		case AF_INET:
+			if (! (flags & EP_ACCEPTALL)) {
+				if (IN_IS_ADDR_UNSPECIFIED(&ptr.sin->sin_addr) 
+				 || IN_IS_ADDR_LOOPBACK(&ptr.sin->sin_addr)
+				    /* the next one filters both EXPERIMENTAL, BADCLASS and MULTICAST. */
+				 || ((ntohl(ptr.sin->sin_addr.s_addr) & 0xe0000000) == 0xe0000000)
+				 || (ptr.sin->sin_addr.s_addr == INADDR_BROADCAST)) {
+					if (TRACE_BOOL(ANNOYING + 1)) {
+						TRACE_DEBUG(ANNOYING, "  DEBUG:fd_ep_add_merge  Address was ignored, not added.");
+					}
+					return 0;
+				}
+			}
+			port = &ptr.sin->sin_port;
+			break;
+
+		case AF_INET6:
+			if (! (flags & EP_ACCEPTALL)) {
+				if (IN6_IS_ADDR_UNSPECIFIED(&ptr.sin6->sin6_addr) 
+				 || IN6_IS_ADDR_LOOPBACK(&ptr.sin6->sin6_addr)
+				 || IN6_IS_ADDR_MULTICAST(&ptr.sin6->sin6_addr)
+				 || IN6_IS_ADDR_LINKLOCAL(&ptr.sin6->sin6_addr)
+				 || IN6_IS_ADDR_SITELOCAL(&ptr.sin6->sin6_addr)) {
+					if (TRACE_BOOL(ANNOYING + 1)) {
+						TRACE_DEBUG(ANNOYING, "  DEBUG:fd_ep_add_merge  Address was ignored, not added.");
+					}
+					return 0;
+				}
+			}
+			port = &ptr.sin6->sin6_port;
+			break;
+
+		default:
+			if (TRACE_BOOL(ANNOYING + 1)) {
+				TRACE_DEBUG(ANNOYING, "  DEBUG:fd_ep_add_merge  Address family was unknown, not added.");
+			}
+			return 0;
+	}
+
+	/* remove the ACCEPTALL flag */
+	flags &= ~EP_ACCEPTALL;
+	
+	/* Search place in the list */
+	for (li = list->next; li != list; li = li->next) {
+		ep = (struct fd_endpoint *)li;
+		in_port_t * ep_port;
+		
+		/* First, compare the address family */
+		if (ep->sa.sa_family < sa->sa_family)
+			continue;
+		if (ep->sa.sa_family > sa->sa_family)
+			break;
+		
+		/* Then compare the address field */
+		switch (sa->sa_family) {
+			case AF_INET:
+				cmp = memcmp(&ep->sin.sin_addr, &ptr.sin->sin_addr, sizeof(struct in_addr));
+				ep_port = &ep->sin.sin_port;
+				break;
+			case AF_INET6:
+				cmp = memcmp(&ep->sin6.sin6_addr, &ptr.sin6->sin6_addr, sizeof(struct in6_addr));
+				ep_port = &ep->sin6.sin6_port;
+				break;
+			default:
+				ASSERT( 0 ); /* we got a different value previously in this same function */
+		}
+		if (cmp < 0)
+			continue;
+		if (cmp > 0)
+			break;
+		
+		/* Finally compare the port, only if not 0 */
+		if (*port == 0)
+			break;
+		if (*ep_port == 0) {
+			/* save the port information in the list, and break */
+			*ep_port = *port;
+			break;
+		}
+		if (*ep_port < *port)
+			continue;
+		if (*ep_port > *port)
+			cmp = 1;
+		break;
+	}
+	
+	if (cmp) {
+		/* new item to be added */
+		CHECK_MALLOC( ep = malloc(sizeof(struct fd_endpoint)) );
+		memset(ep, 0, sizeof(struct fd_endpoint));
+		fd_list_init(&ep->chain, NULL);
+		memcpy(&ep->ss, sa, sl);
+		
+		/* Insert in the list */
+		fd_list_insert_before(li, &ep->chain);
+	}
+	
+	/* Merge the flags */
+	ep->flags |= flags;
+	
+	if (TRACE_BOOL(ANNOYING + 1)) {
+		TRACE_DEBUG(ANNOYING, "  DEBUG:fd_ep_add_merge  New list:");
+		fd_ep_dump( 4, list );
+	}
+	return 0;
+}
+
+/* Delete endpoints that do not have a matching flag from a list (0: delete all endpoints) */
+int fd_ep_filter( struct fd_list * list, uint32_t flags )
+{
+	struct fd_list * li;
+	
+	TRACE_ENTRY("%p %x", list, flags);
+	CHECK_PARAMS(list);
+	
+	if (TRACE_BOOL(ANNOYING + 1)) {
+		TRACE_DEBUG(ANNOYING, "  DEBUG:fd_ep_filter  Filter this list for flags %x:", flags);
+		fd_ep_dump( 4, list );
+	}
+	for (li = list->next; li != list; li = li->next) {
+		struct fd_endpoint * ep = (struct fd_endpoint *)li;
+		
+		if (! (ep->flags & flags)) {
+			li = li->prev;
+			fd_list_unlink(&ep->chain);
+			free(ep);
+		}
+	}
+	
+	if (TRACE_BOOL(ANNOYING + 1)) {
+		TRACE_DEBUG(ANNOYING, "  DEBUG:fd_ep_filter  Resulting list:");
+		fd_ep_dump( 4, list );
+	}
+	return 0;
+}
+
+/* Keep only endpoints of the same family as af */
+int fd_ep_filter_family( struct fd_list * list, int af )
+{
+	struct fd_list * li;
+	
+	TRACE_ENTRY("%p %d", list, af);
+	CHECK_PARAMS(list);
+	
+	if (TRACE_BOOL(ANNOYING + 1)) {
+		TRACE_DEBUG(ANNOYING, "  DEBUG:fd_ep_filter_family  Filter this list for family %d:", af);
+		fd_ep_dump( 4, list );
+	}
+	for (li = list->next; li != list; li = li->next) {
+		struct fd_endpoint * ep = (struct fd_endpoint *)li;
+		
+		if (ep->sa.sa_family != af) {
+			li = li->prev;
+			fd_list_unlink(&ep->chain);
+			free(ep);
+		}
+	}
+	
+	if (TRACE_BOOL(ANNOYING + 1)) {
+		TRACE_DEBUG(ANNOYING, "  DEBUG:fd_ep_filter_family  Resulting list:");
+		fd_ep_dump( 4, list );
+	}
+	return 0;
+}
+
+/* Remove any endpoint from the exclude list in the list */
+int fd_ep_filter_list( struct fd_list * list, struct fd_list * exclude_list )
+{
+	struct fd_list * li_out, *li_ex, *li;
+	struct fd_endpoint * out, * ex;
+	
+	TRACE_ENTRY("%p %p", list, exclude_list);
+	CHECK_PARAMS(list && exclude_list);
+	
+	/* initialize. Both lists are ordered */
+	li_out = list->next;
+	li_ex = exclude_list->next;
+	
+	if (TRACE_BOOL(ANNOYING + 1)) {
+		TRACE_DEBUG(ANNOYING, "  DEBUG:fd_ep_filter_list  Filter this list ");
+		fd_ep_dump( 4, list );
+		TRACE_DEBUG(ANNOYING, "  DEBUG:fd_ep_filter_list  Removing from list");
+		fd_ep_dump( 6, exclude_list );
+	}
+	/* Now browse both lists in parallel */
+	while ((li_out != list) && (li_ex != exclude_list)) {
+		int cmp;
+
+		out = (struct fd_endpoint *)li_out;
+		ex = (struct fd_endpoint *)li_ex;
+
+		/* Compare the next elements families */
+		if (out->sa.sa_family < ex->sa.sa_family) {
+			li_out = li_out->next;
+			continue;
+		}
+		if (out->sa.sa_family > ex->sa.sa_family) {
+			li_ex = li_ex->next;
+			continue;
+		}
+
+		/* Then compare the address fields */
+		switch (out->sa.sa_family) {
+			case AF_INET:
+				cmp = memcmp(&out->sin.sin_addr, &ex->sin.sin_addr, sizeof(struct in_addr));
+				break;
+			case AF_INET6:
+				cmp = memcmp(&out->sin6.sin6_addr, &ex->sin6.sin6_addr, sizeof(struct in6_addr));
+				break;
+			default:
+				/* Filter this out */
+				cmp = 0;
+		}
+		if (cmp < 0) {
+			li_out = li_out->next;
+			continue;
+		}
+		if (cmp > 0) {
+			li_ex = li_ex->next;
+			continue;
+		}
+	
+		/* We remove this element then loop */
+		li = li_out;
+		li_out = li->next;
+		fd_list_unlink(li);
+		free(li);
+	}
+	
+	if (TRACE_BOOL(ANNOYING + 1)) {
+		TRACE_DEBUG(ANNOYING, "  DEBUG:fd_ep_filter_list  Resulting list:");
+		fd_ep_dump( 4, list );
+	}
+	return 0;
+
+}
+
+
+/* Reset the given flag(s) from all items in the list */
+int fd_ep_clearflags( struct fd_list * list, uint32_t flags )
+{
+	struct fd_list * li;
+	
+	TRACE_ENTRY("%p %x", list, flags);
+	CHECK_PARAMS(list);
+	
+	for (li = list->next; li != list; li = li->next) {
+		struct fd_endpoint * ep = (struct fd_endpoint *)li;
+		ep->flags &= ~flags;
+		if (ep->flags == 0) {
+			li = li->prev;
+			fd_list_unlink(&ep->chain);
+			free(ep);
+		}
+	}
+	
+	return 0;
+}
+
+void fd_ep_dump_one( char * prefix, struct fd_endpoint * ep, char * suffix )
+{
+	if (prefix)
+		fd_log_debug("%s", prefix);
+	
+	sSA_DUMP_NODE_SERV( &ep->sa, NI_NUMERICHOST | NI_NUMERICSERV );
+	fd_log_debug(" {%s%s%s%s}", 
+			(ep->flags & EP_FL_CONF) 	? "C" : "-",
+			(ep->flags & EP_FL_DISC) 	? "D" : "-",
+			(ep->flags & EP_FL_ADV) 	? "A" : "-",
+			(ep->flags & EP_FL_LL) 		? "L" : "-",
+			(ep->flags & EP_FL_PRIMARY) 	? "P" : "-");
+	if (suffix)
+		fd_log_debug("%s", suffix);
+}
+
+void fd_ep_dump( int indent, struct fd_list * eps )
+{
+	struct fd_list * li;
+	for (li = eps->next; li != eps; li = li->next) {
+		struct fd_endpoint * ep = (struct fd_endpoint *)li;
+		fd_log_debug("%*s", indent, "");
+		fd_ep_dump_one( NULL, ep, "\n" );
+	}
+}
+	
Index: libfdcore/events.c
===================================================================
--- libfdcore/events.c	(revision 658)
+++ libfdcore/events.c	(revision 658)
@@ -0,0 +1,236 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@nict.go.jp>							 *
+*													 *
+* Copyright (c) 2009, WIDE Project and NICT								 *
+* All rights reserved.											 *
+* 													 *
+* Redistribution and use of this software in source and binary forms, with or without modification, are  *
+* permitted provided that the following conditions are met:						 *
+* 													 *
+* * Redistributions of source code must retain the above 						 *
+*   copyright notice, this list of conditions and the 							 *
+*   following disclaimer.										 *
+*    													 *
+* * Redistributions in binary form must reproduce the above 						 *
+*   copyright notice, this list of conditions and the 							 *
+*   following disclaimer in the documentation and/or other						 *
+*   materials provided with the distribution.								 *
+* 													 *
+* * Neither the name of the WIDE Project or NICT nor the 						 *
+*   names of its contributors may be used to endorse or 						 *
+*   promote products derived from this software without 						 *
+*   specific prior written permission of WIDE Project and 						 *
+*   NICT.												 *
+* 													 *
+* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED *
+* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
+* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR *
+* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 	 *
+* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 	 *
+* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR *
+* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF   *
+* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.								 *
+*********************************************************************************************************/
+
+#include "fdcore-internal.h"
+
+/* Events are a subset of fifo queues, with a known type */
+
+int fd_event_send(struct fifo *queue, int code, size_t datasz, void * data)
+{
+	struct fd_event * ev;
+	CHECK_MALLOC( ev = malloc(sizeof(struct fd_event)) );
+	ev->code = code;
+	ev->size = datasz;
+	ev->data = data;
+	CHECK_FCT( fd_fifo_post(queue, &ev) );
+	return 0;
+}
+
+int fd_event_get(struct fifo *queue, int *code, size_t *datasz, void ** data)
+{
+	struct fd_event * ev;
+	CHECK_FCT( fd_fifo_get(queue, &ev) );
+	if (code)
+		*code = ev->code;
+	if (datasz)
+		*datasz = ev->size;
+	if (data)
+		*data = ev->data;
+	free(ev);
+	return 0;
+}
+
+int fd_event_timedget(struct fifo *queue, struct timespec * timeout, int timeoutcode, int *code, size_t *datasz, void ** data)
+{
+	struct fd_event * ev;
+	int ret = 0;
+	ret = fd_fifo_timedget(queue, &ev, timeout);
+	if (ret == ETIMEDOUT) {
+		if (code)
+			*code = timeoutcode;
+		if (datasz)
+			*datasz = 0;
+		if (data)
+			*data = NULL;
+	} else {
+		CHECK_FCT( ret );
+		if (code)
+			*code = ev->code;
+		if (datasz)
+			*datasz = ev->size;
+		if (data)
+			*data = ev->data;
+		free(ev);
+	}
+	return 0;
+}
+
+void fd_event_destroy(struct fifo **queue, void (*free_cb)(void * data))
+{
+	struct fd_event * ev;
+	/* Purge all events, and free the associated data if any */
+	while (fd_fifo_tryget( *queue, &ev ) == 0) {
+		(*free_cb)(ev->data);
+		free(ev);
+	}
+	CHECK_FCT_DO( fd_fifo_del(queue), /* continue */ );
+	return ;
+} 
+
+const char * fd_ev_str(int event)
+{
+	switch (event) {
+	#define case_str( _val )\
+		case _val : return #_val
+		case_str(FDEV_TERMINATE);
+		case_str(FDEV_DUMP_DICT);
+		case_str(FDEV_DUMP_EXT);
+		case_str(FDEV_DUMP_SERV);
+		case_str(FDEV_DUMP_QUEUES);
+		case_str(FDEV_DUMP_CONFIG);
+		case_str(FDEV_DUMP_PEERS);
+		case_str(FDEV_TRIGGER);
+		
+		default:
+			TRACE_DEBUG(FULL, "Unknown event : %d", event);
+			return "Unknown event";
+	}
+}
+
+/**********************************************************************/
+/* Trigged events */
+/* This allows extensions to register for / send triggers to other extensions */
+/* It is used for example for users interactions (through signals or ...) */
+
+/* Because the number of triggers is not expected to grow, we use a simple ordered chained list */
+static pthread_rwlock_t trig_rwl = PTHREAD_RWLOCK_INITIALIZER;
+static struct fd_list   trig_list = FD_LIST_INITIALIZER(trig_list); /* The list of triggers ordered by trigger value */
+struct trig_item {
+	struct fd_list 	chain;
+	int  		trig_value;
+	const char * 	trig_module;
+	void 		(*cb)(void);
+};	
+
+/* Add a new entry in the trigger list */
+int fd_event_trig_regcb(int trigger_val, const char * module, void (*cb)(void))
+{
+	struct trig_item * ti;
+	struct fd_list * li;
+	
+	TRACE_ENTRY("%d %p %p", trigger_val, module, cb);
+	CHECK_PARAMS( trigger_val && cb );
+	
+	/* Create a new trig_item */
+	CHECK_MALLOC( ti = malloc(sizeof(struct trig_item)) );
+	memset(ti, 0, sizeof(struct trig_item));
+	fd_list_init(&ti->chain, ti);
+	ti->trig_value = trigger_val;
+	ti->trig_module = module;
+	ti->cb = cb;
+	
+	/* Now insert in the list */
+	CHECK_POSIX( pthread_rwlock_wrlock(&trig_rwl) );
+	
+	for (li = trig_list.next; li != &trig_list; li = li->next) {
+		struct trig_item *t = li->o;
+		if (t->trig_value >= trigger_val)
+			break;
+	}
+	
+	fd_list_insert_before(li, &ti->chain);
+	
+	CHECK_POSIX( pthread_rwlock_unlock(&trig_rwl) );
+
+	return 0;
+}
+
+void fd_event_trig_dump()
+{
+	struct fd_list * li;
+	if (!TRACE_BOOL(FULL))
+		return;
+	
+	CHECK_POSIX_DO( pthread_rwlock_rdlock(&trig_rwl),  );
+	
+	for (li = trig_list.next; li != &trig_list; li = li->next) {
+		struct trig_item *t = li->o;
+		fd_log_debug("  Trigger %d, module '%s': %p\n", t->trig_value, t->trig_module, t->cb);
+	}
+	
+	CHECK_POSIX_DO( pthread_rwlock_unlock(&trig_rwl),  );
+}
+
+static void *call_cb_detached(void * arg)
+{
+	void (*cb)(void) = arg;
+	fd_log_threadname("Trig'd callback thread");
+	TRACE_ENTRY("%p", arg);
+	(*cb)();
+	TRACE_DEBUG(ANNOYING, "Callback %p completed", cb);
+}
+
+int fd_event_trig_call_cb(int trigger_val)
+{
+	struct fd_list * li;
+	pthread_attr_t detached;
+	pthread_t th;
+	
+	CHECK_POSIX( pthread_attr_init(&detached) );
+	CHECK_POSIX( pthread_attr_setdetachstate(&detached, PTHREAD_CREATE_DETACHED) );
+	
+	CHECK_POSIX( pthread_rwlock_rdlock(&trig_rwl)  );
+	
+	for (li = trig_list.next; li != &trig_list; li = li->next) {
+		struct trig_item *t = li->o;
+		if (t->trig_value == trigger_val) {
+			TRACE_DEBUG(FULL, "Trigger %d: Calling %p in %s", t->trig_value, t->cb, t->trig_module);
+			CHECK_POSIX_DO( pthread_create( &th, &detached, call_cb_detached, t->cb ), break );
+		}
+		if (t->trig_value > trigger_val)
+			break;
+	}
+	
+	CHECK_POSIX( pthread_rwlock_unlock(&trig_rwl)  );
+	
+	return 0;
+}
+
+int fd_event_trig_fini(void) {
+	
+	TRACE_ENTRY("");
+	
+	CHECK_POSIX( pthread_rwlock_wrlock(&trig_rwl) );
+	
+	while (!FD_IS_LIST_EMPTY(&trig_list)) {
+		struct fd_list * li = trig_list.next;
+		fd_list_unlink(li);
+		free(li);
+	}
+	
+	CHECK_POSIX( pthread_rwlock_unlock(&trig_rwl) );
+	
+	return 0;
+}
Index: libfdcore/extensions.c
===================================================================
--- libfdcore/extensions.c	(revision 658)
+++ libfdcore/extensions.c	(revision 658)
@@ -0,0 +1,191 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@nict.go.jp>							 *
+*													 *
+* Copyright (c) 2010, 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 "fdcore-internal.h"
+
+#include <dlfcn.h>	/* We may use libtool's <ltdl.h> later for better portability.... */
+
+/* plugins management */
+
+/* List of extensions to load, from the configuration parsing */
+struct fd_ext_info {
+	struct fd_list	chain;		/* link in the list */
+	char 		*filename;	/* extension filename. must be a dynamic library with fd_ext_init symbol. */
+	char 		*conffile;	/* optional configuration file name for the extension */
+	void 		*handler;	/* object returned by dlopen() */
+	void		(*fini)(void);	/* optional address of the fd_ext_fini callback */
+};
+
+/* list of extensions */
+static struct fd_list ext_list = FD_LIST_INITIALIZER(ext_list);
+
+/* Add new extension */
+int fd_ext_add( char * filename, char * conffile )
+{
+	struct fd_ext_info * new;
+	
+	TRACE_ENTRY("%p(%s) %p(%s)", filename, filename?filename:"", conffile, conffile?conffile:"");
+	
+	/* Check the filename is valid */
+	CHECK_PARAMS( filename );
+	
+	/* Create a new object in the list */
+	CHECK_MALLOC(  new = malloc( sizeof(struct fd_ext_info) )  );
+	memset(new, 0, sizeof(struct fd_ext_info));
+	fd_list_init(&new->chain, NULL);
+	new->filename = filename;
+	new->conffile = conffile;
+	fd_list_insert_before( &ext_list, &new->chain );
+	TRACE_DEBUG (FULL, "Extension %s added to the list.", filename);
+	return 0;
+}
+
+/* Dump the list */
+void fd_ext_dump(void)
+{
+	struct fd_list * li;
+	
+	fd_log_debug("Dumping extensions list :\n");
+	
+	for (li = ext_list.next; li != &ext_list; li = li->next)
+	{
+		struct fd_ext_info * ext = (struct fd_ext_info *)li;
+		fd_log_debug(" - '%s'[%s] is %sloaded\n", ext->filename, ext->conffile?:"no conf", ext->handler ? "" : "not ");
+	}
+}
+
+/* Load all extensions in the list */
+int fd_ext_load()
+{
+	int ret;
+	int (*fd_ext_init)(int, int, char *) = NULL;
+	struct fd_list * li;
+	
+	TRACE_ENTRY();
+	
+	/* Loop on all extensions */
+	for (li = ext_list.next; li != &ext_list; li = li->next)
+	{
+		struct fd_ext_info * ext = (struct fd_ext_info *)li;
+		TRACE_DEBUG (INFO, "Loading : %s", ext->filename);
+		
+		/* Load the extension */
+#ifndef DEBUG
+		ext->handler = dlopen(ext->filename, RTLD_LAZY | RTLD_GLOBAL);
+#else /* DEBUG */
+		/* We resolve immediatly so it's easier to find problems in ABI */
+		ext->handler = dlopen(ext->filename, RTLD_NOW | RTLD_GLOBAL);
+#endif /* DEBUG */
+		if (ext->handler == NULL) {
+			/* An error occured */
+			TRACE_DEBUG( NONE, "Loading of extension %s failed:\n %s\n", ext->filename, dlerror());
+			return EINVAL;
+		}
+		
+		/* Resolve the entry point of the extension */
+		fd_ext_init = ( int (*) (int, int, char *) )dlsym( ext->handler, "fd_ext_init" );
+		
+		if (fd_ext_init == NULL) {
+			/* An error occured */
+			TRACE_DEBUG( NONE, "Unable to resolve symbol 'fd_ext_init' for extension %s:\n %s\n", ext->filename, dlerror());
+			return EINVAL;
+		}
+		
+		/* Resolve the exit point of the extension, which is optional for extensions */
+		ext->fini = ( void (*) (void) )dlsym( ext->handler, "fd_ext_fini" );
+		
+		if (ext->fini == NULL) {
+			/* Not provided */
+			TRACE_DEBUG (FULL, "Extension [%s] has no fd_ext_fini function.", ext->filename);
+		} else {
+			/* Provided */
+			TRACE_DEBUG (FULL, "Extension [%s] fd_ext_fini has been resolved successfully.", ext->filename);
+		}
+		
+		/* Now call the entry point to initialize the extension */
+		ret = (*fd_ext_init)( FD_PROJECT_VERSION_MAJOR, FD_PROJECT_VERSION_MINOR, ext->conffile );
+		if (ret != 0) {
+			/* The extension was unable to load cleanly */
+			TRACE_DEBUG( NONE, "Extension %s returned an error during initialization: %s\n", ext->filename, strerror(ret));
+			return ret;
+		}
+		
+		/* Proceed to the next extension */
+	}
+
+	TRACE_DEBUG (INFO, "All extensions loaded.");
+	
+	/* We have finished. */
+	return 0;
+}
+
+/* Now unload the extensions and free the memory */
+int fd_ext_term( void ) 
+{
+	TRACE_ENTRY();
+	
+	/* Loop on all extensions, in FIFO order */
+	while (!FD_IS_LIST_EMPTY(&ext_list))
+	{
+		struct fd_list * li = ext_list.next;
+		struct fd_ext_info * ext = (struct fd_ext_info *)li;
+	
+		/* Unlink this element from the list */
+		fd_list_unlink(li);
+		
+		/* Call the exit point of the extension, if it was resolved */
+		if (ext->fini != NULL) {
+			TRACE_DEBUG (FULL, "Calling [%s]->fd_ext_fini function.", ext->filename);
+			(*ext->fini)();
+		}
+		
+		/* Now unload the extension */
+		if (ext->handler) {
+			TRACE_DEBUG (FULL, "Unloading %s", ext->filename);
+			if ( dlclose(ext->handler) != 0 ) {
+				TRACE_DEBUG (INFO, "Unloading [%s] failed : %s\n", ext->filename, dlerror());
+			}
+		}
+		
+		/* Free the object and continue */
+		free(ext->filename);
+		free(ext->conffile);
+		free(ext);
+	}
+	
+	/* We always return 0 since we would not handle an error anyway... */
+	return 0;
+}
+
Index: libfdcore/fdcore-internal.h
===================================================================
--- libfdcore/fdcore-internal.h	(revision 658)
+++ libfdcore/fdcore-internal.h	(revision 658)
@@ -0,0 +1,347 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@nict.go.jp>							 *
+*													 *
+* Copyright (c) 2010, 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 definitions for internal use in the freeDiameter core library */
+
+#ifndef _FDCORE_INTERNAL_H
+#define _FDCORE_INTERNAL_H
+
+#include <freeDiameter/freeDiameter-host.h>
+#include <freeDiameter/libfdcore.h>
+
+#ifdef DISABLE_SCTP
+#undef IPPROTO_SCTP
+#define IPPROTO_SCTP	(2 = 4) /* some compilation error to spot the references */
+#endif /* DISABLE_SCTP */
+
+/* Timeout for establishing a connection */
+#ifndef CNX_TIMEOUT
+#define  CNX_TIMEOUT	10	/* in seconds */
+#endif /* CNX_TIMEOUT */
+
+/* Timeout for receiving a CER after incoming connection is established */
+#ifndef INCNX_TIMEOUT
+#define  INCNX_TIMEOUT	 20	/* in seconds */
+#endif /* INCNX_TIMEOUT */
+
+/* Timeout for receiving a CEA after CER is sent */
+#ifndef CEA_TIMEOUT
+#define  CEA_TIMEOUT	10	/* in seconds */
+#endif /* CEA_TIMEOUT */
+
+/* The timeout value to wait for answer to a DPR */
+#ifndef DPR_TIMEOUT
+#define DPR_TIMEOUT 	15	/* in seconds */
+#endif /* DPR_TIMEOUT */
+
+/* The Vendor-Id to advertise in CER/CEA */
+#ifndef MY_VENDOR_ID
+#define MY_VENDOR_ID	0 	/* Reserved value to tell it must be ignored */
+#endif /* MY_VENDOR_ID */
+
+
+
+/* Configuration */
+int fd_conf_init();
+int fd_conf_deinit();
+void fd_conf_dump();
+int fd_conf_parse();
+int fddparse(struct fd_config * conf); /* yacc generated */
+
+/* Extensions */
+int fd_ext_add( char * filename, char * conffile );
+int fd_ext_load();
+void fd_ext_dump(void);
+int fd_ext_term(void);
+
+/* Messages */
+int fd_msg_init(void);
+struct dict_object * fd_dict_avp_OSI; /* Origin-State-Id */
+struct dict_object * fd_dict_cmd_CER; /* Capabilities-Exchange-Request */
+struct dict_object * fd_dict_cmd_DWR; /* Device-Watchdog-Request */
+struct dict_object * fd_dict_avp_DC;  /* Disconnect-Cause */
+struct dict_object * fd_dict_cmd_DPR; /* Disconnect-Peer-Request */
+
+/* Global message queues */
+extern struct fifo * fd_g_incoming; /* all messages received from other peers, except local messages (CER, ...) */
+extern struct fifo * fd_g_outgoing; /* messages to be sent to other peers on the network following routing procedure */
+extern struct fifo * fd_g_local; /* messages to be handled to local extensions */
+/* Message queues */
+int fd_queues_init(void);
+int fd_queues_fini(struct fifo ** queue);
+
+/* Trigged events */
+int fd_event_trig_call_cb(int trigger_val);
+int fd_event_trig_fini(void);
+
+/* Create all the dictionary objects defined in the Diameter base RFC. */
+int fd_dict_base_protocol(struct dictionary * dict);
+
+/* Routing */
+int fd_rtdisp_init(void);
+int fd_rtdisp_cleanstop(void);
+int fd_rtdisp_fini(void);
+int fd_rtdisp_cleanup(void);
+
+/* Sentinel for the sent requests list */
+struct sr_list {
+	struct fd_list 	srs; /* requests ordered by hop-by-hop id */
+	struct fd_list  exp; /* requests that have a timeout set, ordered by timeout */
+	pthread_mutex_t	mtx; /* mutex to protect these lists */
+	pthread_cond_t  cnd; /* cond var used by the thread that handles timeouts */
+	pthread_t       thr; /* the thread that handles timeouts (and calls the anscb) */
+};
+
+/* Peers */
+struct fd_peer { /* The "real" definition of the peer structure */
+	
+	/* The public data */
+	struct peer_hdr	 p_hdr;
+	
+	/* Eye catcher, EYEC_PEER */
+	int		 p_eyec;
+	#define EYEC_PEER	0x373C9336
+	
+	/* Origin of this peer object, for debug */
+	char		*p_dbgorig;
+	
+	/* Chaining in peers sublists */
+	struct fd_list	 p_actives;	/* list of peers in the STATE_OPEN state -- used by routing */
+	struct fd_list	 p_expiry; 	/* list of expiring peers, ordered by their timeout value */
+	struct timespec	 p_exp_timer;	/* Timestamp where the peer will expire; updated each time activity is seen on the peer (except DW) */
+	
+	/* Some flags influencing the peer state machine */
+	struct {
+		unsigned pf_responder	: 1;	/* The peer has been created to handle incoming connection */
+		unsigned pf_delete	: 1;	/* Destroy the peer when the connection is terminated */
+		
+		unsigned pf_dw_pending 	: 1;	/* A DWR message was sent and not answered yet */
+		
+		unsigned pf_cnx_pb	: 1;	/* The peer was disconnected because of watchdogs; must exchange 3 watchdogs before putting back to normal */
+		unsigned pf_reopen_cnt	: 2;	/* remaining DW to be exchanged after re-established connection */
+		
+	}		 p_flags;
+	
+	/* The events queue, peer state machine thread, timer for states timeouts */
+	struct fifo	*p_events;	/* The mutex of this FIFO list protects also the state and timer information */
+	pthread_t	 p_psm;
+	struct timespec	 p_psm_timer;
+	
+	/* Outgoing message queue, and thread managing sending the messages */
+	struct fifo	*p_tosend;
+	pthread_t	 p_outthr;
+	
+	/* The next hop-by-hop id value for the link, only read & modified by p_outthr */
+	uint32_t	 p_hbh;
+	
+	/* Sent requests (for fallback), list of struct sentreq ordered by hbh */
+	struct sr_list	 p_sr;
+	
+	/* Data for transitional states before the peer is in OPEN state */
+	struct {
+		struct cnxctx * p_receiver;	/* Only used in case of election */
+		struct msg    * p_cer;		/* Only used in case of election */
+		
+		pthread_t	p_ini_thr;	/* Initiator thread for establishing a connection */
+		struct fd_list  p_connparams;	/* The list of connection attempts, see p_cnx.c */
+	};
+	
+	/* connection context: socket and related information */
+	struct cnxctx	*p_cnxctx;
+	
+	/* Callback for peer validation after the handshake */
+	int		(*p_cb2)(struct peer_info *);
+	
+	/* Callback on initial connection success / failure after the peer was added */
+	void 		(*p_cb)(struct peer_info *, void *);
+	void 		*p_cb_data;
+	
+};
+#define CHECK_PEER( _p ) \
+	(((_p) != NULL) && (((struct fd_peer *)(_p))->p_eyec == EYEC_PEER))
+
+/* Events codespace for struct fd_peer->p_events */
+enum {
+	/* Dump all info about this peer in the debug log */
+	 FDEVP_DUMP_ALL = 1500
+	
+	/* request to terminate this peer : disconnect, requeue all messages */
+	,FDEVP_TERMINATE
+	
+	/* A connection object has received a message. (data contains the buffer) */
+	,FDEVP_CNX_MSG_RECV
+			 
+	/* A connection object has encountered an error (disconnected). */
+	,FDEVP_CNX_ERROR
+	
+	/* Endpoints of a connection have been changed (multihomed SCTP). */
+	,FDEVP_CNX_EP_CHANGE
+	
+	/* The connection is being shutdown (SCTP notification). */
+	,FDEVP_CNX_SHUTDOWN
+	
+	/* A new connection (with a CER) has been received */
+	,FDEVP_CNX_INCOMING
+	
+	/* A new connection has been established to the remote peer (event data is the cnxctx object) */
+	,FDEVP_CNX_ESTABLISHED
+	
+	/* A connection attempt (initiator side) has failed */
+	,FDEVP_CNX_FAILED
+	
+	/* The PSM state is expired */
+	,FDEVP_PSM_TIMEOUT
+	
+};
+#define CHECK_PEVENT( _e ) \
+	(((int)(_e) >= FDEVP_DUMP_ALL) && ((int)(_e) <= FDEVP_PSM_TIMEOUT))
+/* The following macro is actually called in p_psm.c -- another solution would be to declare it static inline */
+#define DECLARE_PEV_STR()				\
+const char * fd_pev_str(int event)			\
+{							\
+	switch (event) {				\
+		case_str(FDEVP_DUMP_ALL);		\
+		case_str(FDEVP_TERMINATE);		\
+		case_str(FDEVP_CNX_MSG_RECV);		\
+		case_str(FDEVP_CNX_ERROR);		\
+		case_str(FDEVP_CNX_EP_CHANGE);		\
+		case_str(FDEVP_CNX_INCOMING);		\
+		case_str(FDEVP_CNX_ESTABLISHED);	\
+		case_str(FDEVP_CNX_FAILED);		\
+		case_str(FDEVP_PSM_TIMEOUT);		\
+	}						\
+	TRACE_DEBUG(FULL, "Unknown event : %d", event);	\
+	return "Unknown event";				\
+}
+const char * fd_pev_str(int event);
+
+/* The data structure for FDEVP_CNX_INCOMING event */
+struct cnx_incoming {
+	struct msg	* cer;		/* the CER message received on this connection */
+	struct cnxctx	* cnx;		/* The connection context */
+	int  		  validate;	/* The peer is new, it must be validated (by an extension) or error CEA to be sent */
+};
+
+
+/* Functions */
+int  fd_peer_fini();
+void fd_peer_dump_list(int details);
+void fd_peer_dump(struct fd_peer * peer, int details);
+int  fd_peer_alloc(struct fd_peer ** ptr);
+int  fd_peer_free(struct fd_peer ** ptr);
+int fd_peer_handle_newCER( struct msg ** cer, struct cnxctx ** cnx );
+/* fd_peer_add declared in freeDiameter.h */
+int fd_peer_validate( struct fd_peer * peer );
+void fd_peer_failover_msg(struct fd_peer * peer);
+
+/* Peer expiry */
+int fd_p_expi_init(void);
+int fd_p_expi_fini(void);
+int fd_p_expi_update(struct fd_peer * peer );
+
+/* Peer state machine */
+int  fd_psm_start();
+int  fd_psm_begin(struct fd_peer * peer );
+int  fd_psm_terminate(struct fd_peer * peer, char * reason );
+void fd_psm_abord(struct fd_peer * peer );
+void fd_psm_next_timeout(struct fd_peer * peer, int add_random, int delay);
+int fd_psm_change_state(struct fd_peer * peer, int new_state);
+void fd_psm_cleanup(struct fd_peer * peer, int terminate);
+
+/* Peer out */
+int fd_out_send(struct msg ** msg, struct cnxctx * cnx, struct fd_peer * peer, uint32_t flags);
+int fd_out_start(struct fd_peer * peer);
+int fd_out_stop(struct fd_peer * peer);
+
+/* Initiating connections */
+int fd_p_cnx_init(struct fd_peer * peer);
+void fd_p_cnx_abort(struct fd_peer * peer, int cleanup_all);
+
+/* Peer sent requests cache */
+int fd_p_sr_store(struct sr_list * srlist, struct msg **req, uint32_t *hbhloc, uint32_t hbh_restore);
+int fd_p_sr_fetch(struct sr_list * srlist, uint32_t hbh, struct msg **req);
+int fd_p_sr_start(struct sr_list * srlist);
+int fd_p_sr_stop(struct sr_list * srlist);
+void fd_p_sr_failover(struct sr_list * srlist);
+
+/* Local Link messages (CER/CEA, DWR/DWA, DPR/DPA) */
+int fd_p_ce_msgrcv(struct msg ** msg, int req, struct fd_peer * peer);
+int fd_p_ce_handle_newCER(struct msg ** msg, struct fd_peer * peer, struct cnxctx ** cnx, int valid);
+int fd_p_ce_handle_newcnx(struct fd_peer * peer, struct cnxctx * initiator);
+int fd_p_ce_process_receiver(struct fd_peer * peer);
+void fd_p_ce_clear_cnx(struct fd_peer * peer, struct cnxctx ** cnx_kept);
+int fd_p_dw_handle(struct msg ** msg, int req, struct fd_peer * peer);
+int fd_p_dw_timeout(struct fd_peer * peer);
+int fd_p_dw_reopen(struct fd_peer * peer);
+int fd_p_dp_handle(struct msg ** msg, int req, struct fd_peer * peer);
+int fd_p_dp_initiate(struct fd_peer * peer, char * reason);
+
+/* Active peers -- routing process should only ever take the read lock, the write lock is managed by PSMs */
+extern struct fd_list fd_g_activ_peers;
+extern pthread_rwlock_t fd_g_activ_peers_rw; /* protect the list */
+
+
+/* Server sockets */
+void fd_servers_dump();
+int  fd_servers_start();
+int  fd_servers_stop();
+
+/* Connection contexts -- there are also definitions in cnxctx.h for the relevant files */
+struct cnxctx * fd_cnx_serv_tcp(uint16_t port, int family, struct fd_endpoint * ep);
+struct cnxctx * fd_cnx_serv_sctp(uint16_t port, struct fd_list * ep_list);
+int             fd_cnx_serv_listen(struct cnxctx * conn);
+struct cnxctx * fd_cnx_serv_accept(struct cnxctx * serv);
+struct cnxctx * fd_cnx_cli_connect_tcp(sSA * sa, socklen_t addrlen);
+struct cnxctx * fd_cnx_cli_connect_sctp(int no_ip6, uint16_t port, struct fd_list * list);
+int             fd_cnx_start_clear(struct cnxctx * conn, int loop);
+void		fd_cnx_sethostname(struct cnxctx * conn, char * hn);
+int             fd_cnx_handshake(struct cnxctx * conn, int mode, char * priority, void * alt_creds);
+char *          fd_cnx_getid(struct cnxctx * conn);
+int		fd_cnx_getproto(struct cnxctx * conn);
+int		fd_cnx_getTLS(struct cnxctx * conn);
+int             fd_cnx_getcred(struct cnxctx * conn, const gnutls_datum_t **cert_list, unsigned int *cert_list_size);
+int 		fd_cnx_get_local_eps(struct fd_list * list);
+int             fd_cnx_getremoteeps(struct cnxctx * conn, struct fd_list * eps);
+char *          fd_cnx_getremoteid(struct cnxctx * conn);
+int             fd_cnx_receive(struct cnxctx * conn, struct timespec * timeout, unsigned char **buf, size_t * len);
+int             fd_cnx_recv_setaltfifo(struct cnxctx * conn, struct fifo * alt_fifo); /* send FDEVP_CNX_MSG_RECV event to the fifo list */
+int             fd_cnx_send(struct cnxctx * conn, unsigned char * buf, size_t len, uint32_t flags);
+void            fd_cnx_destroy(struct cnxctx * conn);
+
+/* Flags for the fd_cnx_send function : */
+#define FD_CNX_ORDERED		(1 << 0)	/* All messages sent with this flag set will be delivered in the same order. No guarantee on other messages */
+#define FD_CNX_BROADCAST	(1 << 1)	/* The message is sent over all stream pairs, in case of SCTP. No effect on TCP */
+
+#endif /* _FDCORE_INTERNAL_H */
Index: libfdcore/fdd.l
===================================================================
--- libfdcore/fdd.l	(revision 658)
+++ libfdcore/fdd.l	(revision 658)
@@ -0,0 +1,153 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@nict.go.jp>							 *
+*													 *
+* Copyright (c) 2010, 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.								 *
+*********************************************************************************************************/
+
+/* Lex configuration parser.
+ *
+ * This file defines the token for parsing the daemon's configuration file
+ * Note that each extension has a separate independant configuration file.
+ *
+ * Note : This module is NOT thread-safe. All processing must be done from one thread only.
+ */
+%{
+/* Include the daemon's header files */
+#include "fdcore-internal.h"
+/* Include yacc tokens definitions */
+#include "fdd.tab.h"
+
+/* Update the column information */
+#ifdef DEBUG_LEX
+#define YY_USER_ACTION { 						\
+	yylloc->first_column = yylloc->last_column + 1; 		\
+	yylloc->last_column = yylloc->first_column + yyleng - 1;	\
+	TRACE_DEBUG(FULL, 						\
+		"(%d:%d-%d:%d) matched rule %d, length=%d, txt='%s'\n",	\
+		yylloc->first_line, yylloc->first_column, 		\
+		yylloc->last_line, yylloc->last_column, 		\
+		yy_act, yyleng, yytext); 				\
+}
+#else /* DEBUG_LEX */
+#define YY_USER_ACTION { 						\
+	yylloc->first_column = yylloc->last_column + 1; 		\
+	yylloc->last_column = yylloc->first_column + yyleng - 1;	\
+}
+#endif
+
+/* %option noinput ? */
+#define YY_NO_INPUT
+%}
+
+%option bison-bridge bison-locations
+%option noyywrap
+%option nounput
+
+/* Quoted string. Multilines do not match. */
+qstring		\"[^\"\n]*\"
+
+%%
+
+<*>\n			{ 
+				/* Update the line count */
+				yylloc->first_line++; 
+				yylloc->last_line++; 
+				yylloc->last_column=0; 
+			} 
+
+<*>([[:space:]]{-}[\n])+	; /* Eat all spaces, not new lines */
+<*>#.*$			; /* Eat all comments */
+
+{qstring}		{
+				/* First copy the string without the quotes for use in the yacc parser */
+				CHECK_MALLOC_DO(	yylval->string = strdup(yytext+1), /* This allocates one useless tail char but... it's easier :D */ 
+							return LEX_ERROR  );/* on error, trig an error in yacc parser */
+
+				yylval->string[yyleng-2] = '\0';
+				
+				/* the yacc parser will check the string is valid */
+				return QSTRING;
+			}
+			
+[[:digit:]]+		{
+				/* Convert this to an integer value */
+				int ret = sscanf(yytext, "%i", &yylval->integer);
+				if (ret != 1) {
+					/* No matching: an error occurred */
+					fprintf(stderr, "Unable to convert the value '%s' to a valid number: %s\n", yytext, strerror(errno));
+					return LEX_ERROR; /* trig an error in yacc parser */
+					/* Maybe we could REJECT instead of failing here? */
+				}
+				return INTEGER;
+			}
+				
+	/* Full words tokens (keywords) */
+(?i:"Identity")		{ return IDENTITY;	}
+(?i:"Realm")		{ return REALM;   	}
+(?i:"Port")		{ return PORT;    	}
+(?i:"SecPort")		{ return SECPORT;  	}
+(?i:"No_IPv6")		{ return NOIP6;		}
+(?i:"No_IP")		{ return NOIP;		}
+(?i:"No_TCP")		{ return NOTCP;		}
+(?i:"No_SCTP")		{ return NOSCTP;	}
+(?i:"Prefer_TCP")	{ return PREFERTCP;	}
+(?i:"TLS_old_method")	{ return OLDTLS;	}
+(?i:"SCTP_streams")	{ return SCTPSTREAMS;	}
+(?i:"AppServThreads")	{ return APPSERVTHREADS;}
+(?i:"ListenOn")		{ return LISTENON;	}
+(?i:"TcTimer")		{ return TCTIMER;	}
+(?i:"TwTimer")		{ return TWTIMER;	}
+(?i:"NoRelay")		{ return NORELAY;	}
+(?i:"LoadExtension")	{ return LOADEXT;	}
+(?i:"ConnectPeer")	{ return CONNPEER;	}
+(?i:"ConnectTo")	{ return CONNTO;	}
+(?i:"No_TLS")		{ return NOTLS;		}
+(?i:"TLS_Cred")		{ return TLS_CRED;	}
+(?i:"TLS_CA")		{ return TLS_CA;	}
+(?i:"TLS_CRL")		{ return TLS_CRL;	}
+(?i:"TLS_Prio")		{ return TLS_PRIO;	}
+(?i:"TLS_DH_bits")	{ return TLS_DH_BITS;	}
+(?i:"TLS_DH_file")	{ return TLS_DH_FILE;	}
+
+
+	/* Valid single characters for yyparse */
+<*>[=,:;{}]		{ return yytext[0]; }
+
+	/* Unrecognized token */
+<*>[[:alnum:]]+		|	/* This rule is only useful to print a complete token in error messages */
+	/* Unrecognized character */
+<*>.			{ 
+				fprintf(stderr, "Unrecognized text on line %d col %d: '%s'.\n", yylloc->first_line, yylloc->first_column, yytext);
+			 	return LEX_ERROR; 
+			}
+
+%%
Index: libfdcore/fdd.y
===================================================================
--- libfdcore/fdd.y	(revision 658)
+++ libfdcore/fdd.y	(revision 658)
@@ -0,0 +1,600 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@nict.go.jp>							 *
+*													 *
+* Copyright (c) 2010, 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.								 *
+*********************************************************************************************************/
+
+/* Yacc configuration parser.
+ *
+ * This file defines the grammar of the configuration file.
+ * Note that each extension has a separate independant configuration file.
+ *
+ * Note : This module is NOT thread-safe. All processing must be done from one thread only.
+ */
+
+/* For development only : */
+%debug 
+%error-verbose
+
+%parse-param {struct fd_config * conf}
+
+/* Keep track of location */
+%locations 
+%pure-parser
+
+%{
+#include "fdcore-internal.h"
+#include "fdd.tab.h"	/* bug : bison does not define the YYLTYPE before including this bloc, so... */
+
+/* The Lex parser prototype */
+int fddlex(YYSTYPE *lvalp, YYLTYPE *llocp);
+
+/* Function to report error */
+void yyerror (YYLTYPE *ploc, struct fd_config * conf, char const *s)
+{
+	if (ploc->first_line != ploc->last_line)
+		fprintf(stderr, "%s:%d.%d-%d.%d : %s\n", conf->cnf_file, ploc->first_line, ploc->first_column, ploc->last_line, ploc->last_column, s);
+	else if (ploc->first_column != ploc->last_column)
+		fprintf(stderr, "%s:%d.%d-%d : %s\n", conf->cnf_file, ploc->first_line, ploc->first_column, ploc->last_column, s);
+	else
+		fprintf(stderr, "%s:%d.%d : %s\n", conf->cnf_file, ploc->first_line, ploc->first_column, s);
+}
+
+int got_peer_noip = 0;
+int got_peer_noipv6 = 0;
+int got_peer_notcp = 0;
+int got_peer_nosctp = 0;
+
+struct peer_info fddpi;
+
+%}
+
+/* Values returned by lex for token */
+%union {
+	char 		 *string;	/* The string is allocated by strdup in lex.*/
+	int		  integer;	/* Store integer values */
+}
+
+/* In case of error in the lexical analysis */
+%token 		LEX_ERROR
+
+%token <string>	QSTRING
+%token <integer> INTEGER
+
+%type <string> 	extconf
+
+%token		IDENTITY
+%token		REALM
+%token		PORT
+%token		SECPORT
+%token		NOIP
+%token		NOIP6
+%token		NOTCP
+%token		NOSCTP
+%token		PREFERTCP
+%token		OLDTLS
+%token		NOTLS
+%token		SCTPSTREAMS
+%token		APPSERVTHREADS
+%token		LISTENON
+%token		TCTIMER
+%token		TWTIMER
+%token		NORELAY
+%token		LOADEXT
+%token		CONNPEER
+%token		CONNTO
+%token		TLS_CRED
+%token		TLS_CA
+%token		TLS_CRL
+%token		TLS_PRIO
+%token		TLS_DH_BITS
+%token		TLS_DH_FILE
+
+
+/* -------------------------------------- */
+%%
+
+	/* The grammar definition - Sections blocs. */
+conffile:		/* Empty is OK -- for simplicity here, we reject in daemon later */
+			| conffile identity
+			| conffile realm
+			| conffile tctimer
+			| conffile twtimer
+			| conffile port
+			| conffile secport
+			| conffile sctpstreams
+			| conffile listenon
+			| conffile norelay
+			| conffile appservthreads
+			| conffile noip
+			| conffile noip6
+			| conffile notcp
+			| conffile nosctp
+			| conffile prefertcp
+			| conffile oldtls
+			| conffile loadext
+			| conffile connpeer
+			| conffile tls_cred
+			| conffile tls_ca
+			| conffile tls_crl
+			| conffile tls_prio
+			| conffile tls_dh
+			| conffile errors
+			{
+				yyerror(&yylloc, conf, "An error occurred while parsing the configuration file");
+				return EINVAL;
+			}
+			;
+
+			/* Lexical or syntax error */
+errors:			LEX_ERROR
+			| error
+			;
+
+identity:		IDENTITY '=' QSTRING ';'
+			{
+				conf->cnf_diamid = $3;
+			}
+			;
+
+realm:			REALM '=' QSTRING ';'
+			{
+				conf->cnf_diamrlm = $3;
+			}
+			;
+
+tctimer:		TCTIMER '=' INTEGER ';'
+			{
+				CHECK_PARAMS_DO( ($3 > 0),
+					{ yyerror (&yylloc, conf, "Invalid value"); YYERROR; } );
+				conf->cnf_timer_tc = (unsigned int)$3;
+			}
+			;
+
+twtimer:		TWTIMER '=' INTEGER ';'
+			{
+				CHECK_PARAMS_DO( ($3 > 5),
+					{ yyerror (&yylloc, conf, "Invalid value"); YYERROR; } );
+				conf->cnf_timer_tw = (unsigned int)$3;
+			}
+			;
+
+port:			PORT '=' INTEGER ';'
+			{
+				CHECK_PARAMS_DO( ($3 > 0) && ($3 < 1<<16),
+					{ yyerror (&yylloc, conf, "Invalid value"); YYERROR; } );
+				conf->cnf_port = (uint16_t)$3;
+			}
+			;
+
+secport:		SECPORT '=' INTEGER ';'
+			{
+				CHECK_PARAMS_DO( ($3 > 0) && ($3 < 1<<16),
+					{ yyerror (&yylloc, conf, "Invalid value"); YYERROR; } );
+				conf->cnf_port_tls = (uint16_t)$3;
+			}
+			;
+
+sctpstreams:		SCTPSTREAMS '=' INTEGER ';'
+			{
+				CHECK_PARAMS_DO( ($3 > 0) && ($3 < 1<<16),
+					{ yyerror (&yylloc, conf, "Invalid value"); YYERROR; } );
+				conf->cnf_sctp_str = (uint16_t)$3;
+			}
+			;
+
+listenon:		LISTENON '=' QSTRING ';'
+			{
+				struct addrinfo hints, *ai;
+				int ret;
+				
+				memset(&hints, 0, sizeof(hints));
+				hints.ai_flags = AI_PASSIVE | AI_NUMERICHOST;
+				ret = getaddrinfo($3, NULL, &hints, &ai);
+				if (ret) { yyerror (&yylloc, conf, gai_strerror(ret)); YYERROR; }
+				CHECK_FCT_DO( fd_ep_add_merge( &conf->cnf_endpoints, ai->ai_addr, ai->ai_addrlen, EP_FL_CONF ), YYERROR );
+				freeaddrinfo(ai);
+				free($3);
+			}
+			;
+
+norelay:		NORELAY ';'
+			{
+				conf->cnf_flags.no_fwd = 1;
+			}
+			;
+
+appservthreads:		APPSERVTHREADS '=' INTEGER ';'
+			{
+				CHECK_PARAMS_DO( ($3 > 0) && ($3 < 1024),
+					{ yyerror (&yylloc, conf, "Invalid value"); YYERROR; } );
+				conf->cnf_dispthr = (uint16_t)$3;
+			}
+			;
+
+noip:			NOIP ';'
+			{
+				if (got_peer_noipv6) { 
+					yyerror (&yylloc, conf, "No_IP conflicts with a ConnectPeer directive No_IPv6."); 
+					YYERROR; 
+				}
+				conf->cnf_flags.no_ip4 = 1;
+			}
+			;
+
+noip6:			NOIP6 ';'
+			{
+				if (got_peer_noip) { 
+					yyerror (&yylloc, conf, "No_IP conflicts with a ConnectPeer directive No_IP."); 
+					YYERROR; 
+				}
+				conf->cnf_flags.no_ip6 = 1;
+			}
+			;
+
+notcp:			NOTCP ';'
+			{
+				#ifdef DISABLE_SCTP
+				yyerror (&yylloc, conf, "No_TCP cannot be specified for daemon compiled with DISABLE_SCTP option."); 
+				YYERROR; 
+				#endif
+				if (conf->cnf_flags.no_sctp)
+				{
+					yyerror (&yylloc, conf, "No_TCP conflicts with No_SCTP directive." ); 
+					YYERROR; 
+				}
+				if (got_peer_nosctp) { 
+					yyerror (&yylloc, conf, "No_TCP conflicts with a ConnectPeer directive No_SCTP."); 
+					YYERROR; 
+				}
+				conf->cnf_flags.no_tcp = 1;
+			}
+			;
+
+nosctp:			NOSCTP ';'
+			{
+				if (conf->cnf_flags.no_tcp)
+				{
+					yyerror (&yylloc, conf, "No_SCTP conflicts with No_TCP directive." ); 
+					YYERROR; 
+				}
+				if (got_peer_notcp) { 
+					yyerror (&yylloc, conf, "No_SCTP conflicts with a ConnectPeer directive No_TCP.");
+					YYERROR;
+				}
+				conf->cnf_flags.no_sctp = 1;
+			}
+			;
+
+prefertcp:		PREFERTCP ';'
+			{
+				conf->cnf_flags.pr_tcp = 1;
+			}
+			;
+
+oldtls:			OLDTLS ';'
+			{
+				conf->cnf_flags.tls_alg = 1;
+			}
+			;
+
+loadext:		LOADEXT '=' QSTRING extconf ';'
+			{
+				char * fname;
+				char * cfname;
+				FILE * fd;
+				
+				/* Try and open the extension file */
+				fname = $3;
+				fd = fopen(fname, "r");
+				if ((fd == NULL) && (*fname != '/')) {
+					char * bkp = fname;
+					CHECK_MALLOC_DO( fname = malloc( strlen(bkp) + strlen(DEFAULT_EXTENSIONS_PATH) + 2 ),
+						{ yyerror (&yylloc, conf, "Not enough memory"); YYERROR; } );
+					sprintf(fname, DEFAULT_EXTENSIONS_PATH "/%s", bkp);
+					free(bkp);
+					fd = fopen(fname, "r");
+				}
+				if (fd == NULL) {
+					int ret = errno;
+					TRACE_DEBUG(INFO, "Unable to open extension file %s for reading: %s\n", fname, strerror(ret));
+					yyerror (&yylloc, conf, "Error adding extension"); 
+					YYERROR;
+				}
+				fclose(fd);
+				
+				/* Try and open the configuration file (optional) */
+				cfname = $4;
+				if (cfname) {
+					fd = fopen(cfname, "r");
+					if ((fd == NULL) && (*cfname != '/')) {
+						char * test;
+						CHECK_MALLOC_DO( test = malloc( strlen(cfname) + strlen(DEFAULT_CONF_PATH) + 2 ),
+							{ yyerror (&yylloc, conf, "Not enough memory"); YYERROR; } );
+						sprintf(test, DEFAULT_CONF_PATH "/%s", cfname);
+						fd = fopen(test, "r");
+						if (fd) {
+							free(cfname);
+							cfname=test;
+						} else {
+							/* This is not an error, we allow an extension to wait for something else than a real conf file. */
+							free(test);
+						}
+					}
+					if (fd)
+						fclose(fd);
+				}
+				
+				CHECK_FCT_DO( fd_ext_add( fname, cfname ),
+					{ yyerror (&yylloc, conf, "Error adding extension"); YYERROR; } );
+			}
+			;
+			
+extconf:		/* empty */
+			{
+				$$ = NULL;
+			}
+			| ':' QSTRING
+			{
+				$$ = $2;
+			}
+			;
+			
+connpeer:		{
+				memset(&fddpi, 0, sizeof(fddpi));
+				fddpi.config.pic_flags.persist = PI_PRST_ALWAYS;
+				fd_list_init( &fddpi.pi_endpoints, NULL );
+			}
+			CONNPEER '=' QSTRING peerinfo ';'
+			{
+				fddpi.pi_diamid = $4;
+				CHECK_FCT_DO( fd_peer_add ( &fddpi, conf->cnf_file, NULL, NULL ),
+					{ yyerror (&yylloc, conf, "Error adding ConnectPeer information"); YYERROR; } );
+					
+				/* Now destroy any content in the structure */
+				free(fddpi.pi_diamid);
+				free(fddpi.config.pic_realm);
+				free(fddpi.config.pic_priority);
+				while (!FD_IS_LIST_EMPTY(&fddpi.pi_endpoints)) {
+					struct fd_list * li = fddpi.pi_endpoints.next;
+					fd_list_unlink(li);
+					free(li);
+				}
+			}
+			;
+			
+peerinfo:		/* empty */
+			| '{' peerparams '}'
+			;
+			
+peerparams:		/* empty */
+			| peerparams NOIP ';'
+			{
+				if ((conf->cnf_flags.no_ip6) || (fddpi.config.pic_flags.pro3 == PI_P3_IP)) { 
+					yyerror (&yylloc, conf, "No_IP conflicts with a No_IPv6 directive.");
+					YYERROR;
+				}
+				got_peer_noip++;
+				fddpi.config.pic_flags.pro3 = PI_P3_IPv6;
+			}
+			| peerparams NOIP6 ';'
+			{
+				if ((conf->cnf_flags.no_ip4) || (fddpi.config.pic_flags.pro3 == PI_P3_IPv6)) { 
+					yyerror (&yylloc, conf, "No_IPv6 conflicts with a No_IP directive.");
+					YYERROR;
+				}
+				got_peer_noipv6++;
+				fddpi.config.pic_flags.pro3 = PI_P3_IP;
+			}
+			| peerparams NOTCP ';'
+			{
+				#ifdef DISABLE_SCTP
+					yyerror (&yylloc, conf, "No_TCP cannot be specified in daemon compiled with DISABLE_SCTP option.");
+					YYERROR;
+				#endif
+				if ((conf->cnf_flags.no_sctp) || (fddpi.config.pic_flags.pro4 == PI_P4_TCP)) { 
+					yyerror (&yylloc, conf, "No_TCP conflicts with a No_SCTP directive.");
+					YYERROR;
+				}
+				got_peer_notcp++;
+				fddpi.config.pic_flags.pro4 = PI_P4_SCTP;
+			}
+			| peerparams NOSCTP ';'
+			{
+				if ((conf->cnf_flags.no_tcp) || (fddpi.config.pic_flags.pro4 == PI_P4_SCTP)) { 
+					yyerror (&yylloc, conf, "No_SCTP conflicts with a No_TCP directive.");
+					YYERROR;
+				}
+				got_peer_nosctp++;
+				fddpi.config.pic_flags.pro4 = PI_P4_TCP;
+			}
+			| peerparams PREFERTCP ';'
+			{
+				fddpi.config.pic_flags.alg = PI_ALGPREF_TCP;
+			}
+			| peerparams OLDTLS ';'
+			{
+				fddpi.config.pic_flags.sec |= PI_SEC_TLS_OLD;
+			}
+			| peerparams NOTLS ';'
+			{
+				fddpi.config.pic_flags.sec |= PI_SEC_NONE;
+			}
+			| peerparams REALM '=' QSTRING ';'
+			{
+				fddpi.config.pic_realm = $4;
+			}
+			| peerparams PORT '=' INTEGER ';'
+			{
+				CHECK_PARAMS_DO( ($4 > 0) && ($4 < 1<<16),
+					{ yyerror (&yylloc, conf, "Invalid port value"); YYERROR; } );
+				fddpi.config.pic_port = (uint16_t)$4;
+			}
+			| peerparams TCTIMER '=' INTEGER ';'
+			{
+				fddpi.config.pic_tctimer = $4;
+			}
+			| peerparams TWTIMER '=' INTEGER ';'
+			{
+				fddpi.config.pic_twtimer = $4;
+			}
+			| peerparams TLS_PRIO '=' QSTRING ';'
+			{
+				fddpi.config.pic_priority = $4;
+			}
+			| peerparams CONNTO '=' QSTRING ';'
+			{
+				struct addrinfo hints, *ai;
+				int ret;
+				int disc = 0;
+				
+				memset(&hints, 0, sizeof(hints));
+				hints.ai_flags = AI_ADDRCONFIG | AI_NUMERICHOST;
+				ret = getaddrinfo($4, NULL, &hints, &ai);
+				if (ret == EAI_NONAME) {
+					/* The name was maybe not numeric, try again */
+					disc = EP_FL_DISC;
+					hints.ai_flags &= ~ AI_NUMERICHOST;
+					ret = getaddrinfo($4, NULL, &hints, &ai);
+				}
+				if (ret) { yyerror (&yylloc, conf, gai_strerror(ret)); YYERROR; }
+				
+				CHECK_FCT_DO( fd_ep_add_merge( &fddpi.pi_endpoints, ai->ai_addr, ai->ai_addrlen, EP_FL_CONF | disc ), YYERROR );
+				free($4);
+				freeaddrinfo(ai);
+			}
+			;
+
+tls_cred:		TLS_CRED '=' QSTRING ',' QSTRING ';'
+			{
+				FILE * fd;
+				fd = fopen($3, "r");
+				if (fd == NULL) {
+					int ret = errno;
+					TRACE_DEBUG(INFO, "Unable to open certificate file %s for reading: %s\n", $3, strerror(ret));
+					yyerror (&yylloc, conf, "Error on file name"); 
+					YYERROR;
+				}
+				fclose(fd);
+				fd = fopen($5, "r");
+				if (fd == NULL) {
+					int ret = errno;
+					TRACE_DEBUG(INFO, "Unable to open private key file %s for reading: %s\n", $5, strerror(ret));
+					yyerror (&yylloc, conf, "Error on file name"); 
+					YYERROR;
+				}
+				fclose(fd);
+				conf->cnf_sec_data.cert_file = $3;
+				conf->cnf_sec_data.key_file = $5;
+				
+				CHECK_GNUTLS_DO( gnutls_certificate_set_x509_key_file( 
+							conf->cnf_sec_data.credentials,
+							conf->cnf_sec_data.cert_file,
+							conf->cnf_sec_data.key_file,
+							GNUTLS_X509_FMT_PEM),
+						{ yyerror (&yylloc, conf, "Error opening certificate or private key file."); YYERROR; } );
+			}
+			;
+
+tls_ca:			TLS_CA '=' QSTRING ';'
+			{
+				FILE * fd;
+				fd = fopen($3, "r");
+				if (fd == NULL) {
+					int ret = errno;
+					TRACE_DEBUG(INFO, "Unable to open CA file %s for reading: %s\n", $3, strerror(ret));
+					yyerror (&yylloc, conf, "Error on file name"); 
+					YYERROR;
+				}
+				fclose(fd);
+				conf->cnf_sec_data.ca_file = $3;
+				CHECK_GNUTLS_DO( conf->cnf_sec_data.ca_file_nr += gnutls_certificate_set_x509_trust_file( 
+							conf->cnf_sec_data.credentials,
+							conf->cnf_sec_data.ca_file,
+							GNUTLS_X509_FMT_PEM),
+						{ yyerror (&yylloc, conf, "Error setting CA parameters."); YYERROR; } );
+			}
+			;
+			
+tls_crl:		TLS_CRL '=' QSTRING ';'
+			{
+				FILE * fd;
+				fd = fopen($3, "r");
+				if (fd == NULL) {
+					int ret = errno;
+					TRACE_DEBUG(INFO, "Unable to open CRL file %s for reading: %s\n", $3, strerror(ret));
+					yyerror (&yylloc, conf, "Error on file name"); 
+					YYERROR;
+				}
+				fclose(fd);
+				conf->cnf_sec_data.crl_file = $3;
+				CHECK_GNUTLS_DO( gnutls_certificate_set_x509_crl_file( 
+							conf->cnf_sec_data.credentials,
+							conf->cnf_sec_data.ca_file,
+							GNUTLS_X509_FMT_PEM),
+						{ yyerror (&yylloc, conf, "Error setting CRL parameters."); YYERROR; } );
+			}
+			;
+			
+tls_prio:		TLS_PRIO '=' QSTRING ';'
+			{
+				const char * err_pos = NULL;
+				conf->cnf_sec_data.prio_string = $3;
+				CHECK_GNUTLS_DO( gnutls_priority_init( 
+							&conf->cnf_sec_data.prio_cache,
+							conf->cnf_sec_data.prio_string,
+							&err_pos),
+						{ yyerror (&yylloc, conf, "Error setting Priority parameter.");
+						  fprintf(stderr, "Error at position : %s\n", err_pos);
+						  YYERROR; } );
+			}
+			;
+			
+tls_dh:			TLS_DH_BITS '=' INTEGER ';'
+			{
+				conf->cnf_sec_data.dh_bits = $3;
+			}
+			| TLS_DH_FILE '=' QSTRING ';'
+			{
+				FILE * fd;
+				free(conf->cnf_sec_data.dh_file);
+				conf->cnf_sec_data.dh_file = $3;
+				fd = fopen($3, "r");
+				if (fd == NULL) {
+					int ret = errno;
+					TRACE_DEBUG(INFO, "Unable to open DH file %s for reading: %s\n", $3, strerror(ret));
+					yyerror (&yylloc, conf, "Error on file name"); 
+					YYERROR;
+				}
+				fclose(fd);
+			}
+			;
Index: libfdcore/messages.c
===================================================================
--- libfdcore/messages.c	(revision 658)
+++ libfdcore/messages.c	(revision 658)
@@ -0,0 +1,371 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@nict.go.jp>							 *
+*													 *
+* 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 "fdcore-internal.h"
+
+static struct dict_object * dict_avp_OH  = NULL; /* Origin-Host */
+static struct dict_object * dict_avp_OR  = NULL; /* Origin-Realm */
+static struct dict_object * dict_avp_EM  = NULL; /* Error-Message */
+static struct dict_object * dict_avp_ERH = NULL; /* Error-Reporting-Host */
+static struct dict_object * dict_avp_FAVP= NULL; /* Failed-AVP */
+static struct dict_object * dict_avp_RC  = NULL; /* Result-Code */
+struct dict_object * fd_dict_avp_OSI = NULL; /* Origin-State-Id */
+struct dict_object * fd_dict_cmd_CER = NULL; /* Capabilities-Exchange-Request */
+struct dict_object * fd_dict_cmd_DWR = NULL; /* Device-Watchdog-Request */
+struct dict_object * fd_dict_avp_DC  = NULL; /* Disconnect-Cause */
+struct dict_object * fd_dict_cmd_DPR = NULL; /* Disconnect-Peer-Request */
+
+/* Resolve the dictionary objects */
+int fd_msg_init(void)
+{
+	TRACE_ENTRY("");
+	
+	/* Initialize the dictionary objects that we may use frequently */
+	CHECK_FCT(  fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Origin-Host",     	&dict_avp_OH  , ENOENT)  );
+	CHECK_FCT(  fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Origin-Realm",    	&dict_avp_OR  , ENOENT)  );
+	CHECK_FCT(  fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Origin-State-Id", 	&fd_dict_avp_OSI , ENOENT)  );
+	
+	CHECK_FCT(  fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Result-Code",     	&dict_avp_RC  , ENOENT)  );
+	CHECK_FCT(  fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Error-Message",   	&dict_avp_EM  , ENOENT)  );
+	CHECK_FCT(  fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Error-Reporting-Host", &dict_avp_ERH , ENOENT)  );
+	CHECK_FCT(  fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Failed-AVP",      	&dict_avp_FAVP, ENOENT)  );
+	
+	CHECK_FCT(  fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Disconnect-Cause", 	&fd_dict_avp_DC , ENOENT)  );
+	
+	CHECK_FCT( fd_dict_search ( fd_g_config->cnf_dict, DICT_COMMAND, CMD_BY_NAME, "Capabilities-Exchange-Request", &fd_dict_cmd_CER, ENOENT ) );
+	CHECK_FCT( fd_dict_search ( fd_g_config->cnf_dict, DICT_COMMAND, CMD_BY_NAME, "Device-Watchdog-Request", &fd_dict_cmd_DWR, ENOENT ) );
+	CHECK_FCT( fd_dict_search ( fd_g_config->cnf_dict, DICT_COMMAND, CMD_BY_NAME, "Disconnect-Peer-Request", &fd_dict_cmd_DPR, ENOENT ) );
+	
+	
+	return 0;
+}
+
+/* Add Origin-Host, Origin-Realm, Origin-State-Id AVPS at the end of the message */
+int fd_msg_add_origin ( struct msg * msg, int osi )
+{
+	union avp_value val;
+	struct avp * avp_OH  = NULL;
+	struct avp * avp_OR  = NULL;
+	struct avp * avp_OSI = NULL;
+	
+	TRACE_ENTRY("%p", msg);
+	CHECK_PARAMS(  msg  );
+	
+	/* Create the Origin-Host AVP */
+	CHECK_FCT( fd_msg_avp_new( dict_avp_OH, 0, &avp_OH ) );
+	
+	/* Set its value */
+	memset(&val, 0, sizeof(val));
+	val.os.data = (unsigned char *)fd_g_config->cnf_diamid;
+	val.os.len  = fd_g_config->cnf_diamid_len;
+	CHECK_FCT( fd_msg_avp_setvalue( avp_OH, &val ) );
+	
+	/* Add it to the message */
+	CHECK_FCT( fd_msg_avp_add( msg, MSG_BRW_LAST_CHILD, avp_OH ) );
+	
+	
+	/* Create the Origin-Realm AVP */
+	CHECK_FCT( fd_msg_avp_new( dict_avp_OR, 0, &avp_OR ) );
+	
+	/* Set its value */
+	memset(&val, 0, sizeof(val));
+	val.os.data = (unsigned char *)fd_g_config->cnf_diamrlm;
+	val.os.len  = fd_g_config->cnf_diamrlm_len;
+	CHECK_FCT( fd_msg_avp_setvalue( avp_OR, &val ) );
+	
+	/* Add it to the message */
+	CHECK_FCT( fd_msg_avp_add( msg, MSG_BRW_LAST_CHILD, avp_OR ) );
+	
+	if (osi) {
+		/* Create the Origin-State-Id AVP */
+		CHECK_FCT( fd_msg_avp_new( fd_dict_avp_OSI, 0, &avp_OSI ) );
+
+		/* Set its value */
+		memset(&val, 0, sizeof(val));
+		val.u32 = fd_g_config->cnf_orstateid;
+		CHECK_FCT( fd_msg_avp_setvalue( avp_OSI, &val ) );
+
+		/* Add it to the message */
+		CHECK_FCT( fd_msg_avp_add( msg, MSG_BRW_LAST_CHILD, avp_OSI ) );
+	}
+	
+	return 0;
+}
+
+/* Add Result-Code and eventually Failed-AVP, Error-Message and Error-Reporting-Host AVPs */
+int fd_msg_rescode_set( struct msg * msg, char * rescode, char * errormsg, struct avp * optavp, int type_id )
+{
+	union avp_value val;
+	struct avp * avp_RC  = NULL;
+	struct avp * avp_EM  = NULL;
+	struct avp * avp_ERH = NULL;
+	struct avp * avp_FAVP= NULL;
+	uint32_t rc_val = 0;
+	int set_e_bit=0;
+	int std_err_msg=0;
+	
+	TRACE_ENTRY("%p %s %p %p %d", msg, rescode, errormsg, optavp, type_id);
+		
+	CHECK_PARAMS(  msg && rescode  );
+	
+	/* Find the enum value corresponding to the rescode string, this will give the class of error */
+	{
+		struct dict_object * enum_obj = NULL;
+		struct dict_enumval_request req;
+		memset(&req, 0, sizeof(struct dict_enumval_request));
+		
+		/* First, get the enumerated type of the Result-Code AVP */
+		CHECK_FCT(  fd_dict_search( fd_g_config->cnf_dict, DICT_TYPE, TYPE_OF_AVP, dict_avp_RC, &(req.type_obj), ENOENT  )  );
+		
+		/* Now search for the value given as parameter */
+		req.search.enum_name = rescode;
+		CHECK_FCT(  fd_dict_search( fd_g_config->cnf_dict, DICT_ENUMVAL, ENUMVAL_BY_STRUCT, &req, &enum_obj, ENOTSUP)  );
+		
+		/* finally retrieve its data */
+		CHECK_FCT_DO(  fd_dict_getval( enum_obj, &(req.search) ), return EINVAL );
+		
+		/* copy the found value, we're done */
+		rc_val = req.search.enum_value.u32;
+	}
+	
+	if (type_id == 1) {
+		/* Add the Origin-Host and Origin-Realm AVP */
+		CHECK_FCT( fd_msg_add_origin ( msg, 0 ) );
+	}
+	
+	/* Create the Result-Code AVP */
+	CHECK_FCT( fd_msg_avp_new( dict_avp_RC, 0, &avp_RC ) );
+	
+	/* Set its value */
+	memset(&val, 0, sizeof(val));
+	val.u32  = rc_val;
+	CHECK_FCT( fd_msg_avp_setvalue( avp_RC, &val ) );
+	
+	/* Add it to the message */
+	CHECK_FCT( fd_msg_avp_add( msg, MSG_BRW_LAST_CHILD, avp_RC ) );
+	
+	if (type_id == 2) {
+		/* Add the Error-Reporting-Host AVP */
+		
+		CHECK_FCT( fd_msg_avp_new( dict_avp_ERH, 0, &avp_ERH ) );
+
+		/* Set its value */
+		memset(&val, 0, sizeof(val));
+		val.os.data = (unsigned char *)fd_g_config->cnf_diamid;
+		val.os.len  = fd_g_config->cnf_diamid_len;
+		CHECK_FCT( fd_msg_avp_setvalue( avp_ERH, &val ) );
+
+		/* Add it to the message */
+		CHECK_FCT( fd_msg_avp_add( msg, MSG_BRW_LAST_CHILD, avp_ERH ) );
+	
+	}
+	
+	/* Now add the optavp in a FailedAVP if provided */
+	if (optavp) {
+		/* Create the Failed-AVP AVP */
+		CHECK_FCT( fd_msg_avp_new( dict_avp_FAVP, 0, &avp_FAVP ) );
+
+		/* Add the passed AVP inside it */
+		CHECK_FCT( fd_msg_avp_add( avp_FAVP, MSG_BRW_LAST_CHILD, optavp ) );
+		
+		/* And add to the message */
+		CHECK_FCT( fd_msg_avp_add( msg, MSG_BRW_LAST_CHILD, avp_FAVP ) );
+	}
+	
+	
+	/* Deal with the 'E' bit and the error message */
+	switch (rc_val / 1000) {
+		case 1:	/* Informational */
+		case 2: /* Success */
+			/* Nothing special here: no E bit, no error message unless one is specified */
+			break;
+			
+		case 3: /* Protocol Errors */
+			set_e_bit = 1;
+			std_err_msg = 1;
+			break;
+			
+		case 4: /* Transcient Failure */
+		case 5: /* Permanent Failure */
+		default:
+			std_err_msg = 1;
+			break;
+			
+	}
+	
+	{
+		struct msg_hdr * hdr = NULL;
+		
+		CHECK_FCT(  fd_msg_hdr( msg, &hdr )  );
+		
+		if (set_e_bit)
+			hdr->msg_flags |= CMD_FLAG_ERROR;
+		else
+			hdr->msg_flags &= ! CMD_FLAG_ERROR;
+	}
+	
+	if (std_err_msg || errormsg) {
+		/* Add the Error-Message AVP */
+		
+		CHECK_FCT( fd_msg_avp_new( dict_avp_EM, 0, &avp_EM ) );
+
+		/* Set its value */
+		memset(&val, 0, sizeof(val));
+		
+		if (errormsg) {
+			val.os.data = (unsigned char *)errormsg;
+			val.os.len  = strlen(errormsg);
+		} else {
+			val.os.data = (unsigned char *)rescode;
+			val.os.len  = strlen(rescode);
+		}
+		CHECK_FCT( fd_msg_avp_setvalue( avp_EM, &val ) );
+
+		/* Add it to the message */
+		CHECK_FCT( fd_msg_avp_add( msg, MSG_BRW_LAST_CHILD, avp_EM ) );
+	}
+	
+	return 0;
+}
+
+/* Send a message and optionaly register a callback for an answer */
+int fd_msg_send ( struct msg ** pmsg, void (*anscb)(void *, struct msg **), void * data )
+{
+	TRACE_ENTRY("%p %p %p", pmsg, anscb, data);
+	CHECK_PARAMS( pmsg );
+	
+	/* Save the callback in the message */
+	if (anscb) {
+		CHECK_FCT(  fd_msg_anscb_associate( *pmsg, anscb, data, NULL /* we should maybe use a safeguard here like 1 hour or so? */ )  );
+	}
+	
+	/* Post the message in the outgoing queue */
+	CHECK_FCT( fd_fifo_post(fd_g_outgoing, pmsg) );
+	
+	return 0;
+}
+
+/* The variation of the same function with a timeout callback */
+int fd_msg_send_timeout ( struct msg ** pmsg, void (*anscb)(void *, struct msg **), void * data, const struct timespec *timeout )
+{
+	TRACE_ENTRY("%p %p %p", pmsg, anscb, data, timeout);
+	CHECK_PARAMS( pmsg && anscb && timeout );
+	
+	/* Save the callback in the message, with the timeout */
+	CHECK_FCT(  fd_msg_anscb_associate( *pmsg, anscb, data, timeout )  );
+	
+	/* Post the message in the outgoing queue */
+	CHECK_FCT( fd_fifo_post(fd_g_outgoing, pmsg) );
+	
+	return 0;
+}
+
+
+/* Parse a message against our dictionary, and in case of error log and eventually build the error reply -- returns the parsing status */
+int fd_msg_parse_or_error( struct msg ** msg )
+{
+	int ret = 0;
+	struct msg * m;
+	struct msg_hdr * hdr = NULL;
+	struct fd_pei	pei;
+	
+	TRACE_ENTRY("%p", msg);
+	
+	CHECK_PARAMS(msg && *msg);
+	m = *msg;
+	
+	/* Parse the message against our dictionary */
+	ret = fd_msg_parse_rules ( m, fd_g_config->cnf_dict, &pei);
+	if 	((ret != EBADMSG) 	/* Parsing grouped AVP failed / Conflicting rule found */
+		&& (ret != ENOTSUP))	/* Command is not supported / Mandatory AVP is not supported */
+		return ret;
+	
+	fd_log_debug("The following message does not comply to the dictionary and/or rules (%s):\n", pei.pei_errcode);
+	fd_msg_dump_walk(NONE, m);
+	
+	CHECK_FCT( fd_msg_hdr(m, &hdr) );
+	
+	/* Now create an answer error if the message is a query */
+	if (hdr->msg_flags & CMD_FLAG_REQUEST) {
+		
+		/* Create the error message */
+		CHECK_FCT( fd_msg_new_answer_from_req ( fd_g_config->cnf_dict, msg, pei.pei_protoerr ? MSGFL_ANSW_ERROR : 0 ) );
+		
+		/* Set the error code */
+		CHECK_FCT( fd_msg_rescode_set(*msg, pei.pei_errcode, pei.pei_message, pei.pei_avp, 1 ) );
+		
+	} else {
+		do { /* Rescue error messages */
+			struct avp * avp;
+			union avp_value * rc = NULL;
+			
+			/* Search the Result-Code AVP */
+			CHECK_FCT_DO(  fd_msg_browse(*msg, MSG_BRW_FIRST_CHILD, &avp, NULL), break  );
+			while (avp) {
+				struct avp_hdr * ahdr;
+				CHECK_FCT_DO(  fd_msg_avp_hdr( avp, &ahdr ), break  );
+				
+				if ((ahdr->avp_code == AC_RESULT_CODE) && (! (ahdr->avp_flags & AVP_FLAG_VENDOR)) ) {
+					/* Parse this AVP */
+					ASSERT( ahdr->avp_value );
+					rc = ahdr->avp_value;
+					break;
+				}
+				
+				/* Go to next AVP */
+				CHECK_FCT_DO(  fd_msg_browse(avp, MSG_BRW_NEXT, &avp, NULL), break  );
+			}
+			
+			if (rc) {
+				switch (rc->u32 / 1000) {
+					case 1:	/* 1xxx : Informational */
+					case 2:	/* 2xxx : Sucess */
+						/* In these cases, we want the message to validate the ABNF, so we will discard the bad message */
+						break;
+						
+					default: /* Other errors */
+						/* We let the application decide what to do with the message, we rescue it */
+						return 0;
+				}
+			}
+		} while (0);
+		
+		/* Just discard */
+		CHECK_FCT( fd_msg_free( m ) );
+		*msg = NULL;
+	}
+	
+	return EBADMSG; /* We convert ENOTSUP to EBADMSG as well */
+}
Index: libfdcore/p_ce.c
===================================================================
--- libfdcore/p_ce.c	(revision 658)
+++ libfdcore/p_ce.c	(revision 658)
@@ -0,0 +1,972 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@nict.go.jp>							 *
+*													 *
+* Copyright (c) 2010, 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 "fdcore-internal.h"
+
+/* This file contains code to handle Capabilities Exchange messages (CER and CEA) and election process */
+
+/* Compilation option:
+ USE_CEA_BROADCAST
+ 	Define this to enable sending multiple copies of the CEA in case of SCTP connection.
+	This avoids a race condition when sending an application message over a different stream
+	than the CEA, it might be delivered first and thus ignored.
+*/
+
+/* Save a connection as peer's principal */
+static int set_peer_cnx(struct fd_peer * peer, struct cnxctx **cnx)
+{
+	CHECK_PARAMS( peer->p_cnxctx == NULL );
+	
+	/* Save the connection in peer */
+	peer->p_cnxctx = *cnx;
+	*cnx = NULL;
+	
+	/* Set the events to be sent to the PSM */
+	CHECK_FCT( fd_cnx_recv_setaltfifo(peer->p_cnxctx, peer->p_events) );
+	
+	/* Read the credentials if possible */
+	if (fd_cnx_getTLS(peer->p_cnxctx)) {
+		CHECK_FCT( fd_cnx_getcred(peer->p_cnxctx, &peer->p_hdr.info.runtime.pir_cert_list, &peer->p_hdr.info.runtime.pir_cert_list_size) );
+	}
+	
+	/* Read the endpoints, maybe used to reconnect to the peer later */
+	CHECK_FCT( fd_cnx_getremoteeps(peer->p_cnxctx, &peer->p_hdr.info.pi_endpoints) );
+	
+	/* Read the protocol */
+	peer->p_hdr.info.runtime.pir_proto = fd_cnx_getproto(peer->p_cnxctx);
+	
+	return 0;
+}
+
+/* Delete the peer connection, and cleanup associated information */
+void fd_p_ce_clear_cnx(struct fd_peer * peer, struct cnxctx ** cnx_kept)
+{
+	peer->p_hdr.info.runtime.pir_cert_list = NULL;
+	peer->p_hdr.info.runtime.pir_cert_list_size = 0;
+	peer->p_hdr.info.runtime.pir_proto = 0;
+	
+	if (peer->p_cnxctx) {
+		if (cnx_kept != NULL) {
+			*cnx_kept = peer->p_cnxctx;
+		} else {
+			fd_cnx_destroy(peer->p_cnxctx);
+		}
+		peer->p_cnxctx = NULL;
+	}
+}
+
+/* Election: compare the Diameter Ids, return true if the election is won */
+static __inline__ int election_result(struct fd_peer * peer)
+{
+	int ret = (strcasecmp(peer->p_hdr.info.pi_diamid, fd_g_config->cnf_diamid) < 0);
+	if (ret) {
+		TRACE_DEBUG(INFO, "Election WON against peer '%s'", peer->p_hdr.info.pi_diamid);
+	} else {
+		TRACE_DEBUG(INFO, "Election LOST against peer '%s'", peer->p_hdr.info.pi_diamid);
+	}
+	return ret;
+}
+
+/* Add AVPs about local information in a CER or CEA */
+static int add_CE_info(struct msg *msg, struct cnxctx * cnx, int isi_tls, int isi_none)
+{
+	struct dict_object * dictobj = NULL;
+	struct avp * avp = NULL;
+	union avp_value val;
+	struct fd_list *li;
+	
+	/* Add the Origin-* AVPs */
+	CHECK_FCT( fd_msg_add_origin ( msg, 1 ) );
+	
+	/* Find the model for Host-IP-Address AVP */
+	CHECK_FCT(  fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Host-IP-Address", &dictobj, ENOENT )  );
+		
+	/* Add the AVP(s) -- not sure what is the purpose... We could probably only add the primary one ? */
+	for (li = fd_g_config->cnf_endpoints.next; li != &fd_g_config->cnf_endpoints; li = li->next) {
+		struct fd_endpoint * ep = (struct fd_endpoint *)li;
+		
+		CHECK_FCT( fd_msg_avp_new ( dictobj, 0, &avp ) );
+		CHECK_FCT( fd_msg_avp_value_encode ( &ep->ss, avp ) );
+		CHECK_FCT( fd_msg_avp_add( msg, MSG_BRW_LAST_CHILD, avp ) );
+	}
+	
+	/* Vendor-Id, Product-Name, and Firmware-Revision AVPs */
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Vendor-Id", &dictobj, ENOENT )  );
+	CHECK_FCT( fd_msg_avp_new ( dictobj, 0, &avp ) );
+	val.u32 = MY_VENDOR_ID;
+	CHECK_FCT( fd_msg_avp_setvalue( avp, &val ) );
+	CHECK_FCT( fd_msg_avp_add( msg, MSG_BRW_LAST_CHILD, avp ) );
+	
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Product-Name", &dictobj, ENOENT )  );
+	CHECK_FCT( fd_msg_avp_new ( dictobj, 0, &avp ) );
+	val.os.data = (unsigned char *)FD_PROJECT_NAME;
+	val.os.len = strlen(FD_PROJECT_NAME);
+	CHECK_FCT( fd_msg_avp_setvalue( avp, &val ) );
+	CHECK_FCT( fd_msg_avp_add( msg, MSG_BRW_LAST_CHILD, avp ) );
+	
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Firmware-Revision", &dictobj, ENOENT )  );
+	CHECK_FCT( fd_msg_avp_new ( dictobj, 0, &avp ) );
+	val.u32 = (uint32_t)(FD_PROJECT_VERSION_MAJOR * 10000 + FD_PROJECT_VERSION_MINOR * 100 + FD_PROJECT_VERSION_REV);
+	CHECK_FCT( fd_msg_avp_setvalue( avp, &val ) );
+	CHECK_FCT( fd_msg_avp_add( msg, MSG_BRW_LAST_CHILD, avp ) );
+	
+	
+	/* Add the Inband-Security-Id AVP if needed */
+	if (isi_tls || isi_none) {
+		CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Inband-Security-Id", &dictobj, ENOENT )  );
+		
+		if (isi_none) {
+			CHECK_FCT( fd_msg_avp_new ( dictobj, 0, &avp ) );
+			val.u32 = ACV_ISI_NO_INBAND_SECURITY;
+			CHECK_FCT( fd_msg_avp_setvalue( avp, &val ) );
+			CHECK_FCT( fd_msg_avp_add( msg, MSG_BRW_LAST_CHILD, avp ) );
+		}
+		
+		if (isi_tls) {
+			CHECK_FCT( fd_msg_avp_new ( dictobj, 0, &avp ) );
+			val.u32 = ACV_ISI_TLS;
+			CHECK_FCT( fd_msg_avp_setvalue( avp, &val ) );
+			CHECK_FCT( fd_msg_avp_add( msg, MSG_BRW_LAST_CHILD, avp ) );
+		}
+	}
+	
+	/* List of local applications */
+	{
+		struct dict_object * dictobj_auth = NULL;
+		struct dict_object * dictobj_acct = NULL;
+		struct dict_object * dictobj_vid = NULL;
+		
+		CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Vendor-Specific-Application-Id", &dictobj, ENOENT )  );
+		CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Vendor-Id", &dictobj_vid, ENOENT )  );
+		CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Auth-Application-Id", &dictobj_auth, ENOENT )  );
+		CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Acct-Application-Id", &dictobj_acct, ENOENT )  );
+		
+		for (li = fd_g_config->cnf_apps.next; li != &fd_g_config->cnf_apps; li = li->next) {
+			struct fd_app * a = (struct fd_app *)(li);
+
+			if (a->flags.auth) {
+				CHECK_FCT( fd_msg_avp_new ( dictobj_auth, 0, &avp ) );
+				val.u32 = a->appid;
+				CHECK_FCT( fd_msg_avp_setvalue( avp, &val ) );
+				if (a->vndid != 0) {
+					struct avp * avp2 = NULL;
+					CHECK_FCT( fd_msg_avp_new ( dictobj, 0, &avp2 ) );
+					CHECK_FCT( fd_msg_avp_add( avp2, MSG_BRW_LAST_CHILD, avp ) );
+					avp = avp2;
+					CHECK_FCT( fd_msg_avp_new ( dictobj_vid, 0, &avp2 ) );
+					val.u32 = a->vndid;
+					CHECK_FCT( fd_msg_avp_setvalue( avp2, &val ) );
+					CHECK_FCT( fd_msg_avp_add( avp, MSG_BRW_LAST_CHILD, avp2 ) );
+				}
+				CHECK_FCT( fd_msg_avp_add( msg, MSG_BRW_LAST_CHILD, avp ) );
+			}
+			if (a->flags.acct) {
+				CHECK_FCT( fd_msg_avp_new ( dictobj_acct, 0, &avp ) );
+				val.u32 = a->appid;
+				CHECK_FCT( fd_msg_avp_setvalue( avp, &val ) );
+				if (a->vndid != 0) {
+					struct avp * avp2 = NULL;
+					CHECK_FCT( fd_msg_avp_new ( dictobj, 0, &avp2 ) );
+					CHECK_FCT( fd_msg_avp_add( avp2, MSG_BRW_LAST_CHILD, avp ) );
+					avp = avp2;
+					CHECK_FCT( fd_msg_avp_new ( dictobj_vid, 0, &avp2 ) );
+					val.u32 = a->vndid;
+					CHECK_FCT( fd_msg_avp_setvalue( avp2, &val ) );
+					CHECK_FCT( fd_msg_avp_add( avp, MSG_BRW_LAST_CHILD, avp2 ) );
+				}
+				CHECK_FCT( fd_msg_avp_add( msg, MSG_BRW_LAST_CHILD, avp ) );
+			}
+		}
+		
+		/* do not forget the relay application */
+		if (! fd_g_config->cnf_flags.no_fwd) {
+			CHECK_FCT( fd_msg_avp_new ( dictobj_auth, 0, &avp ) );
+			val.u32 = AI_RELAY;
+			CHECK_FCT( fd_msg_avp_setvalue( avp, &val ) );
+			CHECK_FCT( fd_msg_avp_add( msg, MSG_BRW_LAST_CHILD, avp ) );
+		}
+	}
+	
+	/* Add the list of supported vendors */
+	{
+		uint32_t * array = fd_dict_get_vendorid_list(fd_g_config->cnf_dict);
+		if (array) {
+			int i = 0;
+			CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Supported-Vendor-Id", &dictobj, ENOENT )  );
+			
+			while (array[i] != 0) {
+				CHECK_FCT( fd_msg_avp_new ( dictobj, 0, &avp ) );
+				val.u32 = array[i];
+				CHECK_FCT( fd_msg_avp_setvalue( avp, &val ) );
+				CHECK_FCT( fd_msg_avp_add( msg, MSG_BRW_LAST_CHILD, avp ) );
+				i++;
+			}
+			
+			free(array);
+		}
+	}
+	
+	return 0;
+}
+
+/* Remove any information saved from a previous CER/CEA exchange */
+static void cleanup_remote_CE_info(struct fd_peer * peer)
+{
+	free(peer->p_hdr.info.runtime.pir_realm);
+	peer->p_hdr.info.runtime.pir_realm = NULL;
+	peer->p_hdr.info.runtime.pir_vendorid = 0;
+	peer->p_hdr.info.runtime.pir_orstate = 0;
+	free(peer->p_hdr.info.runtime.pir_prodname);
+	peer->p_hdr.info.runtime.pir_prodname = NULL;
+	peer->p_hdr.info.runtime.pir_firmrev = 0;
+	peer->p_hdr.info.runtime.pir_relay = 0;
+	peer->p_hdr.info.runtime.pir_isi = 0;
+	while (!FD_IS_LIST_EMPTY(&peer->p_hdr.info.runtime.pir_apps)) {
+		struct fd_list * li = peer->p_hdr.info.runtime.pir_apps.next;
+		fd_list_unlink(li);
+		free(li);
+	}
+	
+	fd_ep_clearflags( &peer->p_hdr.info.pi_endpoints, EP_FL_ADV /* Remove previously advertised endpoints */ );
+}
+
+/* Extract information sent by the remote peer and save it in our peer structure */
+static int save_remote_CE_info(struct msg * msg, struct fd_peer * peer, char ** error_code, uint32_t *rc)
+{
+	struct avp * avp = NULL;
+	
+	cleanup_remote_CE_info(peer);
+	
+	CHECK_FCT( fd_msg_browse( msg, MSG_BRW_FIRST_CHILD, &avp, NULL) );
+	
+	/* Loop on all AVPs and save what we are interrested into */
+	while (avp) {
+		struct avp_hdr * hdr;
+
+		CHECK_FCT(  fd_msg_avp_hdr( avp, &hdr )  );
+
+		if (hdr->avp_flags & AVP_FLAG_VENDOR) {
+			/* Ignore all vendor-specific AVPs in CER/CEA because we don't support any currently */
+			TRACE_DEBUG(FULL, "Ignored a vendor AVP in CER / CEA");
+			fd_msg_dump_one(FULL, avp);
+			goto next;
+		}
+
+		switch (hdr->avp_code) {
+			case AC_RESULT_CODE: /* Result-Code */
+				if (hdr->avp_value == NULL) {
+					/* This is a sanity check */
+					TRACE_DEBUG(NONE, "Ignored an AVP with unset value in CER/CEA");
+					fd_msg_dump_one(NONE, avp);
+					ASSERT(0); /* To check if this really happens, and understand why... */
+					goto next;
+				}
+				
+				if (rc)
+					*rc = hdr->avp_value->u32;
+				break;
+		
+			case AC_ORIGIN_HOST: /* Origin-Host */
+				if (hdr->avp_value == NULL) {
+					/* This is a sanity check */
+					TRACE_DEBUG(NONE, "Ignored an AVP with unset value in CER/CEA");
+					fd_msg_dump_one(NONE, avp);
+					ASSERT(0); /* To check if this really happens, and understand why... */
+					goto next;
+				}
+				
+				/* We check that the value matches what we know, otherwise disconnect the peer */
+				/* here also, using strcasecmp on (supposed) UTF8 data might be bad idea... to be improved */
+				if (strncasecmp((char *)hdr->avp_value->os.data, peer->p_hdr.info.pi_diamid, hdr->avp_value->os.len)) {
+					TRACE_DEBUG(INFO, "Received a message with Origin-Host set to '%.*s' while expecting '%s'\n", 
+							hdr->avp_value->os.len, hdr->avp_value->os.data, peer->p_hdr.info.pi_diamid);
+					*error_code = "DIAMETER_UNKNOWN_PEER";
+					return EINVAL;
+				}
+
+				break;
+		
+			case AC_ORIGIN_REALM: /* Origin-Realm */
+				if (hdr->avp_value == NULL) {
+					/* This is a sanity check */
+					TRACE_DEBUG(NONE, "Ignored an AVP with unset value in CER/CEA");
+					fd_msg_dump_one(NONE, avp);
+					ASSERT(0); /* To check if this really happens, and understand why... */
+					goto next;
+				}
+				
+				/* In case of multiple AVPs */
+				if (peer->p_hdr.info.runtime.pir_realm) {
+					TRACE_DEBUG(INFO, "Ignored multiple instances of the Origin-Realm AVP");
+					goto next;
+				}
+				
+				/* Save the value -- we don't change the case to avoid risking breaking UTF-8 with poor tolower() impls. */
+				CHECK_MALLOC(  peer->p_hdr.info.runtime.pir_realm = calloc( hdr->avp_value->os.len + 1, 1 )  );
+				memcpy(peer->p_hdr.info.runtime.pir_realm, hdr->avp_value->os.data, hdr->avp_value->os.len);
+				break;
+
+			case AC_HOST_IP_ADDRESS: /* Host-IP-Address */
+				if (hdr->avp_value == NULL) {
+					/* This is a sanity check */
+					TRACE_DEBUG(NONE, "Ignored an AVP with unset value in CER/CEA");
+					fd_msg_dump_one(NONE, avp);
+					ASSERT(0); /* To check if this really happens, and understand why... */
+					goto next;
+				}
+				{
+					sSS	ss;
+
+					/* Get the sockaddr value */
+					memset(&ss, 0, sizeof(ss));
+					CHECK_FCT( fd_msg_avp_value_interpret( avp, &ss) );
+
+					/* Save this endpoint in the list as advertized */
+					CHECK_FCT( fd_ep_add_merge( &peer->p_hdr.info.pi_endpoints, (sSA *)&ss, sizeof(sSS), EP_FL_ADV ) );
+				}
+				break;
+
+			case AC_VENDOR_ID: /* Vendor-Id */
+				if (hdr->avp_value == NULL) {
+					/* This is a sanity check */
+					TRACE_DEBUG(NONE, "Ignored an AVP with unset value in CER/CEA");
+					fd_msg_dump_one(NONE, avp);
+					ASSERT(0); /* To check if this really happens, and understand why... */
+					goto next;
+				}
+				
+				/* In case of multiple AVPs */
+				if (peer->p_hdr.info.runtime.pir_vendorid) {
+					TRACE_DEBUG(INFO, "Ignored multiple instances of the Vendor-Id AVP");
+					goto next;
+				}
+				
+				peer->p_hdr.info.runtime.pir_vendorid = hdr->avp_value->u32;
+				break;
+
+			case AC_PRODUCT_NAME: /* Product-Name */
+				if (hdr->avp_value == NULL) {
+					/* This is a sanity check */
+					TRACE_DEBUG(NONE, "Ignored an AVP with unset value in CER/CEA");
+					fd_msg_dump_one(NONE, avp);
+					ASSERT(0); /* To check if this really happens, and understand why... */
+					goto next;
+				}
+				
+				/* In case of multiple AVPs */
+				if (peer->p_hdr.info.runtime.pir_prodname) {
+					TRACE_DEBUG(INFO, "Ignored multiple instances of the Product-Name AVP");
+					goto next;
+				}
+
+				CHECK_MALLOC( peer->p_hdr.info.runtime.pir_prodname = calloc( hdr->avp_value->os.len + 1, 1 )  );
+				memcpy(peer->p_hdr.info.runtime.pir_prodname, hdr->avp_value->os.data, hdr->avp_value->os.len);
+				break;
+
+			case AC_ORIGIN_STATE_ID: /* Origin-State-Id */
+				if (hdr->avp_value == NULL) {
+					/* This is a sanity check */
+					TRACE_DEBUG(NONE, "Ignored an AVP with unset value in CER/CEA");
+					fd_msg_dump_one(NONE, avp);
+					ASSERT(0); /* To check if this really happens, and understand why... */
+					goto next;
+				}
+				
+				/* In case of multiple AVPs */
+				if (peer->p_hdr.info.runtime.pir_orstate) {
+					TRACE_DEBUG(INFO, "Ignored multiple instances of the Origin-State-Id AVP");
+					goto next;
+				}
+				
+				peer->p_hdr.info.runtime.pir_orstate = hdr->avp_value->u32;
+				break;
+
+			case AC_SUPPORTED_VENDOR_ID: /* Supported-Vendor-Id */
+				if (hdr->avp_value == NULL) {
+					/* This is a sanity check */
+					TRACE_DEBUG(NONE, "Ignored an AVP with unset value in CER/CEA");
+					fd_msg_dump_one(NONE, avp);
+					ASSERT(0); /* To check if this really happens, and understand why... */
+					goto next;
+				}
+				
+				TRACE_DEBUG(FULL, "'%s' supports a subset of vendor %d features.", peer->p_hdr.info.pi_diamid, hdr->avp_value->u32);
+				break;
+
+			case AC_VENDOR_SPECIFIC_APPLICATION_ID: /* Vendor-Specific-Application-Id (grouped)*/
+				{
+					struct avp * inavp = NULL;
+					application_id_t aid = 0;
+					vendor_id_t vid = 0;
+					int auth = 0;
+					int acct = 0;
+
+					/* get the first child AVP */
+					CHECK_FCT(  fd_msg_browse(avp, MSG_BRW_FIRST_CHILD, &inavp, NULL)  );
+
+					while (inavp) {
+						struct avp_hdr * inhdr;
+						CHECK_FCT(  fd_msg_avp_hdr( inavp, &inhdr )  );
+
+						if (inhdr->avp_flags & AVP_FLAG_VENDOR) {
+							TRACE_DEBUG(FULL, "Ignored a vendor AVP inside Vendor-Specific-Application-Id AVP");
+							fd_msg_dump_one(FULL, avp);
+							goto innext;
+						}
+
+						if (inhdr->avp_value == NULL) {
+							/* This is a sanity check */
+							TRACE_DEBUG(NONE, "Ignored an AVP with unset value in CER/CEA");
+							fd_msg_dump_one(NONE, avp);
+							ASSERT(0); /* To check if this really happens, and understand why... */
+							goto innext;
+						}
+						switch (inhdr->avp_code) {
+							case AC_VENDOR_ID: /* Vendor-Id */
+								vid = inhdr->avp_value->u32;
+								break;
+							case AC_AUTH_APPLICATION_ID: /* Auth-Application-Id */
+								aid = inhdr->avp_value->u32;
+								auth += 1;
+								break;
+							case AC_ACCT_APPLICATION_ID: /* Acct-Application-Id */
+								aid = inhdr->avp_value->u32;
+								acct += 1;
+								break;
+							/* ignore other AVPs */
+						}
+
+					innext:			
+						/* Go to next in AVP */
+						CHECK_FCT( fd_msg_browse(inavp, MSG_BRW_NEXT, &inavp, NULL) );
+					}
+					
+					if (auth + acct != 1) {
+						TRACE_DEBUG(FULL, "Invalid Vendor-Specific-Application-Id AVP received, ignored");
+						fd_msg_dump_one(FULL, avp);
+					} else {
+						/* Add an entry in the list */
+						CHECK_FCT( fd_app_merge(&peer->p_hdr.info.runtime.pir_apps, aid, vid, auth, acct) );
+					}
+				}
+				break;
+
+			case AC_AUTH_APPLICATION_ID: /* Auth-Application-Id */
+				if (hdr->avp_value == NULL) {
+					/* This is a sanity check */
+					TRACE_DEBUG(NONE, "Ignored an AVP with unset value in CER/CEA");
+					fd_msg_dump_one(NONE, avp);
+					ASSERT(0); /* To check if this really happens, and understand why... */
+					goto next;
+				}
+				
+				if (hdr->avp_value->u32 == AI_RELAY) {
+					peer->p_hdr.info.runtime.pir_relay = 1;
+				} else {
+					CHECK_FCT( fd_app_merge(&peer->p_hdr.info.runtime.pir_apps, hdr->avp_value->u32, 0, 1, 0) );
+				}
+				break;
+
+			case AC_ACCT_APPLICATION_ID: /* Acct-Application-Id */
+				if (hdr->avp_value == NULL) {
+					/* This is a sanity check */
+					TRACE_DEBUG(NONE, "Ignored an AVP with unset value in CER/CEA");
+					fd_msg_dump_one(NONE, avp);
+					ASSERT(0); /* To check if this really happens, and understand why... */
+					goto next;
+				}
+				
+				if (hdr->avp_value->u32 == AI_RELAY) {
+					peer->p_hdr.info.runtime.pir_relay = 1;
+				} else {
+					/* Not clear if the relay application can be inside this AVP... */
+					CHECK_FCT( fd_app_merge(&peer->p_hdr.info.runtime.pir_apps, hdr->avp_value->u32, 0, 0, 1) );
+				}
+				break;
+
+			case AC_FIRMWARE_REVISION: /* Firmware-Revision */
+				if (hdr->avp_value == NULL) {
+					/* This is a sanity check */
+					TRACE_DEBUG(NONE, "Ignored an AVP with unset value in CER/CEA");
+					fd_msg_dump_one(NONE, avp);
+					ASSERT(0); /* To check if this really happens, and understand why... */
+					goto next;
+				}
+				
+				peer->p_hdr.info.runtime.pir_firmrev = hdr->avp_value->u32;
+				break;
+
+			case AC_INBAND_SECURITY_ID: /* Inband-Security-Id */
+				if (hdr->avp_value == NULL) {
+					/* This is a sanity check */
+					TRACE_DEBUG(NONE, "Ignored an AVP with unset value in CER/CEA");
+					fd_msg_dump_one(NONE, avp);
+					ASSERT(0); /* To check if this really happens, and understand why... */
+					goto next;
+				}
+				ASSERT( hdr->avp_value->u32 < 32 ); /* if false, we have to change the code bellow */
+				peer->p_hdr.info.runtime.pir_isi |= (1 << hdr->avp_value->u32);
+				break;
+		}
+
+next:			
+		/* Go to next AVP */
+		CHECK_FCT( fd_msg_browse(avp, MSG_BRW_NEXT, &avp, NULL) );
+	}
+	
+	return 0;
+}
+
+/* Create a CER message for sending */ 
+static int create_CER(struct fd_peer * peer, struct cnxctx * cnx, struct msg ** cer)
+{
+	int isi_tls = 0;
+	int isi_none = 0;
+	
+	/* Find CER dictionary object and create an instance */
+	CHECK_FCT( fd_msg_new ( fd_dict_cmd_CER, MSGFL_ALLOC_ETEID, cer ) );
+	
+	/* Do we need Inband-Security-Id AVPs ? */
+	if (!fd_cnx_getTLS(cnx)) {
+		isi_none = peer->p_hdr.info.config.pic_flags.sec & PI_SEC_NONE; /* we add it event if the peer does not use the old mechanism */
+		isi_tls  = peer->p_hdr.info.config.pic_flags.sec & PI_SEC_TLS_OLD;
+	}
+	
+	/* Add the information about the local peer */
+	CHECK_FCT( add_CE_info(*cer, cnx, isi_tls, isi_none) );
+	
+	/* Done! */
+	return 0;
+}
+
+
+/* Continue with the initiator side */
+static int to_waitcea(struct fd_peer * peer, struct cnxctx * cnx)
+{
+	/* We sent a CER on the connection, set the event queue so that we receive the CEA */
+	CHECK_FCT( set_peer_cnx(peer, &cnx) );
+	
+	/* Change state and reset the timer */
+	CHECK_FCT( fd_psm_change_state(peer, STATE_WAITCEA) );
+	fd_psm_next_timeout(peer, 0, CEA_TIMEOUT);
+	
+	return 0;
+}
+
+/* Reject an incoming connection attempt */
+static void receiver_reject(struct cnxctx * recv_cnx, struct msg ** cer, char * rescode, char * errormsg)
+{
+	/* Create and send the CEA with appropriate error code */
+	CHECK_FCT_DO( fd_msg_new_answer_from_req ( fd_g_config->cnf_dict, cer, MSGFL_ANSW_ERROR ), goto destroy );
+	CHECK_FCT_DO( fd_msg_rescode_set(*cer, rescode, errormsg, NULL, 1 ), goto destroy );
+	CHECK_FCT_DO( fd_out_send(cer, recv_cnx, NULL, FD_CNX_ORDERED), goto destroy );
+	
+	/* And now destroy this connection */
+destroy:
+	fd_cnx_destroy(recv_cnx);
+	if (*cer) {
+		fd_msg_free(*cer);
+		*cer = NULL;
+	}
+}
+
+/* We have established a new connection to the remote peer, send CER and eventually process the election */
+int fd_p_ce_handle_newcnx(struct fd_peer * peer, struct cnxctx * initiator)
+{
+	struct msg * cer = NULL;
+	
+	/* Send CER on the new connection */
+	CHECK_FCT( create_CER(peer, initiator, &cer) );
+	CHECK_FCT( fd_out_send(&cer, initiator, peer, FD_CNX_ORDERED) );
+	
+	/* Are we doing an election ? */
+	fd_cpu_flush_cache();
+	if (peer->p_hdr.info.runtime.pir_state == STATE_WAITCNXACK_ELEC) {
+		if (election_result(peer)) {
+			/* Close initiator connection */
+			fd_cnx_destroy(initiator);
+
+			/* Process with the receiver side */
+			CHECK_FCT( fd_p_ce_process_receiver(peer) );
+
+		} else {
+
+			/* Answer an ELECTION LOST to the receiver side */
+			receiver_reject(peer->p_receiver, &peer->p_cer, "ELECTION_LOST", NULL);
+			peer->p_receiver = NULL;
+			CHECK_FCT( to_waitcea(peer, initiator) );
+		}
+	} else {
+		/* No election (yet) */
+		CHECK_FCT( to_waitcea(peer, initiator) );
+	}
+	
+	return 0;
+}
+
+/* We have received a Capabilities Exchange message on the peer connection */
+int fd_p_ce_msgrcv(struct msg ** msg, int req, struct fd_peer * peer)
+{
+	char * ec;
+	uint32_t rc = 0;
+	TRACE_ENTRY("%p %p", msg, peer);
+	CHECK_PARAMS( msg && *msg && CHECK_PEER(peer) );
+	
+	/* The only valid situation where we are called is in WAITCEA and we receive a CEA (we may have won an election) */
+	
+	/* Note : to implement Capabilities Update, we would need to change here */
+	
+	/* If it is a CER, just reply an error */
+	if (req) {
+		/* Create the error message */
+		CHECK_FCT( fd_msg_new_answer_from_req ( fd_g_config->cnf_dict, msg, MSGFL_ANSW_ERROR ) );
+		
+		/* Set the error code */
+		CHECK_FCT( fd_msg_rescode_set(*msg, "DIAMETER_COMMAND_UNSUPPORTED", "No CER allowed in current state", NULL, 1 ) );
+
+		/* msg now contains an answer message to send back */
+		CHECK_FCT_DO( fd_out_send(msg, NULL, peer, FD_CNX_ORDERED), /* In case of error the message has already been dumped */ );
+	}
+	
+	/* If the state is not WAITCEA, just discard the message */
+	fd_cpu_flush_cache();
+	if (req || (peer->p_hdr.info.runtime.pir_state != STATE_WAITCEA)) {
+		if (*msg) {
+			fd_log_debug("Received CER/CEA message while in state '%s', discarded.\n", STATE_STR(peer->p_hdr.info.runtime.pir_state));
+			fd_msg_dump_walk(NONE, *msg);
+			CHECK_FCT_DO( fd_msg_free(*msg), /* continue */);
+			*msg = NULL;
+		}
+		
+		return 0;
+	}
+	
+	/* Save info from the CEA into the peer */
+	CHECK_FCT_DO( save_remote_CE_info(*msg, peer, &ec, &rc), goto cleanup );
+	
+	/* Dispose of the message, we don't need it anymore */
+	CHECK_FCT_DO( fd_msg_free(*msg), /* continue */ );
+	*msg = NULL;
+	
+	/* Check the Result-Code */
+	switch (rc) {
+		case ER_DIAMETER_SUCCESS:
+			/* No problem, we can continue */
+			break;
+			
+		case ER_DIAMETER_TOO_BUSY:
+			/* Retry later */
+			TRACE_DEBUG(INFO, "Peer %s replied a CEA with Result-Code AVP DIAMETER_TOO_BUSY, will retry later.", peer->p_hdr.info.pi_diamid);
+			fd_psm_cleanup(peer, 0);
+			fd_psm_next_timeout(peer, 0, 300);
+			return 0;
+		
+		case ER_ELECTION_LOST:
+			/* Ok, just wait for a little while for the CER to be processed on the other connection. */
+			TRACE_DEBUG(FULL, "Peer %s replied a CEA with Result-Code AVP ELECTION_LOST, waiting for events.", peer->p_hdr.info.pi_diamid);
+			return 0;
+		
+		default:
+			/* In any other case, we abort all attempts to connect to this peer */
+			TRACE_DEBUG(INFO, "Peer %s replied a CEA with Result-Code AVP %d, aborting connection attempts.", peer->p_hdr.info.pi_diamid, rc);
+			return EINVAL;
+	}
+	
+	/* Handshake if needed, start clear otherwise */
+	if ( ! fd_cnx_getTLS(peer->p_cnxctx) ) {
+		int todo = peer->p_hdr.info.config.pic_flags.sec & peer->p_hdr.info.runtime.pir_isi ;
+		/* Special case: if the peer did not send a ISI AVP */
+		if (peer->p_hdr.info.runtime.pir_isi == 0)
+			todo = peer->p_hdr.info.config.pic_flags.sec;
+		
+		if (todo == PI_SEC_NONE) {
+			/* Ok for clear connection */
+			TRACE_DEBUG(INFO, "No TLS protection negotiated with peer '%s'.", peer->p_hdr.info.pi_diamid);
+			CHECK_FCT( fd_cnx_start_clear(peer->p_cnxctx, 1) );
+		} else {
+			
+			fd_psm_change_state(peer, STATE_OPEN_HANDSHAKE);
+			CHECK_FCT_DO( fd_cnx_handshake(peer->p_cnxctx, GNUTLS_CLIENT, peer->p_hdr.info.config.pic_priority, NULL),
+				{
+					/* Handshake failed ...  */
+					fd_log_debug("TLS Handshake failed with peer '%s', resetting the connection\n", peer->p_hdr.info.pi_diamid);
+					goto cleanup;
+				} );
+
+			/* Retrieve the credentials */
+			CHECK_FCT( fd_cnx_getcred(peer->p_cnxctx, &peer->p_hdr.info.runtime.pir_cert_list, &peer->p_hdr.info.runtime.pir_cert_list_size) );
+		}
+	}
+	
+	/* Move to next state */
+	if (peer->p_flags.pf_cnx_pb) {
+		fd_psm_change_state(peer, STATE_REOPEN );
+		CHECK_FCT( fd_p_dw_reopen(peer) );
+	} else {
+		fd_psm_change_state(peer, STATE_OPEN );
+		fd_psm_next_timeout(peer, 1, peer->p_hdr.info.config.pic_twtimer ?: fd_g_config->cnf_timer_tw);
+	}
+	
+	return 0;
+	
+cleanup:
+	fd_p_ce_clear_cnx(peer, NULL);
+
+	/* Send the error to the peer */
+	CHECK_FCT( fd_event_send(peer->p_events, FDEVP_CNX_ERROR, 0, NULL) );
+
+	return 0;
+}
+
+/* Handle the receiver side to go to OPEN state (any election is resolved) */
+int fd_p_ce_process_receiver(struct fd_peer * peer)
+{
+	char * ec = NULL;
+	struct msg * msg = NULL;
+	int isi = 0;
+	int fatal = 0;
+	
+	TRACE_ENTRY("%p", peer);
+	
+	CHECK_FCT( set_peer_cnx(peer, &peer->p_receiver) );
+	msg = peer->p_cer;
+	peer->p_cer = NULL;
+	
+	/* Parse the content of the received CER */
+	CHECK_FCT_DO( save_remote_CE_info(msg, peer, &ec, NULL), goto error_abort );
+	
+	/* Validate the peer if needed */
+	if (peer->p_flags.pf_responder) {
+		int res = fd_peer_validate( peer );
+		if (res < 0) {
+			TRACE_DEBUG(INFO, "Rejected CER from peer '%s', validation failed (returning DIAMETER_UNKNOWN_PEER).\n", peer->p_hdr.info.pi_diamid);
+			ec = "DIAMETER_UNKNOWN_PEER";
+			goto error_abort;
+		}
+		CHECK_FCT( res );
+	}
+	
+	/* Check if we have common applications */
+	if ( fd_g_config->cnf_flags.no_fwd && (! peer->p_hdr.info.runtime.pir_relay) ) {
+		int got_common;
+		CHECK_FCT( fd_app_check_common( &fd_g_config->cnf_apps, &peer->p_hdr.info.runtime.pir_apps, &got_common) );
+		if (!got_common) {
+			TRACE_DEBUG(INFO, "No common application with peer '%s', sending DIAMETER_NO_COMMON_APPLICATION", peer->p_hdr.info.pi_diamid);
+			ec = "DIAMETER_NO_COMMON_APPLICATION";
+			fatal = 1;
+			goto error_abort;
+		}
+	}
+	
+	/* Do we agree on ISI ? */
+	if ( ! fd_cnx_getTLS(peer->p_cnxctx) ) {
+		
+		/* In case of responder, the validate callback must have set the config.pic_flags.sec value already */
+	
+		/* First case: we are not using old mechanism: ISI are deprecated, we ignore it. */
+		if ( ! (peer->p_hdr.info.config.pic_flags.sec & PI_SEC_TLS_OLD)) {
+			/* Just check then that the peer configuration allows for IPsec protection */
+			if (peer->p_hdr.info.config.pic_flags.sec & PI_SEC_NONE) {
+				isi = PI_SEC_NONE;
+			} else {
+				/* otherwise, we should have already been protected. Reject */
+				TRACE_DEBUG(INFO, "Non TLS-protected CER/CEA exchanges are not allowed with this peer, rejecting.");
+			}
+		} else {
+			/* The old mechanism is allowed with this peer. Now, look into the ISI AVP values */
+			
+			/* In case no ISI was present anyway: */
+			if (!peer->p_hdr.info.runtime.pir_isi) {
+				TRACE_DEBUG(INFO, "Inband-Security-Id AVP is missing in received CER.");
+				if (peer->p_hdr.info.config.pic_flags.sec & PI_SEC_NONE) {
+					isi = PI_SEC_NONE;
+					TRACE_DEBUG(INFO, "IPsec protection allowed by configuration, allowing this mechanism to be used.");
+				} else {
+					/* otherwise, we should have already been protected. Reject */
+					TRACE_DEBUG(INFO, "Rejecting the peer connection (please allow IPsec here or configure TLS in the remote peer).");
+				}
+			} else {
+				/* OK, the remote peer did send the ISI AVP. */
+				if ((peer->p_hdr.info.config.pic_flags.sec & PI_SEC_NONE) && (peer->p_hdr.info.runtime.pir_isi & PI_SEC_NONE)) {
+					/* We have allowed IPsec */
+					isi = PI_SEC_NONE;
+				} else if (peer->p_hdr.info.runtime.pir_isi & PI_SEC_TLS_OLD) {
+					/* We can agree on TLS */
+					isi = PI_SEC_TLS_OLD;
+				} else {
+					TRACE_DEBUG(INFO, "Remote peer requested IPsec protection, but local configuration forbids it.");
+				}
+			}
+		}
+	
+		/* If we did not find an agreement */
+		if (!isi) {
+			TRACE_DEBUG(INFO, "No common security mechanism with '%s', sending DIAMETER_NO_COMMON_SECURITY", peer->p_hdr.info.pi_diamid);
+			ec = "DIAMETER_NO_COMMON_SECURITY";
+			fatal = 1;
+			goto error_abort;
+		}
+		
+		/* Do not send the ISI IPsec if we are using the new mechanism */
+		if ((isi == PI_SEC_NONE) && (! (peer->p_hdr.info.config.pic_flags.sec & PI_SEC_TLS_OLD)))
+			isi = 0;
+	}
+	
+	/* Reply a CEA */
+	CHECK_FCT( fd_msg_new_answer_from_req ( fd_g_config->cnf_dict, &msg, 0 ) );
+	CHECK_FCT( fd_msg_rescode_set(msg, "DIAMETER_SUCCESS", NULL, NULL, 0 ) );
+	CHECK_FCT( add_CE_info(msg, peer->p_cnxctx, isi & PI_SEC_TLS_OLD, isi & PI_SEC_NONE) );
+#ifdef USE_CEA_BROADCAST
+	CHECK_FCT( fd_out_send(&msg, peer->p_cnxctx, peer, (isi & PI_SEC_TLS_OLD) ? FD_CNX_ORDERED : FD_CNX_BROADCAST) ); /* Broadcast in order to avoid further messages sent over a different stream be delivered first... */
+#else /* USE_CEA_BROADCAST */
+	CHECK_FCT( fd_out_send(&msg, peer->p_cnxctx, peer, FD_CNX_ORDERED ) );
+#endif /* USE_CEA_BROADCAST */
+	
+	/* Handshake if needed */
+	if (isi & PI_SEC_TLS_OLD) {
+		fd_psm_change_state(peer, STATE_OPEN_HANDSHAKE);
+		CHECK_FCT_DO( fd_cnx_handshake(peer->p_cnxctx, GNUTLS_SERVER, peer->p_hdr.info.config.pic_priority, NULL),
+			{
+				/* Handshake failed ...  */
+				fd_log_debug("TLS Handshake failed with peer '%s', resetting the connection\n", peer->p_hdr.info.pi_diamid);
+				goto cleanup;
+			} );
+		
+		/* Retrieve the credentials */
+		CHECK_FCT( fd_cnx_getcred(peer->p_cnxctx, &peer->p_hdr.info.runtime.pir_cert_list, &peer->p_hdr.info.runtime.pir_cert_list_size) );
+		
+		/* Call second validation callback if needed */
+		if (peer->p_cb2) {
+			TRACE_DEBUG(FULL, "Calling second validation callback for %s", peer->p_hdr.info.pi_diamid);
+			CHECK_FCT_DO( (*peer->p_cb2)( &peer->p_hdr.info ),
+				{
+					TRACE_DEBUG(INFO, "Validation callback rejected the peer %s after handshake", peer->p_hdr.info.pi_diamid);
+					CHECK_FCT( fd_psm_terminate( peer, "DO_NOT_WANT_TO_TALK_TO_YOU" ) );
+					return 0;
+				}  );
+		}
+		
+	} else {
+		if ( ! fd_cnx_getTLS(peer->p_cnxctx) ) {
+			TRACE_DEBUG(INFO, "No TLS protection negotiated with peer '%s'.", peer->p_hdr.info.pi_diamid);
+			CHECK_FCT( fd_cnx_start_clear(peer->p_cnxctx, 1) );
+		}
+	}
+		
+	/* Move to OPEN or REOPEN state */
+	if (peer->p_flags.pf_cnx_pb) {
+		fd_psm_change_state(peer, STATE_REOPEN );
+		CHECK_FCT( fd_p_dw_reopen(peer) );
+	} else {
+		fd_psm_change_state(peer, STATE_OPEN );
+		fd_psm_next_timeout(peer, 1, peer->p_hdr.info.config.pic_twtimer ?: fd_g_config->cnf_timer_tw);
+	}
+	
+	return 0;
+
+error_abort:
+	if (ec) {
+		/* Create the error message */
+		CHECK_FCT( fd_msg_new_answer_from_req ( fd_g_config->cnf_dict, &msg, MSGFL_ANSW_ERROR ) );
+
+		/* Set the error code */
+		CHECK_FCT( fd_msg_rescode_set(msg, ec, NULL, NULL, 1 ) );
+
+		/* msg now contains an answer message to send back */
+		CHECK_FCT_DO( fd_out_send(&msg, peer->p_cnxctx, peer, FD_CNX_ORDERED), /* In case of error the message has already been dumped */ );
+	}
+	
+cleanup:
+	if (msg) {
+		fd_msg_free(msg);
+	}
+	fd_p_ce_clear_cnx(peer, NULL);
+
+	/* Send the error to the peer */
+	CHECK_FCT( fd_event_send(peer->p_events, fatal ? FDEVP_TERMINATE : FDEVP_CNX_ERROR, 0, NULL) );
+
+	return 0;
+}
+
+/* We have received a CER on a new connection for this peer */
+int fd_p_ce_handle_newCER(struct msg ** msg, struct fd_peer * peer, struct cnxctx ** cnx, int valid)
+{
+	fd_cpu_flush_cache();
+	switch (peer->p_hdr.info.runtime.pir_state) {
+		case STATE_CLOSED:
+			peer->p_receiver = *cnx;
+			*cnx = NULL;
+			peer->p_cer = *msg;
+			*msg = NULL;
+			CHECK_FCT( fd_p_ce_process_receiver(peer) );
+			break;
+
+		case STATE_WAITCNXACK:
+			/* Save the parameters in the peer, move to STATE_WAITCNXACK_ELEC */
+			peer->p_receiver = *cnx;
+			*cnx = NULL;
+			peer->p_cer = *msg;
+			*msg = NULL;
+			CHECK_FCT( fd_psm_change_state(peer, STATE_WAITCNXACK_ELEC) );
+			break;
+			
+		case STATE_WAITCEA:
+			if (election_result(peer)) {
+				
+				/* Close initiator connection (was already set as principal) */
+				fd_p_ce_clear_cnx(peer, NULL);
+				
+				/* and go on with the receiver side */
+				peer->p_receiver = *cnx;
+				*cnx = NULL;
+				peer->p_cer = *msg;
+				*msg = NULL;
+				CHECK_FCT( fd_p_ce_process_receiver(peer) );
+
+			} else {
+
+				/* Answer an ELECTION LOST to the receiver side and continue */
+				receiver_reject(*cnx, msg, "ELECTION_LOST", "Please answer my CER instead, you won the election.");
+				*cnx = NULL;
+			}
+			break;
+
+		default:
+			receiver_reject(*cnx, msg, "DIAMETER_UNABLE_TO_COMPLY", "Invalid state to receive a new connection attempt");
+			*cnx = NULL;
+	}
+				
+	return 0;
+}
Index: libfdcore/p_cnx.c
===================================================================
--- libfdcore/p_cnx.c	(revision 658)
+++ libfdcore/p_cnx.c	(revision 658)
@@ -0,0 +1,329 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@nict.go.jp>							 *
+*													 *
+* Copyright (c) 2010, 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 "fdcore-internal.h"
+
+/* This file contains code used by a peer state machine to initiate a connection to remote peer */
+
+struct next_conn {
+	struct fd_list	chain;
+	int		proto;	/* Protocol of the next attempt */
+	union {
+		sSS	ss;	/* The address, only for TCP */
+		sSA4	sin;
+		sSA6	sin6;
+	};
+	uint16_t	port;	/* The port, for SCTP (included in ss for TCP) */
+	int		dotls;	/* Handshake TLS after connection ? */
+};
+
+static __inline__ void failed_connection_attempt(struct fd_peer * peer)
+{
+	/* Simply remove the first item in the list if not empty */
+	if (! FD_IS_LIST_EMPTY(&peer->p_connparams) ) {
+		struct fd_list * li = peer->p_connparams.next;
+		fd_list_unlink(li);
+		free(li);
+	}
+}
+
+static void empty_connection_list(struct fd_peer * peer)
+{
+	/* Remove all items */
+	while (!FD_IS_LIST_EMPTY(&peer->p_connparams)) {
+		failed_connection_attempt(peer);
+	}
+}
+
+static int prepare_connection_list(struct fd_peer * peer)
+{
+	struct fd_list * li, *last_prio;
+	struct next_conn   * new; 
+	
+	uint16_t	port_no; /* network order */
+	int		dotls_immediate;
+	
+	TRACE_ENTRY("%p", peer);
+	 
+	/* Resolve peer address(es) if needed */
+	if (FD_IS_LIST_EMPTY(&peer->p_hdr.info.pi_endpoints)) {
+		struct addrinfo hints, *ai, *aip;
+		int ret;
+
+		memset(&hints, 0, sizeof(hints));
+		hints.ai_flags = AI_ADDRCONFIG;
+		ret = getaddrinfo(peer->p_hdr.info.pi_diamid, NULL, &hints, &ai);
+		if (ret) {
+			fd_log_debug("Unable to resolve address for peer '%s' (%s), aborting\n", peer->p_hdr.info.pi_diamid, gai_strerror(ret));
+			if (ret != EAI_AGAIN)
+				fd_psm_terminate( peer, NULL );
+			return 0;
+		}
+		
+		for (aip = ai; aip != NULL; aip = aip->ai_next) {
+			CHECK_FCT( fd_ep_add_merge( &peer->p_hdr.info.pi_endpoints, aip->ai_addr, aip->ai_addrlen, EP_FL_DISC ) );
+		}
+		freeaddrinfo(ai);
+	}
+	
+	/* Remove addresses from unwanted family */
+	if (peer->p_hdr.info.config.pic_flags.pro3) {
+		CHECK_FCT( fd_ep_filter_family(
+					&peer->p_hdr.info.pi_endpoints, 
+					(peer->p_hdr.info.config.pic_flags.pro3 == PI_P3_IP) ? 
+						AF_INET 
+						: AF_INET6));
+	}
+	if (fd_g_config->cnf_flags.no_ip4) {
+		CHECK_FCT( fd_ep_filter_family(
+					&peer->p_hdr.info.pi_endpoints, 
+					AF_INET6));
+	}
+	if (fd_g_config->cnf_flags.no_ip6) {
+		CHECK_FCT( fd_ep_filter_family(
+					&peer->p_hdr.info.pi_endpoints, 
+					AF_INET));
+	}
+	
+	/* Remove any local address that would be here, it should not happen but it does sometimes... */
+	CHECK_FCT( fd_ep_filter_list(&peer->p_hdr.info.pi_endpoints, &fd_g_config->cnf_endpoints) );
+	
+	/* Now check we have at least one address to attempt */
+	if (FD_IS_LIST_EMPTY(&peer->p_hdr.info.pi_endpoints)) {
+		fd_log_debug("No address %savailable to connect to peer '%s', aborting\n", peer->p_hdr.info.config.pic_flags.pro3 ? "in the configured family " : "", peer->p_hdr.info.pi_diamid);
+		fd_psm_terminate( peer, NULL );
+		return 0;
+	}
+	
+	/* Cleanup any previous list */
+	empty_connection_list(peer);
+	
+	/* Prepare the parameters */
+	if ((peer->p_hdr.info.config.pic_flags.sec != PI_SEC_DEFAULT) || (fd_g_config->cnf_flags.tls_alg)) {
+		dotls_immediate = 0;
+		port_no = htons(peer->p_hdr.info.config.pic_port ?: fd_g_config->cnf_port);
+	} else {
+		dotls_immediate = 1;
+		port_no = htons(peer->p_hdr.info.config.pic_port ?: fd_g_config->cnf_port_tls);
+	}
+	
+	last_prio = &peer->p_connparams;
+	
+	/* Create TCP parameters unless specified otherwise */
+	if ((!fd_g_config->cnf_flags.no_tcp) && (peer->p_hdr.info.config.pic_flags.pro4 != PI_P4_SCTP)) {
+		for (li = peer->p_hdr.info.pi_endpoints.next; li != &peer->p_hdr.info.pi_endpoints; li = li->next) {
+			struct fd_endpoint * ep = (struct fd_endpoint *)li;
+			
+			CHECK_MALLOC( new = malloc(sizeof(struct next_conn)) );
+			memset(new, 0, sizeof(struct next_conn));
+			fd_list_init(&new->chain, new);
+			
+			new->proto = IPPROTO_TCP;
+			
+			memcpy( &new->ss, &ep->ss, sizeof(sSS) );
+			switch (new->ss.ss_family) {
+				case AF_INET:
+					new->sin.sin_port = port_no;
+					break;
+				case AF_INET6:
+					new->sin6.sin6_port = port_no;
+					break;
+				default:
+					free(new);
+					continue; /* Move to the next endpoint */
+			}
+			
+			new->dotls = dotls_immediate;
+			
+			/* Add the new entry to the appropriate position (conf and disc go first) */
+			if (ep->flags & (EP_FL_CONF | EP_FL_DISC)) {
+				fd_list_insert_after(last_prio, &new->chain);
+				last_prio = &new->chain;
+			} else {
+				fd_list_insert_before(&peer->p_connparams, &new->chain);
+			}
+		}
+	}
+	
+	/* Now, add the SCTP entry, if not disabled */
+#ifndef DISABLE_SCTP
+	if ((!fd_g_config->cnf_flags.no_sctp) && (peer->p_hdr.info.config.pic_flags.pro4 != PI_P4_TCP)) {
+		struct next_conn   * new;
+		
+		CHECK_MALLOC( new = malloc(sizeof(struct next_conn)) );
+		memset(new, 0, sizeof(struct next_conn));
+		fd_list_init(&new->chain, new);
+
+		new->proto = IPPROTO_SCTP;
+		new->port  = ntohs(port_no); /* back to host byte order... */
+		new->dotls = dotls_immediate;
+
+		/* Add the new entry to the appropriate position (depending on preferences) */
+		if ((fd_g_config->cnf_flags.pr_tcp) || (peer->p_hdr.info.config.pic_flags.alg == PI_ALGPREF_TCP)) {
+			fd_list_insert_after(last_prio, &new->chain);
+		} else {
+			fd_list_insert_after(&peer->p_connparams, &new->chain); /* very first position */
+		}
+	}
+#endif /* DISABLE_SCTP */
+	
+	return 0;
+}
+
+
+/* The thread that attempts the connection */
+static void * connect_thr(void * arg)
+{
+	struct fd_peer * peer = arg;
+	struct cnxctx * cnx = NULL;
+	struct next_conn * nc = NULL;
+	int rebuilt = 0;
+	
+	TRACE_ENTRY("%p", arg);
+	CHECK_PARAMS_DO( CHECK_PEER(peer), return NULL );
+
+	/* Set the thread name */
+	{
+		char buf[48];
+		sprintf(buf, "ConnTo:%.*s", (int)(sizeof(buf)) - 8, peer->p_hdr.info.pi_diamid);
+		fd_log_threadname ( buf );
+	}
+	
+	do {
+		/* Rebuild the list if needed, if it is empty -- but at most once */
+		if (FD_IS_LIST_EMPTY(&peer->p_connparams)) {
+			if (! rebuilt) {
+				CHECK_FCT_DO( prepare_connection_list(peer), goto fatal_error );
+				rebuilt ++;
+			}
+			if (FD_IS_LIST_EMPTY(&peer->p_connparams)) {
+				/* We encountered an error or we have looped over all the addresses of the peer. */
+				TRACE_DEBUG(INFO, "Unable to connect to the peer %s, aborting attempts for now.", peer->p_hdr.info.pi_diamid);
+				CHECK_FCT_DO( fd_event_send(peer->p_events, FDEVP_CNX_FAILED, 0, NULL), goto fatal_error );
+				return NULL;
+			}
+		}
+		
+		/* Attempt connection to the first entry */
+		nc = (struct next_conn *)(peer->p_connparams.next);
+		
+		switch (nc->proto) {
+			case IPPROTO_TCP:
+				cnx = fd_cnx_cli_connect_tcp((sSA *)&nc->ss, sSAlen(&nc->ss));
+				break;
+#ifndef DISABLE_SCTP			
+			case IPPROTO_SCTP:
+				cnx = fd_cnx_cli_connect_sctp((peer->p_hdr.info.config.pic_flags.pro3 == PI_P3_IP) ?: fd_g_config->cnf_flags.no_ip6, nc->port, &peer->p_hdr.info.pi_endpoints);
+				break;
+#endif /* DISABLE_SCTP */
+		}
+		
+		if (cnx)
+			break;
+		
+		/* Pop these parameters and continue */
+		failed_connection_attempt(peer);
+		
+		pthread_testcancel();
+		
+	} while (!cnx); /* and until cancellation */
+	
+	/* Now, we have an established connection in cnx */
+	
+	pthread_cleanup_push((void *)fd_cnx_destroy, cnx);
+	
+	/* Set the hostname in the connection, so that handshake verifies the remote identity */
+	fd_cnx_sethostname(cnx,peer->p_hdr.info.pi_diamid);
+	
+	/* Handshake if needed (secure port) */
+	if (nc->dotls) {
+		CHECK_FCT_DO( fd_cnx_handshake(cnx, GNUTLS_CLIENT, peer->p_hdr.info.config.pic_priority, NULL),
+			{
+				/* Handshake failed ...  */
+				fd_log_debug("TLS Handshake failed with peer '%s', resetting the connection\n", peer->p_hdr.info.pi_diamid);
+				fd_cnx_destroy(cnx);
+				empty_connection_list(peer);
+				fd_ep_filter(&peer->p_hdr.info.pi_endpoints, EP_FL_CONF);
+				return NULL;
+			} );
+	} else {
+		/* Prepare to receive the next message */
+		CHECK_FCT_DO( fd_cnx_start_clear(cnx, 0), goto fatal_error );
+	}
+	
+	/* Upon success, generate FDEVP_CNX_ESTABLISHED */
+	CHECK_FCT_DO( fd_event_send(peer->p_events, FDEVP_CNX_ESTABLISHED, 0, cnx), goto fatal_error );
+	
+	pthread_cleanup_pop(0);
+	
+	return NULL;
+	
+fatal_error:
+	/* Cleanup the connection */
+	if (cnx)
+		fd_cnx_destroy(cnx);
+
+	/* Generate a termination event */
+	CHECK_FCT_DO(fd_event_send(fd_g_config->cnf_main_ev, FDEV_TERMINATE, 0, NULL), );
+	
+	return NULL;
+}
+
+
+/* Initiate a connection attempt to a remote peer */
+int fd_p_cnx_init(struct fd_peer * peer)
+{
+	TRACE_ENTRY("%p", peer);
+	
+	/* Start the connect thread */
+	CHECK_FCT( pthread_create(&peer->p_ini_thr, NULL, connect_thr, peer) );
+	return 0;
+}
+
+/* Cancel a connection attempt */
+void fd_p_cnx_abort(struct fd_peer * peer, int cleanup_all)
+{
+	TRACE_ENTRY("%p %d", peer, cleanup_all);
+	CHECK_PARAMS_DO( CHECK_PEER(peer), return );
+	
+	if (peer->p_ini_thr != (pthread_t)NULL) {
+		CHECK_FCT_DO( fd_thr_term(&peer->p_ini_thr), /* continue */);
+		failed_connection_attempt(peer);
+	}
+	
+	if (cleanup_all) {
+		empty_connection_list(peer);
+	}
+}
Index: libfdcore/p_dp.c
===================================================================
--- libfdcore/p_dp.c	(revision 658)
+++ libfdcore/p_dp.c	(revision 658)
@@ -0,0 +1,174 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@nict.go.jp>							 *
+*													 *
+* Copyright (c) 2010, 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 "fdcore-internal.h"
+
+/* This file contains code to handle Disconnect Peer messages (DPR and DPA) */
+
+/* Handle a received message */
+int fd_p_dp_handle(struct msg ** msg, int req, struct fd_peer * peer)
+{
+	TRACE_ENTRY("%p %d %p", msg, req, peer);
+	
+	if (req) {
+		/* We received a DPR, save the Disconnect-Cause and terminate the connection */
+		struct avp * dc;
+		int delay = peer->p_hdr.info.config.pic_tctimer ?: fd_g_config->cnf_timer_tc;
+		
+		CHECK_FCT( fd_msg_search_avp ( *msg, fd_dict_avp_DC, &dc ));
+		if (dc) {
+			/* Check the value is consistent with the saved one */
+			struct avp_hdr * hdr;
+			CHECK_FCT(  fd_msg_avp_hdr( dc, &hdr )  );
+			if (hdr->avp_value == NULL) {
+				/* This is a sanity check */
+				TRACE_DEBUG(NONE, "BUG: Unset value in Disconnect-Cause in DPR");
+				fd_msg_dump_one(NONE, dc);
+				ASSERT(0); /* To check if this really happens, and understand why... */
+			}
+
+			peer->p_hdr.info.runtime.pir_lastDC = hdr->avp_value->u32;
+			
+			switch (hdr->avp_value->u32) {
+				case ACV_DC_REBOOTING:
+				default:
+					/* We use TcTimer to attempt reconnection */
+					break;
+				case ACV_DC_BUSY:
+					/* No need to hammer the overloaded peer */
+					delay *= 10;
+					break;
+				case ACV_DC_NOT_FRIEND:
+					/* He does not want to speak to us... let's retry a lot later maybe */
+					delay *= 200;
+					break;
+			}
+		}
+		if (TRACE_BOOL(INFO)) {
+			if (dc) {
+				struct dict_object * dictobj = NULL;
+				struct dict_enumval_request er;
+				memset(&er, 0, sizeof(er));
+				CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_TYPE, TYPE_OF_AVP, fd_dict_avp_DC, &er.type_obj, ENOENT )  );
+				er.search.enum_value.u32 = peer->p_hdr.info.runtime.pir_lastDC;
+				CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_ENUMVAL, ENUMVAL_BY_STRUCT, &er, &dictobj, 0 )  );
+				if (dictobj) {
+					CHECK_FCT( fd_dict_getval( dictobj, &er.search ) );
+					TRACE_DEBUG(INFO, "Peer '%s' sent a DPR with cause: %s\n", peer->p_hdr.info.pi_diamid, er.search.enum_name);
+				} else {
+					TRACE_DEBUG(INFO, "Peer '%s' sent a DPR with unknown cause: %u\n", peer->p_hdr.info.pi_diamid, peer->p_hdr.info.runtime.pir_lastDC);
+				}
+			} else {
+				TRACE_DEBUG(INFO, "Peer '%s' sent a DPR without Disconnect-Cause AVP\n", peer->p_hdr.info.pi_diamid);
+			}
+		}
+		
+		/* Now reply with a DPA */
+		CHECK_FCT( fd_msg_new_answer_from_req ( fd_g_config->cnf_dict, msg, 0 ) );
+		CHECK_FCT( fd_msg_rescode_set( *msg, "DIAMETER_SUCCESS", NULL, NULL, 1 ) );
+		
+		/* Move to CLOSING state to failover outgoing messages (and avoid failing the DPA...) */
+		CHECK_FCT( fd_psm_change_state(peer, STATE_CLOSING) );
+		
+		/* Now send the DPA */
+		CHECK_FCT( fd_out_send( msg, NULL, peer, FD_CNX_ORDERED) );
+		
+		/* Move to CLOSED state */
+		fd_psm_cleanup(peer, 0);
+		
+		/* Reset the timer for next connection attempt -- we'll retry sooner or later depending on the disconnection cause */
+		fd_psm_next_timeout(peer, 1, delay);
+		
+	} else {
+		/* We received a DPA */
+		fd_cpu_flush_cache();
+		if (peer->p_hdr.info.runtime.pir_state != STATE_CLOSING) {
+			TRACE_DEBUG(INFO, "Ignoring DPA received in state %s", STATE_STR(peer->p_hdr.info.runtime.pir_state));
+		}
+			
+		/* In theory, we should control the Result-Code AVP. But since we will not go back to OPEN state here anyway, let's skip it */
+		CHECK_FCT_DO( fd_msg_free( *msg ), /* continue */ );
+		*msg = NULL;
+		
+		/* The calling function handles cleaning the PSM and terminating the peer since we return in CLOSING state */
+	}
+	
+	return 0;
+}
+
+/* Start disconnection of a peer: send DPR */
+int fd_p_dp_initiate(struct fd_peer * peer, char * reason)
+{
+	struct msg * msg = NULL;
+	struct dict_object * dictobj = NULL;
+	struct avp * avp = NULL;
+	struct dict_enumval_request er;
+	union avp_value val;
+	
+	TRACE_ENTRY("%p %p", peer, reason);
+	
+	/* Create a new DWR instance */
+	CHECK_FCT( fd_msg_new ( fd_dict_cmd_DPR, MSGFL_ALLOC_ETEID, &msg ) );
+	
+	/* Add the Origin information */
+	CHECK_FCT( fd_msg_add_origin ( msg, 0 ) );
+	
+	/* Add the Disconnect-Cause */
+	CHECK_FCT( fd_msg_avp_new ( fd_dict_avp_DC, 0, &avp ) );
+	
+	/* Search the value in the dictionary */
+	memset(&er, 0, sizeof(er));
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_TYPE, TYPE_OF_AVP, fd_dict_avp_DC, &er.type_obj, ENOENT )  );
+	er.search.enum_name = reason ?: "REBOOTING";
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_ENUMVAL, ENUMVAL_BY_STRUCT, &er, &dictobj, ENOENT )  );
+	CHECK_FCT( fd_dict_getval( dictobj, &er.search ) );
+	
+	/* Set the value in the AVP */
+	val.u32 = er.search.enum_value.u32;
+	CHECK_FCT( fd_msg_avp_setvalue( avp, &val ) );
+	CHECK_FCT( fd_msg_avp_add( msg, MSG_BRW_LAST_CHILD, avp ) );
+	
+	/* Save the value also in the peer */
+	peer->p_hdr.info.runtime.pir_lastDC = val.u32;
+	
+	/* Update the peer state and timer */
+	CHECK_FCT( fd_psm_change_state(peer, STATE_CLOSING) );
+	fd_psm_next_timeout(peer, 0, DPR_TIMEOUT);
+	
+	/* Now send the DPR message */
+	CHECK_FCT_DO( fd_out_send(&msg, NULL, peer, FD_CNX_ORDERED), /* ignore since we are on timeout anyway */ );
+	
+	return 0;
+}
Index: libfdcore/p_dw.c
===================================================================
--- libfdcore/p_dw.c	(revision 658)
+++ libfdcore/p_dw.c	(revision 658)
@@ -0,0 +1,171 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@nict.go.jp>							 *
+*													 *
+* Copyright (c) 2010, 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 "fdcore-internal.h"
+
+/* This file contains code to handle Device Watchdog messages (DWR and DWA) */
+
+/* Check the value of Origin-State-Id is consistent in a DWR or  DWA -- we just log if it is not the case */
+static void check_state_id(struct msg * msg, struct fd_peer * peer)
+{
+	struct avp * osi;
+	/* Check if the request contains the Origin-State-Id */
+	CHECK_FCT_DO( fd_msg_search_avp ( msg, fd_dict_avp_OSI, &osi ), return );
+	if (osi) {
+		/* Check the value is consistent with the saved one */
+		struct avp_hdr * hdr;
+		CHECK_FCT_DO(  fd_msg_avp_hdr( osi, &hdr ), return  );
+		if (hdr->avp_value == NULL) {
+			/* This is a sanity check */
+			TRACE_DEBUG(NONE, "BUG: Unset value in Origin-State-Id in DWR / DWA");
+			fd_msg_dump_one(NONE, osi);
+			ASSERT(0); /* To check if this really happens, and understand why... */
+		}
+
+		if (peer->p_hdr.info.runtime.pir_orstate != hdr->avp_value->u32) {
+			fd_log_debug("Received a new Origin-State-Id from peer %s! (%x / %x)\n", 
+				peer->p_hdr.info.pi_diamid, 
+				hdr->avp_value->u32,
+				peer->p_hdr.info.runtime.pir_orstate );
+		}
+	}
+}
+
+/* Create and send a DWR */
+static int send_DWR(struct fd_peer * peer)
+{
+	struct msg * msg = NULL;
+	
+	/* Create a new DWR instance */
+	CHECK_FCT( fd_msg_new ( fd_dict_cmd_DWR, MSGFL_ALLOC_ETEID, &msg ) );
+	
+	/* Add the content of the message (only the origin) */
+	CHECK_FCT( fd_msg_add_origin ( msg, 1 ) );
+	
+	/* Now send this message */
+	CHECK_FCT( fd_out_send(&msg, NULL, peer, FD_CNX_ORDERED) );
+	
+	/* And mark the pending DW */
+	peer->p_flags.pf_dw_pending = 1;
+	
+	return 0;
+}
+
+/* Handle an incoming message */
+int fd_p_dw_handle(struct msg ** msg, int req, struct fd_peer * peer)
+{
+	int reset_tmr = 0;
+	
+	TRACE_ENTRY("%p %d %p", msg, req, peer);
+	
+	/* Check the value of OSI for information */
+	check_state_id(*msg, peer);
+	
+	if (req) {
+		/* If we receive a DWR, send back a DWA */
+		CHECK_FCT( fd_msg_new_answer_from_req ( fd_g_config->cnf_dict, msg, 0 ) );
+		CHECK_FCT( fd_msg_rescode_set( *msg, "DIAMETER_SUCCESS", NULL, NULL, 0 ) );
+		CHECK_FCT( fd_msg_add_origin ( *msg, 1 ) );
+		CHECK_FCT( fd_out_send( msg, peer->p_cnxctx, peer, FD_CNX_ORDERED) );
+		
+	} else {
+		/* Just discard the DWA */
+		CHECK_FCT_DO( fd_msg_free(*msg), /* continue */ );
+		*msg = NULL;
+		
+		/* And clear the pending DW flag */
+		peer->p_flags.pf_dw_pending = 0;
+	}
+	
+	/* Now update timeout */
+	if (req) {
+		/* Update timeout only if we did not already send a DWR ourselves */
+		reset_tmr = !peer->p_flags.pf_dw_pending;
+	} else {
+		/* Reset the timer */
+		reset_tmr = 1;
+	}
+	if (reset_tmr) {
+		fd_psm_next_timeout(peer, 1, peer->p_hdr.info.config.pic_twtimer ?: fd_g_config->cnf_timer_tw);
+	}
+	
+	/* If we are in REOPEN state, increment the counter */
+	fd_cpu_flush_cache();
+	if (peer->p_hdr.info.runtime.pir_state == STATE_REOPEN) {
+		peer->p_flags.pf_reopen_cnt += 1;
+		
+		if (peer->p_flags.pf_reopen_cnt) {
+			/* Send a new DWR */
+			CHECK_FCT( send_DWR(peer) );
+		} else {
+			/* Move to OPEN state */
+			CHECK_FCT( fd_psm_change_state(peer, STATE_OPEN) );
+		}
+	}
+		
+	return 0;
+}
+
+/* Handle a timeout in the PSM (OPEN or REOPEN state only) */
+int fd_p_dw_timeout(struct fd_peer * peer)
+{
+	TRACE_ENTRY("%p", peer);
+
+	if (peer->p_flags.pf_dw_pending) {
+		/* We have sent a DWR and received no answer during TwTimer */
+		CHECK_FCT( fd_psm_change_state(peer, STATE_SUSPECT) );
+		fd_psm_next_timeout(peer, 0, 2 * (peer->p_hdr.info.config.pic_twtimer ?: fd_g_config->cnf_timer_tw) );
+	} else {
+		/* The timeout has expired, send a DWR */
+		CHECK_FCT( send_DWR(peer) );
+		fd_psm_next_timeout(peer, 0, peer->p_hdr.info.config.pic_twtimer ?: fd_g_config->cnf_timer_tw );
+	}
+	
+	return 0;
+}
+
+/* Handle DW exchanges after the peer has come alive again */
+int fd_p_dw_reopen(struct fd_peer * peer)
+{
+	TRACE_ENTRY("%p", peer);
+
+	peer->p_flags.pf_reopen_cnt = 1;
+	peer->p_flags.pf_cnx_pb = 0;
+	CHECK_FCT( send_DWR(peer) );
+	
+	return 0;
+}
+
+
Index: libfdcore/p_expiry.c
===================================================================
--- libfdcore/p_expiry.c	(revision 658)
+++ libfdcore/p_expiry.c	(revision 658)
@@ -0,0 +1,207 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@nict.go.jp>							 *
+*													 *
+* Copyright (c) 2010, 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 "fdcore-internal.h"
+
+/* Delay for garbage collection of expired peers, in seconds */
+#define GC_TIME		120
+
+static pthread_t       exp_thr;
+static pthread_t       gc_thr;
+static struct fd_list  exp_list = FD_LIST_INITIALIZER( exp_list );
+static pthread_cond_t  exp_cnd  = PTHREAD_COND_INITIALIZER;
+static pthread_mutex_t exp_mtx  = PTHREAD_MUTEX_INITIALIZER;
+
+static void * gc_th_fct(void * arg)
+{
+	fd_log_threadname ( "Peers/garb. col." );
+	TRACE_ENTRY( "%p", arg );
+	
+	do {
+		struct fd_list * li, purge = FD_LIST_INITIALIZER(purge);
+		
+		sleep(GC_TIME);	/* sleep is a cancellation point */
+		
+		/* Now check in the peers list if any peer can be deleted */
+		CHECK_FCT_DO( pthread_rwlock_wrlock(&fd_g_peers_rw), goto error );
+		
+		for (li = fd_g_peers.next; li != &fd_g_peers; li = li->next) {
+			struct fd_peer * peer = (struct fd_peer *)li;
+			
+			fd_cpu_flush_cache();
+			if (peer->p_hdr.info.runtime.pir_state != STATE_ZOMBIE)
+				continue;
+			
+			if (peer->p_hdr.info.config.pic_flags.persist == PI_PRST_ALWAYS)
+				continue; /* This peer was not supposed to terminate, keep it in the list for debug */
+			
+			/* Ok, the peer was expired, let's remove it */
+			li = li->prev; /* to avoid breaking the loop */
+			fd_list_unlink(&peer->p_hdr.chain);
+			fd_list_insert_before(&purge, &peer->p_hdr.chain);
+		}
+
+		CHECK_FCT_DO( pthread_rwlock_unlock(&fd_g_peers_rw), goto error );
+		
+		/* Now delete peers that are in the purge list */
+		while (!FD_IS_LIST_EMPTY(&purge)) {
+			struct fd_peer * peer = (struct fd_peer *)(purge.next);
+			fd_list_unlink(&peer->p_hdr.chain);
+			TRACE_DEBUG(INFO, "Garbage Collect: delete zombie peer '%s'", peer->p_hdr.info.pi_diamid);
+			CHECK_FCT_DO( fd_peer_free(&peer), /* Continue... what else to do ? */ );
+		}
+	} while (1);
+	
+error:
+	TRACE_DEBUG(INFO, "An error occurred in peers module! GC thread is terminating...");
+	ASSERT(0);
+	CHECK_FCT_DO(fd_event_send(fd_g_config->cnf_main_ev, FDEV_TERMINATE, 0, NULL), );
+	return NULL;
+}
+
+
+static void * exp_th_fct(void * arg)
+{
+	fd_log_threadname ( "Peers/expire" );
+	TRACE_ENTRY( "%p", arg );
+	
+	CHECK_POSIX_DO( pthread_mutex_lock(&exp_mtx),  goto error );
+	pthread_cleanup_push( fd_cleanup_mutex, &exp_mtx );
+	
+	do {
+		struct timespec	now;
+		struct fd_peer * first;
+		
+		/* Check if there are expiring sessions available */
+		if (FD_IS_LIST_EMPTY(&exp_list)) {
+			/* Just wait for a change or cancelation */
+			CHECK_POSIX_DO( pthread_cond_wait( &exp_cnd, &exp_mtx ), goto error );
+			/* Restart the loop on wakeup */
+			continue;
+		}
+		
+		/* Get the pointer to the peer that expires first */
+		first = (struct fd_peer *)(exp_list.next->o);
+		ASSERT( CHECK_PEER(first) );
+		
+		/* Get the current time */
+		CHECK_SYS_DO(  clock_gettime(CLOCK_REALTIME, &now),  goto error  );
+
+		/* If first peer is not expired, we just wait until it happens */
+		if ( TS_IS_INFERIOR( &now, &first->p_exp_timer ) ) {
+			
+			CHECK_POSIX_DO2(  pthread_cond_timedwait( &exp_cnd, &exp_mtx, &first->p_exp_timer ),  
+					ETIMEDOUT, /* ETIMEDOUT is a normal return value, continue */,
+					/* on other error, */ goto error );
+	
+			/* on wakeup, loop */
+			continue;
+		}
+		
+		/* Now, the first peer in the list is expired; signal it */
+		fd_list_unlink( &first->p_expiry );
+		CHECK_FCT_DO( fd_event_send(first->p_events, FDEVP_TERMINATE, 0, "DO_NOT_WANT_TO_TALK_TO_YOU"), goto error );
+		
+	} while (1);
+	
+	pthread_cleanup_pop( 1 );
+error:
+	TRACE_DEBUG(INFO, "An error occurred in peers module! Expiry thread is terminating...");
+	ASSERT(0);
+	CHECK_FCT_DO(fd_event_send(fd_g_config->cnf_main_ev, FDEV_TERMINATE, 0, NULL), );
+	return NULL;
+}
+
+/* Initialize peers expiry mechanism */
+int fd_p_expi_init(void)
+{
+	TRACE_ENTRY();
+	CHECK_FCT( pthread_create( &exp_thr, NULL, exp_th_fct, NULL ) );
+	CHECK_FCT( pthread_create( &gc_thr,  NULL, gc_th_fct,  NULL ) );
+	return 0;
+}
+
+/* Finish peers expiry mechanism */
+int fd_p_expi_fini(void)
+{
+	CHECK_FCT_DO( fd_thr_term(&exp_thr), );
+	CHECK_POSIX( pthread_mutex_lock(&exp_mtx) );
+	while (!FD_IS_LIST_EMPTY(&exp_list)) {
+		struct fd_peer * peer = (struct fd_peer *)(exp_list.next->o);
+		fd_list_unlink(&peer->p_expiry );
+	}
+	CHECK_POSIX( pthread_mutex_unlock(&exp_mtx) );
+	
+	CHECK_FCT_DO( fd_thr_term(&gc_thr), );
+	return 0;
+}
+
+/* Add / requeue a peer in the expiry list */
+int fd_p_expi_update(struct fd_peer * peer )
+{
+	TRACE_ENTRY("%p", peer);
+	CHECK_PARAMS( CHECK_PEER(peer) );
+	
+	CHECK_POSIX( pthread_mutex_lock(&exp_mtx) );
+	
+	fd_list_unlink(&peer->p_expiry );
+	
+	/* if peer expires */
+	if (peer->p_hdr.info.config.pic_flags.exp) {
+		struct fd_list * li;
+		
+		/* update the p_exp_timer value */
+		CHECK_SYS(  clock_gettime(CLOCK_REALTIME, &peer->p_exp_timer)  );
+		peer->p_exp_timer.tv_sec += peer->p_hdr.info.config.pic_lft;
+		
+		/* add to the expiry list in appropriate position (probably around the end) */
+		for (li = exp_list.prev; li != &exp_list; li = li->prev) {
+			struct fd_peer * p = (struct fd_peer *)(li->o);
+			if (TS_IS_INFERIOR( &p->p_exp_timer, &peer->p_exp_timer ) )
+				break;
+		}
+		
+		fd_list_insert_after(li, &peer->p_expiry);
+		
+		/* signal the expiry thread if we added in first position */
+		if (li == &exp_list) {
+			CHECK_POSIX( pthread_cond_signal(&exp_cnd) );
+		}
+	}
+	
+	CHECK_POSIX( pthread_mutex_unlock(&exp_mtx) );
+	return 0;
+}
+
Index: libfdcore/p_out.c
===================================================================
--- libfdcore/p_out.c	(revision 658)
+++ libfdcore/p_out.c	(revision 658)
@@ -0,0 +1,198 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@nict.go.jp>							 *
+*													 *
+* Copyright (c) 2010, 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 "fdcore-internal.h"
+
+/* Alloc a new hbh for requests, bufferize the message and send on the connection, save in sentreq if provided */
+static int do_send(struct msg ** msg, uint32_t flags, struct cnxctx * cnx, uint32_t * hbh, struct sr_list * srl)
+{
+	struct msg_hdr * hdr;
+	int msg_is_a_req;
+	uint8_t * buf;
+	size_t sz;
+	int ret;
+	uint32_t bkp_hbh = 0;
+	
+	TRACE_ENTRY("%p %x %p %p %p", msg, flags, cnx, hbh, srl);
+	
+	/* Retrieve the message header */
+	CHECK_FCT( fd_msg_hdr(*msg, &hdr) );
+	
+	msg_is_a_req = (hdr->msg_flags & CMD_FLAG_REQUEST);
+	if (msg_is_a_req) {
+		CHECK_PARAMS(hbh && srl);
+		/* Alloc the hop-by-hop id and increment the value for next message */
+		bkp_hbh = hdr->msg_hbhid;
+		hdr->msg_hbhid = *hbh;
+		*hbh = hdr->msg_hbhid + 1;
+	}
+	
+	/* Log the message */
+	if (TRACE_BOOL(FULL)) {
+		CHECK_FCT_DO(  fd_msg_update_length(*msg), /* continue */  );
+		TRACE_DEBUG(FULL, "Sending the following message on connection '%s':", fd_cnx_getid(cnx));
+		fd_msg_dump_walk(FULL, *msg);
+	}
+	
+	/* Create the message buffer */
+	CHECK_FCT(fd_msg_bufferize( *msg, &buf, &sz ));
+	pthread_cleanup_push( free, buf );
+	
+	/* Save a request before sending so that there is no race condition with the answer */
+	if (msg_is_a_req) {
+		CHECK_FCT_DO( ret = fd_p_sr_store(srl, msg, &hdr->msg_hbhid, bkp_hbh), { free(buf); return ret; } );
+	}
+	
+	/* Send the message */
+	CHECK_FCT_DO( ret = fd_cnx_send(cnx, buf, sz, flags), { free(buf); return ret; } );
+	pthread_cleanup_pop(1);
+	
+	/* Free remaining messages (i.e. answers) */
+	if (*msg) {
+		CHECK_FCT( fd_msg_free(*msg) );
+		*msg = NULL;
+	}
+	
+	return 0;
+}
+
+static void cleanup_requeue(void * arg)
+{
+	struct msg *msg = arg;
+	CHECK_FCT_DO(fd_fifo_post(fd_g_outgoing, &msg),
+			CHECK_FCT_DO(fd_msg_free(msg), /* What can we do more? */));
+}
+
+/* The code of the "out" thread */
+static void * out_thr(void * arg)
+{
+	struct fd_peer * peer = arg;
+	ASSERT( CHECK_PEER(peer) );
+	
+	/* Set the thread name */
+	{
+		char buf[48];
+		sprintf(buf, "OUT/%.*s", (int)sizeof(buf) - 5, peer->p_hdr.info.pi_diamid);
+		fd_log_threadname ( buf );
+	}
+	
+	/* Loop until cancelation */
+	while (1) {
+		struct msg * msg;
+		
+		/* Retrieve next message to send */
+		CHECK_FCT_DO( fd_fifo_get(peer->p_tosend, &msg), goto error );
+		
+		/* Now if we are cancelled, we requeue this message */
+		pthread_cleanup_push(cleanup_requeue, msg);
+		
+		/* Send the message, log any error */
+		CHECK_FCT_DO( do_send(&msg, 0, peer->p_cnxctx, &peer->p_hbh, &peer->p_sr),
+			{
+				if (msg) {
+					fd_log_debug("An error occurred while sending this message, it was lost:\n");
+					fd_msg_dump_walk(NONE, msg);
+					fd_msg_free(msg);
+				}
+			} );
+			
+		/* Loop */
+		pthread_cleanup_pop(0);
+	}
+	
+error:
+	/* It is not really a connection error, but the effect is the same, we are not able to send anymore message */
+	CHECK_FCT_DO( fd_event_send(peer->p_events, FDEVP_CNX_ERROR, 0, NULL), /* What do we do if it fails? */ );
+	return NULL;
+}
+
+/* Wrapper to sending a message either by out thread (peer in OPEN state) or directly; cnx or peer must be provided. Flags are valid only for direct sending, not through thread (unused) */
+int fd_out_send(struct msg ** msg, struct cnxctx * cnx, struct fd_peer * peer, uint32_t flags)
+{
+	TRACE_ENTRY("%p %p %p %x", msg, cnx, peer, flags);
+	CHECK_PARAMS( msg && *msg && (cnx || (peer && peer->p_cnxctx)));
+	
+	fd_cpu_flush_cache();
+	if (peer && (peer->p_hdr.info.runtime.pir_state == STATE_OPEN)) {
+		/* Normal case: just queue for the out thread to pick it up */
+		CHECK_FCT( fd_fifo_post(peer->p_tosend, msg) );
+		
+	} else {
+		uint32_t *hbh = NULL;
+		
+		/* In other cases, the thread is not running, so we handle the sending directly */
+		if (peer)
+			hbh = &peer->p_hbh;
+
+		if (!cnx)
+			cnx = peer->p_cnxctx;
+
+		/* Do send the message */
+		CHECK_FCT_DO( do_send(msg, flags, cnx, hbh, peer ? &peer->p_sr : NULL),
+			{
+				if (msg) {
+					fd_log_debug("An error occurred while sending this message, it was lost:\n");
+					fd_msg_dump_walk(NONE, *msg);
+					fd_msg_free(*msg);
+					*msg = NULL;
+				}
+			} );
+	}
+	
+	return 0;
+}
+
+/* Start the "out" thread that picks messages in p_tosend and send them on p_cnxctx */
+int fd_out_start(struct fd_peer * peer)
+{
+	TRACE_ENTRY("%p", peer);
+	CHECK_PARAMS( CHECK_PEER(peer) && (peer->p_outthr == (pthread_t)NULL) );
+	
+	CHECK_POSIX( pthread_create(&peer->p_outthr, NULL, out_thr, peer) );
+	
+	return 0;
+}
+
+/* Stop that thread */
+int fd_out_stop(struct fd_peer * peer)
+{
+	TRACE_ENTRY("%p", peer);
+	CHECK_PARAMS( CHECK_PEER(peer) );
+	
+	CHECK_FCT( fd_thr_term(&peer->p_outthr) );
+	
+	return 0;
+}
+		
Index: libfdcore/p_psm.c
===================================================================
--- libfdcore/p_psm.c	(revision 658)
+++ libfdcore/p_psm.c	(revision 658)
@@ -0,0 +1,775 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@nict.go.jp>							 *
+*													 *
+* Copyright (c) 2010, 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 "fdcore-internal.h"
+
+/* The actual declaration of peer_state_str */
+DECLARE_STATE_STR();
+
+/* Helper for next macro */
+#define case_str( _val )		\
+	case _val : return #_val
+
+DECLARE_PEV_STR();
+
+/************************************************************************/
+/*                      Delayed startup                                 */
+/************************************************************************/
+static int started = 0;
+static pthread_mutex_t  started_mtx = PTHREAD_MUTEX_INITIALIZER;
+static pthread_cond_t   started_cnd = PTHREAD_COND_INITIALIZER;
+
+/* Wait for start signal */
+static int fd_psm_waitstart()
+{
+	TRACE_ENTRY("");
+	CHECK_POSIX( pthread_mutex_lock(&started_mtx) );
+awake:	
+	if (! started) {
+		pthread_cleanup_push( fd_cleanup_mutex, &started_mtx );
+		CHECK_POSIX( pthread_cond_wait(&started_cnd, &started_mtx) );
+		pthread_cleanup_pop( 0 );
+		goto awake;
+	}
+	CHECK_POSIX( pthread_mutex_unlock(&started_mtx) );
+	return 0;
+}
+
+/* Allow the state machines to start */
+int fd_psm_start()
+{
+	TRACE_ENTRY("");
+	CHECK_POSIX( pthread_mutex_lock(&started_mtx) );
+	started = 1;
+	CHECK_POSIX( pthread_cond_broadcast(&started_cnd) );
+	CHECK_POSIX( pthread_mutex_unlock(&started_mtx) );
+	return 0;
+}
+
+
+/************************************************************************/
+/*                 Manage the list of active peers                      */
+/************************************************************************/
+
+/* Enter/leave OPEN state */
+static int enter_open_state(struct fd_peer * peer)
+{
+	struct fd_list * li;
+	CHECK_PARAMS( FD_IS_LIST_EMPTY(&peer->p_actives) );
+	
+	/* Callback registered by the credential validator (fd_peer_validate_register) */
+	if (peer->p_cb2) {
+		CHECK_FCT_DO( (*peer->p_cb2)(&peer->p_hdr.info),
+			{
+				TRACE_DEBUG(FULL, "Validation failed, terminating the connection");
+				fd_psm_terminate(peer, "DO_NOT_WANT_TO_TALK_TO_YOU" );
+			} );
+		peer->p_cb2 = NULL;
+		return 0;
+	}
+	/* Insert in the active peers list */
+	CHECK_POSIX( pthread_rwlock_wrlock(&fd_g_activ_peers_rw) );
+	for (li = fd_g_activ_peers.next; li != &fd_g_activ_peers; li = li->next) {
+		struct fd_peer * next_p = (struct fd_peer *)li->o;
+		int cmp = strcmp(peer->p_hdr.info.pi_diamid, next_p->p_hdr.info.pi_diamid);
+		if (cmp < 0)
+			break;
+	}
+	fd_list_insert_before(li, &peer->p_actives);
+	CHECK_POSIX( pthread_rwlock_unlock(&fd_g_activ_peers_rw) );
+	
+	/* Callback registered when the peer was added, by fd_peer_add */
+	if (peer->p_cb) {
+		TRACE_DEBUG(FULL, "Calling add callback for peer %s", peer->p_hdr.info.pi_diamid);
+		(*peer->p_cb)(&peer->p_hdr.info, peer->p_cb_data);
+		peer->p_cb = NULL;
+		peer->p_cb_data = NULL;
+	}
+	
+	/* Start the thread to handle outgoing messages */
+	CHECK_FCT( fd_out_start(peer) );
+	
+	/* Update the expiry timer now */
+	CHECK_FCT( fd_p_expi_update(peer) );
+	
+	return 0;
+}
+static int leave_open_state(struct fd_peer * peer)
+{
+	/* Remove from active peers list */
+	CHECK_POSIX( pthread_rwlock_wrlock(&fd_g_activ_peers_rw) );
+	fd_list_unlink( &peer->p_actives );
+	CHECK_POSIX( pthread_rwlock_unlock(&fd_g_activ_peers_rw) );
+	
+	/* Stop the "out" thread */
+	CHECK_FCT( fd_out_stop(peer) );
+	
+	/* Failover the messages */
+	fd_peer_failover_msg(peer);
+	
+	return 0;
+}
+
+
+/************************************************************************/
+/*                      Helpers for state changes                       */
+/************************************************************************/
+
+/* Cleanup pending events in the peer */
+void fd_psm_events_free(struct fd_peer * peer)
+{
+	struct fd_event * ev;
+	/* Purge all events, and free the associated data if any */
+	while (fd_fifo_tryget( peer->p_events, &ev ) == 0) {
+		switch (ev->code) {
+			case FDEVP_CNX_ESTABLISHED: {
+				fd_cnx_destroy(ev->data);
+			}
+			break;
+			
+			case FDEVP_TERMINATE:
+				/* Do not free the string since it is a constant */
+			break;
+			
+			case FDEVP_CNX_INCOMING: {
+				struct cnx_incoming * evd = ev->data;
+				CHECK_FCT_DO( fd_msg_free(evd->cer), /* continue */);
+				fd_cnx_destroy(evd->cnx);
+			}
+			default:
+				free(ev->data);
+		}
+		free(ev);
+	}
+}
+
+
+/* Change state */
+int fd_psm_change_state(struct fd_peer * peer, int new_state)
+{
+	int old;
+	
+	TRACE_ENTRY("%p %d(%s)", peer, new_state, STATE_STR(new_state));
+	CHECK_PARAMS( CHECK_PEER(peer) );
+	fd_cpu_flush_cache();
+	old = peer->p_hdr.info.runtime.pir_state;
+	if (old == new_state)
+		return 0;
+	
+	TRACE_DEBUG(((old == STATE_OPEN) || (new_state == STATE_OPEN)) ? INFO : FULL, "'%s'\t-> '%s'\t'%s'",
+			STATE_STR(old),
+			STATE_STR(new_state),
+			peer->p_hdr.info.pi_diamid);
+	
+	peer->p_hdr.info.runtime.pir_state = new_state;
+	fd_cpu_flush_cache();
+	
+	if (old == STATE_OPEN) {
+		CHECK_FCT( leave_open_state(peer) );
+	}
+	
+	if (new_state == STATE_OPEN) {
+		CHECK_FCT( enter_open_state(peer) );
+	}
+	
+	if (new_state == STATE_CLOSED) {
+		/* Purge event list */
+		fd_psm_events_free(peer);
+		
+		/* If the peer is not persistant, we destroy it */
+		if (peer->p_hdr.info.config.pic_flags.persist == PI_PRST_NONE) {
+			CHECK_FCT( fd_event_send(peer->p_events, FDEVP_TERMINATE, 0, NULL) );
+		}
+	}
+	
+	return 0;
+}
+
+/* Set timeout timer of next event */
+void fd_psm_next_timeout(struct fd_peer * peer, int add_random, int delay)
+{
+	TRACE_DEBUG(FULL, "Peer timeout reset to %d seconds%s", delay, add_random ? " (+/- 2)" : "" );
+	
+	/* Initialize the timer */
+	CHECK_POSIX_DO(  clock_gettime( CLOCK_REALTIME,  &peer->p_psm_timer ), ASSERT(0) );
+	
+	if (add_random) {
+		if (delay > 2)
+			delay -= 2;
+		else
+			delay = 0;
+
+		/* Add a random value between 0 and 4sec */
+		peer->p_psm_timer.tv_sec += random() % 4;
+		peer->p_psm_timer.tv_nsec+= random() % 1000000000L;
+		if (peer->p_psm_timer.tv_nsec > 1000000000L) {
+			peer->p_psm_timer.tv_nsec -= 1000000000L;
+			peer->p_psm_timer.tv_sec ++;
+		}
+	}
+	
+	peer->p_psm_timer.tv_sec += delay;
+	
+#ifdef SLOW_PSM
+	/* temporary for debug */
+	peer->p_psm_timer.tv_sec += 10;
+#endif
+}
+
+/* Cleanup the peer */
+void fd_psm_cleanup(struct fd_peer * peer, int terminate)
+{
+	/* Move to CLOSED state: failover messages, stop OUT thread, unlink peer from active list */
+	fd_cpu_flush_cache();
+	if (peer->p_hdr.info.runtime.pir_state != STATE_ZOMBIE) {
+		CHECK_FCT_DO( fd_psm_change_state(peer, STATE_CLOSED), /* continue */ );
+	}
+	
+	fd_p_cnx_abort(peer, terminate);
+	
+	fd_p_ce_clear_cnx(peer, NULL);
+	
+	if (peer->p_receiver) {
+		fd_cnx_destroy(peer->p_receiver);
+		peer->p_receiver = NULL;
+	}
+	
+	if (terminate) {
+		fd_psm_events_free(peer);
+		CHECK_FCT_DO( fd_fifo_del(&peer->p_events), /* continue */ );
+	}
+	
+}
+
+
+/************************************************************************/
+/*                      The PSM thread                                  */
+/************************************************************************/
+/* Cancelation cleanup : set ZOMBIE state in the peer */
+void cleanup_setstate(void * arg) 
+{
+	struct fd_peer * peer = (struct fd_peer *)arg;
+	CHECK_PARAMS_DO( CHECK_PEER(peer), return );
+	peer->p_hdr.info.runtime.pir_state = STATE_ZOMBIE;
+	fd_cpu_flush_cache();
+	return;
+}
+
+/* The state machine thread (controler) */
+static void * p_psm_th( void * arg )
+{
+	struct fd_peer * peer = (struct fd_peer *)arg;
+	int created_started = started ? 1 : 0;
+	int event;
+	size_t ev_sz;
+	void * ev_data;
+	
+	CHECK_PARAMS_DO( CHECK_PEER(peer), ASSERT(0) );
+	
+	pthread_cleanup_push( cleanup_setstate, arg );
+	
+	/* Set the thread name */
+	{
+		char buf[48];
+		sprintf(buf, "PSM/%.*s", (int)sizeof(buf) - 5, peer->p_hdr.info.pi_diamid);
+		fd_log_threadname ( buf );
+	}
+	
+	/* The state machine starts in CLOSED state */
+	peer->p_hdr.info.runtime.pir_state = STATE_CLOSED;
+
+	/* Wait that the PSM are authorized to start in the daemon */
+	CHECK_FCT_DO( fd_psm_waitstart(), goto psm_end );
+	
+	/* Initialize the timer */
+	if (peer->p_flags.pf_responder) {
+		fd_psm_next_timeout(peer, 0, INCNX_TIMEOUT);
+	} else {
+		fd_psm_next_timeout(peer, created_started, 0);
+	}
+	
+psm_loop:
+	/* Get next event */
+	TRACE_DEBUG(FULL, "'%s' in state '%s' waiting for next event.",
+			peer->p_hdr.info.pi_diamid, STATE_STR(peer->p_hdr.info.runtime.pir_state));
+	CHECK_FCT_DO( fd_event_timedget(peer->p_events, &peer->p_psm_timer, FDEVP_PSM_TIMEOUT, &event, &ev_sz, &ev_data), goto psm_end );
+	TRACE_DEBUG(FULL, "'%s'\t<-- '%s'\t(%p,%zd)\t'%s'",
+			STATE_STR(peer->p_hdr.info.runtime.pir_state),
+			fd_pev_str(event), ev_data, ev_sz,
+			peer->p_hdr.info.pi_diamid);
+
+	/* Now, the action depends on the current state and the incoming event */
+
+	/* The following states are impossible */
+	ASSERT( peer->p_hdr.info.runtime.pir_state != STATE_NEW );
+	ASSERT( peer->p_hdr.info.runtime.pir_state != STATE_ZOMBIE );
+	ASSERT( peer->p_hdr.info.runtime.pir_state != STATE_OPEN_HANDSHAKE ); /* because it should exist only between two loops */
+
+	/* Purge invalid events */
+	if (!CHECK_PEVENT(event)) {
+		TRACE_DEBUG(INFO, "Invalid event received in PSM '%s' : %d", peer->p_hdr.info.pi_diamid, event);
+		goto psm_loop;
+	}
+
+	/* Handle the (easy) debug event now */
+	if (event == FDEVP_DUMP_ALL) {
+		fd_peer_dump(peer, ANNOYING);
+		goto psm_loop;
+	}
+
+	/* Requests to terminate the peer object */
+	if (event == FDEVP_TERMINATE) {
+		switch (peer->p_hdr.info.runtime.pir_state) {
+			case STATE_OPEN:
+			case STATE_REOPEN:
+				/* We cannot just close the conenction, we have to send a DPR first */
+				CHECK_FCT_DO( fd_p_dp_initiate(peer, ev_data), goto psm_end );
+				goto psm_loop;
+			
+			/*	
+			case STATE_CLOSING:
+			case STATE_WAITCNXACK:
+			case STATE_WAITCNXACK_ELEC:
+			case STATE_WAITCEA:
+			case STATE_SUSPECT:
+			case STATE_CLOSED:
+			*/
+			default:
+				/* In these cases, we just cleanup the peer object (if needed) and terminate */
+				goto psm_end;
+		}
+	}
+	
+	/* A message was received */
+	if (event == FDEVP_CNX_MSG_RECV) {
+		struct msg * msg = NULL;
+		struct msg_hdr * hdr;
+		
+		/* If the current state does not allow receiving messages, just drop it */
+		if (peer->p_hdr.info.runtime.pir_state == STATE_CLOSED) {
+			TRACE_DEBUG(FULL, "Purging message in queue while in CLOSED state (%zdb)", ev_sz);
+			free(ev_data);
+			goto psm_loop;
+		}
+		
+		/* Parse the received buffer */
+		CHECK_FCT_DO( fd_msg_parse_buffer( (void *)&ev_data, ev_sz, &msg), 
+			{
+				fd_log_debug("Received invalid data from peer '%s', closing the connection\n", peer->p_hdr.info.pi_diamid);
+				free(ev_data);
+				CHECK_FCT_DO( fd_event_send(peer->p_events, FDEVP_CNX_ERROR, 0, NULL), goto psm_reset );
+				goto psm_loop;
+			} );
+		
+		TRACE_DEBUG(FULL, "Received a message (%zdb) from '%s'", ev_sz, peer->p_hdr.info.pi_diamid);
+		if (TRACE_BOOL(FULL+1)) {
+			CHECK_FCT_DO( fd_msg_parse_dict( msg, fd_g_config->cnf_dict, NULL ), );
+			fd_msg_dump_walk(FULL, msg);
+		} else {
+			fd_msg_dump_one(FULL, msg);
+		}
+	
+		/* Extract the header */
+		CHECK_FCT_DO( fd_msg_hdr(msg, &hdr), goto psm_end );
+		
+		/* If it is an answer, associate with the request or drop */
+		if (!(hdr->msg_flags & CMD_FLAG_REQUEST)) {
+			struct msg * req;
+			/* Search matching request (same hbhid) */
+			CHECK_FCT_DO( fd_p_sr_fetch(&peer->p_sr, hdr->msg_hbhid, &req), goto psm_end );
+			if (req == NULL) {
+				fd_log_debug("Received a Diameter answer message with no corresponding sent request, discarding.\n");
+				fd_msg_dump_walk(NONE, msg);
+				fd_msg_free(msg);
+				goto psm_loop;
+			}
+			
+			/* Associate */
+			CHECK_FCT_DO( fd_msg_answ_associate( msg, req ), goto psm_end );
+		}
+		
+		/* Now handle non-link-local messages */
+		if (fd_msg_is_routable(msg)) {
+			switch (peer->p_hdr.info.runtime.pir_state) {
+				/* To maximize compatibility -- should not be a security issue here */
+				case STATE_REOPEN:
+				case STATE_SUSPECT:
+				case STATE_CLOSING:
+					TRACE_DEBUG(FULL, "Accepted a message while not in OPEN state... ");
+				/* The standard situation : */
+				case STATE_OPEN:
+					/* We received a valid routable message, update the expiry timer */
+					CHECK_FCT_DO( fd_p_expi_update(peer), goto psm_end );
+
+					/* Set the message source and add the Route-Record */
+					CHECK_FCT_DO( fd_msg_source_set( msg, peer->p_hdr.info.pi_diamid, 1, fd_g_config->cnf_dict ), goto psm_end);
+
+					/* Requeue to the global incoming queue */
+					CHECK_FCT_DO(fd_fifo_post(fd_g_incoming, &msg), goto psm_end );
+
+					/* Update the peer timer (only in OPEN state) */
+					if ((peer->p_hdr.info.runtime.pir_state == STATE_OPEN) && (!peer->p_flags.pf_dw_pending)) {
+						fd_psm_next_timeout(peer, 1, peer->p_hdr.info.config.pic_twtimer ?: fd_g_config->cnf_timer_tw);
+					}
+					break;
+					
+				/* In other states, we discard the message, it is either old or invalid to send it for the remote peer */
+				case STATE_WAITCNXACK:
+				case STATE_WAITCNXACK_ELEC:
+				case STATE_WAITCEA:
+				case STATE_CLOSED:
+				default:
+					/* In such case, just discard the message */
+					fd_log_debug("Received a routable message while not in OPEN state from peer '%s', discarded.\n", peer->p_hdr.info.pi_diamid);
+					fd_msg_dump_walk(NONE, msg);
+					fd_msg_free(msg);
+			}
+			goto psm_loop;
+		}
+		
+		/* Link-local message: They must be understood by our dictionary, otherwise we return an error */
+		{
+			int ret = fd_msg_parse_or_error( &msg );
+			if (ret != EBADMSG) {
+				CHECK_FCT_DO( ret, goto psm_end );
+			} else {
+				if (msg) {
+					/* Send the error back to the peer */
+					CHECK_FCT_DO( fd_out_send(&msg, NULL, peer, FD_CNX_ORDERED), /* In case of error the message has already been dumped */ );
+					if (msg) {
+						CHECK_FCT_DO( fd_msg_free(msg), goto psm_end);
+					}
+				} else {
+					/* We received an invalid answer, let's disconnect */
+					CHECK_FCT_DO( fd_event_send(peer->p_events, FDEVP_CNX_ERROR, 0, NULL), goto psm_reset );
+				}
+				goto psm_loop;
+			}
+		}
+		
+		/* Handle the LL message and update the expiry timer appropriately */
+		switch (hdr->msg_code) {
+			case CC_CAPABILITIES_EXCHANGE:
+				CHECK_FCT_DO( fd_p_ce_msgrcv(&msg, (hdr->msg_flags & CMD_FLAG_REQUEST), peer), goto psm_reset );
+				break;
+			
+			case CC_DISCONNECT_PEER:
+				CHECK_FCT_DO( fd_p_dp_handle(&msg, (hdr->msg_flags & CMD_FLAG_REQUEST), peer), goto psm_reset );
+				if (peer->p_hdr.info.runtime.pir_state == STATE_CLOSING)
+					goto psm_end;
+				break;
+			
+			case CC_DEVICE_WATCHDOG:
+				CHECK_FCT_DO( fd_p_dw_handle(&msg, (hdr->msg_flags & CMD_FLAG_REQUEST), peer), goto psm_reset );
+				break;
+			
+			default:
+				/* Unknown / unexpected / invalid message */
+				fd_log_debug("Received an unknown local message from peer '%s', discarded.\n", peer->p_hdr.info.pi_diamid);
+				fd_msg_dump_walk(NONE, msg);
+				if (hdr->msg_flags & CMD_FLAG_REQUEST) {
+					do {
+						/* Reply with an error code */
+						CHECK_FCT_DO( fd_msg_new_answer_from_req ( fd_g_config->cnf_dict, &msg, MSGFL_ANSW_ERROR ), break );
+
+						/* Set the error code */
+						CHECK_FCT_DO( fd_msg_rescode_set(msg, "DIAMETER_INVALID_HDR_BITS", NULL, NULL, 1 ), break );
+
+						/* Send the answer */
+						CHECK_FCT_DO( fd_out_send(&msg, peer->p_cnxctx, peer, FD_CNX_ORDERED), break );
+					} while (0);
+				} else {
+					/* We did ASK for it ??? */
+					fd_log_debug("Invalid PXY flag in answer header ?\n");
+				}
+				
+				/* Cleanup the message if not done */
+				if (msg) {
+					CHECK_FCT_DO( fd_msg_free(msg), /* continue */);
+					msg = NULL;
+				}
+		};
+		
+		/* At this point the message must have been fully handled already */
+		if (msg) {
+			fd_log_debug("Internal error: unhandled message (discarded)!\n");
+			fd_msg_dump_walk(NONE, msg);
+			fd_msg_free(msg);
+		}
+		
+		goto psm_loop;
+	}
+	
+	/* The connection object is broken */
+	if (event == FDEVP_CNX_ERROR) {
+		switch (peer->p_hdr.info.runtime.pir_state) {
+			case STATE_WAITCNXACK_ELEC:
+				/* Abort the initiating side */
+				fd_p_cnx_abort(peer, 0);
+				/* Process the receiver side */
+				CHECK_FCT_DO( fd_p_ce_process_receiver(peer), goto psm_end );
+				break;
+			
+			case STATE_WAITCEA:
+			case STATE_OPEN:
+			case STATE_REOPEN:
+			case STATE_WAITCNXACK:
+			case STATE_SUSPECT:
+			default:
+				/* Mark the connection problem */
+				peer->p_flags.pf_cnx_pb = 1;
+				
+				/* Destroy the connection, restart the timer to a new connection attempt */
+				fd_psm_next_timeout(peer, 1, peer->p_hdr.info.config.pic_tctimer ?: fd_g_config->cnf_timer_tc);
+				
+			case STATE_CLOSED:
+				goto psm_reset;
+				
+			case STATE_CLOSING:
+				/* We sent a DPR so we are terminating, do not wait for DPA */
+				goto psm_end;
+				
+		}
+		goto psm_loop;
+	}
+	
+	/* The connection notified a change in endpoints */
+	if (event == FDEVP_CNX_EP_CHANGE) {
+		/* We actually don't care if we are in OPEN state here... */
+		
+		/* Cleanup the remote LL and primary addresses */
+		CHECK_FCT_DO( fd_ep_filter( &peer->p_hdr.info.pi_endpoints, EP_FL_CONF | EP_FL_DISC | EP_FL_ADV ), /* ignore the error */);
+		CHECK_FCT_DO( fd_ep_clearflags( &peer->p_hdr.info.pi_endpoints, EP_FL_PRIMARY ), /* ignore the error */);
+		
+		/* Get the new ones */
+		CHECK_FCT_DO( fd_cnx_getremoteeps(peer->p_cnxctx, &peer->p_hdr.info.pi_endpoints), /* ignore the error */);
+		
+		/* We do not support local endpoints change currently, but it could be added here if needed (refresh fd_g_config->cnf_endpoints)*/
+		
+		if (TRACE_BOOL(ANNOYING)) {
+			TRACE_DEBUG(ANNOYING, "New remote endpoint(s):" );
+			fd_ep_dump(6, &peer->p_hdr.info.pi_endpoints);
+		}
+		
+		/* Done */
+		goto psm_loop;
+	}
+	
+	/* A new connection was established and CER containing this peer id was received */
+	if (event == FDEVP_CNX_INCOMING) {
+		struct cnx_incoming * params = ev_data;
+		ASSERT(params);
+		
+		/* Handle the message */
+		CHECK_FCT_DO( fd_p_ce_handle_newCER(&params->cer, peer, &params->cnx, params->validate), goto psm_end );
+		
+		/* Cleanup if needed */
+		if (params->cnx) {
+			fd_cnx_destroy(params->cnx);
+			params->cnx = NULL;
+		}
+		if (params->cer) {
+			CHECK_FCT_DO( fd_msg_free(params->cer), );
+			params->cer = NULL;
+		}
+		
+		/* Loop */
+		free(ev_data);
+		goto psm_loop;
+	}
+	
+	/* A new connection has been established with the remote peer */
+	if (event == FDEVP_CNX_ESTABLISHED) {
+		struct cnxctx * cnx = ev_data;
+		
+		/* Release the resources of the connecting thread */
+		CHECK_POSIX_DO( pthread_join( peer->p_ini_thr, NULL), /* ignore, it is not a big deal */);
+		peer->p_ini_thr = (pthread_t)NULL;
+		
+		switch (peer->p_hdr.info.runtime.pir_state) {
+			case STATE_WAITCNXACK_ELEC:
+			case STATE_WAITCNXACK:
+				fd_p_ce_handle_newcnx(peer, cnx);
+				break;
+				
+			default:
+				/* Just abort the attempt and continue */
+				TRACE_DEBUG(FULL, "Connection attempt successful but current state is %s, closing...", STATE_STR(peer->p_hdr.info.runtime.pir_state));
+				fd_cnx_destroy(cnx);
+		}
+		
+		goto psm_loop;
+	}
+	
+	/* A new connection has not been established with the remote peer */
+	if (event == FDEVP_CNX_FAILED) {
+		
+		/* Release the resources of the connecting thread */
+		CHECK_POSIX_DO( pthread_join( peer->p_ini_thr, NULL), /* ignore, it is not a big deal */);
+		peer->p_ini_thr = (pthread_t)NULL;
+		
+		switch (peer->p_hdr.info.runtime.pir_state) {
+			case STATE_WAITCNXACK_ELEC:
+				/* Abort the initiating side */
+				fd_p_cnx_abort(peer, 0);
+				/* Process the receiver side */
+				CHECK_FCT_DO( fd_p_ce_process_receiver(peer), goto psm_end );
+				break;
+				
+			case STATE_WAITCNXACK:
+				/* Go back to CLOSE */
+				fd_psm_next_timeout(peer, 1, peer->p_hdr.info.config.pic_tctimer ?: fd_g_config->cnf_timer_tc);
+				goto psm_reset;
+				
+			default:
+				/* Just ignore */
+				TRACE_DEBUG(FULL, "Connection attempt failed but current state is %s, ignoring...", STATE_STR(peer->p_hdr.info.runtime.pir_state));
+		}
+		
+		goto psm_loop;
+	}
+	
+	/* The timeout for the current state has been reached */
+	if (event == FDEVP_PSM_TIMEOUT) {
+		switch (peer->p_hdr.info.runtime.pir_state) {
+			case STATE_OPEN:
+			case STATE_REOPEN:
+				CHECK_FCT_DO( fd_p_dw_timeout(peer), goto psm_end );
+				goto psm_loop;
+				
+			case STATE_CLOSED:
+				CHECK_FCT_DO( fd_psm_change_state(peer, STATE_WAITCNXACK), goto psm_end );
+				fd_psm_next_timeout(peer, 0, CNX_TIMEOUT);
+				CHECK_FCT_DO( fd_p_cnx_init(peer), goto psm_end );
+				goto psm_loop;
+				
+			case STATE_SUSPECT:
+				/* Mark the connection problem */
+				peer->p_flags.pf_cnx_pb = 1;
+				
+			case STATE_CLOSING:
+			case STATE_WAITCNXACK:
+			case STATE_WAITCEA:
+				/* Destroy the connection, restart the timer to a new connection attempt */
+				fd_psm_next_timeout(peer, 1, peer->p_hdr.info.config.pic_tctimer ?: fd_g_config->cnf_timer_tc);
+				goto psm_reset;
+				
+			case STATE_WAITCNXACK_ELEC:
+				/* Abort the initiating side */
+				fd_p_cnx_abort(peer, 0);
+				/* Process the receiver side */
+				CHECK_FCT_DO( fd_p_ce_process_receiver(peer), goto psm_end );
+				goto psm_loop;
+			
+			default:
+				ASSERT(0); /* implementation problem, we did not foresee this case? */
+		}
+	}
+	
+	/* Default action : the handling has not yet been implemented. [for debug only] */
+	TRACE_DEBUG(INFO, "Missing handler in PSM for '%s'\t<-- '%s'", STATE_STR(peer->p_hdr.info.runtime.pir_state), fd_pev_str(event));
+psm_reset:
+	if (peer->p_flags.pf_delete)
+		goto psm_end;
+	fd_psm_cleanup(peer, 0);
+	goto psm_loop;
+	
+psm_end:
+	fd_psm_cleanup(peer, 1);
+	TRACE_DEBUG(INFO, "'%s'\t-> STATE_ZOMBIE (terminated)\t'%s'",
+			STATE_STR(peer->p_hdr.info.runtime.pir_state),
+			peer->p_hdr.info.pi_diamid);
+	pthread_cleanup_pop(1); /* set STATE_ZOMBIE */
+	fd_cpu_flush_cache();
+	peer->p_psm = (pthread_t)NULL;
+	pthread_detach(pthread_self());
+	return NULL;
+}
+
+
+/************************************************************************/
+/*                      Functions to control the PSM                    */
+/************************************************************************/
+/* Create the PSM thread of one peer structure */
+int fd_psm_begin(struct fd_peer * peer )
+{
+	TRACE_ENTRY("%p", peer);
+	
+	/* Check the peer and state are OK */
+	CHECK_PARAMS( CHECK_PEER(peer) && (peer->p_hdr.info.runtime.pir_state == STATE_NEW) );
+	
+	/* Create the FIFO for events */
+	CHECK_FCT( fd_fifo_new(&peer->p_events) );
+	
+	/* Create the PSM controler thread */
+	CHECK_POSIX( pthread_create( &peer->p_psm, NULL, p_psm_th, peer ) );
+	
+	/* We're done */
+	return 0;
+}
+
+/* End the PSM (clean ending) */
+int fd_psm_terminate(struct fd_peer * peer, char * reason )
+{
+	TRACE_ENTRY("%p", peer);
+	CHECK_PARAMS( CHECK_PEER(peer) );
+	
+	fd_cpu_flush_cache();
+	if (peer->p_hdr.info.runtime.pir_state != STATE_ZOMBIE) {
+		CHECK_FCT( fd_event_send(peer->p_events, FDEVP_TERMINATE, 0, reason) );
+	} else {
+		TRACE_DEBUG(FULL, "Peer '%s' was already terminated", peer->p_hdr.info.pi_diamid);
+	}
+	return 0;
+}
+
+/* End the PSM & cleanup the peer structure */
+void fd_psm_abord(struct fd_peer * peer )
+{
+	TRACE_ENTRY("%p", peer);
+	
+	/* Cancel PSM thread */
+	CHECK_FCT_DO( fd_thr_term(&peer->p_psm), /* continue */ );
+	
+	/* Cleanup the data */
+	fd_psm_cleanup(peer, 1);
+	
+	/* Destroy the event list */
+	CHECK_FCT_DO( fd_fifo_del(&peer->p_events), /* continue */ );
+	
+	/* Remaining cleanups are performed in fd_peer_free */
+	return;
+}
+
Index: libfdcore/p_sr.c
===================================================================
--- libfdcore/p_sr.c	(revision 658)
+++ libfdcore/p_sr.c	(revision 658)
@@ -0,0 +1,321 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@nict.go.jp>							 *
+*													 *
+* Copyright (c) 2010, 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 "fdcore-internal.h"
+
+#ifndef SR_DEBUG_LVL
+#define SR_DEBUG_LVL ANNOYING
+#endif /* SR_DEBUG_LVL */
+
+/* Structure to store a sent request */
+struct sentreq {
+	struct fd_list	chain; 	/* the "o" field points directly to the hop-by-hop of the request (uint32_t *)  */
+	struct msg	*req;	/* A request that was sent and not yet answered. */
+	uint32_t	prevhbh;/* The value to set in the hbh header when the message is retrieved */
+	struct fd_list  expire; /* the list of expiring requests */
+	struct timespec added_on; /* the time the request was added */
+};
+
+/* Find an element in the hbh list, or the following one */
+static struct fd_list * find_or_next(struct fd_list * srlist, uint32_t hbh, int * match)
+{
+	struct fd_list * li;
+	*match = 0;
+	for (li = srlist->next; li != srlist; li = li->next) {
+		uint32_t * nexthbh = li->o;
+		if (*nexthbh < hbh)
+			continue;
+		if (*nexthbh == hbh)
+			*match = 1;
+		break;
+	}
+	return li;
+}
+
+static void srl_dump(const char * text, struct fd_list * srlist)
+{
+	struct fd_list * li;
+	struct timespec now;
+	if (!TRACE_BOOL(SR_DEBUG_LVL))
+		return;
+	CHECK_SYS_DO( clock_gettime(CLOCK_REALTIME, &now), );
+	fd_log_debug("%sSentReq list @%p:\n", text, srlist);
+	for (li = srlist->next; li != srlist; li = li->next) {
+		struct sentreq * sr = (struct sentreq *)li;
+		uint32_t * nexthbh = li->o;
+		fd_log_debug(" - Next req (%x): [since %ld.%06ld sec]\n", *nexthbh, 
+			(now.tv_nsec >= sr->added_on.tv_nsec) ? (now.tv_sec - sr->added_on.tv_sec) : (now.tv_sec - sr->added_on.tv_sec - 1),
+			(now.tv_nsec >= sr->added_on.tv_nsec) ? (now.tv_nsec - sr->added_on.tv_nsec) / 1000 : (now.tv_nsec - sr->added_on.tv_nsec + 1000000000) / 1000);
+		fd_msg_dump_one(SR_DEBUG_LVL + 1, sr->req);
+	}
+}
+
+/* (detached) thread that calls the anscb on expired messages. 
+  We do it in a separate thread to avoid blocking the reception of new messages during this time */
+static void * call_anscb_expire(void * arg) {
+	struct msg * expired_req = arg;
+	
+	void (*anscb)(void *, struct msg **);
+	void * data;
+	
+	TRACE_ENTRY("%p", arg);
+	CHECK_PARAMS_DO( arg, return NULL );
+	
+	/* Set the thread name */
+	fd_log_threadname ( "Expired req cb." );
+	
+	/* Log */
+	TRACE_DEBUG(INFO, "The expiration timer for a request has been reached, abording this attempt now & calling cb...");
+	
+	/* Retrieve callback in the message */
+	CHECK_FCT_DO( fd_msg_anscb_get( expired_req, &anscb, &data ), return NULL);
+	ASSERT(anscb);
+
+	/* Call it */
+	(*anscb)(data, &expired_req);
+	
+	/* If the callback did not dispose of the message, do it now */
+	if (expired_req) {
+		CHECK_FCT_DO( fd_msg_free(expired_req), /* ignore */ );
+	}
+	
+	/* Finish */
+	return NULL;
+}
+
+/* thread that handles messages expiring. The thread is started / stopped only when needed */
+static void * sr_expiry_th(void * arg) {
+	struct sr_list * srlist = arg;
+	struct msg * expired_req;
+	pthread_attr_t detached;
+	
+	TRACE_ENTRY("%p", arg);
+	CHECK_PARAMS_DO( arg, return NULL );
+	
+	/* Set the thread name */
+	{
+		char buf[48];
+		sprintf(buf, "ReqExp/%.*s", (int)sizeof(buf) - 8, ((struct fd_peer *)(srlist->exp.o))->p_hdr.info.pi_diamid);
+		fd_log_threadname ( buf );
+	}
+	
+	CHECK_POSIX_DO( pthread_attr_init(&detached), return NULL );
+	CHECK_POSIX_DO( pthread_attr_setdetachstate(&detached, PTHREAD_CREATE_DETACHED), return NULL );
+	
+	CHECK_POSIX_DO( pthread_mutex_lock(&srlist->mtx),  return NULL );
+	pthread_cleanup_push( fd_cleanup_mutex, &srlist->mtx );
+	
+	do {
+		struct timespec	now, *t;
+		struct sentreq * first;
+		pthread_t th;
+		
+		/* Check if there are expiring requests available */
+		if (FD_IS_LIST_EMPTY(&srlist->exp)) {
+			/* Just wait for a change or cancelation */
+			CHECK_POSIX_DO( pthread_cond_wait( &srlist->cnd, &srlist->mtx ), goto error );
+			/* Restart the loop on wakeup */
+			continue;
+		}
+		
+		/* Get the pointer to the request that expires first */
+		first = (struct sentreq *)(srlist->exp.next->o);
+		t = fd_msg_anscb_gettimeout( first->req );
+		ASSERT(t);
+		
+		/* Get the current time */
+		CHECK_SYS_DO(  clock_gettime(CLOCK_REALTIME, &now),  goto error  );
+
+		/* If first request is not expired, we just wait until it happens */
+		if ( TS_IS_INFERIOR( &now, t ) ) {
+			
+			CHECK_POSIX_DO2(  pthread_cond_timedwait( &srlist->cnd, &srlist->mtx, t ),  
+					ETIMEDOUT, /* ETIMEDOUT is a normal return value, continue */,
+					/* on other error, */ goto error );
+	
+			/* on wakeup, loop */
+			continue;
+		}
+		
+		/* Now, the first request in the list is expired; remove it and call the anscb for it in a new thread */
+		fd_list_unlink(&first->chain);
+		fd_list_unlink(&first->expire);
+		expired_req = first->req;
+		free(first);
+		
+		CHECK_POSIX_DO( pthread_create( &th, &detached, call_anscb_expire, expired_req ), goto error );
+
+		/* loop */
+	} while (1);
+error:	
+	; /* pthread_cleanup_pop sometimes expands as "} ..." and the label beofre this cause some compilers to complain... */
+	pthread_cleanup_pop( 1 );
+	return NULL;
+}
+
+
+/* Store a new sent request */
+int fd_p_sr_store(struct sr_list * srlist, struct msg **req, uint32_t *hbhloc, uint32_t hbh_restore)
+{
+	struct sentreq * sr;
+	struct fd_list * next;
+	int match;
+	struct timespec * ts;
+	
+	TRACE_ENTRY("%p %p %p %x", srlist, req, hbhloc, hbh_restore);
+	CHECK_PARAMS(srlist && req && *req && hbhloc);
+	
+	CHECK_MALLOC( sr = malloc(sizeof(struct sentreq)) );
+	memset(sr, 0, sizeof(struct sentreq));
+	fd_list_init(&sr->chain, hbhloc);
+	sr->req = *req;
+	sr->prevhbh = hbh_restore;
+	fd_list_init(&sr->expire, sr);
+	CHECK_SYS( clock_gettime(CLOCK_REALTIME, &sr->added_on) );
+	
+	/* Search the place in the list */
+	CHECK_POSIX( pthread_mutex_lock(&srlist->mtx) );
+	next = find_or_next(&srlist->srs, *hbhloc, &match);
+	if (match) {
+		TRACE_DEBUG(INFO, "A request with the same hop-by-hop Id was already sent: error");
+		free(sr);
+		CHECK_POSIX_DO( pthread_mutex_unlock(&srlist->mtx), /* ignore */ );
+		return EINVAL;
+	}
+	
+	/* Save in the list */
+	*req = NULL;
+	fd_list_insert_before(next, &sr->chain);
+	srl_dump("Saved new request, ", &srlist->srs);
+	
+	/* In case of request with a timeout, also store in the timeout list */
+	ts = fd_msg_anscb_gettimeout( sr->req );
+	if (ts) {
+		struct fd_list * li;
+		struct timespec * t;
+		
+		/* browse srlist->exp from the end */
+		for (li = srlist->exp.prev; li != &srlist->exp; li = li->prev) {
+			struct sentreq * s = (struct sentreq *)(li->o);
+			t = fd_msg_anscb_gettimeout( s->req );
+			ASSERT( t ); /* sanity */
+			if (TS_IS_INFERIOR(t, ts))
+				break;
+		}
+		
+		fd_list_insert_after(li, &sr->expire);
+	
+		/* if the thread does not exist yet, create it */
+		if (srlist->thr == (pthread_t)NULL) {
+			CHECK_POSIX_DO( pthread_create(&srlist->thr, NULL, sr_expiry_th, srlist), /* continue anyway */);
+		} else {
+			/* or, if added in first position, signal the condvar to update the sleep time of the thread */
+			if (li == &srlist->exp) {
+				CHECK_POSIX_DO( pthread_cond_signal(&srlist->cnd), /* continue anyway */);
+			}
+		}
+	}
+	
+	CHECK_POSIX( pthread_mutex_unlock(&srlist->mtx) );
+	return 0;
+}
+
+/* Fetch a request by hbh */
+int fd_p_sr_fetch(struct sr_list * srlist, uint32_t hbh, struct msg **req)
+{
+	struct sentreq * sr;
+	int match;
+	
+	TRACE_ENTRY("%p %x %p", srlist, hbh, req);
+	CHECK_PARAMS(srlist && req);
+	
+	/* Search the request in the list */
+	CHECK_POSIX( pthread_mutex_lock(&srlist->mtx) );
+	srl_dump("Fetching a request, ", &srlist->srs);
+	sr = (struct sentreq *)find_or_next(&srlist->srs, hbh, &match);
+	if (!match) {
+		TRACE_DEBUG(INFO, "There is no saved request with this hop-by-hop id (%x)", hbh);
+		*req = NULL;
+	} else {
+		/* Restore hop-by-hop id */
+		*((uint32_t *)sr->chain.o) = sr->prevhbh;
+		/* Unlink */
+		fd_list_unlink(&sr->chain);
+		fd_list_unlink(&sr->expire);
+		*req = sr->req;
+		free(sr);
+	}
+	CHECK_POSIX( pthread_mutex_unlock(&srlist->mtx) );
+	
+	/* do not stop the expire thread here, it might cause creating/destroying it very often otherwise */
+
+	/* Done */
+	return 0;
+}
+
+/* Failover requests (free or requeue routables) */
+void fd_p_sr_failover(struct sr_list * srlist)
+{
+	CHECK_POSIX_DO( pthread_mutex_lock(&srlist->mtx), /* continue anyway */ );
+	while (!FD_IS_LIST_EMPTY(&srlist->srs)) {
+		struct sentreq * sr = (struct sentreq *)(srlist->srs.next);
+		fd_list_unlink(&sr->chain);
+		fd_list_unlink(&sr->expire);
+		if (fd_msg_is_routable(sr->req)) {
+			struct msg_hdr * hdr = NULL;
+			
+			/* Set the 'T' flag */
+			CHECK_FCT_DO(fd_msg_hdr(sr->req, &hdr), /* continue */);
+			if (hdr)
+				hdr->msg_flags |= CMD_FLAG_RETRANSMIT;
+			
+			/* Requeue for sending to another peer */
+			CHECK_FCT_DO(fd_fifo_post(fd_g_outgoing, &sr->req),
+					CHECK_FCT_DO(fd_msg_free(sr->req), /* What can we do more? */));
+		} else {
+			/* Just free the request... */
+			CHECK_FCT_DO(fd_msg_free(sr->req), /* Ignore */);
+		}
+		free(sr);
+	}
+	/* The list of expiring requests must be empty now */
+	ASSERT( FD_IS_LIST_EMPTY(&srlist->exp) );
+	
+	CHECK_POSIX_DO( pthread_mutex_unlock(&srlist->mtx), /* continue anyway */ );
+	
+	/* Terminate the expiry thread (must be done when the lock can be taken) */
+	CHECK_FCT_DO( fd_thr_term(&srlist->thr), /* ignore error */ );
+}
+
Index: libfdcore/peers.c
===================================================================
--- libfdcore/peers.c	(revision 658)
+++ libfdcore/peers.c	(revision 658)
@@ -0,0 +1,546 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@nict.go.jp>							 *
+*													 *
+* Copyright (c) 2010, 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 "fdcore-internal.h"
+
+/* Global list of peers */
+struct fd_list   fd_g_peers = FD_LIST_INITIALIZER(fd_g_peers);
+pthread_rwlock_t fd_g_peers_rw = PTHREAD_RWLOCK_INITIALIZER;
+
+/* List of active peers */
+struct fd_list   fd_g_activ_peers = FD_LIST_INITIALIZER(fd_g_activ_peers);	/* peers linked by their p_actives oredered by p_diamid */
+pthread_rwlock_t fd_g_activ_peers_rw = PTHREAD_RWLOCK_INITIALIZER;
+
+/* List of validation callbacks (registered with fd_peer_validate_register) */
+static struct fd_list validators = FD_LIST_INITIALIZER(validators);	/* list items are simple fd_list with "o" pointing to the callback */
+static pthread_rwlock_t validators_rw = PTHREAD_RWLOCK_INITIALIZER;
+
+
+/* Alloc / reinit a peer structure. if *ptr is not NULL, it must already point to a valid struct fd_peer. */
+int fd_peer_alloc(struct fd_peer ** ptr)
+{
+	struct fd_peer *p;
+	
+	TRACE_ENTRY("%p", ptr);
+	CHECK_PARAMS(ptr);
+	
+	if (*ptr) {
+		p = *ptr;
+	} else {
+		CHECK_MALLOC( p = malloc(sizeof(struct fd_peer)) );
+		*ptr = p;
+	}
+	
+	/* Now initialize the content */
+	memset(p, 0, sizeof(struct fd_peer));
+	
+	fd_list_init(&p->p_hdr.chain, p);
+	
+	fd_list_init(&p->p_hdr.info.pi_endpoints, p);
+	fd_list_init(&p->p_hdr.info.runtime.pir_apps, p);
+	
+	p->p_eyec = EYEC_PEER;
+	fd_list_init(&p->p_actives, p);
+	fd_list_init(&p->p_expiry, p);
+	CHECK_FCT( fd_fifo_new(&p->p_tosend) );
+	p->p_hbh = lrand48();
+	
+	fd_list_init(&p->p_sr.srs, p);
+	fd_list_init(&p->p_sr.exp, p);
+	CHECK_POSIX( pthread_mutex_init(&p->p_sr.mtx, NULL) );
+	CHECK_POSIX( pthread_cond_init(&p->p_sr.cnd, NULL) );
+	
+	fd_list_init(&p->p_connparams, p);
+	
+	return 0;
+}
+
+/* Add a new peer entry */
+int fd_peer_add ( struct peer_info * info, char * orig_dbg, void (*cb)(struct peer_info *, void *), void * cb_data )
+{
+	struct fd_peer *p = NULL;
+	struct fd_list * li;
+	int ret = 0;
+	TRACE_ENTRY("%p %p %p %p", info, orig_dbg, cb, cb_data);
+	CHECK_PARAMS(info && info->pi_diamid);
+	
+	/* Create a structure to contain the new peer information */
+	CHECK_FCT( fd_peer_alloc(&p) );
+	
+	/* Copy the informations from the parameters received */
+	CHECK_MALLOC( p->p_hdr.info.pi_diamid = strdup(info->pi_diamid) );
+	
+	memcpy( &p->p_hdr.info.config, &info->config, sizeof(p->p_hdr.info.config) );
+	/* Duplicate the strings if provided */
+	if (info->config.pic_realm) {
+		CHECK_MALLOC( p->p_hdr.info.config.pic_realm = strdup(info->config.pic_realm) );
+	}
+	if (info->config.pic_priority) {
+		CHECK_MALLOC( p->p_hdr.info.config.pic_realm = strdup(info->config.pic_priority) );
+	}
+	
+	/* Move the list of endpoints into the peer */
+	if (info->pi_endpoints.next)
+		while (!FD_IS_LIST_EMPTY( &info->pi_endpoints ) ) {
+			li = info->pi_endpoints.next;
+			fd_list_unlink(li);
+			fd_list_insert_before(&p->p_hdr.info.pi_endpoints, li);
+		}
+	
+	/* The internal data */
+	if (orig_dbg) {
+		CHECK_MALLOC( p->p_dbgorig = strdup(orig_dbg) );
+	} else {
+		CHECK_MALLOC( p->p_dbgorig = strdup("unknown") );
+	}
+	p->p_cb = cb;
+	p->p_cb_data = cb_data;
+	
+	/* Ok, now check if we don't already have an entry with the same Diameter Id, and insert this one */
+	CHECK_POSIX( pthread_rwlock_wrlock(&fd_g_peers_rw) );
+	
+	for (li = fd_g_peers.next; li != &fd_g_peers; li = li->next) {
+		struct fd_peer * next = (struct fd_peer *)li;
+		int cmp = strcasecmp( p->p_hdr.info.pi_diamid, next->p_hdr.info.pi_diamid );
+		if (cmp > 0)
+			continue;
+		if (cmp == 0)
+			ret = EEXIST;
+		break;
+	}
+	
+	/* We can insert the new peer object */
+	if (! ret)
+		do {
+			/* Update expiry list */
+			CHECK_FCT_DO( ret = fd_p_expi_update( p ), break );
+
+			/* Insert the new element in the list */
+			fd_list_insert_before( li, &p->p_hdr.chain );
+		} while (0);
+
+	CHECK_POSIX( pthread_rwlock_unlock(&fd_g_peers_rw) );
+	if (ret) {
+		CHECK_FCT( fd_peer_free(&p) );
+	} else {
+		CHECK_FCT( fd_psm_begin(p) );
+	}
+	return ret;
+}
+
+/* Search for a peer */
+int fd_peer_getbyid( char * diamid, struct peer_hdr ** peer )
+{
+	struct fd_list * li;
+	
+	TRACE_ENTRY("%p %p", diamid, peer);
+	CHECK_PARAMS( diamid && peer );
+	
+	*peer = NULL;
+	
+	/* Search in the list */
+	CHECK_POSIX( pthread_rwlock_rdlock(&fd_g_peers_rw) );
+	for (li = fd_g_peers.next; li != &fd_g_peers; li = li->next) {
+		struct fd_peer * next = (struct fd_peer *)li;
+		int cmp = strcasecmp( diamid, next->p_hdr.info.pi_diamid );
+		if (cmp > 0)
+			continue;
+		if (cmp == 0)
+			*peer = &next->p_hdr;
+		break;
+	}
+	CHECK_POSIX( pthread_rwlock_unlock(&fd_g_peers_rw) );
+	
+	return 0;
+}
+
+
+#define free_null( _v ) 	\
+	if (_v) {		\
+		free(_v);	\
+		(_v) = NULL;	\
+	}
+	
+#define free_list( _l ) 						\
+	while (!FD_IS_LIST_EMPTY(_l)) {					\
+		struct fd_list * __li = ((struct fd_list *)(_l))->next;	\
+		fd_list_unlink(__li);					\
+		free(__li);						\
+	}
+
+/* Empty the lists of p_tosend and p_sentreq messages */
+void fd_peer_failover_msg(struct fd_peer * peer)
+{
+	struct msg *m;
+	TRACE_ENTRY("%p", peer);
+	CHECK_PARAMS_DO(CHECK_PEER(peer), return);
+	
+	/* Requeue all messages in the "out" queue */
+	while ( fd_fifo_tryget(peer->p_tosend, &m) == 0 ) {
+		CHECK_FCT_DO(fd_fifo_post(fd_g_outgoing, &m), 
+				/* fallback: destroy the message */
+				CHECK_FCT_DO(fd_msg_free(m), /* What can we do more? */));
+	}
+	
+	/* Requeue all routable sent requests */
+	fd_p_sr_failover(&peer->p_sr);
+	
+	/* Done */
+	return;
+}
+
+/* Destroy a structure once cleanups have been performed (fd_psm_abord, ...) */
+int fd_peer_free(struct fd_peer ** ptr)
+{
+	struct fd_peer *p;
+	
+	TRACE_ENTRY("%p", ptr);
+	CHECK_PARAMS(ptr);
+	p = *ptr;
+	*ptr = NULL;
+	CHECK_PARAMS(p);
+	
+	CHECK_PARAMS( FD_IS_LIST_EMPTY(&p->p_hdr.chain) );
+	
+	free_null(p->p_hdr.info.pi_diamid);
+	
+	free_null(p->p_hdr.info.config.pic_realm); 
+	free_null(p->p_hdr.info.config.pic_priority); 
+	
+	free_null(p->p_hdr.info.runtime.pir_realm);
+	free_null(p->p_hdr.info.runtime.pir_prodname);
+	free_list( &p->p_hdr.info.runtime.pir_apps );
+	
+	free_list( &p->p_hdr.info.pi_endpoints );
+	
+	free_null(p->p_dbgorig);
+	
+	fd_list_unlink(&p->p_expiry);
+	fd_list_unlink(&p->p_actives);
+	
+	CHECK_FCT_DO( fd_fifo_del(&p->p_tosend), /* continue */ );
+	CHECK_POSIX_DO( pthread_mutex_destroy(&p->p_sr.mtx), /* continue */);
+	CHECK_POSIX_DO( pthread_cond_destroy(&p->p_sr.cnd), /* continue */);
+	
+	/* If the callback is still around... */
+	if (p->p_cb)
+		(*p->p_cb)(NULL, p->p_cb_data);
+	
+	/* Free the structure */
+	free(p);
+	return 0;
+}
+
+/* Terminate peer module (destroy all peers, first gently, then violently) */
+int fd_peer_fini()
+{
+	struct fd_list * li;
+	struct fd_list purge = FD_LIST_INITIALIZER(purge); /* Store zombie peers here */
+	int list_empty;
+	struct timespec	wait_until, now;
+	
+	TRACE_ENTRY();
+	
+	CHECK_FCT_DO(fd_p_expi_fini(), /* continue */);
+	
+	TRACE_DEBUG(INFO, "Sending terminate signal to all peer connections");
+	
+	CHECK_FCT_DO( pthread_rwlock_wrlock(&fd_g_peers_rw), /* continue */ );
+	for (li = fd_g_peers.next; li != &fd_g_peers; li = li->next) {
+		struct fd_peer * peer = (struct fd_peer *)li;
+		
+		fd_cpu_flush_cache();
+		if (peer->p_hdr.info.runtime.pir_state != STATE_ZOMBIE) {
+			CHECK_FCT_DO( fd_psm_terminate(peer, "REBOOTING"), /* continue */ );
+		} else {
+			li = li->prev; /* to avoid breaking the loop */
+			fd_list_unlink(&peer->p_hdr.chain);
+			fd_list_insert_before(&purge, &peer->p_hdr.chain);
+		}
+	}
+	list_empty = FD_IS_LIST_EMPTY(&fd_g_peers);
+	CHECK_FCT_DO( pthread_rwlock_unlock(&fd_g_peers_rw), /* continue */ );
+	
+	if (!list_empty) {
+		CHECK_SYS(  clock_gettime(CLOCK_REALTIME, &now)  );
+		TRACE_DEBUG(INFO, "Waiting for connections shutdown... (%d sec max)", DPR_TIMEOUT + 1);
+		wait_until.tv_sec  = now.tv_sec + DPR_TIMEOUT + 1;
+		wait_until.tv_nsec = now.tv_nsec;
+	}
+	
+	while ((!list_empty) && (TS_IS_INFERIOR(&now, &wait_until))) {
+		
+		/* Allow the PSM(s) to execute */
+		sched_yield();
+		
+		/* Remove zombie peers */
+		CHECK_FCT_DO( pthread_rwlock_wrlock(&fd_g_peers_rw), /* continue */ );
+		for (li = fd_g_peers.next; li != &fd_g_peers; li = li->next) {
+			struct fd_peer * peer = (struct fd_peer *)li;
+			fd_cpu_flush_cache();
+			if (peer->p_hdr.info.runtime.pir_state == STATE_ZOMBIE) {
+				li = li->prev; /* to avoid breaking the loop */
+				fd_list_unlink(&peer->p_hdr.chain);
+				fd_list_insert_before(&purge, &peer->p_hdr.chain);
+			}
+		}
+		list_empty = FD_IS_LIST_EMPTY(&fd_g_peers);
+		CHECK_FCT_DO( pthread_rwlock_unlock(&fd_g_peers_rw), /* continue */ );
+		CHECK_SYS(  clock_gettime(CLOCK_REALTIME, &now)  );
+	}
+	
+	if (!list_empty) {
+		TRACE_DEBUG(INFO, "Forcing connections shutdown");
+		CHECK_FCT_DO( pthread_rwlock_wrlock(&fd_g_peers_rw), /* continue */ );
+		while (!FD_IS_LIST_EMPTY(&fd_g_peers)) {
+			struct fd_peer * peer = (struct fd_peer *)(fd_g_peers.next);
+			fd_psm_abord(peer);
+			fd_list_unlink(&peer->p_hdr.chain);
+			fd_list_insert_before(&purge, &peer->p_hdr.chain);
+		}
+		CHECK_FCT_DO( pthread_rwlock_unlock(&fd_g_peers_rw), /* continue */ );
+	}
+	
+	/* Free memory objects of all peers */
+	while (!FD_IS_LIST_EMPTY(&purge)) {
+		struct fd_peer * peer = (struct fd_peer *)(purge.next);
+		fd_list_unlink(&peer->p_hdr.chain);
+		fd_peer_free(&peer);
+	}
+	
+	/* Now empty the validators list */
+	CHECK_FCT_DO( pthread_rwlock_wrlock(&validators_rw), /* continue */ );
+	while (!FD_IS_LIST_EMPTY( &validators )) {
+		struct fd_list * v = validators.next;
+		fd_list_unlink(v);
+		free(v);
+	}
+	CHECK_FCT_DO( pthread_rwlock_unlock(&validators_rw), /* continue */ );
+	
+	return 0;
+}
+
+/* Dump info of one peer */
+void fd_peer_dump(struct fd_peer * peer, int details)
+{
+	if (peer->p_eyec != EYEC_PEER) {
+		fd_log_debug("  Invalid peer @ %p !\n", peer);
+		return;
+	}
+
+	fd_log_debug(">  %s\t%s", STATE_STR(peer->p_hdr.info.runtime.pir_state), peer->p_hdr.info.pi_diamid);
+	if (details > INFO) {
+		fd_log_debug("\t(rlm:%s)", peer->p_hdr.info.runtime.pir_realm ?: "(unknown)");
+		if (peer->p_hdr.info.runtime.pir_prodname)
+			fd_log_debug("\t['%s' %u]", peer->p_hdr.info.runtime.pir_prodname, peer->p_hdr.info.runtime.pir_firmrev);
+	}
+	fd_log_debug("\n");
+	if (details > FULL) {
+		/* Dump all info */
+		fd_log_debug("\tEntry origin : %s\n", peer->p_dbgorig?: "not set");
+		fd_log_debug("\tConfig flags : %s%s%s%s%s - %s%s%s\n", 
+				peer->p_hdr.info.config.pic_flags.pro3 == PI_P3_DEFAULT ? "" :
+					(peer->p_hdr.info.config.pic_flags.pro3 == PI_P3_IP ? "IP." : "IPv6."),
+				peer->p_hdr.info.config.pic_flags.pro4 == PI_P4_DEFAULT ? "" :
+					(peer->p_hdr.info.config.pic_flags.pro4 == PI_P4_TCP ? "TCP." : "SCTP."),
+				peer->p_hdr.info.config.pic_flags.alg ? "PrefTCP." : "",
+				peer->p_hdr.info.config.pic_flags.sec & PI_SEC_NONE ? "NoTLSok" :"",
+				peer->p_hdr.info.config.pic_flags.sec & PI_SEC_TLS_OLD ? "OldTLS" :"",
+				peer->p_hdr.info.config.pic_flags.exp ? "Expire." : "",
+				peer->p_hdr.info.config.pic_flags.persist ? "Persist." : ""
+				);
+		fd_log_debug("\tLifetime : %d sec\n", peer->p_hdr.info.config.pic_lft);
+	}
+}
+
+/* Dump the list of peers */
+void fd_peer_dump_list(int details)
+{
+	struct fd_list * li;
+	
+	fd_log_debug("Dumping list of peers :\n");
+	CHECK_FCT_DO( pthread_rwlock_rdlock(&fd_g_peers_rw), /* continue */ );
+	
+	for (li = fd_g_peers.next; li != &fd_g_peers; li = li->next) {
+		struct fd_peer * np = (struct fd_peer *)li;
+		fd_peer_dump(np, details);
+	}
+	
+	CHECK_FCT_DO( pthread_rwlock_unlock(&fd_g_peers_rw), /* continue */ );
+}
+
+/* Handle an incoming CER request on a new connection */
+int fd_peer_handle_newCER( struct msg ** cer, struct cnxctx ** cnx )
+{
+	struct msg * msg;
+	struct dict_object *avp_oh_model;
+	avp_code_t code = AC_ORIGIN_HOST;
+	struct avp *avp_oh;
+	struct avp_hdr * avp_hdr;
+	struct fd_list * li;
+	int found = 0;
+	int ret = 0;
+	struct fd_peer * peer;
+	struct cnx_incoming * ev_data;
+	
+	TRACE_ENTRY("%p %p", cer, cnx);
+	CHECK_PARAMS(cer && *cer && cnx && *cnx);
+	
+	msg = *cer; 
+	
+	/* Find the Diameter Identity of the remote peer in the message */
+	CHECK_FCT( fd_dict_search ( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_CODE, &code, &avp_oh_model, ENOENT) );
+	CHECK_FCT( fd_msg_search_avp ( msg, avp_oh_model, &avp_oh ) );
+	CHECK_FCT( fd_msg_avp_hdr ( avp_oh, &avp_hdr ) );
+	
+	/* Search if we already have this peer id in our list. We take directly the write lock so that we don't need to upgrade if it is a new peer.
+	 * There is space for a small optimization here if needed.
+	 */
+	CHECK_POSIX( pthread_rwlock_wrlock(&fd_g_peers_rw) );
+	
+	for (li = fd_g_peers.next; li != &fd_g_peers; li = li->next) {
+		peer = (struct fd_peer *)li;
+		/* It is probably unwise to use strcasecmp on UTF8 data... To be improved! */
+		int cmp = strncasecmp( (char *)avp_hdr->avp_value->os.data, peer->p_hdr.info.pi_diamid, avp_hdr->avp_value->os.len );
+		if (cmp > 0)
+			continue;
+		if (cmp == 0)
+			found = 1;
+		break;
+	}
+	
+	if (!found) {
+		/* Create a new peer entry for this new remote peer */
+		peer = NULL;
+		CHECK_FCT_DO( ret = fd_peer_alloc(&peer), goto out );
+		
+		/* Set the peer Diameter Id and the responder flag parameters */
+		CHECK_MALLOC_DO( peer->p_hdr.info.pi_diamid = malloc(avp_hdr->avp_value->os.len + 1), { ret = ENOMEM; goto out; } );
+		memcpy(peer->p_hdr.info.pi_diamid, avp_hdr->avp_value->os.data, avp_hdr->avp_value->os.len);
+		peer->p_hdr.info.pi_diamid[avp_hdr->avp_value->os.len] = '\0';
+		CHECK_MALLOC_DO( peer->p_dbgorig = strdup(fd_cnx_getid(*cnx)), { ret = ENOMEM; goto out; } );
+		peer->p_flags.pf_responder = 1;
+		peer->p_flags.pf_delete = 1;
+		
+		/* Set this peer to expire on inactivity */
+		peer->p_hdr.info.config.pic_flags.exp 	= PI_EXP_INACTIVE;
+		peer->p_hdr.info.config.pic_lft		= 3600;	/* 1 hour without any message 
+		-- RFC3539 states that this must not be inferior to BRINGDOWN_INTERVAL = 5 minutes */
+		
+		/* Insert the new peer in the list (the PSM will take care of setting the expiry after validation) */
+		fd_list_insert_before( li, &peer->p_hdr.chain );
+		
+		/* Start the PSM, which will receive the event bellow */
+		CHECK_FCT_DO( ret = fd_psm_begin(peer), goto out );
+	} else {
+		/* Check if the peer is in zombie state */
+		fd_cpu_flush_cache();
+		if (peer->p_hdr.info.runtime.pir_state == STATE_ZOMBIE) {
+			/* Re-activate the peer */
+			if (peer->p_hdr.info.config.pic_flags.exp)
+				peer->p_flags.pf_responder = 1;
+			peer->p_hdr.info.runtime.pir_state = STATE_NEW;
+			CHECK_FCT_DO( ret = fd_psm_begin(peer), goto out );
+		}
+	}
+		
+	/* Send the new connection event to the PSM */
+	CHECK_MALLOC_DO( ev_data = malloc(sizeof(struct cnx_incoming)), { ret = ENOMEM; goto out; } );
+	memset(ev_data, 0, sizeof(ev_data));
+	
+	ev_data->cer = msg;
+	ev_data->cnx = *cnx;
+	ev_data->validate = !found;
+	
+	CHECK_FCT_DO( ret = fd_event_send(peer->p_events, FDEVP_CNX_INCOMING, sizeof(ev_data), ev_data), goto out );
+	
+out:	
+	CHECK_POSIX( pthread_rwlock_unlock(&fd_g_peers_rw) );
+
+	if (ret == 0) {
+		/* Reset the "out" parameters, so that they are not cleanup on function return. */
+		*cer = NULL;
+		*cnx = NULL;
+	}
+	
+	return ret;
+}
+
+/* Save a callback to accept / reject incoming unknown peers */
+int fd_peer_validate_register ( int (*peer_validate)(struct peer_info * /* info */, int * /* auth */, int (**cb2)(struct peer_info *)) )
+{
+	struct fd_list * v;
+	
+	TRACE_ENTRY("%p", peer_validate);
+	CHECK_PARAMS(peer_validate);
+	
+	/* Alloc a new entry */
+	CHECK_MALLOC( v = malloc(sizeof(struct fd_list)) );
+	fd_list_init( v, peer_validate );
+	
+	/* Add at the beginning of the list */
+	CHECK_FCT( pthread_rwlock_wrlock(&validators_rw) );
+	fd_list_insert_after(&validators, v);
+	CHECK_FCT( pthread_rwlock_unlock(&validators_rw));
+	
+	/* Done! */
+	return 0;
+}
+
+/* Validate a peer by calling the callbacks in turn -- return 0 if the peer is validated, ! 0 in case of error (>0) or if the peer is rejected (-1) */
+int fd_peer_validate( struct fd_peer * peer )
+{
+	int ret = 0;
+	struct fd_list * v;
+	
+	CHECK_FCT( pthread_rwlock_rdlock(&validators_rw) );
+	for (v = validators.next; v != &validators; v = v->next) {
+		int auth = 0;
+		pthread_cleanup_push(fd_cleanup_rwlock, &validators_rw);
+		CHECK_FCT_DO( ret = ((int(*)(struct peer_info *, int *, int (**)(struct peer_info *)))(v->o)) (&peer->p_hdr.info, &auth, &peer->p_cb2), goto out );
+		pthread_cleanup_pop(0);
+		if (auth) {
+			ret = (auth > 0) ? 0 : -1;
+			goto out;
+		}
+		peer->p_cb2 = NULL;
+	}
+	
+	/* No callback has given a firm result, the default is to reject */
+	ret = -1;
+out:
+	CHECK_FCT( pthread_rwlock_unlock(&validators_rw));
+	return ret;
+}
Index: libfdcore/queues.c
===================================================================
--- libfdcore/queues.c	(revision 658)
+++ libfdcore/queues.c	(revision 658)
@@ -0,0 +1,81 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@nict.go.jp>							 *
+*													 *
+* Copyright (c) 2009, WIDE Project and NICT								 *
+* All rights reserved.											 *
+* 													 *
+* Redistribution and use of this software in source and binary forms, with or without modification, are  *
+* permitted provided that the following conditions are met:						 *
+* 													 *
+* * Redistributions of source code must retain the above 						 *
+*   copyright notice, this list of conditions and the 							 *
+*   following disclaimer.										 *
+*    													 *
+* * Redistributions in binary form must reproduce the above 						 *
+*   copyright notice, this list of conditions and the 							 *
+*   following disclaimer in the documentation and/or other						 *
+*   materials provided with the distribution.								 *
+* 													 *
+* * Neither the name of the WIDE Project or NICT nor the 						 *
+*   names of its contributors may be used to endorse or 						 *
+*   promote products derived from this software without 						 *
+*   specific prior written permission of WIDE Project and 						 *
+*   NICT.												 *
+* 													 *
+* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED *
+* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
+* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR *
+* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 	 *
+* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 	 *
+* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR *
+* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF   *
+* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.								 *
+*********************************************************************************************************/
+
+#include "fdcore-internal.h"
+
+/* The global message queues */
+struct fifo * fd_g_incoming = NULL;
+struct fifo * fd_g_outgoing = NULL;
+struct fifo * fd_g_local = NULL;
+
+/* Initialize the message queues. */
+int fd_queues_init(void)
+{
+	TRACE_ENTRY();
+	CHECK_FCT( fd_fifo_new ( &fd_g_incoming ) );
+	CHECK_FCT( fd_fifo_new ( &fd_g_outgoing ) );
+	CHECK_FCT( fd_fifo_new ( &fd_g_local ) );
+	return 0;
+}
+
+/* Destroy a queue after emptying it (and dumping the content) */
+int fd_queues_fini(struct fifo ** queue)
+{
+	struct msg * msg;
+	int ret = 0;
+	
+	TRACE_ENTRY("%p", queue);
+	
+	/* Note : the threads that post into this queue should already been stopped before this !!! */
+
+	/* Empty all contents */
+	while (1) {
+		/* Check if there is a message in the queue */
+		ret = fd_fifo_tryget(*queue, &msg);
+		if (ret == EWOULDBLOCK)
+			break;
+		CHECK_FCT(ret);
+		
+		/* We got one! */
+		fd_log_debug("The following message is lost because the daemon is stopping:\n");
+		fd_msg_dump_walk(NONE, msg);
+		fd_msg_free(msg);
+	}
+	
+	/* Now, delete the empty queue */
+	CHECK_FCT( fd_fifo_del ( queue ) );
+	
+	return 0;
+}
Index: libfdcore/routing_dispatch.c
===================================================================
--- libfdcore/routing_dispatch.c	(revision 658)
+++ libfdcore/routing_dispatch.c	(revision 658)
@@ -0,0 +1,1233 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@nict.go.jp>							 *
+*													 *
+* Copyright (c) 2010, 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 "fdcore-internal.h"
+
+/********************************************************************************/
+/*              First part : handling the extensions callbacks                  */
+/********************************************************************************/
+
+/* Lists of the callbacks, and locks to protect them */
+static pthread_rwlock_t rt_fwd_lock = PTHREAD_RWLOCK_INITIALIZER;
+static struct fd_list 	rt_fwd_list = FD_LIST_INITIALIZER_O(rt_fwd_list, &rt_fwd_lock);
+
+static pthread_rwlock_t rt_out_lock = PTHREAD_RWLOCK_INITIALIZER;
+static struct fd_list 	rt_out_list = FD_LIST_INITIALIZER_O(rt_out_list, &rt_out_lock);
+
+/* Items in the lists are the same */
+struct rt_hdl {
+	struct fd_list	chain;	/* link in the rt_fwd_list or rt_out_list */
+	void *		cbdata;	/* the registered data */
+	union {
+		int	order;	/* This value is used to sort the list */
+		int 	dir;	/* It is the direction for FWD handlers */
+		int	prio;	/* and the priority for OUT handlers */
+	};
+	union {
+		int (*rt_fwd_cb)(void * cbdata, struct msg ** msg);
+		int (*rt_out_cb)(void * cbdata, struct msg * msg, struct fd_list * candidates);
+	};
+};	
+
+/* Add a new entry in the list */
+static int add_ordered(struct rt_hdl * new, struct fd_list * list)
+{
+	/* The list is ordered by prio parameter */
+	struct fd_list * li;
+	
+	CHECK_POSIX( pthread_rwlock_wrlock(list->o) );
+	
+	for (li = list->next; li != list; li = li->next) {
+		struct rt_hdl * h = (struct rt_hdl *) li;
+		if (new->order <= h->order)
+			break;
+	}
+	
+	fd_list_insert_before(li, &new->chain);
+	
+	CHECK_POSIX( pthread_rwlock_unlock(list->o) );
+	
+	return 0;
+}
+
+/* Register a new FWD callback */
+int fd_rt_fwd_register ( int (*rt_fwd_cb)(void * cbdata, struct msg ** msg), void * cbdata, enum fd_rt_fwd_dir dir, struct fd_rt_fwd_hdl ** handler )
+{
+	struct rt_hdl * new;
+	
+	TRACE_ENTRY("%p %p %d %p", rt_fwd_cb, cbdata, dir, handler);
+	CHECK_PARAMS( rt_fwd_cb );
+	CHECK_PARAMS( (dir >= RT_FWD_REQ) && ( dir <= RT_FWD_ANS) );
+	
+	/* Create a new container */
+	CHECK_MALLOC(new = malloc(sizeof(struct rt_hdl)));
+	memset(new, 0, sizeof(struct rt_hdl));
+	
+	/* Write the content */
+	fd_list_init(&new->chain, NULL);
+	new->cbdata 	= cbdata;
+	new->dir    	= dir;
+	new->rt_fwd_cb 	= rt_fwd_cb;
+	
+	/* Save this in the list */
+	CHECK_FCT( add_ordered(new, &rt_fwd_list) );
+	
+	/* Give it back to the extension if needed */
+	if (handler)
+		*handler = (void *)new;
+	
+	return 0;
+}
+
+/* Remove it */
+int fd_rt_fwd_unregister ( struct fd_rt_fwd_hdl * handler, void ** cbdata )
+{
+	struct rt_hdl * del;
+	TRACE_ENTRY( "%p %p", handler, cbdata);
+	CHECK_PARAMS( handler );
+	
+	del = (struct rt_hdl *)handler;
+	CHECK_PARAMS( del->chain.head == &rt_fwd_list );
+	
+	/* Unlink */
+	CHECK_POSIX( pthread_rwlock_wrlock(&rt_fwd_lock) );
+	fd_list_unlink(&del->chain);
+	CHECK_POSIX( pthread_rwlock_unlock(&rt_fwd_lock) );
+	
+	if (cbdata)
+		*cbdata = del->cbdata;
+	
+	free(del);
+	return 0;
+}
+
+/* Register a new OUT callback */
+int fd_rt_out_register ( int (*rt_out_cb)(void * cbdata, struct msg * msg, struct fd_list * candidates), void * cbdata, int priority, struct fd_rt_out_hdl ** handler )
+{
+	struct rt_hdl * new;
+	
+	TRACE_ENTRY("%p %p %d %p", rt_out_cb, cbdata, priority, handler);
+	CHECK_PARAMS( rt_out_cb );
+	
+	/* Create a new container */
+	CHECK_MALLOC(new = malloc(sizeof(struct rt_hdl)));
+	memset(new, 0, sizeof(struct rt_hdl));
+	
+	/* Write the content */
+	fd_list_init(&new->chain, NULL);
+	new->cbdata 	= cbdata;
+	new->prio    	= priority;
+	new->rt_out_cb 	= rt_out_cb;
+	
+	/* Save this in the list */
+	CHECK_FCT( add_ordered(new, &rt_out_list) );
+	
+	/* Give it back to the extension if needed */
+	if (handler)
+		*handler = (void *)new;
+	
+	return 0;
+}
+
+/* Remove it */
+int fd_rt_out_unregister ( struct fd_rt_out_hdl * handler, void ** cbdata )
+{
+	struct rt_hdl * del;
+	TRACE_ENTRY( "%p %p", handler, cbdata);
+	CHECK_PARAMS( handler );
+	
+	del = (struct rt_hdl *)handler;
+	CHECK_PARAMS( del->chain.head == &rt_out_list );
+	
+	/* Unlink */
+	CHECK_POSIX( pthread_rwlock_wrlock(&rt_out_lock) );
+	fd_list_unlink(&del->chain);
+	CHECK_POSIX( pthread_rwlock_unlock(&rt_out_lock) );
+	
+	if (cbdata)
+		*cbdata = del->cbdata;
+	
+	free(del);
+	return 0;
+}
+
+/********************************************************************************/
+/*                      Some default OUT routing callbacks                      */
+/********************************************************************************/
+
+/* Prevent sending to peers that do not support the message application */
+static int dont_send_if_no_common_app(void * cbdata, struct msg * msg, struct fd_list * candidates)
+{
+	struct fd_list * li;
+	struct msg_hdr * hdr;
+	
+	TRACE_ENTRY("%p %p %p", cbdata, msg, candidates);
+	CHECK_PARAMS(msg && candidates);
+	
+	CHECK_FCT( fd_msg_hdr(msg, &hdr) );
+	
+	/* For Base Diameter Protocol, every peer is supposed to support it, so skip */
+	if (hdr->msg_appl == 0)
+		return 0;
+	
+	/* Otherwise, check that the peers support the application */
+	for (li = candidates->next; li != candidates; li = li->next) {
+		struct rtd_candidate *c = (struct rtd_candidate *) li;
+		struct fd_peer * peer;
+		struct fd_app *found;
+		CHECK_FCT( fd_peer_getbyid( c->diamid, (void *)&peer ) );
+		if (peer && (peer->p_hdr.info.runtime.pir_relay == 0)) {
+			/* Check if the remote peer advertised the message's appli */
+			CHECK_FCT( fd_app_check(&peer->p_hdr.info.runtime.pir_apps, hdr->msg_appl, &found) );
+			if (!found)
+				c->score += FD_SCORE_NO_DELIVERY;
+		}
+	}
+
+	return 0;
+}
+
+/* Detect if the Destination-Host and Destination-Realm match the peer */
+static int score_destination_avp(void * cbdata, struct msg * msg, struct fd_list * candidates)
+{
+	struct fd_list * li;
+	struct avp * avp;
+	union avp_value *dh = NULL, *dr = NULL;
+	
+	TRACE_ENTRY("%p %p %p", cbdata, msg, candidates);
+	CHECK_PARAMS(msg && candidates);
+	
+	/* Search the Destination-Host and Destination-Realm AVPs -- we could also use fd_msg_search_avp here, but this one is slightly more efficient */
+	CHECK_FCT(  fd_msg_browse(msg, MSG_BRW_FIRST_CHILD, &avp, NULL) );
+	while (avp) {
+		struct avp_hdr * ahdr;
+		CHECK_FCT(  fd_msg_avp_hdr( avp, &ahdr ) );
+
+		if (! (ahdr->avp_flags & AVP_FLAG_VENDOR)) {
+			switch (ahdr->avp_code) {
+				case AC_DESTINATION_HOST:
+					/* Parse this AVP */
+					CHECK_FCT( fd_msg_parse_dict ( avp, fd_g_config->cnf_dict, NULL ) );
+					ASSERT( ahdr->avp_value );
+					dh = ahdr->avp_value;
+					break;
+
+				case AC_DESTINATION_REALM:
+					/* Parse this AVP */
+					CHECK_FCT( fd_msg_parse_dict ( avp, fd_g_config->cnf_dict, NULL ) );
+					ASSERT( ahdr->avp_value );
+					dr = ahdr->avp_value;
+					break;
+			}
+		}
+
+		if (dh && dr)
+			break;
+
+		/* Go to next AVP */
+		CHECK_FCT(  fd_msg_browse(avp, MSG_BRW_NEXT, &avp, NULL) );
+	}
+	
+	/* Now, check each candidate against these AVP values */
+	for (li = candidates->next; li != candidates; li = li->next) {
+		struct rtd_candidate *c = (struct rtd_candidate *) li;
+		struct fd_peer * peer;
+		CHECK_FCT( fd_peer_getbyid( c->diamid, (void *)&peer ) );
+		if (peer) {
+			if (dh 
+				&& (dh->os.len == strlen(peer->p_hdr.info.pi_diamid)) 
+				/* Here again we use strncasecmp on UTF8 data... This should probably be changed. */
+				&& (strncasecmp(peer->p_hdr.info.pi_diamid, (char *)dh->os.data, dh->os.len) == 0)) {
+				/* The candidate is the Destination-Host */
+				c->score += FD_SCORE_FINALDEST;
+			} else {
+				if (dr  && peer->p_hdr.info.runtime.pir_realm 
+					&& (dr->os.len == strlen(peer->p_hdr.info.runtime.pir_realm)) 
+					/* Yet another case where we use strncasecmp on UTF8 data... Hmmm :-( */
+					&& (strncasecmp(peer->p_hdr.info.runtime.pir_realm, (char *)dr->os.data, dr->os.len) == 0)) {
+					/* The candidate's realm matchs the Destination-Realm */
+					c->score += FD_SCORE_REALM;
+				}
+			}
+		}
+	}
+
+	return 0;
+}
+
+/********************************************************************************/
+/*                        Helper functions                                      */
+/********************************************************************************/
+
+/* Find (first) '!' and '@' positions in a UTF-8 encoded string (User-Name AVP value) */
+static void nai_get_indexes(union avp_value * un, int * excl_idx, int * at_idx)
+{
+	int i;
+	
+	TRACE_ENTRY("%p %p %p", un, excl_idx, at_idx);
+	CHECK_PARAMS_DO( un && excl_idx, return );
+	*excl_idx = 0;
+	
+	/* Search if there is a '!' before any '@' -- do we need to check it contains a '.' ? */
+	for (i = 0; i < un->os.len; i++) {
+		/* The '!' marks the decorated NAI */
+		if ( un->os.data[i] == (unsigned char) '!' ) {
+			if (!*excl_idx)
+				*excl_idx = i;
+			if (!at_idx)
+				return;
+		}
+		/* If we reach the realm part, we can stop */
+		if ( un->os.data[i] == (unsigned char) '@' ) {
+			if (at_idx)
+				*at_idx = i;
+			break;
+		}
+		/* Skip escaped characters */
+		if ( un->os.data[i] == (unsigned char) '\\' ) {
+			i++;
+			continue;
+		}
+		/* Skip UTF-8 characters spanning on several bytes */
+		if ( (un->os.data[i] & 0xF8) == 0xF0 ) { /* 11110zzz */
+			i += 3;
+			continue;
+		}
+		if ( (un->os.data[i] & 0xF0) == 0xE0 ) { /* 1110yyyy */
+			i += 2;
+			continue;
+		}
+		if ( (un->os.data[i] & 0xE0) == 0xC0 ) { /* 110yyyxx */
+			i += 1;
+			continue;
+		}
+	}
+	
+	return;
+}	
+
+/* Test if a User-Name AVP contains a Decorated NAI -- RFC4282, RFC5729 */
+static int is_decorated_NAI(union avp_value * un)
+{
+	int i;
+	TRACE_ENTRY("%p", un);
+	
+	/* If there was no User-Name, we return false */
+	if (un == NULL)
+		return 0;
+	
+	nai_get_indexes(un, &i, NULL);
+	
+	return i;
+}
+
+/* Create new User-Name and Destination-Realm values */
+static int process_decorated_NAI(union avp_value * un, union avp_value * dr)
+{
+	int at_idx = 0, sep_idx = 0;
+	unsigned char * old_un;
+	TRACE_ENTRY("%p %p", un, dr);
+	CHECK_PARAMS(un && dr);
+	
+	/* Save the decorated User-Name, for example 'homerealm.example.net!user@otherrealm.example.net' */
+	old_un = un->os.data;
+	
+	/* Search the positions of the first '!' and the '@' in the string */
+	nai_get_indexes(un, &sep_idx, &at_idx);
+	CHECK_PARAMS( (0 < sep_idx) && (sep_idx < at_idx) && (at_idx < un->os.len));
+	
+	/* Create the new User-Name value */
+	CHECK_MALLOC( un->os.data = malloc( at_idx ) );
+	memcpy( un->os.data, old_un + sep_idx + 1, at_idx - sep_idx ); /* user@ */
+	memcpy( un->os.data + at_idx - sep_idx, old_un, sep_idx ); /* homerealm.example.net */
+	
+	/* Create the new Destination-Realm value */
+	CHECK_MALLOC( dr->os.data = realloc(dr->os.data, sep_idx) );
+	memcpy( dr->os.data, old_un, sep_idx );
+	dr->os.len = sep_idx;
+	
+	TRACE_DEBUG(FULL, "Processed Decorated NAI : '%.*s' became '%.*s' (%.*s)",
+				un->os.len, old_un,
+				at_idx, un->os.data,
+				dr->os.len, dr->os.data);
+	
+	un->os.len = at_idx;
+	free(old_un);
+	
+	return 0;
+}
+
+/* Function to return an error to an incoming request */
+static int return_error(struct msg ** pmsg, char * error_code, char * error_message, struct avp * failedavp)
+{
+	struct fd_peer * peer;
+	int is_loc = 0;
+
+	/* Get the source of the message */
+	{
+		char * id;
+		CHECK_FCT( fd_msg_source_get( *pmsg, &id ) );
+		
+		if (id == NULL) {
+			is_loc = 1; /* The message was issued locally */
+		} else {
+		
+			/* Search the peer with this id */
+			CHECK_FCT( fd_peer_getbyid( id, (void *)&peer ) );
+
+			if (!peer) {
+				TRACE_DEBUG(INFO, "Unable to send error '%s' to deleted peer '%s' in reply to:", error_code, id);
+				fd_msg_dump_walk(INFO, *pmsg);
+				fd_msg_free(*pmsg);
+				*pmsg = NULL;
+				return 0;
+			}
+		}
+	}
+	
+	/* Create the error message */
+	CHECK_FCT( fd_msg_new_answer_from_req ( fd_g_config->cnf_dict, pmsg, MSGFL_ANSW_ERROR ) );
+
+	/* Set the error code */
+	CHECK_FCT( fd_msg_rescode_set(*pmsg, error_code, error_message, failedavp, 1 ) );
+
+	/* Send the answer */
+	if (is_loc) {
+		CHECK_FCT( fd_fifo_post(fd_g_incoming, pmsg) );
+	} else {
+		CHECK_FCT( fd_out_send(pmsg, NULL, peer, 0) );
+	}
+	
+	/* Done */
+	return 0;
+}
+
+
+/****************************************************************************/
+/*         Second part : threads moving messages in the daemon              */
+/****************************************************************************/
+
+/* These are the functions of each threads: dispatch & routing */
+/* The DISPATCH message processing */
+static int msg_dispatch(struct msg ** pmsg)
+{
+	struct msg_hdr * hdr;
+	int is_req = 0, ret;
+	struct session * sess;
+	enum disp_action action;
+	const char * ec = NULL;
+	const char * em = NULL;
+
+	/* Read the message header */
+	CHECK_FCT( fd_msg_hdr(*pmsg, &hdr) );
+	is_req = hdr->msg_flags & CMD_FLAG_REQUEST;
+	
+	/* Note: if the message is for local delivery, we should test for duplicate
+	  (draft-asveren-dime-dupcons-00). This may conflict with path validation decisions, no clear answer yet */
+
+	/* At this point, we need to understand the message content, so parse it */
+	CHECK_FCT_DO( ret = fd_msg_parse_or_error( pmsg ),
+		{
+			/* in case of error, the message is already dump'd */
+			if ((ret == EBADMSG) && (*pmsg != NULL)) {
+				/* msg now contains the answer message to send back */
+				CHECK_FCT( fd_fifo_post(fd_g_outgoing, pmsg) );
+			}
+			if (*pmsg) {	/* another error happen'd */
+				TRACE_DEBUG(INFO, "An unexpected error occurred (%s), discarding a message:", strerror(ret));
+				fd_msg_dump_walk(INFO, *pmsg);
+				CHECK_FCT_DO( fd_msg_free(*pmsg), /* continue */);
+				*pmsg = NULL;
+			}
+			/* We're done with this one */
+			return 0;
+		} );
+
+	/* First, if the original request was registered with a callback and we receive the answer, call it. */
+	if ( ! is_req ) {
+		struct msg * qry;
+		void (*anscb)(void *, struct msg **) = NULL;
+		void * data = NULL;
+
+		/* Retrieve the corresponding query */
+		CHECK_FCT( fd_msg_answ_getq( *pmsg, &qry ) );
+
+		/* Retrieve any registered handler */
+		CHECK_FCT( fd_msg_anscb_get( qry, &anscb, &data ) );
+
+		/* If a callback was registered, pass the message to it */
+		if (anscb != NULL) {
+
+			TRACE_DEBUG(FULL, "Calling callback registered when query was sent (%p, %p)", anscb, data);
+			(*anscb)(data, pmsg);
+			
+			/* If the message is processed, we're done */
+			if (*pmsg == NULL) {
+				return 0;
+			}
+		}
+	}
+	
+	/* Retrieve the session of the message */
+	CHECK_FCT( fd_msg_sess_get(fd_g_config->cnf_dict, *pmsg, &sess, NULL) );
+
+	/* Now, call any callback registered for the message */
+	CHECK_FCT( fd_msg_dispatch ( pmsg, sess, &action, &ec) );
+
+	/* Now, act depending on msg and action and ec */
+	if (*pmsg)
+		switch ( action ) {
+			case DISP_ACT_CONT:
+				/* No callback has handled the message, let's reply with a generic error */
+				em = "The message was not handled by any extension callback";
+				ec = "DIAMETER_COMMAND_UNSUPPORTED";
+			
+			case DISP_ACT_ERROR:
+				/* We have a problem with delivering the message */
+				if (ec == NULL) {
+					ec = "DIAMETER_UNABLE_TO_COMPLY";
+				}
+				
+				if (!is_req) {
+					TRACE_DEBUG(INFO, "Received an answer to a localy issued query, but no handler processed this answer!");
+					fd_msg_dump_walk(INFO, *pmsg);
+					fd_msg_free(*pmsg);
+					*pmsg = NULL;
+					break;
+				}
+				
+				/* Create an answer with the error code and message */
+				CHECK_FCT( fd_msg_new_answer_from_req ( fd_g_config->cnf_dict, pmsg, 0 ) );
+				CHECK_FCT( fd_msg_rescode_set(*pmsg, (char *)ec, (char *)em, NULL, 1 ) );
+				
+			case DISP_ACT_SEND:
+				/* Now, send the message */
+				CHECK_FCT( fd_fifo_post(fd_g_outgoing, pmsg) );
+		}
+	
+	/* We're done with dispatching this message */
+	return 0;
+}
+
+/* The ROUTING-IN message processing */
+static int msg_rt_in(struct msg ** pmsg)
+{
+	struct msg_hdr * hdr;
+	int is_req = 0;
+	int is_err = 0;
+	char * qry_src = NULL;
+
+	/* Read the message header */
+	CHECK_FCT( fd_msg_hdr(*pmsg, &hdr) );
+	is_req = hdr->msg_flags & CMD_FLAG_REQUEST;
+	is_err = hdr->msg_flags & CMD_FLAG_ERROR;
+
+	/* Handle incorrect bits */
+	if (is_req && is_err) {
+		CHECK_FCT( return_error( pmsg, "DIAMETER_INVALID_HDR_BITS", "R & E bits were set", NULL) );
+		return 0;
+	}
+	
+	/* If it is a request, we must analyze its content to decide what we do with it */
+	if (is_req) {
+		struct avp * avp, *un = NULL;
+		union avp_value * un_val = NULL, *dr_val = NULL;
+		enum status { UNKNOWN, YES, NO };
+		/* Are we Destination-Host? */
+		enum status is_dest_host = UNKNOWN;
+		/* Are we Destination-Realm? */
+		enum status is_dest_realm = UNKNOWN;
+		/* Do we support the application of the message? */
+		enum status is_local_app = UNKNOWN;
+
+		/* Check if we have local support for the message application */
+		if ( (hdr->msg_appl == 0) || (hdr->msg_appl == AI_RELAY) ) {
+			TRACE_DEBUG(INFO, "Received a routable message with application id 0, returning DIAMETER_APPLICATION_UNSUPPORTED");
+			CHECK_FCT( return_error( pmsg, "DIAMETER_APPLICATION_UNSUPPORTED", "Routable message with application id 0 or relay", NULL) );
+			return 0;
+		} else {
+			struct fd_app * app;
+			CHECK_FCT( fd_app_check(&fd_g_config->cnf_apps, hdr->msg_appl, &app) );
+			is_local_app = (app ? YES : NO);
+		}
+
+		/* Parse the message for Dest-Host and Dest-Realm */
+		CHECK_FCT(  fd_msg_browse(*pmsg, MSG_BRW_FIRST_CHILD, &avp, NULL)  );
+		while (avp) {
+			struct avp_hdr * ahdr;
+			struct fd_pei error_info;
+			int ret;
+			CHECK_FCT(  fd_msg_avp_hdr( avp, &ahdr )  );
+
+			if (! (ahdr->avp_flags & AVP_FLAG_VENDOR)) {
+				switch (ahdr->avp_code) {
+					case AC_DESTINATION_HOST:
+						/* Parse this AVP */
+						CHECK_FCT_DO( ret = fd_msg_parse_dict ( avp, fd_g_config->cnf_dict, &error_info ),
+							{
+								if (error_info.pei_errcode) {
+									CHECK_FCT( return_error( pmsg, error_info.pei_errcode, error_info.pei_message, error_info.pei_avp) );
+									return 0;
+								} else {
+									return ret;
+								}
+							} );
+						ASSERT( ahdr->avp_value );
+						/* Compare the Destination-Host AVP of the message with our identity */
+						if (ahdr->avp_value->os.len != fd_g_config->cnf_diamid_len) {
+							is_dest_host = NO;
+						} else {
+							is_dest_host = (strncasecmp(fd_g_config->cnf_diamid, (char *)ahdr->avp_value->os.data, fd_g_config->cnf_diamid_len) 
+										? NO : YES);
+						}
+						break;
+
+					case AC_DESTINATION_REALM:
+						/* Parse this AVP */
+						CHECK_FCT_DO( ret = fd_msg_parse_dict ( avp, fd_g_config->cnf_dict, &error_info ),
+							{
+								if (error_info.pei_errcode) {
+									CHECK_FCT( return_error( pmsg, error_info.pei_errcode, error_info.pei_message, error_info.pei_avp) );
+									return 0;
+								} else {
+									return ret;
+								}
+							} );
+						ASSERT( ahdr->avp_value );
+						dr_val = ahdr->avp_value;
+						/* Compare the Destination-Realm AVP of the message with our identity */
+						if (ahdr->avp_value->os.len != fd_g_config->cnf_diamrlm_len) {
+							is_dest_realm = NO;
+						} else {
+							is_dest_realm = (strncasecmp(fd_g_config->cnf_diamrlm, (char *)ahdr->avp_value->os.data, fd_g_config->cnf_diamrlm_len) 
+										? NO : YES);
+						}
+						break;
+
+					case AC_USER_NAME:
+						/* Parse this AVP */
+						CHECK_FCT_DO( ret = fd_msg_parse_dict ( avp, fd_g_config->cnf_dict, &error_info ),
+							{
+								if (error_info.pei_errcode) {
+									CHECK_FCT( return_error( pmsg, error_info.pei_errcode, error_info.pei_message, error_info.pei_avp) );
+									return 0;
+								} else {
+									return ret;
+								}
+							} );
+						ASSERT( ahdr->avp_value );
+						un = avp;
+						un_val = ahdr->avp_value;
+						break;
+				}
+			}
+
+			if ((is_dest_host != UNKNOWN) && (is_dest_realm != UNKNOWN) && un)
+				break;
+
+			/* Go to next AVP */
+			CHECK_FCT(  fd_msg_browse(avp, MSG_BRW_NEXT, &avp, NULL)  );
+		}
+
+		/* OK, now decide what we do with the request */
+
+		/* Handle the missing routing AVPs first */
+		if ( is_dest_realm == UNKNOWN ) {
+			CHECK_FCT( return_error( pmsg, "DIAMETER_COMMAND_UNSUPPORTED", "Non-routable message not supported (invalid bit ? missing Destination-Realm ?)", NULL) );
+			return 0;
+		}
+
+		/* If we are listed as Destination-Host */
+		if (is_dest_host == YES) {
+			if (is_local_app == YES) {
+				/* Ok, give the message to the dispatch thread */
+				CHECK_FCT( fd_fifo_post(fd_g_local, pmsg) );
+			} else {
+				/* We don't support the application, reply an error */
+				CHECK_FCT( return_error( pmsg, "DIAMETER_APPLICATION_UNSUPPORTED", NULL, NULL) );
+			}
+			return 0;
+		}
+
+		/* If the message is explicitely for someone else */
+		if ((is_dest_host == NO) || (is_dest_realm == NO)) {
+			if (fd_g_config->cnf_flags.no_fwd) {
+				CHECK_FCT( return_error( pmsg, "DIAMETER_UNABLE_TO_DELIVER", "This peer is not an agent", NULL) );
+				return 0;
+			}
+		} else {
+		/* Destination-Host was not set, and Destination-Realm is matching : we may handle or pass to a fellow peer */
+
+			/* test for decorated NAI  (RFC5729 section 4.4) */
+			if (is_decorated_NAI(un_val)) {
+				/* Handle the decorated NAI */
+				CHECK_FCT_DO( process_decorated_NAI(un_val, dr_val),
+					{
+						/* If the process failed, we assume it is because of the AVP format */
+						CHECK_FCT( return_error( pmsg, "DIAMETER_INVALID_AVP_VALUE", "Failed to process decorated NAI", un) );
+						return 0;
+					} );
+
+				/* We have transformed the AVP, now submit it again in the queue */
+				CHECK_FCT(fd_fifo_post(fd_g_incoming, pmsg) );
+				return 0;
+			}
+
+			if (is_local_app == YES) {
+				/* Handle localy since we are able to */
+				CHECK_FCT(fd_fifo_post(fd_g_local, pmsg) );
+				return 0;
+			}
+
+			if (fd_g_config->cnf_flags.no_fwd) {
+				/* We return an error */
+				CHECK_FCT( return_error( pmsg, "DIAMETER_APPLICATION_UNSUPPORTED", NULL, NULL) );
+				return 0;
+			}
+		}
+
+		/* From that point, for requests, we will call the registered callbacks, then forward to another peer */
+
+	} else {
+		/* The message is an answer */
+		struct msg * qry;
+
+		/* Retrieve the corresponding query and its origin */
+		CHECK_FCT( fd_msg_answ_getq( *pmsg, &qry ) );
+		CHECK_FCT( fd_msg_source_get( qry, &qry_src ) );
+
+		if ((!qry_src) && (!is_err)) {
+			/* The message is a normal answer to a request issued localy, we do not call the callbacks chain on it. */
+			CHECK_FCT(fd_fifo_post(fd_g_local, pmsg) );
+			return 0;
+		}
+
+		/* From that point, for answers, we will call the registered callbacks, then pass it to the dispatch module or forward it */
+	}
+
+	/* Call all registered callbacks for this message */
+	{
+		struct fd_list * li;
+
+		CHECK_FCT( pthread_rwlock_rdlock( &rt_fwd_lock ) );
+		pthread_cleanup_push( fd_cleanup_rwlock, &rt_fwd_lock );
+
+		/* requests: dir = 1 & 2 => in order; answers = 3 & 2 => in reverse order */
+		for (	li = (is_req ? rt_fwd_list.next : rt_fwd_list.prev) ; *pmsg && (li != &rt_fwd_list) ; li = (is_req ? li->next : li->prev) ) {
+			struct rt_hdl * rh = (struct rt_hdl *)li;
+
+			if (is_req && (rh->dir > RT_FWD_ALL))
+				break;
+			if ((!is_req) && (rh->dir < RT_FWD_ALL))
+				break;
+
+			/* Ok, call this cb */
+			TRACE_DEBUG(ANNOYING, "Calling next FWD callback on %p : %p", *pmsg, rh->rt_fwd_cb);
+			CHECK_FCT_DO( (*rh->rt_fwd_cb)(rh->cbdata, pmsg),
+				{
+					TRACE_DEBUG(INFO, "A FWD routing callback returned an error, message discarded.");
+					fd_msg_dump_walk(INFO, *pmsg);
+					fd_msg_free(*pmsg);
+					*pmsg = NULL;
+				} );
+		}
+
+		pthread_cleanup_pop(0);
+		CHECK_FCT( pthread_rwlock_unlock( &rt_fwd_lock ) );
+
+		/* If a callback has handled the message, we stop now */
+		if (!*pmsg)
+			return 0;
+	}
+
+	/* Now pass the message to the next step: either forward to another peer, or dispatch to local extensions */
+	if (is_req || qry_src) {
+		CHECK_FCT(fd_fifo_post(fd_g_outgoing, pmsg) );
+	} else {
+		CHECK_FCT(fd_fifo_post(fd_g_local, pmsg) );
+	}
+
+	/* We're done with this message */
+	return 0;
+}
+		
+
+/* The ROUTING-OUT message processing */
+static int msg_rt_out(struct msg ** pmsg)
+{
+	struct rt_data * rtd = NULL;
+	struct msg_hdr * hdr;
+	int is_req = 0;
+	int ret;
+	struct fd_list * li, *candidates;
+	struct avp * avp;
+	struct rtd_candidate * c;
+	
+	/* Read the message header */
+	CHECK_FCT( fd_msg_hdr(*pmsg, &hdr) );
+	is_req = hdr->msg_flags & CMD_FLAG_REQUEST;
+	
+	/* For answers, the routing is very easy */
+	if ( ! is_req ) {
+		struct msg * qry;
+		char * qry_src = NULL;
+		struct msg_hdr * qry_hdr;
+		struct fd_peer * peer = NULL;
+
+		/* Retrieve the corresponding query and its origin */
+		CHECK_FCT( fd_msg_answ_getq( *pmsg, &qry ) );
+		CHECK_FCT( fd_msg_source_get( qry, &qry_src ) );
+
+		ASSERT( qry_src ); /* if it is NULL, the message should have been in the LOCAL queue! */
+
+		/* Find the peer corresponding to this name */
+		CHECK_FCT( fd_peer_getbyid( qry_src, (void *) &peer ) );
+		fd_cpu_flush_cache();
+		if ((!peer) || (peer->p_hdr.info.runtime.pir_state != STATE_OPEN)) {
+			TRACE_DEBUG(INFO, "Unable to forward answer message to peer '%s', deleted or not in OPEN state.", qry_src);
+			fd_msg_dump_walk(INFO, *pmsg);
+			fd_msg_free(*pmsg);
+			*pmsg = NULL;
+			return 0;
+		}
+
+		/* We must restore the hop-by-hop id */
+		CHECK_FCT( fd_msg_hdr(qry, &qry_hdr) );
+		hdr->msg_hbhid = qry_hdr->msg_hbhid;
+
+		/* Push the message into this peer */
+		CHECK_FCT( fd_out_send(pmsg, NULL, peer, 0) );
+
+		/* We're done with this answer */
+		return 0;
+	}
+	
+	/* From that point, the message is a request */
+
+	/* Get the routing data out of the message if any (in case of re-transmit) */
+	CHECK_FCT( fd_msg_rt_get ( *pmsg, &rtd ) );
+
+	/* If there is no routing data already, let's create it */
+	if (rtd == NULL) {
+		CHECK_FCT( fd_rtd_init(&rtd) );
+
+		/* Add all peers currently in OPEN state */
+		CHECK_FCT( pthread_rwlock_rdlock(&fd_g_activ_peers_rw) );
+		for (li = fd_g_activ_peers.next; li != &fd_g_activ_peers; li = li->next) {
+			struct fd_peer * p = (struct fd_peer *)li->o;
+			CHECK_FCT_DO( ret = fd_rtd_candidate_add(rtd, p->p_hdr.info.pi_diamid, p->p_hdr.info.runtime.pir_realm), { CHECK_FCT_DO( pthread_rwlock_unlock(&fd_g_activ_peers_rw), ); return ret; } );
+		}
+		CHECK_FCT( pthread_rwlock_unlock(&fd_g_activ_peers_rw) );
+
+		/* Now let's remove all peers from the Route-Records */
+		CHECK_FCT(  fd_msg_browse(*pmsg, MSG_BRW_FIRST_CHILD, &avp, NULL)  );
+		while (avp) {
+			struct avp_hdr * ahdr;
+			struct fd_pei error_info;
+			CHECK_FCT(  fd_msg_avp_hdr( avp, &ahdr )  );
+
+			if ((ahdr->avp_code == AC_ROUTE_RECORD) && (! (ahdr->avp_flags & AVP_FLAG_VENDOR)) ) {
+				/* Parse this AVP */
+				CHECK_FCT_DO( ret = fd_msg_parse_dict ( avp, fd_g_config->cnf_dict, &error_info ),
+					{
+						if (error_info.pei_errcode) {
+							CHECK_FCT( return_error( pmsg, error_info.pei_errcode, error_info.pei_message, error_info.pei_avp) );
+							return 0;
+						} else {
+							return ret;
+						}
+					} );
+				ASSERT( ahdr->avp_value );
+				/* Remove this value from the list */
+				fd_rtd_candidate_del(rtd, (char *)ahdr->avp_value->os.data, ahdr->avp_value->os.len);
+			}
+
+			/* Go to next AVP */
+			CHECK_FCT(  fd_msg_browse(avp, MSG_BRW_NEXT, &avp, NULL)  );
+		}
+	}
+
+	/* Note: we reset the scores and pass the message to the callbacks, maybe we could re-use the saved scores when we have received an error ? */
+
+	/* Ok, we have our list in rtd now, let's (re)initialize the scores */
+	fd_rtd_candidate_extract(rtd, &candidates, FD_SCORE_INI);
+
+	/* Pass the list to registered callbacks (even if it is empty) */
+	{
+		CHECK_FCT( pthread_rwlock_rdlock( &rt_out_lock ) );
+		pthread_cleanup_push( fd_cleanup_rwlock, &rt_out_lock );
+
+		/* We call the cb by reverse priority order */
+		for (	li = rt_out_list.prev ; li != &rt_out_list ; li = li->prev ) {
+			struct rt_hdl * rh = (struct rt_hdl *)li;
+
+			TRACE_DEBUG(ANNOYING, "Calling next OUT callback on %p : %p (prio %d)", *pmsg, rh->rt_out_cb, rh->prio);
+			CHECK_FCT_DO( ret = (*rh->rt_out_cb)(rh->cbdata, *pmsg, candidates),
+				{
+					TRACE_DEBUG(INFO, "An OUT routing callback returned an error (%s) ! Message discarded.", strerror(ret));
+					fd_msg_dump_walk(INFO, *pmsg);
+					fd_msg_free(*pmsg);
+					*pmsg = NULL;
+					break;
+				} );
+		}
+
+		pthread_cleanup_pop(0);
+		CHECK_FCT( pthread_rwlock_unlock( &rt_out_lock ) );
+
+		/* If an error occurred, skip to the next message */
+		if (! *pmsg) {
+			if (rtd)
+				fd_rtd_free(&rtd);
+			return 0;
+		}
+	}
+	
+	/* Order the candidate peers by score attributed by the callbacks */
+	CHECK_FCT( fd_rtd_candidate_reorder(candidates) );
+
+	/* Save the routing information in the message */
+	CHECK_FCT( fd_msg_rt_associate ( *pmsg, &rtd ) );
+
+	/* Now try sending the message */
+	for (li = candidates->prev; li != candidates; li = li->prev) {
+		struct fd_peer * peer;
+
+		c = (struct rtd_candidate *) li;
+
+		/* Stop when we have reached the end of valid candidates */
+		if (c->score < 0)
+			break;
+
+		/* Search for the peer */
+		CHECK_FCT( fd_peer_getbyid( c->diamid, (void *)&peer ) );
+
+		fd_cpu_flush_cache();
+		if (peer && (peer->p_hdr.info.runtime.pir_state == STATE_OPEN)) {
+			/* Send to this one */
+			CHECK_FCT_DO( fd_out_send(pmsg, NULL, peer, 0), continue );
+			
+			/* If the sending was successful */
+			break;
+		}
+	}
+
+	/* If the message has not been sent, return an error */
+	if (*pmsg) {
+		TRACE_DEBUG(INFO, "Could not send the following message, replying with UNABLE_TO_DELIVER");
+		fd_msg_dump_walk(INFO, *pmsg);
+		return_error( pmsg, "DIAMETER_UNABLE_TO_DELIVER", "No suitable candidate to route the message to", NULL);
+	}
+
+	/* We're done with this message */
+	
+	return 0;
+}
+
+
+/********************************************************************************/
+/*                     Management of the threads                                */
+/********************************************************************************/
+
+/* Note: in the first version, we only create one thread of each kind.
+ We could improve the scalability by using the threshold feature of the queues
+ to create additional threads if a queue is filling up, or at least giving a configurable
+ number of threads of each kind.
+ */
+
+/* Control of the threads */
+static enum { RUN = 0, STOP = 1 } order_val = RUN;
+static pthread_mutex_t order_lock = PTHREAD_MUTEX_INITIALIZER;
+
+/* Threads report their status */
+enum thread_state { INITIAL = 0, RUNNING = 1, TERMINATED = 2 };
+static void cleanup_state(void * state_loc)
+{
+	if (state_loc)
+		*(enum thread_state *)state_loc = TERMINATED;
+}
+
+/* This is the common thread code (same for routing and dispatching) */
+static void * process_thr(void * arg, int (*action_cb)(struct msg ** pmsg), struct fifo * queue, char * action_name)
+{
+	TRACE_ENTRY("%p %p %p %p", arg, action_cb, queue, action_name);
+	
+	/* Set the thread name */
+	{
+		char buf[48];
+		snprintf(buf, sizeof(buf), "%s (%p)", action_name, arg);
+		fd_log_threadname ( buf );
+	}
+	
+	/* The thread reports its status when canceled */
+	CHECK_PARAMS_DO(arg, return NULL);
+	pthread_cleanup_push( cleanup_state, arg );
+	
+	/* Mark the thread running */
+	*(enum thread_state *)arg = RUNNING;
+	fd_cpu_flush_cache();
+	
+	do {
+		struct msg * msg;
+	
+		/* Test the current order */
+		{
+			int must_stop;
+			CHECK_POSIX_DO( pthread_mutex_lock(&order_lock), goto end ); /* we lock to flush the caches */
+			must_stop = (order_val == STOP);
+			CHECK_POSIX_DO( pthread_mutex_unlock(&order_lock), goto end );
+			if (must_stop)
+				goto end;
+			
+			pthread_testcancel();
+		}
+		
+		/* Ok, we are allowed to run */
+		
+		/* Get the next message from the queue */
+		{
+			int ret;
+			ret = fd_fifo_get ( queue, &msg );
+			if (ret == EPIPE)
+				/* The queue was destroyed, we are probably exiting */
+				goto end;
+			
+			/* check if another error occurred */
+			CHECK_FCT_DO( ret, goto fatal_error );
+		}
+		
+		if (TRACE_BOOL(FULL)) {
+			TRACE_DEBUG(FULL, "Picked next message");
+			fd_msg_dump_one(ANNOYING, msg);
+		}
+		
+		/* Now process the message */
+		CHECK_FCT_DO( (*action_cb)(&msg), goto fatal_error);
+
+		/* We're done with this message */
+	
+	} while (1);
+	
+fatal_error:
+	TRACE_DEBUG(INFO, "An unrecoverable error occurred, %s thread is terminating...", action_name);
+	CHECK_FCT_DO(fd_event_send(fd_g_config->cnf_main_ev, FDEV_TERMINATE, 0, NULL), );
+	
+end:	
+	; /* noop so that we get rid of "label at end of compund statement" warning */
+	/* Mark the thread as terminated */
+	pthread_cleanup_pop(1);
+	return NULL;
+}
+
+/* The dispatch thread */
+static void * dispatch_thr(void * arg)
+{
+	return process_thr(arg, msg_dispatch, fd_g_local, "Dispatch");
+}
+
+/* The (routing-in) thread -- see description in freeDiameter.h */
+static void * routing_in_thr(void * arg)
+{
+	return process_thr(arg, msg_rt_in, fd_g_incoming, "Routing-IN");
+}
+
+/* The (routing-out) thread -- see description in freeDiameter.h */
+static void * routing_out_thr(void * arg)
+{
+	return process_thr(arg, msg_rt_out, fd_g_outgoing, "Routing-OUT");
+}
+
+
+/********************************************************************************/
+/*                     The functions for the other files                        */
+/********************************************************************************/
+
+static pthread_t * dispatch = NULL;
+static enum thread_state * disp_state = NULL;
+
+/* Later: make this more dynamic */
+static pthread_t rt_out = (pthread_t)NULL;
+static enum thread_state out_state = INITIAL;
+
+static pthread_t rt_in  = (pthread_t)NULL;
+static enum thread_state in_state = INITIAL;
+
+/* Initialize the routing and dispatch threads */
+int fd_rtdisp_init(void)
+{
+	int i;
+	
+	/* Prepare the array for dispatch */
+	CHECK_MALLOC( dispatch = calloc(fd_g_config->cnf_dispthr, sizeof(pthread_t)) );
+	CHECK_MALLOC( disp_state = calloc(fd_g_config->cnf_dispthr, sizeof(enum thread_state)) );
+	
+	/* Create the threads */
+	for (i=0; i < fd_g_config->cnf_dispthr; i++) {
+		CHECK_POSIX( pthread_create( &dispatch[i], NULL, dispatch_thr, &disp_state[i] ) );
+	}
+	CHECK_POSIX( pthread_create( &rt_out, NULL, routing_out_thr, &out_state) );
+	CHECK_POSIX( pthread_create( &rt_in,  NULL, routing_in_thr,  &in_state) );
+	
+	/* Later: TODO("Set the thresholds for the queues to create more threads as needed"); */
+	
+	/* Register the built-in callbacks */
+	CHECK_FCT( fd_rt_out_register( dont_send_if_no_common_app, NULL, 10, NULL ) );
+	CHECK_FCT( fd_rt_out_register( score_destination_avp, NULL, 10, NULL ) );
+	return 0;
+}
+
+/* Ask the thread to terminate after next iteration */
+int fd_rtdisp_cleanstop(void)
+{
+	CHECK_POSIX( pthread_mutex_lock(&order_lock) );
+	order_val = STOP;
+	CHECK_POSIX( pthread_mutex_unlock(&order_lock) );
+
+	return 0;
+}
+
+static void stop_thread_delayed(enum thread_state *st, pthread_t * thr, char * th_name)
+{
+	TRACE_ENTRY("%p %p", st, thr);
+	CHECK_PARAMS_DO(st && thr, return);
+
+	/* Wait for a second for the thread to complete, by monitoring my_state */
+	fd_cpu_flush_cache();
+	if (*st != TERMINATED) {
+		TRACE_DEBUG(INFO, "Waiting for the %s thread to have a chance to terminate", th_name);
+		do {
+			struct timespec	 ts, ts_final;
+
+			CHECK_SYS_DO( clock_gettime(CLOCK_REALTIME, &ts), break );
+			
+			ts_final.tv_sec = ts.tv_sec + 1;
+			ts_final.tv_nsec = ts.tv_nsec;
+			
+			while (TS_IS_INFERIOR( &ts, &ts_final )) {
+				if (*st == TERMINATED)
+					break;
+				
+				usleep(100000);
+				CHECK_SYS_DO( clock_gettime(CLOCK_REALTIME, &ts), break );
+			}
+		} while (0);
+	}
+
+	/* Now stop the thread and reclaim its resources */
+	CHECK_FCT_DO( fd_thr_term(thr ), /* continue */);
+	
+}
+
+/* Stop the thread after up to one second of wait */
+int fd_rtdisp_fini(void)
+{
+	int i;
+	
+	/* Destroy the incoming queue */
+	CHECK_FCT_DO( fd_queues_fini(&fd_g_incoming), /* ignore */);
+	
+	/* Stop the routing IN thread */
+	stop_thread_delayed(&in_state, &rt_in, "IN routing");
+	
+	/* Destroy the outgoing queue */
+	CHECK_FCT_DO( fd_queues_fini(&fd_g_outgoing), /* ignore */);
+	
+	/* Stop the routing OUT thread */
+	stop_thread_delayed(&out_state, &rt_out, "OUT routing");
+	
+	/* Destroy the local queue */
+	CHECK_FCT_DO( fd_queues_fini(&fd_g_local), /* ignore */);
+	
+	/* Stop the Dispatch thread */
+	for (i=0; i < fd_g_config->cnf_dispthr; i++) {
+		stop_thread_delayed(&disp_state[i], &dispatch[i], "Dispatching");
+	}
+	
+	return 0;
+}
+
+/* Cleanup handlers */
+int fd_rtdisp_cleanup(void)
+{
+	/* Cleanup all remaining handlers */
+	while (!FD_IS_LIST_EMPTY(&rt_fwd_list)) {
+		CHECK_FCT_DO( fd_rt_fwd_unregister ( (void *)rt_fwd_list.next, NULL ), /* continue */ );
+	}
+	while (!FD_IS_LIST_EMPTY(&rt_out_list)) {
+		CHECK_FCT_DO( fd_rt_out_unregister ( (void *)rt_out_list.next, NULL ), /* continue */ );
+	}
+	
+	fd_disp_unregister_all(); /* destroy remaining handlers */
+
+	return 0;
+}
+
+
+/********************************************************************************/
+/*                     For extensiosn to register a new appl                    */
+/********************************************************************************/
+
+/* Add an application into the peer's supported apps */
+int fd_disp_app_support ( struct dict_object * app, struct dict_object * vendor, int auth, int acct )
+{
+	application_id_t aid = 0;
+	vendor_id_t	 vid = 0;
+	
+	TRACE_ENTRY("%p %p %d %d", app, vendor, auth, acct);
+	CHECK_PARAMS( app && (auth || acct) );
+	
+	{
+		enum dict_object_type type = 0;
+		struct dict_application_data data;
+		CHECK_FCT( fd_dict_gettype(app, &type) );
+		CHECK_PARAMS( type == DICT_APPLICATION );
+		CHECK_FCT( fd_dict_getval(app, &data) );
+		aid = data.application_id;
+	}
+
+	if (vendor) {
+		enum dict_object_type type = 0;
+		struct dict_vendor_data data;
+		CHECK_FCT( fd_dict_gettype(vendor, &type) );
+		CHECK_PARAMS( type == DICT_VENDOR );
+		CHECK_FCT( fd_dict_getval(vendor, &data) );
+		vid = data.vendor_id;
+	}
+	
+	return fd_app_merge(&fd_g_config->cnf_apps, aid, vid, auth, acct);
+}
+
+
+
Index: libfdcore/sctp.c
===================================================================
--- libfdcore/sctp.c	(revision 658)
+++ libfdcore/sctp.c	(revision 658)
@@ -0,0 +1,1232 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@nict.go.jp>							 *
+*													 *
+* Copyright (c) 2010, 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 "fdcore-internal.h"
+#include "cnxctx.h"
+
+#include <netinet/sctp.h>
+#include <sys/uio.h>
+
+/* Size of buffer to receive ancilliary data. May need to be enlarged if more sockopt are set... */
+#ifndef CMSG_BUF_LEN
+#define CMSG_BUF_LEN	1024
+#endif /* CMSG_BUF_LEN */
+
+/* Level of SCTP-specific traces */
+#ifdef DEBUG_SCTP
+#define SCTP_LEVEL	FULL
+#else /* DEBUG_SCTP */
+#define SCTP_LEVEL	(FCTS + 1)
+#endif /* DEBUG_SCTP */
+
+/* Temper with the retransmission timers to try and improve disconnection detection response? Undef this to keep the defaults of SCTP stack */
+#ifndef USE_DEFAULT_SCTP_RTX_PARAMS	/* make this a configuration option if useful */
+#define ADJUST_RTX_PARAMS
+#endif /* USE_DEFAULT_SCTP_RTX_PARAMS */
+
+/* Pre-binding socket options -- # streams read in config */
+/* The code of this file is based on draft-ietf-tsvwg-sctpsocket, versions 17 to 21 */
+static int fd_setsockopt_prebind(int sk)
+{
+	socklen_t sz;
+	
+	TRACE_ENTRY( "%d", sk);
+	
+	CHECK_PARAMS( sk > 0 );
+	
+#ifdef DEBUG
+	{
+		int reuse = 1;
+		CHECK_SYS(  setsockopt(sk, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse))  );
+	}
+#endif /* DEBUG	*/
+	
+#ifdef ADJUST_RTX_PARAMS
+	/* Set the retransmit parameters */
+	#ifdef SCTP_RTOINFO
+	{
+		struct sctp_rtoinfo rtoinfo;
+		memset(&rtoinfo, 0, sizeof(rtoinfo));
+
+		if (TRACE_BOOL(SCTP_LEVEL)) {
+			sz = sizeof(rtoinfo);
+			/* Read socket defaults */
+			CHECK_SYS(  getsockopt(sk, IPPROTO_SCTP, SCTP_RTOINFO, &rtoinfo, &sz)  );
+			if (sz != sizeof(rtoinfo))
+			{
+				TRACE_DEBUG(INFO, "Invalid size of socket option: %d / %d", sz, (socklen_t)sizeof(rtoinfo));
+				return ENOTSUP;
+			}
+			fd_log_debug( "Def SCTP_RTOINFO : srto_initial : %u\n", rtoinfo.srto_initial);
+			fd_log_debug( "                   srto_min     : %u\n", rtoinfo.srto_min);
+			fd_log_debug( "                   srto_max     : %u\n", rtoinfo.srto_max);
+		}
+
+		/* rtoinfo.srto_initial: Estimate of the RTT before it can be measured; keep the default value */
+		rtoinfo.srto_max = 5000; /* Maximum retransmit timer (in ms), we want fast retransmission time. */
+		rtoinfo.srto_min = 1000; /* Value under which the RTO does not descend, we set this value to not conflict with srto_max */
+
+		/* Set the option to the socket */
+		CHECK_SYS(  setsockopt(sk, IPPROTO_SCTP, SCTP_RTOINFO, &rtoinfo, sizeof(rtoinfo))  );
+		
+		if (TRACE_BOOL(SCTP_LEVEL)) {
+			/* Check new values */
+			CHECK_SYS(  getsockopt(sk, IPPROTO_SCTP, SCTP_RTOINFO, &rtoinfo, &sz)  );
+			fd_log_debug( "New SCTP_RTOINFO : srto_initial : %u\n", rtoinfo.srto_initial);
+			fd_log_debug( "                   srto_max     : %u\n", rtoinfo.srto_max);
+			fd_log_debug( "                   srto_min     : %u\n", rtoinfo.srto_min);
+		}
+	}
+	#else /* SCTP_RTOINFO */
+	TRACE_DEBUG(SCTP_LEVEL, "Skipping SCTP_RTOINFO");
+	#endif /* SCTP_RTOINFO */
+	
+	/* Set the association parameters: max number of retransmits, ... */
+	#ifdef SCTP_ASSOCINFO
+	{
+		struct sctp_assocparams assoc;
+		memset(&assoc, 0, sizeof(assoc));
+
+		if (TRACE_BOOL(SCTP_LEVEL)) {
+			sz = sizeof(assoc);
+			/* Read socket defaults */
+			CHECK_SYS(  getsockopt(sk, IPPROTO_SCTP, SCTP_ASSOCINFO, &assoc, &sz)  );
+			if (sz != sizeof(assoc))
+			{
+				TRACE_DEBUG(INFO, "Invalid size of socket option: %d / %d", sz, (socklen_t)sizeof(assoc));
+				return ENOTSUP;
+			}
+			fd_log_debug( "Def SCTP_ASSOCINFO : sasoc_asocmaxrxt               : %hu\n", assoc.sasoc_asocmaxrxt);
+			fd_log_debug( "                     sasoc_number_peer_destinations : %hu\n", assoc.sasoc_number_peer_destinations);
+			fd_log_debug( "                     sasoc_peer_rwnd                : %u\n" , assoc.sasoc_peer_rwnd);
+			fd_log_debug( "                     sasoc_local_rwnd               : %u\n" , assoc.sasoc_local_rwnd);
+			fd_log_debug( "                     sasoc_cookie_life              : %u\n" , assoc.sasoc_cookie_life);
+		}
+
+		assoc.sasoc_asocmaxrxt = 4;	/* Maximum number of retransmission attempts: we want fast detection of errors */
+						/* Note that this must remain less than the sum of retransmission parameters of the different paths. */
+		
+		/* Set the option to the socket */
+		CHECK_SYS(  setsockopt(sk, IPPROTO_SCTP, SCTP_ASSOCINFO, &assoc, sizeof(assoc))  );
+		
+		if (TRACE_BOOL(SCTP_LEVEL)) {
+			/* Check new values */
+			CHECK_SYS(  getsockopt(sk, IPPROTO_SCTP, SCTP_ASSOCINFO, &assoc, &sz)  );
+			fd_log_debug( "New SCTP_ASSOCINFO : sasoc_asocmaxrxt               : %hu\n", assoc.sasoc_asocmaxrxt);
+			fd_log_debug( "                     sasoc_number_peer_destinations : %hu\n", assoc.sasoc_number_peer_destinations);
+			fd_log_debug( "                     sasoc_peer_rwnd                : %u\n" , assoc.sasoc_peer_rwnd);
+			fd_log_debug( "                     sasoc_local_rwnd               : %u\n" , assoc.sasoc_local_rwnd);
+			fd_log_debug( "                     sasoc_cookie_life              : %u\n" , assoc.sasoc_cookie_life);
+		}
+	}
+	#else /* SCTP_ASSOCINFO */
+	TRACE_DEBUG(SCTP_LEVEL, "Skipping SCTP_ASSOCINFO");
+	#endif /* SCTP_ASSOCINFO */
+#endif /* ADJUST_RTX_PARAMS */
+	
+	/* Set the INIT parameters, such as number of streams */
+	#ifdef SCTP_INITMSG
+	{
+		struct sctp_initmsg init;
+		memset(&init, 0, sizeof(init));
+		
+		if (TRACE_BOOL(SCTP_LEVEL)) {
+			sz = sizeof(init);
+
+			/* Read socket defaults */
+			CHECK_SYS(  getsockopt(sk, IPPROTO_SCTP, SCTP_INITMSG, &init, &sz)  );
+			if (sz != sizeof(init))
+			{
+				TRACE_DEBUG(INFO, "Invalid size of socket option: %d / %d", sz, (socklen_t)sizeof(init));
+				return ENOTSUP;
+			}
+			fd_log_debug( "Def SCTP_INITMSG : sinit_num_ostreams   : %hu\n", init.sinit_num_ostreams);
+			fd_log_debug( "                   sinit_max_instreams  : %hu\n", init.sinit_max_instreams);
+			fd_log_debug( "                   sinit_max_attempts   : %hu\n", init.sinit_max_attempts);
+			fd_log_debug( "                   sinit_max_init_timeo : %hu\n", init.sinit_max_init_timeo);
+		}
+
+		/* Set the init options -- need to receive SCTP_COMM_UP to confirm the requested parameters, but we don't care (best effort) */
+		init.sinit_num_ostreams	  = fd_g_config->cnf_sctp_str;	/* desired number of outgoing streams */
+		init.sinit_max_init_timeo = CNX_TIMEOUT * 1000;
+
+		/* Set the option to the socket */
+		CHECK_SYS(  setsockopt(sk, IPPROTO_SCTP, SCTP_INITMSG, &init, sizeof(init))  );
+		
+		if (TRACE_BOOL(SCTP_LEVEL)) {
+			/* Check new values */
+			CHECK_SYS(  getsockopt(sk, IPPROTO_SCTP, SCTP_INITMSG, &init, &sz)  );
+			fd_log_debug( "New SCTP_INITMSG : sinit_num_ostreams   : %hu\n", init.sinit_num_ostreams);
+			fd_log_debug( "                   sinit_max_instreams  : %hu\n", init.sinit_max_instreams);
+			fd_log_debug( "                   sinit_max_attempts   : %hu\n", init.sinit_max_attempts);
+			fd_log_debug( "                   sinit_max_init_timeo : %hu\n", init.sinit_max_init_timeo);
+		}
+	}
+	#else /* SCTP_INITMSG */
+	TRACE_DEBUG(SCTP_LEVEL, "Skipping SCTP_INITMSG");
+	#endif /* SCTP_INITMSG */
+	
+	/* The SO_LINGER option will be reset if we want to perform SCTP ABORT */
+	#ifdef SO_LINGER
+	{
+		struct linger linger;
+		memset(&linger, 0, sizeof(linger));
+		
+		if (TRACE_BOOL(SCTP_LEVEL)) {
+			sz = sizeof(linger);
+			/* Read socket defaults */
+			CHECK_SYS(  getsockopt(sk, SOL_SOCKET, SO_LINGER, &linger, &sz)  );
+			if (sz != sizeof(linger))
+			{
+				TRACE_DEBUG(INFO, "Invalid size of socket option: %d / %d", sz, (socklen_t)sizeof(linger));
+				return ENOTSUP;
+			}
+			fd_log_debug( "Def SO_LINGER : l_onoff  : %d\n", linger.l_onoff);
+			fd_log_debug( " 	       l_linger : %d\n", linger.l_linger);
+		}
+		
+		linger.l_onoff	= 0;	/* Do not activate the linger */
+		linger.l_linger = 0;	/* Ignored, but it would mean : Return immediately when closing (=> abort) (graceful shutdown in background) */
+		
+		/* Set the option */
+		CHECK_SYS(  setsockopt(sk, SOL_SOCKET, SO_LINGER, &linger, sizeof(linger))  );
+		
+		if (TRACE_BOOL(SCTP_LEVEL)) {
+			/* Check new values */
+			CHECK_SYS(  getsockopt(sk, SOL_SOCKET, SO_LINGER, &linger, &sz)  );
+			fd_log_debug( "New SO_LINGER : l_onoff  : %d\n", linger.l_onoff);
+			fd_log_debug( "		  l_linger : %d\n", linger.l_linger);
+		}
+	}
+	#else /* SO_LINGER */
+	TRACE_DEBUG(SCTP_LEVEL, "Skipping SO_LINGER");
+	#endif /* SO_LINGER */
+	
+	/* Set the NODELAY option (Nagle-like algorithm) */
+	#ifdef SCTP_NODELAY
+	{
+		int nodelay;
+		
+		if (TRACE_BOOL(SCTP_LEVEL)) {
+			sz = sizeof(nodelay);
+			/* Read socket defaults */
+			CHECK_SYS(  getsockopt(sk, IPPROTO_SCTP, SCTP_NODELAY, &nodelay, &sz)  );
+			if (sz != sizeof(nodelay))
+			{
+				TRACE_DEBUG(INFO, "Invalid size of socket option: %d / %d", sz, (socklen_t)sizeof(nodelay));
+				return ENOTSUP;
+			}
+			fd_log_debug( "Def SCTP_NODELAY value : %s\n", nodelay ? "true" : "false");
+		}
+
+		nodelay = 1;	/* We turn ON the Nagle algorithm (probably the default already), since we might have several messages to send through the same proxy (not the same session). */
+		
+		/* Set the option to the socket */
+		CHECK_SYS(  setsockopt(sk, IPPROTO_SCTP, SCTP_NODELAY, &nodelay, sizeof(nodelay))  );
+		
+		if (TRACE_BOOL(SCTP_LEVEL)) {
+			/* Check new values */
+			CHECK_SYS(  getsockopt(sk, IPPROTO_SCTP, SCTP_NODELAY, &nodelay, &sz)  );
+			fd_log_debug( "New SCTP_NODELAY value : %s\n", nodelay ? "true" : "false");
+		}
+	}
+	#else /* SCTP_NODELAY */
+	TRACE_DEBUG(SCTP_LEVEL, "Skipping SCTP_NODELAY");
+	#endif /* SCTP_NODELAY */
+	
+	/*
+	   SO_RCVBUF			size of receiver window
+	   SO_SNDBUF			size of pending data to send
+	   SCTP_AUTOCLOSE		for one-to-many only
+	   SCTP_PRIMARY_ADDR		use this address as primary locally
+	   SCTP_ADAPTATION_LAYER	set adaptation layer indication, we don't use this 
+	*/
+	
+	/* Set the SCTP_DISABLE_FRAGMENTS option, required for TLS */
+	#ifdef SCTP_DISABLE_FRAGMENTS
+	{
+		int nofrag;
+		
+		if (TRACE_BOOL(SCTP_LEVEL)) {
+			sz = sizeof(nofrag);
+			/* Read socket defaults */
+			CHECK_SYS(  getsockopt(sk, IPPROTO_SCTP, SCTP_DISABLE_FRAGMENTS, &nofrag, &sz)  );
+			if (sz != sizeof(nofrag))
+			{
+				TRACE_DEBUG(INFO, "Invalid size of socket option: %d / %d", sz, (socklen_t)sizeof(nofrag));
+				return ENOTSUP;
+			}
+			fd_log_debug( "Def SCTP_DISABLE_FRAGMENTS value : %s\n", nofrag ? "true" : "false");
+		}
+
+		nofrag = 0;	/* We turn ON the fragmentation, since Diameter messages & TLS messages can be quite large. */
+		
+		/* Set the option to the socket */
+		CHECK_SYS(  setsockopt(sk, IPPROTO_SCTP, SCTP_DISABLE_FRAGMENTS, &nofrag, sizeof(nofrag))  );
+		
+		if (TRACE_BOOL(SCTP_LEVEL)) {
+			/* Check new values */
+			CHECK_SYS(  getsockopt(sk, IPPROTO_SCTP, SCTP_DISABLE_FRAGMENTS, &nofrag, &sz)  );
+			fd_log_debug( "New SCTP_DISABLE_FRAGMENTS value : %s\n", nofrag ? "true" : "false");
+		}
+	}
+	#else /* SCTP_DISABLE_FRAGMENTS */
+	# error "TLS requires support of SCTP_DISABLE_FRAGMENTS"
+	#endif /* SCTP_DISABLE_FRAGMENTS */
+	
+	/* SCTP_PEER_ADDR_PARAMS	control heartbeat per peer address. We set it as a default for all addresses in the association; not sure if it works ... */
+	#ifdef SCTP_PEER_ADDR_PARAMS
+	{
+		struct sctp_paddrparams parms;
+		memset(&parms, 0, sizeof(parms));
+		
+		/* Some kernel versions need this to be set */
+		parms.spp_address.ss_family = AF_INET;
+		
+		if (TRACE_BOOL(SCTP_LEVEL)) {
+			sz = sizeof(parms);
+
+			/* Read socket defaults */
+			CHECK_SYS(  getsockopt(sk, IPPROTO_SCTP, SCTP_PEER_ADDR_PARAMS, &parms, &sz)  );
+			if (sz != sizeof(parms))
+			{
+				TRACE_DEBUG(INFO, "Invalid size of socket option: %d / %d", sz, (socklen_t)sizeof(parms));
+				return ENOTSUP;
+			}
+			fd_log_debug( "Def SCTP_PEER_ADDR_PARAMS : spp_hbinterval    : %u\n",  parms.spp_hbinterval);
+			fd_log_debug( "                            spp_pathmaxrxt    : %hu\n", parms.spp_pathmaxrxt);
+			fd_log_debug( "                            spp_pathmtu       : %u\n",  parms.spp_pathmtu);
+			fd_log_debug( "                            spp_flags         : %x\n",  parms.spp_flags);
+			// fd_log_debug( "                            spp_ipv6_flowlabel: %u\n",  parms.spp_ipv6_flowlabel);
+			// fd_log_debug( "                            spp_ipv4_tos      : %hhu\n",parms.spp_ipv4_tos);
+		}
+
+		parms.spp_flags = SPP_HB_ENABLE;	/* Enable heartbeat for the association */
+		#ifdef SPP_PMTUD_ENABLE
+		parms.spp_flags |= SPP_PMTUD_ENABLE;	/* also enable path MTU discovery mechanism */
+		#endif /* SPP_PMTUD_ENABLE */
+		
+#ifdef ADJUST_RTX_PARAMS
+		parms.spp_hbinterval = 6000;		/* Send an heartbeat every 6 seconds to quickly start retransmissions */
+		/* parms.spp_pathmaxrxt : max nbr of restransmissions on this address. There is a relationship with sasoc_asocmaxrxt, so we leave the default here */
+#endif /* ADJUST_RTX_PARAMS */
+
+		/* Set the option to the socket */
+		CHECK_SYS(  setsockopt(sk, IPPROTO_SCTP, SCTP_PEER_ADDR_PARAMS, &parms, sizeof(parms)) );
+		
+		if (TRACE_BOOL(SCTP_LEVEL)) {
+			/* Check new values */
+			CHECK_SYS(  getsockopt(sk, IPPROTO_SCTP, SCTP_PEER_ADDR_PARAMS, &parms, &sz)  );
+			fd_log_debug( "New SCTP_PEER_ADDR_PARAMS : spp_hbinterval    : %u\n",  parms.spp_hbinterval);
+			fd_log_debug( "                            spp_pathmaxrxt    : %hu\n", parms.spp_pathmaxrxt);
+			fd_log_debug( "                            spp_pathmtu       : %u\n",  parms.spp_pathmtu);
+			fd_log_debug( "                            spp_flags         : %x\n",  parms.spp_flags);
+			// fd_log_debug( "                            spp_ipv6_flowlabel: %u\n",  parms.spp_ipv6_flowlabel);
+			// fd_log_debug( "                            spp_ipv4_tos      : %hhu\n",parms.spp_ipv4_tos);
+		}
+	}
+	#else /* SCTP_PEER_ADDR_PARAMS */
+	TRACE_DEBUG(SCTP_LEVEL, "Skipping SCTP_PEER_ADDR_PARAMS");
+	#endif /* SCTP_PEER_ADDR_PARAMS */
+	
+	/*
+	   SCTP_DEFAULT_SEND_PARAM	parameters for the sendto() call, we don't use it.
+	*/
+
+	/* Subscribe to some notifications */
+	#ifdef SCTP_EVENTS
+	{
+		struct sctp_event_subscribe event;
+
+		memset(&event, 0, sizeof(event));
+		event.sctp_data_io_event	= 1;	/* to receive the stream ID in SCTP_SNDRCV ancilliary data on message reception */
+		event.sctp_association_event	= 0;	/* new or closed associations (mostly for one-to-many style sockets) */
+		event.sctp_address_event	= 1;	/* address changes */
+		event.sctp_send_failure_event	= 1;	/* delivery failures */
+		event.sctp_peer_error_event	= 1;	/* remote peer sends an error */
+		event.sctp_shutdown_event	= 1;	/* peer has sent a SHUTDOWN */
+		event.sctp_partial_delivery_event = 1;	/* a partial delivery is aborted, probably indicating the connection is being shutdown */
+		// event.sctp_adaptation_layer_event = 0;	/* adaptation layer notifications */
+		// event.sctp_authentication_event = 0;	/* when new key is made active */
+
+		/* Set the option to the socket */
+		CHECK_SYS(  setsockopt(sk, IPPROTO_SCTP, SCTP_EVENTS, &event, sizeof(event)) );
+		
+		if (TRACE_BOOL(SCTP_LEVEL)) {
+			sz = sizeof(event);
+			CHECK_SYS(  getsockopt(sk, IPPROTO_SCTP, SCTP_EVENTS, &event, &sz) );
+			if (sz != sizeof(event))
+			{
+				TRACE_DEBUG(INFO, "Invalid size of socket option: %d / %d", sz, (socklen_t)sizeof(event));
+				return ENOTSUP;
+			}
+
+			fd_log_debug( "SCTP_EVENTS : sctp_data_io_event          : %hhu\n", event.sctp_data_io_event);
+			fd_log_debug( "       	     sctp_association_event      : %hhu\n", event.sctp_association_event);
+			fd_log_debug( "       	     sctp_address_event	         : %hhu\n", event.sctp_address_event);
+			fd_log_debug( "       	     sctp_send_failure_event     : %hhu\n", event.sctp_send_failure_event);
+			fd_log_debug( "       	     sctp_peer_error_event       : %hhu\n", event.sctp_peer_error_event);
+			fd_log_debug( "       	     sctp_shutdown_event	 : %hhu\n", event.sctp_shutdown_event);
+			fd_log_debug( "       	     sctp_partial_delivery_event : %hhu\n", event.sctp_partial_delivery_event);
+			fd_log_debug( "       	     sctp_adaptation_layer_event : %hhu\n", event.sctp_adaptation_layer_event);
+			// fd_log_debug( "             sctp_authentication_event    : %hhu\n", event.sctp_authentication_event);
+		}
+	}
+	#else /* SCTP_EVENTS */
+	TRACE_DEBUG(SCTP_LEVEL, "Skipping SCTP_EVENTS");
+	#endif /* SCTP_EVENTS */
+	
+	/* Set the v4 mapped addresses option */
+	#ifdef SCTP_I_WANT_MAPPED_V4_ADDR
+	{
+		int v4mapped;
+		
+		if (TRACE_BOOL(SCTP_LEVEL)) {
+			sz = sizeof(v4mapped);
+			/* Read socket defaults */
+			CHECK_SYS(  getsockopt(sk, IPPROTO_SCTP, SCTP_I_WANT_MAPPED_V4_ADDR, &v4mapped, &sz)  );
+			if (sz != sizeof(v4mapped))
+			{
+				TRACE_DEBUG(INFO, "Invalid size of socket option: %d / %d", sz, (socklen_t)sizeof(v4mapped));
+				return ENOTSUP;
+			}
+			fd_log_debug( "Def SCTP_I_WANT_MAPPED_V4_ADDR value : %s\n", v4mapped ? "true" : "false");
+		}
+
+		#ifndef SCTP_USE_MAPPED_ADDRESSES
+		v4mapped = 0;	/* We don't want v4 mapped addresses */
+		#else /* SCTP_USE_MAPPED_ADDRESSES */
+		v4mapped = 1;	/* but we may have to, otherwise the bind fails in some environments */
+		#endif /* SCTP_USE_MAPPED_ADDRESSES */
+		
+		/* Set the option to the socket */
+		CHECK_SYS(  setsockopt(sk, IPPROTO_SCTP, SCTP_I_WANT_MAPPED_V4_ADDR, &v4mapped, sizeof(v4mapped))  );
+		
+		if (TRACE_BOOL(SCTP_LEVEL)) {
+			/* Check new values */
+			CHECK_SYS(  getsockopt(sk, IPPROTO_SCTP, SCTP_I_WANT_MAPPED_V4_ADDR, &v4mapped, &sz)  );
+			fd_log_debug( "New SCTP_I_WANT_MAPPED_V4_ADDR value : %s\n", v4mapped ? "true" : "false");
+		}
+	}
+	#else /* SCTP_I_WANT_MAPPED_V4_ADDR */
+	TRACE_DEBUG(SCTP_LEVEL, "Skipping SCTP_I_WANT_MAPPED_V4_ADDR");
+	#endif /* SCTP_I_WANT_MAPPED_V4_ADDR */
+	
+	/*
+	   SCTP_MAXSEG			max size of fragmented segments -- bound to PMTU
+	   SCTP_HMAC_IDENT		authentication algorithms
+	   SCTP_AUTH_ACTIVE_KEY		set the active key
+	   SCTP_DELAYED_SACK		control delayed acks
+	*/
+	
+	
+	/* Set the interleaving option */
+	#ifdef SCTP_FRAGMENT_INTERLEAVE
+	{
+		int interleave;
+		
+		if (TRACE_BOOL(SCTP_LEVEL)) {
+			sz = sizeof(interleave);
+			/* Read socket defaults */
+			CHECK_SYS(  getsockopt(sk, IPPROTO_SCTP, SCTP_FRAGMENT_INTERLEAVE, &interleave, &sz)  );
+			if (sz != sizeof(interleave))
+			{
+				TRACE_DEBUG(INFO, "Invalid size of socket option: %d / %d", sz, (socklen_t)sizeof(interleave));
+				return ENOTSUP;
+			}
+			fd_log_debug( "Def SCTP_FRAGMENT_INTERLEAVE value : %d\n", interleave);
+		}
+
+		#if 0
+		interleave = 2;	/* Allow partial delivery on several streams at the same time, since we are stream-aware in our security modules */
+		#else /* 0 */
+		interleave = 1;	/* hmmm actually, we are not yet capable of handling this, and we don t need it. */
+		#endif /* 0 */
+		
+		/* Set the option to the socket */
+		CHECK_SYS(  setsockopt(sk, IPPROTO_SCTP, SCTP_FRAGMENT_INTERLEAVE, &interleave, sizeof(interleave))  );
+		
+		if (TRACE_BOOL(SCTP_LEVEL)) {
+			/* Check new values */
+			CHECK_SYS(  getsockopt(sk, IPPROTO_SCTP, SCTP_FRAGMENT_INTERLEAVE, &interleave, &sz)  );
+			fd_log_debug( "New SCTP_FRAGMENT_INTERLEAVE value : %d\n", interleave);
+		}
+	}
+	#else /* SCTP_FRAGMENT_INTERLEAVE */
+	TRACE_DEBUG(SCTP_LEVEL, "Skipping SCTP_FRAGMENT_INTERLEAVE");
+	#endif /* SCTP_FRAGMENT_INTERLEAVE */
+	
+	/*
+	   SCTP_PARTIAL_DELIVERY_POINT	control partial delivery size
+	   SCTP_USE_EXT_RCVINFO		use extended receive info structure (information about the next message if available)
+	 */
+	/* SCTP_AUTO_ASCONF is set by the postbind function */
+	/*
+	   SCTP_MAX_BURST		number of packets that can be burst emitted
+	   SCTP_CONTEXT			save a context information along with the association.
+	 */
+	 
+	/* SCTP_EXPLICIT_EOR: we assume implicit EOR in freeDiameter, so let's ensure this is known by the stack */
+	#ifdef SCTP_EXPLICIT_EOR
+	{
+		int bool;
+		
+		if (TRACE_BOOL(SCTP_LEVEL)) {
+			sz = sizeof(bool);
+			/* Read socket defaults */
+			CHECK_SYS(  getsockopt(sk, IPPROTO_SCTP, SCTP_EXPLICIT_EOR, &bool, &sz)  );
+			if (sz != sizeof(bool))
+			{
+				TRACE_DEBUG(INFO, "Invalid size of socket option: %d / %d", sz, (socklen_t)sizeof(bool));
+				return ENOTSUP;
+			}
+			fd_log_debug( "Def SCTP_EXPLICIT_EOR value : %s\n", bool ? "true" : "false");
+		}
+
+		bool = 0;
+		
+		/* Set the option to the socket */
+		CHECK_SYS(  setsockopt(sk, IPPROTO_SCTP, SCTP_EXPLICIT_EOR, &bool, sizeof(bool))  );
+		
+		if (TRACE_BOOL(SCTP_LEVEL)) {
+			/* Check new values */
+			CHECK_SYS(  getsockopt(sk, IPPROTO_SCTP, SCTP_EXPLICIT_EOR, &bool, &sz)  );
+			fd_log_debug( "New SCTP_EXPLICIT_EOR value : %s\n", bool ? "true" : "false");
+		}
+	}
+	#else /* SCTP_EXPLICIT_EOR */
+	TRACE_DEBUG(SCTP_LEVEL, "Skipping SCTP_EXPLICIT_EOR");
+	#endif /* SCTP_EXPLICIT_EOR */
+	
+	/*
+	   SCTP_REUSE_PORT		share one listening port with several sockets
+	   SCTP_EVENT			same as EVENTS ?
+	*/
+	
+	/* In case of no_ip4, force the v6only option */
+	#ifdef IPV6_V6ONLY
+	if (fd_g_config->cnf_flags.no_ip4) {
+		int opt = 1;
+		CHECK_SYS(setsockopt(sk, IPPROTO_IPV6, IPV6_V6ONLY, &opt, sizeof(opt)));
+	}
+	#endif /* IPV6_V6ONLY */
+	
+	return 0;
+}
+
+
+/* Post-binding socket options */
+static int fd_setsockopt_postbind(int sk, int bound_to_default)
+{
+	TRACE_ENTRY( "%d %d", sk, bound_to_default);
+	
+	CHECK_PARAMS( (sk > 0) );
+	
+	/* Set the ASCONF option */
+	#ifdef SCTP_AUTO_ASCONF
+	if (bound_to_default) {
+		int asconf;
+		
+		if (TRACE_BOOL(SCTP_LEVEL)) {
+			socklen_t sz;
+
+			sz = sizeof(asconf);
+			/* Read socket defaults */
+			CHECK_SYS(  getsockopt(sk, IPPROTO_SCTP, SCTP_AUTO_ASCONF, &asconf, &sz)  );
+			if (sz != sizeof(asconf))
+			{
+				TRACE_DEBUG(INFO, "Invalid size of socket option: %d / %d", sz, (socklen_t)sizeof(asconf));
+				return ENOTSUP;
+			}
+			fd_log_debug( "Def SCTP_AUTO_ASCONF value : %s\n", asconf ? "true" : "false");
+		}
+
+		asconf = 1;	/* allow automatic use of added or removed addresses in the association (for bound-all sockets) */
+		
+		/* Set the option to the socket */
+		CHECK_SYS(  setsockopt(sk, IPPROTO_SCTP, SCTP_AUTO_ASCONF, &asconf, sizeof(asconf))  );
+		
+		if (TRACE_BOOL(SCTP_LEVEL)) {
+			socklen_t sz = sizeof(asconf);
+			/* Check new values */
+			CHECK_SYS(  getsockopt(sk, IPPROTO_SCTP, SCTP_AUTO_ASCONF, &asconf, &sz)  );
+			fd_log_debug( "New SCTP_AUTO_ASCONF value : %s\n", asconf ? "true" : "false");
+		}
+	}
+	#else /* SCTP_AUTO_ASCONF */
+	TRACE_DEBUG(SCTP_LEVEL, "Skipping SCTP_AUTO_ASCONF");
+	#endif /* SCTP_AUTO_ASCONF */
+	
+	return 0;
+}
+
+/* Add addresses from a list to an array, with filter on the flags */
+static int add_addresses_from_list_mask(uint8_t ** array, size_t * size, int * addr_count, int target_family, uint16_t port, struct fd_list * list, uint32_t mask, uint32_t val)
+{
+	struct fd_list * li;
+	int to_add4 = 0;
+	int to_add6 = 0;
+	union {
+		uint8_t *buf;
+		sSA4	*sin;
+		sSA6	*sin6;
+	} ptr;
+	size_t sz;
+	
+	/* First, count the number of addresses to add */
+	for (li = list->next; li != list; li = li->next) {
+		struct fd_endpoint * ep = (struct fd_endpoint *) li;
+		
+		/* Do the flag match ? */
+		if ((val & mask) != (ep->flags & mask))
+			continue;
+		
+		if (ep->sa.sa_family == AF_INET) {
+			to_add4 ++;
+		} else {
+			to_add6 ++;
+		}
+	}
+	
+	if ((to_add4 + to_add6) == 0)
+		return 0; /* nothing to do */
+	
+	/* The size to add */
+	if (target_family == AF_INET) {
+		sz = to_add4 * sizeof(sSA4);
+	} else {
+		#ifndef SCTP_USE_MAPPED_ADDRESSES
+			sz = (to_add4 * sizeof(sSA4)) + (to_add6 * sizeof(sSA6));
+		#else /* SCTP_USE_MAPPED_ADDRESSES */
+			sz = (to_add4 + to_add6) * sizeof(sSA6);
+		#endif /* SCTP_USE_MAPPED_ADDRESSES */
+	}
+	
+	/* Now, (re)alloc the array to store the new addresses */
+	CHECK_MALLOC( *array = realloc(*array, *size + sz) );
+	
+	/* Finally, add the addresses */
+	for (li = list->next; li != list; li = li->next) {
+		struct fd_endpoint * ep = (struct fd_endpoint *) li;
+		
+		/* Skip v6 addresses for v4 socket */
+		if ((target_family == AF_INET) && (ep->sa.sa_family == AF_INET6))
+			continue;
+		
+		/* Are the flags matching ? */
+		if ((val & mask) != (ep->flags & mask))
+			continue;
+		
+		/* Size of the new SA we are adding (array may contain a mix of sockaddr_in and sockaddr_in6) */
+		#ifndef SCTP_USE_MAPPED_ADDRESSES
+		if (ep->sa.sa_family == AF_INET6)
+		#else /* SCTP_USE_MAPPED_ADDRESSES */
+		if (target_family == AF_INET6) {
+		#endif /* SCTP_USE_MAPPED_ADDRESSES */
+			sz = sizeof(sSA6);
+		else
+			sz = sizeof(sSA4);
+		
+		/* Place where we add the new address */
+		ptr.buf = *array + *size; /* place of the new SA */
+		
+		/* Update other information */
+		*size += sz;
+		*addr_count += 1;
+		
+		/* And write the addr in the buffer */
+		if (sz == sizeof(sSA4)) {
+			memcpy(ptr.buf, &ep->sin, sz);
+			ptr.sin->sin_port = port;
+		} else {
+			if (ep->sa.sa_family == AF_INET) { /* We must map the address */ 
+				memset(ptr.buf, 0, sz);
+				ptr.sin6->sin6_family = AF_INET6;
+				IN6_ADDR_V4MAP( &ptr.sin6->sin6_addr.s6_addr, ep->sin.sin_addr.s_addr );
+			} else {
+				memcpy(ptr.sin6, &ep->sin6, sz);
+			}
+			ptr.sin6->sin6_port = port;
+		}
+	}
+	
+	return 0;
+}
+
+/* Create a socket server and bind it according to daemon s configuration */
+int fd_sctp_create_bind_server( int * sock, int family, struct fd_list * list, uint16_t port )
+{
+	int bind_default;
+	
+	TRACE_ENTRY("%p %i %p %hu", sock, family, list, port);
+	CHECK_PARAMS(sock);
+	
+	/* Create the socket */
+	CHECK_SYS( *sock = socket(family, SOCK_STREAM, IPPROTO_SCTP) );
+	
+	/* Set pre-binding socket options, including number of streams etc... */
+	CHECK_FCT( fd_setsockopt_prebind(*sock) );
+	
+	bind_default = (! list) || (FD_IS_LIST_EMPTY(list)) ;
+redo:
+	if ( bind_default ) {
+		/* Implicit endpoints : bind to default addresses */
+		union {
+			sSS  ss;
+			sSA  sa;
+			sSA4 sin;
+			sSA6 sin6;
+		} s;
+		
+		/* 0.0.0.0 and [::] are all zeros */
+		memset(&s, 0, sizeof(s));
+		
+		s.sa.sa_family = family;
+		
+		if (family == AF_INET)
+			s.sin.sin_port = htons(port);
+		else
+			s.sin6.sin6_port = htons(port);
+		
+		CHECK_SYS( bind(*sock, &s.sa, sSAlen(&s)) );
+		
+	} else {
+		/* Explicit endpoints to bind to from config */
+		
+		sSA * sar = NULL; /* array of addresses */
+		size_t sz = 0; /* size of the array */
+		int count = 0; /* number of sock addr in the array */
+		
+		/* Create the array of configured addresses */
+		CHECK_FCT( add_addresses_from_list_mask((void *)&sar, &sz, &count, family, htons(port), list, EP_FL_CONF, EP_FL_CONF) );
+		
+		if (!count) {
+			/* None of the addresses in the list came from configuration, we bind to default */
+			bind_default = 1;
+			goto redo;
+		}
+		
+		if (TRACE_BOOL(SCTP_LEVEL)) {
+			union {
+				sSA	*sa;
+				uint8_t *buf;
+			} ptr;
+			int i;
+			ptr.sa = sar;
+			fd_log_debug("Calling sctp_bindx with the following address array:\n");
+			for (i = 0; i < count; i++) {
+				TRACE_DEBUG_sSA(FULL, "    - ", ptr.sa, NI_NUMERICHOST | NI_NUMERICSERV, "" );
+				ptr.buf += (ptr.sa->sa_family == AF_INET) ? sizeof(sSA4) : sizeof(sSA6) ;
+			}
+		}
+		
+		/* Bind to this array */
+		CHECK_SYS(  sctp_bindx(*sock, sar, count, SCTP_BINDX_ADD_ADDR)  );
+		
+		/* We don't need sar anymore */
+		free(sar);
+	}
+	
+	/* Now, the server is bound, set remaining sockopt */
+	CHECK_FCT( fd_setsockopt_postbind(*sock, bind_default) );
+	
+	/* Debug: show all local listening addresses */
+	if (TRACE_BOOL(SCTP_LEVEL)) {
+		sSA *sar;
+		union {
+			sSA	*sa;
+			uint8_t *buf;
+		} ptr;
+		int sz;
+		
+		CHECK_SYS(  sz = sctp_getladdrs(*sock, 0, &sar)  );
+		
+		fd_log_debug("SCTP server bound on :\n");
+		for (ptr.sa = sar; sz-- > 0; ptr.buf += (ptr.sa->sa_family == AF_INET) ? sizeof(sSA4) : sizeof(sSA6)) {
+			TRACE_DEBUG_sSA(FULL, "    - ", ptr.sa, NI_NUMERICHOST | NI_NUMERICSERV, "" );
+		}
+		sctp_freeladdrs(sar);
+	}
+
+	return 0;
+}
+
+/* Allow clients connections on server sockets */
+int fd_sctp_listen( int sock )
+{
+	TRACE_ENTRY("%d", sock);
+	CHECK_SYS( listen(sock, 5) );
+	return 0;
+}
+
+/* Create a client socket and connect to remote server */
+int fd_sctp_client( int *sock, int no_ip6, uint16_t port, struct fd_list * list )
+{
+	int family;
+	union {
+		uint8_t *buf;
+		sSA	*sa;
+	} sar;
+	size_t size = 0;
+	int count = 0;
+	int ret;
+	
+	sar.buf = NULL;
+	
+	TRACE_ENTRY("%p %i %hu %p", sock, no_ip6, port, list);
+	CHECK_PARAMS( sock && list && (!FD_IS_LIST_EMPTY(list)) );
+	
+	if (no_ip6) {
+		family = AF_INET;
+	} else {
+		family = AF_INET6;
+	}
+	
+	/* Create the socket */
+	CHECK_SYS( *sock = socket(family, SOCK_STREAM, IPPROTO_SCTP) );
+	
+	/* Cleanup if we are cancelled */
+	pthread_cleanup_push(fd_cleanup_socket, sock);
+	
+	/* Set the socket options */
+	CHECK_FCT_DO( ret = fd_setsockopt_prebind(*sock), goto fail );
+	
+	/* Create the array of addresses, add first the configured addresses, then the discovered, then the other ones */
+	CHECK_FCT_DO( ret = add_addresses_from_list_mask(&sar.buf, &size, &count, family, htons(port), list, EP_FL_CONF,              EP_FL_CONF	), goto fail );
+	CHECK_FCT_DO( ret = add_addresses_from_list_mask(&sar.buf, &size, &count, family, htons(port), list, EP_FL_CONF | EP_FL_DISC, EP_FL_DISC	), goto fail );
+	CHECK_FCT_DO( ret = add_addresses_from_list_mask(&sar.buf, &size, &count, family, htons(port), list, EP_FL_CONF | EP_FL_DISC, 0		), goto fail );
+	
+	/* Try connecting */
+	if (TRACE_BOOL(FULL)) {
+		TRACE_DEBUG(FULL, "Attempting SCTP connection (%d addresses attempted) :", count);
+		/* Dump the SAs */
+		union {
+			uint8_t *buf;
+			sSA	*sa;
+			sSA4	*sin;
+			sSA6	*sin6;
+		} ptr;
+		int i;
+		ptr.buf = sar.buf;
+		for (i=0; i< count; i++) {
+			TRACE_DEBUG_sSA(FULL, "  - ", ptr.sa, NI_NUMERICHOST | NI_NUMERICSERV, "" );
+			ptr.buf += (ptr.sa->sa_family == AF_INET) ? sizeof(sSA4) : sizeof(sSA6);
+		}
+	}
+	
+#ifdef SCTP_CONNECTX_4_ARGS
+	ret = sctp_connectx(*sock, sar.sa, count, NULL);
+#else /* SCTP_CONNECTX_4_ARGS */
+	ret = sctp_connectx(*sock, sar.sa, count);
+#endif /* SCTP_CONNECTX_4_ARGS */
+	
+	if (ret < 0) {
+		int lvl;
+		switch (ret = errno) {
+			case ECONNREFUSED:
+			
+				/* "Normal" errors */
+				lvl = FULL;
+				break;
+			default:
+				lvl = INFO;
+		}
+		/* Some errors are expected, we log at different level */
+		TRACE_DEBUG( lvl, "sctp_connectx returned an error: %s", strerror(ret));
+		goto fail;
+	}
+	
+	free(sar.buf); sar.buf = NULL;
+	
+	/* Set the remaining sockopts */
+	CHECK_FCT_DO( ret = fd_setsockopt_postbind(*sock, 1), goto fail_deco );
+	
+	/* Done! */
+	pthread_cleanup_pop(0);
+	return 0;
+	
+fail_deco:
+	CHECK_SYS_DO( shutdown(*sock, SHUT_RDWR), /* continue */ );
+fail:
+	if (*sock > 0) {
+		CHECK_SYS_DO( close(*sock), /* continue */ );
+		*sock = -1;
+	}
+	free(sar.buf);
+	return ret;
+}
+
+/* Retrieve streams information from a connected association -- optionaly provide the primary address */
+int fd_sctp_get_str_info( int sock, uint16_t *in, uint16_t *out, sSS *primary )
+{
+	struct sctp_status status;
+	socklen_t sz = sizeof(status);
+	
+	TRACE_ENTRY("%d %p %p %p", sock, in, out, primary);
+	CHECK_PARAMS( (sock > 0) && in && out );
+	
+	/* Read the association parameters */
+	memset(&status, 0, sizeof(status));
+	CHECK_SYS(  getsockopt(sock, IPPROTO_SCTP, SCTP_STATUS, &status, &sz) );
+	if (sz != sizeof(status))
+	{
+		TRACE_DEBUG(INFO, "Invalid size of socket option: %d / %zd", sz, sizeof(status));
+		return ENOTSUP;
+	}
+	if (TRACE_BOOL(SCTP_LEVEL)) {
+		fd_log_debug( "SCTP_STATUS : sstat_state                  : %i\n" , status.sstat_state);
+		fd_log_debug( "              sstat_rwnd  	          : %u\n" , status.sstat_rwnd);
+		fd_log_debug( "		     sstat_unackdata	          : %hu\n", status.sstat_unackdata);
+		fd_log_debug( "		     sstat_penddata 	          : %hu\n", status.sstat_penddata);
+		fd_log_debug( "		     sstat_instrms  	          : %hu\n", status.sstat_instrms);
+		fd_log_debug( "		     sstat_outstrms 	          : %hu\n", status.sstat_outstrms);
+		fd_log_debug( "		     sstat_fragmentation_point    : %u\n" , status.sstat_fragmentation_point);
+		fd_log_debug( "		     sstat_primary.spinfo_address : ");
+		sSA_DUMP_NODE_SERV(&status.sstat_primary.spinfo_address, NI_NUMERICHOST | NI_NUMERICSERV );
+		fd_log_debug( "\n" );
+		fd_log_debug( "		     sstat_primary.spinfo_state   : %d\n" , status.sstat_primary.spinfo_state);
+		fd_log_debug( "		     sstat_primary.spinfo_cwnd    : %u\n" , status.sstat_primary.spinfo_cwnd);
+		fd_log_debug( "		     sstat_primary.spinfo_srtt    : %u\n" , status.sstat_primary.spinfo_srtt);
+		fd_log_debug( "		     sstat_primary.spinfo_rto     : %u\n" , status.sstat_primary.spinfo_rto);
+		fd_log_debug( "		     sstat_primary.spinfo_mtu     : %u\n" , status.sstat_primary.spinfo_mtu);
+	}
+	
+	*in = status.sstat_instrms;
+	*out = status.sstat_outstrms;
+	
+	if (primary)
+		memcpy(primary, &status.sstat_primary.spinfo_address, sizeof(sSS));
+	
+	return 0;
+}
+
+/* Get the list of remote endpoints of the socket */
+int fd_sctp_get_remote_ep(int sock, struct fd_list * list)
+{
+	union {
+		sSA	*sa;
+		uint8_t	*buf;
+	} ptr;
+	
+	sSA * data = NULL;
+	int count;
+	
+	TRACE_ENTRY("%d %p", sock, list);
+	CHECK_PARAMS(list);
+	
+	/* Read the list on the socket */
+	CHECK_SYS( count = sctp_getpaddrs(sock, 0, &data)  );
+	ptr.sa = data;
+	
+	while (count) {
+		socklen_t sl;
+		switch (ptr.sa->sa_family) {
+			case AF_INET:	sl = sizeof(sSA4); break;
+			case AF_INET6:	sl = sizeof(sSA6); break;
+			default:
+				TRACE_DEBUG(INFO, "Unknown address family returned in sctp_getpaddrs: %d, skip", ptr.sa->sa_family);
+				/* There is a bug in current Linux kernel: http://www.spinics.net/lists/linux-sctp/msg00760.html */
+				goto stop;
+		}
+				
+		CHECK_FCT( fd_ep_add_merge( list, ptr.sa, sl, EP_FL_LL ) );
+		ptr.buf += sl;
+		count --;
+	}
+stop:	
+	/* Free the list */
+	sctp_freepaddrs(data);
+	
+	/* Now get the primary address, the add function will take care of merging with existing entry */
+	{
+		 
+		struct sctp_status status;
+		socklen_t sz = sizeof(status);
+		int ret;
+		
+		memset(&status, 0, sizeof(status));
+		/* Attempt to use SCTP_STATUS message to retrieve the primary address */
+		CHECK_SYS_DO( ret = getsockopt(sock, IPPROTO_SCTP, SCTP_STATUS, &status, &sz), /* continue */);
+		if (sz != sizeof(status))
+			ret = -1;
+		sz = sizeof(sSS);
+		if (ret < 0)
+		{
+			/* Fallback to getsockname -- not recommended by draft-ietf-tsvwg-sctpsocket-19#section-7.4 */
+			CHECK_SYS(getpeername(sock, (sSA *)&status.sstat_primary.spinfo_address, &sz));
+		}
+			
+		CHECK_FCT( fd_ep_add_merge( list, (sSA *)&status.sstat_primary.spinfo_address, sz, EP_FL_PRIMARY ) );
+	}
+	
+	/* Done! */
+	return 0;
+}
+
+/* Send a buffer over a specified stream */
+int fd_sctp_sendstr(int sock, uint16_t strid, uint8_t * buf, size_t len, uint32_t * cc_status)
+{
+	struct msghdr mhdr;
+	struct iovec  iov;
+	struct cmsghdr 		*hdr;
+	struct sctp_sndrcvinfo	*sndrcv;
+	uint8_t anci[CMSG_SPACE(sizeof(struct sctp_sndrcvinfo))];
+	ssize_t ret;
+	int timedout = 0;
+	
+	TRACE_ENTRY("%d %hu %p %zd %p", sock, strid, buf, len, cc_status);
+	CHECK_PARAMS(cc_status);
+	
+	memset(&mhdr, 0, sizeof(mhdr));
+	memset(&iov,  0, sizeof(iov));
+	memset(&anci, 0, sizeof(anci));
+	
+	/* IO Vector: message data */
+	iov.iov_base = buf;
+	iov.iov_len  = len;
+	
+	/* Anciliary data: specify SCTP stream */
+	hdr = (struct cmsghdr *)anci;
+	sndrcv = (struct sctp_sndrcvinfo *)CMSG_DATA(hdr);
+	hdr->cmsg_len   = sizeof(anci);
+	hdr->cmsg_level = IPPROTO_SCTP;
+	hdr->cmsg_type  = SCTP_SNDRCV;
+	sndrcv->sinfo_stream = strid;
+	/* note : we could store other data also, for example in .sinfo_ppid for remote peer or in .sinfo_context for errors. */
+	
+	/* We don't use mhdr.msg_name here; it could be used to specify an address different from the primary */
+	
+	mhdr.msg_iov    = &iov;
+	mhdr.msg_iovlen = 1;
+	
+	mhdr.msg_control    = anci;
+	mhdr.msg_controllen = sizeof(anci);
+	
+	TRACE_DEBUG(FULL, "Sending %db data on stream %hu of socket %d", len, strid, sock);
+again:	
+	ret = sendmsg(sock, &mhdr, 0);
+	/* Handle special case of timeout */
+	if ((ret < 0) && (errno == EAGAIN)) {
+		if (!(*cc_status & CC_STATUS_CLOSING))
+			goto again; /* don't care, just ignore */
+		if (!timedout) {
+			timedout ++; /* allow for one timeout while closing */
+			goto again;
+		}
+	}
+	
+	CHECK_SYS( ret );
+	ASSERT( ret == len ); /* There should not be partial delivery with sendmsg... */
+	
+	return 0;
+}
+
+/* Receive the next data from the socket, or next notification */
+int fd_sctp_recvmeta(int sock, uint16_t * strid, uint8_t ** buf, size_t * len, int *event, uint32_t * cc_status)
+{
+	ssize_t 		 ret = 0;
+	struct msghdr 		 mhdr;
+	char   			 ancidata[ CMSG_BUF_LEN ];
+	struct iovec 		 iov;
+	uint8_t			*data = NULL;
+	size_t 			 bufsz = 0, datasize = 0;
+	size_t			 mempagesz = sysconf(_SC_PAGESIZE); /* We alloc buffer by memory pages for efficiency */
+	int 			 timedout = 0;
+	
+	TRACE_ENTRY("%d %p %p %p %p %p", sock, strid, buf, len, event, cc_status);
+	CHECK_PARAMS( (sock > 0) && buf && len && event && cc_status );
+	
+	/* Cleanup out parameters */
+	*buf = NULL;
+	*len = 0;
+	*event = 0;
+	
+	/* Prepare header for receiving message */
+	memset(&mhdr, 0, sizeof(mhdr));
+	mhdr.msg_iov    = &iov;
+	mhdr.msg_iovlen = 1;
+	mhdr.msg_control    = &ancidata;
+	mhdr.msg_controllen = sizeof(ancidata);
+	
+	/* We will loop while all data is not received. */
+incomplete:
+	if (datasize == bufsz) {
+		/* The buffer is full, enlarge it */
+		bufsz += mempagesz;
+		CHECK_MALLOC( data = realloc(data, bufsz) );
+	}
+	/* the new data will be received following the preceding */
+	memset(&iov,  0, sizeof(iov));
+	iov.iov_base = data + datasize ;
+	iov.iov_len  = bufsz - datasize;
+
+	/* Receive data from the socket */
+again:
+	pthread_cleanup_push(free, data);
+	ret = recvmsg(sock, &mhdr, 0);
+	pthread_cleanup_pop(0);
+	
+	/* First, handle timeouts (same as fd_cnx_s_recv) */
+	if ((ret < 0) && (errno == EAGAIN)) {
+		if (!(*cc_status & CC_STATUS_CLOSING))
+			goto again; /* don't care, just ignore */
+		if (!timedout) {
+			timedout ++; /* allow for one timeout while closing */
+			goto again;
+		}
+		/* fallback to normal handling */
+	}
+	
+	/* Handle errors */
+	if (ret <= 0) { /* Socket timedout, closed, or an error occurred */
+		CHECK_SYS_DO(ret, /* to log in case of error */);
+		free(data);
+		*event = FDEVP_CNX_ERROR;
+		return 0;
+	}
+	
+	/* Update the size of data we received */
+	datasize += ret;
+
+	/* SCTP provides an indication when we received a full record; loop if it is not the case */
+	if ( ! (mhdr.msg_flags & MSG_EOR) ) {
+		goto incomplete;
+	}
+	
+	/* Handle the case where the data received is a notification */
+	if (mhdr.msg_flags & MSG_NOTIFICATION) {
+		union sctp_notification * notif = (union sctp_notification *) data;
+		
+		TRACE_DEBUG(FULL, "Received %db data of notification on socket %d", datasize, sock);
+	
+		switch (notif->sn_header.sn_type) {
+			
+			case SCTP_ASSOC_CHANGE:
+				TRACE_DEBUG(FULL, "Received SCTP_ASSOC_CHANGE notification");
+				TRACE_DEBUG(SCTP_LEVEL, "    state : %hu", notif->sn_assoc_change.sac_state);
+				TRACE_DEBUG(SCTP_LEVEL, "    error : %hu", notif->sn_assoc_change.sac_error);
+				TRACE_DEBUG(SCTP_LEVEL, "    instr : %hu", notif->sn_assoc_change.sac_inbound_streams);
+				TRACE_DEBUG(SCTP_LEVEL, "   outstr : %hu", notif->sn_assoc_change.sac_outbound_streams);
+				
+				*event = FDEVP_CNX_EP_CHANGE;
+				break;
+	
+			case SCTP_PEER_ADDR_CHANGE:
+				TRACE_DEBUG(FULL, "Received SCTP_PEER_ADDR_CHANGE notification");
+				TRACE_DEBUG_sSA(SCTP_LEVEL, "    intf_change : ", &(notif->sn_paddr_change.spc_aaddr), NI_NUMERICHOST | NI_NUMERICSERV, "" );
+				TRACE_DEBUG(SCTP_LEVEL, "          state : %d", notif->sn_paddr_change.spc_state);
+				TRACE_DEBUG(SCTP_LEVEL, "          error : %d", notif->sn_paddr_change.spc_error);
+				
+				*event = FDEVP_CNX_EP_CHANGE;
+				break;
+	
+			case SCTP_SEND_FAILED:
+				TRACE_DEBUG(FULL, "Received SCTP_SEND_FAILED notification");
+				TRACE_DEBUG(SCTP_LEVEL, "    len : %hu", notif->sn_send_failed.ssf_length);
+				TRACE_DEBUG(SCTP_LEVEL, "    err : %d",  notif->sn_send_failed.ssf_error);
+				
+				*event = FDEVP_CNX_ERROR;
+				break;
+			
+			case SCTP_REMOTE_ERROR:
+				TRACE_DEBUG(FULL, "Received SCTP_REMOTE_ERROR notification");
+				TRACE_DEBUG(SCTP_LEVEL, "    err : %hu", ntohs(notif->sn_remote_error.sre_error));
+				TRACE_DEBUG(SCTP_LEVEL, "    len : %hu", ntohs(notif->sn_remote_error.sre_length));
+				
+				*event = FDEVP_CNX_ERROR;
+				break;
+	
+			case SCTP_SHUTDOWN_EVENT:
+				TRACE_DEBUG(FULL, "Received SCTP_SHUTDOWN_EVENT notification");
+				
+				*event = FDEVP_CNX_SHUTDOWN;
+				break;
+			
+			default:	
+				TRACE_DEBUG(FULL, "Received unknown notification %d, assume error", notif->sn_header.sn_type);
+				*event = FDEVP_CNX_ERROR;
+		}
+		
+		free(data);
+		return 0;
+	}
+	
+	/* From this point, we have received a message */
+	*event = FDEVP_CNX_MSG_RECV;
+	*buf = data;
+	*len = datasize;
+	
+	if (strid) {
+		struct cmsghdr 		*hdr;
+		struct sctp_sndrcvinfo	*sndrcv;
+		
+		/* Handle the anciliary data */
+		for (hdr = CMSG_FIRSTHDR(&mhdr); hdr; hdr = CMSG_NXTHDR(&mhdr, hdr)) {
+
+			/* We deal only with anciliary data at SCTP level */
+			if (hdr->cmsg_level != IPPROTO_SCTP) {
+				TRACE_DEBUG(FULL, "Received some anciliary data at level %d, skipped", hdr->cmsg_level);
+				continue;
+			}
+			
+			/* Also only interested in SCTP_SNDRCV message for the moment */
+			if (hdr->cmsg_type != SCTP_SNDRCV) {
+				TRACE_DEBUG(FULL, "Anciliary block IPPROTO_SCTP / %d, skipped", hdr->cmsg_type);
+				continue;
+			}
+			
+			sndrcv = (struct sctp_sndrcvinfo *) CMSG_DATA(hdr);
+			if (TRACE_BOOL(SCTP_LEVEL)) {
+				fd_log_debug( "Anciliary block IPPROTO_SCTP / SCTP_SNDRCV\n");
+				fd_log_debug( "    sinfo_stream    : %hu\n", sndrcv->sinfo_stream);
+				fd_log_debug( "    sinfo_ssn       : %hu\n", sndrcv->sinfo_ssn);
+				fd_log_debug( "    sinfo_flags     : %hu\n", sndrcv->sinfo_flags);
+				/* fd_log_debug( "    sinfo_pr_policy : %hu\n", sndrcv->sinfo_pr_policy); */
+				fd_log_debug( "    sinfo_ppid      : %u\n" , sndrcv->sinfo_ppid);
+				fd_log_debug( "    sinfo_context   : %u\n" , sndrcv->sinfo_context);
+				/* fd_log_debug( "    sinfo_pr_value  : %u\n" , sndrcv->sinfo_pr_value); */
+				fd_log_debug( "    sinfo_tsn       : %u\n" , sndrcv->sinfo_tsn);
+				fd_log_debug( "    sinfo_cumtsn    : %u\n" , sndrcv->sinfo_cumtsn);
+			}
+
+			*strid = sndrcv->sinfo_stream;
+		}
+		TRACE_DEBUG(FULL, "Received %db data on socket %d, stream %hu", datasize, sock, *strid);
+	} else {
+		TRACE_DEBUG(FULL, "Received %db data on socket %d (stream ignored)", datasize, sock);
+	}
+	
+	return 0;
+}
Index: libfdcore/sctps.c
===================================================================
--- libfdcore/sctps.c	(revision 658)
+++ libfdcore/sctps.c	(revision 658)
@@ -0,0 +1,716 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@nict.go.jp>							 *
+*													 *
+* Copyright (c) 2010, 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 code for TLS over multi-stream SCTP wrapper implementation (GnuTLS does not support this) */
+/* See http://aaa.koganei.wide.ad.jp/blogs/index.php/waaad/2008/08/18/tls-over-sctp for history */
+
+#include "fdcore-internal.h"
+#include "cnxctx.h"
+
+#include <netinet/sctp.h>
+#include <sys/uio.h>
+
+/*
+
+Architecture of this wrapper:
+ - we have several fifo queues (1 per stream pairs).
+ GnuTLS is configured to use custom push / pull functions:
+ - the pull function retrieves the data from the fifo queue corresponding to a stream #.
+ - the push function sends the data on a certain stream.
+ We also have a demux thread that reads the socket and store received data in the appropriate fifo
+ 
+ We have one gnutls_session per stream pair, and as many streams that read the gnutls records and save incoming data to the target queue.
+ 
+This complexity is required because we cannot read a socket for a given stream only; we can only get the next message and find its stream.
+*/
+
+
+
+/*************************************************************/
+/*                      threads                              */
+/*************************************************************/
+
+/* Demux received data and store in the appropriate fifo */
+static void * demuxer(void * arg)
+{
+	struct cnxctx * conn = arg;
+	uint8_t * buf;
+	size_t    bufsz;
+	int	  event;
+	uint16_t  strid;
+	
+	TRACE_ENTRY("%p", arg);
+	CHECK_PARAMS_DO(conn && (conn->cc_socket > 0), goto out);
+	
+	/* Set the thread name */
+	{
+		char buf[48];
+		snprintf(buf, sizeof(buf), "Demuxer (%d:%s)", conn->cc_socket, conn->cc_remid);
+		fd_log_threadname ( buf );
+	}
+	
+	ASSERT( conn->cc_proto == IPPROTO_SCTP );
+	ASSERT( Target_Queue(conn) );
+	ASSERT( conn->cc_sctps_data.array );
+	
+	do {
+		fd_cpu_flush_cache();
+		CHECK_FCT_DO( fd_sctp_recvmeta(conn->cc_socket, &strid, &buf, &bufsz, &event, &conn->cc_status), goto fatal );
+		switch (event) {
+			case FDEVP_CNX_MSG_RECV:
+				/* Demux this message to the appropriate fifo, another thread will pull, gnutls process, and send to target queue */
+				if (strid < conn->cc_sctp_para.pairs) {
+					CHECK_FCT_DO(fd_event_send(conn->cc_sctps_data.array[strid].raw_recv, event, bufsz, buf), goto fatal );
+				} else {
+					TRACE_DEBUG(INFO, "Received packet (%d bytes) on out-of-range stream #%d from %s, discarded.", bufsz, strid, conn->cc_remid);
+					free(buf);
+				}
+				break;
+				
+			case FDEVP_CNX_EP_CHANGE:
+				/* Send this event to the target queue */
+				fd_cpu_flush_cache();
+				CHECK_FCT_DO( fd_event_send( Target_Queue(conn), event, bufsz, buf), goto fatal );
+				break;
+			
+			case FDEVP_CNX_ERROR:
+				fd_cnx_markerror(conn);
+				goto out;
+				
+			case FDEVP_CNX_SHUTDOWN:
+				/* Just ignore the notification for now, we will get another error later anyway */
+				continue;
+				
+			default:
+				goto fatal;
+		}
+		
+	} while (conn->cc_loop);
+	
+out:
+	/* Signal termination of the connection to all decipher threads */
+	for (strid = 0; strid < conn->cc_sctp_para.pairs; strid++) {
+		if (conn->cc_sctps_data.array[strid].raw_recv)
+			CHECK_FCT_DO(fd_event_send(conn->cc_sctps_data.array[strid].raw_recv, FDEVP_CNX_ERROR, 0, NULL), goto fatal );
+	}
+	TRACE_DEBUG(FULL, "Thread terminated");	
+	return NULL;
+	
+fatal:
+	/* An unrecoverable error occurred, stop the daemon */
+	CHECK_FCT_DO(fd_event_send(fd_g_config->cnf_main_ev, FDEV_TERMINATE, 0, NULL), );
+	goto out;
+}
+
+/* Decrypt the data received in this stream pair and store it in the target queue */
+static void * decipher(void * arg)
+{
+	struct sctps_ctx * ctx = arg;
+	struct cnxctx 	 *cnx;
+	
+	TRACE_ENTRY("%p", arg);
+	CHECK_PARAMS_DO(ctx && ctx->raw_recv && ctx->parent, goto error);
+	cnx = ctx->parent;
+	ASSERT( Target_Queue(cnx) );
+	
+	/* Set the thread name */
+	{
+		char buf[48];
+		snprintf(buf, sizeof(buf), "Decipher (%hu@%d:%s)", ctx->strid, cnx->cc_socket, cnx->cc_remid);
+		fd_log_threadname ( buf );
+	}
+	
+	/* The next function loops while there is no error */
+	CHECK_FCT_DO(fd_tls_rcvthr_core(cnx, ctx->strid ? ctx->session : cnx->cc_tls_para.session), /* continue */);
+error:
+	fd_cnx_markerror(cnx);
+	TRACE_DEBUG(FULL, "Thread terminated");	
+	return NULL;
+}
+
+/*************************************************************/
+/*                     push / pull                           */
+/*************************************************************/
+
+/* Send data over the connection, called by gnutls */
+static ssize_t sctps_push(gnutls_transport_ptr_t tr, const void * data, size_t len)
+{
+	struct sctps_ctx * ctx = (struct sctps_ctx *) tr;
+	
+	TRACE_ENTRY("%p %p %zd", tr, data, len);
+	CHECK_PARAMS_DO( tr && data, { errno = EINVAL; return -1; } );
+	
+	fd_cpu_flush_cache();
+	CHECK_FCT_DO( fd_sctp_sendstr(ctx->parent->cc_socket, ctx->strid, (uint8_t *)data, len, &ctx->parent->cc_status), /* errno is already set */ return -1 );
+	
+	return len;
+}
+
+/* Retrieve data received on a stream and already demultiplexed */
+static ssize_t sctps_pull(gnutls_transport_ptr_t tr, void * buf, size_t len)
+{
+	struct sctps_ctx * ctx = (struct sctps_ctx *) tr;
+	size_t pulled = 0;
+	int emptied;
+	
+	TRACE_ENTRY("%p %p %zd", tr, buf, len);
+	CHECK_PARAMS_DO( tr && buf, { errno = EINVAL; return -1; } );
+	
+	/* If we don't have data available now, pull new message from the fifo -- this is blocking (until the queue is destroyed) */
+	if (!ctx->partial.buf) {
+		int ev;
+		CHECK_FCT_DO( errno = fd_event_get(ctx->raw_recv, &ev, &ctx->partial.bufsz, (void *)&ctx->partial.buf), return -1 );
+		if (ev == FDEVP_CNX_ERROR) 
+			return 0; /* connection closed */
+	}
+		
+	pulled = ctx->partial.bufsz - ctx->partial.offset;
+	if (pulled <= len) {
+		emptied = 1;
+	} else {
+		/* limit to the capacity of destination buffer */
+		emptied = 0;
+		pulled = len;
+	}
+
+	/* Store the data in the destination buffer */
+	memcpy(buf, ctx->partial.buf + ctx->partial.offset, pulled);
+
+	/* Free the buffer if we read all its content, and reset the partial structure */
+	if (emptied) {
+		free(ctx->partial.buf);
+		memset(&ctx->partial, 0, sizeof(ctx->partial));
+	} else {
+		ctx->partial.offset += pulled;
+	}
+
+	/* We are done */
+	return pulled;
+}
+
+/* Set the parameters of a session to use the appropriate fifo and stream information */
+static void set_sess_transport(gnutls_session_t session, struct sctps_ctx *ctx)
+{
+	/* Set the transport pointer passed to push & pull callbacks */
+	GNUTLS_TRACE( gnutls_transport_set_ptr( session, (gnutls_transport_ptr_t) ctx ) );
+	
+	/* Reset the low water value, since we don't use sockets */
+	GNUTLS_TRACE( gnutls_transport_set_lowat( session, 0 ) );
+	
+	/* Set the push and pull callbacks */
+	GNUTLS_TRACE( gnutls_transport_set_pull_function(session, sctps_pull) );
+	GNUTLS_TRACE( gnutls_transport_set_push_function(session, sctps_push) );
+
+	return;
+}
+
+/*************************************************************/
+/*               Session resuming support                    */
+/*************************************************************/
+
+struct sr_store {
+	struct fd_list	 list;	/* list of sr_data, ordered by key.size then key.data */
+	pthread_rwlock_t lock;
+	struct cnxctx   *parent;
+	/* Add another list to chain in a global list to implement a garbage collector on sessions -- TODO if needed */
+};
+
+/* Saved master session data for resuming sessions */
+struct sr_data {
+	struct fd_list	chain;
+	gnutls_datum_t	key;
+	gnutls_datum_t 	data;
+};
+
+/* Initialize the store area for a connection */
+static int store_init(struct cnxctx * conn)
+{
+	TRACE_ENTRY("%p", conn);
+	CHECK_PARAMS( conn && !conn->cc_sctps_data.sess_store );
+	
+	CHECK_MALLOC( conn->cc_sctps_data.sess_store = malloc(sizeof(struct sr_store)) );
+	memset(conn->cc_sctps_data.sess_store, 0, sizeof(struct sr_store));
+	
+	fd_list_init(&conn->cc_sctps_data.sess_store->list, NULL);
+	CHECK_POSIX( pthread_rwlock_init(&conn->cc_sctps_data.sess_store->lock, NULL) );
+	conn->cc_sctps_data.sess_store->parent = conn;
+	
+	return 0;
+}
+
+/* Destroy the store area for a connection, and all its content */
+static void store_destroy(struct cnxctx * conn)
+{
+	/* Del all list entries */
+	TRACE_ENTRY("%p", conn);
+	CHECK_PARAMS_DO( conn, return );
+	
+	if (!conn->cc_sctps_data.sess_store)
+		return;
+	
+	CHECK_POSIX_DO( pthread_rwlock_destroy(&conn->cc_sctps_data.sess_store->lock), /* continue */ );
+	
+	while (!FD_IS_LIST_EMPTY(&conn->cc_sctps_data.sess_store->list)) {
+		struct sr_data * sr = (struct sr_data *) conn->cc_sctps_data.sess_store->list.next;
+		fd_list_unlink( &sr->chain );
+		free(sr->key.data);
+		free(sr->data.data);
+		free(sr);
+	}
+	
+	free(conn->cc_sctps_data.sess_store);
+	conn->cc_sctps_data.sess_store = NULL;
+	return;
+}
+
+/* Search the position (or next if not found) of a key in a store */
+static struct fd_list * find_or_next(struct sr_store * sto, gnutls_datum_t key, int * match)
+{
+	struct fd_list * ret;
+	*match = 0;
+	
+	for (ret = sto->list.next; ret != &sto->list; ret = ret->next) {
+		int cmp = 0;
+		struct sr_data * sr = (struct sr_data *)ret;
+		
+		if ( key.size < sr->key.size )
+			break;
+		
+		if ( key.size > sr->key.size )
+			continue;
+		
+		/* Key sizes are equal */
+		cmp = memcmp( key.data, sr->key.data, key.size );
+		
+		if (cmp > 0)
+			continue;
+		
+		if (cmp == 0)
+			*match = 1;
+		
+		break;
+	}
+	
+	return ret;
+}
+
+/* Callbacks for the TLS server side of the connection, called during gnutls_handshake */
+static int sr_store (void *dbf, gnutls_datum_t key, gnutls_datum_t data)
+{
+	struct sr_store * sto = (struct sr_store *)dbf;
+	struct fd_list * li;
+	struct sr_data * sr;
+	int match = 0;
+	int ret = 0;
+	
+	TRACE_DEBUG( GNUTLS_DBG_LEVEL, "GNUTLS Callback: %s", __PRETTY_FUNCTION__ );
+	CHECK_PARAMS_DO( sto && key.data && data.data, return -1 );
+	
+	CHECK_POSIX_DO( pthread_rwlock_wrlock(&sto->lock), return -1 );
+	TRACE_DEBUG_BUFFER(GNUTLS_DBG_LEVEL, "Session store [key ", key.data, key.size, "]");
+	
+	li = find_or_next(sto, key, &match);
+	if (match) {
+		sr = (struct sr_data *)li;
+		
+		/* Check the data is the same */
+		if ((data.size != sr->data.size) || memcmp(data.data, sr->data.data, data.size)) {
+			TRACE_DEBUG(INFO, "GnuTLS tried to store a session with same key and different data!");
+			TRACE_DEBUG_BUFFER(INFO, "Session store [key ", key.data, key.size, "]");
+			TRACE_DEBUG_BUFFER(INFO, "  -- old data [", sr->data.data, sr->data.size, "]");
+			TRACE_DEBUG_BUFFER(INFO, "  -- new data [", data.data, data.size, "]");
+			
+			ret = -1;
+		} else {
+			TRACE_DEBUG(GNUTLS_DBG_LEVEL, "GnuTLS tried to store a session with same key and same data, skipped.");
+		}
+		goto out;
+	}
+	
+	/* Create a new entry */
+	CHECK_MALLOC_DO( sr = malloc(sizeof(struct sr_data)), { ret = -1; goto out; } );
+	memset(sr, 0, sizeof(struct sr_data));
+
+	fd_list_init(&sr->chain, sr);
+
+	CHECK_MALLOC_DO( sr->key.data = malloc(key.size), { ret = -1; goto out; } );
+	sr->key.size = key.size;
+	memcpy(sr->key.data, key.data, key.size);
+
+	CHECK_MALLOC_DO( sr->data.data = malloc(data.size), { ret = -1; goto out; } );
+	sr->data.size = data.size;
+	memcpy(sr->data.data, data.data, data.size);
+	
+	/* Save this new entry in the list, we are done */
+	fd_list_insert_before(li, &sr->chain);
+
+out:	
+	CHECK_POSIX_DO( pthread_rwlock_unlock(&sto->lock), return -1 );
+	return ret;
+}
+
+static int sr_remove (void *dbf, gnutls_datum_t key)
+{
+	struct sr_store * sto = (struct sr_store *)dbf;
+	struct fd_list * li;
+	struct sr_data * sr;
+	int match = 0;
+	int ret = 0;
+	
+	TRACE_DEBUG( GNUTLS_DBG_LEVEL, "GNUTLS Callback: %s", __PRETTY_FUNCTION__ );
+	CHECK_PARAMS_DO( sto && key.data, return -1 );
+	
+	CHECK_POSIX_DO( pthread_rwlock_wrlock(&sto->lock), return -1 );
+	TRACE_DEBUG_BUFFER(GNUTLS_DBG_LEVEL, "Session delete [key ", key.data, key.size, "]");
+	
+	li = find_or_next(sto, key, &match);
+	if (match) {
+		sr = (struct sr_data *)li;
+		
+		/* Destroy this data */
+		fd_list_unlink(li);
+		free(sr->key.data);
+		free(sr->data.data);
+		free(sr);
+	} else {
+		/* It was not found */
+		ret = -1;
+	}
+	
+	CHECK_POSIX_DO( pthread_rwlock_unlock(&sto->lock), return -1 );
+	return ret;
+}
+
+static gnutls_datum_t sr_fetch (void *dbf, gnutls_datum_t key)
+{
+	struct sr_store * sto = (struct sr_store *)dbf;
+	struct fd_list * li;
+	struct sr_data * sr;
+	int match = 0;
+	gnutls_datum_t res = { NULL, 0 };
+	gnutls_datum_t error = { NULL, 0 };
+
+	TRACE_DEBUG( GNUTLS_DBG_LEVEL, "GNUTLS Callback: %s", __PRETTY_FUNCTION__ );
+	CHECK_PARAMS_DO( sto && key.data, return error );
+
+	CHECK_POSIX_DO( pthread_rwlock_rdlock(&sto->lock), return error );
+	TRACE_DEBUG_BUFFER(GNUTLS_DBG_LEVEL, "Session fetch [key ", key.data, key.size, "]");
+	
+	li = find_or_next(sto, key, &match);
+	if (match) {
+		sr = (struct sr_data *)li;
+		GNUTLS_TRACE( CHECK_MALLOC_DO(res.data = gnutls_malloc(sr->data.size), goto out ) );
+		res.size = sr->data.size;
+		memcpy(res.data, sr->data.data, res.size);
+	}
+out:	
+	TRACE_DEBUG(GNUTLS_DBG_LEVEL, "Fetched (%p, %d) from store %p", res.data, res.size, sto);
+	CHECK_POSIX_DO( pthread_rwlock_unlock(&sto->lock), return error);
+	return res;
+}
+
+/* Set the session pointer in a session object */
+static void set_resume_callbacks(gnutls_session_t session, struct cnxctx * conn)
+{
+	TRACE_ENTRY("%p", conn);
+	
+	GNUTLS_TRACE( gnutls_db_set_retrieve_function(session, sr_fetch));
+	GNUTLS_TRACE( gnutls_db_set_remove_function  (session, sr_remove));
+	GNUTLS_TRACE( gnutls_db_set_store_function   (session, sr_store));
+	GNUTLS_TRACE( gnutls_db_set_ptr              (session, conn->cc_sctps_data.sess_store));
+	
+	return;
+}
+
+/* The handshake is made in parallel in several threads to speed up */
+static void * handshake_resume_th(void * arg)
+{
+	struct sctps_ctx * ctx = (struct sctps_ctx *) arg;
+	int resumed;
+	
+	TRACE_ENTRY("%p", arg);
+	
+	/* Set the thread name */
+	{
+		char buf[48];
+		snprintf(buf, sizeof(buf), "Handshake resume (%hu@%d)", ctx->strid, ctx->parent->cc_socket);
+		fd_log_threadname ( buf );
+	}
+	
+	TRACE_DEBUG(FULL, "Starting TLS resumed handshake on stream %hu", ctx->strid);
+	CHECK_GNUTLS_DO( gnutls_handshake( ctx->session ), return NULL);
+			
+	GNUTLS_TRACE( resumed = gnutls_session_is_resumed(ctx->session) );
+	if (!resumed) {
+		/* Check the credentials here also */
+		CHECK_FCT_DO( fd_tls_verify_credentials(ctx->session, ctx->parent, 0), return NULL );
+	}
+	if (TRACE_BOOL(FULL)) {
+		if (resumed) {
+			fd_log_debug("Session was resumed successfully on stream %hu (conn: '%s')\n", ctx->strid, fd_cnx_getid(ctx->parent));
+		} else {
+			fd_log_debug("Session was NOT resumed on stream %hu  (full handshake + verif) (conn: '%s')\n", ctx->strid, fd_cnx_getid(ctx->parent));
+		}
+	}
+			
+	/* Finished, OK */
+	return arg;
+}
+
+
+/*************************************************************/
+/*                     Exported functions                    */
+/*************************************************************/
+
+/* Initialize the wrapper for the connection */
+int fd_sctps_init(struct cnxctx * conn)
+{
+	uint16_t i;
+	
+	TRACE_ENTRY("%p", conn);
+	CHECK_PARAMS( conn && (conn->cc_sctp_para.pairs > 1) && (!conn->cc_sctps_data.array) );
+	
+	/* First, alloc the array and initialize the non-TLS data */
+	CHECK_MALLOC( conn->cc_sctps_data.array = calloc(conn->cc_sctp_para.pairs, sizeof(struct sctps_ctx))  );
+	for (i = 0; i < conn->cc_sctp_para.pairs; i++) {
+		conn->cc_sctps_data.array[i].parent = conn;
+		conn->cc_sctps_data.array[i].strid  = i;
+		CHECK_FCT( fd_fifo_new(&conn->cc_sctps_data.array[i].raw_recv) );
+	}
+	
+	/* Set push/pull functions in the master session, using fifo in array[0] */
+	set_sess_transport(conn->cc_tls_para.session, &conn->cc_sctps_data.array[0]);
+	
+	/* For server side, we also initialize the resuming capabilities */
+	if (conn->cc_tls_para.mode == GNUTLS_SERVER) {
+		
+		/* Prepare the store for sessions data */
+		CHECK_FCT( store_init(conn) );
+		
+		/* Set the callbacks for resuming in the master session */
+		set_resume_callbacks(conn->cc_tls_para.session, conn);
+	}
+
+	/* Start the demux thread */
+	CHECK_POSIX( pthread_create( &conn->cc_rcvthr, NULL, demuxer, conn ) );
+	
+	return 0;
+}
+
+/* Handshake other streams, after full handshake on the master session */
+int fd_sctps_handshake_others(struct cnxctx * conn, char * priority, void * alt_creds)
+{
+	uint16_t i;
+	int errors = 0;
+	gnutls_datum_t 	master_data;
+	
+	TRACE_ENTRY("%p %p", conn, priority);
+	CHECK_PARAMS( conn && (conn->cc_sctp_para.pairs > 1) && conn->cc_sctps_data.array );
+
+	/* Server side: we set all the parameters, the resume callback will take care of resuming the session */
+	/* Client side: we duplicate the parameters of the master session, then set the transport pointer */
+	
+	/* For client side, retrieve the master session parameters */
+	if (conn->cc_tls_para.mode == GNUTLS_CLIENT) {
+		CHECK_GNUTLS_DO( gnutls_session_get_data2(conn->cc_tls_para.session, &master_data), return ENOMEM );
+		/* For debug: */
+		if (TRACE_BOOL(GNUTLS_DBG_LEVEL)) {
+			uint8_t  id[256];
+			size_t	 ids = sizeof(id);
+			CHECK_GNUTLS_DO( gnutls_session_get_id(conn->cc_tls_para.session, id, &ids), /* continue */ );
+			TRACE_DEBUG_BUFFER(GNUTLS_DBG_LEVEL, "Master session id: [", id, ids, "]");
+		}
+	}
+	
+	/* Initialize the session objects and start the handshake in a separate thread */
+	for (i = 1; i < conn->cc_sctp_para.pairs; i++) {
+		/* Set credentials and priority */
+		CHECK_FCT( fd_tls_prepare(&conn->cc_sctps_data.array[i].session, conn->cc_tls_para.mode, priority, alt_creds) );
+		
+		/* For the client, copy data from master session; for the server, set session resuming pointers */
+		if (conn->cc_tls_para.mode == GNUTLS_CLIENT) {
+			CHECK_GNUTLS_DO( gnutls_session_set_data(conn->cc_sctps_data.array[i].session, master_data.data, master_data.size), return ENOMEM );
+		} else {
+			set_resume_callbacks(conn->cc_sctps_data.array[i].session, conn);
+		}
+		
+		/* Set transport parameters */
+		set_sess_transport(conn->cc_sctps_data.array[i].session, &conn->cc_sctps_data.array[i]);
+		
+		/* Start the handshake thread */
+		CHECK_POSIX( pthread_create( &conn->cc_sctps_data.array[i].thr, NULL, handshake_resume_th, &conn->cc_sctps_data.array[i] ) );
+	}
+	
+	/* We can now release the memory of master session data if any */
+	if (conn->cc_tls_para.mode == GNUTLS_CLIENT) {
+		GNUTLS_TRACE( gnutls_free(master_data.data) );
+	}
+	
+	/* Now wait for all handshakes to finish */
+	for (i = 1; i < conn->cc_sctp_para.pairs; i++) {
+		void * ret;
+		CHECK_POSIX( pthread_join(conn->cc_sctps_data.array[i].thr, &ret) );
+		conn->cc_sctps_data.array[i].thr = (pthread_t) NULL;
+		if (ret == NULL) {
+			errors++; /* Handshake failed on this stream */
+		}
+	}
+	
+	if (errors) {
+		TRACE_DEBUG(INFO, "Handshake failed on %d/%hd stream pairs", errors, conn->cc_sctp_para.pairs);
+		fd_cnx_markerror(conn);
+		return ENOTCONN;
+	}
+	
+	return 0;
+}
+
+/* Receive messages from others ? all other stream pairs : the master pair */
+int fd_sctps_startthreads(struct cnxctx * conn, int others)
+{
+	uint16_t i;
+	
+	TRACE_ENTRY("%p", conn);
+	CHECK_PARAMS( conn && conn->cc_sctps_data.array );
+	
+	if (others) {
+		for (i = 1; i < conn->cc_sctp_para.pairs; i++) {
+
+			/* Start the decipher thread */
+			CHECK_POSIX( pthread_create( &conn->cc_sctps_data.array[i].thr, NULL, decipher, &conn->cc_sctps_data.array[i] ) );
+		}
+	} else {
+		CHECK_POSIX( pthread_create( &conn->cc_sctps_data.array[0].thr, NULL, decipher, &conn->cc_sctps_data.array[0] ) );
+	}
+	return 0;
+}
+
+/* Initiate a "bye" on all stream pairs */
+void fd_sctps_bye(struct cnxctx * conn)
+{
+	uint16_t i;
+	
+	CHECK_PARAMS_DO( conn && conn->cc_sctps_data.array, return );
+	
+	/* End all TLS sessions, in series (not as efficient as paralel, but simpler) */
+	for (i = 1; i < conn->cc_sctp_para.pairs; i++) {
+		fd_cpu_flush_cache();
+		if ( ! (conn->cc_status & CC_STATUS_ERROR)) {
+			CHECK_GNUTLS_DO( gnutls_bye(conn->cc_sctps_data.array[i].session, GNUTLS_SHUT_WR), fd_cnx_markerror(conn) );
+		}
+	}
+}
+
+/* After "bye" was sent on all streams, read from sessions until an error is received */
+void fd_sctps_waitthreadsterm(struct cnxctx * conn)
+{
+	uint16_t i;
+	
+	TRACE_ENTRY("%p", conn);
+	CHECK_PARAMS_DO( conn && conn->cc_sctps_data.array, return );
+	
+	for (i = 0; i < conn->cc_sctp_para.pairs; i++) {
+		if (conn->cc_sctps_data.array[i].thr != (pthread_t)NULL) {
+			CHECK_POSIX_DO( pthread_join(conn->cc_sctps_data.array[i].thr, NULL), /* continue */ );
+			conn->cc_sctps_data.array[i].thr = (pthread_t)NULL;
+		}
+	}
+	return;
+}
+
+/* Free gnutls resources of all sessions */
+void fd_sctps_gnutls_deinit_others(struct cnxctx * conn)
+{
+	uint16_t i;
+	
+	TRACE_ENTRY("%p", conn);
+	CHECK_PARAMS_DO( conn && conn->cc_sctps_data.array, return );
+	
+	for (i = 1; i < conn->cc_sctp_para.pairs; i++) {
+		if (conn->cc_sctps_data.array[i].session) {
+			GNUTLS_TRACE( gnutls_deinit(conn->cc_sctps_data.array[i].session) );
+			conn->cc_sctps_data.array[i].session = NULL;
+		}
+	}
+}
+
+
+/* Stop all receiver threads */
+void fd_sctps_stopthreads(struct cnxctx * conn)
+{
+	uint16_t i;
+	
+	TRACE_ENTRY("%p", conn);
+	CHECK_PARAMS_DO( conn && conn->cc_sctps_data.array, return );
+	
+	for (i = 0; i < conn->cc_sctp_para.pairs; i++) {
+		CHECK_FCT_DO( fd_thr_term(&conn->cc_sctps_data.array[i].thr), /* continue */ );
+	}
+	return;
+}
+
+/* Destroy a wrapper context */
+void fd_sctps_destroy(struct cnxctx * conn)
+{
+	uint16_t i;
+	
+	CHECK_PARAMS_DO( conn && conn->cc_sctps_data.array, return );
+	
+	/* Terminate all receiving threads in case we did not do it yet */
+	fd_sctps_stopthreads(conn);
+	
+	/* Now, stop the demux thread */
+	CHECK_FCT_DO( fd_thr_term(&conn->cc_rcvthr), /* continue */ );
+	
+	/* Free remaining data in the array */
+	for (i = 0; i < conn->cc_sctp_para.pairs; i++) {
+		if (conn->cc_sctps_data.array[i].raw_recv)
+			fd_event_destroy( &conn->cc_sctps_data.array[i].raw_recv, free );
+		free(conn->cc_sctps_data.array[i].partial.buf);
+		if (conn->cc_sctps_data.array[i].session) {
+			GNUTLS_TRACE( gnutls_deinit(conn->cc_sctps_data.array[i].session) );
+			conn->cc_sctps_data.array[i].session = NULL;
+		}
+	}
+	
+	/* Free the array itself now */
+	free(conn->cc_sctps_data.array);
+	conn->cc_sctps_data.array = NULL;
+	
+	/* Delete the store of sessions */
+	store_destroy(conn);
+	
+	return ;
+}
Index: libfdcore/server.c
===================================================================
--- libfdcore/server.c	(revision 658)
+++ libfdcore/server.c	(revision 658)
@@ -0,0 +1,399 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@nict.go.jp>							 *
+*													 *
+* Copyright (c) 2010, 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 "fdcore-internal.h"
+
+/* Server (listening) part of the daemon */
+
+struct fd_list		FD_SERVERS = FD_LIST_INITIALIZER(FD_SERVERS);	/* The list of all server objects */
+/* We don't need to protect this list, it is only accessed from the main daemon thread. */
+
+/* Servers information */
+struct server {
+	struct fd_list	chain;		/* link in the FD_SERVERS list */
+
+	struct cnxctx *	conn;		/* server connection context (listening socket) */
+	int 		proto;		/* IPPROTO_TCP or IPPROTO_SCTP */
+	int 		secur;		/* TLS is started immediatly after connection ? */
+	
+	pthread_t	thr;		/* The thread listening for new connections */
+	int		status;		/* 0 : not created; 1 : running; 2 : terminated */
+	
+	struct fd_list	clients;	/* List of clients connected to this server, not yet identified */
+	pthread_mutex_t	clients_mtx;	/* Mutex to protect the list of clients */
+};
+
+/* Client information (connecting peer for which we don't have the CER yet) */
+struct client {
+	struct fd_list	 chain;	/* link in the server's list of clients */
+	struct cnxctx	*conn;	/* Parameters of the connection */
+	struct timespec	 ts;	/* Deadline for receiving CER (after INCNX_TIMEOUT) */
+	pthread_t	 thr; 	/* connection state machine */
+};
+
+
+/* Dump all servers information */
+void fd_servers_dump()
+{
+	struct fd_list * li, *cli;
+	
+	fd_log_debug("Dumping servers list :\n");
+	for (li = FD_SERVERS.next; li != &FD_SERVERS; li = li->next) {
+		struct server * s = (struct server *)li;
+		fd_cpu_flush_cache();
+		fd_log_debug("  Serv %p '%s': %s, %s, %s\n", 
+				s, fd_cnx_getid(s->conn), 
+				IPPROTO_NAME( s->proto ),
+				s->secur ? "Secur" : "NotSecur",
+				(s->status == 0) ? "Thread not created" :
+				((s->status == 1) ? "Thread running" :
+				((s->status == 2) ? "Thread terminated" :
+							  "Thread status unknown")));
+		/* Dump the client list of this server */
+		(void) pthread_mutex_lock(&s->clients_mtx);
+		for (cli = s->clients.next; cli != &s->clients; cli = cli->next) {
+			struct client * c = (struct client *)cli;
+			char bufts[128];
+			fd_log_debug("     Connected: '%s' (timeout: %s)\n",
+					fd_cnx_getid(c->conn),
+					fd_log_time(&c->ts, bufts, sizeof(bufts)));
+		}
+		(void) pthread_mutex_unlock(&s->clients_mtx);
+	}
+}
+
+
+/* The state machine to handle incoming connection before the remote peer is identified */
+static void * client_sm(void * arg)
+{
+	struct client * c = arg;
+	struct server * s = NULL;
+	uint8_t       * buf = NULL;
+	size_t 		bufsz;
+	struct msg    * msg = NULL;
+	struct msg_hdr *hdr = NULL;
+	
+	TRACE_ENTRY("%p", c);
+	
+	CHECK_PARAMS_DO(c && c->conn && c->chain.head, goto fatal_error );
+	
+	s = c->chain.head->o;
+	
+	/* Name the current thread */
+	fd_log_threadname ( fd_cnx_getid(c->conn) );
+	
+	/* Handshake if we are a secure server port, or start clear otherwise */
+	if (s->secur) {
+		int ret = fd_cnx_handshake(c->conn, GNUTLS_SERVER, NULL, NULL);
+		if (ret != 0) {
+			if (TRACE_BOOL(INFO)) {
+				fd_log_debug("TLS handshake failed for client '%s', connection aborted.\n", fd_cnx_getid(c->conn));
+			}
+			goto cleanup;
+		}
+	} else {
+		CHECK_FCT_DO( fd_cnx_start_clear(c->conn, 0), goto cleanup );
+	}
+	
+	/* Set the timeout to receive the first message */
+	CHECK_SYS_DO( clock_gettime(CLOCK_REALTIME, &c->ts), goto fatal_error );
+	c->ts.tv_sec += INCNX_TIMEOUT;
+	
+	/* Receive the first Diameter message on the connection -- cleanup in case of timeout */
+	CHECK_FCT_DO( fd_cnx_receive(c->conn, &c->ts, &buf, &bufsz), goto cleanup );
+	
+	TRACE_DEBUG(FULL, "Received %zdb from new client '%s'", bufsz, fd_cnx_getid(c->conn));
+	
+	/* Try parsing this message */
+	CHECK_FCT_DO( fd_msg_parse_buffer( &buf, bufsz, &msg ), /* Parsing failed */ goto cleanup );
+	
+	/* We expect a CER, it must parse with our dictionary and rules */
+	CHECK_FCT_DO( fd_msg_parse_rules( msg, fd_g_config->cnf_dict, NULL ), /* Parsing failed -- trace details ? */ goto cleanup );
+	
+	fd_msg_dump_walk(FULL, msg);
+	
+	/* Now check we received a CER */
+	CHECK_FCT_DO( fd_msg_hdr ( msg, &hdr ), goto fatal_error );
+	CHECK_PARAMS_DO( (hdr->msg_appl == 0) && (hdr->msg_flags & CMD_FLAG_REQUEST) && (hdr->msg_code == CC_CAPABILITIES_EXCHANGE),
+		{ fd_log_debug("Connection '%s', expecting CER, received something else, closing...\n", fd_cnx_getid(c->conn)); goto cleanup; } );
+	
+	/* Finally, pass the information to the peers module which will handle it next */
+	pthread_cleanup_push((void *)fd_cnx_destroy, c->conn);
+	pthread_cleanup_push((void *)fd_msg_free, msg);
+	CHECK_FCT_DO( fd_peer_handle_newCER( &msg, &c->conn ), goto cleanup );
+	pthread_cleanup_pop(0);
+	pthread_cleanup_pop(0);
+	
+	/* The end, we cleanup the client structure */
+cleanup:
+	/* Unlink the client structure */
+	CHECK_POSIX_DO( pthread_mutex_lock(&s->clients_mtx), goto fatal_error );
+	fd_list_unlink( &c->chain );
+	CHECK_POSIX_DO( pthread_mutex_unlock(&s->clients_mtx), goto fatal_error );
+	
+	/* Destroy the connection object if present */
+	if (c->conn)
+		fd_cnx_destroy(c->conn);
+	
+	/* Cleanup the received buffer if any */
+	free(buf);
+	
+	/* Cleanup the parsed message if any */
+	if (msg) {
+		CHECK_FCT_DO( fd_msg_free(msg), /* continue */);
+	}
+	
+	/* Detach the thread, cleanup the client structure */
+	pthread_detach(pthread_self());
+	free(c);
+	return NULL;
+	
+fatal_error:	/* This has effect to terminate the daemon */
+	CHECK_FCT_DO(fd_event_send(fd_g_config->cnf_main_ev, FDEV_TERMINATE, 0, NULL), );
+	return NULL;
+}
+
+/* The thread managing a server */
+static void * serv_th(void * arg)
+{
+	struct server *s = (struct server *)arg;
+	
+	CHECK_PARAMS_DO(s, goto error);
+	fd_log_threadname ( fd_cnx_getid(s->conn) );
+	s->status = 1;
+	fd_cpu_flush_cache();
+	
+	/* Accept incoming connections */
+	CHECK_FCT_DO( fd_cnx_serv_listen(s->conn), goto error );
+	
+	do {
+		struct client * c = NULL;
+		struct cnxctx * conn = NULL;
+		
+		/* Wait for a new client */
+		CHECK_MALLOC_DO( conn = fd_cnx_serv_accept(s->conn), goto error );
+		
+		TRACE_DEBUG(FULL, "New connection accepted");
+		
+		/* Create a client structure */
+		CHECK_MALLOC_DO( c = malloc(sizeof(struct client)), goto error );
+		memset(c, 0, sizeof(struct client));
+		fd_list_init(&c->chain, c);
+		c->conn = conn;
+		
+		/* Save the client in the list */
+		CHECK_POSIX_DO( pthread_mutex_lock( &s->clients_mtx ), goto error );
+		fd_list_insert_before(&s->clients, &c->chain);
+		CHECK_POSIX_DO( pthread_mutex_unlock( &s->clients_mtx ), goto error );
+
+		/* Start the client thread */
+		CHECK_POSIX_DO( pthread_create( &c->thr, NULL, client_sm, c ), goto error );
+		
+	} while (1);
+	
+error:	
+	if (s)
+		s->status = 2;
+	/* Send error signal to the daemon */
+	TRACE_DEBUG(INFO, "An error occurred in server module! Thread is terminating...");
+	CHECK_FCT_DO(fd_event_send(fd_g_config->cnf_main_ev, FDEV_TERMINATE, 0, NULL), );
+
+	return NULL;
+}
+
+
+/* Create a new server structure */
+static struct server * new_serv( int proto, int secur )
+{
+	struct server * new;
+	
+	/* New server structure */
+	CHECK_MALLOC_DO( new = malloc(sizeof(struct server)), return NULL );
+	
+	memset(new, 0, sizeof(struct server));
+	fd_list_init(&new->chain, new);
+	new->proto = proto;
+	new->secur = secur;
+	CHECK_POSIX_DO( pthread_mutex_init(&new->clients_mtx, NULL), return NULL );
+	fd_list_init(&new->clients, new);
+	
+	return new;
+}
+
+/* Start all the servers */
+int fd_servers_start()
+{
+	struct server * s;
+	
+	int empty_conf_ep = FD_IS_LIST_EMPTY(&fd_g_config->cnf_endpoints);
+	
+	/* SCTP */
+	if (!fd_g_config->cnf_flags.no_sctp) {
+#ifdef DISABLE_SCTP
+		ASSERT(0);
+#else /* DISABLE_SCTP */
+		
+		/* Create the server on unsecure port */
+		CHECK_MALLOC( s = new_serv(IPPROTO_SCTP, 0) );
+		CHECK_MALLOC( s->conn = fd_cnx_serv_sctp(fd_g_config->cnf_port, FD_IS_LIST_EMPTY(&fd_g_config->cnf_endpoints) ? NULL : &fd_g_config->cnf_endpoints) );
+		fd_list_insert_before( &FD_SERVERS, &s->chain );
+		CHECK_POSIX( pthread_create( &s->thr, NULL, serv_th, s ) );
+		
+		/* Create the server on secure port */
+		CHECK_MALLOC( s = new_serv(IPPROTO_SCTP, 1) );
+		CHECK_MALLOC( s->conn = fd_cnx_serv_sctp(fd_g_config->cnf_port_tls, FD_IS_LIST_EMPTY(&fd_g_config->cnf_endpoints) ? NULL : &fd_g_config->cnf_endpoints) );
+		fd_list_insert_before( &FD_SERVERS, &s->chain );
+		CHECK_POSIX( pthread_create( &s->thr, NULL, serv_th, s ) );
+		
+#endif /* DISABLE_SCTP */
+	}
+	
+	/* TCP */
+	if (!fd_g_config->cnf_flags.no_tcp) {
+		
+		if (empty_conf_ep) {
+			/* Bind TCP servers on [0.0.0.0] */
+			if (!fd_g_config->cnf_flags.no_ip4) {
+				
+				CHECK_MALLOC( s = new_serv(IPPROTO_TCP, 0) );
+				CHECK_MALLOC( s->conn = fd_cnx_serv_tcp(fd_g_config->cnf_port, AF_INET, NULL) );
+				fd_list_insert_before( &FD_SERVERS, &s->chain );
+				CHECK_POSIX( pthread_create( &s->thr, NULL, serv_th, s ) );
+
+				CHECK_MALLOC( s = new_serv(IPPROTO_TCP, 1) );
+				CHECK_MALLOC( s->conn = fd_cnx_serv_tcp(fd_g_config->cnf_port_tls, AF_INET, NULL) );
+				fd_list_insert_before( &FD_SERVERS, &s->chain );
+				CHECK_POSIX( pthread_create( &s->thr, NULL, serv_th, s ) );
+			}
+			/* Bind TCP servers on [::] */
+			if (!fd_g_config->cnf_flags.no_ip6) {
+				
+				CHECK_MALLOC( s = new_serv(IPPROTO_TCP, 0) );
+				CHECK_MALLOC( s->conn = fd_cnx_serv_tcp(fd_g_config->cnf_port, AF_INET6, NULL) );
+				fd_list_insert_before( &FD_SERVERS, &s->chain );
+				CHECK_POSIX( pthread_create( &s->thr, NULL, serv_th, s ) );
+
+				CHECK_MALLOC( s = new_serv(IPPROTO_TCP, 1) );
+				CHECK_MALLOC( s->conn = fd_cnx_serv_tcp(fd_g_config->cnf_port_tls, AF_INET6, NULL) );
+				fd_list_insert_before( &FD_SERVERS, &s->chain );
+				CHECK_POSIX( pthread_create( &s->thr, NULL, serv_th, s ) );
+			}
+		} else {
+			/* Create all endpoints -- check flags */
+			struct fd_list * li;
+			for (li = fd_g_config->cnf_endpoints.next; li != &fd_g_config->cnf_endpoints; li = li->next) {
+				struct fd_endpoint * ep = (struct fd_endpoint *)li;
+				sSA * sa = (sSA *) &ep->ss;
+				if (! (ep->flags & EP_FL_CONF))
+					continue;
+				if (fd_g_config->cnf_flags.no_ip4 && (sa->sa_family == AF_INET))
+					continue;
+				if (fd_g_config->cnf_flags.no_ip6 && (sa->sa_family == AF_INET6))
+					continue;
+				
+				CHECK_MALLOC( s = new_serv(IPPROTO_TCP, 0) );
+				CHECK_MALLOC( s->conn = fd_cnx_serv_tcp(fd_g_config->cnf_port, sa->sa_family, ep) );
+				fd_list_insert_before( &FD_SERVERS, &s->chain );
+				CHECK_POSIX( pthread_create( &s->thr, NULL, serv_th, s ) );
+
+				CHECK_MALLOC( s = new_serv(IPPROTO_TCP, 1) );
+				CHECK_MALLOC( s->conn = fd_cnx_serv_tcp(fd_g_config->cnf_port_tls, sa->sa_family, ep) );
+				fd_list_insert_before( &FD_SERVERS, &s->chain );
+				CHECK_POSIX( pthread_create( &s->thr, NULL, serv_th, s ) );
+			}
+		}
+	}
+	
+	/* Now, if we still have not got the list of local adresses, try to read it from the kernel directly */
+	if (FD_IS_LIST_EMPTY(&fd_g_config->cnf_endpoints)) {
+		CHECK_FCT(fd_cnx_get_local_eps(&fd_g_config->cnf_endpoints));
+		if (FD_IS_LIST_EMPTY(&fd_g_config->cnf_endpoints)) {
+			TRACE_DEBUG(INFO, "Unable to find the addresses of the local system. Please use \"ListenOn\" parameter in the configuration.");
+			return EINVAL;
+		}
+	}
+	if (TRACE_BOOL(FULL)){
+		fd_log_debug("  Local server address(es) :\n");
+		fd_ep_dump( 5, &fd_g_config->cnf_endpoints );
+	}
+	return 0;
+}
+
+/* Terminate all the servers */
+int fd_servers_stop()
+{
+	TRACE_ENTRY("");
+	
+	TRACE_DEBUG(INFO, "Shutting down server sockets...");
+	
+	/* Loop on all servers */
+	while (!FD_IS_LIST_EMPTY(&FD_SERVERS)) {
+		struct server * s = (struct server *)(FD_SERVERS.next);
+		
+		/* Lock client list now */
+		CHECK_FCT_DO( pthread_mutex_lock(&s->clients_mtx), /* continue anyway */);
+		
+		/* cancel thread */
+		CHECK_FCT_DO( fd_thr_term(&s->thr), /* continue */);
+		
+		/* destroy server connection context */
+		fd_cnx_destroy(s->conn);
+		
+		/* cancel and destroy all clients */
+		while (!FD_IS_LIST_EMPTY(&s->clients)) {
+			struct client * c = (struct client *)(s->clients.next);
+			
+			/* Destroy client's thread */
+			CHECK_FCT_DO( fd_thr_term(&c->thr), /* continue */);
+			
+			/* Destroy client's connection */
+			fd_cnx_destroy(c->conn);
+			
+			/* Unlink and free the client */
+			fd_list_unlink(&c->chain);
+			free(c);
+		}
+		/* Unlock & destroy */
+		CHECK_FCT_DO( pthread_mutex_unlock(&s->clients_mtx), /* continue anyway */);
+		CHECK_FCT_DO( pthread_mutex_destroy(&s->clients_mtx), /* continue */);
+		
+		/* Now destroy the server object */
+		fd_list_unlink(&s->chain);
+		free(s);
+	}
+	
+	/* We're done! */
+	return 0;
+}
Index: libfdcore/tcp.c
===================================================================
--- libfdcore/tcp.c	(revision 658)
+++ libfdcore/tcp.c	(revision 658)
@@ -0,0 +1,183 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@nict.go.jp>							 *
+*													 *
+* Copyright (c) 2010, 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 "fdcore-internal.h"
+#include "cnxctx.h"
+
+#include <netinet/tcp.h>
+#include <netinet/ip6.h>
+#include <sys/socket.h>
+
+/* Set the socket options for TCP sockets, before bind is called */
+static int fd_tcp_setsockopt(int family, int sk)
+{
+	int ret = 0;
+	int opt;
+	
+	/* Clear the NODELAY option in case it was set, as requested by rfc3539#section-3.2 */
+	/* Note that this is supposed to be the default, so we could probably remove this call ... */
+	opt = 0;
+	ret = setsockopt(sk, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof(opt));
+	if (ret != 0) {
+		ret = errno;
+		TRACE_DEBUG(INFO, "Unable to set the socket TCP_NODELAY option: %s", strerror(ret));
+		return ret;
+	}
+	
+	/* Under Linux, we may also set the TCP_CONGESTION option to one of the following strings:
+	    - reno (default)
+	    - bic
+	    - cubic
+	    - highspeed
+	    - htcp
+	    - hybla
+	    - illinois
+	    - lp
+	    - scalable
+	    - vegas
+	    - veno
+	    - westwood
+	    - yeah
+	*/
+	
+	/* In case of v6 address, force the v6only option, we use a different socket for v4 */
+	#ifdef IPV6_V6ONLY
+	if (family == AF_INET6) {
+		opt = 1;
+		CHECK_SYS(setsockopt(sk, IPPROTO_IPV6, IPV6_V6ONLY, &opt, sizeof(opt)));
+	}
+	#endif /* IPV6_V6ONLY */
+	
+#ifdef DEBUG
+	{
+		opt = 1;
+		CHECK_SYS(  setsockopt(sk, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt))  );
+	}
+#endif /* DEBUG	*/
+	
+	
+	/* There are also others sockopt that can be set, but nothing useful for us AFAICT */
+	
+	return 0;
+}
+
+/* Create a socket server and bind it */
+int fd_tcp_create_bind_server( int * sock, sSA * sa, socklen_t salen )
+{
+	TRACE_ENTRY("%p %p %d", sock, sa, salen);
+	
+	CHECK_PARAMS(  sock && sa  );
+	
+	/* Create the socket */
+	CHECK_SYS(  *sock = socket(sa->sa_family, SOCK_STREAM, IPPROTO_TCP)  );
+
+	/* Set the socket options */
+	CHECK_FCT(  fd_tcp_setsockopt(sa->sa_family, *sock)  );
+	
+	/* Bind the socket */
+	CHECK_SYS(  bind( *sock, sa, salen )  );
+			
+	/* We're done */
+	return 0;
+}
+
+/* Allow clients connections on server sockets */
+int fd_tcp_listen( int sock )
+{
+	TRACE_ENTRY("%d", sock);
+	CHECK_SYS( listen(sock, 5) );
+	return 0;
+}
+
+/* Create a client socket and connect to remote server */
+int fd_tcp_client( int *sock, sSA * sa, socklen_t salen )
+{
+	int ret = 0;
+	int s;
+	
+	TRACE_ENTRY("%p %p %d", sock, sa, salen);
+	CHECK_PARAMS( sock && (*sock <= 0) && sa && salen );
+	
+	/* Create the socket */
+	CHECK_SYS(  s = socket(sa->sa_family, SOCK_STREAM, IPPROTO_TCP)  );
+	
+	/* Cleanup if we are cancelled */
+	pthread_cleanup_push(fd_cleanup_socket, &s);
+	
+	/* Set the socket options */
+	CHECK_FCT(  fd_tcp_setsockopt(sa->sa_family, s)  );
+	
+	TRACE_DEBUG_sSA(FULL, "Attempting TCP connection with peer: ", sa, NI_NUMERICHOST | NI_NUMERICSERV, "..." );
+	
+	/* Try connecting to the remote address */
+	ret = connect(s, sa, salen);
+	
+	pthread_cleanup_pop(0);
+	
+	if (ret < 0) {
+		int lvl;
+		switch (ret = errno) {
+			case ECONNREFUSED:
+			
+				/* "Normal" errors */
+				lvl = FULL;
+				break;
+			default:
+				lvl = INFO;
+		}
+		/* Some errors are expected, we log at different level */
+		TRACE_DEBUG( lvl, "connect returned an error: %s", strerror(ret));
+		CHECK_SYS_DO( close(s), /* continue */ );
+		*sock = -1;
+		return ret;
+	}
+	
+	/* Done! */
+	*sock = s;
+	return ret;
+}
+
+/* Get the remote name of a TCP socket */
+int fd_tcp_get_remote_ep(int sock, sSS * ss, socklen_t *sl)
+{
+	TRACE_ENTRY("%d %p %p", sock, ss, sl);
+	CHECK_PARAMS( ss && sl );
+	
+	*sl = sizeof(sSS);
+	CHECK_SYS(getpeername(sock, (sSA *)ss, sl));
+	
+	return 0;
+}
+
Index: libfdproto/CMakeLists.txt
===================================================================
--- libfdproto/CMakeLists.txt	(revision 658)
+++ libfdproto/CMakeLists.txt	(revision 658)
@@ -0,0 +1,40 @@
+# Name of the subproject
+Project("libfdproto" C)
+
+# List of source files for the library
+SET(LFDPROTO_SRC
+	fdproto-internal.h
+	dictionary.c
+	dispatch.c
+	fifo.c
+	init.c
+	lists.c
+	log.c
+	messages.c
+	rt_data.c
+	sessions.c
+	)
+
+# Save the list of files for testcases in the core's directory
+SET(LFDPROTO_SRC ${LFDPROTO_SRC} PARENT_SCOPE)
+
+# Build as a shared library
+ADD_LIBRARY(libfdproto SHARED ${LFDPROTO_SRC})
+
+# Avoid the liblib name, and set the version
+SET_TARGET_PROPERTIES(libfdproto PROPERTIES 
+	OUTPUT_NAME "fdproto"
+	SOVERSION ${FD_PROJECT_VERSION_API}
+	VERSION ${FD_PROJECT_VERSION_MAJOR}.${FD_PROJECT_VERSION_MINOR}.${FD_PROJECT_VERSION_REV})
+
+# The library itself needs other libraries 
+TARGET_LINK_LIBRARIES(libfdproto ${LFDPROTO_LIBS})
+
+
+####
+## INSTALL section ##
+
+INSTALL(TARGETS libfdproto
+	LIBRARY DESTINATION ${INSTALL_LIBRARY_SUFFIX}
+	COMPONENT freeDiameter-common)
+
Index: libfdproto/dictionary.c
===================================================================
--- libfdproto/dictionary.c	(revision 658)
+++ libfdproto/dictionary.c	(revision 658)
@@ -0,0 +1,2015 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@nict.go.jp>							 *
+*													 *
+* Copyright (c) 2010, 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 "fdproto-internal.h"
+
+/* Names of the base types */
+const char * type_base_name[] = { /* must keep in sync with dict_avp_basetype */
+	"GROUPED", 	/* AVP_TYPE_GROUPED */
+	"OCTETSTRING", 	/* AVP_TYPE_OCTETSTRING */
+	"INTEGER32", 	/* AVP_TYPE_INTEGER32 */
+	"INTEGER64", 	/* AVP_TYPE_INTEGER64 */
+	"UNSIGNED32", 	/* AVP_TYPE_UNSIGNED32 */
+	"UNSIGNED64", 	/* AVP_TYPE_UNSIGNED64 */
+	"FLOAT32", 	/* AVP_TYPE_FLOAT32 */
+	"FLOAT64"	/* AVP_TYPE_FLOAT64 */
+	};
+
+/* The number of lists in an object */
+#define NB_LISTS_PER_OBJ	3
+
+/* Some eye catchers definitions */
+#define OBJECT_EYECATCHER	(0x0b13c7)
+#define DICT_EYECATCHER		(0x00d1c7)
+
+/* Definition of the dictionary objects */
+struct dict_object {
+	enum dict_object_type	type;	/* What type of object is this? */
+	int			objeyec;/* eyecatcher for this object */
+	int			typeyec;/* eyecatcher for this type of object */
+	struct dictionary	*dico;  /* The dictionary this object belongs to */
+	
+	union {
+		struct dict_vendor_data		vendor;
+		struct dict_application_data	application;
+		struct dict_type_data		type;
+		struct dict_enumval_data	enumval;
+		struct dict_avp_data		avp;
+		struct dict_cmd_data		cmd;
+		struct dict_rule_data		rule;
+	} data;				/* The data of this object */
+	
+	struct dict_object *	parent; /* The parent of this object, if any */
+	
+	struct fd_list		list[NB_LISTS_PER_OBJ];/* used to chain objects.*/
+	/* More information about the lists :
+	
+	 - the use for each list depends on the type of object. See detail bellow.
+	 
+	 - a sentinel for a list has its 'o' field cleared. (this is the criteria to detect end of a loop)
+	 
+	 - The lists are always ordered. The criteria are described bellow. the functions to order them are referenced in dict_obj_info
+	 
+	 - The dict_lock must be held for any list operation.
+	 
+	 => VENDORS:
+	 list[0]: list of the vendors, ordered by their id. The sentinel is g_dict_vendors (vendor with id 0)
+	 list[1]: sentinel for the list of AVPs from this vendor, ordered by AVP code.
+	 list[2]: sentinel for the list of AVPs from this vendor, ordered by AVP name.
+	 
+	 => APPLICATIONS:
+	 list[0]: list of the applications, ordered by their id. The sentinel is g_dict_applications (application with id 0)
+	 list[1]: not used
+	 list[2]: not used.
+	 
+	 => TYPES:
+	 list[0]: list of the types, ordered by their names. The sentinel is g_list_types.
+	 list[1]: sentinel for the type_enum list of this type, ordered by their constant name.
+	 list[2]: sentinel for the type_enum list of this type, ordered by their constant value.
+	 
+	 => TYPE_ENUMS:
+	 list[0]: list of the contants for a given type, ordered by the constant name. Sentinel is a (list[1]) element of a TYPE object.
+	 list[1]: list of the contants for a given type, ordered by the constant value. Sentinel is a (list[2]) element of a TYPE object.
+	 list[2]: not used
+	 
+	 => AVPS:
+	 list[0]: list of the AVP from a given vendor, ordered by avp code. Sentinel is a list[1] element of a VENDOR object.
+	 list[1]: list of the AVP from a given vendor, ordered by avp name. Sentinel is a list[2] element of a VENDOR object.
+	 list[2]: sentinel for the rule list that apply to this AVP.
+	 
+	 => COMMANDS:
+	 list[0]: list of the commands, ordered by their names. The sentinel is g_list_cmd_name.
+	 list[1]: list of the commands, ordered by their command code and 'R' flag. The sentinel is g_list_cmd_code.
+	 list[2]: sentinel for the rule list that apply to this command.
+	 
+	 => RULES:
+	 list[0]: list of the rules for a given (grouped) AVP or Command, ordered by the AVP name to which they refer. sentinel is list[2] of a command or (grouped) avp.
+	 list[1]: not used
+	 list[2]: not used.
+	 
+	 */
+	 
+	 /* Sentinel for the dispatch callbacks */
+	 struct fd_list		disp_cbs;
+	
+};
+
+/* Definition of the dictionary structure */
+struct dictionary {
+	int		 	dict_eyec;		/* Eye-catcher for the dictionary (DICT_EYECATCHER) */
+	
+	pthread_rwlock_t 	dict_lock;		/* The global rwlock for the dictionary */
+	
+	struct dict_object	dict_vendors;		/* Sentinel for the list of vendors, corresponding to vendor 0 */
+	struct dict_object	dict_applications;	/* Sentinel for the list of applications, corresponding to app 0 */
+	struct fd_list		dict_types;		/* Sentinel for the list of types */
+	struct fd_list		dict_cmd_name;		/* Sentinel for the list of commands, ordered by names */
+	struct fd_list		dict_cmd_code;		/* Sentinel for the list of commands, ordered by codes */
+	
+	struct dict_object	dict_cmd_error;		/* Special command object for answers with the 'E' bit set */
+	
+	int			dict_count[DICT_TYPE_MAX + 1]; /* Number of objects of each type */
+};
+
+/* Forward declarations of dump functions */
+static void dump_vendor_data 	  ( void * data );
+static void dump_application_data ( void * data );
+static void dump_type_data 	  ( void * data );
+  /* the dump function for enum has a different prototype since it need the datatype */
+static void dump_avp_data 	  ( void * data );
+static void dump_command_data 	  ( void * data );
+static void dump_rule_data 	  ( void * data );
+
+/* Forward declarations of search functions */
+static int search_vendor 	( struct dictionary * dict, int criteria, void * what, struct dict_object **result );
+static int search_application   ( struct dictionary * dict, int criteria, void * what, struct dict_object **result );
+static int search_type 		( struct dictionary * dict, int criteria, void * what, struct dict_object **result );
+static int search_enumval 	( struct dictionary * dict, int criteria, void * what, struct dict_object **result );
+static int search_avp		( struct dictionary * dict, int criteria, void * what, struct dict_object **result );
+static int search_cmd		( struct dictionary * dict, int criteria, void * what, struct dict_object **result );
+static int search_rule		( struct dictionary * dict, int criteria, void * what, struct dict_object **result );
+
+/* The following array contains lot of data about the different types of objects, for automated handling */
+static struct {
+	enum dict_object_type 	type; 		/* information for this type */
+	char *			name;		/* string describing this object, for debug */
+	size_t			datasize;	/* The size of the data structure */
+	int			parent;		/* 0: never; 1: may; 2: must */
+	enum dict_object_type	parenttype;	/* The type of the parent, when relevant */
+	int			eyecatcher;	/* A kind of signature for this object */
+	void 		      (*dump_data)(void * data );	/* The function to dump the data section */
+	int 		      (*search_fct)(struct dictionary * dict, int criteria, void * what, struct dict_object **result );;	/* The function to search an object of this type */
+	int			haslist[NB_LISTS_PER_OBJ];	/* Tell if this list is used */
+} dict_obj_info[] = { { 0, "(error)", 0, 0, 0, 0, NULL, NULL, {0, 0, 0} }
+
+	/* type			 name		datasize		   	  parent  	parenttype 
+			eyecatcher		dump_data	  	search_fct,		haslist[] 	*/
+
+	,{ DICT_VENDOR,		"VENDOR",	sizeof(struct dict_vendor_data),	0, 	0,
+			OBJECT_EYECATCHER + 1, 	dump_vendor_data, 	search_vendor, 		{ 1, 0, 0 } }
+	
+	,{ DICT_APPLICATION,	"APPLICATION",	sizeof(struct dict_application_data),	1, 	DICT_VENDOR,
+			OBJECT_EYECATCHER + 2,	dump_application_data,	search_application,	{ 1, 0, 0 } }
+	
+	,{ DICT_TYPE,		"TYPE",		sizeof(struct dict_type_data),		1, 	DICT_APPLICATION,
+			OBJECT_EYECATCHER + 3,	dump_type_data,		search_type,		{ 1, 0, 0 } }
+	
+	,{ DICT_ENUMVAL,	"ENUMVAL",	sizeof(struct dict_enumval_data),	2, 	DICT_TYPE,
+			OBJECT_EYECATCHER + 4,	NULL,			search_enumval,	{ 1, 1, 0 } }
+	
+	,{ DICT_AVP,		"AVP",		sizeof(struct dict_avp_data),		1, 	DICT_TYPE,
+			OBJECT_EYECATCHER + 5,	dump_avp_data,		search_avp,		{ 1, 1,	0 } }
+	
+	,{ DICT_COMMAND,	"COMMAND",	sizeof(struct dict_cmd_data),		1, 	DICT_APPLICATION,
+			OBJECT_EYECATCHER + 6,	dump_command_data,	search_cmd,		{ 1, 1, 0 } }
+	
+	,{ DICT_RULE,		"RULE",		sizeof(struct dict_rule_data),		2, 	-1 /* special case: grouped avp or command */,
+			OBJECT_EYECATCHER + 7,	dump_rule_data,		search_rule,		{ 1, 0, 0 } }
+	
+};
+	
+/* Macro to verify a "type" value */
+#define CHECK_TYPE( type ) ( ((type) > 0) && ((type) <= DICT_TYPE_MAX) )
+
+/* Cast macro */
+#define _O( object ) ((struct dict_object *) (object))
+
+/* Get information line for a given object */
+#define _OBINFO(object) (dict_obj_info[CHECK_TYPE(_O(object)->type) ? _O(object)->type : 0])
+
+
+
+
+/*******************************************************************************************************/
+/*******************************************************************************************************/
+/*                                                                                                     */
+/*                                  Objects management                                                 */
+/*                                                                                                     */
+/*******************************************************************************************************/
+/*******************************************************************************************************/
+
+/* Functions to manage the objects creation and destruction. */
+
+/* Duplicate a string inplace */
+#define DUP_string( str ) {				\
+	char * __str = (str);				\
+	CHECK_MALLOC( (str) = strdup(__str) );		\
+}
+	
+/* Initialize an object */
+static void init_object( struct dict_object * obj, enum dict_object_type type )
+{
+	int i;
+	
+	TRACE_ENTRY("%p %d", obj, type);
+	
+	/* Clean the object first */
+	memset ( obj, 0, sizeof(struct dict_object));
+	
+	CHECK_PARAMS_DO(  CHECK_TYPE(type),  return  );
+
+	obj->type = type;
+	obj->objeyec = OBJECT_EYECATCHER;
+	obj->typeyec = _OBINFO(obj).eyecatcher;
+
+	/* We don't initialize the data nor the parent here */
+	
+	/* Now init the lists */
+	for (i=0; i<NB_LISTS_PER_OBJ; i++) {
+		if (_OBINFO(obj).haslist[i] != 0) 
+			fd_list_init(&obj->list[i], obj);
+		else
+			fd_list_init(&obj->list[i], NULL);
+	}
+	
+	fd_list_init(&obj->disp_cbs, NULL);
+}
+
+/* Initialize the "data" part of an object */
+static int init_object_data(void * dest, void * source, enum dict_object_type type)
+{
+	TRACE_ENTRY("%p %p %d", dest, source, type);
+	CHECK_PARAMS( dest && source && CHECK_TYPE(type) );
+	
+	/* Generic: copy the full data structure */	
+	memcpy( dest, source, dict_obj_info[type].datasize );
+	
+	/* Then strings must be duplicated, not copied */
+	/* This function might be simplified by always defining the "name" field as the first field of the structures, but... it's error-prone */
+	switch (type) {
+		case DICT_VENDOR:
+			DUP_string( ((struct dict_vendor_data *)dest)->vendor_name );
+			break;
+		
+		case DICT_APPLICATION:
+			DUP_string( ((struct dict_application_data *)dest)->application_name );
+			break;
+			
+		case DICT_TYPE:
+			DUP_string( ((struct dict_type_data *)dest)->type_name );
+			break;
+			
+		case DICT_ENUMVAL:
+			DUP_string( ((struct dict_enumval_data *)dest)->enum_name );
+			break;
+
+		case DICT_AVP:
+			DUP_string( ((struct dict_avp_data *)dest)->avp_name );
+			break;
+			
+		case DICT_COMMAND:
+			DUP_string( ((struct dict_cmd_data *)dest)->cmd_name );
+			break;
+		
+		default:
+			/* Nothing to do for RULES */
+			;
+	}
+	
+	return 0;
+}
+
+/* Check that an object is valid (1: OK, 0: error) */
+static int verify_object( struct dict_object * obj )
+{
+	TRACE_ENTRY("%p", obj);
+	
+	CHECK_PARAMS_DO(  obj
+			&& (obj->objeyec == OBJECT_EYECATCHER)
+			&& CHECK_TYPE(obj->type)
+			&& (obj->typeyec == dict_obj_info[obj->type].eyecatcher),
+		{
+			if (obj) {
+				TRACE_DEBUG(FULL, "Invalid object : %p\n"
+						  "     obj->objeyec : %x / %x\n"
+						  "     obj->type    : %d\n"
+						  "     obj->objeyec : %x / %x\n"
+						  "     obj->typeyec : %x / %x", 
+						obj,
+						obj->objeyec, OBJECT_EYECATCHER,
+						obj->type,
+						obj->objeyec, OBJECT_EYECATCHER,
+						obj->typeyec, _OBINFO(obj).eyecatcher);
+			} else {
+				TRACE_DEBUG(FULL, "Invalid object : NULL pointer");
+			}
+			return 0;
+		}  );
+	
+	/* The object is probably valid. */
+	return 1;
+}
+
+/* Free the data associated to an object */
+static void destroy_object_data(struct dict_object * obj)
+{
+	/* TRACE_ENTRY("%p", obj); */
+	
+	switch (obj->type) {
+		case DICT_VENDOR:
+			free( obj->data.vendor.vendor_name );
+			break;
+		
+		case DICT_APPLICATION:
+			free( obj->data.application.application_name );
+			break;
+			
+		case DICT_TYPE:
+			free( obj->data.type.type_name );
+			break;
+			
+		case DICT_ENUMVAL:
+			free( obj->data.enumval.enum_name );
+			break;
+
+		case DICT_AVP:
+			free( obj->data.avp.avp_name );
+			break;
+			
+		case DICT_COMMAND:
+			free( obj->data.cmd.cmd_name );
+			break;
+		
+		default:
+			/* nothing to do */
+			;
+	}
+}
+
+/* Forward declaration */
+static void destroy_object(struct dict_object * obj);
+
+/* Destroy all objects in a list - the lock must be held */
+static void destroy_list(struct fd_list * head) 
+{
+	/* TRACE_ENTRY("%p", head); */
+	
+	/* loop in the list */
+	while (!FD_IS_LIST_EMPTY(head))
+	{
+		/* When destroying the object, it is unlinked from the list */
+		destroy_object(_O(head->next->o));
+	}
+}
+	
+/* Free an object and its sublists */
+static void destroy_object(struct dict_object * obj)
+{
+	int i;
+	
+	/* TRACE_ENTRY("%p", obj); */
+	
+	/* Update global count */
+	if (obj->dico) 
+		obj->dico->dict_count[obj->type]--;
+	
+	/* Mark the object as invalid */
+	obj->objeyec = 0xdead;
+	
+	/* First, destroy the data associated to the object */
+	destroy_object_data(obj);
+	
+	for (i=0; i<NB_LISTS_PER_OBJ; i++) {
+		if (_OBINFO(obj).haslist[i])
+			/* unlink the element from the list */
+			fd_list_unlink( &obj->list[i] );
+		else
+			/* This is either a sentinel or unused (=emtpy) list, let's destroy it */
+			destroy_list( &obj->list[i] );
+	}
+	
+	/* Unlink all elements from the dispatch list; they will be freed when callback is unregistered */
+	CHECK_POSIX_DO( pthread_rwlock_wrlock(&fd_disp_lock), /* continue */ );
+	while (!FD_IS_LIST_EMPTY(&obj->disp_cbs)) {
+		fd_list_unlink( obj->disp_cbs.next );
+	}
+	CHECK_POSIX_DO( pthread_rwlock_unlock(&fd_disp_lock), /* continue */ );
+	
+	/* Last, destroy the object */
+	free(obj);
+}
+
+/*******************************************************************************************************/
+/*******************************************************************************************************/
+/*                                                                                                     */
+/*                                  Compare functions                                                  */
+/*                                                                                                     */
+/*******************************************************************************************************/
+/*******************************************************************************************************/
+
+/* Compare two values */
+#define ORDER_scalar( i1, i2 ) \
+	((i1 < i2 ) ? -1 : ( i1 > i2 ? 1 : 0 )) 
+
+
+/* Compare two vendor objects by their id (checks already performed) */
+static int order_vendor_by_id ( struct dict_object *o1, struct dict_object *o2 )
+{
+	TRACE_ENTRY("%p %p", o1, o2);
+	
+	return ORDER_scalar( o1->data.vendor.vendor_id, o2->data.vendor.vendor_id );
+}
+
+/* Compare two application objects by their id (checks already performed) */
+static int order_appli_by_id  ( struct dict_object *o1, struct dict_object *o2 )
+{
+	TRACE_ENTRY("%p %p", o1, o2);
+	
+	return ORDER_scalar( o1->data.application.application_id, o2->data.application.application_id );
+}
+
+/* Compare two type objects by their name (checks already performed) */
+static int order_type_by_name ( struct dict_object *o1, struct dict_object *o2 )
+{
+	TRACE_ENTRY("%p %p", o1, o2);
+	
+	return strcmp( o1->data.type.type_name, o2->data.type.type_name );
+}
+
+/* Compare two type_enum objects by their names (checks already performed) */
+static int order_enum_by_name ( struct dict_object *o1, struct dict_object *o2 )
+{
+	TRACE_ENTRY("%p %p", o1, o2);
+	
+	return strcmp( o1->data.enumval.enum_name, o2->data.enumval.enum_name );
+}
+
+/* Compare two type_enum objects by their values (checks already performed) */
+static int order_enum_by_val  ( struct dict_object *o1, struct dict_object *o2 )
+{
+	size_t oslen;
+	int cmp = 0;
+	
+	TRACE_ENTRY("%p %p", o1, o2);
+	
+	/* The comparison function depends on the type of data */
+	switch ( o1->parent->data.type.type_base ) {
+		case AVP_TYPE_OCTETSTRING:
+			oslen = o1->data.enumval.enum_value.os.len;
+			if (o2->data.enumval.enum_value.os.len < oslen)
+				oslen = o2->data.enumval.enum_value.os.len;
+			cmp = memcmp(o1->data.enumval.enum_value.os.data, o2->data.enumval.enum_value.os.data, oslen );
+			return (cmp ? cmp : ORDER_scalar(o1->data.enumval.enum_value.os.len,o2->data.enumval.enum_value.os.len));
+		
+		case AVP_TYPE_INTEGER32:
+			return ORDER_scalar( o1->data.enumval.enum_value.i32, o2->data.enumval.enum_value.i32 );
+
+		case AVP_TYPE_INTEGER64:
+			return ORDER_scalar( o1->data.enumval.enum_value.i64, o2->data.enumval.enum_value.i64 );
+
+		case AVP_TYPE_UNSIGNED32:
+			return ORDER_scalar( o1->data.enumval.enum_value.u32, o2->data.enumval.enum_value.u32 );
+
+		case AVP_TYPE_UNSIGNED64:
+			return ORDER_scalar( o1->data.enumval.enum_value.u64, o2->data.enumval.enum_value.u64 );
+
+		case AVP_TYPE_FLOAT32:
+			return ORDER_scalar( o1->data.enumval.enum_value.f32, o2->data.enumval.enum_value.f32 );
+
+		case AVP_TYPE_FLOAT64:
+			return ORDER_scalar( o1->data.enumval.enum_value.f64, o2->data.enumval.enum_value.f64 );
+
+		case AVP_TYPE_GROUPED:
+		default:
+			ASSERT(0);
+	}
+	return 0;
+}
+
+/* Compare two avp objects by their codes (checks already performed) */
+static int order_avp_by_code  ( struct dict_object *o1, struct dict_object *o2 )
+{
+	TRACE_ENTRY("%p %p", o1, o2);
+	
+	return ORDER_scalar( o1->data.avp.avp_code, o2->data.avp.avp_code );
+}
+
+/* Compare two avp objects by their names (checks already performed) */
+static int order_avp_by_name  ( struct dict_object *o1, struct dict_object *o2 )
+{
+	TRACE_ENTRY("%p %p", o1, o2);
+	
+	return strcmp( o1->data.avp.avp_name, o2->data.avp.avp_name );
+}
+
+/* Compare two command objects by their names (checks already performed) */
+static int order_cmd_by_name  ( struct dict_object *o1, struct dict_object *o2 )
+{
+	TRACE_ENTRY("%p %p", o1, o2);
+	
+	return strcmp( o1->data.cmd.cmd_name, o2->data.cmd.cmd_name );
+}
+
+/* Compare two command objects by their codes and flags (request or answer) (checks already performed) */
+static int order_cmd_by_codefl( struct dict_object *o1, struct dict_object *o2 )
+{
+	uint8_t fl1, fl2;
+	int cmp = 0;
+	
+	TRACE_ENTRY("%p %p", o1, o2);
+	
+	cmp = ORDER_scalar( o1->data.cmd.cmd_code, o2->data.cmd.cmd_code );
+	if (cmp) 
+		return cmp;
+	
+	/* Same command code, we must compare the value of the 'R' flag */
+	fl1 = o1->data.cmd.cmd_flag_val & CMD_FLAG_REQUEST;
+	fl2 = o2->data.cmd.cmd_flag_val & CMD_FLAG_REQUEST;
+	
+	/* We want requests first, so we reverse the operators here */
+	return ORDER_scalar(fl2, fl1);
+		
+}
+
+/* Compare two rule object by the AVP vendor & code that they refer (checks already performed) */
+static int order_rule_by_avpn ( struct dict_object *o1, struct dict_object *o2 )
+{
+	TRACE_ENTRY("%p %p", o1, o2);
+	
+	return ORDER_scalar(o1->data.rule.rule_avp->data.avp.avp_vendor, o2->data.rule.rule_avp->data.avp.avp_vendor) 
+		?: ORDER_scalar(o1->data.rule.rule_avp->data.avp.avp_code, o2->data.rule.rule_avp->data.avp.avp_code) ;
+}
+
+/*******************************************************************************************************/
+/*******************************************************************************************************/
+/*                                                                                                     */
+/*                                  Search  functions                                                  */
+/*                                                                                                     */
+/*******************************************************************************************************/
+/*******************************************************************************************************/
+
+/* Functions used to search for objects in the lists, according to some criteria */
+
+/* On a general note, if result is not NULL, ENOENT is not returned but *result is NULL. */
+
+/* The following macros assume that "what", "ret", "result" (variables), and "end" (label) exist
+in the local context where they are called. They are meant to be called only from the functions that follow. */
+
+/* For searchs of type "xxx_OF_xxx": children's parent or default parent */
+#define SEARCH_childs_parent( type_of_child, default_parent ) {			\
+	struct dict_object *__child = (struct dict_object *) what;		\
+	CHECK_PARAMS_DO( verify_object(__child) && 				\
+		(__child->type == (type_of_child)), 				\
+		   {  ret = EINVAL; goto end;  }  );				\
+	ret = 0;								\
+	if (result)								\
+		*result = (__child->parent ? __child->parent :(default_parent));\
+}
+
+/* For search of strings in lists. isindex= 1 if the string is the ordering key of the list */
+#define SEARCH_string( str, sentinel, datafield, isindex ) {			\
+	char * __str = (char *) str;						\
+	int __cmp;								\
+	struct fd_list * __li;							\
+	ret = 0;								\
+	for  (__li = (sentinel)->next; __li != (sentinel); __li = __li->next) {	\
+		__cmp = strcmp(__str, _O(__li->o)->data. datafield );		\
+		if (__cmp == 0) {						\
+			if (result)						\
+				*result = _O(__li->o);				\
+			goto end;						\
+		}								\
+		if ((isindex) && (__cmp < 0))					\
+			break;							\
+	}									\
+	if (result)								\
+		*result = NULL;							\
+	else									\
+		ret = ENOENT;							\
+}
+
+/* For search of octetstrings in lists (not \0 terminated). */
+#define SEARCH_ocstring( ostr, length, sentinel, osdatafield, isindex ) {	\
+	unsigned char * __ostr = (unsigned char *) ostr;			\
+	int __cmp;								\
+	size_t __len;								\
+	struct fd_list * __li;							\
+	ret = 0;								\
+	for  (__li = (sentinel); __li->next != (sentinel); __li = __li->next) {	\
+		__len = _O(__li->next->o)->data. osdatafield .len;		\
+		if ( __len > (length) )						\
+			__len = (length);					\
+		__cmp = memcmp(__ostr, 						\
+				_O(__li->next->o)->data. osdatafield .data, 	\
+				__len);						\
+		if (! __cmp) {							\
+			__cmp = ORDER_scalar( length,				\
+				_O(__li->next->o)->data. osdatafield .len); 	\
+		}								\
+		if (__cmp == 0) {						\
+			if (result)						\
+				*result = _O(__li->next->o);			\
+			goto end;						\
+		}								\
+		if ((isindex) && (__cmp < 0))					\
+			break;							\
+	}									\
+	if (result)								\
+		*result = NULL;							\
+	else									\
+		ret = ENOENT;							\
+}
+
+/* For search of AVP name in rule lists. */
+#define SEARCH_ruleavpname( str, sentinel ) {					\
+	char * __str = (char *) str;						\
+	int __cmp;								\
+	struct fd_list * __li;							\
+	ret = 0;								\
+	for  (__li = (sentinel); __li->next != (sentinel); __li = __li->next) {	\
+		__cmp = strcmp(__str, 						\
+		  _O(__li->next->o)->data.rule.rule_avp->data.avp.avp_name);\
+		if (__cmp == 0) {						\
+			if (result)						\
+				*result = _O(__li->next->o);			\
+			goto end;						\
+		}								\
+		if (__cmp < 0)							\
+			break;							\
+	}									\
+	if (result)								\
+		*result = NULL;							\
+	else									\
+		ret = ENOENT;							\
+}
+
+/* For search of scalars in lists. isindex= 1 if the value is the ordering key of the list */
+#define SEARCH_scalar( value, sentinel, datafield, isindex, defaultobj ) {	\
+	int __cmp;								\
+	struct fd_list * __li;							\
+	ret = 0;								\
+	if (  ((defaultobj) != NULL) 						\
+		   && (_O(defaultobj)->data. datafield  == value)) {		\
+		if (result)							\
+			*result = _O(defaultobj);				\
+		goto end;							\
+	}									\
+	for  (__li = (sentinel); __li->next != (sentinel); __li = __li->next) {	\
+		__cmp= ORDER_scalar(value, _O(__li->next->o)->data. datafield );\
+		if (__cmp == 0) {						\
+			if (result)						\
+				*result = _O(__li->next->o);			\
+			goto end;						\
+		}								\
+		if ((isindex) && (__cmp < 0))					\
+			break;							\
+	}									\
+	if (result)								\
+		*result = NULL;							\
+	else									\
+		ret = ENOENT;							\
+}
+
+/* For search of commands in lists by code and flag. R_flag_val = 0 or CMD_FLAG_REQUEST */
+#define SEARCH_codefl( value, R_flag_val, sentinel) {					\
+	int __cmp;								\
+	struct fd_list * __li;							\
+	ret = 0;								\
+	for  (	  __li = (sentinel);	 					\
+		  __li->next != (sentinel); 					\
+		  __li = __li->next) {						\
+		__cmp = ORDER_scalar(value, 					\
+				_O(__li->next->o)->data.cmd.cmd_code );		\
+		if (__cmp == 0) {						\
+			uint8_t __mask, __val;					\
+			__mask = _O(__li->next->o)->data.cmd.cmd_flag_mask;	\
+			__val  = _O(__li->next->o)->data.cmd.cmd_flag_val;	\
+			if ( ! (__mask & CMD_FLAG_REQUEST) )			\
+				continue;					\
+			if ( ( __val & CMD_FLAG_REQUEST ) != R_flag_val )	\
+				continue;					\
+			if (result)						\
+				*result = _O(__li->next->o);			\
+			goto end;						\
+		}								\
+		if (__cmp < 0)							\
+			break;							\
+	}									\
+	if (result)								\
+		*result = NULL;							\
+	else									\
+		ret = ENOENT;							\
+}
+
+static int search_vendor ( struct dictionary * dict, int criteria, void * what, struct dict_object **result )
+{
+	int ret = 0;
+	vendor_id_t id;
+	
+	TRACE_ENTRY("%p %d %p %p", dict, criteria, what, result);
+	
+	switch (criteria) {
+		case VENDOR_BY_ID:
+			id = *(vendor_id_t *) what;
+			SEARCH_scalar( id, &dict->dict_vendors.list[0], vendor.vendor_id, 1, &dict->dict_vendors );
+			break;
+				
+		case VENDOR_BY_NAME:
+			/* "what" is a vendor name */
+			SEARCH_string( what, &dict->dict_vendors.list[0], vendor.vendor_name, 0);
+			break;
+			
+		case VENDOR_OF_APPLICATION:
+			/* "what" should be an application object */
+			SEARCH_childs_parent( DICT_APPLICATION, &dict->dict_vendors );
+			break;
+		
+		default:
+			/* Invalid criteria */
+			CHECK_PARAMS( criteria = 0 );
+	}
+end:
+	return ret;
+}
+
+static int search_application ( struct dictionary * dict, int criteria, void * what, struct dict_object **result )
+{
+	int ret = 0;
+	application_id_t id;
+	
+	TRACE_ENTRY("%p %d %p %p", dict, criteria, what, result);
+	
+	switch (criteria) {
+		case APPLICATION_BY_ID:
+			id = *(application_id_t *) what;
+
+			SEARCH_scalar( id, &dict->dict_applications.list[0],  application.application_id, 1, &dict->dict_applications );
+			break;
+				
+		case APPLICATION_BY_NAME:
+			/* "what" is an application name */
+			SEARCH_string( what, &dict->dict_applications.list[0], application.application_name, 0);
+			break;
+			
+		case APPLICATION_OF_TYPE:
+			/* "what" should be a type object */
+			SEARCH_childs_parent( DICT_TYPE, &dict->dict_applications );
+			break;
+		
+		case APPLICATION_OF_COMMAND:
+			/* "what" should be a command object */
+			SEARCH_childs_parent( DICT_COMMAND, &dict->dict_applications );
+			break;
+		
+		default:
+			/* Invalid criteria */
+			CHECK_PARAMS( criteria = 0 );
+	}
+end:
+	return ret;
+}
+
+static int search_type ( struct dictionary * dict, int criteria, void * what, struct dict_object **result )
+{
+	int ret = 0;
+	
+	TRACE_ENTRY("%p %d %p %p", dict, criteria, what, result);
+	
+	switch (criteria) {
+		case TYPE_BY_NAME:
+			/* "what" is a type name */
+			SEARCH_string( what, &dict->dict_types, type.type_name, 1);
+			break;
+			
+		case TYPE_OF_ENUMVAL:
+			/* "what" should be a type_enum object */
+			SEARCH_childs_parent( DICT_ENUMVAL, NULL );
+			break;
+		
+		case TYPE_OF_AVP:
+			/* "what" should be an avp object */
+			SEARCH_childs_parent( DICT_AVP, NULL );
+			break;
+		
+				
+		default:
+			/* Invalid criteria */
+			CHECK_PARAMS( criteria = 0 );
+	}
+end:
+	return ret;
+}
+
+static int search_enumval ( struct dictionary * dict, int criteria, void * what, struct dict_object **result )
+{
+	int ret = 0;
+	
+	TRACE_ENTRY("%p %d %p %p", dict, criteria, what, result);
+	
+	switch (criteria) {
+		case ENUMVAL_BY_STRUCT:
+			{
+				struct dict_object * parent = NULL;
+				struct dict_enumval_request * _what = (struct dict_enumval_request *) what;
+				
+				CHECK_PARAMS(  _what  &&  ( _what->type_obj || _what->type_name )  );
+				
+				if (_what->type_obj != NULL) {
+					parent = _what->type_obj;
+					CHECK_PARAMS(  verify_object(parent)  &&  (parent->type == DICT_TYPE)  );
+				} else {
+					/* We received only the type name, we must find it first */
+					CHECK_FCT_DO( search_type( dict, TYPE_BY_NAME, _what->type_name, &parent ),
+							CHECK_PARAMS( 0 ) );
+				}
+				
+				/* From here the "parent" object is valid */
+				
+				if ( _what->search.enum_name != NULL ) {
+					/* We are looking for this string */
+					SEARCH_string(  _what->search.enum_name, &parent->list[1], enumval.enum_name, 1 );
+				} else {
+					/* We are looking for the value in enum_value */
+					switch (parent->data.type.type_base) {
+						case AVP_TYPE_OCTETSTRING:
+							SEARCH_ocstring( _what->search.enum_value.os.data, 
+									 _what->search.enum_value.os.len, 
+									 &parent->list[2], 
+									 enumval.enum_value.os , 
+									 1 );
+							break;
+
+						case AVP_TYPE_INTEGER32:
+							SEARCH_scalar(	_what->search.enum_value.i32,
+									&parent->list[2],
+									enumval.enum_value.i32,
+									1,
+									(struct dict_object *)NULL);
+							break;
+							
+						case AVP_TYPE_INTEGER64:
+							SEARCH_scalar(	_what->search.enum_value.i64,
+									&parent->list[2],
+									enumval.enum_value.i64,
+									1,
+									(struct dict_object *)NULL);
+							break;
+							
+						case AVP_TYPE_UNSIGNED32:
+							SEARCH_scalar(	_what->search.enum_value.u32,
+									&parent->list[2],
+									enumval.enum_value.u32,
+									1,
+									(struct dict_object *)NULL);
+							break;
+							
+						case AVP_TYPE_UNSIGNED64:
+							SEARCH_scalar(	_what->search.enum_value.u64,
+									&parent->list[2],
+									enumval.enum_value.u64,
+									1,
+									(struct dict_object *)NULL);
+							break;
+							
+						case AVP_TYPE_FLOAT32:
+							SEARCH_scalar(	_what->search.enum_value.f32,
+									&parent->list[2],
+									enumval.enum_value.f32,
+									1,
+									(struct dict_object *)NULL);
+							break;
+							
+						case AVP_TYPE_FLOAT64:
+							SEARCH_scalar(	_what->search.enum_value.f64,
+									&parent->list[2],
+									enumval.enum_value.f64,
+									1,
+									(struct dict_object *)NULL);
+							break;
+							
+						default:
+							/* Invalid parent type basetype */
+							CHECK_PARAMS( parent = NULL );
+					}
+				}
+				
+			}
+			break;
+		
+				
+		default:
+			/* Invalid criteria */
+			CHECK_PARAMS( criteria = 0 );
+	}
+end:
+	return ret;
+}
+
+static int search_avp ( struct dictionary * dict, int criteria, void * what, struct dict_object **result )
+{
+	int ret = 0;
+	
+	TRACE_ENTRY("%p %d %p %p", dict, criteria, what, result);
+	
+	switch (criteria) {
+		case AVP_BY_CODE:
+			{
+				avp_code_t code;
+				code = *(avp_code_t *) what;
+
+				SEARCH_scalar( code, &dict->dict_vendors.list[1],  avp.avp_code, 1, (struct dict_object *)NULL );
+			}
+			break;
+				
+		case AVP_BY_NAME:
+			/* "what" is the AVP name, vendor 0 */
+			SEARCH_string( what, &dict->dict_vendors.list[2], avp.avp_name, 1);
+			break;
+			
+		case AVP_BY_CODE_AND_VENDOR:
+		case AVP_BY_NAME_AND_VENDOR:
+			{
+				struct dict_avp_request * _what = (struct dict_avp_request *) what;
+				struct dict_object * vendor = NULL;
+				
+				CHECK_PARAMS( (criteria != AVP_BY_NAME_AND_VENDOR) || _what->avp_name  );
+				
+				/* Now look for the vendor first */
+				CHECK_FCT( search_vendor( dict, VENDOR_BY_ID, &_what->avp_vendor, &vendor ) );
+				if (vendor == NULL) {
+					if (result)
+						*result = NULL;
+					else
+						ret = ENOENT;
+					goto end;
+				}
+				
+				/* We now have our vendor = head of the appropriate avp list */
+				if (criteria == AVP_BY_NAME_AND_VENDOR) {
+					SEARCH_string( _what->avp_name, &vendor->list[2], avp.avp_name, 1);
+				} else {
+					/* AVP_BY_CODE_AND_VENDOR */
+					SEARCH_scalar( _what->avp_code, &vendor->list[1],  avp.avp_code, 1, (struct dict_object *)NULL );
+				}
+			}
+			break;
+		
+		case AVP_BY_NAME_ALL_VENDORS:
+			{
+				struct fd_list * li;
+				
+				/* First, search for vendor 0 */
+				SEARCH_string( what, &dict->dict_vendors.list[2], avp.avp_name, 1);
+				
+				/* If not found, loop for all vendors, until found */
+				for (li = dict->dict_vendors.list[0].next; li != &dict->dict_vendors.list[0]; li = li->next) {
+					SEARCH_string( what, &_O(li->o)->list[2], avp.avp_name, 1);
+				}
+			}
+			break;
+		
+		default:
+			/* Invalid criteria */
+			CHECK_PARAMS( criteria = 0 );
+	}
+end:
+	return ret;
+}
+
+static int search_cmd ( struct dictionary * dict, int criteria, void * what, struct dict_object **result )
+{
+	int ret = 0;
+	
+	TRACE_ENTRY("%p %d %p %p", dict, criteria, what, result);
+	
+	switch (criteria) {
+		case CMD_BY_NAME:
+			/* "what" is a command name */
+			SEARCH_string( what, &dict->dict_cmd_name, cmd.cmd_name, 1);
+			break;
+			
+		case CMD_BY_CODE_R:
+		case CMD_BY_CODE_A:
+			{
+				command_code_t code;
+				uint8_t searchfl = 0;
+				
+				/* The command code that we are searching */
+				code = *(command_code_t *) what;
+				
+				/* The flag (request or answer) of the command we are searching */
+				if (criteria == CMD_BY_CODE_R) {
+					searchfl = CMD_FLAG_REQUEST;
+				}
+				
+				/* perform the search */
+				SEARCH_codefl( code, searchfl, &dict->dict_cmd_code );
+			}
+			break;
+				
+		case CMD_ANSWER:
+			{
+				/* "what" is a command object of type "request" */
+				struct dict_object * req = (struct dict_object *) what;
+				struct dict_object * ans = NULL;
+				
+				CHECK_PARAMS( verify_object(req) 
+						&& (req->type == DICT_COMMAND)
+						&& (req->data.cmd.cmd_flag_mask & CMD_FLAG_REQUEST)
+						&& (req->data.cmd.cmd_flag_val  & CMD_FLAG_REQUEST) );
+				
+				/* The answer is supposed to be the next element in the list, if it exists */
+				ans = req->list[1].next->o;
+				if ( ans == NULL ) {
+					TRACE_DEBUG( FULL, "the request was the last element in the list" );
+					ret = ENOENT;
+					goto end;
+				}
+				
+				/* Now check that the ans element is really the correct one */
+				if (  (ans->data.cmd.cmd_code != req->data.cmd.cmd_code)
+				   || (!(ans->data.cmd.cmd_flag_mask & CMD_FLAG_REQUEST))
+				   || (  ans->data.cmd.cmd_flag_val  & CMD_FLAG_REQUEST ) ) {
+					TRACE_DEBUG( FULL, "the answer does not follow the request in the list" );
+					ret = ENOENT;
+					goto end;
+				}
+				
+				if (result)
+					*result = ans;
+				ret = 0;
+			}						
+			break;
+			
+		default:
+			/* Invalid criteria */
+			CHECK_PARAMS( criteria = 0 );
+	}
+end:
+	return ret;
+}
+
+static int search_rule ( struct dictionary * dict, int criteria, void * what, struct dict_object **result )
+{
+	int ret = 0;
+	
+	TRACE_ENTRY("%p %d %p %p", dict, criteria, what, result);
+	
+	switch (criteria) {
+		case RULE_BY_AVP_AND_PARENT:
+			{
+				struct dict_object * parent = NULL;
+				struct dict_object * avp = NULL;
+				struct dict_rule_request * _what = (struct dict_rule_request *) what;
+				
+				CHECK_PARAMS( _what 
+						&& (parent = _what->rule_parent)
+						&& (avp    = _what->rule_avp   ) );
+				
+				CHECK_PARAMS( verify_object(parent) 
+						&& ((parent->type == DICT_COMMAND) 
+						 || ((parent->type == DICT_AVP) && (parent->data.avp.avp_basetype == AVP_TYPE_GROUPED))) );
+				
+				CHECK_PARAMS( verify_object(avp) && (avp->type == DICT_AVP) );
+				
+				/* Perform the search */
+				SEARCH_ruleavpname( avp->data.avp.avp_name, &parent->list[2]);
+				
+			}
+			break;
+			
+		default:
+			/* Invalid criteria */
+			CHECK_PARAMS( criteria = 0 );
+	}
+end:
+	return ret;
+}
+
+/*******************************************************************************************************/
+/*******************************************************************************************************/
+/*                                                                                                     */
+/*                                  Dump / debug functions                                             */
+/*                                                                                                     */
+/*******************************************************************************************************/
+/*******************************************************************************************************/
+/* The following functions are used to debug the module, and allow to print out the content of the dictionary */
+static void dump_vendor_data ( void * data )
+{
+	struct dict_vendor_data * vendor = (struct dict_vendor_data *)data;
+	
+	fd_log_debug("data: %-6u \"%s\"", vendor->vendor_id, vendor->vendor_name);
+}
+static void dump_application_data ( void * data )
+{
+	struct dict_application_data * appli = (struct dict_application_data *) data;
+	fd_log_debug("data: %-6u \"%s\"", appli->application_id, appli->application_name);
+}
+static void dump_type_data ( void * data )
+{
+	struct dict_type_data * type = ( struct dict_type_data * ) data;
+	
+	fd_log_debug("data: %-12s \"%s\"", 
+			type_base_name[type->type_base], 
+			type->type_name);
+}
+static void dump_enumval_data ( struct dict_enumval_data * enumval, enum dict_avp_basetype type )
+{
+	const int LEN_MAX = 20;
+	fd_log_debug("data: (%-12s) \"%s\" -> ", type_base_name[type], enumval->enum_name);
+	switch (type) {
+		case AVP_TYPE_OCTETSTRING:
+			{
+				int i, n=LEN_MAX;
+				if (enumval->enum_value.os.len < LEN_MAX)
+					n = enumval->enum_value.os.len;
+				for (i=0; i < n; i++)
+					fd_log_debug("0x%02.2X/'%c' ", enumval->enum_value.os.data[i], ASCII(enumval->enum_value.os.data[i]));
+				if (n == LEN_MAX)
+					fd_log_debug("...");
+			}
+			break;
+		
+		case AVP_TYPE_INTEGER32:
+			fd_log_debug("%i", enumval->enum_value.i32);
+			break;
+
+		case AVP_TYPE_INTEGER64:
+			fd_log_debug("%lli", enumval->enum_value.i64);
+			break;
+
+		case AVP_TYPE_UNSIGNED32:
+			fd_log_debug("%u", enumval->enum_value.u32);
+			break;
+
+		case AVP_TYPE_UNSIGNED64:
+			fd_log_debug("%llu", enumval->enum_value.u64);
+			break;
+
+		case AVP_TYPE_FLOAT32:
+			fd_log_debug("%f", enumval->enum_value.f32);
+			break;
+
+		case AVP_TYPE_FLOAT64:
+			fd_log_debug("%g", enumval->enum_value.f64);
+			break;
+		
+		default:
+			fd_log_debug("??? (ERROR unknown type %d)", type);
+	}
+}
+static void dump_avp_data ( void * data )
+{
+	struct dict_avp_data * avp = (struct dict_avp_data * ) data;
+	fd_log_debug("data: v/m:" DUMP_AVPFL_str "/" DUMP_AVPFL_str ", %12s, %-6u \"%s\"", 
+			DUMP_AVPFL_val(avp->avp_flag_val), 
+			DUMP_AVPFL_val(avp->avp_flag_mask), 
+			type_base_name[avp->avp_basetype], 
+			avp->avp_code, 
+			avp->avp_name );
+}
+static void dump_command_data ( void * data )
+{
+	struct dict_cmd_data * cmd = (struct dict_cmd_data *) data;
+	fd_log_debug("data: v/m:" DUMP_CMDFL_str "/" DUMP_CMDFL_str ", %-6u \"%s\"", 
+			DUMP_CMDFL_val(cmd->cmd_flag_val), DUMP_CMDFL_val(cmd->cmd_flag_mask), cmd->cmd_code, cmd->cmd_name);
+}
+static void dump_rule_data ( void * data )
+{
+	struct dict_rule_data * rule = (struct dict_rule_data * )data;
+	fd_log_debug("data: pos:%d ord:%d m/M:%2d/%2d avp:\"%s\"", 
+			rule->rule_position, 
+			rule->rule_order, 
+			rule->rule_min, 
+			rule->rule_max,
+			rule->rule_avp->data.avp.avp_name);
+}
+
+static void dump_object ( struct dict_object * obj, int parents, int depth, int indent );
+
+static void dump_list ( struct fd_list * sentinel, int parents, int depth, int indent )
+{
+	struct fd_list * li = sentinel;
+	/* We don't lock here, the caller must have taken the dictionary lock for reading already */
+	while (li->next != sentinel)
+	{
+		li = li->next;
+		dump_object( _O(li->o), parents, depth, indent );
+	}
+}
+
+static void dump_object ( struct dict_object * obj, int parents, int depth, int indent )
+{
+	if (obj == NULL)
+		return;
+	
+	if (parents)
+		dump_object (obj->parent, parents-1, 0, indent + 1 );
+	
+	fd_log_debug("%*s@%p: %s%s (p:%-9p) ", 
+			indent,
+			"",
+			obj, 
+			verify_object(obj) ? "" : "INVALID ", 
+			_OBINFO(obj).name, 
+			obj->parent);
+	
+	if (obj->type == DICT_ENUMVAL)
+		dump_enumval_data ( &obj->data.enumval, obj->parent->data.type.type_base );
+	else
+		_OBINFO(obj).dump_data(&obj->data);
+	
+	fd_log_debug("\n");
+	
+	if (depth) {
+		int i;
+		for (i=0; i<NB_LISTS_PER_OBJ; i++) {
+			if ((obj->list[i].o == NULL) && (obj->list[i].next != &obj->list[i])) {
+				fd_log_debug("%*s>%p: list[%d]:\n", indent, "", obj, i);
+				dump_list(&obj->list[i], parents, depth - 1, indent + 2);
+			}
+		}
+	}
+}
+
+void fd_dict_dump_object(struct dict_object * obj)
+{
+	fd_log_debug("Dictionary object %p dump:\n", obj);
+	dump_object( obj, 1, 2, 2 );
+}
+
+void fd_dict_dump(struct dictionary * dict)
+{
+	int i;
+	
+	CHECK_PARAMS_DO(dict && (dict->dict_eyec == DICT_EYECATCHER), return);
+	
+	CHECK_POSIX_DO(  pthread_rwlock_rdlock( &dict->dict_lock ), /* ignore */  );
+	
+	fd_log_debug("######################################################\n");
+	fd_log_debug("###### Dumping vendors, AVPs and related rules #######\n");
+	
+	dump_object( &dict->dict_vendors, 0, 3, 0 );
+	
+	fd_log_debug("###### Dumping applications #######\n");
+
+	dump_object( &dict->dict_applications, 0, 1, 0 );
+	
+	fd_log_debug("###### Dumping types #######\n");
+
+	dump_list( &dict->dict_types, 0, 2, 0 );
+	
+	fd_log_debug("###### Dumping commands per name #######\n");
+
+	dump_list( &dict->dict_cmd_name, 0, 2, 0 );
+	
+	fd_log_debug("###### Dumping commands per code and flags #######\n");
+
+	dump_list( &dict->dict_cmd_code, 0, 0, 0 );
+	
+	fd_log_debug("###### Statistics #######\n");
+
+	for (i=1; i<=DICT_TYPE_MAX; i++)
+		fd_log_debug(" %5d objects of type %s\n", dict->dict_count[i], dict_obj_info[i].name);
+	
+	fd_log_debug("######################################################\n");
+	
+	/* Free the rwlock */
+	CHECK_POSIX_DO(  pthread_rwlock_unlock( &dict->dict_lock ), /* ignore */  );
+}
+
+/**************************** Dump AVP values ********************************/
+
+/* Default dump functions */
+static void dump_val_os(union avp_value * value)
+{
+	int i;
+	for (i = 0; i < value->os.len; i++) {
+		if (i == 24) { /* Dump only up to 24 bytes of the buffer */
+			fd_log_debug("[...] (len=%zd)", value->os.len);
+			break;
+		}
+		fd_log_debug("%02.2X ", value->os.data[i]);
+	}
+}
+
+static void dump_val_i32(union avp_value * value)
+{
+	fd_log_debug("%i (0x%x)", value->i32, value->i32);
+}
+
+static void dump_val_i64(union avp_value * value)
+{
+	fd_log_debug("%lli (0x%llx)", value->i64, value->i64);
+}
+
+static void dump_val_u32(union avp_value * value)
+{
+	fd_log_debug("%u (0x%x)", value->u32, value->u32);
+}
+
+static void dump_val_u64(union avp_value * value)
+{
+	fd_log_debug("%llu (0x%llx)", value->u64, value->u64);
+}
+
+static void dump_val_f32(union avp_value * value)
+{
+	fd_log_debug("%f", value->f32);
+}
+
+static void dump_val_f64(union avp_value * value)
+{
+	fd_log_debug("%g", value->f64);
+}
+
+/* Get the dump function for basic dict_avp_basetype */
+static void (*get_default_dump_val_cb(enum dict_avp_basetype datatype))(union avp_value *)
+{
+	switch (datatype) {
+		case AVP_TYPE_OCTETSTRING:
+			return &dump_val_os;
+		
+		case AVP_TYPE_INTEGER32:
+			return &dump_val_i32;
+
+		case AVP_TYPE_INTEGER64:
+			return &dump_val_i64;
+
+		case AVP_TYPE_UNSIGNED32:
+			return &dump_val_u32;
+
+		case AVP_TYPE_UNSIGNED64:
+			return &dump_val_u64;
+
+		case AVP_TYPE_FLOAT32:
+			return &dump_val_f32;
+
+		case AVP_TYPE_FLOAT64:
+			return &dump_val_f64;
+		
+		case AVP_TYPE_GROUPED:
+			TRACE_DEBUG(FULL, "error: grouped AVP with a value!");
+	}
+	return NULL;
+}
+
+/* indent inside an object (duplicate from messages.c) */
+#define INOBJHDR 	"%*s   "
+#define INOBJHDRVAL 	indent<0 ? 1 : indent, indent<0 ? "-" : "|"
+
+/* Formater for the AVP value dump line */
+static void dump_avp_val(union avp_value *avp_value, void (*dump_val_cb)(union avp_value *avp_value), enum dict_avp_basetype datatype, char * type_name, char * const_name, int indent)
+{
+	/* Header for all AVP values dumps: */
+	fd_log_debug(INOBJHDR "value ", INOBJHDRVAL);
+	
+	/* If the type is provided, write it */
+	if (type_name)
+		fd_log_debug("t: '%s' ", type_name);
+	
+	/* Always give the base datatype anyway */
+	fd_log_debug("(%s) ", type_base_name[datatype]);
+	
+	/* Now, the value */
+	fd_log_debug("v: ");
+	if (const_name)
+		fd_log_debug("'%s' (", const_name);
+	(*dump_val_cb)(avp_value);
+	if (const_name)
+		fd_log_debug(")");
+	
+	/* Done! */
+	fd_log_debug("\n");
+}
+
+/* Dump the value of an AVP of known type */
+void fd_dict_dump_avp_value(union avp_value *avp_value, struct dict_object * model, int indent)
+{
+	void (*dump_val_cb)(union avp_value *avp_value);
+	struct dict_object * type = NULL;
+	char * type_name = NULL;
+	char * const_name = NULL;
+	
+	/* Check the parameters are correct */
+	CHECK_PARAMS_DO( avp_value && verify_object(model) && (model->type == DICT_AVP), return );
+	
+	/* Default: display the value with the formatter for the AVP datatype */
+	CHECK_PARAMS_DO( dump_val_cb = get_default_dump_val_cb(model->data.avp.avp_basetype), return );
+	
+	/* Get the type definition of this AVP */
+	type = model->parent;
+	if (type) {
+		struct dict_enumval_request  request;
+		struct dict_object * enumval = NULL;
+		
+		type_name = type->data.type.type_name;
+		
+		/* overwrite the dump function ? */
+		if (type->data.type.type_dump)
+			dump_val_cb = type->data.type.type_dump;
+		
+		/* Now check if the AVP value matches a constant */
+		memset(&request, 0, sizeof(request));
+		request.type_obj = type;
+		memcpy(&request.search.enum_value, avp_value, sizeof(union avp_value));
+		/* bypass checks */
+		if ((search_enumval( type->dico, ENUMVAL_BY_STRUCT, &request, &enumval ) == 0) && (enumval)) {
+			/* We found a cosntant, get its name */
+			const_name = enumval->data.enumval.enum_name;
+		}
+	}
+	
+	/* And finally, dump the value */
+	dump_avp_val(avp_value, dump_val_cb, model->data.avp.avp_basetype, type_name, const_name, indent);
+}
+
+/*******************************************************************************************************/
+/*******************************************************************************************************/
+/*                                                                                                     */
+/*                                  Exported functions                                                 */
+/*                                                                                                     */
+/*******************************************************************************************************/
+/*******************************************************************************************************/
+
+/* These are the functions exported outside libfreeDiameter. */
+
+/* Get the data associated to an object */
+int fd_dict_gettype ( struct dict_object * object, enum dict_object_type * type)
+{
+	TRACE_ENTRY("%p %p", object, type);
+	
+	CHECK_PARAMS( type && verify_object(object) );
+	
+	/* Copy the value and return */
+	*type = object->type;
+	return 0;
+}
+
+int fd_dict_getdict ( struct dict_object * object, struct dictionary ** dict)
+{
+	TRACE_ENTRY("%p %p", object, dict);
+	
+	CHECK_PARAMS( dict && verify_object(object) );
+	
+	/* Copy the value and return */
+	*dict = object->dico;
+	return 0;
+}
+
+
+/* Get the data associated to an object */
+int fd_dict_getval ( struct dict_object * object, void * val)
+{
+	TRACE_ENTRY("%p %p", object, val);
+	
+	CHECK_PARAMS( val && verify_object(object) );
+	
+	/* Copy the value and return */
+	memcpy(val, &object->data, _OBINFO(object).datasize);;
+	return 0;
+}
+
+/* Add a new object in the dictionary */
+int fd_dict_new ( struct dictionary * dict, enum dict_object_type type, void * data, struct dict_object * parent, struct dict_object **ref )
+{
+	int ret = 0;
+	struct dict_object * new = NULL;
+	struct dict_object * vendor = NULL;
+	struct dict_object * locref = NULL;
+	
+	TRACE_ENTRY("%p %d(%s) %p %p %p", dict, type, dict_obj_info[CHECK_TYPE(type) ? type : 0].name, data, parent, ref);
+	
+	/* Check parameters */
+	CHECK_PARAMS( dict && (dict->dict_eyec == DICT_EYECATCHER) && CHECK_TYPE(type) && data  );
+	
+	/* Check the "parent" parameter */
+	switch (dict_obj_info[type].parent) {
+		case 0:	/* parent is forbidden */
+			CHECK_PARAMS( parent == NULL );
+		
+		case 1:	/* parent is optional */
+			if (parent == NULL)
+				break;
+		
+		case 2: /* parent is mandatory */
+			CHECK_PARAMS(  verify_object(parent)  );
+			
+			if (type == DICT_RULE ) { /* Special case : grouped AVP or Command parents are allowed */
+				CHECK_PARAMS( (parent->type == DICT_COMMAND ) 
+						|| ( (parent->type == DICT_AVP) && (parent->data.avp.avp_basetype == AVP_TYPE_GROUPED ) ) );
+			} else {
+				CHECK_PARAMS( parent->type == dict_obj_info[type].parenttype );
+			}
+	}
+	
+	/* For AVP object, we must also check that the "vendor" referenced exists */
+	if (type == DICT_AVP) {
+		CHECK_FCT_DO(  fd_dict_search( dict, DICT_VENDOR, VENDOR_BY_ID, &(((struct dict_avp_data *)data)->avp_vendor), (void*)&vendor, ENOENT ),
+			{ TRACE_DEBUG(INFO, "Unable to find vendor '%d' referenced in the AVP data", ((struct dict_avp_data *)data)->avp_vendor); return EINVAL; }  );
+		
+		/* Also check if a parent is provided, that the type are the same */
+		if (parent) {
+			CHECK_PARAMS(  parent->data.type.type_base == ((struct dict_avp_data *)data)->avp_basetype  );
+		}
+	}
+	
+	/* For RULE object, we must also check that the "avp" referenced exists */
+	if (type == DICT_RULE) {
+		CHECK_PARAMS(  verify_object(((struct dict_rule_data *)data)->rule_avp)  );
+		CHECK_PARAMS(  ((struct dict_rule_data *)data)->rule_avp->type == DICT_AVP  );
+	}
+	
+	/* For COMMAND object, check that the 'R' flag is fixed */
+	if (type == DICT_COMMAND) {
+		CHECK_PARAMS( ((struct dict_cmd_data *)data)->cmd_flag_mask & CMD_FLAG_REQUEST   );
+	}
+	
+	/* We have to check that the new values are not equal to the sentinels */
+	if (type == DICT_VENDOR) {
+		CHECK_PARAMS( ((struct dict_vendor_data *)data)->vendor_id != 0   );
+	}
+	if (type == DICT_APPLICATION) {
+		CHECK_PARAMS( ((struct dict_application_data *)data)->application_id != 0   );
+	}
+	
+	/* Parameters are valid, create the new object */
+	CHECK_MALLOC(  new = malloc(sizeof(struct dict_object))  );
+	
+	/* Initialize the data of the new object */
+	init_object(new, type);
+	init_object_data(&new->data, data, type);
+	new->dico = dict;
+	new->parent = parent;
+	
+	/* We will change the dictionary => acquire the write lock */
+	CHECK_POSIX_DO(  ret = pthread_rwlock_wrlock(&dict->dict_lock),  goto error_free  );
+	
+	/* Now link the object -- this also checks that no object with same keys already exists */
+	switch (type) {
+		case DICT_VENDOR:
+			/* A vendor object is linked in the g_dict_vendors.list[0], by their id */
+			ret = fd_list_insert_ordered ( &dict->dict_vendors.list[0], &new->list[0], (int (*)(void*, void *))order_vendor_by_id, (void **)&locref );
+			if (ret)
+				goto error_unlock;
+			break;
+		
+		case DICT_APPLICATION:
+			/* An application object is linked in the g_dict_applciations.list[0], by their id */
+			ret = fd_list_insert_ordered ( &dict->dict_applications.list[0], &new->list[0], (int (*)(void*, void *))order_appli_by_id, (void **)&locref );
+			if (ret)
+				goto error_unlock;
+			break;
+		
+		case DICT_TYPE:
+			/* A type object is linked in g_list_types by its name */
+			ret = fd_list_insert_ordered ( &dict->dict_types, &new->list[0], (int (*)(void*, void *))order_type_by_name, (void **)&locref );
+			if (ret)
+				goto error_unlock;
+			break;
+		
+		case DICT_ENUMVAL:
+			/* A type_enum object is linked in it's parent 'type' object lists 1 and 2 by its name and values */
+			ret = fd_list_insert_ordered ( &parent->list[1], &new->list[0], (int (*)(void*, void *))order_enum_by_name, (void **)&locref );
+			if (ret)
+				goto error_unlock;
+			
+			ret = fd_list_insert_ordered ( &parent->list[2], &new->list[1], (int (*)(void*, void *))order_enum_by_val, (void **)&locref );
+			if (ret) { 
+				fd_list_unlink(&new->list[0]); 
+				goto error_unlock; 
+			}
+			break;
+		
+		case DICT_AVP:
+			/* An avp object is linked in lists 1 and 2 of its vendor, by code and name */
+			ret = fd_list_insert_ordered ( &vendor->list[1], &new->list[0], (int (*)(void*, void *))order_avp_by_code, (void **)&locref );
+			if (ret)
+				goto error_unlock;
+			
+			ret = fd_list_insert_ordered ( &vendor->list[2], &new->list[1], (int (*)(void*, void *))order_avp_by_name, (void **)&locref );
+			if (ret) {
+				fd_list_unlink(&new->list[0]);
+				goto error_unlock;
+			}
+			break;
+			
+		case DICT_COMMAND:
+			/* A command object is linked in g_list_cmd_name and g_list_cmd_code by its name and code */
+			ret = fd_list_insert_ordered ( &dict->dict_cmd_code, &new->list[1], (int (*)(void*, void *))order_cmd_by_codefl, (void **)&locref );
+			if (ret)
+				goto error_unlock;
+			
+			ret = fd_list_insert_ordered ( &dict->dict_cmd_name, &new->list[0], (int (*)(void*, void *))order_cmd_by_name, (void **)&locref );
+			if (ret) {
+				fd_list_unlink(&new->list[1]);
+				goto error_unlock;
+			}
+			break;
+		
+		case DICT_RULE:
+			/* A rule object is linked in list[2] of its parent command or AVP by the name of the AVP it refers */
+			ret = fd_list_insert_ordered ( &parent->list[2], &new->list[0], (int (*)(void*, void *))order_rule_by_avpn, (void **)&locref );
+			if (ret)
+				goto error_unlock;
+			break;
+			
+		default:
+			ASSERT(0);
+	}
+	
+	/* A new object has been created, increment the global counter */
+	dict->dict_count[type]++;
+	
+	/* Unlock the dictionary */
+	CHECK_POSIX_DO(  ret = pthread_rwlock_unlock(&dict->dict_lock),  goto error_free  );
+	
+	/* Save the pointer to the new object */
+	if (ref)
+		*ref = new;
+	
+	return 0;
+	
+error_unlock:
+	CHECK_POSIX_DO(  pthread_rwlock_unlock(&dict->dict_lock),  /* continue */  );
+	if (ret == EEXIST) {
+		/* We have a duplicate key in locref. Check if the pointed object is the same or not */
+		switch (type) {
+			case DICT_VENDOR:
+				/* if we are here, it meas the two vendor id are identical */
+				if (strcmp(locref->data.vendor.vendor_name, new->data.vendor.vendor_name)) {
+					TRACE_DEBUG(FULL, "Conflicting vendor name");
+					break;
+				}
+				/* Otherwise (same name), we consider the function succeeded, since the (same) object is in the dictionary */
+				ret = 0; 
+				break;
+
+			case DICT_APPLICATION:
+				/* got same id */
+				if (strcmp(locref->data.application.application_name, new->data.application.application_name)) {
+					TRACE_DEBUG(FULL, "Conflicting application name");
+					break;
+				}
+				ret = 0;
+				break;
+
+			case DICT_TYPE:
+				/* got same name */
+				if (locref->data.type.type_base != new->data.type.type_base) {
+					TRACE_DEBUG(FULL, "Conflicting base type");
+					break;
+				}
+				/* discard new definition only it a callback is provided and different from the previous one */
+				if ((new->data.type.type_interpret) && (locref->data.type.type_interpret != new->data.type.type_interpret)) {
+					TRACE_DEBUG(FULL, "Conflicting interpret cb");
+					break;
+				}
+				if ((new->data.type.type_encode) && (locref->data.type.type_encode != new->data.type.type_encode)) {
+					TRACE_DEBUG(FULL, "Conflicting encode cb");
+					break;
+				}
+				if ((new->data.type.type_dump) && (locref->data.type.type_dump != new->data.type.type_dump)) {
+					TRACE_DEBUG(FULL, "Conflicting dump cb");
+					break;
+				}
+				ret = 0;
+				break;
+
+			case DICT_ENUMVAL:
+				/* got either same name or same value. We check that both are true */
+				if (order_enum_by_name(locref, new)) {
+					TRACE_DEBUG(FULL, "Conflicting enum name");
+					break;
+				}
+				if (order_enum_by_val(locref, new)) {
+					TRACE_DEBUG(FULL, "Conflicting enum value");
+					break;
+				}
+				ret = 0;
+				break;
+
+			case DICT_AVP:
+				/* got either same name or code */
+				if (order_avp_by_code(locref, new)) {
+					TRACE_DEBUG(FULL, "Conflicting AVP code");
+					break;
+				}
+				if (order_avp_by_name(locref, new)) {
+					TRACE_DEBUG(FULL, "Conflicting AVP name");
+					break;
+				}
+				if  (locref->data.avp.avp_vendor != new->data.avp.avp_vendor) {
+					TRACE_DEBUG(FULL, "Conflicting AVP vendor");
+					break;
+				}
+				if  (locref->data.avp.avp_flag_mask != new->data.avp.avp_flag_mask) {
+					TRACE_DEBUG(FULL, "Conflicting AVP flags mask");
+					break;
+				}
+				if  ((locref->data.avp.avp_flag_val & locref->data.avp.avp_flag_mask) != (new->data.avp.avp_flag_val & new->data.avp.avp_flag_mask)) {
+					TRACE_DEBUG(FULL, "Conflicting AVP flags value");
+					break;
+				}
+				if  (locref->data.avp.avp_basetype != new->data.avp.avp_basetype) {
+					TRACE_DEBUG(FULL, "Conflicting AVP base type");
+					break;
+				}
+				ret = 0;
+				break;
+
+			case DICT_COMMAND:
+				/* We got either same name, or same code + R flag */
+				if (order_cmd_by_name(locref, new)) {
+					TRACE_DEBUG(FULL, "Conflicting command name");
+					break;
+				}
+				if (locref->data.cmd.cmd_code != new->data.cmd.cmd_code) {
+					TRACE_DEBUG(FULL, "Conflicting command code");
+					break;
+				}
+				if (locref->data.cmd.cmd_flag_mask != new->data.cmd.cmd_flag_mask) {
+					TRACE_DEBUG(FULL, "Conflicting command flags mask %hhx:%hhx", locref->data.cmd.cmd_flag_mask, new->data.cmd.cmd_flag_mask);
+					break;
+				}
+				if ((locref->data.cmd.cmd_flag_val & locref->data.cmd.cmd_flag_mask) != (new->data.cmd.cmd_flag_val & new->data.cmd.cmd_flag_mask)) {
+					TRACE_DEBUG(FULL, "Conflicting command flags value");
+					break;
+				}
+				ret = 0;
+				break;
+
+			case DICT_RULE:
+				/* Both rules point to the same AVPs */
+				if (locref->data.rule.rule_position != new->data.rule.rule_position) {
+					TRACE_DEBUG(FULL, "Conflicting rule position");
+					break;
+				}
+				if ( ((locref->data.rule.rule_position == RULE_FIXED_HEAD) ||
+					(locref->data.rule.rule_position == RULE_FIXED_TAIL))
+				    && (locref->data.rule.rule_order != new->data.rule.rule_order)) {
+					TRACE_DEBUG(FULL, "Conflicting rule order");
+					break;
+				}
+				if (locref->data.rule.rule_min != new->data.rule.rule_min) {
+					int r1 = locref->data.rule.rule_min;
+					int r2 = new->data.rule.rule_min;
+					int p  = locref->data.rule.rule_position;
+					if (  ((r1 != -1) && (r2 != -1)) /* none of the definitions contains the "default" value */
+					   || ((p == RULE_OPTIONAL) && (r1 != 0) && (r2 != 0)) /* the other value is not 0 for an optional rule */
+					   || ((r1 != 1) && (r2 != 1)) /* the other value is not 1 for another rule */
+					) {
+						TRACE_DEBUG(FULL, "Conflicting rule min");
+						break;
+					}
+				}
+				if (locref->data.rule.rule_max != new->data.rule.rule_max) {
+					TRACE_DEBUG(FULL, "Conflicting rule max");
+					break;
+				}
+				ret = 0;
+				break;
+		}
+		if (ret) {
+			TRACE_DEBUG(INFO, "An existing object with different non-key data was found: EEXIST");
+		} else {
+			TRACE_DEBUG(FULL, "An existing object with the same data was found, ignoring the error...");
+		}
+		if (ref)
+			*ref = locref;
+	} else {
+		CHECK_FCT_DO( ret, ); /* log the error */ 
+	}
+
+error_free:
+	free(new);
+	return ret;
+}
+
+int fd_dict_search ( struct dictionary * dict, enum dict_object_type type, int criteria, void * what, struct dict_object **result, int retval )
+{
+	int ret = 0;
+	
+	TRACE_ENTRY("%p %d(%s) %d %p %p %d", dict, type, dict_obj_info[CHECK_TYPE(type) ? type : 0].name, criteria, what, result, retval);
+	
+	/* Check param */
+	CHECK_PARAMS( dict && (dict->dict_eyec == DICT_EYECATCHER) && CHECK_TYPE(type) );
+	
+	/* Lock the dictionary for reading */
+	CHECK_POSIX(  pthread_rwlock_rdlock(&dict->dict_lock)  );
+	
+	/* Now call the type-specific search function */
+	ret = dict_obj_info[type].search_fct (dict, criteria, what, result);
+	
+	/* Unlock */
+	CHECK_POSIX(  pthread_rwlock_unlock(&dict->dict_lock)  );
+	
+	/* Update the return value as needed */
+	if ((result != NULL) && (*result == NULL))
+		ret = retval;
+	
+	return ret;
+}
+
+/*******************************************************************************************************/
+/*******************************************************************************************************/
+/*                                                                                                     */
+/*                                  The init/fini functions                                            */
+/*                                                                                                     */
+/*******************************************************************************************************/
+/*******************************************************************************************************/
+
+/* Initialize the dictionary */
+int fd_dict_init ( struct dictionary ** dict)
+{
+	struct dictionary * new = NULL;
+	
+	TRACE_ENTRY("");
+	
+	/* Sanity checks */
+	ASSERT( (sizeof(type_base_name) / sizeof(type_base_name[0])) == (AVP_TYPE_MAX + 1) );
+	ASSERT( (sizeof(dict_obj_info)  / sizeof(dict_obj_info[0]))  == (DICT_TYPE_MAX + 1) );
+	CHECK_PARAMS(dict);
+	
+	/* Allocate the memory for the dictionary */
+	CHECK_MALLOC( new = malloc(sizeof(struct dictionary)) );
+	memset(new, 0, sizeof(struct dictionary));
+	
+	new->dict_eyec = DICT_EYECATCHER;
+	
+	/* Initialize the lock for the dictionary */
+	CHECK_POSIX(  pthread_rwlock_init(&new->dict_lock, NULL)  );
+	
+	/* Initialize the sentinel for vendors and AVP lists */
+	init_object( &new->dict_vendors, DICT_VENDOR );
+	new->dict_vendors.data.vendor.vendor_name = "(no vendor)";
+	new->dict_vendors.list[0].o = NULL; /* overwrite since element is also sentinel for this list. */
+	new->dict_vendors.dico = new;
+	
+	/* Initialize the sentinel for applications */
+	init_object( &new->dict_applications, DICT_APPLICATION );
+	new->dict_applications.data.application.application_name = "Diameter Common Messages";
+	new->dict_applications.list[0].o = NULL; /* overwrite since since element is also sentinel for this list. */
+	new->dict_applications.dico = new;
+			
+	/* Initialize the sentinel for types */
+	fd_list_init ( &new->dict_types, NULL );
+	
+	/* Initialize the sentinels for commands */
+	fd_list_init ( &new->dict_cmd_name, NULL );
+	fd_list_init ( &new->dict_cmd_code, NULL );
+	
+	/* Initialize the error command object */
+	init_object( &new->dict_cmd_error, DICT_COMMAND );
+	new->dict_cmd_error.data.cmd.cmd_name="(generic error format)";
+	new->dict_cmd_error.data.cmd.cmd_flag_mask=CMD_FLAG_ERROR | CMD_FLAG_REQUEST | CMD_FLAG_RETRANSMIT;
+	new->dict_cmd_error.data.cmd.cmd_flag_val =CMD_FLAG_ERROR;
+	new->dict_cmd_error.dico = new;
+	
+	*dict = new;
+	
+	/* Done */
+	return 0;
+}
+
+/* Destroy a dictionary */
+int fd_dict_fini ( struct dictionary ** dict)
+{
+	int i;
+	
+	TRACE_ENTRY("");
+	CHECK_PARAMS( dict && *dict && ((*dict)->dict_eyec == DICT_EYECATCHER) );
+	
+	/* Acquire the write lock to make sure no other operation is ongoing */
+	CHECK_POSIX(  pthread_rwlock_wrlock(&(*dict)->dict_lock)  );
+	
+	/* Empty all the lists, free the elements */
+	destroy_list ( &(*dict)->dict_cmd_error.list[2] );
+	destroy_list ( &(*dict)->dict_cmd_code );
+	destroy_list ( &(*dict)->dict_cmd_name );
+	destroy_list ( &(*dict)->dict_types );
+	for (i=0; i< NB_LISTS_PER_OBJ; i++) {
+		destroy_list ( &(*dict)->dict_applications.list[i] );
+		destroy_list ( &(*dict)->dict_vendors.list[i] );
+	}
+	
+	/* Dictionary is empty, now destroy the lock */
+	CHECK_POSIX(  pthread_rwlock_unlock(&(*dict)->dict_lock)  );
+	CHECK_POSIX(  pthread_rwlock_destroy(&(*dict)->dict_lock)  );
+	
+	free(*dict);
+	*dict = NULL;
+	
+	return 0;
+}
+
+/*******************************************************************************************************/
+/*******************************************************************************************************/
+/*                                                                                                     */
+/*                                  Other functions                                                    */
+/*                                                                                                     */
+/*******************************************************************************************************/
+/*******************************************************************************************************/
+
+/* Iterate a callback on the rules for an object */
+int fd_dict_iterate_rules ( struct dict_object *parent, void * data, int (*cb)(void *, struct dict_rule_data *) )
+{
+	int ret = 0;
+	struct fd_list * li;
+	
+	TRACE_ENTRY("%p %p %p", parent, data, cb);
+	
+	/* Check parameters */
+	CHECK_PARAMS(  verify_object(parent)  );
+	CHECK_PARAMS(  (parent->type == DICT_COMMAND) 
+			|| ((parent->type == DICT_AVP) && (parent->data.avp.avp_basetype == AVP_TYPE_GROUPED)) );
+	TRACE_DEBUG (FULL, "Iterating on rules of %s: '%s'.", 
+			_OBINFO(parent).name, 
+			parent->type == DICT_COMMAND ? 
+				  parent->data.cmd.cmd_name
+				: parent->data.avp.avp_name);
+	
+	/* Acquire the read lock  */
+	CHECK_POSIX(  pthread_rwlock_rdlock(&parent->dico->dict_lock)  );
+	
+	/* go through the list and call the cb on each rule data */
+	for (li = &(parent->list[2]); li->next != &(parent->list[2]); li = li->next) {
+		ret = (*cb)(data, &(_O(li->next->o)->data.rule));
+		if (ret != 0)
+			break;
+	}
+		
+	/* Release the lock */
+	CHECK_POSIX(  pthread_rwlock_unlock(&parent->dico->dict_lock)  );
+	
+	return ret;
+}
+
+/* Create the list of vendors. Returns a 0-terminated array, that must be freed after use. Returns NULL on error. */
+uint32_t * fd_dict_get_vendorid_list(struct dictionary * dict)
+{
+	uint32_t * ret = NULL;
+	int i = 0;
+	struct fd_list * li;
+	
+	TRACE_ENTRY();
+	
+	/* Acquire the read lock */
+	CHECK_POSIX_DO(  pthread_rwlock_rdlock(&dict->dict_lock), return NULL  );
+	
+	/* Allocate an array to contain all the elements */
+	CHECK_MALLOC_DO( ret = calloc( dict->dict_count[DICT_VENDOR] + 1, sizeof(uint32_t) ), goto out );
+	
+	/* Copy the vendors IDs */
+	for (li = dict->dict_vendors.list[0].next; li != &(dict->dict_vendors.list[0]); li = li->next) {
+		ret[i] = _O(li->o)->data.vendor.vendor_id;
+		i++;
+		ASSERT( i <= dict->dict_count[DICT_VENDOR] );
+	}
+out:	
+	/* Release the lock */
+	CHECK_POSIX_DO(  pthread_rwlock_unlock(&dict->dict_lock), return NULL  );
+	
+	return ret;
+}
+
+/* Return the location of the cb list for an object, after checking its type */
+int fd_dict_disp_cb(enum dict_object_type type, struct dict_object *obj, struct fd_list ** cb_list)
+{
+	TRACE_ENTRY("%d %p %p", type, obj, cb_list);
+	CHECK_PARAMS( verify_object(obj) );
+	CHECK_PARAMS( _OBINFO(obj).type == type );
+	CHECK_PARAMS( cb_list );
+	*cb_list = &obj->disp_cbs;
+	return 0;
+}
+
+int fd_dict_get_error_cmd(struct dictionary * dict, struct dict_object **obj)
+{
+	TRACE_ENTRY("%p %p", dict, obj);
+	CHECK_PARAMS( dict && (dict->dict_eyec == DICT_EYECATCHER) && obj );
+	*obj = &dict->dict_cmd_error;
+	return 0;
+}
Index: libfdproto/dispatch.c
===================================================================
--- libfdproto/dispatch.c	(revision 658)
+++ libfdproto/dispatch.c	(revision 658)
@@ -0,0 +1,218 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@nict.go.jp>							 *
+*													 *
+* Copyright (c) 2010, 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 "fdproto-internal.h"
+
+/* The dispatch module in the library is quite simple: callbacks are saved in a global list
+ * in no particular order. In addition, they are also linked from the dictionary objects they
+ * refer to. */
+
+/* Protection for the lists managed in this module. */
+pthread_rwlock_t fd_disp_lock = PTHREAD_RWLOCK_INITIALIZER;
+
+/* List of all registered handlers -- useful if we want to cleanup properly at some point... */
+static struct fd_list all_handlers = FD_LIST_INITIALIZER( all_handlers );
+
+/* List of handlers registered for DISP_HOW_ANY. Other handlers are stored in the dictionary */
+static struct fd_list any_handlers = FD_LIST_INITIALIZER( any_handlers );
+
+/* The structure to store a callback */
+struct disp_hdl {
+	int		 eyec;	/* Eye catcher, DISP_EYEC */
+	struct fd_list	 all;	/* link in the all_handlers list */
+	struct fd_list	 parent;/* link in dictionary cb_list or in any_handlers */
+	enum disp_how	 how;	/* Copy of registration parameter */
+	struct disp_when when;	/* Copy of registration parameter */
+	int		(*cb)( struct msg **, struct avp *, struct session *, void *, enum disp_action *);	/* The callback itself */
+	void            *opaque; /* opaque data passed back to the callback */
+};
+
+#define DISP_EYEC	0xD15241C1
+#define VALIDATE_HDL( _hdl ) \
+	( ( ( _hdl ) != NULL ) && ( ((struct disp_hdl *)( _hdl ))->eyec == DISP_EYEC ) )
+
+/**************************************************************************************/
+
+/* Call CBs from a given list (any_handlers if cb_list is NULL) -- must have locked fd_disp_lock before */
+int fd_disp_call_cb_int( struct fd_list * cb_list, struct msg ** msg, struct avp *avp, struct session *sess, enum disp_action *action, 
+			struct dict_object * obj_app, struct dict_object * obj_cmd, struct dict_object * obj_avp, struct dict_object * obj_enu)
+{
+	struct fd_list * senti, *li;
+	TRACE_ENTRY("%p %p %p %p %p %p %p %p %p", cb_list, msg, avp, sess, action, obj_app, obj_cmd, obj_avp, obj_enu);
+	CHECK_PARAMS(msg && action);
+	
+	senti = cb_list;
+	if (!senti)
+		senti = &any_handlers;
+	
+	for (li = senti->next; li != senti; li = li->next) {
+		struct disp_hdl * hdl = (struct disp_hdl *)(li->o);
+		
+		TRACE_DEBUG(ANNOYING, "when: %p %p %p %p", hdl->when.app, hdl->when.command, hdl->when.avp, hdl->when.value);
+		
+		/* Check this handler matches this message / avp */
+		if (hdl->when.app     && (hdl->when.app     != obj_app))
+			continue;
+		if (hdl->when.command && (hdl->when.command != obj_cmd))
+			continue;
+		if (hdl->when.avp     && (hdl->when.avp     != obj_avp))
+			continue;
+		if (hdl->when.value   && (hdl->when.value   != obj_enu))
+			continue;
+		
+		/* We have a match, the cb must be called. */
+		CHECK_FCT( (*hdl->cb)(msg, avp, sess, hdl->opaque, action) );
+		
+		if (*action != DISP_ACT_CONT)
+			break;
+		
+		if ( *msg  == NULL )
+			break;
+	}
+	
+	/* We're done on this list */
+	return 0;
+}
+
+/**************************************************************************************/
+
+/* Create a new handler and link it */
+int fd_disp_register ( int (*cb)( struct msg **, struct avp *, struct session *, void *, enum disp_action *), 
+			enum disp_how how, struct disp_when * when, void * opaque, struct disp_hdl ** handle )
+{
+	struct fd_list * cb_list = NULL;
+	struct disp_hdl * new;
+	struct dict_object * type_enum, * type_avp;
+	struct dictionary  * dict = NULL;
+	
+	TRACE_ENTRY("%p %d %p %p", cb, how, when, handle);
+	CHECK_PARAMS( cb && ( (how == DISP_HOW_ANY) || when ));
+	
+	switch (how) {
+		case DISP_HOW_ANY:
+			cb_list = &any_handlers;
+			break;
+		
+		case DISP_HOW_APPID:
+			CHECK_FCT( fd_dict_disp_cb(DICT_APPLICATION, when->app, &cb_list) );
+			break;
+		
+		case DISP_HOW_CC:
+			CHECK_FCT( fd_dict_disp_cb(DICT_COMMAND, when->command, &cb_list) );
+			break;
+		
+		case DISP_HOW_AVP_ENUMVAL:
+			CHECK_FCT( fd_dict_disp_cb(DICT_ENUMVAL, when->value, &cb_list) ); /* cb_list is then overwriten */
+			CHECK_FCT( fd_dict_getdict(when->value, &dict) );
+			CHECK_FCT( fd_dict_search(dict, DICT_TYPE, TYPE_OF_ENUMVAL, when->value, &type_enum, EINVAL) );
+		case DISP_HOW_AVP:
+			CHECK_FCT( fd_dict_disp_cb(DICT_AVP, when->avp, &cb_list) );
+			if (dict) {
+				CHECK_FCT( fd_dict_search(dict, DICT_TYPE, TYPE_OF_AVP, when->avp, &type_avp, EINVAL) );
+				CHECK_PARAMS( type_enum == type_avp );
+			}
+			break;
+		
+		default:
+			CHECK_PARAMS(how = 0);
+	}
+	/* We might further check optional fields, but we trust the caller ^^ */
+	
+	/* Create the new handler */
+	CHECK_MALLOC( new = malloc( sizeof(struct disp_hdl) ) );
+	memset(new, 0, sizeof(struct disp_hdl));
+	new->eyec = DISP_EYEC;
+	fd_list_init(&new->all, new);
+	fd_list_init(&new->parent, new);
+	new->how = how;
+	switch (how) {
+		case DISP_HOW_ANY:
+			/* there is no "when" in that case */
+			break;
+		case DISP_HOW_AVP_ENUMVAL:
+			new->when.value   = when->value;
+		case DISP_HOW_AVP:
+			new->when.avp     = when->avp;
+		case DISP_HOW_CC:
+			new->when.command = when->command;
+		case DISP_HOW_APPID:
+			new->when.app     = when->app;
+	}
+	new->cb = cb;
+	new->opaque = opaque;
+	
+	/* Now, link this new element in the appropriate lists */
+	CHECK_POSIX( pthread_rwlock_wrlock(&fd_disp_lock) );
+	fd_list_insert_before(&all_handlers, &new->all);
+	fd_list_insert_before(cb_list, &new->parent);
+	CHECK_POSIX( pthread_rwlock_unlock(&fd_disp_lock) );
+	
+	/* We're done */
+	if (handle)
+		*handle = new;
+	
+	return 0;
+}
+
+/* Delete a handler */
+int fd_disp_unregister ( struct disp_hdl ** handle, void ** opaque )
+{
+	struct disp_hdl * del;
+	TRACE_ENTRY("%p", handle);
+	CHECK_PARAMS( handle && VALIDATE_HDL(*handle) );
+	del = *handle;
+	*handle = NULL;
+	
+	CHECK_POSIX( pthread_rwlock_wrlock(&fd_disp_lock) );
+	fd_list_unlink(&del->all);
+	fd_list_unlink(&del->parent);
+	CHECK_POSIX( pthread_rwlock_unlock(&fd_disp_lock) );
+	
+	if (opaque)
+		*opaque = del->opaque;
+	
+	free(del);
+	return 0;
+}
+
+/* Delete all handlers */
+void fd_disp_unregister_all ( void )
+{
+	TRACE_ENTRY("");
+	while (!FD_IS_LIST_EMPTY(&all_handlers)) {
+		CHECK_FCT_DO( fd_disp_unregister((void *)&(all_handlers.next->o), NULL), /* continue */ );
+	}
+	return;
+}
Index: libfdproto/fdproto-internal.h
===================================================================
--- libfdproto/fdproto-internal.h	(revision 658)
+++ libfdproto/fdproto-internal.h	(revision 658)
@@ -0,0 +1,65 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@nict.go.jp>							 *
+*													 *
+* Copyright (c) 2010, 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 definitions for internal use in the freeDiameter protocol library */
+
+#ifndef _LIBFDPROTO_INTERNAL_H
+#define _LIBFDPROTO_INTERNAL_H
+
+#include <freeDiameter/freeDiameter-host.h>
+#include <freeDiameter/libfdproto.h>
+
+/* Internal to the library */
+extern const char * type_base_name[];
+void fd_msg_eteid_init(void);
+int fd_sess_init(void);
+void fd_sess_fini(void);
+
+/* Iterator on the rules of a parent object */
+int fd_dict_iterate_rules ( struct dict_object *parent, void * data, int (*cb)(void *, struct dict_rule_data *) );
+
+/* Dispatch / messages / dictionary API */
+int fd_dict_disp_cb(enum dict_object_type type, struct dict_object *obj, struct fd_list ** cb_list);
+void fd_dict_dump_avp_value(union avp_value *avp_value, struct dict_object * model, int indent);
+int fd_disp_call_cb_int( struct fd_list * cb_list, struct msg ** msg, struct avp *avp, struct session *sess, enum disp_action *action, 
+			struct dict_object * obj_app, struct dict_object * obj_cmd, struct dict_object * obj_avp, struct dict_object * obj_enu);
+extern pthread_rwlock_t fd_disp_lock;
+
+/* Messages / sessions API */
+int fd_sess_fromsid_msg ( unsigned char * sid, size_t len, struct session ** session, int * new);
+int fd_sess_ref_msg ( struct session * session );
+int fd_sess_reclaim_msg ( struct session ** session );
+
+#endif /* _LIBFDPROTO_INTERNAL_H */
Index: libfdproto/fifo.c
===================================================================
--- libfdproto/fifo.c	(revision 658)
+++ libfdproto/fifo.c	(revision 658)
@@ -0,0 +1,500 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@nict.go.jp>							 *
+*													 *
+* Copyright (c) 2010, 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.								 *
+*********************************************************************************************************/
+
+/* FIFO queues module.
+ *
+ * The threads that call these functions must be in the cancellation state PTHREAD_CANCEL_ENABLE and type PTHREAD_CANCEL_DEFERRED.
+ * This is the default state and type on thread creation.
+ *
+ * In order to destroy properly a queue, the application must:
+ *  -> shutdown any process that can add into the queue first.
+ *  -> pthread_cancel any thread that could be waiting on the queue.
+ *  -> consume any element that is in the queue, using fd_qu_tryget_int.
+ *  -> then destroy the queue using fd_mq_del.
+ */
+
+#include "fdproto-internal.h"
+
+/* Definition of a FIFO queue object */
+struct fifo {
+	int		eyec;	/* An eye catcher, also used to check a queue is valid. FIFO_EYEC */
+	
+	pthread_mutex_t	mtx;	/* Mutex protecting this queue */
+	pthread_cond_t	cond;	/* condition variable of the list */
+	
+	struct fd_list	list;	/* sentinel for the list of elements */
+	int		count;	/* number of objects in the list */
+	int		thrs;	/* number of threads waiting for a new element (when count is 0) */
+	
+	uint16_t	high;	/* High level threshold (see libfreeDiameter.h for details) */
+	uint16_t	low;	/* Low level threshhold */
+	void 		*data;	/* Opaque pointer for threshold callbacks */
+	void		(*h_cb)(struct fifo *, void **); /* The callbacks */
+	void		(*l_cb)(struct fifo *, void **);
+	int 		highest;/* The highest count value for which h_cb has been called */
+	int		highest_ever; /* The max count value this queue has reached (for tweaking) */
+};
+
+/* The eye catcher value */
+#define FIFO_EYEC	0xe7ec1130
+
+/* Macro to check a pointer */
+#define CHECK_FIFO( _queue ) (( (_queue) != NULL) && ( (_queue)->eyec == FIFO_EYEC) )
+
+
+/* Create a new queue */
+int fd_fifo_new ( struct fifo ** queue )
+{
+	struct fifo * new;
+	
+	TRACE_ENTRY( "%p", queue );
+	
+	CHECK_PARAMS( queue );
+	
+	/* Create a new object */
+	CHECK_MALLOC( new = malloc (sizeof (struct fifo) )  );
+	
+	/* Initialize the content */
+	memset(new, 0, sizeof(struct fifo));
+	
+	new->eyec = FIFO_EYEC;
+	CHECK_POSIX( pthread_mutex_init(&new->mtx, NULL) );
+	CHECK_POSIX( pthread_cond_init(&new->cond, NULL) );
+	
+	fd_list_init(&new->list, NULL);
+	
+	/* We're done */
+	*queue = new;
+	return 0;
+}
+
+/* Dump the content of a queue */
+void fd_fifo_dump(int level, char * name, struct fifo * queue, void (*dump_item)(int level, void * item))
+{
+	TRACE_ENTRY("%i %p %p %p", level, name, queue, dump_item);
+	
+	if (!TRACE_BOOL(level))
+		return;
+	
+	fd_log_debug("Dumping queue '%s' (%p):\n", name ?: "?", queue);
+	if (!CHECK_FIFO( queue )) {
+		fd_log_debug("  Queue invalid!\n");
+		if (queue)
+			fd_log_debug("  (%x != %x)\n", queue->eyec, FIFO_EYEC);
+		return;
+	}
+	
+	CHECK_POSIX_DO(  pthread_mutex_lock( &queue->mtx ), /* continue */  );
+	fd_log_debug("   %d elements in queue / %d threads waiting\n", queue->count, queue->thrs);
+	fd_log_debug("   thresholds: %d / %d (h:%d), cb: %p,%p (%p), highest: %d\n",
+			queue->high, queue->low, queue->highest, 
+			queue->h_cb, queue->l_cb, queue->data,
+			queue->highest_ever);
+	
+	if (dump_item) {
+		struct fd_list * li;
+		int i = 0;
+		for (li = queue->list.next; li != &queue->list; li = li->next) {
+			fd_log_debug("  [%i] item %p in fifo %p:\n", i++, li->o, queue);
+			(*dump_item)(level, li->o);
+		}
+	}
+	CHECK_POSIX_DO(  pthread_mutex_unlock( &queue->mtx ), /* continue */  );
+	
+}
+
+/* Delete a queue. It must be empty. */ 
+int fd_fifo_del ( struct fifo  ** queue )
+{
+	struct fifo * q;
+	int loops = 0;
+	
+	TRACE_ENTRY( "%p", queue );
+
+	CHECK_PARAMS( queue && CHECK_FIFO( *queue ) );
+	
+	q = *queue;
+	
+	CHECK_POSIX(  pthread_mutex_lock( &q->mtx )  );
+	
+	if ((q->count != 0) || (q->data != NULL)) {
+		TRACE_DEBUG(INFO, "The queue cannot be destroyed (%d, %p)", q->count, q->data);
+		CHECK_POSIX_DO(  pthread_mutex_unlock( &q->mtx ), /* no fallback */  );
+		return EINVAL;
+	}
+	
+	/* Ok, now invalidate the queue */
+	q->eyec = 0xdead;
+	
+	/* Have all waiting threads return an error */
+	while (q->thrs) {
+		CHECK_POSIX(  pthread_mutex_unlock( &q->mtx ));
+		CHECK_POSIX(  pthread_cond_signal(&q->cond)  );
+		sched_yield();
+		if (loops >= 10)
+			/* sleep for a few milliseconds */
+			usleep(50000);
+		
+		CHECK_POSIX(  pthread_mutex_lock( &q->mtx )  );
+		ASSERT( ++loops < 20 ); /* detect infinite loops */
+	}
+	
+	/* sanity check */
+	ASSERT(FD_IS_LIST_EMPTY(&q->list));
+	
+	/* And destroy it */
+	CHECK_POSIX(  pthread_mutex_unlock( &q->mtx )  );
+	
+	CHECK_POSIX_DO(  pthread_cond_destroy( &q->cond ),  );
+	
+	CHECK_POSIX_DO(  pthread_mutex_destroy( &q->mtx ),  );
+	
+	free(q);
+	*queue = NULL;
+	
+	return 0;
+}
+
+/* Move the content of old into new, and update loc_update atomically. We leave the old queue empty but valid */
+int fd_fifo_move ( struct fifo * old, struct fifo * new, struct fifo ** loc_update )
+{
+	int loops = 0;
+	
+	TRACE_ENTRY("%p %p %p", old, new, loc_update);
+	CHECK_PARAMS( CHECK_FIFO( old ) && CHECK_FIFO( new ));
+	
+	CHECK_PARAMS( ! old->data );
+	if (new->high) {
+		TODO("Implement support for thresholds in fd_fifo_move...");
+	}
+	
+	/* Update loc_update */
+	if (loc_update)
+		*loc_update = new;
+	
+	/* Lock the queues */
+	CHECK_POSIX(  pthread_mutex_lock( &old->mtx )  );
+	CHECK_POSIX(  pthread_mutex_lock( &new->mtx )  );
+	
+	/* Any waiting thread on the old queue returns an error */
+	old->eyec = 0xdead;
+	while (old->thrs) {
+		CHECK_POSIX(  pthread_mutex_unlock( &old->mtx ));
+		CHECK_POSIX(  pthread_cond_signal(&old->cond)  );
+		sched_yield();
+		if (loops >= 10)
+			/* sleep for a few milliseconds */
+			usleep(50000);
+		
+		CHECK_POSIX(  pthread_mutex_lock( &old->mtx )  );
+		ASSERT( ++loops < 20 ); /* detect infinite loops */
+	}
+	
+	/* Move all data from old to new */
+	fd_list_move_end( &new->list, &old->list );
+	if (old->count && (!new->count)) {
+		CHECK_POSIX(  pthread_cond_signal(&new->cond)  );
+	}
+	new->count += old->count;
+	
+	/* Reset old */
+	old->count = 0;
+	old->eyec = FIFO_EYEC;
+	
+	/* Unlock, we're done */
+	CHECK_POSIX(  pthread_mutex_unlock( &new->mtx )  );
+	CHECK_POSIX(  pthread_mutex_unlock( &old->mtx )  );
+	
+	return 0;
+}
+
+/* Get the length of the queue */
+int fd_fifo_length ( struct fifo * queue, int * length )
+{
+	TRACE_ENTRY( "%p %p", queue, length );
+	
+	/* Check the parameters */
+	CHECK_PARAMS( CHECK_FIFO( queue ) && length );
+	
+	/* lock the queue */
+	CHECK_POSIX(  pthread_mutex_lock( &queue->mtx )  );
+	
+	/* Retrieve the count */
+	*length = queue->count;
+	
+	/* Unlock */
+	CHECK_POSIX(  pthread_mutex_unlock( &queue->mtx )  );
+	
+	/* Done */
+	return 0;
+}
+
+/* alternate version with no error checking */
+int fd_fifo_length_noerr ( struct fifo * queue )
+{
+	if ( !CHECK_FIFO( queue ) )
+		return 0;
+	
+	return queue->count; /* Let's hope it's read atomically, since we are not locking... */
+}
+
+/* Set the thresholds of the queue */
+int fd_fifo_setthrhd ( struct fifo * queue, void * data, uint16_t high, void (*h_cb)(struct fifo *, void **), uint16_t low, void (*l_cb)(struct fifo *, void **) )
+{
+	TRACE_ENTRY( "%p %p %hu %p %hu %p", queue, data, high, h_cb, low, l_cb );
+	
+	/* Check the parameters */
+	CHECK_PARAMS( CHECK_FIFO( queue ) && (high > low) && (queue->data == NULL) );
+	
+	/* lock the queue */
+	CHECK_POSIX(  pthread_mutex_lock( &queue->mtx )  );
+	
+	/* Save the values */
+	queue->high = high;
+	queue->low  = low;
+	queue->data = data;
+	queue->h_cb = h_cb;
+	queue->l_cb = l_cb;
+	
+	/* Unlock */
+	CHECK_POSIX(  pthread_mutex_unlock( &queue->mtx )  );
+	
+	/* Done */
+	return 0;
+}
+
+/* Post a new item in the queue */
+int fd_fifo_post_int ( struct fifo * queue, void ** item )
+{
+	struct fd_list * new;
+	int call_cb = 0;
+	
+	TRACE_ENTRY( "%p %p", queue, item );
+	
+	/* Check the parameters */
+	CHECK_PARAMS( CHECK_FIFO( queue ) && item && *item );
+	
+	/* Create a new list item */
+	CHECK_MALLOC(  new = malloc (sizeof (struct fd_list))  );
+	
+	fd_list_init(new, *item);
+	*item = NULL;
+	
+	/* lock the queue */
+	CHECK_POSIX(  pthread_mutex_lock( &queue->mtx )  );
+	
+	/* Add the new item at the end */
+	fd_list_insert_before( &queue->list, new);
+	queue->count++;
+	if (queue->highest_ever < queue->count)
+		queue->highest_ever = queue->count;
+	if (queue->high && ((queue->count % queue->high) == 0)) {
+		call_cb = 1;
+		queue->highest = queue->count;
+	}
+	
+	/* Signal if threads are asleep */
+	if (queue->thrs > 0) {
+		CHECK_POSIX(  pthread_cond_signal(&queue->cond)  );
+	}
+	
+	/* Unlock */
+	CHECK_POSIX(  pthread_mutex_unlock( &queue->mtx )  );
+	
+	/* Call high-watermark cb as needed */
+	if (call_cb && queue->h_cb)
+		(*queue->h_cb)(queue, &queue->data);
+	
+	/* Done */
+	return 0;
+}
+
+/* Pop the first item from the queue */
+static void * mq_pop(struct fifo * queue)
+{
+	void * ret = NULL;
+	struct fd_list * li;
+	
+	ASSERT( ! FD_IS_LIST_EMPTY(&queue->list) );
+	
+	fd_list_unlink(li = queue->list.next);
+	queue->count--;
+	ret = li->o;
+	free(li);
+	
+	return ret;
+}
+
+/* Check if the low watermark callback must be called. */
+static __inline__ int test_l_cb(struct fifo * queue)
+{
+	if ((queue->high == 0) || (queue->low == 0) || (queue->l_cb == 0))
+		return 0;
+	
+	if (((queue->count % queue->high) == queue->low) && (queue->highest > queue->count)) {
+		queue->highest -= queue->high;
+		return 1;
+	}
+	
+	return 0;
+}
+
+/* Try poping an item */
+int fd_fifo_tryget_int ( struct fifo * queue, void ** item )
+{
+	int wouldblock = 0;
+	int call_cb = 0;
+	
+	TRACE_ENTRY( "%p %p", queue, item );
+	
+	/* Check the parameters */
+	CHECK_PARAMS( CHECK_FIFO( queue ) && item );
+	
+	/* lock the queue */
+	CHECK_POSIX(  pthread_mutex_lock( &queue->mtx )  );
+	
+	/* Check queue status */
+	if (queue->count > 0) {
+		/* There are elements in the queue, so pick the first one */
+		*item = mq_pop(queue);
+		call_cb = test_l_cb(queue);
+	} else {
+		wouldblock = 1;
+		*item = NULL;
+	}
+		
+	/* Unlock */
+	CHECK_POSIX(  pthread_mutex_unlock( &queue->mtx )  );
+	
+	/* Call low watermark callback as needed */
+	if (call_cb)
+		(*queue->l_cb)(queue, &queue->data);
+	
+	/* Done */
+	return wouldblock ? EWOULDBLOCK : 0;
+}
+
+/* This handler is called when a thread is blocked on a queue, and cancelled */
+static void fifo_cleanup(void * queue)
+{
+	struct fifo * q = (struct fifo *)queue;
+	TRACE_ENTRY( "%p", queue );
+	
+	/* The thread has been cancelled, therefore it does not wait on the queue anymore */
+	q->thrs--;
+	
+	/* Now unlock the queue, and we're done */
+	CHECK_POSIX_DO(  pthread_mutex_unlock( &q->mtx ),  /* nothing */  );
+	
+	/* End of cleanup handler */
+	return;
+}
+
+/* The internal function for fd_fifo_timedget and fd_fifo_get */
+static int fifo_tget ( struct fifo * queue, void ** item, int istimed, const struct timespec *abstime)
+{
+	int timedout = 0;
+	int call_cb = 0;
+	
+	/* Check the parameters */
+	CHECK_PARAMS( CHECK_FIFO( queue ) && item && (abstime || !istimed) );
+	
+	/* Initialize the return value */
+	*item = NULL;
+	
+	/* lock the queue */
+	CHECK_POSIX(  pthread_mutex_lock( &queue->mtx )  );
+	
+awaken:
+	/* Check queue status */
+	if (!CHECK_FIFO( queue )) {
+		/* The queue is being destroyed */
+		CHECK_POSIX(  pthread_mutex_unlock( &queue->mtx )  );
+		TRACE_DEBUG(FULL, "The queue is being destroyed -> EPIPE");
+		return EPIPE;
+	}
+		
+	if (queue->count > 0) {
+		/* There are items in the queue, so pick the first one */
+		*item = mq_pop(queue);
+		call_cb = test_l_cb(queue);
+	} else {
+		int ret = 0;
+		/* We have to wait for a new item */
+		queue->thrs++ ;
+		pthread_cleanup_push( fifo_cleanup, queue);
+		if (istimed) {
+			ret = pthread_cond_timedwait( &queue->cond, &queue->mtx, abstime );
+		} else {
+			ret = pthread_cond_wait( &queue->cond, &queue->mtx );
+		}
+		pthread_cleanup_pop(0);
+		queue->thrs-- ;
+		if (ret == 0)
+			goto awaken;  /* test for spurious wake-ups */
+		
+		if (istimed && (ret == ETIMEDOUT)) {
+			timedout = 1;
+		} else {
+			/* Unexpected error condition (means we need to debug) */
+			ASSERT( ret == 0 /* never true */ );
+		}
+	}
+	
+	/* Unlock */
+	CHECK_POSIX(  pthread_mutex_unlock( &queue->mtx )  );
+	
+	/* Call low watermark callback as needed */
+	if (call_cb)
+		(*queue->l_cb)(queue, &queue->data);
+	
+	/* Done */
+	return timedout ? ETIMEDOUT : 0;
+}
+
+/* Get the next available item, block until there is one */
+int fd_fifo_get_int ( struct fifo * queue, void ** item )
+{
+	TRACE_ENTRY( "%p %p", queue, item );
+	return fifo_tget(queue, item, 0, NULL);
+}
+
+/* Get the next available item, block until there is one, or the timeout expires */
+int fd_fifo_timedget_int ( struct fifo * queue, void ** item, const struct timespec *abstime )
+{
+	TRACE_ENTRY( "%p %p %p", queue, item, abstime );
+	return fifo_tget(queue, item, 1, abstime);
+}
+
Index: libfdproto/init.c
===================================================================
--- libfdproto/init.c	(revision 658)
+++ libfdproto/init.c	(revision 658)
@@ -0,0 +1,64 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@nict.go.jp>							 *
+*													 *
+* Copyright (c) 2010, 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 "fdproto-internal.h"
+
+/* Only for CPU cache flush */
+pthread_mutex_t fd_cpu_mtx_dummy = PTHREAD_MUTEX_INITIALIZER;
+
+/* Initialize library variables and threads */
+int fd_libproto_init()
+{
+	int ret = 0;
+	
+	/* Create the thread key that contains thread name for debug messages */
+	ret = pthread_key_create(&fd_log_thname, free);
+	if (ret != 0) {
+		fprintf(stderr, "Error initializing the libfreeDiameter library: %s\n", strerror(ret) );
+		return ret;
+	}
+	
+	/* Initialize the modules that need it */
+	fd_msg_eteid_init();
+	CHECK_FCT( fd_sess_init() );
+	
+	return 0;
+}
+
+/* Stop all threads created in the library */
+void fd_libproto_fini(void)
+{
+	fd_sess_fini();
+}
Index: libfdproto/lists.c
===================================================================
--- libfdproto/lists.c	(revision 658)
+++ libfdproto/lists.c	(revision 658)
@@ -0,0 +1,290 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@nict.go.jp>							 *
+*													 *
+* Copyright (c) 2009, WIDE Project and NICT								 *
+* All rights reserved.											 *
+* 													 *
+* Redistribution and use of this software in source and binary forms, with or without modification, are  *
+* permitted provided that the following conditions are met:						 *
+* 													 *
+* * Redistributions of source code must retain the above 						 *
+*   copyright notice, this list of conditions and the 							 *
+*   following disclaimer.										 *
+*    													 *
+* * Redistributions in binary form must reproduce the above 						 *
+*   copyright notice, this list of conditions and the 							 *
+*   following disclaimer in the documentation and/or other						 *
+*   materials provided with the distribution.								 *
+* 													 *
+* * Neither the name of the WIDE Project or NICT nor the 						 *
+*   names of its contributors may be used to endorse or 						 *
+*   promote products derived from this software without 						 *
+*   specific prior written permission of WIDE Project and 						 *
+*   NICT.												 *
+* 													 *
+* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED *
+* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
+* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR *
+* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 	 *
+* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 	 *
+* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR *
+* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF   *
+* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.								 *
+*********************************************************************************************************/
+
+#include "fdproto-internal.h"
+
+/* Initialize a list element */
+void fd_list_init ( struct fd_list * list, void * obj )
+{
+	memset(list, 0, sizeof(struct fd_list));
+	list->next = list;
+	list->prev = list;
+	list->head = list;
+	list->o    = obj;
+}
+
+#define CHECK_SINGLE( li ) {			\
+	ASSERT( ((struct fd_list *)(li))->next == (li) );	\
+	ASSERT( ((struct fd_list *)(li))->prev == (li) );	\
+	ASSERT( ((struct fd_list *)(li))->head == (li) );	\
+}
+
+/* insert after a reference, checks done */
+static void list_insert_after( struct fd_list * ref, struct fd_list * item )
+{
+	item->prev = ref;
+	item->next = ref->next;
+	item->head = ref->head;
+	ref->next->prev = item;
+	ref->next = item;
+}
+
+/* insert after a reference */
+void fd_list_insert_after  ( struct fd_list * ref, struct fd_list * item )
+{
+	ASSERT(item != NULL);
+	ASSERT(ref != NULL);
+	CHECK_SINGLE ( item );
+	ASSERT(ref->head != item);
+	list_insert_after(ref, item);
+}
+
+/* Move all elements of list senti at the end of list ref */
+void fd_list_move_end(struct fd_list * ref, struct fd_list * senti)
+{
+	ASSERT(ref->head == ref);
+	ASSERT(senti->head == senti);
+	
+	if (senti->next == senti)
+		return;
+	
+	senti->next->prev = ref->prev;
+	ref->prev->next   = senti->next;
+	senti->prev->next = ref;
+	ref->prev         = senti->prev;
+	senti->prev = senti;
+	senti->next = senti;
+}
+
+/* insert before a reference, checks done */
+static void list_insert_before ( struct fd_list * ref, struct fd_list * item )
+{
+	item->prev = ref->prev;
+	item->next = ref;
+	item->head = ref->head;
+	ref->prev->next = item;
+	ref->prev = item;
+}
+
+/* insert before a reference */
+void fd_list_insert_before ( struct fd_list * ref, struct fd_list * item )
+{
+	ASSERT(item != NULL);
+	ASSERT(ref != NULL);
+	CHECK_SINGLE ( item );
+	ASSERT(ref->head != item);
+	list_insert_before(ref, item);
+}
+
+/* Insert an item in an ordered list -- ordering function provided. If duplicate object found, it is returned in ref_duplicate */
+int fd_list_insert_ordered( struct fd_list * head, struct fd_list * item, int (*cmp_fct)(void *, void *), void ** ref_duplicate)
+{
+	struct fd_list * ptr = head;
+	int cmp;
+	
+	/* Some debug sanity checks */
+	ASSERT(head != NULL);
+	ASSERT(item != NULL);
+	ASSERT(cmp_fct != NULL);
+	ASSERT(head->head == head);
+	CHECK_SINGLE ( item );
+	
+	/* loop in the list */
+	while (ptr->next != head)
+	{
+		/* Compare the object to insert with the next object in list */
+		cmp = cmp_fct( item->o, ptr->next->o );
+		if (!cmp) {
+			/* An element with the same key already exists */
+			if (ref_duplicate != NULL)
+				*ref_duplicate = ptr->next->o;
+			return EEXIST;
+		}
+		
+		if (cmp < 0)
+			break; /* We must insert the element here */
+		
+		ptr = ptr->next;
+	}
+	
+	/* Now insert the element between ptr and ptr->next */
+	list_insert_after( ptr, item );
+	
+	/* Ok */
+	return 0;
+}
+	
+/* Unlink an object */
+void fd_list_unlink ( struct fd_list * item )
+{
+	ASSERT(item != NULL);
+	if (item->head == item)
+		return;
+	/* unlink */
+	item->next->prev = item->prev;
+	item->prev->next = item->next;
+	/* sanitize */
+	item->next = item;
+	item->prev = item;
+	item->head = item;
+}
+
+
+/********************************************************************************************************/
+/* Hash function -- credits to Austin Appleby, thank you ^^ */
+/* See http://murmurhash.googlepages.com for more information on this function */
+
+/* the strings are NOT always aligned properly (ex: received in RADIUS message), so we use the aligned MurmurHash2 function as needed */
+#define _HASH_MIX(h,k,m) { k *= m; k ^= k >> r; k *= m; h *= m; h ^= k; }
+uint32_t fd_hash ( char * string, size_t len )
+{
+	uint32_t hash = len;
+	char * data = string;
+	
+	const unsigned int m = 0x5bd1e995;
+	const int r = 24;
+	int align = (long)string & 3;
+	
+	if (!align || (len < 4)) {
+		
+		/* In case data is aligned, MurmurHash2 function */
+		while(len >= 4)
+		{
+			/* Mix 4 bytes at a time into the hash */
+			uint32_t k = *(uint32_t *)data;	/* We don't care about the byte order */
+
+			_HASH_MIX(hash, k, m);
+
+			data += 4;
+			len -= 4;
+		}
+
+		/* Handle the last few bytes of the input */
+		switch(len) {
+			case 3: hash ^= data[2] << 16;
+			case 2: hash ^= data[1] << 8;
+			case 1: hash ^= data[0];
+	        		hash *= m;
+		}
+		
+	} else {
+		/* Unaligned data, use alignment-safe slower version */
+		
+		/* Pre-load the temp registers */
+		uint32_t t = 0, d = 0;
+		switch(align)
+		{
+			case 1: t |= data[2] << 16;
+			case 2: t |= data[1] << 8;
+			case 3: t |= data[0];
+		}
+		t <<= (8 * align);
+
+		data += 4-align;
+		len -= 4-align;
+		
+		/* From this point, "data" can be read by chunks of 4 bytes */
+		
+		int sl = 8 * (4-align);
+		int sr = 8 * align;
+
+		/* Mix */
+		while(len >= 4)
+		{
+			uint32_t k;
+			
+			d = *(unsigned int *)data;
+			k = (t >> sr) | (d << sl);
+
+			_HASH_MIX(hash, k, m);
+
+			t = d;
+
+			data += 4;
+			len -= 4;
+		}
+
+		/* Handle leftover data in temp registers */
+		d = 0;
+		if(len >= align)
+		{
+			uint32_t k;
+			
+			switch(align)
+			{
+			case 3: d |= data[2] << 16;
+			case 2: d |= data[1] << 8;
+			case 1: d |= data[0];
+			}
+
+			k = (t >> sr) | (d << sl);
+			_HASH_MIX(hash, k, m);
+
+			data += align;
+			len -= align;
+
+			/* Handle tail bytes */
+
+			switch(len)
+			{
+			case 3: hash ^= data[2] << 16;
+			case 2: hash ^= data[1] << 8;
+			case 1: hash ^= data[0];
+					hash *= m;
+			};
+		}
+		else
+		{
+			switch(len)
+			{
+			case 3: d |= data[2] << 16;
+			case 2: d |= data[1] << 8;
+			case 1: d |= data[0];
+			case 0: hash ^= (t >> sr) | (d << sl);
+					hash *= m;
+			}
+		}
+
+
+	}
+
+	/* Do a few final mixes of the hash to ensure the last few
+	   bytes are well-incorporated. */
+	hash ^= hash >> 13;
+	hash *= m;
+	hash ^= hash >> 15;
+
+	return hash;
+} 
Index: libfdproto/log.c
===================================================================
--- libfdproto/log.c	(revision 658)
+++ libfdproto/log.c	(revision 658)
@@ -0,0 +1,125 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@nict.go.jp>							 *
+*													 *
+* Copyright (c) 2010, 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 "fdproto-internal.h"
+
+#include <stdarg.h>
+
+pthread_mutex_t fd_log_lock = PTHREAD_MUTEX_INITIALIZER;
+pthread_key_t	fd_log_thname;
+int fd_g_debug_lvl = 0;
+
+/* These may be used to pass specific debug requests via the command-line parameters */
+char * fd_debug_one_function = NULL;
+char * fd_debug_one_file = NULL;
+
+/* Useless function, only to ease setting up a breakpoint in gdb (break fd_breakhere) -- use TRACE_HERE */
+int fd_breaks = 0;
+int fd_breakhere(void) { return ++fd_breaks; }
+
+static void fd_cleanup_mutex_silent( void * mutex )
+{
+	(void)pthread_mutex_unlock((pthread_mutex_t *)mutex);
+}
+
+
+/* Log a debug message */
+void fd_log_debug ( const char * format, ... )
+{
+	va_list ap;
+	
+	(void)pthread_mutex_lock(&fd_log_lock);
+	
+	pthread_cleanup_push(fd_cleanup_mutex_silent, &fd_log_lock);
+	
+	va_start(ap, format);
+	vfprintf( stdout, format, ap);
+	va_end(ap);
+	fflush(stdout);
+
+	pthread_cleanup_pop(0);
+	
+	(void)pthread_mutex_unlock(&fd_log_lock);
+}
+
+/* Function to set the thread's friendly name */
+void fd_log_threadname ( char * name )
+{
+	void * val = NULL;
+	
+	TRACE_ENTRY("%p(%s)", name, name?:"/");
+	
+	/* First, check if a value is already assigned to the current thread */
+	val = pthread_getspecific(fd_log_thname);
+	if (val != NULL) {
+		TRACE_DEBUG(FULL, "Freeing old thread name: %s", val);
+		free(val);
+	}
+	
+	/* Now create the new string */
+	if (name == NULL) {
+		CHECK_POSIX_DO( pthread_setspecific(fd_log_thname, NULL), /* continue */);
+		return;
+	}
+	
+	CHECK_MALLOC_DO( val = strdup(name), return );
+	
+	CHECK_POSIX_DO( pthread_setspecific(fd_log_thname, val), /* continue */);
+	return;
+}
+
+/* Write time into a buffer */
+char * fd_log_time ( struct timespec * ts, char * buf, size_t len )
+{
+	int ret;
+	size_t offset = 0;
+	struct timespec tp;
+	struct tm tm;
+	
+	/* Get current time */
+	if (!ts) {
+		ret = clock_gettime(CLOCK_REALTIME, &tp);
+		if (ret != 0) {
+			snprintf(buf, len, "%s", strerror(ret));
+			return buf;
+		}
+		ts = &tp;
+	}
+	
+	offset += strftime(buf + offset, len - offset, "%D,%T", localtime_r( &ts->tv_sec , &tm ));
+	offset += snprintf(buf + offset, len - offset, ".%6.6ld", ts->tv_nsec / 1000);
+
+	return buf;
+}
Index: libfdproto/messages.c
===================================================================
--- libfdproto/messages.c	(revision 658)
+++ libfdproto/messages.c	(revision 658)
@@ -0,0 +1,2310 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@nict.go.jp>							 *
+*													 *
+* 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.								 *
+*********************************************************************************************************/
+
+/* Messages module.
+ * 
+ * This module allows to manipulate the msg and avp structures that represents a Diameter message in memory.
+ */
+
+#include "fdproto-internal.h"
+
+#include <sys/param.h>
+
+/* Type of object */
+enum msg_objtype {
+	MSG_MSG = 1, 
+	MSG_AVP
+};
+
+/* Chaining of elements as a free hierarchy */
+struct msg_avp_chain {
+	struct fd_list		chaining;	/* Chaining information at this level. */
+	struct fd_list		children;	/* sentinel for the children of this object */
+	enum msg_objtype 	type;		/* Type of this object, _MSG_MSG or _MSG_AVP */
+};
+
+/* Return the chain information from an AVP or MSG. Since it's the first field, we just cast */
+#define _C(_x) ((struct msg_avp_chain *)(_x))
+
+/* Some details about chaining:
+ *
+ *  A message is made of a header ( msg ) and 0 or more AVPs ( avp ).
+ * The structure is a kind of tree, where some AVPs (grouped AVPs) can contain other AVPs.
+ * Exemple:
+ * msg
+ *  |-avp
+ *  |-gavp
+ *  |   |-avp
+ *  |   |-avp
+ *  |   \-avp
+ *  |-avp
+ *  \-avp
+ *
+ * Each item (msg or avp) structure begins with a msg_avp_chain structure.
+ * The element at the top of the hierarchy (msg in our example) has all the fields of its "chaining" equal to the same value.
+ *
+ * All elements at the same level are linked by their "chaining" list.
+ * The "children" list is the sentinel for the lists of children of this element.
+ */
+
+/* The following definitions are used to recognize objects in memory. */
+#define MSG_MSG_EYEC	(0x11355463)
+#define MSG_AVP_EYEC	(0x11355467)
+
+/* The following structure represents an AVP instance. */
+struct avp {
+	struct msg_avp_chain	 avp_chain;		/* Chaining information of this AVP */
+	int			 avp_eyec;		/* Must be equal to MSG_AVP_EYEC */
+	struct dict_object	*avp_model;		/* If not NULL, pointer to the dictionary object of this avp */
+	struct avp_hdr		 avp_public;		/* AVP data that can be managed by other modules */
+	
+	uint8_t			*avp_source;		/* If the message was parsed from a buffer, pointer to the AVP data start in the buffer. */
+	uint8_t			*avp_rawdata;		/* when the data can not be interpreted, the raw data is copied here. The header is not part of it. */
+	size_t			 avp_rawlen;		/* The length of the raw buffer. */
+	union avp_value		 avp_storage;		/* To avoid many alloc/free, store the integer values here and set avp_public.avp_data to &storage */
+	int			 avp_mustfreeos;	/* 1 if an octetstring is malloc'd in avp_storage and must be freed. */
+};
+
+/* Macro to compute the AVP header size */
+#define AVPHDRSZ_NOVEND	8
+#define AVPHDRSZ_VENDOR	12
+#define GETAVPHDRSZ( _flag ) ((_flag & AVP_FLAG_VENDOR) ? AVPHDRSZ_VENDOR : AVPHDRSZ_NOVEND)
+
+/* Macro to cast a msg_avp_t */
+#define _A(_x) ((struct avp *)(_x))
+/* Check the type and eyecatcher */
+#define CHECK_AVP(_x) ((_C(_x)->type == MSG_AVP) && (_A(_x)->avp_eyec == MSG_AVP_EYEC))
+
+/* The following structure represents an instance of a message (command and children AVPs). */
+struct msg {
+	struct msg_avp_chain	 msg_chain;		/* List of the AVPs in the message */
+	int			 msg_eyec;		/* Must be equal to MSG_MSG_EYEC */
+	struct dict_object	*msg_model;		/* If not NULL, pointer to the dictionary object of this message */
+	struct msg_hdr		 msg_public;		/* Message data that can be managed by extensions. */
+	
+	uint8_t			*msg_rawbuffer;		/* data buffer that was received, saved during fd_msg_parse_buffer and freed in fd_msg_parse_dict */
+	int			 msg_routable;		/* Is this a routable message? (0: undef, 1: routable, 2: non routable) */
+	struct msg		*msg_query;		/* the associated query if the message is a received answer */
+	int			 msg_associated;	/* and the counter part information in the query, to avoid double free */
+	struct rt_data		*msg_rtdata;		/* Routing list for the query */
+	struct session		*msg_sess;		/* Cached message session if any */
+	struct {
+			void (*fct)(void *, struct msg **);
+			void * data;
+			struct timespec timeout;
+		}		 msg_cb;		/* Callback to be called when an answer is received, if not NULL */
+	char *			 msg_src_id;		/* Diameter Id of the peer this message was received from. This string is malloc'd and must be freed */
+};
+
+/* Macro to compute the message header size */
+#define GETMSGHDRSZ() 	20
+
+/* Macro to cast a msg_avp_t */
+#define _M(_x) ((struct msg *)(_x))
+/* Check the type and eyecatcher */
+#define CHECK_MSG(_x) ((_C(_x)->type == MSG_MSG) && (_M(_x)->msg_eyec == MSG_MSG_EYEC))
+
+#define VALIDATE_OBJ(_x) ( (CHECK_MSG(_x)) || (CHECK_AVP(_x)) )
+
+
+/* Macro to validate a MSGFL_ value */
+#define CHECK_AVPFL(_fl) ( ((_fl) & (- (AVPFL_MAX << 1) )) == 0 )
+#define CHECK_MSGFL(_fl) ( ((_fl) & (- (MSGFL_MAX << 1) )) == 0 )
+
+
+/* initial sizes of AVP from their types, in bytes. */
+static int avp_value_sizes[] = { 
+	 0,	/* AVP_TYPE_GROUPED: size is dynamic */
+	 0,	/* AVP_TYPE_OCTETSTRING: size is dynamic */
+	 4,	/* AVP_TYPE_INTEGER32: size is 32 bits */
+	 8,	/* AVP_TYPE_INTEGER64: size is 64 bits */
+	 4,	/* AVP_TYPE_UNSIGNED32: size is 32 bits */
+	 8,	/* AVP_TYPE_UNSIGNED64: size is 64 bits */
+	 4,	/* AVP_TYPE_FLOAT32: size is 32 bits */
+	 8	/* AVP_TYPE_FLOAT64: size is 64 bits */
+};
+#define CHECK_BASETYPE( _type ) ( ((_type) <= AVP_TYPE_MAX) && ((_type) >= 0) )
+#define GETINITIALSIZE( _type, _vend ) (avp_value_sizes[ CHECK_BASETYPE(_type) ? (_type) : 0] + GETAVPHDRSZ(_vend))
+
+/* Forward declaration */
+static int parsedict_do_msg(struct dictionary * dict, struct msg * msg, int only_hdr, struct fd_pei *error_info);
+
+/***************************************************************************************************************/
+/* Creating objects */
+
+/* Initialize a msg_avp_chain structure */
+static void init_chain(struct msg_avp_chain * chain, int type)
+{
+	fd_list_init( &chain->chaining, (void *)chain);
+	fd_list_init( &chain->children, (void *)chain);
+	chain->type = type;
+}
+
+/* Initialize a new AVP object */
+static void init_avp ( struct avp * avp )
+{
+	TRACE_ENTRY("%p", avp);
+	
+	memset(avp, 0, sizeof(struct avp));
+	init_chain( &avp->avp_chain, MSG_AVP);
+	avp->avp_eyec = MSG_AVP_EYEC;
+}
+	
+/* Initialize a new MSG object */
+static void init_msg ( struct msg * msg )
+{
+	TRACE_ENTRY("%p", msg);
+	
+	memset(msg, 0, sizeof(struct msg));
+	init_chain( &msg->msg_chain, MSG_MSG);
+	msg->msg_eyec = MSG_MSG_EYEC;
+}
+
+
+/* Create a new AVP instance */
+int fd_msg_avp_new ( struct dict_object * model, int flags, struct avp ** avp )
+{
+	struct avp *new = NULL;
+	
+	TRACE_ENTRY("%p %x %p", model, flags, avp);
+	
+	/* Check the parameters */
+	CHECK_PARAMS(  avp && CHECK_AVPFL(flags)  );
+	
+	if (model) {
+		enum dict_object_type 	 dicttype;
+		CHECK_PARAMS( (fd_dict_gettype(model, &dicttype) == 0) && (dicttype == DICT_AVP) );
+	}
+	
+	/* Create a new object */
+	CHECK_MALLOC(  new = malloc (sizeof(struct avp))  );
+	
+	/* Initialize the fields */
+	init_avp(new);
+	
+	if (model) {
+		struct dict_avp_data dictdata;
+		
+		CHECK_FCT(  fd_dict_getval(model, &dictdata)  );
+	
+		new->avp_model = model;
+		new->avp_public.avp_code    = dictdata.avp_code;
+		new->avp_public.avp_flags   = dictdata.avp_flag_val;
+		new->avp_public.avp_len = GETINITIALSIZE(dictdata.avp_basetype, dictdata.avp_flag_val );
+		new->avp_public.avp_vendor  = dictdata.avp_vendor;
+	}
+	
+	if (flags & AVPFL_SET_BLANK_VALUE) {
+		new->avp_public.avp_value = &new->avp_storage;
+	}
+	
+	/* The new object is ready, return */
+	*avp = new;
+	return 0;
+}
+
+/* Create a new message instance */
+int fd_msg_new ( struct dict_object * model, int flags, struct msg ** msg )
+{
+	struct msg * new = NULL;
+	
+	TRACE_ENTRY("%p %x %p", model, flags, msg);
+	
+	/* Check the parameters */
+	CHECK_PARAMS(  msg && CHECK_MSGFL(flags)  );
+	
+	if (model) {
+		enum dict_object_type 	 dicttype;
+		CHECK_PARAMS( (fd_dict_gettype(model, &dicttype) == 0) && (dicttype == DICT_COMMAND) );
+	}
+	
+	/* Create a new object */
+	CHECK_MALLOC(  new = malloc (sizeof(struct msg))  );
+	
+	/* Initialize the fields */
+	init_msg(new);
+	new->msg_public.msg_version	= DIAMETER_VERSION;
+	new->msg_public.msg_length	= GETMSGHDRSZ(); /* This will be updated later */
+
+	if (model) {
+		struct dictionary 	*dict;
+		struct dict_cmd_data     dictdata;
+		struct dict_object     	*dictappl;
+		
+		CHECK_FCT( fd_dict_getdict(model, &dict) );
+		CHECK_FCT( fd_dict_getval(model, &dictdata)  );
+		
+		new->msg_model = model;
+		new->msg_public.msg_flags	= dictdata.cmd_flag_val;
+		new->msg_public.msg_code	= dictdata.cmd_code;
+
+		/* Initialize application from the parent, if any */
+		CHECK_FCT(  fd_dict_search( dict, DICT_APPLICATION, APPLICATION_OF_COMMAND, model, &dictappl, 0)  );
+		if (dictappl != NULL) {
+			struct dict_application_data appdata;
+			CHECK_FCT(  fd_dict_getval(dictappl, &appdata)  );
+			new->msg_public.msg_appl = appdata.application_id;
+		}
+	}
+	
+	if (flags & MSGFL_ALLOC_ETEID) {
+		new->msg_public.msg_eteid = fd_msg_eteid_get();
+	}
+	
+	/* The new object is ready, return */
+	*msg = new;
+	return 0;
+}	
+
+/* Create answer from a request */
+int fd_msg_new_answer_from_req ( struct dictionary * dict, struct msg ** msg, int flags )
+{
+	struct dict_object * model = NULL;
+	struct msg *qry, *ans;
+	struct session * sess = NULL;
+	
+	TRACE_ENTRY("%p %x", msg, flags);
+	
+	/* Check the parameters */
+	CHECK_PARAMS(  msg );
+	qry = *msg;
+	CHECK_PARAMS( CHECK_MSG(qry) && (qry->msg_public.msg_flags & CMD_FLAG_REQUEST) );
+	
+	if (! (flags & MSGFL_ANSW_NOSID)) {
+		/* Get the session of the message */
+		CHECK_FCT_DO( fd_msg_sess_get(dict, qry, &sess, NULL), /* ignore an error */ );
+	}
+	
+	/* Find the model for the answer */
+	if (flags & MSGFL_ANSW_ERROR) {
+		/* The model is the generic error format */
+		CHECK_FCT( fd_dict_get_error_cmd(dict, &model) );
+	} else {
+		/* The model is the answer corresponding to the query. It supposes that these are defined in the dictionary */
+		CHECK_FCT_DO(  parsedict_do_msg( dict, qry, 1, NULL), /* continue */  );
+		if (qry->msg_model) {
+			CHECK_FCT(  fd_dict_search ( dict, DICT_COMMAND, CMD_ANSWER, qry->msg_model, &model, EINVAL )  );
+		}
+	}
+	
+	/* Create the answer */
+	CHECK_FCT(  fd_msg_new( model, flags, &ans )  );
+	
+	/* Set informations in the answer as in the query */
+	ans->msg_public.msg_code = qry->msg_public.msg_code; /* useful for MSGFL_ANSW_ERROR */
+	ans->msg_public.msg_appl = qry->msg_public.msg_appl;
+	ans->msg_public.msg_eteid = qry->msg_public.msg_eteid;
+	ans->msg_public.msg_hbhid = qry->msg_public.msg_hbhid;
+	
+	/* Add the Session-Id AVP if session is known */
+	if (sess && dict) {
+		struct dict_object * sess_id_avp;
+		char * sid;
+		struct avp * avp;
+		union avp_value val;
+		
+		CHECK_FCT( fd_dict_search( dict, DICT_AVP, AVP_BY_NAME, "Session-Id", &sess_id_avp, ENOENT) );
+		CHECK_FCT( fd_sess_getsid ( sess, &sid ) );
+		CHECK_FCT( fd_msg_avp_new ( sess_id_avp, 0, &avp ) );
+		val.os.data = (unsigned char *)sid;
+		val.os.len  = strlen(sid);
+		CHECK_FCT( fd_msg_avp_setvalue( avp, &val ) );
+		CHECK_FCT( fd_msg_avp_add( ans, MSG_BRW_FIRST_CHILD, avp ) );
+		ans->msg_sess = sess;
+		CHECK_FCT( fd_sess_ref_msg(sess) );
+	}
+	
+	/* associate with query */
+	ans->msg_query = qry;
+	qry->msg_associated = 1;
+	
+	/* Done */
+	*msg = ans;
+	return 0;
+}
+
+/***************************************************************************************************************/
+
+/* Explore a message */
+int fd_msg_browse_internal ( msg_or_avp * reference, enum msg_brw_dir dir, msg_or_avp ** found, int * depth )
+{
+	struct msg_avp_chain *result = NULL;
+	int diff = 0;
+	struct fd_list *li = NULL;
+	
+	TRACE_ENTRY("%p %d %p %p", reference, dir, found, depth);
+	
+	/* Initialize the "found" result if any */
+	if (found)
+		*found = NULL;
+	
+	/* Check the parameters */
+	CHECK_PARAMS(  VALIDATE_OBJ(reference)  );
+	
+	TRACE_DEBUG(FCTS, "chaining(%p): nxt:%p prv:%p hea:%p top:%p", 
+			&_C(reference)->chaining,
+			_C(reference)->chaining.next,
+			_C(reference)->chaining.prev,
+			_C(reference)->chaining.head,
+			_C(reference)->chaining.o);
+	TRACE_DEBUG(FCTS, "children(%p): nxt:%p prv:%p hea:%p top:%p", 
+			&_C(reference)->children,
+			_C(reference)->children.next,
+			_C(reference)->children.prev,
+			_C(reference)->children.head,
+			_C(reference)->children.o);
+
+	/* Now search */
+	switch (dir) {
+		case MSG_BRW_NEXT:
+			/* Check the reference is an AVP */
+			CHECK_PARAMS(  _C(reference)->type == MSG_AVP  );
+
+			li = &_C(reference)->chaining;
+			
+			/* Check if the next element is not the sentinel ( ==> the parent) */
+			if (li->next != li->head)
+				result = _C(li->next->o);
+			break;
+
+		case MSG_BRW_PREV:
+			/* Check the reference is an AVP */
+			CHECK_PARAMS(  _C(reference)->type == MSG_AVP  );
+
+			li = &_C(reference)->chaining;
+			
+			/* Check if the prev element is not the sentinel ( ==> the parent) */
+			if (li->prev != li->head)
+				result = _C(li->prev->o);
+			break;
+
+		case MSG_BRW_FIRST_CHILD:
+			li = &_C(reference)->children;
+			if (! FD_IS_LIST_EMPTY(li)) {
+				result = _C(li->next->o);
+				diff = 1;
+			}
+			break;
+
+		case MSG_BRW_LAST_CHILD:
+			li = &_C(reference)->children;
+			if (! FD_IS_LIST_EMPTY(li)) {
+				result = _C(li->prev->o);
+				diff = 1;
+			}
+			break;
+
+		case MSG_BRW_PARENT:
+			/* If the object is not chained, it has no parent */
+			li = &_C(reference)->chaining;
+			if (li != li->head) {
+				/* The sentinel is the parent's children list */
+				result = _C(li->head->o);
+				diff = -1;
+			}
+			break;
+
+		case MSG_BRW_WALK:
+			/* First, try to find a child */
+			li = &_C(reference)->children;
+			if ( ! FD_IS_LIST_EMPTY(li) ) {
+				result = _C(li->next->o);
+				diff = 1;
+				break;
+			}
+			
+			/* Then try to find a "next" at this level or one of the parent's */
+			li = &_C(reference)->chaining;
+			do {
+				/* If this element has a "next" element, return it */
+				if (li->next != li->head) {
+					result = _C(li->next->o);
+					break;
+				}
+				/* otherwise, check if we have a parent */
+				if (li == li->head) {
+					/* no parent */
+					break;
+				}
+				/* Go to the parent's chaining information and loop */
+				diff -= 1;
+				li = &_C(li->head->o)->chaining;
+			} while (1); 
+			break;
+			
+		default:
+			/* Other directions are invalid */
+			CHECK_PARAMS( dir = 0 );
+	}
+	
+	/* Save the found object, if any */
+	if (found && result)
+		*found = (void *)result;
+	
+	/* Modify the depth according to the walk direction */
+	if (depth && diff)
+		(*depth) += diff;
+	
+	/* Return ENOENT if found was NULL */
+	if ((!found) && (!result))
+		return ENOENT;
+	else
+		return 0;
+}
+
+/* Add an AVP into a tree */
+int fd_msg_avp_add ( msg_or_avp * reference, enum msg_brw_dir dir, struct avp *avp)
+{
+	TRACE_ENTRY("%p %d %p", reference, dir, avp);
+	
+	/* Check the parameters */
+	CHECK_PARAMS(  VALIDATE_OBJ(reference)  &&  CHECK_AVP(avp)  &&  FD_IS_LIST_EMPTY(&avp->avp_chain.chaining)  );
+	
+	/* Now insert */
+	switch (dir) {
+		case MSG_BRW_NEXT:
+			/* Check the reference is an AVP -- we do not chain AVPs at same level as msgs. */
+			CHECK_PARAMS(  _C(reference)->type == MSG_AVP  );
+			
+			/* Insert the new avp after the reference */
+			fd_list_insert_after( &_A(reference)->avp_chain.chaining, &avp->avp_chain.chaining );
+			break;
+
+		case MSG_BRW_PREV:
+			/* Check the reference is an AVP */
+			CHECK_PARAMS(  _C(reference)->type == MSG_AVP  );
+			
+			/* Insert the new avp before the reference */
+			fd_list_insert_before( &_A(reference)->avp_chain.chaining, &avp->avp_chain.chaining );
+			break;
+
+		case MSG_BRW_FIRST_CHILD:
+			/* Insert the new avp after the children sentinel */
+			fd_list_insert_after( &_C(reference)->children, &avp->avp_chain.chaining );
+			break;
+
+		case MSG_BRW_LAST_CHILD:
+			/* Insert the new avp before the children sentinel */
+			fd_list_insert_before( &_C(reference)->children, &avp->avp_chain.chaining );
+			break;
+
+		default:
+			/* Other directions are invalid */
+			CHECK_PARAMS( dir = 0 );
+	}
+			
+	return 0;
+}
+
+/* Search a given AVP model in a message */
+int fd_msg_search_avp ( struct msg * msg, struct dict_object * what, struct avp ** avp )
+{
+	struct avp * nextavp;
+	struct dict_avp_data 	dictdata;
+	enum dict_object_type 	dicttype;
+	
+	TRACE_ENTRY("%p %p %p", msg, what, avp);
+	
+	CHECK_PARAMS( CHECK_MSG(msg) && what );
+	
+	CHECK_PARAMS( (fd_dict_gettype(what, &dicttype) == 0) && (dicttype == DICT_AVP) );
+	CHECK_FCT(  fd_dict_getval(what, &dictdata)  );
+	
+	/* Loop on all top AVPs */
+	CHECK_FCT(  fd_msg_browse(msg, MSG_BRW_FIRST_CHILD, (void *)&nextavp, NULL)  );
+	while (nextavp) {
+		
+		if ( (nextavp->avp_public.avp_code   == dictdata.avp_code)
+		  && (nextavp->avp_public.avp_vendor == dictdata.avp_vendor) ) /* always 0 if no V flag */
+			break;
+		
+		/* Otherwise move to next AVP in the message */
+		CHECK_FCT( fd_msg_browse(nextavp, MSG_BRW_NEXT, (void *)&nextavp, NULL) );
+	}
+	
+	if (avp)
+		*avp = nextavp;
+	
+	if (avp && nextavp) {
+		struct dictionary * dict;
+		CHECK_FCT( fd_dict_getdict( what, &dict) );
+		CHECK_FCT_DO( fd_msg_parse_dict( nextavp, dict, NULL ), /* nothing */ );
+	}
+	
+	if (avp || nextavp)
+		return 0;
+	else
+		return ENOENT;
+}
+
+
+/***************************************************************************************************************/
+/* Deleting objects */
+
+/* Destroy and free an AVP or message */
+static int destroy_obj (struct msg_avp_chain * obj )
+{
+	TRACE_ENTRY("%p", obj);
+	
+	/* Check the parameter is a valid object */
+	CHECK_PARAMS(  VALIDATE_OBJ(obj) && FD_IS_LIST_EMPTY( &obj->children ) );
+
+	/* Unlink this object if needed */
+	fd_list_unlink( &obj->chaining );
+	
+	/* Free the octetstring if needed */
+	if ((obj->type == MSG_AVP) && (_A(obj)->avp_mustfreeos == 1)) {
+		free(_A(obj)->avp_storage.os.data);
+	}
+	/* Free the rawdata if needed */
+	if ((obj->type == MSG_AVP) && (_A(obj)->avp_rawdata != NULL)) {
+		free(_A(obj)->avp_rawdata);
+	}
+	if ((obj->type == MSG_MSG) && (_M(obj)->msg_rawbuffer != NULL)) {
+		free(_M(obj)->msg_rawbuffer);
+	}
+	
+	if ((obj->type == MSG_MSG) && (_M(obj)->msg_src_id != NULL)) {
+		free(_M(obj)->msg_src_id);
+	}
+	
+	if ((obj->type == MSG_MSG) && (_M(obj)->msg_rtdata != NULL)) {
+		fd_rtd_free(&_M(obj)->msg_rtdata);
+	}
+	
+	if ((obj->type == MSG_MSG) && (_M(obj)->msg_sess != NULL)) {
+		CHECK_FCT_DO( fd_sess_reclaim_msg ( &_M(obj)->msg_sess ), /* continue */);
+	}
+	
+	/* free the object */
+	free(obj);
+	
+	return 0;
+}
+
+/* Destroy an object and all its children */
+static void destroy_tree(struct msg_avp_chain * obj)
+{
+	struct fd_list *rem;
+	
+	TRACE_ENTRY("%p", obj);
+	
+	/* Destroy any subtree */
+	while ( (rem = obj->children.next) != &obj->children)
+		destroy_tree(_C(rem->o));
+	
+	/* Then unlink and destroy the object */
+	CHECK_FCT_DO(  destroy_obj(obj),  /* nothing */  );
+}
+
+/* Free an object and its tree */
+int fd_msg_free ( msg_or_avp * object )
+{
+	TRACE_ENTRY("%p", object);
+	
+	if (CHECK_MSG(object)) {
+		if (_M(object)->msg_query) {
+			_M(_M(object)->msg_query)->msg_associated = 0;
+			CHECK_FCT(  fd_msg_free( _M(object)->msg_query )  );
+			_M(object)->msg_query = NULL;
+		} else {
+			if (_M(object)->msg_associated) {
+				TRACE_DEBUG(INFO, "Not freeing query %p referenced in an answer (will be freed along the answer).", object);
+				return 0;
+			}
+		}
+	}
+	
+	destroy_tree(_C(object));
+	return 0;
+}
+
+
+/***************************************************************************************************************/
+/* Debug functions: dumping */
+
+/* indent inside an object */
+#define INOBJHDR 	"%*s   "
+#define INOBJHDRVAL 	indent<0 ? 1 : indent, indent<0 ? "-" : "|"
+
+/* Dump a msg_t object */
+static void obj_dump_msg (struct msg * msg, int indent )
+{
+	int ret = 0;
+	
+	fd_log_debug("%*sMSG: %p\n", INOBJHDRVAL, msg);
+	
+	if (!CHECK_MSG(msg)) {
+		fd_log_debug(INOBJHDR "INVALID!\n", INOBJHDRVAL);
+		return;
+	}
+	
+	if (!msg->msg_model) {
+		
+		fd_log_debug(INOBJHDR "(no model)\n", INOBJHDRVAL);
+		
+	} else {
+		
+		enum dict_object_type dicttype;
+		struct dict_cmd_data  dictdata;
+		ret = fd_dict_gettype(msg->msg_model, &dicttype);
+		if (ret || (dicttype != DICT_COMMAND)) {
+			fd_log_debug(INOBJHDR "(invalid model: %d %d)\n", INOBJHDRVAL, ret, dicttype);
+			goto public;
+		}
+		ret = fd_dict_getval(msg->msg_model, &dictdata);
+		if (ret != 0) {
+			fd_log_debug(INOBJHDR "(error getting model data: %s)\n", INOBJHDRVAL, strerror(ret));
+			goto public;
+		}
+		fd_log_debug(INOBJHDR "model : v/m:" DUMP_CMDFL_str "/" DUMP_CMDFL_str ", %u \"%s\"\n", INOBJHDRVAL, 
+			DUMP_CMDFL_val(dictdata.cmd_flag_val), DUMP_CMDFL_val(dictdata.cmd_flag_mask), dictdata.cmd_code, dictdata.cmd_name);
+	}
+public:	
+	fd_log_debug(INOBJHDR "public: V:%d L:%d fl:" DUMP_CMDFL_str " CC:%u A:%d hi:%x ei:%x\n", INOBJHDRVAL, 
+		msg->msg_public.msg_version,
+		msg->msg_public.msg_length,
+		DUMP_CMDFL_val(msg->msg_public.msg_flags),
+		msg->msg_public.msg_code,
+		msg->msg_public.msg_appl,
+		msg->msg_public.msg_hbhid,
+		msg->msg_public.msg_eteid
+		);
+	fd_log_debug(INOBJHDR "intern: rwb:%p rt:%d cb:%p(%p) qry:%p asso:%d sess:%p src:%s\n", 
+			INOBJHDRVAL, msg->msg_rawbuffer, msg->msg_routable, msg->msg_cb.fct, msg->msg_cb.data, msg->msg_query, msg->msg_associated, msg->msg_sess, msg->msg_src_id?:"(nil)");
+}
+
+/* Dump an avp object */
+static void obj_dump_avp ( struct avp * avp, int indent )
+{
+	int ret = 0;
+	enum dict_avp_basetype type = -1;
+	
+	if (!CHECK_AVP(avp)) {
+		fd_log_debug(INOBJHDR "INVALID!\n", INOBJHDRVAL);
+		return;
+	}
+	
+	if (!avp->avp_model) {
+		
+		fd_log_debug(INOBJHDR "(no model)\n", INOBJHDRVAL);
+		
+	} else {
+		
+		enum dict_object_type dicttype;
+		struct dict_avp_data dictdata;
+		ret = fd_dict_gettype(avp->avp_model, &dicttype);
+		if (ret || (dicttype != DICT_AVP)) {
+			fd_log_debug(INOBJHDR "(invalid model: %d %d)\n", INOBJHDRVAL, ret, dicttype);
+			goto public;
+		}
+		ret = fd_dict_getval(avp->avp_model, &dictdata);
+		if (ret != 0) {
+			fd_log_debug(INOBJHDR "(error getting model data: %s)\n", INOBJHDRVAL, strerror(ret));
+			goto public;
+		}
+		fd_log_debug(INOBJHDR "model : v/m:" DUMP_AVPFL_str "/" DUMP_AVPFL_str ", %12s, %u \"%s\"\n", INOBJHDRVAL, 
+			DUMP_AVPFL_val(dictdata.avp_flag_val), 
+			DUMP_AVPFL_val(dictdata.avp_flag_mask), 
+			type_base_name[dictdata.avp_basetype], 
+			dictdata.avp_code, 
+			dictdata.avp_name );
+		type = dictdata.avp_basetype;
+	}
+public:	
+	fd_log_debug(INOBJHDR "public: C:%u fl:" DUMP_AVPFL_str " L:%d V:%u  data:@%p\n", INOBJHDRVAL, 
+		avp->avp_public.avp_code,
+		DUMP_AVPFL_val(avp->avp_public.avp_flags),
+		avp->avp_public.avp_len,
+		avp->avp_public.avp_vendor,
+		avp->avp_public.avp_value
+		);
+	/* Dump the value if set */
+	if (avp->avp_public.avp_value) {
+		if (!avp->avp_model) {
+			fd_log_debug(INOBJHDR "(data set but no model: ERROR)\n", INOBJHDRVAL);
+		} else {
+			fd_dict_dump_avp_value(avp->avp_public.avp_value, avp->avp_model, indent);
+		}
+	}
+
+	fd_log_debug(INOBJHDR "intern: src:%p mf:%d raw:%p(%d)\n", INOBJHDRVAL, avp->avp_source, avp->avp_mustfreeos, avp->avp_rawdata, avp->avp_rawlen);
+}
+
+/* Dump a single object content */
+static void msg_dump_intern ( int level, msg_or_avp * obj, int indent )
+{
+	/* Log only if we are at least at level */
+	if ( ! TRACE_BOOL(level) )
+		return;
+	
+	/* Check the object */
+	if (!VALIDATE_OBJ(obj)) {
+		fd_log_debug( ">>> invalid object (%p)!.\n", obj);
+		return;
+	}
+	
+	/* Dump the object */
+	switch (_C(obj)->type) {
+		case MSG_AVP:
+			obj_dump_avp ( _A(obj), indent );
+			break;
+		
+		case MSG_MSG:
+			obj_dump_msg ( _M(obj), indent );
+			break;
+		
+		default:
+			ASSERT(0);
+	}
+}
+
+/* Dump a message content -- for debug mostly */
+void fd_msg_dump_walk ( int level, msg_or_avp *obj )
+{
+	msg_or_avp * ref = obj;
+	int indent = 1;
+	
+	TRACE_DEBUG(level, "------ Dumping object %p (w)-------", obj);
+	do {
+		msg_dump_intern ( level, ref, indent );
+		
+		/* Now find the next object */
+		CHECK_FCT_DO(  fd_msg_browse ( ref, MSG_BRW_WALK, &ref, &indent ), break  );
+		
+		/* dump next object */
+	} while (ref);
+	
+	TRACE_DEBUG(level, "------ /end of object %p -------", obj);
+}
+
+/* Dump a single object content -- for debug mostly */
+void fd_msg_dump_one ( int level, msg_or_avp * obj )
+{
+	TRACE_DEBUG(level, "------ Dumping object %p (s)-------", obj);
+	msg_dump_intern ( level, obj, 1 );
+	TRACE_DEBUG(level, "------ /end of object %p -------", obj);
+}
+
+
+/***************************************************************************************************************/
+/* Simple meta-data management */
+
+/* Retrieve the model of an object */
+int fd_msg_model ( msg_or_avp * reference, struct dict_object ** model )
+{
+	TRACE_ENTRY("%p %p", reference, model);
+	
+	/* Check the parameters */
+	CHECK_PARAMS(  model && VALIDATE_OBJ(reference)  );
+	
+	/* copy the model reference */
+	switch (_C(reference)->type) {
+		case MSG_AVP:
+			*model = _A(reference)->avp_model;
+			break;
+		
+		case MSG_MSG:
+			*model = _M(reference)->msg_model;
+			break;
+		
+		default:
+			CHECK_PARAMS(0);
+	}
+	
+	return 0;
+}
+
+/* Retrieve the address of the msg_public field of a message */
+int fd_msg_hdr ( struct msg *msg, struct msg_hdr **pdata )
+{
+	TRACE_ENTRY("%p %p", msg, pdata);
+	CHECK_PARAMS(  CHECK_MSG(msg) && pdata  );
+	
+	*pdata = &msg->msg_public;
+	return 0;
+}
+
+/* Retrieve the address of the avp_public field of an avp */
+int fd_msg_avp_hdr ( struct avp *avp, struct avp_hdr **pdata )
+{
+	TRACE_ENTRY("%p %p", avp, pdata);
+	CHECK_PARAMS(  CHECK_AVP(avp) && pdata  );
+	
+	*pdata = &avp->avp_public;
+	return 0;
+}
+
+/* Associate answers and queries */
+int fd_msg_answ_associate( struct msg * answer, struct msg * query )
+{
+	TRACE_ENTRY( "%p %p", answer, query );
+	
+	CHECK_PARAMS(  CHECK_MSG(answer) && CHECK_MSG(query) && (answer->msg_query == NULL )  );
+	
+	answer->msg_query = query;
+	query->msg_associated = 1;
+	
+	return 0;
+}	
+
+int fd_msg_answ_getq( struct msg * answer, struct msg ** query )
+{
+	TRACE_ENTRY( "%p %p", answer, query );
+	
+	CHECK_PARAMS(  CHECK_MSG(answer) && query  );
+	
+	*query = answer->msg_query;
+	
+	return 0;
+}	
+
+int fd_msg_answ_detach( struct msg * answer )
+{
+	TRACE_ENTRY( "%p", answer );
+	
+	CHECK_PARAMS(  CHECK_MSG(answer) );
+	
+	answer->msg_query->msg_associated = 0;
+	answer->msg_query = NULL;
+	
+	return 0;
+}
+
+/* Associate / get answer callbacks */
+int fd_msg_anscb_associate( struct msg * msg, void ( *anscb)(void *, struct msg **), void  * data, const struct timespec *timeout )
+{
+	TRACE_ENTRY("%p %p %p", msg, anscb, data);
+	
+	/* Check the parameters */
+	CHECK_PARAMS( CHECK_MSG(msg) && anscb );
+	CHECK_PARAMS( msg->msg_public.msg_flags & CMD_FLAG_REQUEST ); /* we associate with requests only */
+	CHECK_PARAMS( msg->msg_cb.fct == NULL ); /* No cb is already registered */
+	
+	/* Associate callback and data with the message, if any */
+	msg->msg_cb.fct = anscb;
+	msg->msg_cb.data = data;
+	if (timeout) {
+		memcpy(&msg->msg_cb.timeout, timeout, sizeof(struct timespec));
+	}
+	
+	return 0;
+}	
+
+int fd_msg_anscb_get( struct msg * msg, void (**anscb)(void *, struct msg **), void ** data )
+{
+	TRACE_ENTRY("%p %p %p", msg, anscb, data);
+	
+	/* Check the parameters */
+	CHECK_PARAMS( CHECK_MSG(msg) && anscb && data );
+	
+	/* Copy the result */
+	*anscb = msg->msg_cb.fct;
+	*data  = msg->msg_cb.data;
+	
+	return 0;
+}
+
+struct timespec *fd_msg_anscb_gettimeout( struct msg * msg )
+{
+	TRACE_ENTRY("%p", msg);
+	
+	/* Check the parameters */
+	CHECK_PARAMS_DO( CHECK_MSG(msg), return NULL );
+	
+	if (!msg->msg_cb.timeout.tv_sec) {
+		return NULL;
+	}
+	
+	return &msg->msg_cb.timeout;
+}
+
+/* Associate routing lists */
+int fd_msg_rt_associate( struct msg * msg, struct rt_data ** rtd )
+{
+	TRACE_ENTRY( "%p %p", msg, rtd );
+	
+	CHECK_PARAMS(  CHECK_MSG(msg) && rtd  );
+	
+	msg->msg_rtdata = *rtd;
+	*rtd = NULL;
+	
+	return 0;
+}
+
+int fd_msg_rt_get( struct msg * msg, struct rt_data ** rtd )
+{
+	TRACE_ENTRY( "%p %p", msg, rtd );
+	
+	CHECK_PARAMS(  CHECK_MSG(msg) && rtd  );
+	
+	*rtd = msg->msg_rtdata;
+	msg->msg_rtdata = NULL;
+	
+	return 0;
+}	
+
+/* Find if a message is routable */
+int fd_msg_is_routable ( struct msg * msg )
+{
+	TRACE_ENTRY("%p", msg);
+	
+	CHECK_PARAMS_DO(  CHECK_MSG(msg),  return 0 /* pretend the message is not routable */ );
+	
+	if ( ! msg->msg_routable ) {
+		/* To define if a message is routable, we rely on the "PXY" flag (for application 0). */
+		msg->msg_routable = ((msg->msg_public.msg_appl != 0) || (msg->msg_public.msg_flags & CMD_FLAG_PROXIABLE)) ? 1 : 2;
+		
+		/* Note : the 'real' criteria according to the Diameter I-D is that the message is 
+		 routable if and only if the "Destination-Realm" AVP is required by the command ABNF.
+		 We could make a test for this here, but it's more computational work and our test
+		 seems accurate (until proven otherwise...) */
+	}
+	
+	return (msg->msg_routable == 1) ? 1 : 0;
+}
+
+/* Associate source peer */
+int fd_msg_source_set( struct msg * msg, char * diamid, int add_rr, struct dictionary * dict )
+{
+	TRACE_ENTRY( "%p %p %d %p", msg, diamid, add_rr, dict);
+	
+	/* Check we received a valid message */
+	CHECK_PARAMS( CHECK_MSG(msg) && ( (! add_rr) || dict ) );
+	
+	/* Cleanup any previous source */
+	free(msg->msg_src_id); msg->msg_src_id = NULL;
+	
+	/* If the request is to cleanup the source, we are done */
+	if (diamid == NULL) {
+		return 0;
+	}
+	
+	/* Otherwise save the new informations */
+	CHECK_MALLOC( msg->msg_src_id = strdup(diamid) );
+	
+	if (add_rr) {
+		struct dict_object 	*avp_rr_model;
+		avp_code_t 		 code = AC_ROUTE_RECORD;
+		struct avp 		*avp;
+		union avp_value		 val;
+		
+		/* Find the model for Route-Record in the dictionary */
+		CHECK_FCT( fd_dict_search ( dict, DICT_AVP, AVP_BY_CODE, &code, &avp_rr_model, ENOENT) );
+		
+		/* Create the AVP with this model */
+		CHECK_FCT( fd_msg_avp_new ( avp_rr_model, 0, &avp ) );
+		
+		/* Set the AVP value with the diameter id */
+		memset(&val, 0, sizeof(val));
+		val.os.data = (unsigned char *)diamid;
+		val.os.len  = strlen(diamid);
+		CHECK_FCT( fd_msg_avp_setvalue( avp, &val ) );
+
+		/* Add the AVP in the message */
+		CHECK_FCT( fd_msg_avp_add( msg, MSG_BRW_LAST_CHILD, avp ) );
+	}
+	
+	/* done */
+	return 0;
+}
+
+int fd_msg_source_get( struct msg * msg, char ** diamid )
+{
+	TRACE_ENTRY( "%p %p", msg, diamid);
+	
+	/* Check we received valid parameters */
+	CHECK_PARAMS( CHECK_MSG(msg) );
+	CHECK_PARAMS( diamid );
+	
+	/* Copy the informations */
+	*diamid = msg->msg_src_id;
+	
+	/* done */
+	return 0;
+}
+
+/* Retrieve the session of the message */
+int fd_msg_sess_get(struct dictionary * dict, struct msg * msg, struct session ** session, int * new)
+{
+	struct avp * avp;
+	
+	TRACE_ENTRY("%p %p %p", msg, session, new);
+	
+	/* Check we received valid parameters */
+	CHECK_PARAMS( CHECK_MSG(msg) );
+	CHECK_PARAMS( session );
+	
+	/* If we already resolved the session, just send it back */
+	if (msg->msg_sess) {
+		*session = msg->msg_sess;
+		if (new)
+			*new = 0;
+		return 0;
+	}
+	
+	/* OK, we have to search for Session-Id AVP -- it is usually the first AVP, but let's be permissive here */
+	/* -- note: we accept messages that have not yet been dictionary parsed... */
+	CHECK_FCT(  fd_msg_browse(msg, MSG_BRW_FIRST_CHILD, &avp, NULL)  );
+	while (avp) {
+		if ( (avp->avp_public.avp_code   == AC_SESSION_ID)
+		  && (avp->avp_public.avp_vendor == 0) )
+			break;
+		
+		/* Otherwise move to next AVP in the message */
+		CHECK_FCT( fd_msg_browse(avp, MSG_BRW_NEXT, &avp, NULL) );
+	}
+	
+	if (!avp) {
+		TRACE_DEBUG(FULL, "No Session-Id AVP found in message %p", msg);
+		*session = NULL;
+		return 0;
+	}
+	
+	if (!avp->avp_model) {
+		CHECK_FCT( fd_msg_parse_dict ( avp, dict, NULL ) );
+	}
+	
+	ASSERT( avp->avp_public.avp_value );
+	
+	/* Resolve the session and we are done */
+	CHECK_FCT( fd_sess_fromsid_msg ( avp->avp_public.avp_value->os.data, avp->avp_public.avp_value->os.len, &msg->msg_sess, new) );
+	*session = msg->msg_sess;
+	
+	return 0;
+}
+
+
+/******************* End-to-end counter *********************/
+uint32_t fd_eteid;
+pthread_mutex_t fd_eteid_lck = PTHREAD_MUTEX_INITIALIZER;
+
+void fd_msg_eteid_init(void)
+{
+	fd_eteid = ((uint32_t)time(NULL) << 20) | ((uint32_t)lrand48() & ( (1 << 20) - 1 ));
+}
+
+uint32_t fd_msg_eteid_get ( void )
+{
+	uint32_t ret;
+	
+	CHECK_POSIX_DO( pthread_mutex_lock(&fd_eteid_lck), /* continue */ );
+	
+	ret = fd_eteid ++;
+	
+	CHECK_POSIX_DO( pthread_mutex_unlock(&fd_eteid_lck), /* continue */ );
+	
+	return ret;
+}
+
+/***************************************************************************************************************/
+/* Manage AVPs values */
+
+/* Set the value of an AVP */
+int fd_msg_avp_setvalue ( struct avp *avp, union avp_value *value )
+{
+	enum dict_avp_basetype type = -1;
+	
+	TRACE_ENTRY("%p %p", avp, value);
+	
+	/* Check parameter */
+	CHECK_PARAMS(  CHECK_AVP(avp) && avp->avp_model  );
+	
+	/* Retrieve information from the AVP model */
+	{
+		enum dict_object_type dicttype;
+		struct dict_avp_data  dictdata;
+		
+		CHECK_PARAMS( (fd_dict_gettype(avp->avp_model, &dicttype) == 0) && (dicttype == DICT_AVP) );
+		CHECK_FCT(  fd_dict_getval(avp->avp_model, &dictdata)  );
+		type = dictdata.avp_basetype;
+		CHECK_PARAMS(  type != AVP_TYPE_GROUPED  );
+	}
+	
+	/* First, clean any previous value */
+	if (avp->avp_mustfreeos != 0) {
+		free(avp->avp_storage.os.data);
+		avp->avp_mustfreeos = 0;
+	}
+	
+	memset(&avp->avp_storage, 0, sizeof(union avp_value));
+	
+	/* If the request was to delete a value: */
+	if (!value) {
+		avp->avp_public.avp_value = NULL;
+		return 0;
+	}
+	
+	/* Now we have to set the value */
+	memcpy(&avp->avp_storage, value, sizeof(union avp_value));
+	
+	/* Copy an octetstring if needed. */
+	if (type == AVP_TYPE_OCTETSTRING) {
+		if (value->os.len) {
+			CHECK_MALLOC(  avp->avp_storage.os.data = malloc(value->os.len)  );
+			avp->avp_mustfreeos = 1;
+			memcpy(avp->avp_storage.os.data, value->os.data, value->os.len);
+		} else {
+			avp->avp_storage.os.data = NULL;
+		}
+	}
+	
+	/* Set the data pointer of the public part */
+	avp->avp_public.avp_value = &avp->avp_storage;
+	
+	return 0;		
+}
+
+/* Set the value of an AVP, using formatted data */
+int fd_msg_avp_value_encode ( void *data, struct avp *avp )
+{
+	enum dict_avp_basetype type = -1;
+	struct dict_type_data type_data;
+	
+	TRACE_ENTRY("%p %p", data, avp);
+	
+	/* Check parameter */
+	CHECK_PARAMS(  CHECK_AVP(avp) && avp->avp_model  );
+	
+	/* Retrieve information from the AVP model and it's parent type */
+	{
+		enum dict_object_type dicttype;
+		struct dict_avp_data  dictdata;
+		struct dictionary   * dict;
+		struct dict_object  * parenttype = NULL;
+		
+		/* First check the base type of the AVP */
+		CHECK_PARAMS( (fd_dict_gettype(avp->avp_model, &dicttype) == 0) && (dicttype == DICT_AVP) );
+		CHECK_FCT(  fd_dict_getval(avp->avp_model, &dictdata)  );
+		type = dictdata.avp_basetype;
+		CHECK_PARAMS(  type != AVP_TYPE_GROUPED  );
+		
+		/* Then retrieve information about the parent's type (= derived type) */
+		CHECK_FCT(  fd_dict_getdict( avp->avp_model, &dict )  );
+		CHECK_FCT(  fd_dict_search( dict, DICT_TYPE, TYPE_OF_AVP, avp->avp_model, &parenttype, EINVAL)  );
+		CHECK_FCT(  fd_dict_getval(parenttype, &type_data)  );
+		if (type_data.type_encode == NULL) {
+			TRACE_DEBUG(INFO, "This AVP type does not provide a callback to encode formatted data. ENOTSUP.");
+			return ENOTSUP;
+		}
+	}
+	
+	/* Ok, now we can encode the value */
+	
+	/* First, clean any previous value */
+	if (avp->avp_mustfreeos != 0) {
+		free(avp->avp_storage.os.data);
+		avp->avp_mustfreeos = 0;
+	}
+	avp->avp_public.avp_value = NULL;
+	memset(&avp->avp_storage, 0, sizeof(union avp_value));
+	
+	/* Now call the type's callback to encode the data */
+	CHECK_FCT(  (*type_data.type_encode)(data, &avp->avp_storage)  );
+	
+	/* If an octetstring has been allocated, let's mark it to be freed */
+	if (type == AVP_TYPE_OCTETSTRING)
+		avp->avp_mustfreeos = 1;
+	
+	/* Set the data pointer of the public part */
+	avp->avp_public.avp_value = &avp->avp_storage;
+	
+	return 0;		
+}
+
+/* Interpret the value of an AVP into formatted data */
+int fd_msg_avp_value_interpret ( struct avp *avp, void *data )
+{
+	struct dict_type_data type_data;
+	
+	TRACE_ENTRY("%p %p", avp, data);
+	
+	/* Check parameter */
+	CHECK_PARAMS(  CHECK_AVP(avp) && avp->avp_model && avp->avp_public.avp_value  );
+	
+	/* Retrieve information about the AVP parent type */
+	{
+		struct dictionary   * dict;
+		struct dict_object  * parenttype = NULL;
+		
+		CHECK_FCT(  fd_dict_getdict( avp->avp_model, &dict )  );
+		CHECK_FCT(  fd_dict_search( dict, DICT_TYPE, TYPE_OF_AVP, avp->avp_model, &parenttype, EINVAL)  );
+		CHECK_FCT(  fd_dict_getval(parenttype, &type_data)  );
+		if (type_data.type_interpret == NULL) {
+			TRACE_DEBUG(INFO, "This AVP type does not provide a callback to interpret value in formatted data. ENOTSUP.");
+			return ENOTSUP;
+		}
+	}
+	
+	/* Ok, now we can interpret the value */
+	
+	CHECK_FCT(  (*type_data.type_interpret)(avp->avp_public.avp_value, data)  );
+	
+	return 0;		
+}
+
+/***************************************************************************************************************/
+/* Creating a buffer from memory objects (bufferize a struct msg) */
+
+/* Following macros are used to store 32 and 64 bit fields into a buffer in network byte order */
+#define PUT_in_buf_32( _u32data, _bufptr ) {							\
+	*(uint32_t *)(_bufptr) = htonl((uint32_t)(_u32data));					\
+}
+#define PUT_in_buf_64( _u64data, _bufptr ) {							\
+	*(uint64_t *)(_bufptr) = htonll((uint64_t)(_u64data));					\
+}
+
+/* Write a message header in the buffer */
+static int bufferize_msg(unsigned char * buffer, size_t buflen, size_t * offset, struct msg * msg)
+{
+	TRACE_ENTRY("%p %d %p %p", buffer, buflen, offset, msg);
+	
+	if ((buflen - *offset) < GETMSGHDRSZ())
+		return ENOSPC;
+	
+	if (*offset & 0x3)
+		return EFAULT;	/* We are supposed to start on 32 bit boundaries */
+	
+	PUT_in_buf_32(msg->msg_public.msg_length, buffer + *offset);
+	buffer[*offset] = msg->msg_public.msg_version;
+	*offset += 4;
+	
+	PUT_in_buf_32(msg->msg_public.msg_code, buffer + *offset);
+	buffer[*offset] = msg->msg_public.msg_flags;
+	*offset += 4;
+	
+	PUT_in_buf_32(msg->msg_public.msg_appl, buffer + *offset);
+	*offset += 4;
+	
+	PUT_in_buf_32(msg->msg_public.msg_hbhid, buffer + *offset);
+	*offset += 4;
+	
+	PUT_in_buf_32(msg->msg_public.msg_eteid, buffer + *offset);
+	*offset += 4;
+	
+	return 0;
+}
+
+static int bufferize_chain(unsigned char * buffer, size_t buflen, size_t * offset, struct fd_list * list);
+
+/* Write an AVP in the buffer */
+static int bufferize_avp(unsigned char * buffer, size_t buflen, size_t * offset,  struct avp * avp)
+{
+	struct dict_avp_data dictdata;
+	
+	TRACE_ENTRY("%p %d %p %p", buffer, buflen, offset, avp);
+	
+	if ((buflen - *offset) < avp->avp_public.avp_len)
+		return ENOSPC;
+	
+	/* Write the header */
+	PUT_in_buf_32(avp->avp_public.avp_code, buffer + *offset);
+	*offset += 4;
+	
+	PUT_in_buf_32(avp->avp_public.avp_len, buffer + *offset);
+	buffer[*offset] = avp->avp_public.avp_flags;
+	*offset += 4;
+	
+	if (avp->avp_public.avp_flags & AVP_FLAG_VENDOR) {
+		PUT_in_buf_32(avp->avp_public.avp_vendor, buffer + *offset);
+		*offset += 4;
+	}
+	
+	/* Then we must write the AVP value */
+	
+	if (avp->avp_model == NULL) {
+		/* In the case where we don't know the type of AVP, just copy the raw data or source */
+		CHECK_PARAMS( avp->avp_source || avp->avp_rawdata );
+		
+		if ( avp->avp_source != NULL ) {
+			/* the message was not parsed completely */
+			size_t datalen = avp->avp_public.avp_len - GETAVPHDRSZ(avp->avp_public.avp_flags);
+			memcpy(&buffer[*offset], avp->avp_source, datalen);
+			*offset += PAD4(datalen);
+		} else {
+			/* the content was stored in rawdata */
+			memcpy(&buffer[*offset], avp->avp_rawdata, avp->avp_rawlen);
+			*offset += PAD4(avp->avp_rawlen);
+		}
+		
+	} else {
+		/* The AVP is defined in the dictionary */
+		CHECK_FCT(  fd_dict_getval(avp->avp_model, &dictdata)  );
+
+		CHECK_PARAMS( ( dictdata.avp_basetype == AVP_TYPE_GROUPED ) || avp->avp_public.avp_value );
+		
+		switch (dictdata.avp_basetype) {
+			case AVP_TYPE_GROUPED:
+				return bufferize_chain(buffer, buflen, offset, &avp->avp_chain.children);
+
+			case AVP_TYPE_OCTETSTRING:
+				if (avp->avp_public.avp_value->os.len)
+					memcpy(&buffer[*offset], avp->avp_public.avp_value->os.data, avp->avp_public.avp_value->os.len);
+				*offset += PAD4(avp->avp_public.avp_value->os.len);
+				break;
+
+			case AVP_TYPE_INTEGER32:
+				PUT_in_buf_32(avp->avp_public.avp_value->i32, buffer + *offset);
+				*offset += 4;
+				break;
+
+			case AVP_TYPE_INTEGER64:
+				PUT_in_buf_64(avp->avp_public.avp_value->i64, buffer + *offset);
+				*offset += 8;
+				break;
+
+			case AVP_TYPE_UNSIGNED32:
+				PUT_in_buf_32(avp->avp_public.avp_value->u32, buffer + *offset);
+				*offset += 4;
+				break;
+
+			case AVP_TYPE_UNSIGNED64:
+				PUT_in_buf_64(avp->avp_public.avp_value->u64, buffer + *offset);
+				*offset += 8;
+				break;
+
+			case AVP_TYPE_FLOAT32:
+				/* We read the f32 as "u32" here to avoid casting to uint make decimals go away. 
+				 The alternative would be something like "*(uint32_t *)(& f32)" but
+				 then the compiler complains about strict-aliasing rules. */
+				PUT_in_buf_32(avp->avp_public.avp_value->u32, buffer + *offset);
+				*offset += 4;
+				break;
+
+			case AVP_TYPE_FLOAT64:
+				/* Same remark as previously */
+				PUT_in_buf_64(avp->avp_public.avp_value->u64, buffer + *offset);
+				*offset += 8;
+				break;
+
+			default:
+				ASSERT(0);
+		}
+	}
+	return 0;
+}
+			
+/* Write a chain of AVPs in the buffer */
+static int bufferize_chain(unsigned char * buffer, size_t buflen, size_t * offset, struct fd_list * list)
+{
+	struct fd_list * avpch;
+	
+	TRACE_ENTRY("%p %d %p %p", buffer, buflen, offset, list);
+	
+	for (avpch = list->next; avpch != list; avpch = avpch->next) {
+		/* Bufferize the AVP */
+		CHECK_FCT( bufferize_avp(buffer, buflen, offset, _A(avpch->o))  );
+	}
+	return 0;
+}
+
+/* Create the message buffer, in network-byte order. We browse the tree twice, this could be probably improved if needed */
+int fd_msg_bufferize ( struct msg * msg, unsigned char ** buffer, size_t * len )
+{
+	int ret = 0;
+	unsigned char * buf = NULL;
+	size_t offset = 0;
+	
+	TRACE_ENTRY("%p %p %p", msg, buffer, len);
+	
+	/* Check the parameters */
+	CHECK_PARAMS(  buffer && CHECK_MSG(msg)  );
+	
+	/* Update the length. This also checks that all AVP have their values set */
+	CHECK_FCT(  fd_msg_update_length(msg)  );
+	
+	/* Now allocate a buffer to store the message */
+	CHECK_MALLOC(  buf = malloc(msg->msg_public.msg_length)  );
+	
+	/* Clear the memory, so that the padding is always 0 (should not matter) */
+	memset(buf, 0, msg->msg_public.msg_length);
+	
+	/* Write the message header in the buffer */
+	CHECK_FCT_DO( ret = bufferize_msg(buf, msg->msg_public.msg_length, &offset, msg), 
+		{
+			free(buf);
+			return ret;
+		}  );
+	
+	/* Write the list of AVPs */
+	CHECK_FCT_DO( ret = bufferize_chain(buf, msg->msg_public.msg_length, &offset, &msg->msg_chain.children),
+		{
+			free(buf);
+			return ret;
+		}  );
+	
+	ASSERT(offset == msg->msg_public.msg_length); /* or the msg_update_length is buggy */
+		
+	if (len) {
+		*len = offset;
+	}
+	
+	*buffer = buf;
+	return 0;
+}
+
+
+/***************************************************************************************************************/
+/* Parsing buffers and building AVP objects lists (not parsing the AVP values which requires dictionary knowledge) */
+
+/* Parse a buffer containing a supposed list of AVPs */
+static int parsebuf_list(unsigned char * buf, size_t buflen, struct fd_list * head)
+{
+	size_t offset = 0;
+	
+	TRACE_ENTRY("%p %d %p", buf, buflen, head);
+	
+	while (offset < buflen) {
+		struct avp * avp;
+		
+		if (buflen - offset <= AVPHDRSZ_NOVEND) {
+			TRACE_DEBUG(INFO, "truncated buffer: remaining only %d bytes", buflen - offset);
+			return EBADMSG;
+		}
+		
+		/* Create a new AVP object */
+		CHECK_MALLOC(  avp = malloc (sizeof(struct avp))  );
+		
+		init_avp(avp);
+		
+		/* Initialize the header */
+		avp->avp_public.avp_code    = ntohl(*(uint32_t *)(buf + offset));
+		avp->avp_public.avp_flags   = buf[offset + 4];
+		avp->avp_public.avp_len     = ((uint32_t)buf[offset+5]) << 16 |  ((uint32_t)buf[offset+6]) << 8 |  ((uint32_t)buf[offset+7]) ;
+		
+		offset += 8;
+		
+		if (avp->avp_public.avp_flags & AVP_FLAG_VENDOR) {
+			if (buflen - offset <= 4) {
+				TRACE_DEBUG(INFO, "truncated buffer: remaining only %d bytes for vendor and data", buflen - offset);
+				free(avp);
+				return EBADMSG;
+			}
+			avp->avp_public.avp_vendor  = ntohl(*(uint32_t *)(buf + offset));
+			offset += 4;
+		}
+		
+		/* Check there is enough remaining data in the buffer */
+		if (buflen - offset < avp->avp_public.avp_len - GETAVPHDRSZ(avp->avp_public.avp_flags)) {
+			TRACE_DEBUG(INFO, "truncated buffer: remaining only %d bytes for data, and avp data size is %d", 
+					buflen - offset, 
+					avp->avp_public.avp_len - GETAVPHDRSZ(avp->avp_public.avp_flags));
+			free(avp);
+			return EBADMSG;
+		}
+		
+		/* buf[offset] is now the beginning of the data */
+		avp->avp_source = &buf[offset];
+		
+		/* Now eat the data and eventual padding */
+		offset += PAD4(avp->avp_public.avp_len - GETAVPHDRSZ(avp->avp_public.avp_flags));
+		
+		/* And insert this avp in the list, at the end */
+		fd_list_insert_before( head, &avp->avp_chain.chaining );
+	}
+	
+	return 0;
+}
+
+/* Create a message object from a buffer. Dictionary objects are not resolved, AVP contents are not interpreted, buffer is saved in msg */
+int fd_msg_parse_buffer ( unsigned char ** buffer, size_t buflen, struct msg ** msg )
+{
+	struct msg * new = NULL;
+	int ret = 0;
+	uint32_t msglen = 0;
+	unsigned char * buf;
+	
+	TRACE_ENTRY("%p %d %p", buffer, buflen, msg);
+	
+	CHECK_PARAMS(  buffer &&  *buffer  &&  msg  &&  (buflen >= GETMSGHDRSZ())  );
+	buf = *buffer;
+	*buffer = NULL;
+	
+	if ( buf[0] != DIAMETER_VERSION) {
+		TRACE_DEBUG(INFO, "Invalid version in message: %d (supported: %d)", buf[0], DIAMETER_VERSION);
+		free(buf);
+		return EBADMSG;
+	}
+	
+	msglen = ntohl(*(uint32_t *)buf) & 0x00ffffff;
+	if ( buflen < msglen ) {  
+		TRACE_DEBUG(INFO, "Truncated message (%d / %d)", buflen, msglen );
+		free(buf);
+		return EBADMSG; 
+	}
+	
+	/* Create a new object */
+	CHECK_MALLOC_DO( new = malloc (sizeof(struct msg)),  { free(buf); return ENOMEM; }  );
+	
+	/* Initialize the fields */
+	init_msg(new);
+	
+	/* Now read from the buffer */
+	new->msg_public.msg_version = buf[0];
+	new->msg_public.msg_length = msglen;
+
+	new->msg_public.msg_flags = buf[4];
+	new->msg_public.msg_code = ntohl(*(uint32_t *)(buf+4)) & 0x00ffffff;
+	
+	new->msg_public.msg_appl = ntohl(*(uint32_t *)(buf+8));
+	new->msg_public.msg_hbhid = ntohl(*(uint32_t *)(buf+12));
+	new->msg_public.msg_eteid = ntohl(*(uint32_t *)(buf+16));
+	
+	new->msg_rawbuffer = buf;
+	
+	/* Parse the AVP list */
+	CHECK_FCT_DO( ret = parsebuf_list(buf + GETMSGHDRSZ(), buflen - GETMSGHDRSZ(), &new->msg_chain.children), { destroy_tree(_C(new)); return ret; }  );
+	
+	*msg = new;
+	return 0;
+}
+
+		
+/***************************************************************************************************************/
+/* Parsing messages and AVP with dictionary information */
+
+/* Resolve dictionary objects of the cmd and avp instances, from their headers.
+ * When the model is found, the data is interpreted from the avp_source buffer and copied to avp_storage.
+ * When the model is not found, the data is copied as rawdata and saved (in case we FW the message).
+ * Therefore, after this function has been called, the source buffer can be freed.
+ * For command, if the dictionary model is not found, an error is returned.
+ */
+
+static int parsedict_do_chain(struct dictionary * dict, struct fd_list * head, int mandatory, struct fd_pei *error_info);
+
+/* Process an AVP. If we are not in recheck, the avp_source must be set. */
+static int parsedict_do_avp(struct dictionary * dict, struct avp * avp, int mandatory, struct fd_pei *error_info)
+{
+	struct dict_avp_data dictdata;
+	
+	TRACE_ENTRY("%p %p %d %p", dict, avp, mandatory, error_info);
+	
+	/* First check we received an AVP as input */
+	CHECK_PARAMS(  CHECK_AVP(avp) );
+	
+	if (avp->avp_model != NULL) {
+		/* the model has already been resolved. we do check it is still valid */
+
+		CHECK_FCT(  fd_dict_getval(avp->avp_model, &dictdata)  );
+
+		if ( avp->avp_public.avp_code == dictdata.avp_code  ) {
+			/* Ok then just process the children if any */
+			return parsedict_do_chain(dict, &avp->avp_chain.children, mandatory && (avp->avp_public.avp_flags & AVP_FLAG_MANDATORY), error_info);
+		} else {
+			/* We just erase the old model */
+			avp->avp_model = NULL;
+		}
+	}
+	
+	/* Now try and resolve the model from the avp code and vendor */
+	if (avp->avp_public.avp_flags & AVP_FLAG_VENDOR) {
+		struct dict_avp_request avpreq;
+		avpreq.avp_vendor = avp->avp_public.avp_vendor;
+		avpreq.avp_code = avp->avp_public.avp_code;
+		CHECK_FCT( fd_dict_search ( dict, DICT_AVP, AVP_BY_CODE_AND_VENDOR, &avpreq, &avp->avp_model, 0));
+	} else {
+		/* no vendor */
+		CHECK_FCT( fd_dict_search ( dict, DICT_AVP, AVP_BY_CODE, &avp->avp_public.avp_code, &avp->avp_model, 0));
+	}
+	
+	/* First handle the case where we have not found this AVP in the dictionary */
+	if (!avp->avp_model) {
+		
+		if (mandatory && (avp->avp_public.avp_flags & AVP_FLAG_MANDATORY)) {
+			TRACE_DEBUG(INFO, "Unsupported mandatory AVP found:");
+			msg_dump_intern(INFO, avp, 2);
+			if (error_info) {
+				error_info->pei_errcode = "DIAMETER_AVP_UNSUPPORTED";
+				error_info->pei_avp = avp;
+			}
+			return ENOTSUP;
+		}
+		
+		if (avp->avp_source) {
+			/* we must copy the data from the source to the internal buffer area */
+			CHECK_PARAMS( !avp->avp_rawdata  );
+			
+			avp->avp_rawlen = avp->avp_public.avp_len - GETAVPHDRSZ( avp->avp_public.avp_flags );
+			
+			if (avp->avp_rawlen) {
+				CHECK_MALLOC(  avp->avp_rawdata = malloc(avp->avp_rawlen)  );
+			
+				memcpy(avp->avp_rawdata, avp->avp_source, avp->avp_rawlen);
+			}
+			
+			avp->avp_source = NULL;
+			
+			TRACE_DEBUG(FULL, "Unsupported optional AVP found, raw source data saved in avp_rawdata.");
+		}
+		
+		return 0;
+	}
+	
+	/* Ok we have resolved the object. Now we need to interpret its content. */
+	
+	CHECK_FCT(  fd_dict_getval(avp->avp_model, &dictdata)  );
+	
+	if (avp->avp_rawdata) {
+		/* This happens if the dictionary object was defined after the first check */
+		avp->avp_source = avp->avp_rawdata;
+	}
+	
+	/* A bit of sanity here... */
+	ASSERT(CHECK_BASETYPE(dictdata.avp_basetype));
+	
+	/* Check the size is valid */
+	if ((avp_value_sizes[dictdata.avp_basetype] != 0) &&
+	    (avp->avp_public.avp_len - GETAVPHDRSZ( avp->avp_public.avp_flags ) != avp_value_sizes[dictdata.avp_basetype])) {
+		TRACE_DEBUG(INFO, "The AVP size is not suitable for the type.");
+		if (error_info) {
+			error_info->pei_errcode = "DIAMETER_INVALID_AVP_LENGTH";
+			error_info->pei_avp = avp;
+		}
+		return EBADMSG;
+	}
+	
+	/* Now get the value inside */
+	switch (dictdata.avp_basetype) {
+		case AVP_TYPE_GROUPED: {
+			int ret;
+			
+			/* This is a grouped AVP, so let's parse the list of AVPs inside */
+			CHECK_FCT_DO(  ret = parsebuf_list(avp->avp_source, avp->avp_public.avp_len - GETAVPHDRSZ( avp->avp_public.avp_flags ), &avp->avp_chain.children),
+				{
+					if ((ret == EBADMSG) && (error_info)) {
+						error_info->pei_errcode = "DIAMETER_INVALID_AVP_VALUE";
+						error_info->pei_avp = avp;
+					}
+					return ret;
+				}  );
+			
+			return parsedict_do_chain(dict, &avp->avp_chain.children, mandatory && (avp->avp_public.avp_flags & AVP_FLAG_MANDATORY), error_info);
+		}
+			
+		case AVP_TYPE_OCTETSTRING:
+			/* We just have to copy the string into the storage area */
+			CHECK_PARAMS_DO( avp->avp_public.avp_len >= GETAVPHDRSZ( avp->avp_public.avp_flags ),
+				{
+					if (error_info) {
+						error_info->pei_errcode = "DIAMETER_INVALID_AVP_LENGTH";
+						error_info->pei_avp = avp;
+					}
+					return EBADMSG;
+				} );
+			avp->avp_storage.os.len = avp->avp_public.avp_len - GETAVPHDRSZ( avp->avp_public.avp_flags );
+			if (avp->avp_storage.os.len) {
+				CHECK_MALLOC(  avp->avp_storage.os.data = malloc(avp->avp_storage.os.len)  );
+				avp->avp_mustfreeos = 1;
+				memcpy(avp->avp_storage.os.data, avp->avp_source, avp->avp_storage.os.len);
+			} else {
+				avp->avp_storage.os.data = NULL;
+			}
+			break;
+		
+		case AVP_TYPE_INTEGER32:
+			avp->avp_storage.i32 = (int32_t)ntohl(*(uint32_t *)avp->avp_source);
+			break;
+	
+		case AVP_TYPE_INTEGER64:
+			avp->avp_storage.i64 = (int64_t)ntohll(*(uint64_t *)avp->avp_source);
+			break;
+	
+		case AVP_TYPE_UNSIGNED32:
+		case AVP_TYPE_FLOAT32: /* For float, we must not cast, or the value is changed. Instead we use implicit cast by changing the member of the union */
+			avp->avp_storage.u32 = (uint32_t)ntohl(*(uint32_t *)avp->avp_source);
+			break;
+	
+		case AVP_TYPE_UNSIGNED64:
+		case AVP_TYPE_FLOAT64: /* same as 32 bits */
+			avp->avp_storage.u64 = (uint64_t)ntohll(*(uint64_t *)avp->avp_source);
+			break;
+	
+	}
+	
+	/* The value is now set, so set the data pointer and return 0 */
+	avp->avp_public.avp_value = &avp->avp_storage;
+	return 0;
+}
+
+/* Process a list of AVPs */
+static int parsedict_do_chain(struct dictionary * dict, struct fd_list * head, int mandatory, struct fd_pei *error_info)
+{
+	struct fd_list * avpch;
+	
+	TRACE_ENTRY("%p %p %d %p", dict, head, mandatory, error_info);
+	
+	/* Sanity check */
+	ASSERT ( head == head->head );
+	
+	/* Now process the list */
+	for (avpch=head->next; avpch != head; avpch = avpch->next) {
+		CHECK_FCT(  parsedict_do_avp(dict, _A(avpch->o), mandatory, error_info)  );
+	}
+	
+	/* Done */
+	return 0;
+}
+
+/* Process a msg header. */
+static int parsedict_do_msg(struct dictionary * dict, struct msg * msg, int only_hdr, struct fd_pei *error_info)
+{
+	int ret = 0;
+	
+	TRACE_ENTRY("%p %p %d %p", dict, msg, only_hdr, error_info);
+	
+	CHECK_PARAMS(  CHECK_MSG(msg)  );
+	
+	/* Look for the model from the header */
+	CHECK_FCT_DO( ret = fd_dict_search ( dict, DICT_COMMAND, 
+			(msg->msg_public.msg_flags & CMD_FLAG_REQUEST) ? CMD_BY_CODE_R : CMD_BY_CODE_A,
+			&msg->msg_public.msg_code,
+			&msg->msg_model, ENOTSUP),
+		{
+			if ((ret == ENOTSUP) && (error_info)) {
+				error_info->pei_errcode = "DIAMETER_COMMAND_UNSUPPORTED";
+				error_info->pei_protoerr = 1;
+			}
+			return ret;
+		} );
+	
+	if (!only_hdr) {
+		/* Then process the children */
+		ret = parsedict_do_chain(dict, &msg->msg_chain.children, 1, error_info);
+
+		/* Free the raw buffer if any */
+		if ((ret == 0) && (msg->msg_rawbuffer != NULL)) {
+			free(msg->msg_rawbuffer);
+			msg->msg_rawbuffer=NULL;
+		}
+	}
+	
+	return ret;
+}
+
+int fd_msg_parse_dict ( msg_or_avp * object, struct dictionary * dict, struct fd_pei *error_info )
+{
+	TRACE_ENTRY("%p %p %p", dict, object, error_info);
+	
+	CHECK_PARAMS(  VALIDATE_OBJ(object)  );
+	
+	if (error_info)
+		memset(error_info, 0, sizeof(struct fd_pei));
+	
+	switch (_C(object)->type) {
+		case MSG_MSG:
+			return parsedict_do_msg(dict, _M(object), 0, error_info);
+		
+		case MSG_AVP:
+			return parsedict_do_avp(dict, _A(object), 0, error_info);
+		
+		default:
+			ASSERT(0);
+	}
+	return EINVAL;
+}
+
+/***************************************************************************************************************/
+/* Parsing messages and AVP for rules (ABNF) compliance */
+
+/* This function is used to get stats (first occurence position, last occurence position, number of occurences) 
+   of AVP instances of a given model in a chain of AVP */
+static void parserules_stat_avps( struct dict_object * model_avp, struct fd_list *list, int * count, int * firstpos, int * lastpos) 
+{
+	struct fd_list * li;
+	int curpos = 0; /* The current position in the list */
+	
+	TRACE_ENTRY("%p %p %p %p %p", model_avp, list, count, firstpos, lastpos);
+	
+	*count = 0;	/* number of instances found */
+	*firstpos = 0;	/* position of the first instance */
+	*lastpos = 0;	/* position of the last instance, starting from the end */
+	
+	for (li = list->next; li != list; li = li->next) {
+		/* Increment the current position counter */
+		curpos++;
+		
+		/* If we previously saved a "lastpos" information, increment it */
+		if (*lastpos != 0)
+			(*lastpos)++;
+		
+		/* Check the type of the next AVP. We can compare the references directly, it is safe. */
+		if (_A(li->o)->avp_model == model_avp) {
+			
+			/* This AVP is of the type we are searching */
+			(*count)++;
+			
+			/* If we don't have yet a "firstpos", save it */
+			if (*firstpos == 0)
+				*firstpos = curpos;
+			
+			/* Reset the lastpos */
+			(*lastpos) = 1;
+		}
+	}
+}
+
+/* We use this structure as parameter for the next function */
+struct parserules_data {
+	struct fd_list  * sentinel;  	/* Sentinel of the list of children AVP */
+	struct fd_pei 	* pei;   	/* If the rule conflicts, save the error here */
+};
+
+/* Create an empty AVP of a given model (to use in Failed-AVP) */
+static struct avp * empty_avp(struct dict_object * model_avp)
+{
+	struct avp * avp = NULL;
+	struct dict_avp_data avp_info;
+	union avp_value val;
+	unsigned char os[1] = { '\0' };
+	
+	/* Create an instance */
+	CHECK_FCT_DO( fd_msg_avp_new(model_avp, 0, &avp ), return NULL );
+	
+	/* Type of the AVP */
+	CHECK_FCT_DO( fd_dict_getval(model_avp, &avp_info), return NULL );
+	
+	/* Prepare the empty value */
+	memset(&val, 0, sizeof(val));
+	switch (avp_info.avp_basetype) {
+		case AVP_TYPE_OCTETSTRING:
+			val.os.data = os;
+			val.os.len  = sizeof(os);
+		case AVP_TYPE_INTEGER32:
+		case AVP_TYPE_INTEGER64:
+		case AVP_TYPE_UNSIGNED32:
+		case AVP_TYPE_UNSIGNED64:
+		case AVP_TYPE_FLOAT32:
+		case AVP_TYPE_FLOAT64:
+			CHECK_FCT_DO( fd_msg_avp_setvalue(avp, &val), return NULL );
+		case AVP_TYPE_GROUPED:
+			/* For AVP_TYPE_GROUPED we don't do anything */
+			break;
+		default:
+			ASSERT(0); /* not handled */
+	}
+	
+	return avp;
+}
+
+/* Check that a list of AVPs is compliant with a given rule -- will be iterated on the list of rules */
+static int parserules_check_one_rule(void * data, struct dict_rule_data *rule)
+{
+	int count, first, last, min;
+	struct parserules_data * pr_data = data;
+	char * avp_name = "<unresolved name>";
+	
+	TRACE_ENTRY("%p %p", data, rule);
+	
+	/* Get statistics of the AVP concerned by this rule in the parent instance */
+	parserules_stat_avps( rule->rule_avp, pr_data->sentinel, &count, &first, &last);
+	
+	if (TRACE_BOOL(INFO))
+	{
+		struct dict_avp_data avpdata;
+		int ret;
+		ret = fd_dict_getval(rule->rule_avp, &avpdata);
+		if (ret == 0)
+			avp_name = avpdata.avp_name;
+		
+		TRACE_DEBUG(ANNOYING, "Checking rule: p:%d(%d) m/M:%2d/%2d. Counted %d (first: %d, last:%d) of AVP '%s'", 
+					rule->rule_position,
+					rule->rule_order,
+					rule->rule_min,
+					rule->rule_max,
+					count, 
+					first, 
+					last,
+					avp_name
+				);
+	}
+	
+	/* Now check the rule is not conflicting */
+	
+	/* Check the "min" value */
+	if ((min = rule->rule_min) == -1) {
+		if (rule->rule_position == RULE_OPTIONAL)
+			min = 0;
+		else
+			min = 1;
+	}
+	if (count < min) {
+		TRACE_DEBUG(INFO, "Conflicting rule: the number of occurences (%d) is < the rule min (%d) for '%s'.", count, min, avp_name);
+		if (pr_data->pei) {
+			pr_data->pei->pei_errcode = "DIAMETER_MISSING_AVP";
+			pr_data->pei->pei_avp = empty_avp(rule->rule_avp);
+		}
+		return EBADMSG;
+	}
+	
+	/* Check the "max" value */
+	if ((rule->rule_max != -1) && (count > rule->rule_max)) {
+		TRACE_DEBUG(INFO, "Conflicting rule: the number of occurences (%d) is > the rule max (%d) for '%s'.", count, rule->rule_max, avp_name);
+		if (pr_data->pei) {
+			if (rule->rule_max == 0)
+				pr_data->pei->pei_errcode = "DIAMETER_AVP_NOT_ALLOWED";
+			else
+				pr_data->pei->pei_errcode = "DIAMETER_AVP_OCCURS_TOO_MANY_TIMES";
+			pr_data->pei->pei_avp = empty_avp(rule->rule_avp); /* Well we are supposed to return the (max + 1)th instance of the AVP instead... Pfff... */ TODO("Improve...");
+		}
+		return EBADMSG;
+	}
+		
+	/* Check the position and order (if relevant) */
+	switch (rule->rule_position) {
+		case RULE_OPTIONAL:
+		case RULE_REQUIRED:
+			/* No special position constraints */
+			break;
+		
+		case RULE_FIXED_HEAD:
+			/* Since "0*1<fixed>" is a valid rule specifier, we only reject cases where the AVP appears *after* its fixed position */
+			if (first > rule->rule_order) {
+				TRACE_DEBUG(INFO, "Conflicting rule: the FIXED_HEAD AVP appears first in (%d) position, the rule requires (%d) for '%s'.", first, rule->rule_order, avp_name);
+				if (pr_data->pei) {
+					pr_data->pei->pei_errcode = "DIAMETER_MISSING_AVP";
+					pr_data->pei->pei_message = "AVP was not in its fixed position";
+					pr_data->pei->pei_avp = empty_avp(rule->rule_avp);
+				}
+				return EBADMSG;
+			}
+			break;
+	
+		case RULE_FIXED_TAIL:
+			/* Since "0*1<fixed>" is a valid rule specifier, we only reject cases where the AVP appears *before* its fixed position */
+			if (last > rule->rule_order) {	/* We have a ">" here because we count in reverse order (i.e. from the end) */
+				TRACE_DEBUG(INFO, "Conflicting rule: the FIXED_TAIL AVP appears last in (%d) position, the rule requires (%d) for '%s'.", last, rule->rule_order, avp_name);
+				if (pr_data->pei) {
+					pr_data->pei->pei_errcode = "DIAMETER_MISSING_AVP";
+					pr_data->pei->pei_message = "AVP was not in its fixed position";
+					pr_data->pei->pei_avp = empty_avp(rule->rule_avp);
+				}
+				return EBADMSG;
+			}
+			break;
+		
+		default:
+			/* What is this position ??? */
+			ASSERT(0);
+			return ENOTSUP;
+	}
+	
+	/* We've checked all the parameters */
+	return 0;
+}
+
+/* Check the rules recursively */
+static int parserules_do ( struct dictionary * dict, msg_or_avp * object, struct fd_pei *error_info, int mandatory)
+{
+	struct parserules_data data;
+	struct dict_object * model = NULL;
+	
+	TRACE_ENTRY("%p %p %p %d", dict, object, error_info, mandatory);
+	
+	/* object has already been checked and dict-parsed when we are called. */
+	
+	/* First, handle the cases where there is no model */
+	{
+		if (CHECK_MSG(object)) {
+			if ( _M(object)->msg_public.msg_flags & CMD_FLAG_ERROR ) {
+				/* The case of error messages: the ABNF is different */
+				CHECK_FCT( fd_dict_get_error_cmd(dict, &model) );
+			} else {
+				model = _M(object)->msg_model;
+			}
+			/* Commands MUST be supported in the dictionary */
+			if (model == NULL) {
+				TRACE_DEBUG(INFO, "Message with no dictionary model. EBADMSG");
+				if (error_info) {
+					error_info->pei_errcode = "DIAMETER_COMMAND_UNSUPPORTED";
+					error_info->pei_protoerr = 1;
+				}
+				return EBADMSG;
+			}
+		}
+
+		/* AVP with the 'M' flag must also be recognized in the dictionary -- except inside an optional grouped AVP */
+		if (CHECK_AVP(object) && ((model = _A(object)->avp_model) == NULL)) {
+			if ( mandatory && (_A(object)->avp_public.avp_flags & AVP_FLAG_MANDATORY)) {
+				/* Return an error in this case */
+				TRACE_DEBUG(INFO, "Mandatory AVP with no dictionary model. EBADMSG");
+				if (error_info) {
+					error_info->pei_errcode = "DIAMETER_AVP_UNSUPPORTED";
+					error_info->pei_avp = object;
+				}
+				return EBADMSG;
+			} else {
+				/* We don't know any rule for this object, so assume OK */
+				TRACE_DEBUG(FULL, "Unknown informational AVP, ignoring...");
+				return 0;
+			}
+		}
+	}
+	
+	/* At this point we know "model" is set and points to the object's model */
+	
+	/* If we are an AVP with no children, just return OK */
+	if (CHECK_AVP(object)) {
+		struct dict_avp_data	dictdata;
+		CHECK_FCT(  fd_dict_getval(model, &dictdata)  );
+		if (dictdata.avp_basetype != AVP_TYPE_GROUPED) {
+			/* This object has no children and no rules */
+			return 0;
+		}
+	}
+	
+	/* If this object has children, first check the rules for all its children */
+	{
+		int is_child_mand = 0;
+		struct fd_list * ch = NULL;
+		if (  CHECK_MSG(object) 
+		   || (mandatory && (_A(object)->avp_public.avp_flags & AVP_FLAG_MANDATORY)) )
+			is_child_mand = 1;
+		for (ch = _C(object)->children.next; ch != &_C(object)->children; ch = ch->next) {
+			CHECK_FCT(  parserules_do ( dict, _C(ch->o), error_info, is_child_mand )  );
+		}
+	}
+
+	/* Now check all rules of this object */
+	data.sentinel = &_C(object)->children;
+	data.pei  = error_info;
+	CHECK_FCT( fd_dict_iterate_rules ( model, &data, parserules_check_one_rule ) );
+	
+	return 0;
+}
+
+int fd_msg_parse_rules ( msg_or_avp * object, struct dictionary * dict, struct fd_pei *error_info)
+{
+	TRACE_ENTRY("%p %p %p", object, dict, error_info);
+	
+	if (error_info)
+		memset(error_info, 0, sizeof(struct fd_pei));
+	
+	/* Resolve the dictionary objects when missing. This also validates the object. */
+	CHECK_FCT(  fd_msg_parse_dict ( object, dict, error_info )  );
+	
+	/* Call the recursive function */
+	return parserules_do ( dict, object, error_info, 1 ) ;
+}
+
+/***************************************************************************************************************/
+
+/* Compute the lengh of an object and its subtree. */
+int fd_msg_update_length ( msg_or_avp * object )
+{
+	size_t sz = 0;
+	struct dict_object * model;
+	union {
+		struct dict_cmd_data   cmddata;
+		struct dict_avp_data   avpdata;
+	} dictdata;
+	
+	TRACE_ENTRY("%p", object);
+	
+	/* Get the model of the object. This also validates the object */
+	CHECK_FCT( fd_msg_model ( object, &model ) );
+	
+	/* Get the information of the model */
+	if (model) {
+		CHECK_FCT(  fd_dict_getval(model, &dictdata)  );
+	} else {
+		/* For unknown AVP, just don't change the size */
+		if (_C(object)->type == MSG_AVP)
+			return 0;
+	}
+	
+	/* Deal with easy cases: AVPs without children */
+	if ((_C(object)->type == MSG_AVP) && (dictdata.avpdata.avp_basetype != AVP_TYPE_GROUPED)) {
+		/* Sanity check */
+		ASSERT(FD_IS_LIST_EMPTY(&_A(object)->avp_chain.children));
+		
+		/* Now check that the data is set in the AVP */
+		CHECK_PARAMS(  _A(object)->avp_public.avp_value  );
+		
+		sz = GETAVPHDRSZ( _A(object)->avp_public.avp_flags );
+		
+		switch (dictdata.avpdata.avp_basetype) {
+			case AVP_TYPE_OCTETSTRING:
+				sz += _A(object)->avp_public.avp_value->os.len;
+				break;
+			
+			case AVP_TYPE_INTEGER32:
+			case AVP_TYPE_INTEGER64:
+			case AVP_TYPE_UNSIGNED32:
+			case AVP_TYPE_UNSIGNED64:
+			case AVP_TYPE_FLOAT32:
+			case AVP_TYPE_FLOAT64:
+				sz += avp_value_sizes[dictdata.avpdata.avp_basetype];
+				break;
+			
+			default:
+				/* Something went wrong... */
+				ASSERT(0);
+		}
+	}
+	else  /* message or grouped AVP */
+	{
+		struct fd_list * ch = NULL;
+		
+		/* First, compute the header size */
+		if (_C(object)->type == MSG_AVP) {
+			sz = GETAVPHDRSZ( _A(object)->avp_public.avp_flags );
+		} else {
+			sz = GETMSGHDRSZ( );
+		}
+		
+		/* Recurse in all children and update the sz information */
+		for (ch = _C(object)->children.next; ch != &_C(object)->children; ch = ch->next) {
+			CHECK_FCT(  fd_msg_update_length ( ch->o )  );
+			
+			/* Add the padded size to the parent */
+			sz += PAD4( _A(ch->o)->avp_public.avp_len );
+		}
+	}
+	
+	/* When we arrive here, the "sz" variable contains the size to write in the object */
+	if (_C(object)->type == MSG_AVP) 
+		_A(object)->avp_public.avp_len = sz;
+	else
+		_M(object)->msg_public.msg_length = sz;
+	
+	return 0;
+}
+
+/***************************************************************************************************************/
+/* Macro to check if further callbacks must be called */
+#define TEST_ACTION_STOP()					\
+	if ((*msg == NULL) || (*action != DISP_ACT_CONT))	\
+		goto no_error;
+
+/* Call all dispatch callbacks for a given message */
+int fd_msg_dispatch ( struct msg ** msg, struct session * session, enum disp_action *action, const char ** error_code)
+{
+	struct dictionary  * dict;
+	struct dict_object * app;
+	struct dict_object * cmd;
+	struct avp * avp;
+	struct fd_list * cb_list;
+	int ret = 0;
+	
+	TRACE_ENTRY("%p %p %p %p", msg, session, action, error_code);
+	CHECK_PARAMS( msg && CHECK_MSG(*msg) && action);
+	
+	if (error_code)
+		*error_code = NULL;
+	*action = DISP_ACT_CONT;
+	
+	/* Take the dispatch lock */
+	CHECK_FCT( pthread_rwlock_rdlock(&fd_disp_lock) );
+	pthread_cleanup_push( fd_cleanup_rwlock, &fd_disp_lock );
+	
+	/* First, call the DISP_HOW_ANY callbacks */
+	CHECK_FCT_DO( ret = fd_disp_call_cb_int( NULL, msg, NULL, session, action, NULL, NULL, NULL, NULL ), goto error );
+
+	TEST_ACTION_STOP();
+	
+	/* If we don't know the model at this point, we stop cause we cannot get the dictionary. It's invalid: an error should already have been trigged by ANY callbacks */
+	CHECK_PARAMS_DO(cmd = (*msg)->msg_model, { ret = EINVAL; goto error; } );
+	
+	/* Now resolve message application */
+	CHECK_FCT_DO( ret = fd_dict_getdict( cmd, &dict ), goto error );
+	CHECK_FCT_DO( ret = fd_dict_search( dict, DICT_APPLICATION, APPLICATION_BY_ID, &(*msg)->msg_public.msg_appl, &app, 0 ), goto error );
+	
+	if (app == NULL) {
+		if ((*msg)->msg_public.msg_flags & CMD_FLAG_REQUEST) {
+			if (error_code)
+				*error_code = "DIAMETER_APPLICATION_UNSUPPORTED";
+			*action = DISP_ACT_ERROR;
+		} else {
+			TRACE_DEBUG(INFO, "Received an answer to a local query with an unsupported application %d, discarding...",  (*msg)->msg_public.msg_appl);
+			fd_msg_dump_walk(INFO, *msg);
+			fd_msg_free(*msg);
+			*msg = NULL;
+		}
+		goto no_error;
+	}
+	
+	/* So start browsing the message */
+	CHECK_FCT_DO( ret = fd_msg_browse( *msg, MSG_BRW_FIRST_CHILD, &avp, NULL ), goto error );
+	while (avp != NULL) {
+		/* For unknown AVP, we don't have a callback registered, so just skip */
+		if (avp->avp_model) {
+			struct dict_object * enumval = NULL;
+			
+			/* Get the list of callback for this AVP */
+			CHECK_FCT_DO( ret = fd_dict_disp_cb(DICT_AVP, avp->avp_model, &cb_list), goto error );
+			
+			/* We search enumerated values only in case of non-grouped AVP */
+			if ( avp->avp_public.avp_value ) {
+				struct dict_object * type;
+				/* Check if the AVP has a constant value */
+				CHECK_FCT_DO( ret = fd_dict_search(dict, DICT_TYPE, TYPE_OF_AVP, avp->avp_model, &type, 0), goto error );
+				if (type) {
+					struct dict_enumval_request req;
+					memset(&req, 0, sizeof(struct dict_enumval_request));
+					req.type_obj = type;
+					memcpy( &req.search.enum_value, avp->avp_public.avp_value, sizeof(union avp_value) );
+					CHECK_FCT_DO( ret = fd_dict_search(dict, DICT_ENUMVAL, ENUMVAL_BY_STRUCT, &req, &enumval, 0), goto error );
+				}
+			}
+			
+			/* Call the callbacks */
+			CHECK_FCT_DO( ret = fd_disp_call_cb_int( cb_list, msg, avp, session, action, app, cmd, avp->avp_model, enumval ), goto error );
+			TEST_ACTION_STOP();
+		}
+		/* Go to next AVP */
+		CHECK_FCT_DO(  ret = fd_msg_browse( avp, MSG_BRW_WALK, &avp, NULL ), goto error );
+	}
+		
+	/* Now call command and application callbacks */
+	CHECK_FCT_DO( ret = fd_dict_disp_cb(DICT_COMMAND, cmd, &cb_list), goto error );
+	CHECK_FCT_DO( ret = fd_disp_call_cb_int( cb_list, msg, NULL, session, action, app, cmd, NULL, NULL ), goto error );
+	TEST_ACTION_STOP();
+	
+	if (app) {
+		CHECK_FCT_DO( ret = fd_dict_disp_cb(DICT_APPLICATION, app, &cb_list), goto error );
+		CHECK_FCT_DO( ret = fd_disp_call_cb_int( cb_list, msg, NULL, session, action, app, cmd, NULL, NULL ), goto error );
+		TEST_ACTION_STOP();
+	}
+	
+	pthread_cleanup_pop(0);
+	
+no_error:
+	CHECK_POSIX(pthread_rwlock_unlock(&fd_disp_lock) );
+	return 0;
+	
+error:
+	CHECK_POSIX_DO(pthread_rwlock_unlock(&fd_disp_lock), /* ignore */ );
+	return ret;
+}
Index: libfdproto/rt_data.c
===================================================================
--- libfdproto/rt_data.c	(revision 658)
+++ libfdproto/rt_data.c	(revision 658)
@@ -0,0 +1,306 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@nict.go.jp>							 *
+*													 *
+* Copyright (c) 2010, 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.								 *
+*********************************************************************************************************/
+
+/* Routing module helpers.
+ * 
+ * This file provides support for the rt_data structure manipulation.
+ */
+
+#include "fdproto-internal.h"
+
+/* Structure that contains the routing data for a message */
+struct rt_data {
+	int		extracted;	/* if 0, candidates is ordered by diamid, otherwise the order is unspecified */
+	struct fd_list	candidates;	/* All the candidates. Items are struct rtd_candidate. */
+	struct fd_list	errors;		/* All errors received from other peers for this message */
+};
+
+/* Items of the errors list */
+struct rtd_error {
+	struct fd_list	chain;	/* link in the list, ordered by nexthop */
+	char * 		nexthop;/* the peer the message was sent to */
+	char *		erh;	/* the origin of the error */
+	uint32_t	code;	/* the error code */
+};
+
+/* Create a new structure to store routing data */
+int  fd_rtd_init(struct rt_data ** rtd)
+{
+	struct rt_data *new;
+	TRACE_ENTRY("%p", rtd);
+	CHECK_PARAMS(rtd);
+	
+	/* Alloc the structure */
+	CHECK_MALLOC( new = malloc(sizeof(struct rt_data)) );
+	memset(new, 0, sizeof(struct rt_data) );
+	fd_list_init(&new->candidates, new);
+	fd_list_init(&new->errors, new);
+	
+	*rtd = new;
+	return 0;
+}
+
+/* Destroy the routing data */
+void fd_rtd_free(struct rt_data ** rtd)
+{
+	struct rt_data *old;
+	
+	TRACE_ENTRY("%p", rtd);
+	CHECK_PARAMS_DO(rtd, return );
+	
+	old = *rtd;
+	*rtd = NULL;
+	
+	while (!FD_IS_LIST_EMPTY(&old->candidates)) {
+		struct rtd_candidate * c = (struct rtd_candidate *) old->candidates.next;
+		
+		fd_list_unlink(&c->chain);
+		free(c->diamid);
+		free(c->realm);
+		free(c);
+	}
+	
+	while (!FD_IS_LIST_EMPTY(&old->errors)) {
+		struct rtd_error * c = (struct rtd_error *) old->errors.next;
+		
+		fd_list_unlink(&c->chain);
+		free(c->nexthop);
+		free(c->erh);
+		free(c);
+	}
+	
+	free(old);
+	
+	return;
+}
+
+/* Add a peer to the candidates list */
+int  fd_rtd_candidate_add(struct rt_data * rtd, char * peerid, char * realm)
+{
+	struct fd_list * prev;
+	struct rtd_candidate * new;
+	
+	TRACE_ENTRY("%p %p", rtd, peerid);
+	CHECK_PARAMS(rtd && peerid);
+	
+	/* Since the peers are ordered when they are added (fd_g_activ_peers) we search for the position from the end -- this should be efficient */
+	for (prev = rtd->candidates.prev; prev != &rtd->candidates; prev = prev->prev) {
+		struct rtd_candidate * cp = (struct rtd_candidate *) prev;
+		int cmp = strcmp(peerid, cp->diamid);
+		if (cmp > 0)
+			break;
+		if (cmp == 0)
+			/* The candidate is already in the list */
+			return 0;
+	}
+	
+	CHECK_MALLOC( new = malloc(sizeof(struct rtd_candidate)) );
+	memset(new, 0, sizeof(struct rtd_candidate) );
+	fd_list_init(&new->chain, NULL);
+	CHECK_MALLOC( new->diamid = strdup(peerid) );
+	CHECK_MALLOC( new->realm = strdup(realm) );
+	
+	fd_list_insert_after(prev, &new->chain);
+	
+	return 0;
+}
+
+/* Remove a peer from the candidates (if it is found) */
+void fd_rtd_candidate_del(struct rt_data * rtd, char * peerid, size_t sz /* if !0, peerid does not need to be \0 terminated */)
+{
+	struct fd_list * li;
+	
+	TRACE_ENTRY("%p %p %zd", rtd, peerid, sz);
+	CHECK_PARAMS_DO( rtd && peerid , return );
+	
+	for (li = rtd->candidates.next; li != &rtd->candidates; li = li->next) {
+		struct rtd_candidate * c = (struct rtd_candidate *) li;
+		int cmp;
+		if (sz) {
+			cmp = strncmp(peerid, c->diamid, sz);
+			if (!cmp) {
+				int len = strlen(c->diamid);
+				if (sz < len)
+					cmp = -1;
+				else if (sz == len)
+					cmp = 0;
+				else cmp = 1;
+			}
+		} else {
+			cmp = strcmp(peerid, c->diamid);
+		}
+		
+		if (!cmp) {
+			/* Found it! Remove it */
+			fd_list_unlink(&c->chain);
+			free(c->diamid);
+			free(c->realm);
+			free(c);
+			break;
+		}
+		
+		if (cmp > 0)
+			continue;
+		
+		/* The list is ordered only if not extracted */
+		if (! rtd->extracted)
+			break;
+	}
+	
+	return;
+}
+
+/* If a peer returned a protocol error for this message, save it so that we don't try to send it there again */
+int  fd_rtd_error_add(struct rt_data * rtd, char * sentto, uint8_t * origin, size_t originsz, uint32_t rcode)
+{
+	struct fd_list * li;
+	int match = 0;
+	
+	TRACE_ENTRY("%p %p %p %d", rtd, sentto, origin, rcode);
+	CHECK_PARAMS( rtd && sentto ); /* origin may be NULL */
+	
+	/* First add the new error entry */
+	for (li = rtd->errors.next; li != &rtd->errors; li = li->next) {
+		struct rtd_error * e = (struct rtd_error *) li;
+		int cmp = strcmp(sentto, e->nexthop);
+		if (cmp > 0)
+			continue;
+		if (!cmp)
+			match = 1;
+		break;
+	}
+	
+	/* If we already had this entry, we should not have sent the message again to this peer... anyway... */
+	if (!match) {
+		/* Add a new entry in the error list */
+		struct rtd_error * new;
+		CHECK_MALLOC( new = malloc(sizeof(struct rtd_error)) );
+		memset(new, 0, sizeof(struct rtd_error));
+		fd_list_init(&new->chain, NULL);
+		CHECK_MALLOC(new->nexthop = strdup(sentto));
+		if (origin) {
+			CHECK_MALLOC( new->erh = malloc(originsz + 1) );
+			memcpy(new->erh, origin, originsz);
+			new->erh[originsz] = '\0';
+		}
+		new->code = rcode;
+		fd_list_insert_before(li, &new->chain);
+	}
+	
+	/* Finally, remove this (these) peers from the candidate list */
+	fd_rtd_candidate_del(rtd, sentto, 0);
+	if (origin)
+		fd_rtd_candidate_del(rtd, (char *)origin, originsz);
+	
+	/* Done! */
+	return 0;
+}
+
+/* Extract the list of valid candidates, and initialize their scores */
+void fd_rtd_candidate_extract(struct rt_data * rtd, struct fd_list ** candidates, int ini_score)
+{
+	struct fd_list * li;
+	
+	TRACE_ENTRY("%p %p", rtd, candidates);
+	CHECK_PARAMS_DO( candidates, return );
+	CHECK_PARAMS_DO( rtd, { *candidates = NULL; return; } );
+	
+	*candidates = &rtd->candidates;
+	
+	/* Reset all scores to INITIAL score */
+	for (li = rtd->candidates.next; li != &rtd->candidates; li = li->next) {
+		struct rtd_candidate * c = (struct rtd_candidate *) li;
+		c->score = ini_score;
+	}
+	
+	rtd->extracted = 1;
+	return;
+}
+
+/* Reorder the list of peers. If several peer have the same highest score, they are randomized. */
+int  fd_rtd_candidate_reorder(struct fd_list * candidates)
+{
+	struct fd_list unordered = FD_LIST_INITIALIZER(unordered), *li;
+	struct fd_list highest = FD_LIST_INITIALIZER(highest);
+	int hs = -1;
+	
+	TRACE_ENTRY("%p", candidates);
+	CHECK_PARAMS( candidates );
+	
+	/* First, move all items from candidates to the undordered list */
+	fd_list_move_end(&unordered, candidates);
+	
+	/* Now extract each element from unordered and add it back to list ordered by score */
+	while (!FD_IS_LIST_EMPTY(&unordered)) {
+		struct rtd_candidate * c = (struct rtd_candidate *) unordered.next;
+		
+		fd_list_unlink(&c->chain);
+		
+		/* If this candidate has a higher score than the previous ones */
+		if (c->score > hs) {
+			/* Then we move the previous high score items at end of the list */
+			fd_list_move_end(candidates, &highest);
+			
+			/* And the new high score is this reset */
+			hs = c->score;
+		}
+		
+		/* If this candidate equals the higher score, add it into highest list at a random place */
+		if (c->score == hs) {
+			if (rand() & 1) {
+				fd_list_insert_after(&highest, &c->chain);
+			} else {
+				fd_list_insert_before(&highest, &c->chain);
+			}
+		/* Otherwise, insert at normal place in the list */
+		} else {
+			/* Find the position in ordered candidates list */
+			for (li = candidates->next; li != candidates; li = li->next) {
+				struct rtd_candidate * cnext = (struct rtd_candidate *) li;
+				if (cnext->score >= c->score)
+					break;
+			}
+
+			/* Add the element there */
+			fd_list_insert_before(li, &c->chain);
+		}
+	}
+	
+	/* Now simply move back all the "highest" candidates at the end of the list */
+	fd_list_move_end(candidates, &highest);
+	
+	return 0;
+}
+
Index: libfdproto/sessions.c
===================================================================
--- libfdproto/sessions.c	(revision 658)
+++ libfdproto/sessions.c	(revision 658)
@@ -0,0 +1,815 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@nict.go.jp>							 *
+*													 *
+* Copyright (c) 2010, WIDE Project and NICT								 *
+* All rights reserved.											 *
+* 													 *
+* Redistribution and use of this software in source and binary forms, with or without modification, are  *
+* permitted provided that the following conditions are met:						 *
+* 													 *
+* * Redistributions of source code must retain the above 						 *
+*   copyright notice, this list of conditions and the 							 *
+*   following disclaimer.										 *
+*    													 *
+* * Redistributions in binary form must reproduce the above 						 *
+*   copyright notice, this list of conditions and the 							 *
+*   following disclaimer in the documentation and/or other						 *
+*   materials provided with the distribution.								 *
+* 													 *
+* * Neither the name of the WIDE Project or NICT nor the 						 *
+*   names of its contributors may be used to endorse or 						 *
+*   promote products derived from this software without 						 *
+*   specific prior written permission of WIDE Project and 						 *
+*   NICT.												 *
+* 													 *
+* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED *
+* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
+* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR *
+* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 	 *
+* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 	 *
+* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR *
+* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF   *
+* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.								 *
+*********************************************************************************************************/
+
+/* Sessions module.
+ * 
+ * Basic functionalities to help implementing User sessions state machines from RFC3588.
+ */
+
+#include "fdproto-internal.h"
+
+/*********************** Parameters **********************/
+
+/* Size of the hash table containing the session objects (pow of 2. ex: 6 => 2^6 = 64). must be between 0 and 31. */
+#ifndef SESS_HASH_SIZE
+#define SESS_HASH_SIZE	6
+#endif /* SESS_HASH_SIZE */
+
+/* Default lifetime of a session, in seconds. (31 days = 2678400 seconds) */
+#ifndef SESS_DEFAULT_LIFETIME
+#define SESS_DEFAULT_LIFETIME	2678400
+#endif /* SESS_DEFAULT_LIFETIME */
+
+/********************** /Parameters **********************/
+
+/* Eyescatchers definitions */
+#define SH_EYEC 0x53554AD1
+#define SD_EYEC 0x5355D474
+#define SI_EYEC 0x53551D
+
+/* Macro to check an object is valid */
+#define VALIDATE_SH( _obj ) ( ((_obj) != NULL) && ( ((struct session_handler *)(_obj))->eyec == SH_EYEC) )
+#define VALIDATE_SI( _obj ) ( ((_obj) != NULL) && ( ((struct session         *)(_obj))->eyec == SI_EYEC) )
+
+
+/* Handlers registered by users of the session module */
+struct session_handler {
+	int		  eyec;	/* An eye catcher also used to ensure the object is valid, must be SH_EYEC */
+	int		  id;	/* A unique integer to identify this handler */
+	void 		(*cleanup)(session_state *, char *, void *); /* The cleanup function to be called for cleaning a state */
+	void             *opaque; /* a value that is passed as is to the cleanup callback */
+};
+
+static int 		hdl_id = 0;				/* A global counter to initialize the id field */
+static pthread_mutex_t	hdl_lock = PTHREAD_MUTEX_INITIALIZER;	/* lock to protect hdl_id; we could use atomic operations otherwise (less portable) */
+
+
+/* Data structures linked from the sessions, containing the applications states */
+struct state {
+	int			 eyec;	/* Must be SD_EYEC */
+	session_state		*state;	/* The state registered by the application, never NULL (or the whole object is deleted) */
+	struct fd_list		 chain;	/* Chaining in the list of session's states ordered by hdl->id */
+	union {
+		struct session_handler	*hdl;	/* The handler for which this state was registered */
+		char 			*sid;	/* For deleted state, the sid of the session it belong to */
+	};
+};
+
+/* Session object, one for each value of Session-Id AVP */
+struct session {
+	int 		eyec;	/* Eyecatcher, SI_EYEC */
+	
+	char *		sid;	/* The \0-terminated Session-Id */
+	uint32_t	hash;	/* computed hash of sid */
+	struct fd_list	chain_h;/* chaining in the hash table of sessions. */
+	
+	struct timespec	timeout;/* Timeout date for the session */
+	struct fd_list	expire;	/* List of expiring sessions, ordered by timeouts. */
+	
+	pthread_mutex_t stlock;	/* A lock to protect the list of states associated with this session */
+	struct fd_list	states;	/* Sentinel for the list of states of this session. */
+	int		msg_cnt;/* Reference counter for the messages pointing to this session */
+};
+
+/* Sessions hash table, to allow fast sid to session retrieval */
+static struct {
+	struct fd_list	sentinel;	/* sentinel element for this sublist */
+	pthread_mutex_t lock;		/* the mutex for this sublist -- we might probably change it to rwlock for a little optimization */
+} sess_hash [ 1 << SESS_HASH_SIZE ] ;
+#define H_MASK( __hash ) ((__hash) & (( 1 << SESS_HASH_SIZE ) - 1))
+#define H_LIST( _hash ) (&(sess_hash[H_MASK(_hash)].sentinel))
+#define H_LOCK( _hash ) (&(sess_hash[H_MASK(_hash)].lock    ))
+
+/* The following are used to generate sid values that are eternaly unique */
+static uint32_t   	sid_h;	/* initialized to the current time in fd_sess_init */
+static uint32_t   	sid_l;	/* incremented each time a session id is created */
+static pthread_mutex_t 	sid_lock = PTHREAD_MUTEX_INITIALIZER;
+
+/* Expiring sessions management */
+static struct fd_list	exp_sentinel = FD_LIST_INITIALIZER(exp_sentinel);	/* list of sessions ordered by their timeout date */
+static pthread_mutex_t	exp_lock = PTHREAD_MUTEX_INITIALIZER;	/* lock protecting the list. */
+static pthread_cond_t	exp_cond = PTHREAD_COND_INITIALIZER;	/* condvar used by the expiry mecahinsm. */
+static pthread_t	exp_thr; 	/* The expiry thread that handles cleanup of expired sessions */
+
+/* Hierarchy of the locks, to avoid deadlocks:
+ *  hash lock > state lock > expiry lock
+ * i.e. state lock can be taken while holding the hash lock, but not while holding the expiry lock.
+ * As well, the hash lock cannot be taken while holding a state lock.
+ */
+
+/********************************************************************************************************/
+
+/* Initialize a session object. It is not linked now. sid must be already malloc'ed. */
+static struct session * new_session(char * sid, size_t sidlen)
+{
+	struct session * sess;
+	
+	TRACE_ENTRY("%p %d", sid, sidlen);
+	CHECK_PARAMS_DO( sid && sidlen, return NULL );
+	
+	CHECK_MALLOC_DO( sess = malloc(sizeof(struct session)), return NULL );
+	memset(sess, 0, sizeof(struct session));
+	
+	sess->eyec = SI_EYEC;
+	
+	sess->sid  = sid;
+	sess->hash = fd_hash(sid, sidlen);
+	fd_list_init(&sess->chain_h, sess);
+	
+	CHECK_SYS_DO( clock_gettime(CLOCK_REALTIME, &sess->timeout), return NULL );
+	sess->timeout.tv_sec += SESS_DEFAULT_LIFETIME;
+	fd_list_init(&sess->expire, sess);
+	
+	CHECK_POSIX_DO( pthread_mutex_init(&sess->stlock, NULL), return NULL );
+	fd_list_init(&sess->states, sess);
+	
+	return sess;
+}
+	
+/* The expiry thread */
+static void * exp_fct(void * arg)
+{
+	fd_log_threadname ( "Session/expire" );
+	TRACE_ENTRY( "" );
+	
+	
+	do {
+		struct timespec	now;
+		struct session * first;
+		
+		CHECK_POSIX_DO( pthread_mutex_lock(&exp_lock),  break );
+		pthread_cleanup_push( fd_cleanup_mutex, &exp_lock );
+again:		
+		/* Check if there are expiring sessions available */
+		if (FD_IS_LIST_EMPTY(&exp_sentinel)) {
+			/* Just wait for a change or cancelation */
+			CHECK_POSIX_DO( pthread_cond_wait( &exp_cond, &exp_lock ), break );
+			/* Restart the loop on wakeup */
+			goto again;
+		}
+		
+		/* Get the pointer to the session that expires first */
+		first = (struct session *)(exp_sentinel.next->o);
+		ASSERT( VALIDATE_SI(first) );
+		
+		/* Get the current time */
+		CHECK_SYS_DO(  clock_gettime(CLOCK_REALTIME, &now),  break  );
+
+		/* If first session is not expired, we just wait until it happens */
+		if ( TS_IS_INFERIOR( &now, &first->timeout ) ) {
+			
+			CHECK_POSIX_DO2(  pthread_cond_timedwait( &exp_cond, &exp_lock, &first->timeout ),  
+					ETIMEDOUT, /* ETIMEDOUT is a normal error, continue */,
+					/* on other error, */ break );
+	
+			/* on wakeup, loop */
+			goto again;
+		}
+		
+		/* Now, the first session in the list is expired; destroy it */
+		pthread_cleanup_pop( 0 );
+		CHECK_POSIX_DO( pthread_mutex_unlock(&exp_lock),  break );
+		
+		CHECK_FCT_DO( fd_sess_destroy( &first ), break );
+		
+	} while (1);
+	
+	TRACE_DEBUG(INFO, "An error occurred in session module! Expiry thread is terminating...");
+	ASSERT(0);
+	return NULL;
+}
+	
+	
+
+/********************************************************************************************************/
+
+/* Initialize the session module */
+int fd_sess_init(void)
+{
+	int i;
+	
+	TRACE_ENTRY( "" );
+	
+	/* Initialize the global counters */
+	sid_h = (uint32_t) time(NULL);
+	sid_l = 0;
+	
+	/* Initialize the hash table */
+	for (i = 0; i < sizeof(sess_hash) / sizeof(sess_hash[0]); i++) {
+		fd_list_init( &sess_hash[i].sentinel, NULL );
+		CHECK_POSIX(  pthread_mutex_init(&sess_hash[i].lock, NULL)  );
+	}
+	
+	return 0;
+}
+
+/* Run this when initializations are complete. */
+int fd_sess_start(void)
+{
+	/* Start session garbage collector (expiry) */
+	CHECK_POSIX(  pthread_create(&exp_thr, NULL, exp_fct, NULL)  );
+	
+	return 0;
+}
+
+/* Terminate */
+void fd_sess_fini(void)
+{
+	TRACE_ENTRY("");
+	CHECK_FCT_DO( fd_thr_term(&exp_thr), /* continue */ );
+	return;
+}
+
+/* Create a new handler */
+int fd_sess_handler_create_internal ( struct session_handler ** handler, void (*cleanup)(session_state * state, char * sid, void * opaque), void * opaque )
+{
+	struct session_handler *new;
+	
+	TRACE_ENTRY("%p %p", handler, cleanup);
+	
+	CHECK_PARAMS( handler && cleanup );
+	
+	CHECK_MALLOC( new = malloc(sizeof(struct session_handler)) );
+	memset(new, 0, sizeof(struct session_handler));
+	
+	CHECK_POSIX( pthread_mutex_lock(&hdl_lock) );
+	new->id = ++hdl_id;
+	CHECK_POSIX( pthread_mutex_unlock(&hdl_lock) );
+	
+	new->eyec = SH_EYEC;
+	new->cleanup = cleanup;
+	new->opaque = opaque;
+	
+	*handler = new;
+	return 0;
+}
+
+/* Destroy a handler, and all states attached to this handler. This operation is very slow but we don't care since it's rarely used. 
+ * Note that it's better to call this function after all sessions have been deleted... */
+int fd_sess_handler_destroy ( struct session_handler ** handler, void ** opaque )
+{
+	struct session_handler * del;
+	/* place to save the list of states to be cleaned up. We do it after finding them to avoid deadlocks. the "o" field becomes a copy of the sid. */
+	struct fd_list deleted_states = FD_LIST_INITIALIZER( deleted_states );
+	int i;
+	
+	TRACE_ENTRY("%p", handler);
+	CHECK_PARAMS( handler && VALIDATE_SH(*handler) );
+	
+	del = *handler;
+	*handler = NULL;
+	
+	del->eyec = 0xdead; /* The handler is not valid anymore for any other operation */
+	
+	/* Now find all sessions with data registered for this handler, and move this data to the deleted_states list. */
+	for (i = 0; i < sizeof(sess_hash) / sizeof(sess_hash[0]); i++) {
+		struct fd_list * li_si;
+		CHECK_POSIX(  pthread_mutex_lock(&sess_hash[i].lock)  );
+		
+		for (li_si = sess_hash[i].sentinel.next; li_si != &sess_hash[i].sentinel; li_si = li_si->next) {
+			struct fd_list * li_st;
+			struct session * sess = (struct session *)(li_si->o);
+			CHECK_POSIX(  pthread_mutex_lock(&sess->stlock)  );
+			for (li_st = sess->states.next; li_st != &sess->states; li_st = li_st->next) {
+				struct state * st = (struct state *)(li_st->o);
+				/* The list is ordered */
+				if (st->hdl->id < del->id)
+					continue;
+				if (st->hdl->id == del->id) {
+					/* This state belongs to the handler we are deleting, move the item to the deleted_states list */
+					fd_list_unlink(&st->chain);
+					CHECK_MALLOC( st->sid = strdup(sess->sid) );
+					fd_list_insert_before(&deleted_states, &st->chain);
+				}
+				break;
+			}
+			CHECK_POSIX(  pthread_mutex_unlock(&sess->stlock)  );
+		}
+		CHECK_POSIX(  pthread_mutex_unlock(&sess_hash[i].lock)  );
+	}
+	
+	/* Now, delete all states after calling their cleanup handler */
+	while (!FD_IS_LIST_EMPTY(&deleted_states)) {
+		struct state * st = (struct state *)(deleted_states.next->o);
+		TRACE_DEBUG(FULL, "Calling cleanup handler for session '%s' and data %p", st->sid, st->state);
+		(*del->cleanup)(st->state, st->sid, del->opaque);
+		free(st->sid);
+		fd_list_unlink(&st->chain);
+		free(st);
+	}
+	
+	if (opaque)
+		*opaque = del->opaque;
+	
+	/* Free the handler */
+	free(del);
+	
+	return 0;
+}
+
+
+
+/* Create a new session object with the default timeout value, and link it */
+int fd_sess_new ( struct session ** session, char * diamId, char * opt, size_t optlen )
+{
+	char * sid = NULL;
+	size_t sidlen;
+	struct session * sess;
+	struct fd_list * li;
+	int found = 0;
+	
+	TRACE_ENTRY("%p %p %p %d", session, diamId, opt, optlen);
+	CHECK_PARAMS( session && (diamId || opt) );
+	
+	/* Ok, first create the identifier for the string */
+	if (diamId == NULL) {
+		/* opt is the full string */
+		if (optlen) {
+			CHECK_MALLOC( sid = malloc(optlen + 1) );
+			strncpy(sid, opt, optlen);
+			sid[optlen] = '\0';
+			sidlen = optlen;
+		} else {
+			CHECK_MALLOC( sid = strdup(opt) );
+			sidlen = strlen(sid);
+		}
+	} else {
+		uint32_t sid_h_cpy;
+		uint32_t sid_l_cpy;
+		/* "<diamId>;<high32>;<low32>[;opt]" */
+		sidlen = strlen(diamId);
+		sidlen += 22; /* max size of ';<high32>;<low32>' */
+		if (opt)
+			sidlen += 1 + (optlen ?: strlen(opt)) ;
+		sidlen++; /* space for the final \0 also */
+		CHECK_MALLOC( sid = malloc(sidlen) );
+		
+		CHECK_POSIX( pthread_mutex_lock(&sid_lock) );
+		if ( ++sid_l == 0 ) /* overflow */
+			++sid_h;
+		sid_h_cpy = sid_h;
+		sid_l_cpy = sid_l;
+		CHECK_POSIX( pthread_mutex_unlock(&sid_lock) );
+		
+		if (opt) {
+			if (optlen)
+				sidlen = snprintf(sid, sidlen, "%s;%u;%u;%.*s", diamId, sid_h_cpy, sid_l_cpy, (int)optlen, opt);
+			else
+				sidlen = snprintf(sid, sidlen, "%s;%u;%u;%s", diamId, sid_h_cpy, sid_l_cpy, opt);
+		} else {
+			sidlen = snprintf(sid, sidlen, "%s;%u;%u", diamId, sid_h_cpy, sid_l_cpy);
+		}
+	}
+	
+	/* Initialize the session object now, to spend less time inside locked section later. 
+	 * Cons: we malloc then free if there is already a session with same SID; we could malloc later to avoid this. */
+	CHECK_MALLOC( sess = new_session(sid, sidlen) );
+	
+	/* Now find the place to add this object in the hash table. */
+	CHECK_POSIX( pthread_mutex_lock( H_LOCK(sess->hash) ) );
+	pthread_cleanup_push( fd_cleanup_mutex, H_LOCK(sess->hash) );
+	
+	for (li = H_LIST(sess->hash)->next; li != H_LIST(sess->hash); li = li->next) {
+		int cmp;
+		struct session * s = (struct session *)(li->o);
+		
+		/* The list is ordered by hash and sid (in case of collisions) */
+		if (s->hash < sess->hash)
+			continue;
+		if (s->hash > sess->hash)
+			break;
+		
+		cmp = strcasecmp(s->sid, sess->sid);
+		if (cmp < 0)
+			continue;
+		if (cmp > 0)
+			break;
+		
+		/* A session with the same sid was already in the hash table */
+		found = 1;
+		*session = s;
+		break;
+	}
+	
+	/* If the session did not exist, we can link it in global tables */
+	if (!found) {
+		fd_list_insert_before(li, &sess->chain_h); /* hash table */
+		
+		/* We must also insert in the expiry list */
+		CHECK_POSIX( pthread_mutex_lock( &exp_lock ) );
+		pthread_cleanup_push( fd_cleanup_mutex, &exp_lock );
+		
+		/* Find the position in that list. We take it in reverse order */
+		for (li = exp_sentinel.prev; li != &exp_sentinel; li = li->prev) {
+			struct session * s = (struct session *)(li->o);
+			if (TS_IS_INFERIOR( &s->timeout, &sess->timeout ) )
+				break;
+		}
+		fd_list_insert_after( li, &sess->expire );
+
+		/* We added a new expiring element, we must signal */
+		if (li == &exp_sentinel) {
+			CHECK_POSIX( pthread_cond_signal(&exp_cond) );
+		}
+		
+		#if 0
+		if (TRACE_BOOL(ANNOYING)) {	
+			TRACE_DEBUG(FULL, "-- Updated session expiry list --");
+			for (li = exp_sentinel.next; li != &exp_sentinel; li = li->next) {
+				struct session * s = (struct session *)(li->o);
+				fd_sess_dump(FULL, s);
+			}
+			TRACE_DEBUG(FULL, "-- end of expiry list --");
+		}
+		#endif
+		
+		/* We're done */
+		pthread_cleanup_pop(0);
+		CHECK_POSIX( pthread_mutex_unlock( &exp_lock ) );
+	}
+	
+	pthread_cleanup_pop(0);
+	CHECK_POSIX( pthread_mutex_unlock( H_LOCK(sess->hash) ) );
+	
+	/* If a session already existed, we must destroy the new element */
+	if (found) {
+		CHECK_FCT( fd_sess_destroy( &sess ) ); /* we could avoid locking this time for optimization */
+		return EALREADY;
+	}
+	
+	*session = sess;
+	return 0;
+}
+
+/* Find or create a session */
+int fd_sess_fromsid ( char * sid, size_t len, struct session ** session, int * new)
+{
+	int ret;
+	
+	TRACE_ENTRY("%p %d %p %p", sid, len, session, new);
+	CHECK_PARAMS( sid && session );
+	
+	/* All the work is done in sess_new */
+	ret = fd_sess_new ( session, NULL, sid, len );
+	switch (ret) {
+		case 0:
+		case EALREADY:
+			break;
+		
+		default:
+			CHECK_FCT(ret);
+	}
+	
+	if (new)
+		*new = ret ? 0 : 1;
+	
+	return 0;
+}
+
+/* Get the sid of a session */
+int fd_sess_getsid ( struct session * session, char ** sid )
+{
+	TRACE_ENTRY("%p %p", session, sid);
+	
+	CHECK_PARAMS( VALIDATE_SI(session) && sid );
+	
+	*sid = session->sid;
+	
+	return 0;
+}
+
+/* Change the timeout value of a session */
+int fd_sess_settimeout( struct session * session, const struct timespec * timeout )
+{
+	struct fd_list * li;
+	
+	TRACE_ENTRY("%p %p", session, timeout);
+	CHECK_PARAMS( VALIDATE_SI(session) && timeout );
+	
+	/* Lock -- do we need to lock the hash table as well? I don't think so... */
+	CHECK_POSIX( pthread_mutex_lock( &exp_lock ) );
+	pthread_cleanup_push( fd_cleanup_mutex, &exp_lock );
+	
+	/* Update the timeout */
+	fd_list_unlink(&session->expire);
+	memcpy(&session->timeout, timeout, sizeof(struct timespec));
+	
+	/* Find the new position in expire list. We take it in normal order */
+	for (li = exp_sentinel.next; li != &exp_sentinel; li = li->next) {
+		struct session * s = (struct session *)(li->o);
+
+		if (TS_IS_INFERIOR( &s->timeout, &session->timeout ) )
+			continue;
+
+		break;
+	}
+	fd_list_insert_before( li, &session->expire );
+
+	/* We added a new expiring element, we must signal if it was in first position */
+	if (session->expire.prev == &exp_sentinel) {
+		CHECK_POSIX( pthread_cond_signal(&exp_cond) );
+	}
+
+	#if 0
+	if (TRACE_BOOL(ANNOYING)) {	
+		TRACE_DEBUG(FULL, "-- Updated session expiry list --");
+		for (li = exp_sentinel.next; li != &exp_sentinel; li = li->next) {
+			struct session * s = (struct session *)(li->o);
+			fd_sess_dump(FULL, s);
+		}
+		TRACE_DEBUG(FULL, "-- end of expiry list --");
+	}
+	#endif
+
+	/* We're done */
+	pthread_cleanup_pop(0);
+	CHECK_POSIX( pthread_mutex_unlock( &exp_lock ) );
+	
+	return 0;
+}
+
+/* Destroy a session immediatly */
+int fd_sess_destroy ( struct session ** session )
+{
+	struct session * sess;
+	
+	TRACE_ENTRY("%p", session);
+	CHECK_PARAMS( session && VALIDATE_SI(*session) );
+	
+	sess = *session;
+	*session = NULL;
+	
+	/* Unlink and invalidate */
+	CHECK_FCT( pthread_mutex_lock( H_LOCK(sess->hash) ) );
+	pthread_cleanup_push( fd_cleanup_mutex, H_LOCK(sess->hash) );
+	CHECK_FCT( pthread_mutex_lock( &exp_lock ) );
+	fd_list_unlink( &sess->chain_h );
+	fd_list_unlink( &sess->expire ); /* no need to signal the condition here */
+	sess->eyec = 0xdead;
+	CHECK_FCT( pthread_mutex_unlock( &exp_lock ) );
+	pthread_cleanup_pop(0);
+	CHECK_FCT( pthread_mutex_unlock( H_LOCK(sess->hash) ) );
+	
+	/* Now destroy all states associated -- we don't take the lock since nobody can access this session anymore (in theory) */
+	while (!FD_IS_LIST_EMPTY(&sess->states)) {
+		struct state * st = (struct state *)(sess->states.next->o);
+		fd_list_unlink(&st->chain);
+		TRACE_DEBUG(FULL, "Calling handler %p cleanup for state registered with session '%s'", st->hdl, sess->sid);
+		(*st->hdl->cleanup)(st->state, sess->sid, st->hdl->opaque);
+		free(st);
+	}
+	
+	/* Finally, destroy the session itself */
+	free(sess->sid);
+	free(sess);
+	
+	return 0;
+}
+
+/* Destroy a session if it is not used */
+int fd_sess_reclaim ( struct session ** session )
+{
+	struct session * sess;
+	uint32_t hash;
+	
+	TRACE_ENTRY("%p", session);
+	CHECK_PARAMS( session && VALIDATE_SI(*session) );
+	
+	sess = *session;
+	hash = sess->hash;
+	*session = NULL;
+	
+	CHECK_POSIX( pthread_mutex_lock( H_LOCK(sess->hash) ) );
+	pthread_cleanup_push( fd_cleanup_mutex, H_LOCK(sess->hash) );
+	CHECK_POSIX( pthread_mutex_lock( &exp_lock ) );
+	if (FD_IS_LIST_EMPTY(&sess->states)) {
+		fd_list_unlink( &sess->chain_h );
+		fd_list_unlink( &sess->expire );
+		sess->eyec = 0xdead;
+		free(sess->sid);
+		free(sess);
+	}
+	CHECK_POSIX( pthread_mutex_unlock( &exp_lock ) );
+	pthread_cleanup_pop(0);
+	CHECK_POSIX( pthread_mutex_unlock( H_LOCK(hash) ) );
+	
+	return 0;
+}
+
+/* Save a state information with a session */
+int fd_sess_state_store_internal ( struct session_handler * handler, struct session * session, session_state ** state )
+{
+	struct state *new;
+	struct fd_list * li;
+	int already = 0;
+	
+	TRACE_ENTRY("%p %p %p", handler, session, state);
+	CHECK_PARAMS( handler && VALIDATE_SH(handler) && session && VALIDATE_SI(session) && state );
+	
+	/* Lock the session state list */
+	CHECK_POSIX( pthread_mutex_lock(&session->stlock) );
+	pthread_cleanup_push( fd_cleanup_mutex, &session->stlock );
+			
+	/* Create the new state object */
+	CHECK_MALLOC(new = malloc(sizeof(struct state)) );
+	memset(new, 0, sizeof(struct state));
+	
+	new->eyec = SD_EYEC;
+	new->state= *state;
+	fd_list_init(&new->chain, new);
+	new->hdl = handler;
+	
+	/* find place for this state in the list */
+	for (li = session->states.next; li != &session->states; li = li->next) {
+		struct state * st = (struct state *)(li->o);
+		/* The list is ordered by handler's id */
+		if (st->hdl->id < handler->id)
+			continue;
+		
+		if (st->hdl->id == handler->id) {
+			TRACE_DEBUG(INFO, "A state was already stored for session '%s' and handler '%p', at location %p", session->sid, st->hdl, st->state);
+			already = 1;
+		}
+		
+		break;
+	}
+	
+	if (!already) {
+		fd_list_insert_before(li, &new->chain);
+		*state = NULL;
+	} else {
+		free(new);
+	}
+	
+	pthread_cleanup_pop(0);
+	CHECK_POSIX( pthread_mutex_unlock(&session->stlock) );
+	
+	return already ? EALREADY : 0;
+}
+
+/* Get the data back */
+int fd_sess_state_retrieve_internal ( struct session_handler * handler, struct session * session, session_state ** state )
+{
+	struct fd_list * li;
+	struct state * st = NULL;
+	
+	TRACE_ENTRY("%p %p %p", handler, session, state);
+	CHECK_PARAMS( handler && VALIDATE_SH(handler) && session && VALIDATE_SI(session) && state );
+	
+	*state = NULL;
+	
+	/* Lock the session state list */
+	CHECK_POSIX( pthread_mutex_lock(&session->stlock) );
+	pthread_cleanup_push( fd_cleanup_mutex, &session->stlock );
+	
+	/* find the state in the list */
+	for (li = session->states.next; li != &session->states; li = li->next) {
+		st = (struct state *)(li->o);
+		
+		/* The list is ordered by handler's id */
+		if (st->hdl->id > handler->id)
+			break;
+	}
+	
+	/* If we found the state */
+	if (st && (st->hdl == handler)) {
+		fd_list_unlink(&st->chain);
+		*state = st->state;
+		free(st);
+	}
+	
+	pthread_cleanup_pop(0);
+	CHECK_POSIX( pthread_mutex_unlock(&session->stlock) );
+	
+	return 0;
+}
+
+/* For the messages module */
+int fd_sess_fromsid_msg ( unsigned char * sid, size_t len, struct session ** session, int * new)
+{
+	TRACE_ENTRY("%p %zd %p %p", sid, len, session, new);
+	CHECK_PARAMS( sid && len && session );
+	
+	/* Get the session object */
+	CHECK_FCT( fd_sess_fromsid ( (char *) sid, len, session, new) );
+	
+	/* Increase count */
+	CHECK_FCT( fd_sess_ref_msg ( *session ) );
+	
+	/* Done */
+	return 0;
+}
+
+int fd_sess_ref_msg ( struct session * session )
+{
+	TRACE_ENTRY("%p", session);
+	CHECK_PARAMS( VALIDATE_SI(session) );
+
+	/* Update the msg refcount */
+	CHECK_POSIX( pthread_mutex_lock(&session->stlock) );
+	session->msg_cnt++;
+	CHECK_POSIX( pthread_mutex_unlock(&session->stlock) );
+	
+	return 0;
+}
+
+int fd_sess_reclaim_msg ( struct session ** session )
+{
+	int reclaim;
+	
+	TRACE_ENTRY("%p", session);
+	CHECK_PARAMS( session && VALIDATE_SI(*session) );
+	
+	/* Update the msg refcount */
+	CHECK_POSIX( pthread_mutex_lock(&(*session)->stlock) );
+	reclaim = (*session)->msg_cnt;
+	(*session)->msg_cnt = reclaim - 1;
+	CHECK_POSIX( pthread_mutex_unlock(&(*session)->stlock) );
+	
+	if (reclaim == 1) {
+		CHECK_FCT(fd_sess_reclaim ( session ));
+	} else {
+		*session = NULL;
+	}
+	return 0;
+}
+
+
+
+/* Dump functions */
+void fd_sess_dump(int level, struct session * session)
+{
+	struct fd_list * li;
+	char buf[30];
+	struct tm tm;
+	
+	if (!TRACE_BOOL(level))
+		return;
+	
+	fd_log_debug("\t  %*s -- Session @%p --\n", level, "", session);
+	if (!VALIDATE_SI(session)) {
+		fd_log_debug("\t  %*s  Invalid session object\n", level, "");
+	} else {
+		
+		fd_log_debug("\t  %*s  sid '%s', hash %x\n", level, "", session->sid, session->hash);
+
+		strftime(buf, sizeof(buf), "%D,%T", localtime_r( &session->timeout.tv_sec , &tm ));
+		fd_log_debug("\t  %*s  timeout %s.%09ld\n", level, "", buf, session->timeout.tv_nsec);
+
+		CHECK_POSIX_DO( pthread_mutex_lock(&session->stlock), /* ignore */ );
+		pthread_cleanup_push( fd_cleanup_mutex, &session->stlock );
+		for (li = session->states.next; li != &session->states; li = li->next) {
+			struct state * st = (struct state *)(li->o);
+			fd_log_debug("\t  %*s    handler %d registered data %p\n", level, "", st->hdl->id, st->state);
+		}
+		pthread_cleanup_pop(0);
+		CHECK_POSIX_DO( pthread_mutex_unlock(&session->stlock), /* ignore */ );
+	}
+	fd_log_debug("\t  %*s -- end of session @%p --\n", level, "", session);
+}
+
+void fd_sess_dump_hdl(int level, struct session_handler * handler)
+{
+	if (!TRACE_BOOL(level))
+		return;
+	
+	fd_log_debug("\t  %*s -- Handler @%p --\n", level, "", handler);
+	if (!VALIDATE_SH(handler)) {
+		fd_log_debug("\t  %*s  Invalid session handler object\n", level, "");
+	} else {
+		fd_log_debug("\t  %*s  id %d, cleanup %p, opaque %p\n", level, "", handler->id, handler->cleanup, handler->opaque);
+	}
+	fd_log_debug("\t  %*s -- end of handler @%p --\n", level, "", handler);
+}	
Index: bfreeDiameter/CMakeLists.txt
===================================================================
--- libfreeDiameter/CMakeLists.txt	(revision 320)
+++ 	(revision )
@@ -1,41 +1,0 @@
-# Name of the subproject
-Project("libfreeDiameter" C)
-
-# List of source files for the library
-SET(LFD_SRC
-	libfD.h
-	dictionary.c
-	dispatch.c
-	fifo.c
-	init.c
-	lists.c
-	log.c
-	messages.c
-	rt_data.c
-	sessions.c
-	signal.c
-	)
-
-# Save the list of files for testcases in the daemon's directory
-SET(LFD_SRC ${LFD_SRC} PARENT_SCOPE)
-
-# Build as a shared library
-ADD_LIBRARY(libfreeDiameter SHARED ${LFD_SRC})
-
-# Avoid the liblib name, and set the version
-SET_TARGET_PROPERTIES(libfreeDiameter PROPERTIES 
-	OUTPUT_NAME "freeDiameter"
-	SOVERSION ${FD_PROJECT_VERSION_API}
-	VERSION ${FD_PROJECT_VERSION_MAJOR}.${FD_PROJECT_VERSION_MINOR}.${FD_PROJECT_VERSION_REV})
-
-# The library itself needs other libraries 
-TARGET_LINK_LIBRARIES(libfreeDiameter ${LFD_LIBS})
-
-
-####
-## INSTALL section ##
-
-INSTALL(TARGETS libfreeDiameter
-	LIBRARY DESTINATION ${INSTALL_LIBRARY_SUFFIX}
-	COMPONENT freeDiameter-common)
-
Index: bfreeDiameter/dictionary.c
===================================================================
--- libfreeDiameter/dictionary.c	(revision 602)
+++ 	(revision )
@@ -1,1843 +1,0 @@
-/*********************************************************************************************************
-* Software License Agreement (BSD License)                                                               *
-* Author: Sebastien Decugis <sdecugis@nict.go.jp>							 *
-*													 *
-* Copyright (c) 2010, 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 "libfD.h"
-
-/* Names of the base types */
-const char * type_base_name[] = { /* must keep in sync with dict_avp_basetype */
-	"GROUPED", 	/* AVP_TYPE_GROUPED */
-	"OCTETSTRING", 	/* AVP_TYPE_OCTETSTRING */
-	"INTEGER32", 	/* AVP_TYPE_INTEGER32 */
-	"INTEGER64", 	/* AVP_TYPE_INTEGER64 */
-	"UNSIGNED32", 	/* AVP_TYPE_UNSIGNED32 */
-	"UNSIGNED64", 	/* AVP_TYPE_UNSIGNED64 */
-	"FLOAT32", 	/* AVP_TYPE_FLOAT32 */
-	"FLOAT64"	/* AVP_TYPE_FLOAT64 */
-	};
-
-/* The number of lists in an object */
-#define NB_LISTS_PER_OBJ	3
-
-/* Some eye catchers definitions */
-#define OBJECT_EYECATCHER	(0x0b13c7)
-#define DICT_EYECATCHER		(0x00d1c7)
-
-/* Definition of the dictionary objects */
-struct dict_object {
-	enum dict_object_type	type;	/* What type of object is this? */
-	int			objeyec;/* eyecatcher for this object */
-	int			typeyec;/* eyecatcher for this type of object */
-	struct dictionary	*dico;  /* The dictionary this object belongs to */
-	
-	union {
-		struct dict_vendor_data		vendor;
-		struct dict_application_data	application;
-		struct dict_type_data		type;
-		struct dict_enumval_data	enumval;
-		struct dict_avp_data		avp;
-		struct dict_cmd_data		cmd;
-		struct dict_rule_data		rule;
-	} data;				/* The data of this object */
-	
-	struct dict_object *	parent; /* The parent of this object, if any */
-	
-	struct fd_list		list[NB_LISTS_PER_OBJ];/* used to chain objects.*/
-	/* More information about the lists :
-	
-	 - the use for each list depends on the type of object. See detail bellow.
-	 
-	 - a sentinel for a list has its 'o' field cleared. (this is the criteria to detect end of a loop)
-	 
-	 - The lists are always ordered. The criteria are described bellow. the functions to order them are referenced in dict_obj_info
-	 
-	 - The dict_lock must be held for any list operation.
-	 
-	 => VENDORS:
-	 list[0]: list of the vendors, ordered by their id. The sentinel is g_dict_vendors (vendor with id 0)
-	 list[1]: sentinel for the list of AVPs from this vendor, ordered by AVP code.
-	 list[2]: sentinel for the list of AVPs from this vendor, ordered by AVP name.
-	 
-	 => APPLICATIONS:
-	 list[0]: list of the applications, ordered by their id. The sentinel is g_dict_applications (application with id 0)
-	 list[1]: not used
-	 list[2]: not used.
-	 
-	 => TYPES:
-	 list[0]: list of the types, ordered by their names. The sentinel is g_list_types.
-	 list[1]: sentinel for the type_enum list of this type, ordered by their constant name.
-	 list[2]: sentinel for the type_enum list of this type, ordered by their constant value.
-	 
-	 => TYPE_ENUMS:
-	 list[0]: list of the contants for a given type, ordered by the constant name. Sentinel is a (list[1]) element of a TYPE object.
-	 list[1]: list of the contants for a given type, ordered by the constant value. Sentinel is a (list[2]) element of a TYPE object.
-	 list[2]: not used
-	 
-	 => AVPS:
-	 list[0]: list of the AVP from a given vendor, ordered by avp code. Sentinel is a list[1] element of a VENDOR object.
-	 list[1]: list of the AVP from a given vendor, ordered by avp name. Sentinel is a list[2] element of a VENDOR object.
-	 list[2]: sentinel for the rule list that apply to this AVP.
-	 
-	 => COMMANDS:
-	 list[0]: list of the commands, ordered by their names. The sentinel is g_list_cmd_name.
-	 list[1]: list of the commands, ordered by their command code and 'R' flag. The sentinel is g_list_cmd_code.
-	 list[2]: sentinel for the rule list that apply to this command.
-	 
-	 => RULES:
-	 list[0]: list of the rules for a given (grouped) AVP or Command, ordered by the AVP name to which they refer. sentinel is list[2] of a command or (grouped) avp.
-	 list[1]: not used
-	 list[2]: not used.
-	 
-	 */
-	 
-	 /* Sentinel for the dispatch callbacks */
-	 struct fd_list		disp_cbs;
-	
-};
-
-/* Definition of the dictionary structure */
-struct dictionary {
-	int		 	dict_eyec;		/* Eye-catcher for the dictionary (DICT_EYECATCHER) */
-	
-	pthread_rwlock_t 	dict_lock;		/* The global rwlock for the dictionary */
-	
-	struct dict_object	dict_vendors;		/* Sentinel for the list of vendors, corresponding to vendor 0 */
-	struct dict_object	dict_applications;	/* Sentinel for the list of applications, corresponding to app 0 */
-	struct fd_list		dict_types;		/* Sentinel for the list of types */
-	struct fd_list		dict_cmd_name;		/* Sentinel for the list of commands, ordered by names */
-	struct fd_list		dict_cmd_code;		/* Sentinel for the list of commands, ordered by codes */
-	
-	struct dict_object	dict_cmd_error;		/* Special command object for answers with the 'E' bit set */
-	
-	int			dict_count[DICT_TYPE_MAX + 1]; /* Number of objects of each type */
-};
-
-/* Forward declarations of dump functions */
-static void dump_vendor_data 	  ( void * data );
-static void dump_application_data ( void * data );
-static void dump_type_data 	  ( void * data );
-  /* the dump function for enum has a different prototype since it need the datatype */
-static void dump_avp_data 	  ( void * data );
-static void dump_command_data 	  ( void * data );
-static void dump_rule_data 	  ( void * data );
-
-/* Forward declarations of search functions */
-static int search_vendor 	( struct dictionary * dict, int criteria, void * what, struct dict_object **result );
-static int search_application   ( struct dictionary * dict, int criteria, void * what, struct dict_object **result );
-static int search_type 		( struct dictionary * dict, int criteria, void * what, struct dict_object **result );
-static int search_enumval 	( struct dictionary * dict, int criteria, void * what, struct dict_object **result );
-static int search_avp		( struct dictionary * dict, int criteria, void * what, struct dict_object **result );
-static int search_cmd		( struct dictionary * dict, int criteria, void * what, struct dict_object **result );
-static int search_rule		( struct dictionary * dict, int criteria, void * what, struct dict_object **result );
-
-/* The following array contains lot of data about the different types of objects, for automated handling */
-static struct {
-	enum dict_object_type 	type; 		/* information for this type */
-	char *			name;		/* string describing this object, for debug */
-	size_t			datasize;	/* The size of the data structure */
-	int			parent;		/* 0: never; 1: may; 2: must */
-	enum dict_object_type	parenttype;	/* The type of the parent, when relevant */
-	int			eyecatcher;	/* A kind of signature for this object */
-	void 		      (*dump_data)(void * data );	/* The function to dump the data section */
-	int 		      (*search_fct)(struct dictionary * dict, int criteria, void * what, struct dict_object **result );;	/* The function to search an object of this type */
-	int			haslist[NB_LISTS_PER_OBJ];	/* Tell if this list is used */
-} dict_obj_info[] = { { 0, "(error)", 0, 0, 0, 0, NULL, NULL, {0, 0, 0} }
-
-	/* type			 name		datasize		   	  parent  	parenttype 
-			eyecatcher		dump_data	  	search_fct,		haslist[] 	*/
-
-	,{ DICT_VENDOR,		"VENDOR",	sizeof(struct dict_vendor_data),	0, 	0,
-			OBJECT_EYECATCHER + 1, 	dump_vendor_data, 	search_vendor, 		{ 1, 0, 0 } }
-	
-	,{ DICT_APPLICATION,	"APPLICATION",	sizeof(struct dict_application_data),	1, 	DICT_VENDOR,
-			OBJECT_EYECATCHER + 2,	dump_application_data,	search_application,	{ 1, 0, 0 } }
-	
-	,{ DICT_TYPE,		"TYPE",		sizeof(struct dict_type_data),		1, 	DICT_APPLICATION,
-			OBJECT_EYECATCHER + 3,	dump_type_data,		search_type,		{ 1, 0, 0 } }
-	
-	,{ DICT_ENUMVAL,	"ENUMVAL",	sizeof(struct dict_enumval_data),	2, 	DICT_TYPE,
-			OBJECT_EYECATCHER + 4,	NULL,			search_enumval,	{ 1, 1, 0 } }
-	
-	,{ DICT_AVP,		"AVP",		sizeof(struct dict_avp_data),		1, 	DICT_TYPE,
-			OBJECT_EYECATCHER + 5,	dump_avp_data,		search_avp,		{ 1, 1,	0 } }
-	
-	,{ DICT_COMMAND,	"COMMAND",	sizeof(struct dict_cmd_data),		1, 	DICT_APPLICATION,
-			OBJECT_EYECATCHER + 6,	dump_command_data,	search_cmd,		{ 1, 1, 0 } }
-	
-	,{ DICT_RULE,		"RULE",		sizeof(struct dict_rule_data),		2, 	-1 /* special case: grouped avp or command */,
-			OBJECT_EYECATCHER + 7,	dump_rule_data,		search_rule,		{ 1, 0, 0 } }
-	
-};
-	
-/* Macro to verify a "type" value */
-#define CHECK_TYPE( type ) ( ((type) > 0) && ((type) <= DICT_TYPE_MAX) )
-
-/* Cast macro */
-#define _O( object ) ((struct dict_object *) (object))
-
-/* Get information line for a given object */
-#define _OBINFO(object) (dict_obj_info[CHECK_TYPE(_O(object)->type) ? _O(object)->type : 0])
-
-
-
-
-/*******************************************************************************************************/
-/*******************************************************************************************************/
-/*                                                                                                     */
-/*                                  Objects management                                                 */
-/*                                                                                                     */
-/*******************************************************************************************************/
-/*******************************************************************************************************/
-
-/* Functions to manage the objects creation and destruction. */
-
-/* Duplicate a string inplace */
-#define DUP_string( str ) {				\
-	char * __str = (str);				\
-	CHECK_MALLOC( (str) = strdup(__str) );		\
-}
-	
-/* Initialize an object */
-static void init_object( struct dict_object * obj, enum dict_object_type type )
-{
-	int i;
-	
-	TRACE_ENTRY("%p %d", obj, type);
-	
-	/* Clean the object first */
-	memset ( obj, 0, sizeof(struct dict_object));
-	
-	CHECK_PARAMS_DO(  CHECK_TYPE(type),  return  );
-
-	obj->type = type;
-	obj->objeyec = OBJECT_EYECATCHER;
-	obj->typeyec = _OBINFO(obj).eyecatcher;
-
-	/* We don't initialize the data nor the parent here */
-	
-	/* Now init the lists */
-	for (i=0; i<NB_LISTS_PER_OBJ; i++) {
-		if (_OBINFO(obj).haslist[i] != 0) 
-			fd_list_init(&obj->list[i], obj);
-		else
-			fd_list_init(&obj->list[i], NULL);
-	}
-	
-	fd_list_init(&obj->disp_cbs, NULL);
-}
-
-/* Initialize the "data" part of an object */
-static int init_object_data(void * dest, void * source, enum dict_object_type type)
-{
-	TRACE_ENTRY("%p %p %d", dest, source, type);
-	CHECK_PARAMS( dest && source && CHECK_TYPE(type) );
-	
-	/* Generic: copy the full data structure */	
-	memcpy( dest, source, dict_obj_info[type].datasize );
-	
-	/* Then strings must be duplicated, not copied */
-	/* This function might be simplified by always defining the "name" field as the first field of the structures, but... it's error-prone */
-	switch (type) {
-		case DICT_VENDOR:
-			DUP_string( ((struct dict_vendor_data *)dest)->vendor_name );
-			break;
-		
-		case DICT_APPLICATION:
-			DUP_string( ((struct dict_application_data *)dest)->application_name );
-			break;
-			
-		case DICT_TYPE:
-			DUP_string( ((struct dict_type_data *)dest)->type_name );
-			break;
-			
-		case DICT_ENUMVAL:
-			DUP_string( ((struct dict_enumval_data *)dest)->enum_name );
-			break;
-
-		case DICT_AVP:
-			DUP_string( ((struct dict_avp_data *)dest)->avp_name );
-			break;
-			
-		case DICT_COMMAND:
-			DUP_string( ((struct dict_cmd_data *)dest)->cmd_name );
-			break;
-		
-		default:
-			/* Nothing to do for RULES */
-			;
-	}
-	
-	return 0;
-}
-
-/* Check that an object is valid (1: OK, 0: error) */
-static int verify_object( struct dict_object * obj )
-{
-	TRACE_ENTRY("%p", obj);
-	
-	CHECK_PARAMS_DO(  obj
-			&& (obj->objeyec == OBJECT_EYECATCHER)
-			&& CHECK_TYPE(obj->type)
-			&& (obj->typeyec == dict_obj_info[obj->type].eyecatcher),
-		{
-			if (obj) {
-				TRACE_DEBUG(FULL, "Invalid object : %p\n"
-						  "     obj->objeyec : %x / %x\n"
-						  "     obj->type    : %d\n"
-						  "     obj->objeyec : %x / %x\n"
-						  "     obj->typeyec : %x / %x", 
-						obj,
-						obj->objeyec, OBJECT_EYECATCHER,
-						obj->type,
-						obj->objeyec, OBJECT_EYECATCHER,
-						obj->typeyec, _OBINFO(obj).eyecatcher);
-			}
-			return 0;
-		}  );
-	
-	/* The object is probably valid. */
-	return 1;
-}
-
-/* Free the data associated to an object */
-static void destroy_object_data(struct dict_object * obj)
-{
-	/* TRACE_ENTRY("%p", obj); */
-	
-	switch (obj->type) {
-		case DICT_VENDOR:
-			free( obj->data.vendor.vendor_name );
-			break;
-		
-		case DICT_APPLICATION:
-			free( obj->data.application.application_name );
-			break;
-			
-		case DICT_TYPE:
-			free( obj->data.type.type_name );
-			break;
-			
-		case DICT_ENUMVAL:
-			free( obj->data.enumval.enum_name );
-			break;
-
-		case DICT_AVP:
-			free( obj->data.avp.avp_name );
-			break;
-			
-		case DICT_COMMAND:
-			free( obj->data.cmd.cmd_name );
-			break;
-		
-		default:
-			/* nothing to do */
-			;
-	}
-}
-
-/* Forward declaration */
-static void destroy_object(struct dict_object * obj);
-
-/* Destroy all objects in a list - the lock must be held */
-static void destroy_list(struct fd_list * head) 
-{
-	/* TRACE_ENTRY("%p", head); */
-	
-	/* loop in the list */
-	while (!FD_IS_LIST_EMPTY(head))
-	{
-		/* When destroying the object, it is unlinked from the list */
-		destroy_object(_O(head->next->o));
-	}
-}
-	
-/* Free an object and its sublists */
-static void destroy_object(struct dict_object * obj)
-{
-	int i;
-	
-	/* TRACE_ENTRY("%p", obj); */
-	
-	/* Update global count */
-	if (obj->dico) 
-		obj->dico->dict_count[obj->type]--;
-	
-	/* Mark the object as invalid */
-	obj->objeyec = 0xdead;
-	
-	/* First, destroy the data associated to the object */
-	destroy_object_data(obj);
-	
-	for (i=0; i<NB_LISTS_PER_OBJ; i++) {
-		if (_OBINFO(obj).haslist[i])
-			/* unlink the element from the list */
-			fd_list_unlink( &obj->list[i] );
-		else
-			/* This is either a sentinel or unused (=emtpy) list, let's destroy it */
-			destroy_list( &obj->list[i] );
-	}
-	
-	/* Unlink all elements from the dispatch list; they will be freed when callback is unregistered */
-	CHECK_POSIX_DO( pthread_rwlock_wrlock(&fd_disp_lock), /* continue */ );
-	while (!FD_IS_LIST_EMPTY(&obj->disp_cbs)) {
-		fd_list_unlink( obj->disp_cbs.next );
-	}
-	CHECK_POSIX_DO( pthread_rwlock_unlock(&fd_disp_lock), /* continue */ );
-	
-	/* Last, destroy the object */
-	free(obj);
-}
-
-/*******************************************************************************************************/
-/*******************************************************************************************************/
-/*                                                                                                     */
-/*                                  Compare functions                                                  */
-/*                                                                                                     */
-/*******************************************************************************************************/
-/*******************************************************************************************************/
-
-/* Compare two values */
-#define ORDER_scalar( i1, i2 ) \
-	((i1 < i2 ) ? -1 : ( i1 > i2 ? 1 : 0 )) 
-
-
-/* Compare two vendor objects by their id (checks already performed) */
-static int order_vendor_by_id ( struct dict_object *o1, struct dict_object *o2 )
-{
-	TRACE_ENTRY("%p %p", o1, o2);
-	
-	return ORDER_scalar( o1->data.vendor.vendor_id, o2->data.vendor.vendor_id );
-}
-
-/* Compare two application objects by their id (checks already performed) */
-static int order_appli_by_id  ( struct dict_object *o1, struct dict_object *o2 )
-{
-	TRACE_ENTRY("%p %p", o1, o2);
-	
-	return ORDER_scalar( o1->data.application.application_id, o2->data.application.application_id );
-}
-
-/* Compare two type objects by their name (checks already performed) */
-static int order_type_by_name ( struct dict_object *o1, struct dict_object *o2 )
-{
-	TRACE_ENTRY("%p %p", o1, o2);
-	
-	return strcmp( o1->data.type.type_name, o2->data.type.type_name );
-}
-
-/* Compare two type_enum objects by their names (checks already performed) */
-static int order_enum_by_name ( struct dict_object *o1, struct dict_object *o2 )
-{
-	TRACE_ENTRY("%p %p", o1, o2);
-	
-	return strcmp( o1->data.enumval.enum_name, o2->data.enumval.enum_name );
-}
-
-/* Compare two type_enum objects by their values (checks already performed) */
-static int order_enum_by_val  ( struct dict_object *o1, struct dict_object *o2 )
-{
-	size_t oslen;
-	int cmp = 0;
-	
-	TRACE_ENTRY("%p %p", o1, o2);
-	
-	/* The comparison function depends on the type of data */
-	switch ( o1->parent->data.type.type_base ) {
-		case AVP_TYPE_OCTETSTRING:
-			oslen = o1->data.enumval.enum_value.os.len;
-			if (o2->data.enumval.enum_value.os.len < oslen)
-				oslen = o2->data.enumval.enum_value.os.len;
-			cmp = memcmp(o1->data.enumval.enum_value.os.data, o2->data.enumval.enum_value.os.data, oslen );
-			return (cmp ? cmp : ORDER_scalar(o1->data.enumval.enum_value.os.len,o2->data.enumval.enum_value.os.len));
-		
-		case AVP_TYPE_INTEGER32:
-			return ORDER_scalar( o1->data.enumval.enum_value.i32, o2->data.enumval.enum_value.i32 );
-
-		case AVP_TYPE_INTEGER64:
-			return ORDER_scalar( o1->data.enumval.enum_value.i64, o2->data.enumval.enum_value.i64 );
-
-		case AVP_TYPE_UNSIGNED32:
-			return ORDER_scalar( o1->data.enumval.enum_value.u32, o2->data.enumval.enum_value.u32 );
-
-		case AVP_TYPE_UNSIGNED64:
-			return ORDER_scalar( o1->data.enumval.enum_value.u64, o2->data.enumval.enum_value.u64 );
-
-		case AVP_TYPE_FLOAT32:
-			return ORDER_scalar( o1->data.enumval.enum_value.f32, o2->data.enumval.enum_value.f32 );
-
-		case AVP_TYPE_FLOAT64:
-			return ORDER_scalar( o1->data.enumval.enum_value.f64, o2->data.enumval.enum_value.f64 );
-
-		case AVP_TYPE_GROUPED:
-		default:
-			ASSERT(0);
-	}
-	return 0;
-}
-
-/* Compare two avp objects by their codes (checks already performed) */
-static int order_avp_by_code  ( struct dict_object *o1, struct dict_object *o2 )
-{
-	TRACE_ENTRY("%p %p", o1, o2);
-	
-	return ORDER_scalar( o1->data.avp.avp_code, o2->data.avp.avp_code );
-}
-
-/* Compare two avp objects by their names (checks already performed) */
-static int order_avp_by_name  ( struct dict_object *o1, struct dict_object *o2 )
-{
-	TRACE_ENTRY("%p %p", o1, o2);
-	
-	return strcmp( o1->data.avp.avp_name, o2->data.avp.avp_name );
-}
-
-/* Compare two command objects by their names (checks already performed) */
-static int order_cmd_by_name  ( struct dict_object *o1, struct dict_object *o2 )
-{
-	TRACE_ENTRY("%p %p", o1, o2);
-	
-	return strcmp( o1->data.cmd.cmd_name, o2->data.cmd.cmd_name );
-}
-
-/* Compare two command objects by their codes and flags (request or answer) (checks already performed) */
-static int order_cmd_by_codefl( struct dict_object *o1, struct dict_object *o2 )
-{
-	uint8_t fl1, fl2;
-	int cmp = 0;
-	
-	TRACE_ENTRY("%p %p", o1, o2);
-	
-	cmp = ORDER_scalar( o1->data.cmd.cmd_code, o2->data.cmd.cmd_code );
-	if (cmp) 
-		return cmp;
-	
-	/* Same command code, we must compare the value of the 'R' flag */
-	fl1 = o1->data.cmd.cmd_flag_val & CMD_FLAG_REQUEST;
-	fl2 = o2->data.cmd.cmd_flag_val & CMD_FLAG_REQUEST;
-	
-	/* We want requests first, so we reverse the operators here */
-	return ORDER_scalar(fl2, fl1);
-		
-}
-
-/* Compare two rule object by the AVP name that they refer (checks already performed) */
-static int order_rule_by_avpn ( struct dict_object *o1, struct dict_object *o2 )
-{
-	TRACE_ENTRY("%p %p", o1, o2);
-	
-	return strcmp( o1->data.rule.rule_avp->data.avp.avp_name, o2->data.rule.rule_avp->data.avp.avp_name );
-}
-
-/*******************************************************************************************************/
-/*******************************************************************************************************/
-/*                                                                                                     */
-/*                                  Search  functions                                                  */
-/*                                                                                                     */
-/*******************************************************************************************************/
-/*******************************************************************************************************/
-
-/* Functions used to search for objects in the lists, according to some criteria */
-
-/* On a general note, if result is not NULL, ENOENT is not returned but *result is NULL. */
-
-/* The following macros assume that "what", "ret", "result" (variables), and "end" (label) exist
-in the local context where they are called. They are meant to be called only from the functions that follow. */
-
-/* For searchs of type "xxx_OF_xxx": children's parent or default parent */
-#define SEARCH_childs_parent( type_of_child, default_parent ) {			\
-	struct dict_object *__child = (struct dict_object *) what;		\
-	CHECK_PARAMS_DO( verify_object(__child) && 				\
-		(__child->type == (type_of_child)), 				\
-		   {  ret = EINVAL; goto end;  }  );				\
-	ret = 0;								\
-	if (result)								\
-		*result = (__child->parent ? __child->parent :(default_parent));\
-}
-
-/* For search of strings in lists. isindex= 1 if the string is the ordering key of the list */
-#define SEARCH_string( str, sentinel, datafield, isindex ) {			\
-	char * __str = (char *) str;						\
-	int __cmp;								\
-	struct fd_list * __li;							\
-	ret = 0;								\
-	for  (__li = (sentinel)->next; __li != (sentinel); __li = __li->next) {	\
-		__cmp = strcmp(__str, _O(__li->o)->data. datafield );		\
-		if (__cmp == 0) {						\
-			if (result)						\
-				*result = _O(__li->o);				\
-			goto end;						\
-		}								\
-		if ((isindex) && (__cmp < 0))					\
-			break;							\
-	}									\
-	if (result)								\
-		*result = NULL;							\
-	else									\
-		ret = ENOENT;							\
-}
-
-/* For search of octetstrings in lists (not \0 terminated). */
-#define SEARCH_ocstring( ostr, length, sentinel, osdatafield, isindex ) {	\
-	unsigned char * __ostr = (unsigned char *) ostr;			\
-	int __cmp;								\
-	size_t __len;								\
-	struct fd_list * __li;							\
-	ret = 0;								\
-	for  (__li = (sentinel); __li->next != (sentinel); __li = __li->next) {	\
-		__len = _O(__li->next->o)->data. osdatafield .len;		\
-		if ( __len > (length) )						\
-			__len = (length);					\
-		__cmp = memcmp(__ostr, 						\
-				_O(__li->next->o)->data. osdatafield .data, 	\
-				__len);						\
-		if (! __cmp) {							\
-			__cmp = ORDER_scalar( length,				\
-				_O(__li->next->o)->data. osdatafield .len); 	\
-		}								\
-		if (__cmp == 0) {						\
-			if (result)						\
-				*result = _O(__li->next->o);			\
-			goto end;						\
-		}								\
-		if ((isindex) && (__cmp < 0))					\
-			break;							\
-	}									\
-	if (result)								\
-		*result = NULL;							\
-	else									\
-		ret = ENOENT;							\
-}
-
-/* For search of AVP name in rule lists. */
-#define SEARCH_ruleavpname( str, sentinel ) {					\
-	char * __str = (char *) str;						\
-	int __cmp;								\
-	struct fd_list * __li;							\
-	ret = 0;								\
-	for  (__li = (sentinel); __li->next != (sentinel); __li = __li->next) {	\
-		__cmp = strcmp(__str, 						\
-		  _O(__li->next->o)->data.rule.rule_avp->data.avp.avp_name);\
-		if (__cmp == 0) {						\
-			if (result)						\
-				*result = _O(__li->next->o);			\
-			goto end;						\
-		}								\
-		if (__cmp < 0)							\
-			break;							\
-	}									\
-	if (result)								\
-		*result = NULL;							\
-	else									\
-		ret = ENOENT;							\
-}
-
-/* For search of scalars in lists. isindex= 1 if the value is the ordering key of the list */
-#define SEARCH_scalar( value, sentinel, datafield, isindex, defaultobj ) {	\
-	int __cmp;								\
-	struct fd_list * __li;							\
-	ret = 0;								\
-	if (  ((defaultobj) != NULL) 						\
-		   && (_O(defaultobj)->data. datafield  == value)) {		\
-		if (result)							\
-			*result = _O(defaultobj);				\
-		goto end;							\
-	}									\
-	for  (__li = (sentinel); __li->next != (sentinel); __li = __li->next) {	\
-		__cmp= ORDER_scalar(value, _O(__li->next->o)->data. datafield );\
-		if (__cmp == 0) {						\
-			if (result)						\
-				*result = _O(__li->next->o);			\
-			goto end;						\
-		}								\
-		if ((isindex) && (__cmp < 0))					\
-			break;							\
-	}									\
-	if (result)								\
-		*result = NULL;							\
-	else									\
-		ret = ENOENT;							\
-}
-
-/* For search of commands in lists by code and flag. R_flag_val = 0 or CMD_FLAG_REQUEST */
-#define SEARCH_codefl( value, R_flag_val, sentinel) {					\
-	int __cmp;								\
-	struct fd_list * __li;							\
-	ret = 0;								\
-	for  (	  __li = (sentinel);	 					\
-		  __li->next != (sentinel); 					\
-		  __li = __li->next) {						\
-		__cmp = ORDER_scalar(value, 					\
-				_O(__li->next->o)->data.cmd.cmd_code );		\
-		if (__cmp == 0) {						\
-			uint8_t __mask, __val;					\
-			__mask = _O(__li->next->o)->data.cmd.cmd_flag_mask;	\
-			__val  = _O(__li->next->o)->data.cmd.cmd_flag_val;	\
-			if ( ! (__mask & CMD_FLAG_REQUEST) )			\
-				continue;					\
-			if ( ( __val & CMD_FLAG_REQUEST ) != R_flag_val )	\
-				continue;					\
-			if (result)						\
-				*result = _O(__li->next->o);			\
-			goto end;						\
-		}								\
-		if (__cmp < 0)							\
-			break;							\
-	}									\
-	if (result)								\
-		*result = NULL;							\
-	else									\
-		ret = ENOENT;							\
-}
-
-static int search_vendor ( struct dictionary * dict, int criteria, void * what, struct dict_object **result )
-{
-	int ret = 0;
-	vendor_id_t id;
-	
-	TRACE_ENTRY("%p %d %p %p", dict, criteria, what, result);
-	
-	switch (criteria) {
-		case VENDOR_BY_ID:
-			id = *(vendor_id_t *) what;
-			SEARCH_scalar( id, &dict->dict_vendors.list[0], vendor.vendor_id, 1, &dict->dict_vendors );
-			break;
-				
-		case VENDOR_BY_NAME:
-			/* "what" is a vendor name */
-			SEARCH_string( what, &dict->dict_vendors.list[0], vendor.vendor_name, 0);
-			break;
-			
-		case VENDOR_OF_APPLICATION:
-			/* "what" should be an application object */
-			SEARCH_childs_parent( DICT_APPLICATION, &dict->dict_vendors );
-			break;
-		
-		default:
-			/* Invalid criteria */
-			CHECK_PARAMS( criteria = 0 );
-	}
-end:
-	return ret;
-}
-
-static int search_application ( struct dictionary * dict, int criteria, void * what, struct dict_object **result )
-{
-	int ret = 0;
-	application_id_t id;
-	
-	TRACE_ENTRY("%p %d %p %p", dict, criteria, what, result);
-	
-	switch (criteria) {
-		case APPLICATION_BY_ID:
-			id = *(application_id_t *) what;
-
-			SEARCH_scalar( id, &dict->dict_applications.list[0],  application.application_id, 1, &dict->dict_applications );
-			break;
-				
-		case APPLICATION_BY_NAME:
-			/* "what" is an application name */
-			SEARCH_string( what, &dict->dict_applications.list[0], application.application_name, 0);
-			break;
-			
-		case APPLICATION_OF_TYPE:
-			/* "what" should be a type object */
-			SEARCH_childs_parent( DICT_TYPE, &dict->dict_applications );
-			break;
-		
-		case APPLICATION_OF_COMMAND:
-			/* "what" should be a command object */
-			SEARCH_childs_parent( DICT_COMMAND, &dict->dict_applications );
-			break;
-		
-		default:
-			/* Invalid criteria */
-			CHECK_PARAMS( criteria = 0 );
-	}
-end:
-	return ret;
-}
-
-static int search_type ( struct dictionary * dict, int criteria, void * what, struct dict_object **result )
-{
-	int ret = 0;
-	
-	TRACE_ENTRY("%p %d %p %p", dict, criteria, what, result);
-	
-	switch (criteria) {
-		case TYPE_BY_NAME:
-			/* "what" is a type name */
-			SEARCH_string( what, &dict->dict_types, type.type_name, 1);
-			break;
-			
-		case TYPE_OF_ENUMVAL:
-			/* "what" should be a type_enum object */
-			SEARCH_childs_parent( DICT_ENUMVAL, NULL );
-			break;
-		
-		case TYPE_OF_AVP:
-			/* "what" should be an avp object */
-			SEARCH_childs_parent( DICT_AVP, NULL );
-			break;
-		
-				
-		default:
-			/* Invalid criteria */
-			CHECK_PARAMS( criteria = 0 );
-	}
-end:
-	return ret;
-}
-
-static int search_enumval ( struct dictionary * dict, int criteria, void * what, struct dict_object **result )
-{
-	int ret = 0;
-	
-	TRACE_ENTRY("%p %d %p %p", dict, criteria, what, result);
-	
-	switch (criteria) {
-		case ENUMVAL_BY_STRUCT:
-			{
-				struct dict_object * parent = NULL;
-				struct dict_enumval_request * _what = (struct dict_enumval_request *) what;
-				
-				CHECK_PARAMS(  _what  &&  ( _what->type_obj || _what->type_name )  );
-				
-				if (_what->type_obj != NULL) {
-					parent = _what->type_obj;
-					CHECK_PARAMS(  verify_object(parent)  &&  (parent->type == DICT_TYPE)  );
-				} else {
-					/* We received only the type name, we must find it first */
-					CHECK_FCT_DO( search_type( dict, TYPE_BY_NAME, _what->type_name, &parent ),
-							CHECK_PARAMS( 0 ) );
-				}
-				
-				/* From here the "parent" object is valid */
-				
-				if ( _what->search.enum_name != NULL ) {
-					/* We are looking for this string */
-					SEARCH_string(  _what->search.enum_name, &parent->list[1], enumval.enum_name, 1 );
-				} else {
-					/* We are looking for the value in enum_value */
-					switch (parent->data.type.type_base) {
-						case AVP_TYPE_OCTETSTRING:
-							SEARCH_ocstring( _what->search.enum_value.os.data, 
-									 _what->search.enum_value.os.len, 
-									 &parent->list[2], 
-									 enumval.enum_value.os , 
-									 1 );
-							break;
-
-						case AVP_TYPE_INTEGER32:
-							SEARCH_scalar(	_what->search.enum_value.i32,
-									&parent->list[2],
-									enumval.enum_value.i32,
-									1,
-									(struct dict_object *)NULL);
-							break;
-							
-						case AVP_TYPE_INTEGER64:
-							SEARCH_scalar(	_what->search.enum_value.i64,
-									&parent->list[2],
-									enumval.enum_value.i64,
-									1,
-									(struct dict_object *)NULL);
-							break;
-							
-						case AVP_TYPE_UNSIGNED32:
-							SEARCH_scalar(	_what->search.enum_value.u32,
-									&parent->list[2],
-									enumval.enum_value.u32,
-									1,
-									(struct dict_object *)NULL);
-							break;
-							
-						case AVP_TYPE_UNSIGNED64:
-							SEARCH_scalar(	_what->search.enum_value.u64,
-									&parent->list[2],
-									enumval.enum_value.u64,
-									1,
-									(struct dict_object *)NULL);
-							break;
-							
-						case AVP_TYPE_FLOAT32:
-							SEARCH_scalar(	_what->search.enum_value.f32,
-									&parent->list[2],
-									enumval.enum_value.f32,
-									1,
-									(struct dict_object *)NULL);
-							break;
-							
-						case AVP_TYPE_FLOAT64:
-							SEARCH_scalar(	_what->search.enum_value.f64,
-									&parent->list[2],
-									enumval.enum_value.f64,
-									1,
-									(struct dict_object *)NULL);
-							break;
-							
-						default:
-							/* Invalid parent type basetype */
-							CHECK_PARAMS( parent = NULL );
-					}
-				}
-				
-			}
-			break;
-		
-				
-		default:
-			/* Invalid criteria */
-			CHECK_PARAMS( criteria = 0 );
-	}
-end:
-	return ret;
-}
-
-static int search_avp ( struct dictionary * dict, int criteria, void * what, struct dict_object **result )
-{
-	int ret = 0;
-	
-	TRACE_ENTRY("%p %d %p %p", dict, criteria, what, result);
-	
-	switch (criteria) {
-		case AVP_BY_CODE:
-			{
-				avp_code_t code;
-				code = *(avp_code_t *) what;
-
-				SEARCH_scalar( code, &dict->dict_vendors.list[1],  avp.avp_code, 1, (struct dict_object *)NULL );
-			}
-			break;
-				
-		case AVP_BY_NAME:
-			/* "what" is the AVP name, vendor 0 */
-			SEARCH_string( what, &dict->dict_vendors.list[2], avp.avp_name, 1);
-			break;
-			
-		case AVP_BY_CODE_AND_VENDOR:
-		case AVP_BY_NAME_AND_VENDOR:
-			{
-				struct dict_avp_request * _what = (struct dict_avp_request *) what;
-				struct dict_object * vendor = NULL;
-				
-				CHECK_PARAMS( (criteria != AVP_BY_NAME_AND_VENDOR) || _what->avp_name  );
-				
-				/* Now look for the vendor first */
-				CHECK_FCT( search_vendor( dict, VENDOR_BY_ID, &_what->avp_vendor, &vendor ) );
-				if (vendor == NULL) {
-					if (result)
-						*result = NULL;
-					else
-						ret = ENOENT;
-					goto end;
-				}
-				
-				/* We now have our vendor = head of the appropriate avp list */
-				if (criteria == AVP_BY_NAME_AND_VENDOR) {
-					SEARCH_string( _what->avp_name, &vendor->list[2], avp.avp_name, 1);
-				} else {
-					/* AVP_BY_CODE_AND_VENDOR */
-					SEARCH_scalar( _what->avp_code, &vendor->list[1],  avp.avp_code, 1, (struct dict_object *)NULL );
-				}
-			}
-			break;
-		
-		case AVP_BY_NAME_ALL_VENDORS:
-			{
-				struct fd_list * li;
-				
-				/* First, search for vendor 0 */
-				SEARCH_string( what, &dict->dict_vendors.list[2], avp.avp_name, 1);
-				
-				/* If not found, loop for all vendors, until found */
-				for (li = dict->dict_vendors.list[0].next; li != &dict->dict_vendors.list[0]; li = li->next) {
-					SEARCH_string( what, &_O(li->o)->list[2], avp.avp_name, 1);
-				}
-			}
-			break;
-		
-		default:
-			/* Invalid criteria */
-			CHECK_PARAMS( criteria = 0 );
-	}
-end:
-	return ret;
-}
-
-static int search_cmd ( struct dictionary * dict, int criteria, void * what, struct dict_object **result )
-{
-	int ret = 0;
-	
-	TRACE_ENTRY("%p %d %p %p", dict, criteria, what, result);
-	
-	switch (criteria) {
-		case CMD_BY_NAME:
-			/* "what" is a command name */
-			SEARCH_string( what, &dict->dict_cmd_name, cmd.cmd_name, 1);
-			break;
-			
-		case CMD_BY_CODE_R:
-		case CMD_BY_CODE_A:
-			{
-				command_code_t code;
-				uint8_t searchfl = 0;
-				
-				/* The command code that we are searching */
-				code = *(command_code_t *) what;
-				
-				/* The flag (request or answer) of the command we are searching */
-				if (criteria == CMD_BY_CODE_R) {
-					searchfl = CMD_FLAG_REQUEST;
-				}
-				
-				/* perform the search */
-				SEARCH_codefl( code, searchfl, &dict->dict_cmd_code );
-			}
-			break;
-				
-		case CMD_ANSWER:
-			{
-				/* "what" is a command object of type "request" */
-				struct dict_object * req = (struct dict_object *) what;
-				struct dict_object * ans = NULL;
-				
-				CHECK_PARAMS( verify_object(req) 
-						&& (req->type == DICT_COMMAND)
-						&& (req->data.cmd.cmd_flag_mask & CMD_FLAG_REQUEST)
-						&& (req->data.cmd.cmd_flag_val  & CMD_FLAG_REQUEST) );
-				
-				/* The answer is supposed to be the next element in the list, if it exists */
-				ans = req->list[1].next->o;
-				if ( ans == NULL ) {
-					TRACE_DEBUG( FULL, "the request was the last element in the list" );
-					ret = ENOENT;
-					goto end;
-				}
-				
-				/* Now check that the ans element is really the correct one */
-				if (  (ans->data.cmd.cmd_code != req->data.cmd.cmd_code)
-				   || (!(ans->data.cmd.cmd_flag_mask & CMD_FLAG_REQUEST))
-				   || (  ans->data.cmd.cmd_flag_val  & CMD_FLAG_REQUEST ) ) {
-					TRACE_DEBUG( FULL, "the answer does not follow the request in the list" );
-					ret = ENOENT;
-					goto end;
-				}
-				
-				if (result)
-					*result = ans;
-				ret = 0;
-			}						
-			break;
-			
-		default:
-			/* Invalid criteria */
-			CHECK_PARAMS( criteria = 0 );
-	}
-end:
-	return ret;
-}
-
-static int search_rule ( struct dictionary * dict, int criteria, void * what, struct dict_object **result )
-{
-	int ret = 0;
-	
-	TRACE_ENTRY("%p %d %p %p", dict, criteria, what, result);
-	
-	switch (criteria) {
-		case RULE_BY_AVP_AND_PARENT:
-			{
-				struct dict_object * parent = NULL;
-				struct dict_object * avp = NULL;
-				struct dict_rule_request * _what = (struct dict_rule_request *) what;
-				
-				CHECK_PARAMS( _what 
-						&& (parent = _what->rule_parent)
-						&& (avp    = _what->rule_avp   ) );
-				
-				CHECK_PARAMS( verify_object(parent) 
-						&& ((parent->type == DICT_COMMAND) 
-						 || ((parent->type == DICT_AVP) && (parent->data.avp.avp_basetype == AVP_TYPE_GROUPED))) );
-				
-				CHECK_PARAMS( verify_object(avp) && (avp->type == DICT_AVP) );
-				
-				/* Perform the search */
-				SEARCH_ruleavpname( avp->data.avp.avp_name, &parent->list[2]);
-				
-			}
-			break;
-			
-		default:
-			/* Invalid criteria */
-			CHECK_PARAMS( criteria = 0 );
-	}
-end:
-	return ret;
-}
-
-/*******************************************************************************************************/
-/*******************************************************************************************************/
-/*                                                                                                     */
-/*                                  Dump / debug functions                                             */
-/*                                                                                                     */
-/*******************************************************************************************************/
-/*******************************************************************************************************/
-/* The following functions are used to debug the module, and allow to print out the content of the dictionary */
-static void dump_vendor_data ( void * data )
-{
-	struct dict_vendor_data * vendor = (struct dict_vendor_data *)data;
-	
-	fd_log_debug("data: %-6u \"%s\"", vendor->vendor_id, vendor->vendor_name);
-}
-static void dump_application_data ( void * data )
-{
-	struct dict_application_data * appli = (struct dict_application_data *) data;
-	fd_log_debug("data: %-6u \"%s\"", appli->application_id, appli->application_name);
-}
-static void dump_type_data ( void * data )
-{
-	struct dict_type_data * type = ( struct dict_type_data * ) data;
-	
-	fd_log_debug("data: %-12s \"%s\"", 
-			type_base_name[type->type_base], 
-			type->type_name);
-}
-static void dump_enumval_data ( struct dict_enumval_data * enumval, enum dict_avp_basetype type )
-{
-	const int LEN_MAX = 20;
-	fd_log_debug("data: (%-12s) \"%s\" -> ", type_base_name[type], enumval->enum_name);
-	switch (type) {
-		case AVP_TYPE_OCTETSTRING:
-			{
-				int i, n=LEN_MAX;
-				if (enumval->enum_value.os.len < LEN_MAX)
-					n = enumval->enum_value.os.len;
-				for (i=0; i < n; i++)
-					fd_log_debug("0x%02.2X/'%c' ", enumval->enum_value.os.data[i], ASCII(enumval->enum_value.os.data[i]));
-				if (n == LEN_MAX)
-					fd_log_debug("...");
-			}
-			break;
-		
-		case AVP_TYPE_INTEGER32:
-			fd_log_debug("%i", enumval->enum_value.i32);
-			break;
-
-		case AVP_TYPE_INTEGER64:
-			fd_log_debug("%lli", enumval->enum_value.i64);
-			break;
-
-		case AVP_TYPE_UNSIGNED32:
-			fd_log_debug("%u", enumval->enum_value.u32);
-			break;
-
-		case AVP_TYPE_UNSIGNED64:
-			fd_log_debug("%llu", enumval->enum_value.u64);
-			break;
-
-		case AVP_TYPE_FLOAT32:
-			fd_log_debug("%f", enumval->enum_value.f32);
-			break;
-
-		case AVP_TYPE_FLOAT64:
-			fd_log_debug("%g", enumval->enum_value.f64);
-			break;
-		
-		default:
-			fd_log_debug("??? (ERROR unknown type %d)", type);
-	}
-}
-static void dump_avp_data ( void * data )
-{
-	struct dict_avp_data * avp = (struct dict_avp_data * ) data;
-	fd_log_debug("data: v/m:" DUMP_AVPFL_str "/" DUMP_AVPFL_str ", %12s, %-6u \"%s\"", 
-			DUMP_AVPFL_val(avp->avp_flag_val), 
-			DUMP_AVPFL_val(avp->avp_flag_mask), 
-			type_base_name[avp->avp_basetype], 
-			avp->avp_code, 
-			avp->avp_name );
-}
-static void dump_command_data ( void * data )
-{
-	struct dict_cmd_data * cmd = (struct dict_cmd_data *) data;
-	fd_log_debug("data: v/m:" DUMP_CMDFL_str "/" DUMP_CMDFL_str ", %-6u \"%s\"", 
-			DUMP_CMDFL_val(cmd->cmd_flag_val), DUMP_CMDFL_val(cmd->cmd_flag_mask), cmd->cmd_code, cmd->cmd_name);
-}
-static void dump_rule_data ( void * data )
-{
-	struct dict_rule_data * rule = (struct dict_rule_data * )data;
-	fd_log_debug("data: pos:%d ord:%d m/M:%2d/%2d avp:\"%s\"", 
-			rule->rule_position, 
-			rule->rule_order, 
-			rule->rule_min, 
-			rule->rule_max,
-			rule->rule_avp->data.avp.avp_name);
-}
-
-static void dump_object ( struct dict_object * obj, int parents, int depth, int indent );
-
-static void dump_list ( struct fd_list * sentinel, int parents, int depth, int indent )
-{
-	struct fd_list * li = sentinel;
-	/* We don't lock here, the caller must have taken the dictionary lock for reading already */
-	while (li->next != sentinel)
-	{
-		li = li->next;
-		dump_object( _O(li->o), parents, depth, indent );
-	}
-}
-
-static void dump_object ( struct dict_object * obj, int parents, int depth, int indent )
-{
-	if (obj == NULL)
-		return;
-	
-	if (parents)
-		dump_object (obj->parent, parents-1, 0, indent + 1 );
-	
-	fd_log_debug("%*s@%p: %s%s (p:%-9p) ", 
-			indent,
-			"",
-			obj, 
-			verify_object(obj) ? "" : "INVALID ", 
-			_OBINFO(obj).name, 
-			obj->parent);
-	
-	if (obj->type == DICT_ENUMVAL)
-		dump_enumval_data ( &obj->data.enumval, obj->parent->data.type.type_base );
-	else
-		_OBINFO(obj).dump_data(&obj->data);
-	
-	fd_log_debug("\n");
-	
-	if (depth) {
-		int i;
-		for (i=0; i<NB_LISTS_PER_OBJ; i++) {
-			if ((obj->list[i].o == NULL) && (obj->list[i].next != &obj->list[i])) {
-				fd_log_debug("%*s>%p: list[%d]:\n", indent, "", obj, i);
-				dump_list(&obj->list[i], parents, depth - 1, indent + 2);
-			}
-		}
-	}
-}
-
-void fd_dict_dump_object(struct dict_object * obj)
-{
-	fd_log_debug("Dictionary object %p dump:\n", obj);
-	dump_object( obj, 1, 2, 2 );
-}
-
-void fd_dict_dump(struct dictionary * dict)
-{
-	int i;
-	
-	CHECK_PARAMS_DO(dict && (dict->dict_eyec == DICT_EYECATCHER), return);
-	
-	CHECK_POSIX_DO(  pthread_rwlock_rdlock( &dict->dict_lock ), /* ignore */  );
-	
-	fd_log_debug("######################################################\n");
-	fd_log_debug("###### Dumping vendors, AVPs and related rules #######\n");
-	
-	dump_object( &dict->dict_vendors, 0, 3, 0 );
-	
-	fd_log_debug("###### Dumping applications #######\n");
-
-	dump_object( &dict->dict_applications, 0, 1, 0 );
-	
-	fd_log_debug("###### Dumping types #######\n");
-
-	dump_list( &dict->dict_types, 0, 2, 0 );
-	
-	fd_log_debug("###### Dumping commands per name #######\n");
-
-	dump_list( &dict->dict_cmd_name, 0, 2, 0 );
-	
-	fd_log_debug("###### Dumping commands per code and flags #######\n");
-
-	dump_list( &dict->dict_cmd_code, 0, 0, 0 );
-	
-	fd_log_debug("###### Statistics #######\n");
-
-	for (i=1; i<=DICT_TYPE_MAX; i++)
-		fd_log_debug(" %5d objects of type %s\n", dict->dict_count[i], dict_obj_info[i].name);
-	
-	fd_log_debug("######################################################\n");
-	
-	/* Free the rwlock */
-	CHECK_POSIX_DO(  pthread_rwlock_unlock( &dict->dict_lock ), /* ignore */  );
-}
-
-/**************************** Dump AVP values ********************************/
-
-/* Default dump functions */
-static void dump_val_os(union avp_value * value)
-{
-	int i;
-	for (i = 0; i < value->os.len; i++) {
-		if (i == 24) { /* Dump only up to 24 bytes of the buffer */
-			fd_log_debug("[...] (len=%zd)", value->os.len);
-			break;
-		}
-		fd_log_debug("%02.2X ", value->os.data[i]);
-	}
-}
-
-static void dump_val_i32(union avp_value * value)
-{
-	fd_log_debug("%i (0x%x)", value->i32, value->i32);
-}
-
-static void dump_val_i64(union avp_value * value)
-{
-	fd_log_debug("%lli (0x%llx)", value->i64, value->i64);
-}
-
-static void dump_val_u32(union avp_value * value)
-{
-	fd_log_debug("%u (0x%x)", value->u32, value->u32);
-}
-
-static void dump_val_u64(union avp_value * value)
-{
-	fd_log_debug("%llu (0x%llx)", value->u64, value->u64);
-}
-
-static void dump_val_f32(union avp_value * value)
-{
-	fd_log_debug("%f", value->f32);
-}
-
-static void dump_val_f64(union avp_value * value)
-{
-	fd_log_debug("%g", value->f64);
-}
-
-/* Get the dump function for basic dict_avp_basetype */
-static void (*get_default_dump_val_cb(enum dict_avp_basetype datatype))(union avp_value *)
-{
-	switch (datatype) {
-		case AVP_TYPE_OCTETSTRING:
-			return &dump_val_os;
-		
-		case AVP_TYPE_INTEGER32:
-			return &dump_val_i32;
-
-		case AVP_TYPE_INTEGER64:
-			return &dump_val_i64;
-
-		case AVP_TYPE_UNSIGNED32:
-			return &dump_val_u32;
-
-		case AVP_TYPE_UNSIGNED64:
-			return &dump_val_u64;
-
-		case AVP_TYPE_FLOAT32:
-			return &dump_val_f32;
-
-		case AVP_TYPE_FLOAT64:
-			return &dump_val_f64;
-		
-		case AVP_TYPE_GROUPED:
-			TRACE_DEBUG(FULL, "error: grouped AVP with a value!");
-	}
-	return NULL;
-}
-
-/* indent inside an object (duplicate from messages.c) */
-#define INOBJHDR 	"%*s   "
-#define INOBJHDRVAL 	indent<0 ? 1 : indent, indent<0 ? "-" : "|"
-
-/* Formater for the AVP value dump line */
-static void dump_avp_val(union avp_value *avp_value, void (*dump_val_cb)(union avp_value *avp_value), enum dict_avp_basetype datatype, char * type_name, char * const_name, int indent)
-{
-	/* Header for all AVP values dumps: */
-	fd_log_debug(INOBJHDR "value ", INOBJHDRVAL);
-	
-	/* If the type is provided, write it */
-	if (type_name)
-		fd_log_debug("t: '%s' ", type_name);
-	
-	/* Always give the base datatype anyway */
-	fd_log_debug("(%s) ", type_base_name[datatype]);
-	
-	/* Now, the value */
-	fd_log_debug("v: ");
-	if (const_name)
-		fd_log_debug("'%s' (", const_name);
-	(*dump_val_cb)(avp_value);
-	if (const_name)
-		fd_log_debug(")");
-	
-	/* Done! */
-	fd_log_debug("\n");
-}
-
-/* Dump the value of an AVP of known type */
-void fd_dict_dump_avp_value(union avp_value *avp_value, struct dict_object * model, int indent)
-{
-	void (*dump_val_cb)(union avp_value *avp_value);
-	struct dict_object * type = NULL;
-	char * type_name = NULL;
-	char * const_name = NULL;
-	
-	/* Check the parameters are correct */
-	CHECK_PARAMS_DO( avp_value && verify_object(model) && (model->type == DICT_AVP), return );
-	
-	/* Default: display the value with the formatter for the AVP datatype */
-	CHECK_PARAMS_DO( dump_val_cb = get_default_dump_val_cb(model->data.avp.avp_basetype), return );
-	
-	/* Get the type definition of this AVP */
-	type = model->parent;
-	if (type) {
-		struct dict_enumval_request  request;
-		struct dict_object * enumval = NULL;
-		
-		type_name = type->data.type.type_name;
-		
-		/* overwrite the dump function ? */
-		if (type->data.type.type_dump)
-			dump_val_cb = type->data.type.type_dump;
-		
-		/* Now check if the AVP value matches a constant */
-		memset(&request, 0, sizeof(request));
-		request.type_obj = type;
-		memcpy(&request.search.enum_value, avp_value, sizeof(union avp_value));
-		/* bypass checks */
-		if ((search_enumval( type->dico, ENUMVAL_BY_STRUCT, &request, &enumval ) == 0) && (enumval)) {
-			/* We found a cosntant, get its name */
-			const_name = enumval->data.enumval.enum_name;
-		}
-	}
-	
-	/* And finally, dump the value */
-	dump_avp_val(avp_value, dump_val_cb, model->data.avp.avp_basetype, type_name, const_name, indent);
-}
-
-/*******************************************************************************************************/
-/*******************************************************************************************************/
-/*                                                                                                     */
-/*                                  Exported functions                                                 */
-/*                                                                                                     */
-/*******************************************************************************************************/
-/*******************************************************************************************************/
-
-/* These are the functions exported outside libfreeDiameter. */
-
-/* Get the data associated to an object */
-int fd_dict_gettype ( struct dict_object * object, enum dict_object_type * type)
-{
-	TRACE_ENTRY("%p %p", object, type);
-	
-	CHECK_PARAMS( type && verify_object(object) );
-	
-	/* Copy the value and return */
-	*type = object->type;
-	return 0;
-}
-
-int fd_dict_getdict ( struct dict_object * object, struct dictionary ** dict)
-{
-	TRACE_ENTRY("%p %p", object, dict);
-	
-	CHECK_PARAMS( dict && verify_object(object) );
-	
-	/* Copy the value and return */
-	*dict = object->dico;
-	return 0;
-}
-
-
-/* Get the data associated to an object */
-int fd_dict_getval ( struct dict_object * object, void * val)
-{
-	TRACE_ENTRY("%p %p", object, val);
-	
-	CHECK_PARAMS( val && verify_object(object) );
-	
-	/* Copy the value and return */
-	memcpy(val, &object->data, _OBINFO(object).datasize);;
-	return 0;
-}
-
-/* Add a new object in the dictionary */
-int fd_dict_new ( struct dictionary * dict, enum dict_object_type type, void * data, struct dict_object * parent, struct dict_object **ref )
-{
-	int ret = 0;
-	struct dict_object * new = NULL;
-	struct dict_object * vendor = NULL;
-	
-	TRACE_ENTRY("%p %d(%s) %p %p %p", dict, type, dict_obj_info[CHECK_TYPE(type) ? type : 0].name, data, parent, ref);
-	
-	/* Check parameters */
-	CHECK_PARAMS( dict && (dict->dict_eyec == DICT_EYECATCHER) && CHECK_TYPE(type) && data  );
-	
-	/* Check the "parent" parameter */
-	switch (dict_obj_info[type].parent) {
-		case 0:	/* parent is forbidden */
-			CHECK_PARAMS( parent == NULL );
-		
-		case 1:	/* parent is optional */
-			if (parent == NULL)
-				break;
-		
-		case 2: /* parent is mandatory */
-			CHECK_PARAMS(  verify_object(parent)  );
-			
-			if (type == DICT_RULE ) { /* Special case : grouped AVP or Command parents are allowed */
-				CHECK_PARAMS( (parent->type == DICT_COMMAND ) 
-						|| ( (parent->type == DICT_AVP) && (parent->data.avp.avp_basetype == AVP_TYPE_GROUPED ) ) );
-			} else {
-				CHECK_PARAMS( parent->type == dict_obj_info[type].parenttype );
-			}
-	}
-	
-	/* For AVP object, we must also check that the "vendor" referenced exists */
-	if (type == DICT_AVP) {
-		CHECK_FCT_DO(  fd_dict_search( dict, DICT_VENDOR, VENDOR_BY_ID, &(((struct dict_avp_data *)data)->avp_vendor), (void*)&vendor, ENOENT ),
-				CHECK_PARAMS( vendor = NULL )  );
-		
-		/* Also check if a parent is provided, that the type are the same */
-		if (parent) {
-			CHECK_PARAMS(  parent->data.type.type_base == ((struct dict_avp_data *)data)->avp_basetype  );
-		}
-	}
-	
-	/* For RULE object, we must also check that the "avp" referenced exists */
-	if (type == DICT_RULE) {
-		CHECK_PARAMS(  verify_object(((struct dict_rule_data *)data)->rule_avp)  );
-		CHECK_PARAMS(  ((struct dict_rule_data *)data)->rule_avp->type == DICT_AVP  );
-	}
-	
-	/* For COMMAND object, check that the 'R' flag is fixed */
-	if (type == DICT_COMMAND) {
-		CHECK_PARAMS( ((struct dict_cmd_data *)data)->cmd_flag_mask & CMD_FLAG_REQUEST   );
-	}
-	
-	/* We have to check that the new values are not equal to the sentinels */
-	if (type == DICT_VENDOR) {
-		CHECK_PARAMS( ((struct dict_vendor_data *)data)->vendor_id != 0   );
-	}
-	if (type == DICT_APPLICATION) {
-		CHECK_PARAMS( ((struct dict_application_data *)data)->application_id != 0   );
-	}
-	
-	/* Parameters are valid, create the new object */
-	CHECK_MALLOC(  new = malloc(sizeof(struct dict_object))  );
-	
-	/* Initialize the data of the new object */
-	init_object(new, type);
-	init_object_data(&new->data, data, type);
-	new->dico = dict;
-	new->parent = parent;
-	
-	/* We will change the dictionary => acquire the write lock */
-	CHECK_POSIX_DO(  ret = pthread_rwlock_wrlock(&dict->dict_lock),  goto error_free  );
-	
-	/* Now link the object -- this also checks that no object with same keys already exists */
-	switch (type) {
-		case DICT_VENDOR:
-			/* A vendor object is linked in the g_dict_vendors.list[0], by their id */
-			CHECK_FCT_DO(  ret = fd_list_insert_ordered ( &dict->dict_vendors.list[0], &new->list[0], (int (*)(void*, void *))order_vendor_by_id, (void **)ref ),
-					goto error_unlock  );
-			break;
-		
-		case DICT_APPLICATION:
-			/* An application object is linked in the g_dict_applciations.list[0], by their id */
-			CHECK_FCT_DO(  ret = fd_list_insert_ordered ( &dict->dict_applications.list[0], &new->list[0], (int (*)(void*, void *))order_appli_by_id, (void **)ref ),
-					goto error_unlock  );
-			break;
-		
-		case DICT_TYPE:
-			/* A type object is linked in g_list_types by its name */
-			CHECK_FCT_DO(  ret = fd_list_insert_ordered ( &dict->dict_types, &new->list[0], (int (*)(void*, void *))order_type_by_name, (void **)ref ),
-					goto error_unlock  );
-			break;
-		
-		case DICT_ENUMVAL:
-			/* A type_enum object is linked in it's parent 'type' object lists 1 and 2 by its name and values */
-			CHECK_FCT_DO(  ret = fd_list_insert_ordered ( &parent->list[1], &new->list[0], (int (*)(void*, void *))order_enum_by_name, (void **)ref ),
-					goto error_unlock  );
-			CHECK_FCT_DO(  ret = fd_list_insert_ordered ( &parent->list[2], &new->list[1], (int (*)(void*, void *))order_enum_by_val, (void **)ref ),
-					{ fd_list_unlink(&new->list[0]); goto error_unlock; }  );
-			break;
-		
-		case DICT_AVP:
-			/* An avp object is linked in lists 1 and 2 of its vendor, by code and name */
-			CHECK_FCT_DO(  ret = fd_list_insert_ordered ( &vendor->list[1], &new->list[0], (int (*)(void*, void *))order_avp_by_code, (void **)ref ),
-					goto error_unlock  );
-			CHECK_FCT_DO(  ret = fd_list_insert_ordered ( &vendor->list[2], &new->list[1], (int (*)(void*, void *))order_avp_by_name, (void **)ref ),
-					{ fd_list_unlink(&new->list[0]); goto error_unlock; }  );
-			break;
-			
-		case DICT_COMMAND:
-			/* A command object is linked in g_list_cmd_name and g_list_cmd_code by its name and code */
-			CHECK_FCT_DO(  ret = fd_list_insert_ordered ( &dict->dict_cmd_code, &new->list[1], (int (*)(void*, void *))order_cmd_by_codefl, (void **)ref ),
-					goto error_unlock  );
-			CHECK_FCT_DO(  ret = fd_list_insert_ordered ( &dict->dict_cmd_name, &new->list[0], (int (*)(void*, void *))order_cmd_by_name, (void **)ref ),
-					{ fd_list_unlink(&new->list[1]); goto error_unlock; }  );
-			break;
-		
-		case DICT_RULE:
-			/* A rule object is linked in list[2] of its parent command or AVP by the name of the AVP it refers */
-			CHECK_FCT_DO(  ret = fd_list_insert_ordered ( &parent->list[2], &new->list[0], (int (*)(void*, void *))order_rule_by_avpn, (void **)ref ),
-					goto error_unlock  );
-			break;
-			
-		default:
-			ASSERT(0);
-	}
-	
-	/* A new object has been created, increment the global counter */
-	dict->dict_count[type]++;
-	
-	/* Unlock the dictionary */
-	CHECK_POSIX_DO(  ret = pthread_rwlock_unlock(&dict->dict_lock),  goto error_free  );
-	
-	/* Save the pointer to the new object */
-	if (ref)
-		*ref = new;
-	
-	return 0;
-	
-error_unlock:
-	CHECK_POSIX_DO(  pthread_rwlock_unlock(&dict->dict_lock),  /* continue */  );
-error_free:
-	free(new);
-	return ret;
-}
-
-int fd_dict_search ( struct dictionary * dict, enum dict_object_type type, int criteria, void * what, struct dict_object **result, int retval )
-{
-	int ret = 0;
-	
-	TRACE_ENTRY("%p %d(%s) %d %p %p %d", dict, type, dict_obj_info[CHECK_TYPE(type) ? type : 0].name, criteria, what, result, retval);
-	
-	/* Check param */
-	CHECK_PARAMS( dict && (dict->dict_eyec == DICT_EYECATCHER) && CHECK_TYPE(type) );
-	
-	/* Lock the dictionary for reading */
-	CHECK_POSIX(  pthread_rwlock_rdlock(&dict->dict_lock)  );
-	
-	/* Now call the type-specific search function */
-	ret = dict_obj_info[type].search_fct (dict, criteria, what, result);
-	
-	/* Unlock */
-	CHECK_POSIX(  pthread_rwlock_unlock(&dict->dict_lock)  );
-	
-	/* Update the return value as needed */
-	if ((result != NULL) && (*result == NULL))
-		ret = retval;
-	
-	return ret;
-}
-
-/*******************************************************************************************************/
-/*******************************************************************************************************/
-/*                                                                                                     */
-/*                                  The init/fini functions                                            */
-/*                                                                                                     */
-/*******************************************************************************************************/
-/*******************************************************************************************************/
-
-/* Initialize the dictionary */
-int fd_dict_init ( struct dictionary ** dict)
-{
-	struct dictionary * new = NULL;
-	
-	TRACE_ENTRY("");
-	
-	/* Sanity checks */
-	ASSERT( (sizeof(type_base_name) / sizeof(type_base_name[0])) == (AVP_TYPE_MAX + 1) );
-	ASSERT( (sizeof(dict_obj_info)  / sizeof(dict_obj_info[0]))  == (DICT_TYPE_MAX + 1) );
-	CHECK_PARAMS(dict);
-	
-	/* Allocate the memory for the dictionary */
-	CHECK_MALLOC( new = malloc(sizeof(struct dictionary)) );
-	memset(new, 0, sizeof(struct dictionary));
-	
-	new->dict_eyec = DICT_EYECATCHER;
-	
-	/* Initialize the lock for the dictionary */
-	CHECK_POSIX(  pthread_rwlock_init(&new->dict_lock, NULL)  );
-	
-	/* Initialize the sentinel for vendors and AVP lists */
-	init_object( &new->dict_vendors, DICT_VENDOR );
-	new->dict_vendors.data.vendor.vendor_name = "(no vendor)";
-	new->dict_vendors.list[0].o = NULL; /* overwrite since element is also sentinel for this list. */
-	new->dict_vendors.dico = new;
-	
-	/* Initialize the sentinel for applications */
-	init_object( &new->dict_applications, DICT_APPLICATION );
-	new->dict_applications.data.application.application_name = "Diameter Common Messages";
-	new->dict_applications.list[0].o = NULL; /* overwrite since since element is also sentinel for this list. */
-	new->dict_applications.dico = new;
-			
-	/* Initialize the sentinel for types */
-	fd_list_init ( &new->dict_types, NULL );
-	
-	/* Initialize the sentinels for commands */
-	fd_list_init ( &new->dict_cmd_name, NULL );
-	fd_list_init ( &new->dict_cmd_code, NULL );
-	
-	/* Initialize the error command object */
-	init_object( &new->dict_cmd_error, DICT_COMMAND );
-	new->dict_cmd_error.data.cmd.cmd_name="(generic error format)";
-	new->dict_cmd_error.data.cmd.cmd_flag_mask=CMD_FLAG_ERROR | CMD_FLAG_REQUEST | CMD_FLAG_RETRANSMIT;
-	new->dict_cmd_error.data.cmd.cmd_flag_val =CMD_FLAG_ERROR;
-	new->dict_cmd_error.dico = new;
-	
-	*dict = new;
-	
-	/* Done */
-	return 0;
-}
-
-/* Destroy a dictionary */
-int fd_dict_fini ( struct dictionary ** dict)
-{
-	int i;
-	
-	TRACE_ENTRY("");
-	CHECK_PARAMS( dict && *dict && ((*dict)->dict_eyec == DICT_EYECATCHER) );
-	
-	/* Acquire the write lock to make sure no other operation is ongoing */
-	CHECK_POSIX(  pthread_rwlock_wrlock(&(*dict)->dict_lock)  );
-	
-	/* Empty all the lists, free the elements */
-	destroy_list ( &(*dict)->dict_cmd_error.list[2] );
-	destroy_list ( &(*dict)->dict_cmd_code );
-	destroy_list ( &(*dict)->dict_cmd_name );
-	destroy_list ( &(*dict)->dict_types );
-	for (i=0; i< NB_LISTS_PER_OBJ; i++) {
-		destroy_list ( &(*dict)->dict_applications.list[i] );
-		destroy_list ( &(*dict)->dict_vendors.list[i] );
-	}
-	
-	/* Dictionary is empty, now destroy the lock */
-	CHECK_POSIX(  pthread_rwlock_unlock(&(*dict)->dict_lock)  );
-	CHECK_POSIX(  pthread_rwlock_destroy(&(*dict)->dict_lock)  );
-	
-	free(*dict);
-	*dict = NULL;
-	
-	return 0;
-}
-
-/*******************************************************************************************************/
-/*******************************************************************************************************/
-/*                                                                                                     */
-/*                                  Other functions                                                    */
-/*                                                                                                     */
-/*******************************************************************************************************/
-/*******************************************************************************************************/
-
-/* Iterate a callback on the rules for an object */
-int fd_dict_iterate_rules ( struct dict_object *parent, void * data, int (*cb)(void *, struct dict_rule_data *) )
-{
-	int ret = 0;
-	struct fd_list * li;
-	
-	TRACE_ENTRY("%p %p %p", parent, data, cb);
-	
-	/* Check parameters */
-	CHECK_PARAMS(  verify_object(parent)  );
-	CHECK_PARAMS(  (parent->type == DICT_COMMAND) 
-			|| ((parent->type == DICT_AVP) && (parent->data.avp.avp_basetype == AVP_TYPE_GROUPED)) );
-	TRACE_DEBUG (FULL, "Iterating on rules of %s: '%s'.", 
-			_OBINFO(parent).name, 
-			parent->type == DICT_COMMAND ? 
-				  parent->data.cmd.cmd_name
-				: parent->data.avp.avp_name);
-	
-	/* Acquire the read lock  */
-	CHECK_POSIX(  pthread_rwlock_rdlock(&parent->dico->dict_lock)  );
-	
-	/* go through the list and call the cb on each rule data */
-	for (li = &(parent->list[2]); li->next != &(parent->list[2]); li = li->next) {
-		ret = (*cb)(data, &(_O(li->next->o)->data.rule));
-		if (ret != 0)
-			break;
-	}
-		
-	/* Release the lock */
-	CHECK_POSIX(  pthread_rwlock_unlock(&parent->dico->dict_lock)  );
-	
-	return ret;
-}
-
-/* Create the list of vendors. Returns a 0-terminated array, that must be freed after use. Returns NULL on error. */
-uint32_t * fd_dict_get_vendorid_list(struct dictionary * dict)
-{
-	uint32_t * ret = NULL;
-	int i = 0;
-	struct fd_list * li;
-	
-	TRACE_ENTRY();
-	
-	/* Acquire the read lock */
-	CHECK_POSIX_DO(  pthread_rwlock_rdlock(&dict->dict_lock), return NULL  );
-	
-	/* Allocate an array to contain all the elements */
-	CHECK_MALLOC_DO( ret = calloc( dict->dict_count[DICT_VENDOR] + 1, sizeof(uint32_t) ), goto out );
-	
-	/* Copy the vendors IDs */
-	for (li = dict->dict_vendors.list[0].next; li != &(dict->dict_vendors.list[0]); li = li->next) {
-		ret[i] = _O(li->o)->data.vendor.vendor_id;
-		i++;
-		ASSERT( i <= dict->dict_count[DICT_VENDOR] );
-	}
-out:	
-	/* Release the lock */
-	CHECK_POSIX_DO(  pthread_rwlock_unlock(&dict->dict_lock), return NULL  );
-	
-	return ret;
-}
-
-/* Return the location of the cb list for an object, after checking its type */
-int fd_dict_disp_cb(enum dict_object_type type, struct dict_object *obj, struct fd_list ** cb_list)
-{
-	TRACE_ENTRY("%d %p %p", type, obj, cb_list);
-	CHECK_PARAMS( verify_object(obj) );
-	CHECK_PARAMS( _OBINFO(obj).type == type );
-	CHECK_PARAMS( cb_list );
-	*cb_list = &obj->disp_cbs;
-	return 0;
-}
-
-int fd_dict_get_error_cmd(struct dictionary * dict, struct dict_object **obj)
-{
-	TRACE_ENTRY("%p %p", dict, obj);
-	CHECK_PARAMS( dict && (dict->dict_eyec == DICT_EYECATCHER) && obj );
-	*obj = &dict->dict_cmd_error;
-	return 0;
-}
Index: bfreeDiameter/dispatch.c
===================================================================
--- libfreeDiameter/dispatch.c	(revision 648)
+++ 	(revision )
@@ -1,218 +1,0 @@
-/*********************************************************************************************************
-* Software License Agreement (BSD License)                                                               *
-* Author: Sebastien Decugis <sdecugis@nict.go.jp>							 *
-*													 *
-* Copyright (c) 2010, 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 "libfD.h"
-
-/* The dispatch module in the library is quite simple: callbacks are saved in a global list
- * in no particular order. In addition, they are also linked from the dictionary objects they
- * refer to. */
-
-/* Protection for the lists managed in this module. */
-pthread_rwlock_t fd_disp_lock = PTHREAD_RWLOCK_INITIALIZER;
-
-/* List of all registered handlers -- useful if we want to cleanup properly at some point... */
-static struct fd_list all_handlers = FD_LIST_INITIALIZER( all_handlers );
-
-/* List of handlers registered for DISP_HOW_ANY. Other handlers are stored in the dictionary */
-static struct fd_list any_handlers = FD_LIST_INITIALIZER( any_handlers );
-
-/* The structure to store a callback */
-struct disp_hdl {
-	int		 eyec;	/* Eye catcher, DISP_EYEC */
-	struct fd_list	 all;	/* link in the all_handlers list */
-	struct fd_list	 parent;/* link in dictionary cb_list or in any_handlers */
-	enum disp_how	 how;	/* Copy of registration parameter */
-	struct disp_when when;	/* Copy of registration parameter */
-	int		(*cb)( struct msg **, struct avp *, struct session *, void *, enum disp_action *);	/* The callback itself */
-	void            *opaque; /* opaque data passed back to the callback */
-};
-
-#define DISP_EYEC	0xD15241C1
-#define VALIDATE_HDL( _hdl ) \
-	( ( ( _hdl ) != NULL ) && ( ((struct disp_hdl *)( _hdl ))->eyec == DISP_EYEC ) )
-
-/**************************************************************************************/
-
-/* Call CBs from a given list (any_handlers if cb_list is NULL) -- must have locked fd_disp_lock before */
-int fd_disp_call_cb_int( struct fd_list * cb_list, struct msg ** msg, struct avp *avp, struct session *sess, enum disp_action *action, 
-			struct dict_object * obj_app, struct dict_object * obj_cmd, struct dict_object * obj_avp, struct dict_object * obj_enu)
-{
-	struct fd_list * senti, *li;
-	TRACE_ENTRY("%p %p %p %p %p %p %p %p %p", cb_list, msg, avp, sess, action, obj_app, obj_cmd, obj_avp, obj_enu);
-	CHECK_PARAMS(msg && action);
-	
-	senti = cb_list;
-	if (!senti)
-		senti = &any_handlers;
-	
-	for (li = senti->next; li != senti; li = li->next) {
-		struct disp_hdl * hdl = (struct disp_hdl *)(li->o);
-		
-		TRACE_DEBUG(ANNOYING, "when: %p %p %p %p", hdl->when.app, hdl->when.command, hdl->when.avp, hdl->when.value);
-		
-		/* Check this handler matches this message / avp */
-		if (hdl->when.app     && (hdl->when.app     != obj_app))
-			continue;
-		if (hdl->when.command && (hdl->when.command != obj_cmd))
-			continue;
-		if (hdl->when.avp     && (hdl->when.avp     != obj_avp))
-			continue;
-		if (hdl->when.value   && (hdl->when.value   != obj_enu))
-			continue;
-		
-		/* We have a match, the cb must be called. */
-		CHECK_FCT( (*hdl->cb)(msg, avp, sess, hdl->opaque, action) );
-		
-		if (*action != DISP_ACT_CONT)
-			break;
-		
-		if ( *msg  == NULL )
-			break;
-	}
-	
-	/* We're done on this list */
-	return 0;
-}
-
-/**************************************************************************************/
-
-/* Create a new handler and link it */
-int fd_disp_register ( int (*cb)( struct msg **, struct avp *, struct session *, void *, enum disp_action *), 
-			enum disp_how how, struct disp_when * when, void * opaque, struct disp_hdl ** handle )
-{
-	struct fd_list * cb_list = NULL;
-	struct disp_hdl * new;
-	struct dict_object * type_enum, * type_avp;
-	struct dictionary  * dict = NULL;
-	
-	TRACE_ENTRY("%p %d %p %p", cb, how, when, handle);
-	CHECK_PARAMS( cb && ( (how == DISP_HOW_ANY) || when ));
-	
-	switch (how) {
-		case DISP_HOW_ANY:
-			cb_list = &any_handlers;
-			break;
-		
-		case DISP_HOW_APPID:
-			CHECK_FCT( fd_dict_disp_cb(DICT_APPLICATION, when->app, &cb_list) );
-			break;
-		
-		case DISP_HOW_CC:
-			CHECK_FCT( fd_dict_disp_cb(DICT_COMMAND, when->command, &cb_list) );
-			break;
-		
-		case DISP_HOW_AVP_ENUMVAL:
-			CHECK_FCT( fd_dict_disp_cb(DICT_ENUMVAL, when->value, &cb_list) ); /* cb_list is then overwriten */
-			CHECK_FCT( fd_dict_getdict(when->value, &dict) );
-			CHECK_FCT( fd_dict_search(dict, DICT_TYPE, TYPE_OF_ENUMVAL, when->value, &type_enum, EINVAL) );
-		case DISP_HOW_AVP:
-			CHECK_FCT( fd_dict_disp_cb(DICT_AVP, when->avp, &cb_list) );
-			if (dict) {
-				CHECK_FCT( fd_dict_search(dict, DICT_TYPE, TYPE_OF_AVP, when->avp, &type_avp, EINVAL) );
-				CHECK_PARAMS( type_enum == type_avp );
-			}
-			break;
-		
-		default:
-			CHECK_PARAMS(how = 0);
-	}
-	/* We might further check optional fields, but we trust the caller ^^ */
-	
-	/* Create the new handler */
-	CHECK_MALLOC( new = malloc( sizeof(struct disp_hdl) ) );
-	memset(new, 0, sizeof(struct disp_hdl));
-	new->eyec = DISP_EYEC;
-	fd_list_init(&new->all, new);
-	fd_list_init(&new->parent, new);
-	new->how = how;
-	switch (how) {
-		case DISP_HOW_ANY:
-			/* there is no "when" in that case */
-			break;
-		case DISP_HOW_AVP_ENUMVAL:
-			new->when.value   = when->value;
-		case DISP_HOW_AVP:
-			new->when.avp     = when->avp;
-		case DISP_HOW_CC:
-			new->when.command = when->command;
-		case DISP_HOW_APPID:
-			new->when.app     = when->app;
-	}
-	new->cb = cb;
-	new->opaque = opaque;
-	
-	/* Now, link this new element in the appropriate lists */
-	CHECK_POSIX( pthread_rwlock_wrlock(&fd_disp_lock) );
-	fd_list_insert_before(&all_handlers, &new->all);
-	fd_list_insert_before(cb_list, &new->parent);
-	CHECK_POSIX( pthread_rwlock_unlock(&fd_disp_lock) );
-	
-	/* We're done */
-	if (handle)
-		*handle = new;
-	
-	return 0;
-}
-
-/* Delete a handler */
-int fd_disp_unregister ( struct disp_hdl ** handle, void ** opaque )
-{
-	struct disp_hdl * del;
-	TRACE_ENTRY("%p", handle);
-	CHECK_PARAMS( handle && VALIDATE_HDL(*handle) );
-	del = *handle;
-	*handle = NULL;
-	
-	CHECK_POSIX( pthread_rwlock_wrlock(&fd_disp_lock) );
-	fd_list_unlink(&del->all);
-	fd_list_unlink(&del->parent);
-	CHECK_POSIX( pthread_rwlock_unlock(&fd_disp_lock) );
-	
-	if (opaque)
-		*opaque = del->opaque;
-	
-	free(del);
-	return 0;
-}
-
-/* Delete all handlers */
-void fd_disp_unregister_all ( void )
-{
-	TRACE_ENTRY("");
-	while (!FD_IS_LIST_EMPTY(&all_handlers)) {
-		CHECK_FCT_DO( fd_disp_unregister((void *)&(all_handlers.next->o), NULL), /* continue */ );
-	}
-	return;
-}
Index: bfreeDiameter/fifo.c
===================================================================
--- libfreeDiameter/fifo.c	(revision 451)
+++ 	(revision )
@@ -1,500 +1,0 @@
-/*********************************************************************************************************
-* Software License Agreement (BSD License)                                                               *
-* Author: Sebastien Decugis <sdecugis@nict.go.jp>							 *
-*													 *
-* Copyright (c) 2010, 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.								 *
-*********************************************************************************************************/
-
-/* FIFO queues module.
- *
- * The threads that call these functions must be in the cancellation state PTHREAD_CANCEL_ENABLE and type PTHREAD_CANCEL_DEFERRED.
- * This is the default state and type on thread creation.
- *
- * In order to destroy properly a queue, the application must:
- *  -> shutdown any process that can add into the queue first.
- *  -> pthread_cancel any thread that could be waiting on the queue.
- *  -> consume any element that is in the queue, using fd_qu_tryget_int.
- *  -> then destroy the queue using fd_mq_del.
- */
-
-#include "libfD.h"
-
-/* Definition of a FIFO queue object */
-struct fifo {
-	int		eyec;	/* An eye catcher, also used to check a queue is valid. FIFO_EYEC */
-	
-	pthread_mutex_t	mtx;	/* Mutex protecting this queue */
-	pthread_cond_t	cond;	/* condition variable of the list */
-	
-	struct fd_list	list;	/* sentinel for the list of elements */
-	int		count;	/* number of objects in the list */
-	int		thrs;	/* number of threads waiting for a new element (when count is 0) */
-	
-	uint16_t	high;	/* High level threshold (see libfreeDiameter.h for details) */
-	uint16_t	low;	/* Low level threshhold */
-	void 		*data;	/* Opaque pointer for threshold callbacks */
-	void		(*h_cb)(struct fifo *, void **); /* The callbacks */
-	void		(*l_cb)(struct fifo *, void **);
-	int 		highest;/* The highest count value for which h_cb has been called */
-	int		highest_ever; /* The max count value this queue has reached (for tweaking) */
-};
-
-/* The eye catcher value */
-#define FIFO_EYEC	0xe7ec1130
-
-/* Macro to check a pointer */
-#define CHECK_FIFO( _queue ) (( (_queue) != NULL) && ( (_queue)->eyec == FIFO_EYEC) )
-
-
-/* Create a new queue */
-int fd_fifo_new ( struct fifo ** queue )
-{
-	struct fifo * new;
-	
-	TRACE_ENTRY( "%p", queue );
-	
-	CHECK_PARAMS( queue );
-	
-	/* Create a new object */
-	CHECK_MALLOC( new = malloc (sizeof (struct fifo) )  );
-	
-	/* Initialize the content */
-	memset(new, 0, sizeof(struct fifo));
-	
-	new->eyec = FIFO_EYEC;
-	CHECK_POSIX( pthread_mutex_init(&new->mtx, NULL) );
-	CHECK_POSIX( pthread_cond_init(&new->cond, NULL) );
-	
-	fd_list_init(&new->list, NULL);
-	
-	/* We're done */
-	*queue = new;
-	return 0;
-}
-
-/* Dump the content of a queue */
-void fd_fifo_dump(int level, char * name, struct fifo * queue, void (*dump_item)(int level, void * item))
-{
-	TRACE_ENTRY("%i %p %p %p", level, name, queue, dump_item);
-	
-	if (!TRACE_BOOL(level))
-		return;
-	
-	fd_log_debug("Dumping queue '%s' (%p):\n", name ?: "?", queue);
-	if (!CHECK_FIFO( queue )) {
-		fd_log_debug("  Queue invalid!\n");
-		if (queue)
-			fd_log_debug("  (%x != %x)\n", queue->eyec, FIFO_EYEC);
-		return;
-	}
-	
-	CHECK_POSIX_DO(  pthread_mutex_lock( &queue->mtx ), /* continue */  );
-	fd_log_debug("   %d elements in queue / %d threads waiting\n", queue->count, queue->thrs);
-	fd_log_debug("   thresholds: %d / %d (h:%d), cb: %p,%p (%p), highest: %d\n",
-			queue->high, queue->low, queue->highest, 
-			queue->h_cb, queue->l_cb, queue->data,
-			queue->highest_ever);
-	
-	if (dump_item) {
-		struct fd_list * li;
-		int i = 0;
-		for (li = queue->list.next; li != &queue->list; li = li->next) {
-			fd_log_debug("  [%i] item %p in fifo %p:\n", i++, li->o, queue);
-			(*dump_item)(level, li->o);
-		}
-	}
-	CHECK_POSIX_DO(  pthread_mutex_unlock( &queue->mtx ), /* continue */  );
-	
-}
-
-/* Delete a queue. It must be empty. */ 
-int fd_fifo_del ( struct fifo  ** queue )
-{
-	struct fifo * q;
-	int loops = 0;
-	
-	TRACE_ENTRY( "%p", queue );
-
-	CHECK_PARAMS( queue && CHECK_FIFO( *queue ) );
-	
-	q = *queue;
-	
-	CHECK_POSIX(  pthread_mutex_lock( &q->mtx )  );
-	
-	if ((q->count != 0) || (q->data != NULL)) {
-		TRACE_DEBUG(INFO, "The queue cannot be destroyed (%d, %p)", q->count, q->data);
-		CHECK_POSIX_DO(  pthread_mutex_unlock( &q->mtx ), /* no fallback */  );
-		return EINVAL;
-	}
-	
-	/* Ok, now invalidate the queue */
-	q->eyec = 0xdead;
-	
-	/* Have all waiting threads return an error */
-	while (q->thrs) {
-		CHECK_POSIX(  pthread_mutex_unlock( &q->mtx ));
-		CHECK_POSIX(  pthread_cond_signal(&q->cond)  );
-		sched_yield();
-		if (loops >= 10)
-			/* sleep for a few milliseconds */
-			usleep(50000);
-		
-		CHECK_POSIX(  pthread_mutex_lock( &q->mtx )  );
-		ASSERT( ++loops < 20 ); /* detect infinite loops */
-	}
-	
-	/* sanity check */
-	ASSERT(FD_IS_LIST_EMPTY(&q->list));
-	
-	/* And destroy it */
-	CHECK_POSIX(  pthread_mutex_unlock( &q->mtx )  );
-	
-	CHECK_POSIX_DO(  pthread_cond_destroy( &q->cond ),  );
-	
-	CHECK_POSIX_DO(  pthread_mutex_destroy( &q->mtx ),  );
-	
-	free(q);
-	*queue = NULL;
-	
-	return 0;
-}
-
-/* Move the content of old into new, and update loc_update atomically. We leave the old queue empty but valid */
-int fd_fifo_move ( struct fifo * old, struct fifo * new, struct fifo ** loc_update )
-{
-	int loops = 0;
-	
-	TRACE_ENTRY("%p %p %p", old, new, loc_update);
-	CHECK_PARAMS( CHECK_FIFO( old ) && CHECK_FIFO( new ));
-	
-	CHECK_PARAMS( ! old->data );
-	if (new->high) {
-		TODO("Implement support for thresholds in fd_fifo_move...");
-	}
-	
-	/* Update loc_update */
-	if (loc_update)
-		*loc_update = new;
-	
-	/* Lock the queues */
-	CHECK_POSIX(  pthread_mutex_lock( &old->mtx )  );
-	CHECK_POSIX(  pthread_mutex_lock( &new->mtx )  );
-	
-	/* Any waiting thread on the old queue returns an error */
-	old->eyec = 0xdead;
-	while (old->thrs) {
-		CHECK_POSIX(  pthread_mutex_unlock( &old->mtx ));
-		CHECK_POSIX(  pthread_cond_signal(&old->cond)  );
-		sched_yield();
-		if (loops >= 10)
-			/* sleep for a few milliseconds */
-			usleep(50000);
-		
-		CHECK_POSIX(  pthread_mutex_lock( &old->mtx )  );
-		ASSERT( ++loops < 20 ); /* detect infinite loops */
-	}
-	
-	/* Move all data from old to new */
-	fd_list_move_end( &new->list, &old->list );
-	if (old->count && (!new->count)) {
-		CHECK_POSIX(  pthread_cond_signal(&new->cond)  );
-	}
-	new->count += old->count;
-	
-	/* Reset old */
-	old->count = 0;
-	old->eyec = FIFO_EYEC;
-	
-	/* Unlock, we're done */
-	CHECK_POSIX(  pthread_mutex_unlock( &new->mtx )  );
-	CHECK_POSIX(  pthread_mutex_unlock( &old->mtx )  );
-	
-	return 0;
-}
-
-/* Get the length of the queue */
-int fd_fifo_length ( struct fifo * queue, int * length )
-{
-	TRACE_ENTRY( "%p %p", queue, length );
-	
-	/* Check the parameters */
-	CHECK_PARAMS( CHECK_FIFO( queue ) && length );
-	
-	/* lock the queue */
-	CHECK_POSIX(  pthread_mutex_lock( &queue->mtx )  );
-	
-	/* Retrieve the count */
-	*length = queue->count;
-	
-	/* Unlock */
-	CHECK_POSIX(  pthread_mutex_unlock( &queue->mtx )  );
-	
-	/* Done */
-	return 0;
-}
-
-/* alternate version with no error checking */
-int fd_fifo_length_noerr ( struct fifo * queue )
-{
-	if ( !CHECK_FIFO( queue ) )
-		return 0;
-	
-	return queue->count; /* Let's hope it's read atomically, since we are not locking... */
-}
-
-/* Set the thresholds of the queue */
-int fd_fifo_setthrhd ( struct fifo * queue, void * data, uint16_t high, void (*h_cb)(struct fifo *, void **), uint16_t low, void (*l_cb)(struct fifo *, void **) )
-{
-	TRACE_ENTRY( "%p %p %hu %p %hu %p", queue, data, high, h_cb, low, l_cb );
-	
-	/* Check the parameters */
-	CHECK_PARAMS( CHECK_FIFO( queue ) && (high > low) && (queue->data == NULL) );
-	
-	/* lock the queue */
-	CHECK_POSIX(  pthread_mutex_lock( &queue->mtx )  );
-	
-	/* Save the values */
-	queue->high = high;
-	queue->low  = low;
-	queue->data = data;
-	queue->h_cb = h_cb;
-	queue->l_cb = l_cb;
-	
-	/* Unlock */
-	CHECK_POSIX(  pthread_mutex_unlock( &queue->mtx )  );
-	
-	/* Done */
-	return 0;
-}
-
-/* Post a new item in the queue */
-int fd_fifo_post_int ( struct fifo * queue, void ** item )
-{
-	struct fd_list * new;
-	int call_cb = 0;
-	
-	TRACE_ENTRY( "%p %p", queue, item );
-	
-	/* Check the parameters */
-	CHECK_PARAMS( CHECK_FIFO( queue ) && item && *item );
-	
-	/* Create a new list item */
-	CHECK_MALLOC(  new = malloc (sizeof (struct fd_list))  );
-	
-	fd_list_init(new, *item);
-	*item = NULL;
-	
-	/* lock the queue */
-	CHECK_POSIX(  pthread_mutex_lock( &queue->mtx )  );
-	
-	/* Add the new item at the end */
-	fd_list_insert_before( &queue->list, new);
-	queue->count++;
-	if (queue->highest_ever < queue->count)
-		queue->highest_ever = queue->count;
-	if (queue->high && ((queue->count % queue->high) == 0)) {
-		call_cb = 1;
-		queue->highest = queue->count;
-	}
-	
-	/* Signal if threads are asleep */
-	if (queue->thrs > 0) {
-		CHECK_POSIX(  pthread_cond_signal(&queue->cond)  );
-	}
-	
-	/* Unlock */
-	CHECK_POSIX(  pthread_mutex_unlock( &queue->mtx )  );
-	
-	/* Call high-watermark cb as needed */
-	if (call_cb && queue->h_cb)
-		(*queue->h_cb)(queue, &queue->data);
-	
-	/* Done */
-	return 0;
-}
-
-/* Pop the first item from the queue */
-static void * mq_pop(struct fifo * queue)
-{
-	void * ret = NULL;
-	struct fd_list * li;
-	
-	ASSERT( ! FD_IS_LIST_EMPTY(&queue->list) );
-	
-	fd_list_unlink(li = queue->list.next);
-	queue->count--;
-	ret = li->o;
-	free(li);
-	
-	return ret;
-}
-
-/* Check if the low watermark callback must be called. */
-static __inline__ int test_l_cb(struct fifo * queue)
-{
-	if ((queue->high == 0) || (queue->low == 0) || (queue->l_cb == 0))
-		return 0;
-	
-	if (((queue->count % queue->high) == queue->low) && (queue->highest > queue->count)) {
-		queue->highest -= queue->high;
-		return 1;
-	}
-	
-	return 0;
-}
-
-/* Try poping an item */
-int fd_fifo_tryget_int ( struct fifo * queue, void ** item )
-{
-	int wouldblock = 0;
-	int call_cb = 0;
-	
-	TRACE_ENTRY( "%p %p", queue, item );
-	
-	/* Check the parameters */
-	CHECK_PARAMS( CHECK_FIFO( queue ) && item );
-	
-	/* lock the queue */
-	CHECK_POSIX(  pthread_mutex_lock( &queue->mtx )  );
-	
-	/* Check queue status */
-	if (queue->count > 0) {
-		/* There are elements in the queue, so pick the first one */
-		*item = mq_pop(queue);
-		call_cb = test_l_cb(queue);
-	} else {
-		wouldblock = 1;
-		*item = NULL;
-	}
-		
-	/* Unlock */
-	CHECK_POSIX(  pthread_mutex_unlock( &queue->mtx )  );
-	
-	/* Call low watermark callback as needed */
-	if (call_cb)
-		(*queue->l_cb)(queue, &queue->data);
-	
-	/* Done */
-	return wouldblock ? EWOULDBLOCK : 0;
-}
-
-/* This handler is called when a thread is blocked on a queue, and cancelled */
-static void fifo_cleanup(void * queue)
-{
-	struct fifo * q = (struct fifo *)queue;
-	TRACE_ENTRY( "%p", queue );
-	
-	/* The thread has been cancelled, therefore it does not wait on the queue anymore */
-	q->thrs--;
-	
-	/* Now unlock the queue, and we're done */
-	CHECK_POSIX_DO(  pthread_mutex_unlock( &q->mtx ),  /* nothing */  );
-	
-	/* End of cleanup handler */
-	return;
-}
-
-/* The internal function for fd_fifo_timedget and fd_fifo_get */
-static int fifo_tget ( struct fifo * queue, void ** item, int istimed, const struct timespec *abstime)
-{
-	int timedout = 0;
-	int call_cb = 0;
-	
-	/* Check the parameters */
-	CHECK_PARAMS( CHECK_FIFO( queue ) && item && (abstime || !istimed) );
-	
-	/* Initialize the return value */
-	*item = NULL;
-	
-	/* lock the queue */
-	CHECK_POSIX(  pthread_mutex_lock( &queue->mtx )  );
-	
-awaken:
-	/* Check queue status */
-	if (!CHECK_FIFO( queue )) {
-		/* The queue is being destroyed */
-		CHECK_POSIX(  pthread_mutex_unlock( &queue->mtx )  );
-		TRACE_DEBUG(FULL, "The queue is being destroyed -> EPIPE");
-		return EPIPE;
-	}
-		
-	if (queue->count > 0) {
-		/* There are items in the queue, so pick the first one */
-		*item = mq_pop(queue);
-		call_cb = test_l_cb(queue);
-	} else {
-		int ret = 0;
-		/* We have to wait for a new item */
-		queue->thrs++ ;
-		pthread_cleanup_push( fifo_cleanup, queue);
-		if (istimed) {
-			ret = pthread_cond_timedwait( &queue->cond, &queue->mtx, abstime );
-		} else {
-			ret = pthread_cond_wait( &queue->cond, &queue->mtx );
-		}
-		pthread_cleanup_pop(0);
-		queue->thrs-- ;
-		if (ret == 0)
-			goto awaken;  /* test for spurious wake-ups */
-		
-		if (istimed && (ret == ETIMEDOUT)) {
-			timedout = 1;
-		} else {
-			/* Unexpected error condition (means we need to debug) */
-			ASSERT( ret == 0 /* never true */ );
-		}
-	}
-	
-	/* Unlock */
-	CHECK_POSIX(  pthread_mutex_unlock( &queue->mtx )  );
-	
-	/* Call low watermark callback as needed */
-	if (call_cb)
-		(*queue->l_cb)(queue, &queue->data);
-	
-	/* Done */
-	return timedout ? ETIMEDOUT : 0;
-}
-
-/* Get the next available item, block until there is one */
-int fd_fifo_get_int ( struct fifo * queue, void ** item )
-{
-	TRACE_ENTRY( "%p %p", queue, item );
-	return fifo_tget(queue, item, 0, NULL);
-}
-
-/* Get the next available item, block until there is one, or the timeout expires */
-int fd_fifo_timedget_int ( struct fifo * queue, void ** item, const struct timespec *abstime )
-{
-	TRACE_ENTRY( "%p %p %p", queue, item, abstime );
-	return fifo_tget(queue, item, 1, abstime);
-}
-
Index: bfreeDiameter/init.c
===================================================================
--- libfreeDiameter/init.c	(revision 454)
+++ 	(revision )
@@ -1,70 +1,0 @@
-/*********************************************************************************************************
-* Software License Agreement (BSD License)                                                               *
-* Author: Sebastien Decugis <sdecugis@nict.go.jp>							 *
-*													 *
-* Copyright (c) 2010, 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 "libfD.h"
-
-/* Only for CPU cache flush */
-pthread_mutex_t fd_cpu_mtx_dummy = PTHREAD_MUTEX_INITIALIZER;
-
-/* Initialize library variables and threads */
-int fd_lib_init(int support_signals)
-{
-	int ret = 0;
-	
-	/* Create the thread key that contains thread name for debug messages */
-	ret = pthread_key_create(&fd_log_thname, free);
-	if (ret != 0) {
-		fprintf(stderr, "Error initializing the libfreeDiameter library: %s\n", strerror(ret) );
-		return ret;
-	}
-	
-	/* Initialize signals if requested */
-	if (support_signals) {
-		CHECK_FCT( fd_sig_init() );
-	}
-	
-	/* Initialize the modules that need it */
-	fd_msg_eteid_init();
-	CHECK_FCT( fd_sess_init() );
-	
-	return 0;
-}
-
-/* Stop all threads created in the library */
-void fd_lib_fini(void)
-{
-	fd_sess_fini();
-	fd_sig_fini();
-}
Index: bfreeDiameter/libfD.h
===================================================================
--- libfreeDiameter/libfD.h	(revision 291)
+++ 	(revision )
@@ -1,67 +1,0 @@
-/*********************************************************************************************************
-* Software License Agreement (BSD License)                                                               *
-* Author: Sebastien Decugis <sdecugis@nict.go.jp>							 *
-*													 *
-* Copyright (c) 2010, 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 definitions for internal use in the libfreeDiameter library */
-
-#ifndef _LIBFD_H
-#define _LIBFD_H
-
-#include <freeDiameter/freeDiameter-host.h>
-#include <freeDiameter/libfreeDiameter.h>
-
-/* Internal to the library */
-extern const char * type_base_name[];
-void fd_msg_eteid_init(void);
-int fd_sess_init(void);
-void fd_sess_fini(void);
-int fd_sig_init(void);
-void fd_sig_fini(void);
-
-/* Iterator on the rules of a parent object */
-int fd_dict_iterate_rules ( struct dict_object *parent, void * data, int (*cb)(void *, struct dict_rule_data *) );
-
-/* Dispatch / messages / dictionary API */
-int fd_dict_disp_cb(enum dict_object_type type, struct dict_object *obj, struct fd_list ** cb_list);
-void fd_dict_dump_avp_value(union avp_value *avp_value, struct dict_object * model, int indent);
-int fd_disp_call_cb_int( struct fd_list * cb_list, struct msg ** msg, struct avp *avp, struct session *sess, enum disp_action *action, 
-			struct dict_object * obj_app, struct dict_object * obj_cmd, struct dict_object * obj_avp, struct dict_object * obj_enu);
-extern pthread_rwlock_t fd_disp_lock;
-
-/* Messages / sessions API */
-int fd_sess_fromsid_msg ( unsigned char * sid, size_t len, struct session ** session, int * new);
-int fd_sess_ref_msg ( struct session * session );
-int fd_sess_reclaim_msg ( struct session ** session );
-
-#endif /* _LIBFD_H */
Index: bfreeDiameter/lists.c
===================================================================
--- libfreeDiameter/lists.c	(revision 83)
+++ 	(revision )
@@ -1,290 +1,0 @@
-/*********************************************************************************************************
-* Software License Agreement (BSD License)                                                               *
-* Author: Sebastien Decugis <sdecugis@nict.go.jp>							 *
-*													 *
-* Copyright (c) 2009, WIDE Project and NICT								 *
-* All rights reserved.											 *
-* 													 *
-* Redistribution and use of this software in source and binary forms, with or without modification, are  *
-* permitted provided that the following conditions are met:						 *
-* 													 *
-* * Redistributions of source code must retain the above 						 *
-*   copyright notice, this list of conditions and the 							 *
-*   following disclaimer.										 *
-*    													 *
-* * Redistributions in binary form must reproduce the above 						 *
-*   copyright notice, this list of conditions and the 							 *
-*   following disclaimer in the documentation and/or other						 *
-*   materials provided with the distribution.								 *
-* 													 *
-* * Neither the name of the WIDE Project or NICT nor the 						 *
-*   names of its contributors may be used to endorse or 						 *
-*   promote products derived from this software without 						 *
-*   specific prior written permission of WIDE Project and 						 *
-*   NICT.												 *
-* 													 *
-* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED *
-* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
-* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR *
-* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 	 *
-* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 	 *
-* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR *
-* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF   *
-* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.								 *
-*********************************************************************************************************/
-
-#include "libfD.h"
-
-/* Initialize a list element */
-void fd_list_init ( struct fd_list * list, void * obj )
-{
-	memset(list, 0, sizeof(struct fd_list));
-	list->next = list;
-	list->prev = list;
-	list->head = list;
-	list->o    = obj;
-}
-
-#define CHECK_SINGLE( li ) {			\
-	ASSERT( ((struct fd_list *)(li))->next == (li) );	\
-	ASSERT( ((struct fd_list *)(li))->prev == (li) );	\
-	ASSERT( ((struct fd_list *)(li))->head == (li) );	\
-}
-
-/* insert after a reference, checks done */
-static void list_insert_after( struct fd_list * ref, struct fd_list * item )
-{
-	item->prev = ref;
-	item->next = ref->next;
-	item->head = ref->head;
-	ref->next->prev = item;
-	ref->next = item;
-}
-
-/* insert after a reference */
-void fd_list_insert_after  ( struct fd_list * ref, struct fd_list * item )
-{
-	ASSERT(item != NULL);
-	ASSERT(ref != NULL);
-	CHECK_SINGLE ( item );
-	ASSERT(ref->head != item);
-	list_insert_after(ref, item);
-}
-
-/* Move all elements of list senti at the end of list ref */
-void fd_list_move_end(struct fd_list * ref, struct fd_list * senti)
-{
-	ASSERT(ref->head == ref);
-	ASSERT(senti->head == senti);
-	
-	if (senti->next == senti)
-		return;
-	
-	senti->next->prev = ref->prev;
-	ref->prev->next   = senti->next;
-	senti->prev->next = ref;
-	ref->prev         = senti->prev;
-	senti->prev = senti;
-	senti->next = senti;
-}
-
-/* insert before a reference, checks done */
-static void list_insert_before ( struct fd_list * ref, struct fd_list * item )
-{
-	item->prev = ref->prev;
-	item->next = ref;
-	item->head = ref->head;
-	ref->prev->next = item;
-	ref->prev = item;
-}
-
-/* insert before a reference */
-void fd_list_insert_before ( struct fd_list * ref, struct fd_list * item )
-{
-	ASSERT(item != NULL);
-	ASSERT(ref != NULL);
-	CHECK_SINGLE ( item );
-	ASSERT(ref->head != item);
-	list_insert_before(ref, item);
-}
-
-/* Insert an item in an ordered list -- ordering function provided. If duplicate object found, it is returned in ref_duplicate */
-int fd_list_insert_ordered( struct fd_list * head, struct fd_list * item, int (*cmp_fct)(void *, void *), void ** ref_duplicate)
-{
-	struct fd_list * ptr = head;
-	int cmp;
-	
-	/* Some debug sanity checks */
-	ASSERT(head != NULL);
-	ASSERT(item != NULL);
-	ASSERT(cmp_fct != NULL);
-	ASSERT(head->head == head);
-	CHECK_SINGLE ( item );
-	
-	/* loop in the list */
-	while (ptr->next != head)
-	{
-		/* Compare the object to insert with the next object in list */
-		cmp = cmp_fct( item->o, ptr->next->o );
-		if (!cmp) {
-			/* An element with the same key already exists */
-			if (ref_duplicate != NULL)
-				*ref_duplicate = ptr->next->o;
-			return EEXIST;
-		}
-		
-		if (cmp < 0)
-			break; /* We must insert the element here */
-		
-		ptr = ptr->next;
-	}
-	
-	/* Now insert the element between ptr and ptr->next */
-	list_insert_after( ptr, item );
-	
-	/* Ok */
-	return 0;
-}
-	
-/* Unlink an object */
-void fd_list_unlink ( struct fd_list * item )
-{
-	ASSERT(item != NULL);
-	if (item->head == item)
-		return;
-	/* unlink */
-	item->next->prev = item->prev;
-	item->prev->next = item->next;
-	/* sanitize */
-	item->next = item;
-	item->prev = item;
-	item->head = item;
-}
-
-
-/********************************************************************************************************/
-/* Hash function -- credits to Austin Appleby, thank you ^^ */
-/* See http://murmurhash.googlepages.com for more information on this function */
-
-/* the strings are NOT always aligned properly (ex: received in RADIUS message), so we use the aligned MurmurHash2 function as needed */
-#define _HASH_MIX(h,k,m) { k *= m; k ^= k >> r; k *= m; h *= m; h ^= k; }
-uint32_t fd_hash ( char * string, size_t len )
-{
-	uint32_t hash = len;
-	char * data = string;
-	
-	const unsigned int m = 0x5bd1e995;
-	const int r = 24;
-	int align = (long)string & 3;
-	
-	if (!align || (len < 4)) {
-		
-		/* In case data is aligned, MurmurHash2 function */
-		while(len >= 4)
-		{
-			/* Mix 4 bytes at a time into the hash */
-			uint32_t k = *(uint32_t *)data;	/* We don't care about the byte order */
-
-			_HASH_MIX(hash, k, m);
-
-			data += 4;
-			len -= 4;
-		}
-
-		/* Handle the last few bytes of the input */
-		switch(len) {
-			case 3: hash ^= data[2] << 16;
-			case 2: hash ^= data[1] << 8;
-			case 1: hash ^= data[0];
-	        		hash *= m;
-		}
-		
-	} else {
-		/* Unaligned data, use alignment-safe slower version */
-		
-		/* Pre-load the temp registers */
-		uint32_t t = 0, d = 0;
-		switch(align)
-		{
-			case 1: t |= data[2] << 16;
-			case 2: t |= data[1] << 8;
-			case 3: t |= data[0];
-		}
-		t <<= (8 * align);
-
-		data += 4-align;
-		len -= 4-align;
-		
-		/* From this point, "data" can be read by chunks of 4 bytes */
-		
-		int sl = 8 * (4-align);
-		int sr = 8 * align;
-
-		/* Mix */
-		while(len >= 4)
-		{
-			uint32_t k;
-			
-			d = *(unsigned int *)data;
-			k = (t >> sr) | (d << sl);
-
-			_HASH_MIX(hash, k, m);
-
-			t = d;
-
-			data += 4;
-			len -= 4;
-		}
-
-		/* Handle leftover data in temp registers */
-		d = 0;
-		if(len >= align)
-		{
-			uint32_t k;
-			
-			switch(align)
-			{
-			case 3: d |= data[2] << 16;
-			case 2: d |= data[1] << 8;
-			case 1: d |= data[0];
-			}
-
-			k = (t >> sr) | (d << sl);
-			_HASH_MIX(hash, k, m);
-
-			data += align;
-			len -= align;
-
-			/* Handle tail bytes */
-
-			switch(len)
-			{
-			case 3: hash ^= data[2] << 16;
-			case 2: hash ^= data[1] << 8;
-			case 1: hash ^= data[0];
-					hash *= m;
-			};
-		}
-		else
-		{
-			switch(len)
-			{
-			case 3: d |= data[2] << 16;
-			case 2: d |= data[1] << 8;
-			case 1: d |= data[0];
-			case 0: hash ^= (t >> sr) | (d << sl);
-					hash *= m;
-			}
-		}
-
-
-	}
-
-	/* Do a few final mixes of the hash to ensure the last few
-	   bytes are well-incorporated. */
-	hash ^= hash >> 13;
-	hash *= m;
-	hash ^= hash >> 15;
-
-	return hash;
-} 
Index: bfreeDiameter/log.c
===================================================================
--- libfreeDiameter/log.c	(revision 653)
+++ 	(revision )
@@ -1,125 +1,0 @@
-/*********************************************************************************************************
-* Software License Agreement (BSD License)                                                               *
-* Author: Sebastien Decugis <sdecugis@nict.go.jp>							 *
-*													 *
-* Copyright (c) 2010, 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 "libfD.h"
-
-#include <stdarg.h>
-
-pthread_mutex_t fd_log_lock = PTHREAD_MUTEX_INITIALIZER;
-pthread_key_t	fd_log_thname;
-int fd_g_debug_lvl = 0;
-
-/* These may be used to pass specific debug requests via the command-line parameters */
-char * fd_debug_one_function = NULL;
-char * fd_debug_one_file = NULL;
-
-/* Useless function, only to ease setting up a breakpoint in gdb (break fd_breakhere) -- use TRACE_HERE */
-int fd_breaks = 0;
-int fd_breakhere(void) { return ++fd_breaks; }
-
-static void fd_cleanup_mutex_silent( void * mutex )
-{
-	(void)pthread_mutex_unlock((pthread_mutex_t *)mutex);
-}
-
-
-/* Log a debug message */
-void fd_log_debug ( const char * format, ... )
-{
-	va_list ap;
-	
-	(void)pthread_mutex_lock(&fd_log_lock);
-	
-	pthread_cleanup_push(fd_cleanup_mutex_silent, &fd_log_lock);
-	
-	va_start(ap, format);
-	vfprintf( stdout, format, ap);
-	va_end(ap);
-	fflush(stdout);
-
-	pthread_cleanup_pop(0);
-	
-	(void)pthread_mutex_unlock(&fd_log_lock);
-}
-
-/* Function to set the thread's friendly name */
-void fd_log_threadname ( char * name )
-{
-	void * val = NULL;
-	
-	TRACE_ENTRY("%p(%s)", name, name?:"/");
-	
-	/* First, check if a value is already assigned to the current thread */
-	val = pthread_getspecific(fd_log_thname);
-	if (val != NULL) {
-		TRACE_DEBUG(FULL, "Freeing old thread name: %s", val);
-		free(val);
-	}
-	
-	/* Now create the new string */
-	if (name == NULL) {
-		CHECK_POSIX_DO( pthread_setspecific(fd_log_thname, NULL), /* continue */);
-		return;
-	}
-	
-	CHECK_MALLOC_DO( val = strdup(name), return );
-	
-	CHECK_POSIX_DO( pthread_setspecific(fd_log_thname, val), /* continue */);
-	return;
-}
-
-/* Write time into a buffer */
-char * fd_log_time ( struct timespec * ts, char * buf, size_t len )
-{
-	int ret;
-	size_t offset = 0;
-	struct timespec tp;
-	struct tm tm;
-	
-	/* Get current time */
-	if (!ts) {
-		ret = clock_gettime(CLOCK_REALTIME, &tp);
-		if (ret != 0) {
-			snprintf(buf, len, "%s", strerror(ret));
-			return buf;
-		}
-		ts = &tp;
-	}
-	
-	offset += strftime(buf + offset, len - offset, "%D,%T", localtime_r( &ts->tv_sec , &tm ));
-	offset += snprintf(buf + offset, len - offset, ".%6.6ld", ts->tv_nsec / 1000);
-
-	return buf;
-}
Index: bfreeDiameter/messages.c
===================================================================
--- libfreeDiameter/messages.c	(revision 656)
+++ 	(revision )
@@ -1,2310 +1,0 @@
-/*********************************************************************************************************
-* Software License Agreement (BSD License)                                                               *
-* Author: Sebastien Decugis <sdecugis@nict.go.jp>							 *
-*													 *
-* 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.								 *
-*********************************************************************************************************/
-
-/* Messages module.
- * 
- * This module allows to manipulate the msg and avp structures that represents a Diameter message in memory.
- */
-
-#include "libfD.h"
-
-#include <sys/param.h>
-
-/* Type of object */
-enum msg_objtype {
-	MSG_MSG = 1, 
-	MSG_AVP
-};
-
-/* Chaining of elements as a free hierarchy */
-struct msg_avp_chain {
-	struct fd_list		chaining;	/* Chaining information at this level. */
-	struct fd_list		children;	/* sentinel for the children of this object */
-	enum msg_objtype 	type;		/* Type of this object, _MSG_MSG or _MSG_AVP */
-};
-
-/* Return the chain information from an AVP or MSG. Since it's the first field, we just cast */
-#define _C(_x) ((struct msg_avp_chain *)(_x))
-
-/* Some details about chaining:
- *
- *  A message is made of a header ( msg ) and 0 or more AVPs ( avp ).
- * The structure is a kind of tree, where some AVPs (grouped AVPs) can contain other AVPs.
- * Exemple:
- * msg
- *  |-avp
- *  |-gavp
- *  |   |-avp
- *  |   |-avp
- *  |   \-avp
- *  |-avp
- *  \-avp
- *
- * Each item (msg or avp) structure begins with a msg_avp_chain structure.
- * The element at the top of the hierarchy (msg in our example) has all the fields of its "chaining" equal to the same value.
- *
- * All elements at the same level are linked by their "chaining" list.
- * The "children" list is the sentinel for the lists of children of this element.
- */
-
-/* The following definitions are used to recognize objects in memory. */
-#define MSG_MSG_EYEC	(0x11355463)
-#define MSG_AVP_EYEC	(0x11355467)
-
-/* The following structure represents an AVP instance. */
-struct avp {
-	struct msg_avp_chain	 avp_chain;		/* Chaining information of this AVP */
-	int			 avp_eyec;		/* Must be equal to MSG_AVP_EYEC */
-	struct dict_object	*avp_model;		/* If not NULL, pointer to the dictionary object of this avp */
-	struct avp_hdr		 avp_public;		/* AVP data that can be managed by other modules */
-	
-	uint8_t			*avp_source;		/* If the message was parsed from a buffer, pointer to the AVP data start in the buffer. */
-	uint8_t			*avp_rawdata;		/* when the data can not be interpreted, the raw data is copied here. The header is not part of it. */
-	size_t			 avp_rawlen;		/* The length of the raw buffer. */
-	union avp_value		 avp_storage;		/* To avoid many alloc/free, store the integer values here and set avp_public.avp_data to &storage */
-	int			 avp_mustfreeos;	/* 1 if an octetstring is malloc'd in avp_storage and must be freed. */
-};
-
-/* Macro to compute the AVP header size */
-#define AVPHDRSZ_NOVEND	8
-#define AVPHDRSZ_VENDOR	12
-#define GETAVPHDRSZ( _flag ) ((_flag & AVP_FLAG_VENDOR) ? AVPHDRSZ_VENDOR : AVPHDRSZ_NOVEND)
-
-/* Macro to cast a msg_avp_t */
-#define _A(_x) ((struct avp *)(_x))
-/* Check the type and eyecatcher */
-#define CHECK_AVP(_x) ((_C(_x)->type == MSG_AVP) && (_A(_x)->avp_eyec == MSG_AVP_EYEC))
-
-/* The following structure represents an instance of a message (command and children AVPs). */
-struct msg {
-	struct msg_avp_chain	 msg_chain;		/* List of the AVPs in the message */
-	int			 msg_eyec;		/* Must be equal to MSG_MSG_EYEC */
-	struct dict_object	*msg_model;		/* If not NULL, pointer to the dictionary object of this message */
-	struct msg_hdr		 msg_public;		/* Message data that can be managed by extensions. */
-	
-	uint8_t			*msg_rawbuffer;		/* data buffer that was received, saved during fd_msg_parse_buffer and freed in fd_msg_parse_dict */
-	int			 msg_routable;		/* Is this a routable message? (0: undef, 1: routable, 2: non routable) */
-	struct msg		*msg_query;		/* the associated query if the message is a received answer */
-	int			 msg_associated;	/* and the counter part information in the query, to avoid double free */
-	struct rt_data		*msg_rtdata;		/* Routing list for the query */
-	struct session		*msg_sess;		/* Cached message session if any */
-	struct {
-			void (*fct)(void *, struct msg **);
-			void * data;
-			struct timespec timeout;
-		}		 msg_cb;		/* Callback to be called when an answer is received, if not NULL */
-	char *			 msg_src_id;		/* Diameter Id of the peer this message was received from. This string is malloc'd and must be freed */
-};
-
-/* Macro to compute the message header size */
-#define GETMSGHDRSZ() 	20
-
-/* Macro to cast a msg_avp_t */
-#define _M(_x) ((struct msg *)(_x))
-/* Check the type and eyecatcher */
-#define CHECK_MSG(_x) ((_C(_x)->type == MSG_MSG) && (_M(_x)->msg_eyec == MSG_MSG_EYEC))
-
-#define VALIDATE_OBJ(_x) ( (CHECK_MSG(_x)) || (CHECK_AVP(_x)) )
-
-
-/* Macro to validate a MSGFL_ value */
-#define CHECK_AVPFL(_fl) ( ((_fl) & (- (AVPFL_MAX << 1) )) == 0 )
-#define CHECK_MSGFL(_fl) ( ((_fl) & (- (MSGFL_MAX << 1) )) == 0 )
-
-
-/* initial sizes of AVP from their types, in bytes. */
-static int avp_value_sizes[] = { 
-	 0,	/* AVP_TYPE_GROUPED: size is dynamic */
-	 0,	/* AVP_TYPE_OCTETSTRING: size is dynamic */
-	 4,	/* AVP_TYPE_INTEGER32: size is 32 bits */
-	 8,	/* AVP_TYPE_INTEGER64: size is 64 bits */
-	 4,	/* AVP_TYPE_UNSIGNED32: size is 32 bits */
-	 8,	/* AVP_TYPE_UNSIGNED64: size is 64 bits */
-	 4,	/* AVP_TYPE_FLOAT32: size is 32 bits */
-	 8	/* AVP_TYPE_FLOAT64: size is 64 bits */
-};
-#define CHECK_BASETYPE( _type ) ( ((_type) <= AVP_TYPE_MAX) && ((_type) >= 0) )
-#define GETINITIALSIZE( _type, _vend ) (avp_value_sizes[ CHECK_BASETYPE(_type) ? (_type) : 0] + GETAVPHDRSZ(_vend))
-
-/* Forward declaration */
-static int parsedict_do_msg(struct dictionary * dict, struct msg * msg, int only_hdr, struct fd_pei *error_info);
-
-/***************************************************************************************************************/
-/* Creating objects */
-
-/* Initialize a msg_avp_chain structure */
-static void init_chain(struct msg_avp_chain * chain, int type)
-{
-	fd_list_init( &chain->chaining, (void *)chain);
-	fd_list_init( &chain->children, (void *)chain);
-	chain->type = type;
-}
-
-/* Initialize a new AVP object */
-static void init_avp ( struct avp * avp )
-{
-	TRACE_ENTRY("%p", avp);
-	
-	memset(avp, 0, sizeof(struct avp));
-	init_chain( &avp->avp_chain, MSG_AVP);
-	avp->avp_eyec = MSG_AVP_EYEC;
-}
-	
-/* Initialize a new MSG object */
-static void init_msg ( struct msg * msg )
-{
-	TRACE_ENTRY("%p", msg);
-	
-	memset(msg, 0, sizeof(struct msg));
-	init_chain( &msg->msg_chain, MSG_MSG);
-	msg->msg_eyec = MSG_MSG_EYEC;
-}
-
-
-/* Create a new AVP instance */
-int fd_msg_avp_new ( struct dict_object * model, int flags, struct avp ** avp )
-{
-	struct avp *new = NULL;
-	
-	TRACE_ENTRY("%p %x %p", model, flags, avp);
-	
-	/* Check the parameters */
-	CHECK_PARAMS(  avp && CHECK_AVPFL(flags)  );
-	
-	if (model) {
-		enum dict_object_type 	 dicttype;
-		CHECK_PARAMS( (fd_dict_gettype(model, &dicttype) == 0) && (dicttype == DICT_AVP) );
-	}
-	
-	/* Create a new object */
-	CHECK_MALLOC(  new = malloc (sizeof(struct avp))  );
-	
-	/* Initialize the fields */
-	init_avp(new);
-	
-	if (model) {
-		struct dict_avp_data dictdata;
-		
-		CHECK_FCT(  fd_dict_getval(model, &dictdata)  );
-	
-		new->avp_model = model;
-		new->avp_public.avp_code    = dictdata.avp_code;
-		new->avp_public.avp_flags   = dictdata.avp_flag_val;
-		new->avp_public.avp_len = GETINITIALSIZE(dictdata.avp_basetype, dictdata.avp_flag_val );
-		new->avp_public.avp_vendor  = dictdata.avp_vendor;
-	}
-	
-	if (flags & AVPFL_SET_BLANK_VALUE) {
-		new->avp_public.avp_value = &new->avp_storage;
-	}
-	
-	/* The new object is ready, return */
-	*avp = new;
-	return 0;
-}
-
-/* Create a new message instance */
-int fd_msg_new ( struct dict_object * model, int flags, struct msg ** msg )
-{
-	struct msg * new = NULL;
-	
-	TRACE_ENTRY("%p %x %p", model, flags, msg);
-	
-	/* Check the parameters */
-	CHECK_PARAMS(  msg && CHECK_MSGFL(flags)  );
-	
-	if (model) {
-		enum dict_object_type 	 dicttype;
-		CHECK_PARAMS( (fd_dict_gettype(model, &dicttype) == 0) && (dicttype == DICT_COMMAND) );
-	}
-	
-	/* Create a new object */
-	CHECK_MALLOC(  new = malloc (sizeof(struct msg))  );
-	
-	/* Initialize the fields */
-	init_msg(new);
-	new->msg_public.msg_version	= DIAMETER_VERSION;
-	new->msg_public.msg_length	= GETMSGHDRSZ(); /* This will be updated later */
-
-	if (model) {
-		struct dictionary 	*dict;
-		struct dict_cmd_data     dictdata;
-		struct dict_object     	*dictappl;
-		
-		CHECK_FCT( fd_dict_getdict(model, &dict) );
-		CHECK_FCT( fd_dict_getval(model, &dictdata)  );
-		
-		new->msg_model = model;
-		new->msg_public.msg_flags	= dictdata.cmd_flag_val;
-		new->msg_public.msg_code	= dictdata.cmd_code;
-
-		/* Initialize application from the parent, if any */
-		CHECK_FCT(  fd_dict_search( dict, DICT_APPLICATION, APPLICATION_OF_COMMAND, model, &dictappl, 0)  );
-		if (dictappl != NULL) {
-			struct dict_application_data appdata;
-			CHECK_FCT(  fd_dict_getval(dictappl, &appdata)  );
-			new->msg_public.msg_appl = appdata.application_id;
-		}
-	}
-	
-	if (flags & MSGFL_ALLOC_ETEID) {
-		new->msg_public.msg_eteid = fd_msg_eteid_get();
-	}
-	
-	/* The new object is ready, return */
-	*msg = new;
-	return 0;
-}	
-
-/* Create answer from a request */
-int fd_msg_new_answer_from_req ( struct dictionary * dict, struct msg ** msg, int flags )
-{
-	struct dict_object * model = NULL;
-	struct msg *qry, *ans;
-	struct session * sess = NULL;
-	
-	TRACE_ENTRY("%p %x", msg, flags);
-	
-	/* Check the parameters */
-	CHECK_PARAMS(  msg );
-	qry = *msg;
-	CHECK_PARAMS( CHECK_MSG(qry) && (qry->msg_public.msg_flags & CMD_FLAG_REQUEST) );
-	
-	if (! (flags & MSGFL_ANSW_NOSID)) {
-		/* Get the session of the message */
-		CHECK_FCT_DO( fd_msg_sess_get(dict, qry, &sess, NULL), /* ignore an error */ );
-	}
-	
-	/* Find the model for the answer */
-	if (flags & MSGFL_ANSW_ERROR) {
-		/* The model is the generic error format */
-		CHECK_FCT( fd_dict_get_error_cmd(dict, &model) );
-	} else {
-		/* The model is the answer corresponding to the query. It supposes that these are defined in the dictionary */
-		CHECK_FCT_DO(  parsedict_do_msg( dict, qry, 1, NULL), /* continue */  );
-		if (qry->msg_model) {
-			CHECK_FCT(  fd_dict_search ( dict, DICT_COMMAND, CMD_ANSWER, qry->msg_model, &model, EINVAL )  );
-		}
-	}
-	
-	/* Create the answer */
-	CHECK_FCT(  fd_msg_new( model, flags, &ans )  );
-	
-	/* Set informations in the answer as in the query */
-	ans->msg_public.msg_code = qry->msg_public.msg_code; /* useful for MSGFL_ANSW_ERROR */
-	ans->msg_public.msg_appl = qry->msg_public.msg_appl;
-	ans->msg_public.msg_eteid = qry->msg_public.msg_eteid;
-	ans->msg_public.msg_hbhid = qry->msg_public.msg_hbhid;
-	
-	/* Add the Session-Id AVP if session is known */
-	if (sess && dict) {
-		struct dict_object * sess_id_avp;
-		char * sid;
-		struct avp * avp;
-		union avp_value val;
-		
-		CHECK_FCT( fd_dict_search( dict, DICT_AVP, AVP_BY_NAME, "Session-Id", &sess_id_avp, ENOENT) );
-		CHECK_FCT( fd_sess_getsid ( sess, &sid ) );
-		CHECK_FCT( fd_msg_avp_new ( sess_id_avp, 0, &avp ) );
-		val.os.data = (unsigned char *)sid;
-		val.os.len  = strlen(sid);
-		CHECK_FCT( fd_msg_avp_setvalue( avp, &val ) );
-		CHECK_FCT( fd_msg_avp_add( ans, MSG_BRW_FIRST_CHILD, avp ) );
-		ans->msg_sess = sess;
-		CHECK_FCT( fd_sess_ref_msg(sess) );
-	}
-	
-	/* associate with query */
-	ans->msg_query = qry;
-	qry->msg_associated = 1;
-	
-	/* Done */
-	*msg = ans;
-	return 0;
-}
-
-/***************************************************************************************************************/
-
-/* Explore a message */
-int fd_msg_browse_internal ( msg_or_avp * reference, enum msg_brw_dir dir, msg_or_avp ** found, int * depth )
-{
-	struct msg_avp_chain *result = NULL;
-	int diff = 0;
-	struct fd_list *li = NULL;
-	
-	TRACE_ENTRY("%p %d %p %p", reference, dir, found, depth);
-	
-	/* Initialize the "found" result if any */
-	if (found)
-		*found = NULL;
-	
-	/* Check the parameters */
-	CHECK_PARAMS(  VALIDATE_OBJ(reference)  );
-	
-	TRACE_DEBUG(FCTS, "chaining(%p): nxt:%p prv:%p hea:%p top:%p", 
-			&_C(reference)->chaining,
-			_C(reference)->chaining.next,
-			_C(reference)->chaining.prev,
-			_C(reference)->chaining.head,
-			_C(reference)->chaining.o);
-	TRACE_DEBUG(FCTS, "children(%p): nxt:%p prv:%p hea:%p top:%p", 
-			&_C(reference)->children,
-			_C(reference)->children.next,
-			_C(reference)->children.prev,
-			_C(reference)->children.head,
-			_C(reference)->children.o);
-
-	/* Now search */
-	switch (dir) {
-		case MSG_BRW_NEXT:
-			/* Check the reference is an AVP */
-			CHECK_PARAMS(  _C(reference)->type == MSG_AVP  );
-
-			li = &_C(reference)->chaining;
-			
-			/* Check if the next element is not the sentinel ( ==> the parent) */
-			if (li->next != li->head)
-				result = _C(li->next->o);
-			break;
-
-		case MSG_BRW_PREV:
-			/* Check the reference is an AVP */
-			CHECK_PARAMS(  _C(reference)->type == MSG_AVP  );
-
-			li = &_C(reference)->chaining;
-			
-			/* Check if the prev element is not the sentinel ( ==> the parent) */
-			if (li->prev != li->head)
-				result = _C(li->prev->o);
-			break;
-
-		case MSG_BRW_FIRST_CHILD:
-			li = &_C(reference)->children;
-			if (! FD_IS_LIST_EMPTY(li)) {
-				result = _C(li->next->o);
-				diff = 1;
-			}
-			break;
-
-		case MSG_BRW_LAST_CHILD:
-			li = &_C(reference)->children;
-			if (! FD_IS_LIST_EMPTY(li)) {
-				result = _C(li->prev->o);
-				diff = 1;
-			}
-			break;
-
-		case MSG_BRW_PARENT:
-			/* If the object is not chained, it has no parent */
-			li = &_C(reference)->chaining;
-			if (li != li->head) {
-				/* The sentinel is the parent's children list */
-				result = _C(li->head->o);
-				diff = -1;
-			}
-			break;
-
-		case MSG_BRW_WALK:
-			/* First, try to find a child */
-			li = &_C(reference)->children;
-			if ( ! FD_IS_LIST_EMPTY(li) ) {
-				result = _C(li->next->o);
-				diff = 1;
-				break;
-			}
-			
-			/* Then try to find a "next" at this level or one of the parent's */
-			li = &_C(reference)->chaining;
-			do {
-				/* If this element has a "next" element, return it */
-				if (li->next != li->head) {
-					result = _C(li->next->o);
-					break;
-				}
-				/* otherwise, check if we have a parent */
-				if (li == li->head) {
-					/* no parent */
-					break;
-				}
-				/* Go to the parent's chaining information and loop */
-				diff -= 1;
-				li = &_C(li->head->o)->chaining;
-			} while (1); 
-			break;
-			
-		default:
-			/* Other directions are invalid */
-			CHECK_PARAMS( dir = 0 );
-	}
-	
-	/* Save the found object, if any */
-	if (found && result)
-		*found = (void *)result;
-	
-	/* Modify the depth according to the walk direction */
-	if (depth && diff)
-		(*depth) += diff;
-	
-	/* Return ENOENT if found was NULL */
-	if ((!found) && (!result))
-		return ENOENT;
-	else
-		return 0;
-}
-
-/* Add an AVP into a tree */
-int fd_msg_avp_add ( msg_or_avp * reference, enum msg_brw_dir dir, struct avp *avp)
-{
-	TRACE_ENTRY("%p %d %p", reference, dir, avp);
-	
-	/* Check the parameters */
-	CHECK_PARAMS(  VALIDATE_OBJ(reference)  &&  CHECK_AVP(avp)  &&  FD_IS_LIST_EMPTY(&avp->avp_chain.chaining)  );
-	
-	/* Now insert */
-	switch (dir) {
-		case MSG_BRW_NEXT:
-			/* Check the reference is an AVP -- we do not chain AVPs at same level as msgs. */
-			CHECK_PARAMS(  _C(reference)->type == MSG_AVP  );
-			
-			/* Insert the new avp after the reference */
-			fd_list_insert_after( &_A(reference)->avp_chain.chaining, &avp->avp_chain.chaining );
-			break;
-
-		case MSG_BRW_PREV:
-			/* Check the reference is an AVP */
-			CHECK_PARAMS(  _C(reference)->type == MSG_AVP  );
-			
-			/* Insert the new avp before the reference */
-			fd_list_insert_before( &_A(reference)->avp_chain.chaining, &avp->avp_chain.chaining );
-			break;
-
-		case MSG_BRW_FIRST_CHILD:
-			/* Insert the new avp after the children sentinel */
-			fd_list_insert_after( &_C(reference)->children, &avp->avp_chain.chaining );
-			break;
-
-		case MSG_BRW_LAST_CHILD:
-			/* Insert the new avp before the children sentinel */
-			fd_list_insert_before( &_C(reference)->children, &avp->avp_chain.chaining );
-			break;
-
-		default:
-			/* Other directions are invalid */
-			CHECK_PARAMS( dir = 0 );
-	}
-			
-	return 0;
-}
-
-/* Search a given AVP model in a message */
-int fd_msg_search_avp ( struct msg * msg, struct dict_object * what, struct avp ** avp )
-{
-	struct avp * nextavp;
-	struct dict_avp_data 	dictdata;
-	enum dict_object_type 	dicttype;
-	
-	TRACE_ENTRY("%p %p %p", msg, what, avp);
-	
-	CHECK_PARAMS( CHECK_MSG(msg) && what );
-	
-	CHECK_PARAMS( (fd_dict_gettype(what, &dicttype) == 0) && (dicttype == DICT_AVP) );
-	CHECK_FCT(  fd_dict_getval(what, &dictdata)  );
-	
-	/* Loop on all top AVPs */
-	CHECK_FCT(  fd_msg_browse(msg, MSG_BRW_FIRST_CHILD, (void *)&nextavp, NULL)  );
-	while (nextavp) {
-		
-		if ( (nextavp->avp_public.avp_code   == dictdata.avp_code)
-		  && (nextavp->avp_public.avp_vendor == dictdata.avp_vendor) ) /* always 0 if no V flag */
-			break;
-		
-		/* Otherwise move to next AVP in the message */
-		CHECK_FCT( fd_msg_browse(nextavp, MSG_BRW_NEXT, (void *)&nextavp, NULL) );
-	}
-	
-	if (avp)
-		*avp = nextavp;
-	
-	if (avp && nextavp) {
-		struct dictionary * dict;
-		CHECK_FCT( fd_dict_getdict( what, &dict) );
-		CHECK_FCT_DO( fd_msg_parse_dict( nextavp, dict, NULL ), /* nothing */ );
-	}
-	
-	if (avp || nextavp)
-		return 0;
-	else
-		return ENOENT;
-}
-
-
-/***************************************************************************************************************/
-/* Deleting objects */
-
-/* Destroy and free an AVP or message */
-static int destroy_obj (struct msg_avp_chain * obj )
-{
-	TRACE_ENTRY("%p", obj);
-	
-	/* Check the parameter is a valid object */
-	CHECK_PARAMS(  VALIDATE_OBJ(obj) && FD_IS_LIST_EMPTY( &obj->children ) );
-
-	/* Unlink this object if needed */
-	fd_list_unlink( &obj->chaining );
-	
-	/* Free the octetstring if needed */
-	if ((obj->type == MSG_AVP) && (_A(obj)->avp_mustfreeos == 1)) {
-		free(_A(obj)->avp_storage.os.data);
-	}
-	/* Free the rawdata if needed */
-	if ((obj->type == MSG_AVP) && (_A(obj)->avp_rawdata != NULL)) {
-		free(_A(obj)->avp_rawdata);
-	}
-	if ((obj->type == MSG_MSG) && (_M(obj)->msg_rawbuffer != NULL)) {
-		free(_M(obj)->msg_rawbuffer);
-	}
-	
-	if ((obj->type == MSG_MSG) && (_M(obj)->msg_src_id != NULL)) {
-		free(_M(obj)->msg_src_id);
-	}
-	
-	if ((obj->type == MSG_MSG) && (_M(obj)->msg_rtdata != NULL)) {
-		fd_rtd_free(&_M(obj)->msg_rtdata);
-	}
-	
-	if ((obj->type == MSG_MSG) && (_M(obj)->msg_sess != NULL)) {
-		CHECK_FCT_DO( fd_sess_reclaim_msg ( &_M(obj)->msg_sess ), /* continue */);
-	}
-	
-	/* free the object */
-	free(obj);
-	
-	return 0;
-}
-
-/* Destroy an object and all its children */
-static void destroy_tree(struct msg_avp_chain * obj)
-{
-	struct fd_list *rem;
-	
-	TRACE_ENTRY("%p", obj);
-	
-	/* Destroy any subtree */
-	while ( (rem = obj->children.next) != &obj->children)
-		destroy_tree(_C(rem->o));
-	
-	/* Then unlink and destroy the object */
-	CHECK_FCT_DO(  destroy_obj(obj),  /* nothing */  );
-}
-
-/* Free an object and its tree */
-int fd_msg_free ( msg_or_avp * object )
-{
-	TRACE_ENTRY("%p", object);
-	
-	if (CHECK_MSG(object)) {
-		if (_M(object)->msg_query) {
-			_M(_M(object)->msg_query)->msg_associated = 0;
-			CHECK_FCT(  fd_msg_free( _M(object)->msg_query )  );
-			_M(object)->msg_query = NULL;
-		} else {
-			if (_M(object)->msg_associated) {
-				TRACE_DEBUG(INFO, "Not freeing query %p referenced in an answer (will be freed along the answer).", object);
-				return 0;
-			}
-		}
-	}
-	
-	destroy_tree(_C(object));
-	return 0;
-}
-
-
-/***************************************************************************************************************/
-/* Debug functions: dumping */
-
-/* indent inside an object */
-#define INOBJHDR 	"%*s   "
-#define INOBJHDRVAL 	indent<0 ? 1 : indent, indent<0 ? "-" : "|"
-
-/* Dump a msg_t object */
-static void obj_dump_msg (struct msg * msg, int indent )
-{
-	int ret = 0;
-	
-	fd_log_debug("%*sMSG: %p\n", INOBJHDRVAL, msg);
-	
-	if (!CHECK_MSG(msg)) {
-		fd_log_debug(INOBJHDR "INVALID!\n", INOBJHDRVAL);
-		return;
-	}
-	
-	if (!msg->msg_model) {
-		
-		fd_log_debug(INOBJHDR "(no model)\n", INOBJHDRVAL);
-		
-	} else {
-		
-		enum dict_object_type dicttype;
-		struct dict_cmd_data  dictdata;
-		ret = fd_dict_gettype(msg->msg_model, &dicttype);
-		if (ret || (dicttype != DICT_COMMAND)) {
-			fd_log_debug(INOBJHDR "(invalid model: %d %d)\n", INOBJHDRVAL, ret, dicttype);
-			goto public;
-		}
-		ret = fd_dict_getval(msg->msg_model, &dictdata);
-		if (ret != 0) {
-			fd_log_debug(INOBJHDR "(error getting model data: %s)\n", INOBJHDRVAL, strerror(ret));
-			goto public;
-		}
-		fd_log_debug(INOBJHDR "model : v/m:" DUMP_CMDFL_str "/" DUMP_CMDFL_str ", %u \"%s\"\n", INOBJHDRVAL, 
-			DUMP_CMDFL_val(dictdata.cmd_flag_val), DUMP_CMDFL_val(dictdata.cmd_flag_mask), dictdata.cmd_code, dictdata.cmd_name);
-	}
-public:	
-	fd_log_debug(INOBJHDR "public: V:%d L:%d fl:" DUMP_CMDFL_str " CC:%u A:%d hi:%x ei:%x\n", INOBJHDRVAL, 
-		msg->msg_public.msg_version,
-		msg->msg_public.msg_length,
-		DUMP_CMDFL_val(msg->msg_public.msg_flags),
-		msg->msg_public.msg_code,
-		msg->msg_public.msg_appl,
-		msg->msg_public.msg_hbhid,
-		msg->msg_public.msg_eteid
-		);
-	fd_log_debug(INOBJHDR "intern: rwb:%p rt:%d cb:%p(%p) qry:%p asso:%d sess:%p src:%s\n", 
-			INOBJHDRVAL, msg->msg_rawbuffer, msg->msg_routable, msg->msg_cb.fct, msg->msg_cb.data, msg->msg_query, msg->msg_associated, msg->msg_sess, msg->msg_src_id?:"(nil)");
-}
-
-/* Dump an avp object */
-static void obj_dump_avp ( struct avp * avp, int indent )
-{
-	int ret = 0;
-	enum dict_avp_basetype type = -1;
-	
-	if (!CHECK_AVP(avp)) {
-		fd_log_debug(INOBJHDR "INVALID!\n", INOBJHDRVAL);
-		return;
-	}
-	
-	if (!avp->avp_model) {
-		
-		fd_log_debug(INOBJHDR "(no model)\n", INOBJHDRVAL);
-		
-	} else {
-		
-		enum dict_object_type dicttype;
-		struct dict_avp_data dictdata;
-		ret = fd_dict_gettype(avp->avp_model, &dicttype);
-		if (ret || (dicttype != DICT_AVP)) {
-			fd_log_debug(INOBJHDR "(invalid model: %d %d)\n", INOBJHDRVAL, ret, dicttype);
-			goto public;
-		}
-		ret = fd_dict_getval(avp->avp_model, &dictdata);
-		if (ret != 0) {
-			fd_log_debug(INOBJHDR "(error getting model data: %s)\n", INOBJHDRVAL, strerror(ret));
-			goto public;
-		}
-		fd_log_debug(INOBJHDR "model : v/m:" DUMP_AVPFL_str "/" DUMP_AVPFL_str ", %12s, %u \"%s\"\n", INOBJHDRVAL, 
-			DUMP_AVPFL_val(dictdata.avp_flag_val), 
-			DUMP_AVPFL_val(dictdata.avp_flag_mask), 
-			type_base_name[dictdata.avp_basetype], 
-			dictdata.avp_code, 
-			dictdata.avp_name );
-		type = dictdata.avp_basetype;
-	}
-public:	
-	fd_log_debug(INOBJHDR "public: C:%u fl:" DUMP_AVPFL_str " L:%d V:%u  data:@%p\n", INOBJHDRVAL, 
-		avp->avp_public.avp_code,
-		DUMP_AVPFL_val(avp->avp_public.avp_flags),
-		avp->avp_public.avp_len,
-		avp->avp_public.avp_vendor,
-		avp->avp_public.avp_value
-		);
-	/* Dump the value if set */
-	if (avp->avp_public.avp_value) {
-		if (!avp->avp_model) {
-			fd_log_debug(INOBJHDR "(data set but no model: ERROR)\n", INOBJHDRVAL);
-		} else {
-			fd_dict_dump_avp_value(avp->avp_public.avp_value, avp->avp_model, indent);
-		}
-	}
-
-	fd_log_debug(INOBJHDR "intern: src:%p mf:%d raw:%p(%d)\n", INOBJHDRVAL, avp->avp_source, avp->avp_mustfreeos, avp->avp_rawdata, avp->avp_rawlen);
-}
-
-/* Dump a single object content */
-static void msg_dump_intern ( int level, msg_or_avp * obj, int indent )
-{
-	/* Log only if we are at least at level */
-	if ( ! TRACE_BOOL(level) )
-		return;
-	
-	/* Check the object */
-	if (!VALIDATE_OBJ(obj)) {
-		fd_log_debug( ">>> invalid object (%p)!.\n", obj);
-		return;
-	}
-	
-	/* Dump the object */
-	switch (_C(obj)->type) {
-		case MSG_AVP:
-			obj_dump_avp ( _A(obj), indent );
-			break;
-		
-		case MSG_MSG:
-			obj_dump_msg ( _M(obj), indent );
-			break;
-		
-		default:
-			ASSERT(0);
-	}
-}
-
-/* Dump a message content -- for debug mostly */
-void fd_msg_dump_walk ( int level, msg_or_avp *obj )
-{
-	msg_or_avp * ref = obj;
-	int indent = 1;
-	
-	TRACE_DEBUG(level, "------ Dumping object %p (w)-------", obj);
-	do {
-		msg_dump_intern ( level, ref, indent );
-		
-		/* Now find the next object */
-		CHECK_FCT_DO(  fd_msg_browse ( ref, MSG_BRW_WALK, &ref, &indent ), break  );
-		
-		/* dump next object */
-	} while (ref);
-	
-	TRACE_DEBUG(level, "------ /end of object %p -------", obj);
-}
-
-/* Dump a single object content -- for debug mostly */
-void fd_msg_dump_one ( int level, msg_or_avp * obj )
-{
-	TRACE_DEBUG(level, "------ Dumping object %p (s)-------", obj);
-	msg_dump_intern ( level, obj, 1 );
-	TRACE_DEBUG(level, "------ /end of object %p -------", obj);
-}
-
-
-/***************************************************************************************************************/
-/* Simple meta-data management */
-
-/* Retrieve the model of an object */
-int fd_msg_model ( msg_or_avp * reference, struct dict_object ** model )
-{
-	TRACE_ENTRY("%p %p", reference, model);
-	
-	/* Check the parameters */
-	CHECK_PARAMS(  model && VALIDATE_OBJ(reference)  );
-	
-	/* copy the model reference */
-	switch (_C(reference)->type) {
-		case MSG_AVP:
-			*model = _A(reference)->avp_model;
-			break;
-		
-		case MSG_MSG:
-			*model = _M(reference)->msg_model;
-			break;
-		
-		default:
-			CHECK_PARAMS(0);
-	}
-	
-	return 0;
-}
-
-/* Retrieve the address of the msg_public field of a message */
-int fd_msg_hdr ( struct msg *msg, struct msg_hdr **pdata )
-{
-	TRACE_ENTRY("%p %p", msg, pdata);
-	CHECK_PARAMS(  CHECK_MSG(msg) && pdata  );
-	
-	*pdata = &msg->msg_public;
-	return 0;
-}
-
-/* Retrieve the address of the avp_public field of an avp */
-int fd_msg_avp_hdr ( struct avp *avp, struct avp_hdr **pdata )
-{
-	TRACE_ENTRY("%p %p", avp, pdata);
-	CHECK_PARAMS(  CHECK_AVP(avp) && pdata  );
-	
-	*pdata = &avp->avp_public;
-	return 0;
-}
-
-/* Associate answers and queries */
-int fd_msg_answ_associate( struct msg * answer, struct msg * query )
-{
-	TRACE_ENTRY( "%p %p", answer, query );
-	
-	CHECK_PARAMS(  CHECK_MSG(answer) && CHECK_MSG(query) && (answer->msg_query == NULL )  );
-	
-	answer->msg_query = query;
-	query->msg_associated = 1;
-	
-	return 0;
-}	
-
-int fd_msg_answ_getq( struct msg * answer, struct msg ** query )
-{
-	TRACE_ENTRY( "%p %p", answer, query );
-	
-	CHECK_PARAMS(  CHECK_MSG(answer) && query  );
-	
-	*query = answer->msg_query;
-	
-	return 0;
-}	
-
-int fd_msg_answ_detach( struct msg * answer )
-{
-	TRACE_ENTRY( "%p", answer );
-	
-	CHECK_PARAMS(  CHECK_MSG(answer) );
-	
-	answer->msg_query->msg_associated = 0;
-	answer->msg_query = NULL;
-	
-	return 0;
-}
-
-/* Associate / get answer callbacks */
-int fd_msg_anscb_associate( struct msg * msg, void ( *anscb)(void *, struct msg **), void  * data, const struct timespec *timeout )
-{
-	TRACE_ENTRY("%p %p %p", msg, anscb, data);
-	
-	/* Check the parameters */
-	CHECK_PARAMS( CHECK_MSG(msg) && anscb );
-	CHECK_PARAMS( msg->msg_public.msg_flags & CMD_FLAG_REQUEST ); /* we associate with requests only */
-	CHECK_PARAMS( msg->msg_cb.fct == NULL ); /* No cb is already registered */
-	
-	/* Associate callback and data with the message, if any */
-	msg->msg_cb.fct = anscb;
-	msg->msg_cb.data = data;
-	if (timeout) {
-		memcpy(&msg->msg_cb.timeout, timeout, sizeof(struct timespec));
-	}
-	
-	return 0;
-}	
-
-int fd_msg_anscb_get( struct msg * msg, void (**anscb)(void *, struct msg **), void ** data )
-{
-	TRACE_ENTRY("%p %p %p", msg, anscb, data);
-	
-	/* Check the parameters */
-	CHECK_PARAMS( CHECK_MSG(msg) && anscb && data );
-	
-	/* Copy the result */
-	*anscb = msg->msg_cb.fct;
-	*data  = msg->msg_cb.data;
-	
-	return 0;
-}
-
-struct timespec *fd_msg_anscb_gettimeout( struct msg * msg )
-{
-	TRACE_ENTRY("%p", msg);
-	
-	/* Check the parameters */
-	CHECK_PARAMS_DO( CHECK_MSG(msg), return NULL );
-	
-	if (!msg->msg_cb.timeout.tv_sec) {
-		return NULL;
-	}
-	
-	return &msg->msg_cb.timeout;
-}
-
-/* Associate routing lists */
-int fd_msg_rt_associate( struct msg * msg, struct rt_data ** rtd )
-{
-	TRACE_ENTRY( "%p %p", msg, rtd );
-	
-	CHECK_PARAMS(  CHECK_MSG(msg) && rtd  );
-	
-	msg->msg_rtdata = *rtd;
-	*rtd = NULL;
-	
-	return 0;
-}
-
-int fd_msg_rt_get( struct msg * msg, struct rt_data ** rtd )
-{
-	TRACE_ENTRY( "%p %p", msg, rtd );
-	
-	CHECK_PARAMS(  CHECK_MSG(msg) && rtd  );
-	
-	*rtd = msg->msg_rtdata;
-	msg->msg_rtdata = NULL;
-	
-	return 0;
-}	
-
-/* Find if a message is routable */
-int fd_msg_is_routable ( struct msg * msg )
-{
-	TRACE_ENTRY("%p", msg);
-	
-	CHECK_PARAMS_DO(  CHECK_MSG(msg),  return 0 /* pretend the message is not routable */ );
-	
-	if ( ! msg->msg_routable ) {
-		/* To define if a message is routable, we rely on the "PXY" flag (for application 0). */
-		msg->msg_routable = ((msg->msg_public.msg_appl != 0) || (msg->msg_public.msg_flags & CMD_FLAG_PROXIABLE)) ? 1 : 2;
-		
-		/* Note : the 'real' criteria according to the Diameter I-D is that the message is 
-		 routable if and only if the "Destination-Realm" AVP is required by the command ABNF.
-		 We could make a test for this here, but it's more computational work and our test
-		 seems accurate (until proven otherwise...) */
-	}
-	
-	return (msg->msg_routable == 1) ? 1 : 0;
-}
-
-/* Associate source peer */
-int fd_msg_source_set( struct msg * msg, char * diamid, int add_rr, struct dictionary * dict )
-{
-	TRACE_ENTRY( "%p %p %d %p", msg, diamid, add_rr, dict);
-	
-	/* Check we received a valid message */
-	CHECK_PARAMS( CHECK_MSG(msg) && ( (! add_rr) || dict ) );
-	
-	/* Cleanup any previous source */
-	free(msg->msg_src_id); msg->msg_src_id = NULL;
-	
-	/* If the request is to cleanup the source, we are done */
-	if (diamid == NULL) {
-		return 0;
-	}
-	
-	/* Otherwise save the new informations */
-	CHECK_MALLOC( msg->msg_src_id = strdup(diamid) );
-	
-	if (add_rr) {
-		struct dict_object 	*avp_rr_model;
-		avp_code_t 		 code = AC_ROUTE_RECORD;
-		struct avp 		*avp;
-		union avp_value		 val;
-		
-		/* Find the model for Route-Record in the dictionary */
-		CHECK_FCT( fd_dict_search ( dict, DICT_AVP, AVP_BY_CODE, &code, &avp_rr_model, ENOENT) );
-		
-		/* Create the AVP with this model */
-		CHECK_FCT( fd_msg_avp_new ( avp_rr_model, 0, &avp ) );
-		
-		/* Set the AVP value with the diameter id */
-		memset(&val, 0, sizeof(val));
-		val.os.data = (unsigned char *)diamid;
-		val.os.len  = strlen(diamid);
-		CHECK_FCT( fd_msg_avp_setvalue( avp, &val ) );
-
-		/* Add the AVP in the message */
-		CHECK_FCT( fd_msg_avp_add( msg, MSG_BRW_LAST_CHILD, avp ) );
-	}
-	
-	/* done */
-	return 0;
-}
-
-int fd_msg_source_get( struct msg * msg, char ** diamid )
-{
-	TRACE_ENTRY( "%p %p", msg, diamid);
-	
-	/* Check we received valid parameters */
-	CHECK_PARAMS( CHECK_MSG(msg) );
-	CHECK_PARAMS( diamid );
-	
-	/* Copy the informations */
-	*diamid = msg->msg_src_id;
-	
-	/* done */
-	return 0;
-}
-
-/* Retrieve the session of the message */
-int fd_msg_sess_get(struct dictionary * dict, struct msg * msg, struct session ** session, int * new)
-{
-	struct avp * avp;
-	
-	TRACE_ENTRY("%p %p %p", msg, session, new);
-	
-	/* Check we received valid parameters */
-	CHECK_PARAMS( CHECK_MSG(msg) );
-	CHECK_PARAMS( session );
-	
-	/* If we already resolved the session, just send it back */
-	if (msg->msg_sess) {
-		*session = msg->msg_sess;
-		if (new)
-			*new = 0;
-		return 0;
-	}
-	
-	/* OK, we have to search for Session-Id AVP -- it is usually the first AVP, but let's be permissive here */
-	/* -- note: we accept messages that have not yet been dictionary parsed... */
-	CHECK_FCT(  fd_msg_browse(msg, MSG_BRW_FIRST_CHILD, &avp, NULL)  );
-	while (avp) {
-		if ( (avp->avp_public.avp_code   == AC_SESSION_ID)
-		  && (avp->avp_public.avp_vendor == 0) )
-			break;
-		
-		/* Otherwise move to next AVP in the message */
-		CHECK_FCT( fd_msg_browse(avp, MSG_BRW_NEXT, &avp, NULL) );
-	}
-	
-	if (!avp) {
-		TRACE_DEBUG(FULL, "No Session-Id AVP found in message %p", msg);
-		*session = NULL;
-		return 0;
-	}
-	
-	if (!avp->avp_model) {
-		CHECK_FCT( fd_msg_parse_dict ( avp, dict, NULL ) );
-	}
-	
-	ASSERT( avp->avp_public.avp_value );
-	
-	/* Resolve the session and we are done */
-	CHECK_FCT( fd_sess_fromsid_msg ( avp->avp_public.avp_value->os.data, avp->avp_public.avp_value->os.len, &msg->msg_sess, new) );
-	*session = msg->msg_sess;
-	
-	return 0;
-}
-
-
-/******************* End-to-end counter *********************/
-uint32_t fd_eteid;
-pthread_mutex_t fd_eteid_lck = PTHREAD_MUTEX_INITIALIZER;
-
-void fd_msg_eteid_init(void)
-{
-	fd_eteid = ((uint32_t)time(NULL) << 20) | ((uint32_t)lrand48() & ( (1 << 20) - 1 ));
-}
-
-uint32_t fd_msg_eteid_get ( void )
-{
-	uint32_t ret;
-	
-	CHECK_POSIX_DO( pthread_mutex_lock(&fd_eteid_lck), /* continue */ );
-	
-	ret = fd_eteid ++;
-	
-	CHECK_POSIX_DO( pthread_mutex_unlock(&fd_eteid_lck), /* continue */ );
-	
-	return ret;
-}
-
-/***************************************************************************************************************/
-/* Manage AVPs values */
-
-/* Set the value of an AVP */
-int fd_msg_avp_setvalue ( struct avp *avp, union avp_value *value )
-{
-	enum dict_avp_basetype type = -1;
-	
-	TRACE_ENTRY("%p %p", avp, value);
-	
-	/* Check parameter */
-	CHECK_PARAMS(  CHECK_AVP(avp) && avp->avp_model  );
-	
-	/* Retrieve information from the AVP model */
-	{
-		enum dict_object_type dicttype;
-		struct dict_avp_data  dictdata;
-		
-		CHECK_PARAMS( (fd_dict_gettype(avp->avp_model, &dicttype) == 0) && (dicttype == DICT_AVP) );
-		CHECK_FCT(  fd_dict_getval(avp->avp_model, &dictdata)  );
-		type = dictdata.avp_basetype;
-		CHECK_PARAMS(  type != AVP_TYPE_GROUPED  );
-	}
-	
-	/* First, clean any previous value */
-	if (avp->avp_mustfreeos != 0) {
-		free(avp->avp_storage.os.data);
-		avp->avp_mustfreeos = 0;
-	}
-	
-	memset(&avp->avp_storage, 0, sizeof(union avp_value));
-	
-	/* If the request was to delete a value: */
-	if (!value) {
-		avp->avp_public.avp_value = NULL;
-		return 0;
-	}
-	
-	/* Now we have to set the value */
-	memcpy(&avp->avp_storage, value, sizeof(union avp_value));
-	
-	/* Copy an octetstring if needed. */
-	if (type == AVP_TYPE_OCTETSTRING) {
-		if (value->os.len) {
-			CHECK_MALLOC(  avp->avp_storage.os.data = malloc(value->os.len)  );
-			avp->avp_mustfreeos = 1;
-			memcpy(avp->avp_storage.os.data, value->os.data, value->os.len);
-		} else {
-			avp->avp_storage.os.data = NULL;
-		}
-	}
-	
-	/* Set the data pointer of the public part */
-	avp->avp_public.avp_value = &avp->avp_storage;
-	
-	return 0;		
-}
-
-/* Set the value of an AVP, using formatted data */
-int fd_msg_avp_value_encode ( void *data, struct avp *avp )
-{
-	enum dict_avp_basetype type = -1;
-	struct dict_type_data type_data;
-	
-	TRACE_ENTRY("%p %p", data, avp);
-	
-	/* Check parameter */
-	CHECK_PARAMS(  CHECK_AVP(avp) && avp->avp_model  );
-	
-	/* Retrieve information from the AVP model and it's parent type */
-	{
-		enum dict_object_type dicttype;
-		struct dict_avp_data  dictdata;
-		struct dictionary   * dict;
-		struct dict_object  * parenttype = NULL;
-		
-		/* First check the base type of the AVP */
-		CHECK_PARAMS( (fd_dict_gettype(avp->avp_model, &dicttype) == 0) && (dicttype == DICT_AVP) );
-		CHECK_FCT(  fd_dict_getval(avp->avp_model, &dictdata)  );
-		type = dictdata.avp_basetype;
-		CHECK_PARAMS(  type != AVP_TYPE_GROUPED  );
-		
-		/* Then retrieve information about the parent's type (= derived type) */
-		CHECK_FCT(  fd_dict_getdict( avp->avp_model, &dict )  );
-		CHECK_FCT(  fd_dict_search( dict, DICT_TYPE, TYPE_OF_AVP, avp->avp_model, &parenttype, EINVAL)  );
-		CHECK_FCT(  fd_dict_getval(parenttype, &type_data)  );
-		if (type_data.type_encode == NULL) {
-			TRACE_DEBUG(INFO, "This AVP type does not provide a callback to encode formatted data. ENOTSUP.");
-			return ENOTSUP;
-		}
-	}
-	
-	/* Ok, now we can encode the value */
-	
-	/* First, clean any previous value */
-	if (avp->avp_mustfreeos != 0) {
-		free(avp->avp_storage.os.data);
-		avp->avp_mustfreeos = 0;
-	}
-	avp->avp_public.avp_value = NULL;
-	memset(&avp->avp_storage, 0, sizeof(union avp_value));
-	
-	/* Now call the type's callback to encode the data */
-	CHECK_FCT(  (*type_data.type_encode)(data, &avp->avp_storage)  );
-	
-	/* If an octetstring has been allocated, let's mark it to be freed */
-	if (type == AVP_TYPE_OCTETSTRING)
-		avp->avp_mustfreeos = 1;
-	
-	/* Set the data pointer of the public part */
-	avp->avp_public.avp_value = &avp->avp_storage;
-	
-	return 0;		
-}
-
-/* Interpret the value of an AVP into formatted data */
-int fd_msg_avp_value_interpret ( struct avp *avp, void *data )
-{
-	struct dict_type_data type_data;
-	
-	TRACE_ENTRY("%p %p", avp, data);
-	
-	/* Check parameter */
-	CHECK_PARAMS(  CHECK_AVP(avp) && avp->avp_model && avp->avp_public.avp_value  );
-	
-	/* Retrieve information about the AVP parent type */
-	{
-		struct dictionary   * dict;
-		struct dict_object  * parenttype = NULL;
-		
-		CHECK_FCT(  fd_dict_getdict( avp->avp_model, &dict )  );
-		CHECK_FCT(  fd_dict_search( dict, DICT_TYPE, TYPE_OF_AVP, avp->avp_model, &parenttype, EINVAL)  );
-		CHECK_FCT(  fd_dict_getval(parenttype, &type_data)  );
-		if (type_data.type_interpret == NULL) {
-			TRACE_DEBUG(INFO, "This AVP type does not provide a callback to interpret value in formatted data. ENOTSUP.");
-			return ENOTSUP;
-		}
-	}
-	
-	/* Ok, now we can interpret the value */
-	
-	CHECK_FCT(  (*type_data.type_interpret)(avp->avp_public.avp_value, data)  );
-	
-	return 0;		
-}
-
-/***************************************************************************************************************/
-/* Creating a buffer from memory objects (bufferize a struct msg) */
-
-/* Following macros are used to store 32 and 64 bit fields into a buffer in network byte order */
-#define PUT_in_buf_32( _u32data, _bufptr ) {							\
-	*(uint32_t *)(_bufptr) = htonl((uint32_t)(_u32data));					\
-}
-#define PUT_in_buf_64( _u64data, _bufptr ) {							\
-	*(uint64_t *)(_bufptr) = htonll((uint64_t)(_u64data));					\
-}
-
-/* Write a message header in the buffer */
-static int bufferize_msg(unsigned char * buffer, size_t buflen, size_t * offset, struct msg * msg)
-{
-	TRACE_ENTRY("%p %d %p %p", buffer, buflen, offset, msg);
-	
-	if ((buflen - *offset) < GETMSGHDRSZ())
-		return ENOSPC;
-	
-	if (*offset & 0x3)
-		return EFAULT;	/* We are supposed to start on 32 bit boundaries */
-	
-	PUT_in_buf_32(msg->msg_public.msg_length, buffer + *offset);
-	buffer[*offset] = msg->msg_public.msg_version;
-	*offset += 4;
-	
-	PUT_in_buf_32(msg->msg_public.msg_code, buffer + *offset);
-	buffer[*offset] = msg->msg_public.msg_flags;
-	*offset += 4;
-	
-	PUT_in_buf_32(msg->msg_public.msg_appl, buffer + *offset);
-	*offset += 4;
-	
-	PUT_in_buf_32(msg->msg_public.msg_hbhid, buffer + *offset);
-	*offset += 4;
-	
-	PUT_in_buf_32(msg->msg_public.msg_eteid, buffer + *offset);
-	*offset += 4;
-	
-	return 0;
-}
-
-static int bufferize_chain(unsigned char * buffer, size_t buflen, size_t * offset, struct fd_list * list);
-
-/* Write an AVP in the buffer */
-static int bufferize_avp(unsigned char * buffer, size_t buflen, size_t * offset,  struct avp * avp)
-{
-	struct dict_avp_data dictdata;
-	
-	TRACE_ENTRY("%p %d %p %p", buffer, buflen, offset, avp);
-	
-	if ((buflen - *offset) < avp->avp_public.avp_len)
-		return ENOSPC;
-	
-	/* Write the header */
-	PUT_in_buf_32(avp->avp_public.avp_code, buffer + *offset);
-	*offset += 4;
-	
-	PUT_in_buf_32(avp->avp_public.avp_len, buffer + *offset);
-	buffer[*offset] = avp->avp_public.avp_flags;
-	*offset += 4;
-	
-	if (avp->avp_public.avp_flags & AVP_FLAG_VENDOR) {
-		PUT_in_buf_32(avp->avp_public.avp_vendor, buffer + *offset);
-		*offset += 4;
-	}
-	
-	/* Then we must write the AVP value */
-	
-	if (avp->avp_model == NULL) {
-		/* In the case where we don't know the type of AVP, just copy the raw data or source */
-		CHECK_PARAMS( avp->avp_source || avp->avp_rawdata );
-		
-		if ( avp->avp_source != NULL ) {
-			/* the message was not parsed completely */
-			size_t datalen = avp->avp_public.avp_len - GETAVPHDRSZ(avp->avp_public.avp_flags);
-			memcpy(&buffer[*offset], avp->avp_source, datalen);
-			*offset += PAD4(datalen);
-		} else {
-			/* the content was stored in rawdata */
-			memcpy(&buffer[*offset], avp->avp_rawdata, avp->avp_rawlen);
-			*offset += PAD4(avp->avp_rawlen);
-		}
-		
-	} else {
-		/* The AVP is defined in the dictionary */
-		CHECK_FCT(  fd_dict_getval(avp->avp_model, &dictdata)  );
-
-		CHECK_PARAMS( ( dictdata.avp_basetype == AVP_TYPE_GROUPED ) || avp->avp_public.avp_value );
-		
-		switch (dictdata.avp_basetype) {
-			case AVP_TYPE_GROUPED:
-				return bufferize_chain(buffer, buflen, offset, &avp->avp_chain.children);
-
-			case AVP_TYPE_OCTETSTRING:
-				if (avp->avp_public.avp_value->os.len)
-					memcpy(&buffer[*offset], avp->avp_public.avp_value->os.data, avp->avp_public.avp_value->os.len);
-				*offset += PAD4(avp->avp_public.avp_value->os.len);
-				break;
-
-			case AVP_TYPE_INTEGER32:
-				PUT_in_buf_32(avp->avp_public.avp_value->i32, buffer + *offset);
-				*offset += 4;
-				break;
-
-			case AVP_TYPE_INTEGER64:
-				PUT_in_buf_64(avp->avp_public.avp_value->i64, buffer + *offset);
-				*offset += 8;
-				break;
-
-			case AVP_TYPE_UNSIGNED32:
-				PUT_in_buf_32(avp->avp_public.avp_value->u32, buffer + *offset);
-				*offset += 4;
-				break;
-
-			case AVP_TYPE_UNSIGNED64:
-				PUT_in_buf_64(avp->avp_public.avp_value->u64, buffer + *offset);
-				*offset += 8;
-				break;
-
-			case AVP_TYPE_FLOAT32:
-				/* We read the f32 as "u32" here to avoid casting to uint make decimals go away. 
-				 The alternative would be something like "*(uint32_t *)(& f32)" but
-				 then the compiler complains about strict-aliasing rules. */
-				PUT_in_buf_32(avp->avp_public.avp_value->u32, buffer + *offset);
-				*offset += 4;
-				break;
-
-			case AVP_TYPE_FLOAT64:
-				/* Same remark as previously */
-				PUT_in_buf_64(avp->avp_public.avp_value->u64, buffer + *offset);
-				*offset += 8;
-				break;
-
-			default:
-				ASSERT(0);
-		}
-	}
-	return 0;
-}
-			
-/* Write a chain of AVPs in the buffer */
-static int bufferize_chain(unsigned char * buffer, size_t buflen, size_t * offset, struct fd_list * list)
-{
-	struct fd_list * avpch;
-	
-	TRACE_ENTRY("%p %d %p %p", buffer, buflen, offset, list);
-	
-	for (avpch = list->next; avpch != list; avpch = avpch->next) {
-		/* Bufferize the AVP */
-		CHECK_FCT( bufferize_avp(buffer, buflen, offset, _A(avpch->o))  );
-	}
-	return 0;
-}
-
-/* Create the message buffer, in network-byte order. We browse the tree twice, this could be probably improved if needed */
-int fd_msg_bufferize ( struct msg * msg, unsigned char ** buffer, size_t * len )
-{
-	int ret = 0;
-	unsigned char * buf = NULL;
-	size_t offset = 0;
-	
-	TRACE_ENTRY("%p %p %p", msg, buffer, len);
-	
-	/* Check the parameters */
-	CHECK_PARAMS(  buffer && CHECK_MSG(msg)  );
-	
-	/* Update the length. This also checks that all AVP have their values set */
-	CHECK_FCT(  fd_msg_update_length(msg)  );
-	
-	/* Now allocate a buffer to store the message */
-	CHECK_MALLOC(  buf = malloc(msg->msg_public.msg_length)  );
-	
-	/* Clear the memory, so that the padding is always 0 (should not matter) */
-	memset(buf, 0, msg->msg_public.msg_length);
-	
-	/* Write the message header in the buffer */
-	CHECK_FCT_DO( ret = bufferize_msg(buf, msg->msg_public.msg_length, &offset, msg), 
-		{
-			free(buf);
-			return ret;
-		}  );
-	
-	/* Write the list of AVPs */
-	CHECK_FCT_DO( ret = bufferize_chain(buf, msg->msg_public.msg_length, &offset, &msg->msg_chain.children),
-		{
-			free(buf);
-			return ret;
-		}  );
-	
-	ASSERT(offset == msg->msg_public.msg_length); /* or the msg_update_length is buggy */
-		
-	if (len) {
-		*len = offset;
-	}
-	
-	*buffer = buf;
-	return 0;
-}
-
-
-/***************************************************************************************************************/
-/* Parsing buffers and building AVP objects lists (not parsing the AVP values which requires dictionary knowledge) */
-
-/* Parse a buffer containing a supposed list of AVPs */
-static int parsebuf_list(unsigned char * buf, size_t buflen, struct fd_list * head)
-{
-	size_t offset = 0;
-	
-	TRACE_ENTRY("%p %d %p", buf, buflen, head);
-	
-	while (offset < buflen) {
-		struct avp * avp;
-		
-		if (buflen - offset <= AVPHDRSZ_NOVEND) {
-			TRACE_DEBUG(INFO, "truncated buffer: remaining only %d bytes", buflen - offset);
-			return EBADMSG;
-		}
-		
-		/* Create a new AVP object */
-		CHECK_MALLOC(  avp = malloc (sizeof(struct avp))  );
-		
-		init_avp(avp);
-		
-		/* Initialize the header */
-		avp->avp_public.avp_code    = ntohl(*(uint32_t *)(buf + offset));
-		avp->avp_public.avp_flags   = buf[offset + 4];
-		avp->avp_public.avp_len     = ((uint32_t)buf[offset+5]) << 16 |  ((uint32_t)buf[offset+6]) << 8 |  ((uint32_t)buf[offset+7]) ;
-		
-		offset += 8;
-		
-		if (avp->avp_public.avp_flags & AVP_FLAG_VENDOR) {
-			if (buflen - offset <= 4) {
-				TRACE_DEBUG(INFO, "truncated buffer: remaining only %d bytes for vendor and data", buflen - offset);
-				free(avp);
-				return EBADMSG;
-			}
-			avp->avp_public.avp_vendor  = ntohl(*(uint32_t *)(buf + offset));
-			offset += 4;
-		}
-		
-		/* Check there is enough remaining data in the buffer */
-		if (buflen - offset < avp->avp_public.avp_len - GETAVPHDRSZ(avp->avp_public.avp_flags)) {
-			TRACE_DEBUG(INFO, "truncated buffer: remaining only %d bytes for data, and avp data size is %d", 
-					buflen - offset, 
-					avp->avp_public.avp_len - GETAVPHDRSZ(avp->avp_public.avp_flags));
-			free(avp);
-			return EBADMSG;
-		}
-		
-		/* buf[offset] is now the beginning of the data */
-		avp->avp_source = &buf[offset];
-		
-		/* Now eat the data and eventual padding */
-		offset += PAD4(avp->avp_public.avp_len - GETAVPHDRSZ(avp->avp_public.avp_flags));
-		
-		/* And insert this avp in the list, at the end */
-		fd_list_insert_before( head, &avp->avp_chain.chaining );
-	}
-	
-	return 0;
-}
-
-/* Create a message object from a buffer. Dictionary objects are not resolved, AVP contents are not interpreted, buffer is saved in msg */
-int fd_msg_parse_buffer ( unsigned char ** buffer, size_t buflen, struct msg ** msg )
-{
-	struct msg * new = NULL;
-	int ret = 0;
-	uint32_t msglen = 0;
-	unsigned char * buf;
-	
-	TRACE_ENTRY("%p %d %p", buffer, buflen, msg);
-	
-	CHECK_PARAMS(  buffer &&  *buffer  &&  msg  &&  (buflen >= GETMSGHDRSZ())  );
-	buf = *buffer;
-	*buffer = NULL;
-	
-	if ( buf[0] != DIAMETER_VERSION) {
-		TRACE_DEBUG(INFO, "Invalid version in message: %d (supported: %d)", buf[0], DIAMETER_VERSION);
-		free(buf);
-		return EBADMSG;
-	}
-	
-	msglen = ntohl(*(uint32_t *)buf) & 0x00ffffff;
-	if ( buflen < msglen ) {  
-		TRACE_DEBUG(INFO, "Truncated message (%d / %d)", buflen, msglen );
-		free(buf);
-		return EBADMSG; 
-	}
-	
-	/* Create a new object */
-	CHECK_MALLOC_DO( new = malloc (sizeof(struct msg)),  { free(buf); return ENOMEM; }  );
-	
-	/* Initialize the fields */
-	init_msg(new);
-	
-	/* Now read from the buffer */
-	new->msg_public.msg_version = buf[0];
-	new->msg_public.msg_length = msglen;
-
-	new->msg_public.msg_flags = buf[4];
-	new->msg_public.msg_code = ntohl(*(uint32_t *)(buf+4)) & 0x00ffffff;
-	
-	new->msg_public.msg_appl = ntohl(*(uint32_t *)(buf+8));
-	new->msg_public.msg_hbhid = ntohl(*(uint32_t *)(buf+12));
-	new->msg_public.msg_eteid = ntohl(*(uint32_t *)(buf+16));
-	
-	new->msg_rawbuffer = buf;
-	
-	/* Parse the AVP list */
-	CHECK_FCT_DO( ret = parsebuf_list(buf + GETMSGHDRSZ(), buflen - GETMSGHDRSZ(), &new->msg_chain.children), { destroy_tree(_C(new)); return ret; }  );
-	
-	*msg = new;
-	return 0;
-}
-
-		
-/***************************************************************************************************************/
-/* Parsing messages and AVP with dictionary information */
-
-/* Resolve dictionary objects of the cmd and avp instances, from their headers.
- * When the model is found, the data is interpreted from the avp_source buffer and copied to avp_storage.
- * When the model is not found, the data is copied as rawdata and saved (in case we FW the message).
- * Therefore, after this function has been called, the source buffer can be freed.
- * For command, if the dictionary model is not found, an error is returned.
- */
-
-static int parsedict_do_chain(struct dictionary * dict, struct fd_list * head, int mandatory, struct fd_pei *error_info);
-
-/* Process an AVP. If we are not in recheck, the avp_source must be set. */
-static int parsedict_do_avp(struct dictionary * dict, struct avp * avp, int mandatory, struct fd_pei *error_info)
-{
-	struct dict_avp_data dictdata;
-	
-	TRACE_ENTRY("%p %p %d %p", dict, avp, mandatory, error_info);
-	
-	/* First check we received an AVP as input */
-	CHECK_PARAMS(  CHECK_AVP(avp) );
-	
-	if (avp->avp_model != NULL) {
-		/* the model has already been resolved. we do check it is still valid */
-
-		CHECK_FCT(  fd_dict_getval(avp->avp_model, &dictdata)  );
-
-		if ( avp->avp_public.avp_code == dictdata.avp_code  ) {
-			/* Ok then just process the children if any */
-			return parsedict_do_chain(dict, &avp->avp_chain.children, mandatory && (avp->avp_public.avp_flags & AVP_FLAG_MANDATORY), error_info);
-		} else {
-			/* We just erase the old model */
-			avp->avp_model = NULL;
-		}
-	}
-	
-	/* Now try and resolve the model from the avp code and vendor */
-	if (avp->avp_public.avp_flags & AVP_FLAG_VENDOR) {
-		struct dict_avp_request avpreq;
-		avpreq.avp_vendor = avp->avp_public.avp_vendor;
-		avpreq.avp_code = avp->avp_public.avp_code;
-		CHECK_FCT( fd_dict_search ( dict, DICT_AVP, AVP_BY_CODE_AND_VENDOR, &avpreq, &avp->avp_model, 0));
-	} else {
-		/* no vendor */
-		CHECK_FCT( fd_dict_search ( dict, DICT_AVP, AVP_BY_CODE, &avp->avp_public.avp_code, &avp->avp_model, 0));
-	}
-	
-	/* First handle the case where we have not found this AVP in the dictionary */
-	if (!avp->avp_model) {
-		
-		if (mandatory && (avp->avp_public.avp_flags & AVP_FLAG_MANDATORY)) {
-			TRACE_DEBUG(INFO, "Unsupported mandatory AVP found:");
-			msg_dump_intern(INFO, avp, 2);
-			if (error_info) {
-				error_info->pei_errcode = "DIAMETER_AVP_UNSUPPORTED";
-				error_info->pei_avp = avp;
-			}
-			return ENOTSUP;
-		}
-		
-		if (avp->avp_source) {
-			/* we must copy the data from the source to the internal buffer area */
-			CHECK_PARAMS( !avp->avp_rawdata  );
-			
-			avp->avp_rawlen = avp->avp_public.avp_len - GETAVPHDRSZ( avp->avp_public.avp_flags );
-			
-			if (avp->avp_rawlen) {
-				CHECK_MALLOC(  avp->avp_rawdata = malloc(avp->avp_rawlen)  );
-			
-				memcpy(avp->avp_rawdata, avp->avp_source, avp->avp_rawlen);
-			}
-			
-			avp->avp_source = NULL;
-			
-			TRACE_DEBUG(FULL, "Unsupported optional AVP found, raw source data saved in avp_rawdata.");
-		}
-		
-		return 0;
-	}
-	
-	/* Ok we have resolved the object. Now we need to interpret its content. */
-	
-	CHECK_FCT(  fd_dict_getval(avp->avp_model, &dictdata)  );
-	
-	if (avp->avp_rawdata) {
-		/* This happens if the dictionary object was defined after the first check */
-		avp->avp_source = avp->avp_rawdata;
-	}
-	
-	/* A bit of sanity here... */
-	ASSERT(CHECK_BASETYPE(dictdata.avp_basetype));
-	
-	/* Check the size is valid */
-	if ((avp_value_sizes[dictdata.avp_basetype] != 0) &&
-	    (avp->avp_public.avp_len - GETAVPHDRSZ( avp->avp_public.avp_flags ) != avp_value_sizes[dictdata.avp_basetype])) {
-		TRACE_DEBUG(INFO, "The AVP size is not suitable for the type.");
-		if (error_info) {
-			error_info->pei_errcode = "DIAMETER_INVALID_AVP_LENGTH";
-			error_info->pei_avp = avp;
-		}
-		return EBADMSG;
-	}
-	
-	/* Now get the value inside */
-	switch (dictdata.avp_basetype) {
-		case AVP_TYPE_GROUPED: {
-			int ret;
-			
-			/* This is a grouped AVP, so let's parse the list of AVPs inside */
-			CHECK_FCT_DO(  ret = parsebuf_list(avp->avp_source, avp->avp_public.avp_len - GETAVPHDRSZ( avp->avp_public.avp_flags ), &avp->avp_chain.children),
-				{
-					if ((ret == EBADMSG) && (error_info)) {
-						error_info->pei_errcode = "DIAMETER_INVALID_AVP_VALUE";
-						error_info->pei_avp = avp;
-					}
-					return ret;
-				}  );
-			
-			return parsedict_do_chain(dict, &avp->avp_chain.children, mandatory && (avp->avp_public.avp_flags & AVP_FLAG_MANDATORY), error_info);
-		}
-			
-		case AVP_TYPE_OCTETSTRING:
-			/* We just have to copy the string into the storage area */
-			CHECK_PARAMS_DO( avp->avp_public.avp_len >= GETAVPHDRSZ( avp->avp_public.avp_flags ),
-				{
-					if (error_info) {
-						error_info->pei_errcode = "DIAMETER_INVALID_AVP_LENGTH";
-						error_info->pei_avp = avp;
-					}
-					return EBADMSG;
-				} );
-			avp->avp_storage.os.len = avp->avp_public.avp_len - GETAVPHDRSZ( avp->avp_public.avp_flags );
-			if (avp->avp_storage.os.len) {
-				CHECK_MALLOC(  avp->avp_storage.os.data = malloc(avp->avp_storage.os.len)  );
-				avp->avp_mustfreeos = 1;
-				memcpy(avp->avp_storage.os.data, avp->avp_source, avp->avp_storage.os.len);
-			} else {
-				avp->avp_storage.os.data = NULL;
-			}
-			break;
-		
-		case AVP_TYPE_INTEGER32:
-			avp->avp_storage.i32 = (int32_t)ntohl(*(uint32_t *)avp->avp_source);
-			break;
-	
-		case AVP_TYPE_INTEGER64:
-			avp->avp_storage.i64 = (int64_t)ntohll(*(uint64_t *)avp->avp_source);
-			break;
-	
-		case AVP_TYPE_UNSIGNED32:
-		case AVP_TYPE_FLOAT32: /* For float, we must not cast, or the value is changed. Instead we use implicit cast by changing the member of the union */
-			avp->avp_storage.u32 = (uint32_t)ntohl(*(uint32_t *)avp->avp_source);
-			break;
-	
-		case AVP_TYPE_UNSIGNED64:
-		case AVP_TYPE_FLOAT64: /* same as 32 bits */
-			avp->avp_storage.u64 = (uint64_t)ntohll(*(uint64_t *)avp->avp_source);
-			break;
-	
-	}
-	
-	/* The value is now set, so set the data pointer and return 0 */
-	avp->avp_public.avp_value = &avp->avp_storage;
-	return 0;
-}
-
-/* Process a list of AVPs */
-static int parsedict_do_chain(struct dictionary * dict, struct fd_list * head, int mandatory, struct fd_pei *error_info)
-{
-	struct fd_list * avpch;
-	
-	TRACE_ENTRY("%p %p %d %p", dict, head, mandatory, error_info);
-	
-	/* Sanity check */
-	ASSERT ( head == head->head );
-	
-	/* Now process the list */
-	for (avpch=head->next; avpch != head; avpch = avpch->next) {
-		CHECK_FCT(  parsedict_do_avp(dict, _A(avpch->o), mandatory, error_info)  );
-	}
-	
-	/* Done */
-	return 0;
-}
-
-/* Process a msg header. */
-static int parsedict_do_msg(struct dictionary * dict, struct msg * msg, int only_hdr, struct fd_pei *error_info)
-{
-	int ret = 0;
-	
-	TRACE_ENTRY("%p %p %d %p", dict, msg, only_hdr, error_info);
-	
-	CHECK_PARAMS(  CHECK_MSG(msg)  );
-	
-	/* Look for the model from the header */
-	CHECK_FCT_DO( ret = fd_dict_search ( dict, DICT_COMMAND, 
-			(msg->msg_public.msg_flags & CMD_FLAG_REQUEST) ? CMD_BY_CODE_R : CMD_BY_CODE_A,
-			&msg->msg_public.msg_code,
-			&msg->msg_model, ENOTSUP),
-		{
-			if ((ret == ENOTSUP) && (error_info)) {
-				error_info->pei_errcode = "DIAMETER_COMMAND_UNSUPPORTED";
-				error_info->pei_protoerr = 1;
-			}
-			return ret;
-		} );
-	
-	if (!only_hdr) {
-		/* Then process the children */
-		ret = parsedict_do_chain(dict, &msg->msg_chain.children, 1, error_info);
-
-		/* Free the raw buffer if any */
-		if ((ret == 0) && (msg->msg_rawbuffer != NULL)) {
-			free(msg->msg_rawbuffer);
-			msg->msg_rawbuffer=NULL;
-		}
-	}
-	
-	return ret;
-}
-
-int fd_msg_parse_dict ( msg_or_avp * object, struct dictionary * dict, struct fd_pei *error_info )
-{
-	TRACE_ENTRY("%p %p %p", dict, object, error_info);
-	
-	CHECK_PARAMS(  VALIDATE_OBJ(object)  );
-	
-	if (error_info)
-		memset(error_info, 0, sizeof(struct fd_pei));
-	
-	switch (_C(object)->type) {
-		case MSG_MSG:
-			return parsedict_do_msg(dict, _M(object), 0, error_info);
-		
-		case MSG_AVP:
-			return parsedict_do_avp(dict, _A(object), 0, error_info);
-		
-		default:
-			ASSERT(0);
-	}
-	return EINVAL;
-}
-
-/***************************************************************************************************************/
-/* Parsing messages and AVP for rules (ABNF) compliance */
-
-/* This function is used to get stats (first occurence position, last occurence position, number of occurences) 
-   of AVP instances of a given model in a chain of AVP */
-static void parserules_stat_avps( struct dict_object * model_avp, struct fd_list *list, int * count, int * firstpos, int * lastpos) 
-{
-	struct fd_list * li;
-	int curpos = 0; /* The current position in the list */
-	
-	TRACE_ENTRY("%p %p %p %p %p", model_avp, list, count, firstpos, lastpos);
-	
-	*count = 0;	/* number of instances found */
-	*firstpos = 0;	/* position of the first instance */
-	*lastpos = 0;	/* position of the last instance, starting from the end */
-	
-	for (li = list->next; li != list; li = li->next) {
-		/* Increment the current position counter */
-		curpos++;
-		
-		/* If we previously saved a "lastpos" information, increment it */
-		if (*lastpos != 0)
-			(*lastpos)++;
-		
-		/* Check the type of the next AVP. We can compare the references directly, it is safe. */
-		if (_A(li->o)->avp_model == model_avp) {
-			
-			/* This AVP is of the type we are searching */
-			(*count)++;
-			
-			/* If we don't have yet a "firstpos", save it */
-			if (*firstpos == 0)
-				*firstpos = curpos;
-			
-			/* Reset the lastpos */
-			(*lastpos) = 1;
-		}
-	}
-}
-
-/* We use this structure as parameter for the next function */
-struct parserules_data {
-	struct fd_list  * sentinel;  	/* Sentinel of the list of children AVP */
-	struct fd_pei 	* pei;   	/* If the rule conflicts, save the error here */
-};
-
-/* Create an empty AVP of a given model (to use in Failed-AVP) */
-static struct avp * empty_avp(struct dict_object * model_avp)
-{
-	struct avp * avp = NULL;
-	struct dict_avp_data avp_info;
-	union avp_value val;
-	unsigned char os[1] = { '\0' };
-	
-	/* Create an instance */
-	CHECK_FCT_DO( fd_msg_avp_new(model_avp, 0, &avp ), return NULL );
-	
-	/* Type of the AVP */
-	CHECK_FCT_DO( fd_dict_getval(model_avp, &avp_info), return NULL );
-	
-	/* Prepare the empty value */
-	memset(&val, 0, sizeof(val));
-	switch (avp_info.avp_basetype) {
-		case AVP_TYPE_OCTETSTRING:
-			val.os.data = os;
-			val.os.len  = sizeof(os);
-		case AVP_TYPE_INTEGER32:
-		case AVP_TYPE_INTEGER64:
-		case AVP_TYPE_UNSIGNED32:
-		case AVP_TYPE_UNSIGNED64:
-		case AVP_TYPE_FLOAT32:
-		case AVP_TYPE_FLOAT64:
-			CHECK_FCT_DO( fd_msg_avp_setvalue(avp, &val), return NULL );
-		case AVP_TYPE_GROUPED:
-			/* For AVP_TYPE_GROUPED we don't do anything */
-			break;
-		default:
-			ASSERT(0); /* not handled */
-	}
-	
-	return avp;
-}
-
-/* Check that a list of AVPs is compliant with a given rule -- will be iterated on the list of rules */
-static int parserules_check_one_rule(void * data, struct dict_rule_data *rule)
-{
-	int count, first, last, min;
-	struct parserules_data * pr_data = data;
-	char * avp_name = "<unresolved name>";
-	
-	TRACE_ENTRY("%p %p", data, rule);
-	
-	/* Get statistics of the AVP concerned by this rule in the parent instance */
-	parserules_stat_avps( rule->rule_avp, pr_data->sentinel, &count, &first, &last);
-	
-	if (TRACE_BOOL(INFO))
-	{
-		struct dict_avp_data avpdata;
-		int ret;
-		ret = fd_dict_getval(rule->rule_avp, &avpdata);
-		if (ret == 0)
-			avp_name = avpdata.avp_name;
-		
-		TRACE_DEBUG(ANNOYING, "Checking rule: p:%d(%d) m/M:%2d/%2d. Counted %d (first: %d, last:%d) of AVP '%s'", 
-					rule->rule_position,
-					rule->rule_order,
-					rule->rule_min,
-					rule->rule_max,
-					count, 
-					first, 
-					last,
-					avp_name
-				);
-	}
-	
-	/* Now check the rule is not conflicting */
-	
-	/* Check the "min" value */
-	if ((min = rule->rule_min) == -1) {
-		if (rule->rule_position == RULE_OPTIONAL)
-			min = 0;
-		else
-			min = 1;
-	}
-	if (count < min) {
-		TRACE_DEBUG(INFO, "Conflicting rule: the number of occurences (%d) is < the rule min (%d) for '%s'.", count, min, avp_name);
-		if (pr_data->pei) {
-			pr_data->pei->pei_errcode = "DIAMETER_MISSING_AVP";
-			pr_data->pei->pei_avp = empty_avp(rule->rule_avp);
-		}
-		return EBADMSG;
-	}
-	
-	/* Check the "max" value */
-	if ((rule->rule_max != -1) && (count > rule->rule_max)) {
-		TRACE_DEBUG(INFO, "Conflicting rule: the number of occurences (%d) is > the rule max (%d) for '%s'.", count, rule->rule_max, avp_name);
-		if (pr_data->pei) {
-			if (rule->rule_max == 0)
-				pr_data->pei->pei_errcode = "DIAMETER_AVP_NOT_ALLOWED";
-			else
-				pr_data->pei->pei_errcode = "DIAMETER_AVP_OCCURS_TOO_MANY_TIMES";
-			pr_data->pei->pei_avp = empty_avp(rule->rule_avp); /* Well we are supposed to return the (max + 1)th instance of the AVP instead... Pfff... */ TODO("Improve...");
-		}
-		return EBADMSG;
-	}
-		
-	/* Check the position and order (if relevant) */
-	switch (rule->rule_position) {
-		case RULE_OPTIONAL:
-		case RULE_REQUIRED:
-			/* No special position constraints */
-			break;
-		
-		case RULE_FIXED_HEAD:
-			/* Since "0*1<fixed>" is a valid rule specifier, we only reject cases where the AVP appears *after* its fixed position */
-			if (first > rule->rule_order) {
-				TRACE_DEBUG(INFO, "Conflicting rule: the FIXED_HEAD AVP appears first in (%d) position, the rule requires (%d) for '%s'.", first, rule->rule_order, avp_name);
-				if (pr_data->pei) {
-					pr_data->pei->pei_errcode = "DIAMETER_MISSING_AVP";
-					pr_data->pei->pei_message = "AVP was not in its fixed position";
-					pr_data->pei->pei_avp = empty_avp(rule->rule_avp);
-				}
-				return EBADMSG;
-			}
-			break;
-	
-		case RULE_FIXED_TAIL:
-			/* Since "0*1<fixed>" is a valid rule specifier, we only reject cases where the AVP appears *before* its fixed position */
-			if (last > rule->rule_order) {	/* We have a ">" here because we count in reverse order (i.e. from the end) */
-				TRACE_DEBUG(INFO, "Conflicting rule: the FIXED_TAIL AVP appears last in (%d) position, the rule requires (%d) for '%s'.", last, rule->rule_order, avp_name);
-				if (pr_data->pei) {
-					pr_data->pei->pei_errcode = "DIAMETER_MISSING_AVP";
-					pr_data->pei->pei_message = "AVP was not in its fixed position";
-					pr_data->pei->pei_avp = empty_avp(rule->rule_avp);
-				}
-				return EBADMSG;
-			}
-			break;
-		
-		default:
-			/* What is this position ??? */
-			ASSERT(0);
-			return ENOTSUP;
-	}
-	
-	/* We've checked all the parameters */
-	return 0;
-}
-
-/* Check the rules recursively */
-static int parserules_do ( struct dictionary * dict, msg_or_avp * object, struct fd_pei *error_info, int mandatory)
-{
-	struct parserules_data data;
-	struct dict_object * model = NULL;
-	
-	TRACE_ENTRY("%p %p %p %d", dict, object, error_info, mandatory);
-	
-	/* object has already been checked and dict-parsed when we are called. */
-	
-	/* First, handle the cases where there is no model */
-	{
-		if (CHECK_MSG(object)) {
-			if ( _M(object)->msg_public.msg_flags & CMD_FLAG_ERROR ) {
-				/* The case of error messages: the ABNF is different */
-				CHECK_FCT( fd_dict_get_error_cmd(dict, &model) );
-			} else {
-				model = _M(object)->msg_model;
-			}
-			/* Commands MUST be supported in the dictionary */
-			if (model == NULL) {
-				TRACE_DEBUG(INFO, "Message with no dictionary model. EBADMSG");
-				if (error_info) {
-					error_info->pei_errcode = "DIAMETER_COMMAND_UNSUPPORTED";
-					error_info->pei_protoerr = 1;
-				}
-				return EBADMSG;
-			}
-		}
-
-		/* AVP with the 'M' flag must also be recognized in the dictionary -- except inside an optional grouped AVP */
-		if (CHECK_AVP(object) && ((model = _A(object)->avp_model) == NULL)) {
-			if ( mandatory && (_A(object)->avp_public.avp_flags & AVP_FLAG_MANDATORY)) {
-				/* Return an error in this case */
-				TRACE_DEBUG(INFO, "Mandatory AVP with no dictionary model. EBADMSG");
-				if (error_info) {
-					error_info->pei_errcode = "DIAMETER_AVP_UNSUPPORTED";
-					error_info->pei_avp = object;
-				}
-				return EBADMSG;
-			} else {
-				/* We don't know any rule for this object, so assume OK */
-				TRACE_DEBUG(FULL, "Unknown informational AVP, ignoring...");
-				return 0;
-			}
-		}
-	}
-	
-	/* At this point we know "model" is set and points to the object's model */
-	
-	/* If we are an AVP with no children, just return OK */
-	if (CHECK_AVP(object)) {
-		struct dict_avp_data	dictdata;
-		CHECK_FCT(  fd_dict_getval(model, &dictdata)  );
-		if (dictdata.avp_basetype != AVP_TYPE_GROUPED) {
-			/* This object has no children and no rules */
-			return 0;
-		}
-	}
-	
-	/* If this object has children, first check the rules for all its children */
-	{
-		int is_child_mand = 0;
-		struct fd_list * ch = NULL;
-		if (  CHECK_MSG(object) 
-		   || (mandatory && (_A(object)->avp_public.avp_flags & AVP_FLAG_MANDATORY)) )
-			is_child_mand = 1;
-		for (ch = _C(object)->children.next; ch != &_C(object)->children; ch = ch->next) {
-			CHECK_FCT(  parserules_do ( dict, _C(ch->o), error_info, is_child_mand )  );
-		}
-	}
-
-	/* Now check all rules of this object */
-	data.sentinel = &_C(object)->children;
-	data.pei  = error_info;
-	CHECK_FCT( fd_dict_iterate_rules ( model, &data, parserules_check_one_rule ) );
-	
-	return 0;
-}
-
-int fd_msg_parse_rules ( msg_or_avp * object, struct dictionary * dict, struct fd_pei *error_info)
-{
-	TRACE_ENTRY("%p %p %p", object, dict, error_info);
-	
-	if (error_info)
-		memset(error_info, 0, sizeof(struct fd_pei));
-	
-	/* Resolve the dictionary objects when missing. This also validates the object. */
-	CHECK_FCT(  fd_msg_parse_dict ( object, dict, error_info )  );
-	
-	/* Call the recursive function */
-	return parserules_do ( dict, object, error_info, 1 ) ;
-}
-
-/***************************************************************************************************************/
-
-/* Compute the lengh of an object and its subtree. */
-int fd_msg_update_length ( msg_or_avp * object )
-{
-	size_t sz = 0;
-	struct dict_object * model;
-	union {
-		struct dict_cmd_data   cmddata;
-		struct dict_avp_data   avpdata;
-	} dictdata;
-	
-	TRACE_ENTRY("%p", object);
-	
-	/* Get the model of the object. This also validates the object */
-	CHECK_FCT( fd_msg_model ( object, &model ) );
-	
-	/* Get the information of the model */
-	if (model) {
-		CHECK_FCT(  fd_dict_getval(model, &dictdata)  );
-	} else {
-		/* For unknown AVP, just don't change the size */
-		if (_C(object)->type == MSG_AVP)
-			return 0;
-	}
-	
-	/* Deal with easy cases: AVPs without children */
-	if ((_C(object)->type == MSG_AVP) && (dictdata.avpdata.avp_basetype != AVP_TYPE_GROUPED)) {
-		/* Sanity check */
-		ASSERT(FD_IS_LIST_EMPTY(&_A(object)->avp_chain.children));
-		
-		/* Now check that the data is set in the AVP */
-		CHECK_PARAMS(  _A(object)->avp_public.avp_value  );
-		
-		sz = GETAVPHDRSZ( _A(object)->avp_public.avp_flags );
-		
-		switch (dictdata.avpdata.avp_basetype) {
-			case AVP_TYPE_OCTETSTRING:
-				sz += _A(object)->avp_public.avp_value->os.len;
-				break;
-			
-			case AVP_TYPE_INTEGER32:
-			case AVP_TYPE_INTEGER64:
-			case AVP_TYPE_UNSIGNED32:
-			case AVP_TYPE_UNSIGNED64:
-			case AVP_TYPE_FLOAT32:
-			case AVP_TYPE_FLOAT64:
-				sz += avp_value_sizes[dictdata.avpdata.avp_basetype];
-				break;
-			
-			default:
-				/* Something went wrong... */
-				ASSERT(0);
-		}
-	}
-	else  /* message or grouped AVP */
-	{
-		struct fd_list * ch = NULL;
-		
-		/* First, compute the header size */
-		if (_C(object)->type == MSG_AVP) {
-			sz = GETAVPHDRSZ( _A(object)->avp_public.avp_flags );
-		} else {
-			sz = GETMSGHDRSZ( );
-		}
-		
-		/* Recurse in all children and update the sz information */
-		for (ch = _C(object)->children.next; ch != &_C(object)->children; ch = ch->next) {
-			CHECK_FCT(  fd_msg_update_length ( ch->o )  );
-			
-			/* Add the padded size to the parent */
-			sz += PAD4( _A(ch->o)->avp_public.avp_len );
-		}
-	}
-	
-	/* When we arrive here, the "sz" variable contains the size to write in the object */
-	if (_C(object)->type == MSG_AVP) 
-		_A(object)->avp_public.avp_len = sz;
-	else
-		_M(object)->msg_public.msg_length = sz;
-	
-	return 0;
-}
-
-/***************************************************************************************************************/
-/* Macro to check if further callbacks must be called */
-#define TEST_ACTION_STOP()					\
-	if ((*msg == NULL) || (*action != DISP_ACT_CONT))	\
-		goto no_error;
-
-/* Call all dispatch callbacks for a given message */
-int fd_msg_dispatch ( struct msg ** msg, struct session * session, enum disp_action *action, const char ** error_code)
-{
-	struct dictionary  * dict;
-	struct dict_object * app;
-	struct dict_object * cmd;
-	struct avp * avp;
-	struct fd_list * cb_list;
-	int ret = 0;
-	
-	TRACE_ENTRY("%p %p %p %p", msg, session, action, error_code);
-	CHECK_PARAMS( msg && CHECK_MSG(*msg) && action);
-	
-	if (error_code)
-		*error_code = NULL;
-	*action = DISP_ACT_CONT;
-	
-	/* Take the dispatch lock */
-	CHECK_FCT( pthread_rwlock_rdlock(&fd_disp_lock) );
-	pthread_cleanup_push( fd_cleanup_rwlock, &fd_disp_lock );
-	
-	/* First, call the DISP_HOW_ANY callbacks */
-	CHECK_FCT_DO( ret = fd_disp_call_cb_int( NULL, msg, NULL, session, action, NULL, NULL, NULL, NULL ), goto error );
-
-	TEST_ACTION_STOP();
-	
-	/* If we don't know the model at this point, we stop cause we cannot get the dictionary. It's invalid: an error should already have been trigged by ANY callbacks */
-	CHECK_PARAMS_DO(cmd = (*msg)->msg_model, { ret = EINVAL; goto error; } );
-	
-	/* Now resolve message application */
-	CHECK_FCT_DO( ret = fd_dict_getdict( cmd, &dict ), goto error );
-	CHECK_FCT_DO( ret = fd_dict_search( dict, DICT_APPLICATION, APPLICATION_BY_ID, &(*msg)->msg_public.msg_appl, &app, 0 ), goto error );
-	
-	if (app == NULL) {
-		if ((*msg)->msg_public.msg_flags & CMD_FLAG_REQUEST) {
-			if (error_code)
-				*error_code = "DIAMETER_APPLICATION_UNSUPPORTED";
-			*action = DISP_ACT_ERROR;
-		} else {
-			TRACE_DEBUG(INFO, "Received an answer to a local query with an unsupported application %d, discarding...",  (*msg)->msg_public.msg_appl);
-			fd_msg_dump_walk(INFO, *msg);
-			fd_msg_free(*msg);
-			*msg = NULL;
-		}
-		goto no_error;
-	}
-	
-	/* So start browsing the message */
-	CHECK_FCT_DO( ret = fd_msg_browse( *msg, MSG_BRW_FIRST_CHILD, &avp, NULL ), goto error );
-	while (avp != NULL) {
-		/* For unknown AVP, we don't have a callback registered, so just skip */
-		if (avp->avp_model) {
-			struct dict_object * enumval = NULL;
-			
-			/* Get the list of callback for this AVP */
-			CHECK_FCT_DO( ret = fd_dict_disp_cb(DICT_AVP, avp->avp_model, &cb_list), goto error );
-			
-			/* We search enumerated values only in case of non-grouped AVP */
-			if ( avp->avp_public.avp_value ) {
-				struct dict_object * type;
-				/* Check if the AVP has a constant value */
-				CHECK_FCT_DO( ret = fd_dict_search(dict, DICT_TYPE, TYPE_OF_AVP, avp->avp_model, &type, 0), goto error );
-				if (type) {
-					struct dict_enumval_request req;
-					memset(&req, 0, sizeof(struct dict_enumval_request));
-					req.type_obj = type;
-					memcpy( &req.search.enum_value, avp->avp_public.avp_value, sizeof(union avp_value) );
-					CHECK_FCT_DO( ret = fd_dict_search(dict, DICT_ENUMVAL, ENUMVAL_BY_STRUCT, &req, &enumval, 0), goto error );
-				}
-			}
-			
-			/* Call the callbacks */
-			CHECK_FCT_DO( ret = fd_disp_call_cb_int( cb_list, msg, avp, session, action, app, cmd, avp->avp_model, enumval ), goto error );
-			TEST_ACTION_STOP();
-		}
-		/* Go to next AVP */
-		CHECK_FCT_DO(  ret = fd_msg_browse( avp, MSG_BRW_WALK, &avp, NULL ), goto error );
-	}
-		
-	/* Now call command and application callbacks */
-	CHECK_FCT_DO( ret = fd_dict_disp_cb(DICT_COMMAND, cmd, &cb_list), goto error );
-	CHECK_FCT_DO( ret = fd_disp_call_cb_int( cb_list, msg, NULL, session, action, app, cmd, NULL, NULL ), goto error );
-	TEST_ACTION_STOP();
-	
-	if (app) {
-		CHECK_FCT_DO( ret = fd_dict_disp_cb(DICT_APPLICATION, app, &cb_list), goto error );
-		CHECK_FCT_DO( ret = fd_disp_call_cb_int( cb_list, msg, NULL, session, action, app, cmd, NULL, NULL ), goto error );
-		TEST_ACTION_STOP();
-	}
-	
-	pthread_cleanup_pop(0);
-	
-no_error:
-	CHECK_POSIX(pthread_rwlock_unlock(&fd_disp_lock) );
-	return 0;
-	
-error:
-	CHECK_POSIX_DO(pthread_rwlock_unlock(&fd_disp_lock), /* ignore */ );
-	return ret;
-}
Index: bfreeDiameter/rt_data.c
===================================================================
--- libfreeDiameter/rt_data.c	(revision 563)
+++ 	(revision )
@@ -1,306 +1,0 @@
-/*********************************************************************************************************
-* Software License Agreement (BSD License)                                                               *
-* Author: Sebastien Decugis <sdecugis@nict.go.jp>							 *
-*													 *
-* Copyright (c) 2010, 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.								 *
-*********************************************************************************************************/
-
-/* Routing module helpers.
- * 
- * This file provides support for the rt_data structure manipulation.
- */
-
-#include "libfD.h"
-
-/* Structure that contains the routing data for a message */
-struct rt_data {
-	int		extracted;	/* if 0, candidates is ordered by diamid, otherwise the order is unspecified */
-	struct fd_list	candidates;	/* All the candidates. Items are struct rtd_candidate. */
-	struct fd_list	errors;		/* All errors received from other peers for this message */
-};
-
-/* Items of the errors list */
-struct rtd_error {
-	struct fd_list	chain;	/* link in the list, ordered by nexthop */
-	char * 		nexthop;/* the peer the message was sent to */
-	char *		erh;	/* the origin of the error */
-	uint32_t	code;	/* the error code */
-};
-
-/* Create a new structure to store routing data */
-int  fd_rtd_init(struct rt_data ** rtd)
-{
-	struct rt_data *new;
-	TRACE_ENTRY("%p", rtd);
-	CHECK_PARAMS(rtd);
-	
-	/* Alloc the structure */
-	CHECK_MALLOC( new = malloc(sizeof(struct rt_data)) );
-	memset(new, 0, sizeof(struct rt_data) );
-	fd_list_init(&new->candidates, new);
-	fd_list_init(&new->errors, new);
-	
-	*rtd = new;
-	return 0;
-}
-
-/* Destroy the routing data */
-void fd_rtd_free(struct rt_data ** rtd)
-{
-	struct rt_data *old;
-	
-	TRACE_ENTRY("%p", rtd);
-	CHECK_PARAMS_DO(rtd, return );
-	
-	old = *rtd;
-	*rtd = NULL;
-	
-	while (!FD_IS_LIST_EMPTY(&old->candidates)) {
-		struct rtd_candidate * c = (struct rtd_candidate *) old->candidates.next;
-		
-		fd_list_unlink(&c->chain);
-		free(c->diamid);
-		free(c->realm);
-		free(c);
-	}
-	
-	while (!FD_IS_LIST_EMPTY(&old->errors)) {
-		struct rtd_error * c = (struct rtd_error *) old->errors.next;
-		
-		fd_list_unlink(&c->chain);
-		free(c->nexthop);
-		free(c->erh);
-		free(c);
-	}
-	
-	free(old);
-	
-	return;
-}
-
-/* Add a peer to the candidates list */
-int  fd_rtd_candidate_add(struct rt_data * rtd, char * peerid, char * realm)
-{
-	struct fd_list * prev;
-	struct rtd_candidate * new;
-	
-	TRACE_ENTRY("%p %p", rtd, peerid);
-	CHECK_PARAMS(rtd && peerid);
-	
-	/* Since the peers are ordered when they are added (fd_g_activ_peers) we search for the position from the end -- this should be efficient */
-	for (prev = rtd->candidates.prev; prev != &rtd->candidates; prev = prev->prev) {
-		struct rtd_candidate * cp = (struct rtd_candidate *) prev;
-		int cmp = strcmp(peerid, cp->diamid);
-		if (cmp > 0)
-			break;
-		if (cmp == 0)
-			/* The candidate is already in the list */
-			return 0;
-	}
-	
-	CHECK_MALLOC( new = malloc(sizeof(struct rtd_candidate)) );
-	memset(new, 0, sizeof(struct rtd_candidate) );
-	fd_list_init(&new->chain, NULL);
-	CHECK_MALLOC( new->diamid = strdup(peerid) );
-	CHECK_MALLOC( new->realm = strdup(realm) );
-	
-	fd_list_insert_after(prev, &new->chain);
-	
-	return 0;
-}
-
-/* Remove a peer from the candidates (if it is found) */
-void fd_rtd_candidate_del(struct rt_data * rtd, char * peerid, size_t sz /* if !0, peerid does not need to be \0 terminated */)
-{
-	struct fd_list * li;
-	
-	TRACE_ENTRY("%p %p %zd", rtd, peerid, sz);
-	CHECK_PARAMS_DO( rtd && peerid , return );
-	
-	for (li = rtd->candidates.next; li != &rtd->candidates; li = li->next) {
-		struct rtd_candidate * c = (struct rtd_candidate *) li;
-		int cmp;
-		if (sz) {
-			cmp = strncmp(peerid, c->diamid, sz);
-			if (!cmp) {
-				int len = strlen(c->diamid);
-				if (sz < len)
-					cmp = -1;
-				else if (sz == len)
-					cmp = 0;
-				else cmp = 1;
-			}
-		} else {
-			cmp = strcmp(peerid, c->diamid);
-		}
-		
-		if (!cmp) {
-			/* Found it! Remove it */
-			fd_list_unlink(&c->chain);
-			free(c->diamid);
-			free(c->realm);
-			free(c);
-			break;
-		}
-		
-		if (cmp > 0)
-			continue;
-		
-		/* The list is ordered only if not extracted */
-		if (! rtd->extracted)
-			break;
-	}
-	
-	return;
-}
-
-/* If a peer returned a protocol error for this message, save it so that we don't try to send it there again */
-int  fd_rtd_error_add(struct rt_data * rtd, char * sentto, uint8_t * origin, size_t originsz, uint32_t rcode)
-{
-	struct fd_list * li;
-	int match = 0;
-	
-	TRACE_ENTRY("%p %p %p %d", rtd, sentto, origin, rcode);
-	CHECK_PARAMS( rtd && sentto ); /* origin may be NULL */
-	
-	/* First add the new error entry */
-	for (li = rtd->errors.next; li != &rtd->errors; li = li->next) {
-		struct rtd_error * e = (struct rtd_error *) li;
-		int cmp = strcmp(sentto, e->nexthop);
-		if (cmp > 0)
-			continue;
-		if (!cmp)
-			match = 1;
-		break;
-	}
-	
-	/* If we already had this entry, we should not have sent the message again to this peer... anyway... */
-	if (!match) {
-		/* Add a new entry in the error list */
-		struct rtd_error * new;
-		CHECK_MALLOC( new = malloc(sizeof(struct rtd_error)) );
-		memset(new, 0, sizeof(struct rtd_error));
-		fd_list_init(&new->chain, NULL);
-		CHECK_MALLOC(new->nexthop = strdup(sentto));
-		if (origin) {
-			CHECK_MALLOC( new->erh = malloc(originsz + 1) );
-			memcpy(new->erh, origin, originsz);
-			new->erh[originsz] = '\0';
-		}
-		new->code = rcode;
-		fd_list_insert_before(li, &new->chain);
-	}
-	
-	/* Finally, remove this (these) peers from the candidate list */
-	fd_rtd_candidate_del(rtd, sentto, 0);
-	if (origin)
-		fd_rtd_candidate_del(rtd, (char *)origin, originsz);
-	
-	/* Done! */
-	return 0;
-}
-
-/* Extract the list of valid candidates, and initialize their scores */
-void fd_rtd_candidate_extract(struct rt_data * rtd, struct fd_list ** candidates, int ini_score)
-{
-	struct fd_list * li;
-	
-	TRACE_ENTRY("%p %p", rtd, candidates);
-	CHECK_PARAMS_DO( candidates, return );
-	CHECK_PARAMS_DO( rtd, { *candidates = NULL; return; } );
-	
-	*candidates = &rtd->candidates;
-	
-	/* Reset all scores to INITIAL score */
-	for (li = rtd->candidates.next; li != &rtd->candidates; li = li->next) {
-		struct rtd_candidate * c = (struct rtd_candidate *) li;
-		c->score = ini_score;
-	}
-	
-	rtd->extracted = 1;
-	return;
-}
-
-/* Reorder the list of peers. If several peer have the same highest score, they are randomized. */
-int  fd_rtd_candidate_reorder(struct fd_list * candidates)
-{
-	struct fd_list unordered = FD_LIST_INITIALIZER(unordered), *li;
-	struct fd_list highest = FD_LIST_INITIALIZER(highest);
-	int hs = -1;
-	
-	TRACE_ENTRY("%p", candidates);
-	CHECK_PARAMS( candidates );
-	
-	/* First, move all items from candidates to the undordered list */
-	fd_list_move_end(&unordered, candidates);
-	
-	/* Now extract each element from unordered and add it back to list ordered by score */
-	while (!FD_IS_LIST_EMPTY(&unordered)) {
-		struct rtd_candidate * c = (struct rtd_candidate *) unordered.next;
-		
-		fd_list_unlink(&c->chain);
-		
-		/* If this candidate has a higher score than the previous ones */
-		if (c->score > hs) {
-			/* Then we move the previous high score items at end of the list */
-			fd_list_move_end(candidates, &highest);
-			
-			/* And the new high score is this reset */
-			hs = c->score;
-		}
-		
-		/* If this candidate equals the higher score, add it into highest list at a random place */
-		if (c->score == hs) {
-			if (rand() & 1) {
-				fd_list_insert_after(&highest, &c->chain);
-			} else {
-				fd_list_insert_before(&highest, &c->chain);
-			}
-		/* Otherwise, insert at normal place in the list */
-		} else {
-			/* Find the position in ordered candidates list */
-			for (li = candidates->next; li != candidates; li = li->next) {
-				struct rtd_candidate * cnext = (struct rtd_candidate *) li;
-				if (cnext->score >= c->score)
-					break;
-			}
-
-			/* Add the element there */
-			fd_list_insert_before(li, &c->chain);
-		}
-	}
-	
-	/* Now simply move back all the "highest" candidates at the end of the list */
-	fd_list_move_end(candidates, &highest);
-	
-	return 0;
-}
-
Index: bfreeDiameter/sessions.c
===================================================================
--- libfreeDiameter/sessions.c	(revision 639)
+++ 	(revision )
@@ -1,815 +1,0 @@
-/*********************************************************************************************************
-* Software License Agreement (BSD License)                                                               *
-* Author: Sebastien Decugis <sdecugis@nict.go.jp>							 *
-*													 *
-* Copyright (c) 2010, WIDE Project and NICT								 *
-* All rights reserved.											 *
-* 													 *
-* Redistribution and use of this software in source and binary forms, with or without modification, are  *
-* permitted provided that the following conditions are met:						 *
-* 													 *
-* * Redistributions of source code must retain the above 						 *
-*   copyright notice, this list of conditions and the 							 *
-*   following disclaimer.										 *
-*    													 *
-* * Redistributions in binary form must reproduce the above 						 *
-*   copyright notice, this list of conditions and the 							 *
-*   following disclaimer in the documentation and/or other						 *
-*   materials provided with the distribution.								 *
-* 													 *
-* * Neither the name of the WIDE Project or NICT nor the 						 *
-*   names of its contributors may be used to endorse or 						 *
-*   promote products derived from this software without 						 *
-*   specific prior written permission of WIDE Project and 						 *
-*   NICT.												 *
-* 													 *
-* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED *
-* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
-* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR *
-* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 	 *
-* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 	 *
-* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR *
-* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF   *
-* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.								 *
-*********************************************************************************************************/
-
-/* Sessions module.
- * 
- * Basic functionalities to help implementing User sessions state machines from RFC3588.
- */
-
-#include "libfD.h"
-
-/*********************** Parameters **********************/
-
-/* Size of the hash table containing the session objects (pow of 2. ex: 6 => 2^6 = 64). must be between 0 and 31. */
-#ifndef SESS_HASH_SIZE
-#define SESS_HASH_SIZE	6
-#endif /* SESS_HASH_SIZE */
-
-/* Default lifetime of a session, in seconds. (31 days = 2678400 seconds) */
-#ifndef SESS_DEFAULT_LIFETIME
-#define SESS_DEFAULT_LIFETIME	2678400
-#endif /* SESS_DEFAULT_LIFETIME */
-
-/********************** /Parameters **********************/
-
-/* Eyescatchers definitions */
-#define SH_EYEC 0x53554AD1
-#define SD_EYEC 0x5355D474
-#define SI_EYEC 0x53551D
-
-/* Macro to check an object is valid */
-#define VALIDATE_SH( _obj ) ( ((_obj) != NULL) && ( ((struct session_handler *)(_obj))->eyec == SH_EYEC) )
-#define VALIDATE_SI( _obj ) ( ((_obj) != NULL) && ( ((struct session         *)(_obj))->eyec == SI_EYEC) )
-
-
-/* Handlers registered by users of the session module */
-struct session_handler {
-	int		  eyec;	/* An eye catcher also used to ensure the object is valid, must be SH_EYEC */
-	int		  id;	/* A unique integer to identify this handler */
-	void 		(*cleanup)(session_state *, char *, void *); /* The cleanup function to be called for cleaning a state */
-	void             *opaque; /* a value that is passed as is to the cleanup callback */
-};
-
-static int 		hdl_id = 0;				/* A global counter to initialize the id field */
-static pthread_mutex_t	hdl_lock = PTHREAD_MUTEX_INITIALIZER;	/* lock to protect hdl_id; we could use atomic operations otherwise (less portable) */
-
-
-/* Data structures linked from the sessions, containing the applications states */
-struct state {
-	int			 eyec;	/* Must be SD_EYEC */
-	session_state		*state;	/* The state registered by the application, never NULL (or the whole object is deleted) */
-	struct fd_list		 chain;	/* Chaining in the list of session's states ordered by hdl->id */
-	union {
-		struct session_handler	*hdl;	/* The handler for which this state was registered */
-		char 			*sid;	/* For deleted state, the sid of the session it belong to */
-	};
-};
-
-/* Session object, one for each value of Session-Id AVP */
-struct session {
-	int 		eyec;	/* Eyecatcher, SI_EYEC */
-	
-	char *		sid;	/* The \0-terminated Session-Id */
-	uint32_t	hash;	/* computed hash of sid */
-	struct fd_list	chain_h;/* chaining in the hash table of sessions. */
-	
-	struct timespec	timeout;/* Timeout date for the session */
-	struct fd_list	expire;	/* List of expiring sessions, ordered by timeouts. */
-	
-	pthread_mutex_t stlock;	/* A lock to protect the list of states associated with this session */
-	struct fd_list	states;	/* Sentinel for the list of states of this session. */
-	int		msg_cnt;/* Reference counter for the messages pointing to this session */
-};
-
-/* Sessions hash table, to allow fast sid to session retrieval */
-static struct {
-	struct fd_list	sentinel;	/* sentinel element for this sublist */
-	pthread_mutex_t lock;		/* the mutex for this sublist -- we might probably change it to rwlock for a little optimization */
-} sess_hash [ 1 << SESS_HASH_SIZE ] ;
-#define H_MASK( __hash ) ((__hash) & (( 1 << SESS_HASH_SIZE ) - 1))
-#define H_LIST( _hash ) (&(sess_hash[H_MASK(_hash)].sentinel))
-#define H_LOCK( _hash ) (&(sess_hash[H_MASK(_hash)].lock    ))
-
-/* The following are used to generate sid values that are eternaly unique */
-static uint32_t   	sid_h;	/* initialized to the current time in fd_sess_init */
-static uint32_t   	sid_l;	/* incremented each time a session id is created */
-static pthread_mutex_t 	sid_lock = PTHREAD_MUTEX_INITIALIZER;
-
-/* Expiring sessions management */
-static struct fd_list	exp_sentinel = FD_LIST_INITIALIZER(exp_sentinel);	/* list of sessions ordered by their timeout date */
-static pthread_mutex_t	exp_lock = PTHREAD_MUTEX_INITIALIZER;	/* lock protecting the list. */
-static pthread_cond_t	exp_cond = PTHREAD_COND_INITIALIZER;	/* condvar used by the expiry mecahinsm. */
-static pthread_t	exp_thr; 	/* The expiry thread that handles cleanup of expired sessions */
-
-/* Hierarchy of the locks, to avoid deadlocks:
- *  hash lock > state lock > expiry lock
- * i.e. state lock can be taken while holding the hash lock, but not while holding the expiry lock.
- * As well, the hash lock cannot be taken while holding a state lock.
- */
-
-/********************************************************************************************************/
-
-/* Initialize a session object. It is not linked now. sid must be already malloc'ed. */
-static struct session * new_session(char * sid, size_t sidlen)
-{
-	struct session * sess;
-	
-	TRACE_ENTRY("%p %d", sid, sidlen);
-	CHECK_PARAMS_DO( sid && sidlen, return NULL );
-	
-	CHECK_MALLOC_DO( sess = malloc(sizeof(struct session)), return NULL );
-	memset(sess, 0, sizeof(struct session));
-	
-	sess->eyec = SI_EYEC;
-	
-	sess->sid  = sid;
-	sess->hash = fd_hash(sid, sidlen);
-	fd_list_init(&sess->chain_h, sess);
-	
-	CHECK_SYS_DO( clock_gettime(CLOCK_REALTIME, &sess->timeout), return NULL );
-	sess->timeout.tv_sec += SESS_DEFAULT_LIFETIME;
-	fd_list_init(&sess->expire, sess);
-	
-	CHECK_POSIX_DO( pthread_mutex_init(&sess->stlock, NULL), return NULL );
-	fd_list_init(&sess->states, sess);
-	
-	return sess;
-}
-	
-/* The expiry thread */
-static void * exp_fct(void * arg)
-{
-	fd_log_threadname ( "Session/expire" );
-	TRACE_ENTRY( "" );
-	
-	
-	do {
-		struct timespec	now;
-		struct session * first;
-		
-		CHECK_POSIX_DO( pthread_mutex_lock(&exp_lock),  break );
-		pthread_cleanup_push( fd_cleanup_mutex, &exp_lock );
-again:		
-		/* Check if there are expiring sessions available */
-		if (FD_IS_LIST_EMPTY(&exp_sentinel)) {
-			/* Just wait for a change or cancelation */
-			CHECK_POSIX_DO( pthread_cond_wait( &exp_cond, &exp_lock ), break );
-			/* Restart the loop on wakeup */
-			goto again;
-		}
-		
-		/* Get the pointer to the session that expires first */
-		first = (struct session *)(exp_sentinel.next->o);
-		ASSERT( VALIDATE_SI(first) );
-		
-		/* Get the current time */
-		CHECK_SYS_DO(  clock_gettime(CLOCK_REALTIME, &now),  break  );
-
-		/* If first session is not expired, we just wait until it happens */
-		if ( TS_IS_INFERIOR( &now, &first->timeout ) ) {
-			
-			CHECK_POSIX_DO2(  pthread_cond_timedwait( &exp_cond, &exp_lock, &first->timeout ),  
-					ETIMEDOUT, /* ETIMEDOUT is a normal error, continue */,
-					/* on other error, */ break );
-	
-			/* on wakeup, loop */
-			goto again;
-		}
-		
-		/* Now, the first session in the list is expired; destroy it */
-		pthread_cleanup_pop( 0 );
-		CHECK_POSIX_DO( pthread_mutex_unlock(&exp_lock),  break );
-		
-		CHECK_FCT_DO( fd_sess_destroy( &first ), break );
-		
-	} while (1);
-	
-	TRACE_DEBUG(INFO, "An error occurred in session module! Expiry thread is terminating...");
-	ASSERT(0);
-	return NULL;
-}
-	
-	
-
-/********************************************************************************************************/
-
-/* Initialize the session module */
-int fd_sess_init(void)
-{
-	int i;
-	
-	TRACE_ENTRY( "" );
-	
-	/* Initialize the global counters */
-	sid_h = (uint32_t) time(NULL);
-	sid_l = 0;
-	
-	/* Initialize the hash table */
-	for (i = 0; i < sizeof(sess_hash) / sizeof(sess_hash[0]); i++) {
-		fd_list_init( &sess_hash[i].sentinel, NULL );
-		CHECK_POSIX(  pthread_mutex_init(&sess_hash[i].lock, NULL)  );
-	}
-	
-	return 0;
-}
-
-/* Run this when initializations are complete. */
-int fd_sess_start(void)
-{
-	/* Start session garbage collector (expiry) */
-	CHECK_POSIX(  pthread_create(&exp_thr, NULL, exp_fct, NULL)  );
-	
-	return 0;
-}
-
-/* Terminate */
-void fd_sess_fini(void)
-{
-	TRACE_ENTRY("");
-	CHECK_FCT_DO( fd_thr_term(&exp_thr), /* continue */ );
-	return;
-}
-
-/* Create a new handler */
-int fd_sess_handler_create_internal ( struct session_handler ** handler, void (*cleanup)(session_state * state, char * sid, void * opaque), void * opaque )
-{
-	struct session_handler *new;
-	
-	TRACE_ENTRY("%p %p", handler, cleanup);
-	
-	CHECK_PARAMS( handler && cleanup );
-	
-	CHECK_MALLOC( new = malloc(sizeof(struct session_handler)) );
-	memset(new, 0, sizeof(struct session_handler));
-	
-	CHECK_POSIX( pthread_mutex_lock(&hdl_lock) );
-	new->id = ++hdl_id;
-	CHECK_POSIX( pthread_mutex_unlock(&hdl_lock) );
-	
-	new->eyec = SH_EYEC;
-	new->cleanup = cleanup;
-	new->opaque = opaque;
-	
-	*handler = new;
-	return 0;
-}
-
-/* Destroy a handler, and all states attached to this handler. This operation is very slow but we don't care since it's rarely used. 
- * Note that it's better to call this function after all sessions have been deleted... */
-int fd_sess_handler_destroy ( struct session_handler ** handler, void ** opaque )
-{
-	struct session_handler * del;
-	/* place to save the list of states to be cleaned up. We do it after finding them to avoid deadlocks. the "o" field becomes a copy of the sid. */
-	struct fd_list deleted_states = FD_LIST_INITIALIZER( deleted_states );
-	int i;
-	
-	TRACE_ENTRY("%p", handler);
-	CHECK_PARAMS( handler && VALIDATE_SH(*handler) );
-	
-	del = *handler;
-	*handler = NULL;
-	
-	del->eyec = 0xdead; /* The handler is not valid anymore for any other operation */
-	
-	/* Now find all sessions with data registered for this handler, and move this data to the deleted_states list. */
-	for (i = 0; i < sizeof(sess_hash) / sizeof(sess_hash[0]); i++) {
-		struct fd_list * li_si;
-		CHECK_POSIX(  pthread_mutex_lock(&sess_hash[i].lock)  );
-		
-		for (li_si = sess_hash[i].sentinel.next; li_si != &sess_hash[i].sentinel; li_si = li_si->next) {
-			struct fd_list * li_st;
-			struct session * sess = (struct session *)(li_si->o);
-			CHECK_POSIX(  pthread_mutex_lock(&sess->stlock)  );
-			for (li_st = sess->states.next; li_st != &sess->states; li_st = li_st->next) {
-				struct state * st = (struct state *)(li_st->o);
-				/* The list is ordered */
-				if (st->hdl->id < del->id)
-					continue;
-				if (st->hdl->id == del->id) {
-					/* This state belongs to the handler we are deleting, move the item to the deleted_states list */
-					fd_list_unlink(&st->chain);
-					CHECK_MALLOC( st->sid = strdup(sess->sid) );
-					fd_list_insert_before(&deleted_states, &st->chain);
-				}
-				break;
-			}
-			CHECK_POSIX(  pthread_mutex_unlock(&sess->stlock)  );
-		}
-		CHECK_POSIX(  pthread_mutex_unlock(&sess_hash[i].lock)  );
-	}
-	
-	/* Now, delete all states after calling their cleanup handler */
-	while (!FD_IS_LIST_EMPTY(&deleted_states)) {
-		struct state * st = (struct state *)(deleted_states.next->o);
-		TRACE_DEBUG(FULL, "Calling cleanup handler for session '%s' and data %p", st->sid, st->state);
-		(*del->cleanup)(st->state, st->sid, del->opaque);
-		free(st->sid);
-		fd_list_unlink(&st->chain);
-		free(st);
-	}
-	
-	if (opaque)
-		*opaque = del->opaque;
-	
-	/* Free the handler */
-	free(del);
-	
-	return 0;
-}
-
-
-
-/* Create a new session object with the default timeout value, and link it */
-int fd_sess_new ( struct session ** session, char * diamId, char * opt, size_t optlen )
-{
-	char * sid = NULL;
-	size_t sidlen;
-	struct session * sess;
-	struct fd_list * li;
-	int found = 0;
-	
-	TRACE_ENTRY("%p %p %p %d", session, diamId, opt, optlen);
-	CHECK_PARAMS( session && (diamId || opt) );
-	
-	/* Ok, first create the identifier for the string */
-	if (diamId == NULL) {
-		/* opt is the full string */
-		if (optlen) {
-			CHECK_MALLOC( sid = malloc(optlen + 1) );
-			strncpy(sid, opt, optlen);
-			sid[optlen] = '\0';
-			sidlen = optlen;
-		} else {
-			CHECK_MALLOC( sid = strdup(opt) );
-			sidlen = strlen(sid);
-		}
-	} else {
-		uint32_t sid_h_cpy;
-		uint32_t sid_l_cpy;
-		/* "<diamId>;<high32>;<low32>[;opt]" */
-		sidlen = strlen(diamId);
-		sidlen += 22; /* max size of ';<high32>;<low32>' */
-		if (opt)
-			sidlen += 1 + (optlen ?: strlen(opt)) ;
-		sidlen++; /* space for the final \0 also */
-		CHECK_MALLOC( sid = malloc(sidlen) );
-		
-		CHECK_POSIX( pthread_mutex_lock(&sid_lock) );
-		if ( ++sid_l == 0 ) /* overflow */
-			++sid_h;
-		sid_h_cpy = sid_h;
-		sid_l_cpy = sid_l;
-		CHECK_POSIX( pthread_mutex_unlock(&sid_lock) );
-		
-		if (opt) {
-			if (optlen)
-				sidlen = snprintf(sid, sidlen, "%s;%u;%u;%.*s", diamId, sid_h_cpy, sid_l_cpy, (int)optlen, opt);
-			else
-				sidlen = snprintf(sid, sidlen, "%s;%u;%u;%s", diamId, sid_h_cpy, sid_l_cpy, opt);
-		} else {
-			sidlen = snprintf(sid, sidlen, "%s;%u;%u", diamId, sid_h_cpy, sid_l_cpy);
-		}
-	}
-	
-	/* Initialize the session object now, to spend less time inside locked section later. 
-	 * Cons: we malloc then free if there is already a session with same SID; we could malloc later to avoid this. */
-	CHECK_MALLOC( sess = new_session(sid, sidlen) );
-	
-	/* Now find the place to add this object in the hash table. */
-	CHECK_POSIX( pthread_mutex_lock( H_LOCK(sess->hash) ) );
-	pthread_cleanup_push( fd_cleanup_mutex, H_LOCK(sess->hash) );
-	
-	for (li = H_LIST(sess->hash)->next; li != H_LIST(sess->hash); li = li->next) {
-		int cmp;
-		struct session * s = (struct session *)(li->o);
-		
-		/* The list is ordered by hash and sid (in case of collisions) */
-		if (s->hash < sess->hash)
-			continue;
-		if (s->hash > sess->hash)
-			break;
-		
-		cmp = strcasecmp(s->sid, sess->sid);
-		if (cmp < 0)
-			continue;
-		if (cmp > 0)
-			break;
-		
-		/* A session with the same sid was already in the hash table */
-		found = 1;
-		*session = s;
-		break;
-	}
-	
-	/* If the session did not exist, we can link it in global tables */
-	if (!found) {
-		fd_list_insert_before(li, &sess->chain_h); /* hash table */
-		
-		/* We must also insert in the expiry list */
-		CHECK_POSIX( pthread_mutex_lock( &exp_lock ) );
-		pthread_cleanup_push( fd_cleanup_mutex, &exp_lock );
-		
-		/* Find the position in that list. We take it in reverse order */
-		for (li = exp_sentinel.prev; li != &exp_sentinel; li = li->prev) {
-			struct session * s = (struct session *)(li->o);
-			if (TS_IS_INFERIOR( &s->timeout, &sess->timeout ) )
-				break;
-		}
-		fd_list_insert_after( li, &sess->expire );
-
-		/* We added a new expiring element, we must signal */
-		if (li == &exp_sentinel) {
-			CHECK_POSIX( pthread_cond_signal(&exp_cond) );
-		}
-		
-		#if 0
-		if (TRACE_BOOL(ANNOYING)) {	
-			TRACE_DEBUG(FULL, "-- Updated session expiry list --");
-			for (li = exp_sentinel.next; li != &exp_sentinel; li = li->next) {
-				struct session * s = (struct session *)(li->o);
-				fd_sess_dump(FULL, s);
-			}
-			TRACE_DEBUG(FULL, "-- end of expiry list --");
-		}
-		#endif
-		
-		/* We're done */
-		pthread_cleanup_pop(0);
-		CHECK_POSIX( pthread_mutex_unlock( &exp_lock ) );
-	}
-	
-	pthread_cleanup_pop(0);
-	CHECK_POSIX( pthread_mutex_unlock( H_LOCK(sess->hash) ) );
-	
-	/* If a session already existed, we must destroy the new element */
-	if (found) {
-		CHECK_FCT( fd_sess_destroy( &sess ) ); /* we could avoid locking this time for optimization */
-		return EALREADY;
-	}
-	
-	*session = sess;
-	return 0;
-}
-
-/* Find or create a session */
-int fd_sess_fromsid ( char * sid, size_t len, struct session ** session, int * new)
-{
-	int ret;
-	
-	TRACE_ENTRY("%p %d %p %p", sid, len, session, new);
-	CHECK_PARAMS( sid && session );
-	
-	/* All the work is done in sess_new */
-	ret = fd_sess_new ( session, NULL, sid, len );
-	switch (ret) {
-		case 0:
-		case EALREADY:
-			break;
-		
-		default:
-			CHECK_FCT(ret);
-	}
-	
-	if (new)
-		*new = ret ? 0 : 1;
-	
-	return 0;
-}
-
-/* Get the sid of a session */
-int fd_sess_getsid ( struct session * session, char ** sid )
-{
-	TRACE_ENTRY("%p %p", session, sid);
-	
-	CHECK_PARAMS( VALIDATE_SI(session) && sid );
-	
-	*sid = session->sid;
-	
-	return 0;
-}
-
-/* Change the timeout value of a session */
-int fd_sess_settimeout( struct session * session, const struct timespec * timeout )
-{
-	struct fd_list * li;
-	
-	TRACE_ENTRY("%p %p", session, timeout);
-	CHECK_PARAMS( VALIDATE_SI(session) && timeout );
-	
-	/* Lock -- do we need to lock the hash table as well? I don't think so... */
-	CHECK_POSIX( pthread_mutex_lock( &exp_lock ) );
-	pthread_cleanup_push( fd_cleanup_mutex, &exp_lock );
-	
-	/* Update the timeout */
-	fd_list_unlink(&session->expire);
-	memcpy(&session->timeout, timeout, sizeof(struct timespec));
-	
-	/* Find the new position in expire list. We take it in normal order */
-	for (li = exp_sentinel.next; li != &exp_sentinel; li = li->next) {
-		struct session * s = (struct session *)(li->o);
-
-		if (TS_IS_INFERIOR( &s->timeout, &session->timeout ) )
-			continue;
-
-		break;
-	}
-	fd_list_insert_before( li, &session->expire );
-
-	/* We added a new expiring element, we must signal if it was in first position */
-	if (session->expire.prev == &exp_sentinel) {
-		CHECK_POSIX( pthread_cond_signal(&exp_cond) );
-	}
-
-	#if 0
-	if (TRACE_BOOL(ANNOYING)) {	
-		TRACE_DEBUG(FULL, "-- Updated session expiry list --");
-		for (li = exp_sentinel.next; li != &exp_sentinel; li = li->next) {
-			struct session * s = (struct session *)(li->o);
-			fd_sess_dump(FULL, s);
-		}
-		TRACE_DEBUG(FULL, "-- end of expiry list --");
-	}
-	#endif
-
-	/* We're done */
-	pthread_cleanup_pop(0);
-	CHECK_POSIX( pthread_mutex_unlock( &exp_lock ) );
-	
-	return 0;
-}
-
-/* Destroy a session immediatly */
-int fd_sess_destroy ( struct session ** session )
-{
-	struct session * sess;
-	
-	TRACE_ENTRY("%p", session);
-	CHECK_PARAMS( session && VALIDATE_SI(*session) );
-	
-	sess = *session;
-	*session = NULL;
-	
-	/* Unlink and invalidate */
-	CHECK_FCT( pthread_mutex_lock( H_LOCK(sess->hash) ) );
-	pthread_cleanup_push( fd_cleanup_mutex, H_LOCK(sess->hash) );
-	CHECK_FCT( pthread_mutex_lock( &exp_lock ) );
-	fd_list_unlink( &sess->chain_h );
-	fd_list_unlink( &sess->expire ); /* no need to signal the condition here */
-	sess->eyec = 0xdead;
-	CHECK_FCT( pthread_mutex_unlock( &exp_lock ) );
-	pthread_cleanup_pop(0);
-	CHECK_FCT( pthread_mutex_unlock( H_LOCK(sess->hash) ) );
-	
-	/* Now destroy all states associated -- we don't take the lock since nobody can access this session anymore (in theory) */
-	while (!FD_IS_LIST_EMPTY(&sess->states)) {
-		struct state * st = (struct state *)(sess->states.next->o);
-		fd_list_unlink(&st->chain);
-		TRACE_DEBUG(FULL, "Calling handler %p cleanup for state registered with session '%s'", st->hdl, sess->sid);
-		(*st->hdl->cleanup)(st->state, sess->sid, st->hdl->opaque);
-		free(st);
-	}
-	
-	/* Finally, destroy the session itself */
-	free(sess->sid);
-	free(sess);
-	
-	return 0;
-}
-
-/* Destroy a session if it is not used */
-int fd_sess_reclaim ( struct session ** session )
-{
-	struct session * sess;
-	uint32_t hash;
-	
-	TRACE_ENTRY("%p", session);
-	CHECK_PARAMS( session && VALIDATE_SI(*session) );
-	
-	sess = *session;
-	hash = sess->hash;
-	*session = NULL;
-	
-	CHECK_POSIX( pthread_mutex_lock( H_LOCK(sess->hash) ) );
-	pthread_cleanup_push( fd_cleanup_mutex, H_LOCK(sess->hash) );
-	CHECK_POSIX( pthread_mutex_lock( &exp_lock ) );
-	if (FD_IS_LIST_EMPTY(&sess->states)) {
-		fd_list_unlink( &sess->chain_h );
-		fd_list_unlink( &sess->expire );
-		sess->eyec = 0xdead;
-		free(sess->sid);
-		free(sess);
-	}
-	CHECK_POSIX( pthread_mutex_unlock( &exp_lock ) );
-	pthread_cleanup_pop(0);
-	CHECK_POSIX( pthread_mutex_unlock( H_LOCK(hash) ) );
-	
-	return 0;
-}
-
-/* Save a state information with a session */
-int fd_sess_state_store_internal ( struct session_handler * handler, struct session * session, session_state ** state )
-{
-	struct state *new;
-	struct fd_list * li;
-	int already = 0;
-	
-	TRACE_ENTRY("%p %p %p", handler, session, state);
-	CHECK_PARAMS( handler && VALIDATE_SH(handler) && session && VALIDATE_SI(session) && state );
-	
-	/* Lock the session state list */
-	CHECK_POSIX( pthread_mutex_lock(&session->stlock) );
-	pthread_cleanup_push( fd_cleanup_mutex, &session->stlock );
-			
-	/* Create the new state object */
-	CHECK_MALLOC(new = malloc(sizeof(struct state)) );
-	memset(new, 0, sizeof(struct state));
-	
-	new->eyec = SD_EYEC;
-	new->state= *state;
-	fd_list_init(&new->chain, new);
-	new->hdl = handler;
-	
-	/* find place for this state in the list */
-	for (li = session->states.next; li != &session->states; li = li->next) {
-		struct state * st = (struct state *)(li->o);
-		/* The list is ordered by handler's id */
-		if (st->hdl->id < handler->id)
-			continue;
-		
-		if (st->hdl->id == handler->id) {
-			TRACE_DEBUG(INFO, "A state was already stored for session '%s' and handler '%p', at location %p", session->sid, st->hdl, st->state);
-			already = 1;
-		}
-		
-		break;
-	}
-	
-	if (!already) {
-		fd_list_insert_before(li, &new->chain);
-		*state = NULL;
-	} else {
-		free(new);
-	}
-	
-	pthread_cleanup_pop(0);
-	CHECK_POSIX( pthread_mutex_unlock(&session->stlock) );
-	
-	return already ? EALREADY : 0;
-}
-
-/* Get the data back */
-int fd_sess_state_retrieve_internal ( struct session_handler * handler, struct session * session, session_state ** state )
-{
-	struct fd_list * li;
-	struct state * st = NULL;
-	
-	TRACE_ENTRY("%p %p %p", handler, session, state);
-	CHECK_PARAMS( handler && VALIDATE_SH(handler) && session && VALIDATE_SI(session) && state );
-	
-	*state = NULL;
-	
-	/* Lock the session state list */
-	CHECK_POSIX( pthread_mutex_lock(&session->stlock) );
-	pthread_cleanup_push( fd_cleanup_mutex, &session->stlock );
-	
-	/* find the state in the list */
-	for (li = session->states.next; li != &session->states; li = li->next) {
-		st = (struct state *)(li->o);
-		
-		/* The list is ordered by handler's id */
-		if (st->hdl->id > handler->id)
-			break;
-	}
-	
-	/* If we found the state */
-	if (st && (st->hdl == handler)) {
-		fd_list_unlink(&st->chain);
-		*state = st->state;
-		free(st);
-	}
-	
-	pthread_cleanup_pop(0);
-	CHECK_POSIX( pthread_mutex_unlock(&session->stlock) );
-	
-	return 0;
-}
-
-/* For the messages module */
-int fd_sess_fromsid_msg ( unsigned char * sid, size_t len, struct session ** session, int * new)
-{
-	TRACE_ENTRY("%p %zd %p %p", sid, len, session, new);
-	CHECK_PARAMS( sid && len && session );
-	
-	/* Get the session object */
-	CHECK_FCT( fd_sess_fromsid ( (char *) sid, len, session, new) );
-	
-	/* Increase count */
-	CHECK_FCT( fd_sess_ref_msg ( *session ) );
-	
-	/* Done */
-	return 0;
-}
-
-int fd_sess_ref_msg ( struct session * session )
-{
-	TRACE_ENTRY("%p", session);
-	CHECK_PARAMS( VALIDATE_SI(session) );
-
-	/* Update the msg refcount */
-	CHECK_POSIX( pthread_mutex_lock(&session->stlock) );
-	session->msg_cnt++;
-	CHECK_POSIX( pthread_mutex_unlock(&session->stlock) );
-	
-	return 0;
-}
-
-int fd_sess_reclaim_msg ( struct session ** session )
-{
-	int reclaim;
-	
-	TRACE_ENTRY("%p", session);
-	CHECK_PARAMS( session && VALIDATE_SI(*session) );
-	
-	/* Update the msg refcount */
-	CHECK_POSIX( pthread_mutex_lock(&(*session)->stlock) );
-	reclaim = (*session)->msg_cnt;
-	(*session)->msg_cnt = reclaim - 1;
-	CHECK_POSIX( pthread_mutex_unlock(&(*session)->stlock) );
-	
-	if (reclaim == 1) {
-		CHECK_FCT(fd_sess_reclaim ( session ));
-	} else {
-		*session = NULL;
-	}
-	return 0;
-}
-
-
-
-/* Dump functions */
-void fd_sess_dump(int level, struct session * session)
-{
-	struct fd_list * li;
-	char buf[30];
-	struct tm tm;
-	
-	if (!TRACE_BOOL(level))
-		return;
-	
-	fd_log_debug("\t  %*s -- Session @%p --\n", level, "", session);
-	if (!VALIDATE_SI(session)) {
-		fd_log_debug("\t  %*s  Invalid session object\n", level, "");
-	} else {
-		
-		fd_log_debug("\t  %*s  sid '%s', hash %x\n", level, "", session->sid, session->hash);
-
-		strftime(buf, sizeof(buf), "%D,%T", localtime_r( &session->timeout.tv_sec , &tm ));
-		fd_log_debug("\t  %*s  timeout %s.%09ld\n", level, "", buf, session->timeout.tv_nsec);
-
-		CHECK_POSIX_DO( pthread_mutex_lock(&session->stlock), /* ignore */ );
-		pthread_cleanup_push( fd_cleanup_mutex, &session->stlock );
-		for (li = session->states.next; li != &session->states; li = li->next) {
-			struct state * st = (struct state *)(li->o);
-			fd_log_debug("\t  %*s    handler %d registered data %p\n", level, "", st->hdl->id, st->state);
-		}
-		pthread_cleanup_pop(0);
-		CHECK_POSIX_DO( pthread_mutex_unlock(&session->stlock), /* ignore */ );
-	}
-	fd_log_debug("\t  %*s -- end of session @%p --\n", level, "", session);
-}
-
-void fd_sess_dump_hdl(int level, struct session_handler * handler)
-{
-	if (!TRACE_BOOL(level))
-		return;
-	
-	fd_log_debug("\t  %*s -- Handler @%p --\n", level, "", handler);
-	if (!VALIDATE_SH(handler)) {
-		fd_log_debug("\t  %*s  Invalid session handler object\n", level, "");
-	} else {
-		fd_log_debug("\t  %*s  id %d, cleanup %p, opaque %p\n", level, "", handler->id, handler->cleanup, handler->opaque);
-	}
-	fd_log_debug("\t  %*s -- end of handler @%p --\n", level, "", handler);
-}	
Index: bfreeDiameter/signal.c
===================================================================
--- libfreeDiameter/signal.c	(revision 258)
+++ 	(revision )
@@ -1,456 +1,0 @@
-/*********************************************************************************************************
-* Software License Agreement (BSD License)                                                               *
-* Author: Sebastien Decugis <sdecugis@nict.go.jp>							 *
-*													 *
-* Copyright (c) 2010, 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 "libfD.h"
-
-#include <signal.h>
-
-/* A structure to hold the registered signal handlers */
-struct sig_hdl {
-	struct fd_list	chain;		/* Link in the sig_hdl_list ordered by signal */
-	int 		signal;		/* The signal this handler is registered for */
-	void 		(*sig_hdl)(int);/* The callback to call when the signal is caught */
-	pthread_t	sig_thr;	/* The thread that receives the signal */
-	char 		*modname;	/* The name of the module who registered the signal (for debug) */
-};
-
-/* The list of sig_hdl, and the mutex to protect it */
-static struct fd_list	sig_hdl_list = FD_LIST_INITIALIZER(sig_hdl_list);
-static pthread_mutex_t sig_hdl_lock  = PTHREAD_MUTEX_INITIALIZER;
-
-/* Detached thread attribute */
-static pthread_attr_t detached;
-
-/* Note if the module was initialized */
-static int sig_initialized = 0;
-
-/* signals short names list */
-static int abbrevs_init(void);
-
-/* Initialize the support for signals */
-int fd_sig_init(void)
-{
-	sigset_t sig_all;
-	
-	TRACE_ENTRY("");
-	
-	if (sig_initialized)
-		return 0;
-	
-	/* Initialize the list of abbreviations */
-	CHECK_FCT(abbrevs_init());
-	
-	/* Block all signals from the current thread and all its future children, so that signals are delivered only to our sig_hdl->sig_thr */
-	sigfillset(&sig_all);
-	CHECK_POSIX(  pthread_sigmask(SIG_BLOCK, &sig_all, NULL)  );
-	
-	/* Initialize the detached attribute */
-	CHECK_POSIX( pthread_attr_init(&detached) );
-	CHECK_POSIX( pthread_attr_setdetachstate(&detached, PTHREAD_CREATE_DETACHED) );
-	
-	sig_initialized++;
-	
-	/* That's all we have to do here ... */
-	return 0;
-}
-
-
-/* This thread (created detached) does call the signal handler */
-static void * signal_caller(void * arg)
-{
-	struct sig_hdl * me = arg;
-	char buf[40];
-	
-	/* Name this thread */
-	snprintf(buf, sizeof(buf), "cb %d:%s:%s", me->signal, fd_sig_abbrev(me->signal), me->modname);
-	fd_log_threadname ( buf );
-	
-	TRACE_ENTRY("%p", me);
-	
-	/* Call the signal handler */
-	(*me->sig_hdl)(me->signal);
-	
-	TRACE_DEBUG(CALL, "Callback completed");
-	
-	/* Done! */
-	return NULL;
-}
-
-/* This thread "really" receives the signal and starts the signal_caller thread */
-static void * signal_catcher(void * arg)
-{
-	struct sig_hdl * me = arg;
-	sigset_t ss;
-	char buf[40];
-	
-	ASSERT(arg);
-	
-	/* Name this thread */
-	snprintf(buf, sizeof(buf), "catch %d:%s:%s", me->signal, fd_sig_abbrev(me->signal), me->modname);
-	fd_log_threadname ( buf );
-	
-	TRACE_ENTRY("%p", me);
-	
-	sigemptyset(&ss);
-	sigaddset(&ss, me->signal);
-	
-	/* Now loop on the reception of the signal */
-	while (1) {
-		int sig;
-		pthread_t th;
-		
-		/* Wait to receive the next signal */
-		CHECK_POSIX_DO( sigwait(&ss, &sig), break );
-		
-		TRACE_DEBUG(FULL, "Signal %d caught", sig);
-		
-		/* Create the thread that will call the handler */
-		CHECK_POSIX_DO( pthread_create( &th, &detached, signal_caller, arg ), break );
-	}
-	
-	/* An error occurred... What should we do ? */
-	TRACE_DEBUG(INFO, "!!! ERROR !!! The signal catcher for %d:%s:%s is terminating!", me->signal, fd_sig_abbrev(me->signal), me->modname);
-	
-	/* Better way to handle this ? */
-	ASSERT(0);
-	return NULL;
-}
-
-/* Register a new callback to be called on reception of a given signal (it receives the signal as parameter) */
-/* EALREADY will be returned if there is already a callback registered on this signal */
-/* NOTE: the signal handler will be called from a new detached thread */
-int fd_sig_register(int signal, char * modname, void (*handler)(int signal))
-{
-	struct sig_hdl * new;
-	struct fd_list * next = NULL;
-	int matched = 0;
-	
-	TRACE_ENTRY("%d %p %p", signal, modname, handler);
-	CHECK_PARAMS( signal && modname && handler && sig_initialized );
-	
-	/* Alloc all memory before taking the lock  */
-	CHECK_MALLOC( new = malloc(sizeof(struct sig_hdl)) );
-	memset(new, 0, sizeof(struct sig_hdl));
-	fd_list_init(&new->chain, new);
-	new->signal = signal;
-	new->sig_hdl = handler;
-	CHECK_MALLOC_DO( new->modname = strdup(modname), { free(new); return ENOMEM; } );
-	
-	/* Search in the list if we already have a handler for this signal */
-	CHECK_POSIX(pthread_mutex_lock(&sig_hdl_lock));
-	for (next = sig_hdl_list.next; next != &sig_hdl_list; next = next->next) {
-		struct sig_hdl * nh = (struct sig_hdl *)next;
-		
-		if (nh->signal < signal)
-			continue;
-		if (nh->signal == signal) {
-			matched = 1;
-			TRACE_DEBUG(INFO, "Signal %d (%s) is already registered by module '%s'", signal, fd_sig_abbrev(signal), nh->modname);
-		}
-		break;
-	}
-	if (!matched)
-		/* Insert the new element before the next handle */
-		fd_list_insert_before(next, &new->chain);
-		
-	CHECK_POSIX(pthread_mutex_unlock(&sig_hdl_lock));
-	
-	if (matched)
-		return EALREADY; /* Already registered... */
-	
-	/* OK, now start the thread */
-	CHECK_POSIX( pthread_create( &new->sig_thr, NULL, signal_catcher, new ) );
-	
-	/* All good */
-	return 0;
-}
-
-/* Dump list of handlers */
-void fd_sig_dump(int level, int indent)
-{
-	struct fd_list * li = NULL;
-	
-	if (!TRACE_BOOL(level))
-		return;
-	
-	
-	CHECK_POSIX_DO(pthread_mutex_lock(&sig_hdl_lock), /* continue */);
-	if (!FD_IS_LIST_EMPTY(&sig_hdl_list))
-		fd_log_debug("%*sList of registered signal handlers:\n", indent, "");
-	
-	for (li = sig_hdl_list.next; li != &sig_hdl_list; li = li->next) {
-		struct sig_hdl * h = (struct sig_hdl *)li;
-		
-		fd_log_debug("%*s  - sig %*d (%s) => %p in module %s\n", indent, "", 2, h->signal, fd_sig_abbrev(h->signal), h->sig_hdl, h->modname);
-	}
-	CHECK_POSIX_DO(pthread_mutex_unlock(&sig_hdl_lock), /* continue */);
-	
-	return;
-}
-
-
-/* Remove a callback */
-int fd_sig_unregister(int signal)
-{
-	struct fd_list * li = NULL;
-	struct sig_hdl * h = NULL;
-	
-	TRACE_ENTRY("%d", signal);
-	CHECK_PARAMS( signal && sig_initialized );
-	
-	/* Search in the list if we already have a handler for this signal */
-	CHECK_POSIX(pthread_mutex_lock(&sig_hdl_lock));
-	for (li = sig_hdl_list.next; li != &sig_hdl_list; li = li->next) {
-		struct sig_hdl * nh = (struct sig_hdl *)li;
-		
-		if (nh->signal < signal)
-			continue;
-		if (nh->signal == signal) {
-			h = nh;
-			fd_list_unlink(&h->chain);
-		}
-		break;
-	}
-	CHECK_POSIX(pthread_mutex_unlock(&sig_hdl_lock));
-	
-	if (!h)
-		return ENOENT;
-	
-	/* Stop the catcher thread */
-	CHECK_FCT( fd_thr_term(&h->sig_thr) );
-
-	/* Free */
-	free(h->modname);
-	free(h);
-	
-	/* All good */
-	return 0;
-}
-
-/* Terminate the signal handler thread -- must be called if fd_sig_init was called */
-void fd_sig_fini(void)
-{
-	if (!sig_initialized)
-		return;
-	
-	/* For each registered handler, stop the thread, free the associated memory */
-	CHECK_POSIX_DO(pthread_mutex_lock(&sig_hdl_lock), /* continue */);
-	while (!FD_IS_LIST_EMPTY(&sig_hdl_list)) {
-		struct sig_hdl * h = (struct sig_hdl *)sig_hdl_list.next;
-		
-		/* Unlink */
-		fd_list_unlink(&h->chain);
-		
-		/* Stop the catcher thread */
-		CHECK_FCT_DO( fd_thr_term(&h->sig_thr), /* continue */ );
-		
-		/* Free */
-		free(h->modname);
-		free(h);
-	}
-	CHECK_POSIX_DO(pthread_mutex_unlock(&sig_hdl_lock), /* continue */);
-
-	/* Destroy the thread attribute */
-	CHECK_POSIX_DO( pthread_attr_destroy(&detached), /* continue */ );
-	return;
-}
-
-
-/**************************************************************************************/
-
-static char **abbrevs;
-static size_t abbrevs_nr = 0;
-
-/* Initialize the array of signals */
-static int abbrevs_init(void)
-{
-	int i;
-	
-	#define SIGNAL_MAX_LIMIT	100 /* Do not save signals with value > this */
-
-	/* The raw list of signals in the system -- might be incomplete, add any signal you need */
-	struct sig_abb_raw {
-		int	 sig;
-		char 	*name;
-	} abbrevs_raw[] = {
-		{ 0, "[unknown signal]" }
-	#define RAW_SIGNAL( _sig ) ,{ _sig, #_sig }
-	#ifdef SIGBUS
-		RAW_SIGNAL( SIGBUS )
-	#endif /* SIGBUS */
-	#ifdef SIGTTIN
-		RAW_SIGNAL( SIGTTIN )
-	#endif /* SIGTTIN */
-	#ifdef SIGTTOU
-		RAW_SIGNAL( SIGTTOU )
-	#endif /* SIGTTOU */
-	#ifdef SIGPROF
-		RAW_SIGNAL( SIGPROF )
-	#endif /* SIGPROF */
-	#ifdef SIGALRM
-		RAW_SIGNAL( SIGALRM )
-	#endif /* SIGALRM */
-	#ifdef SIGFPE
-		RAW_SIGNAL( SIGFPE )
-	#endif /* SIGFPE */
-	#ifdef SIGSTKFLT
-		RAW_SIGNAL( SIGSTKFLT )
-	#endif /* SIGSTKFLT */
-	#ifdef SIGSTKSZ
-		RAW_SIGNAL( SIGSTKSZ )
-	#endif /* SIGSTKSZ */
-	#ifdef SIGUSR1
-		RAW_SIGNAL( SIGUSR1 )
-	#endif /* SIGUSR1 */
-	#ifdef SIGURG
-		RAW_SIGNAL( SIGURG )
-	#endif /* SIGURG */
-	#ifdef SIGIO
-		RAW_SIGNAL( SIGIO )
-	#endif /* SIGIO */
-	#ifdef SIGQUIT
-		RAW_SIGNAL( SIGQUIT )
-	#endif /* SIGQUIT */
-	#ifdef SIGABRT
-		RAW_SIGNAL( SIGABRT )
-	#endif /* SIGABRT */
-	#ifdef SIGTRAP
-		RAW_SIGNAL( SIGTRAP )
-	#endif /* SIGTRAP */
-	#ifdef SIGVTALRM
-		RAW_SIGNAL( SIGVTALRM )
-	#endif /* SIGVTALRM */
-	#ifdef SIGSEGV
-		RAW_SIGNAL( SIGSEGV )
-	#endif /* SIGSEGV */
-	#ifdef SIGCONT
-		RAW_SIGNAL( SIGCONT )
-	#endif /* SIGCONT */
-	#ifdef SIGPIPE
-		RAW_SIGNAL( SIGPIPE )
-	#endif /* SIGPIPE */
-	#ifdef SIGWINCH
-		RAW_SIGNAL( SIGWINCH )
-	#endif /* SIGWINCH */
-	#ifdef SIGXFSZ
-		RAW_SIGNAL( SIGXFSZ )
-	#endif /* SIGXFSZ */
-	#ifdef SIGHUP
-		RAW_SIGNAL( SIGHUP )
-	#endif /* SIGHUP */
-	#ifdef SIGCHLD
-		RAW_SIGNAL( SIGCHLD )
-	#endif /* SIGCHLD */
-	#ifdef SIGSYS
-		RAW_SIGNAL( SIGSYS )
-	#endif /* SIGSYS */
-	#ifdef SIGSTOP
-		RAW_SIGNAL( SIGSTOP )
-	#endif /* SIGSTOP */
-	#ifdef SIGUSR2
-		RAW_SIGNAL( SIGUSR2 )
-	#endif /* SIGUSR2 */
-	#ifdef SIGTSTP
-		RAW_SIGNAL( SIGTSTP )
-	#endif /* SIGTSTP */
-	#ifdef SIGKILL
-		RAW_SIGNAL( SIGKILL )
-	#endif /* SIGKILL */
-	#ifdef SIGXCPU
-		RAW_SIGNAL( SIGXCPU )
-	#endif /* SIGXCPU */
-	#ifdef SIGUNUSED
-		RAW_SIGNAL( SIGUNUSED )
-	#endif /* SIGUNUSED */
-	#ifdef SIGPWR
-		RAW_SIGNAL( SIGPWR )
-	#endif /* SIGPWR */
-	#ifdef SIGILL
-		RAW_SIGNAL( SIGILL )
-	#endif /* SIGILL */
-	#ifdef SIGINT
-		RAW_SIGNAL( SIGINT )
-	#endif /* SIGINT */
-	#ifdef SIGIOT
-		RAW_SIGNAL( SIGIOT )
-	#endif /* SIGIOT */
-	#ifdef SIGTERM
-		RAW_SIGNAL( SIGTERM )
-	#endif /* SIGTERM */
-	};
-
-	TRACE_ENTRY("");
-	
-	/* First pass: find the highest signal number */
-	for (i=0; i < sizeof(abbrevs_raw)/sizeof(abbrevs_raw[0]); i++) {
-		if (abbrevs_raw[i].sig > SIGNAL_MAX_LIMIT) {
-			TRACE_DEBUG(ANNOYING, "Ignoring signal %s (%d), increase SIGNAL_MAX_LIMIT if you want it included", abbrevs_raw[i].name, abbrevs_raw[i].sig);
-			continue;
-		}
-		if (abbrevs_raw[i].sig > abbrevs_nr)
-			abbrevs_nr = abbrevs_raw[i].sig;
-	}
-	
-	/* Now, alloc the array */
-	abbrevs_nr++; /* 0-based */
-	CHECK_MALLOC( abbrevs = calloc( abbrevs_nr, sizeof(char *) ) );
-	
-	/* Second pass: add all the signals in the array */
-	for (i=0; i < sizeof(abbrevs_raw)/sizeof(abbrevs_raw[0]); i++) {
-		if (abbrevs_raw[i].sig > SIGNAL_MAX_LIMIT)
-			continue;
-		
-		if (abbrevs[abbrevs_raw[i].sig] == NULL)
-			abbrevs[abbrevs_raw[i].sig] = abbrevs_raw[i].name;
-	}
-	
-	/* Third pass: Set all missing signals to the undef value */
-	for (i=0; i < abbrevs_nr; i++) {
-		if (abbrevs[i] == NULL)
-			abbrevs[i] = abbrevs_raw[0].name;
-	}
-	
-	/* Done! */
-	return 0;
-}
-
-/* Names of signals */
-const char * fd_sig_abbrev(int signal)
-{
-	if (signal < abbrevs_nr)
-		return abbrevs[signal];
-	return abbrevs[0];
-}
-
Index: tests/CMakeLists.txt
===================================================================
--- tests/CMakeLists.txt	(revision 658)
+++ tests/CMakeLists.txt	(revision 658)
@@ -0,0 +1,99 @@
+# 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: 5)?")
+IF(TEST_TIMEOUT)
+	ADD_DEFINITIONS(-DTEST_TIMEOUT=${TEST_TIMEOUT})
+ENDIF(TEST_TIMEOUT)
+
+
+#############################
+# List the test cases
+SET(TEST_LIST
+	testsctp
+	testlist
+	testdict
+	testmesg
+	testfifo
+	testsess
+	testdisp
+	testcnx
+)
+
+#############################
+# Some parameters for the tests
+
+ADD_DEFINITIONS(-DTEST_DEBUG)
+ADD_DEFINITIONS(-DTRACE_LEVEL=NONE)
+
+INCLUDE_DIRECTORIES( "../libfdproto" )
+INCLUDE_DIRECTORIES( "../libfdcore" )
+
+
+##############################
+# 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 ${${TEST}_ADDITIONAL_LIB})
+   ADD_TEST(${TEST} ${EXECUTABLE_OUTPUT_PATH}/${TEST})
+ENDFOREACH( TEST )
+
+
+####
+## INSTALL section ##
+
+# we do not install the tests
Index: tests/testappacct.c
===================================================================
--- tests/testappacct.c	(revision 658)
+++ tests/testappacct.c	(revision 658)
@@ -0,0 +1,266 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@nict.go.jp>							 *
+*													 *
+* Copyright (c) 2010, 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>
+(
+  ts 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;
+}
+
+/* Main test routine */
+int main(int argc, char *argv[])
+{
+	extern PGconn *conn; /* in acct_db.c */
+	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;
+	char * sess_bkp;
+	
+	/* First, initialize the daemon modules */
+	INIT_FD();
+	fd_g_config->cnf_diamid = strdup("test.app.acct");
+	fd_g_config->cnf_diamid_len = strlen(fd_g_config->cnf_diamid);
+	fd_g_config->cnf_diamrlm = strdup("app.acct");
+	fd_g_config->cnf_diamrlm_len = strlen(fd_g_config->cnf_diamrlm);
+	
+	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) );
+	}
+	
+	/* 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;
+			char * s;
+			CHECK( 0, fd_sess_new( &sess, fd_g_config->cnf_diamid, NULL, 0) );
+			CHECK( 0, fd_sess_getsid(sess, &s) );
+			sess_bkp = strdup(s);
+
+			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 = (unsigned char *)sess_bkp;
+			avp_val.os.len = strlen(sess_bkp);
+			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", 1, 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;
+		char * s;
+		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) */
+		s = PQgetvalue(res, 0, 0);
+		CHECK( 0, strcmp(s, sess_bkp) );
+		
+		PQclear(res);
+	}  
+
+	/* That's all for the tests yet */
+	free(sess_bkp);
+	fd_ext_fini();
+	
+	PASSTEST();
+} 
+	
Index: tests/testcnx.c
===================================================================
--- tests/testcnx.c	(revision 658)
+++ tests/testcnx.c	(revision 658)
@@ -0,0 +1,1607 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@nict.go.jp>							 *
+*													 *
+* Copyright (c) 2010, 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";
+
+
+struct fd_list eps = FD_LIST_INITIALIZER(eps);
+
+struct connect_flags {
+	int	proto;
+	int	expect_failure; /* 0 or 1 */
+};
+
+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;
+}
+
+struct handshake_flags {
+	struct cnxctx * cnx;
+	gnutls_certificate_credentials_t	creds;
+	int ret;
+};
+
+void * handshake_thr(void * arg)
+{
+	struct handshake_flags * hf = arg;
+	fd_log_threadname ( "testcnx:handshake" );
+	hf->ret = fd_cnx_handshake(hf->cnx, GNUTLS_CLIENT, NULL, hf->creds);
+	return NULL;
+}
+	
+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) };
+	
+	struct cnxctx * listener;
+#ifndef DISABLE_SCTP
+	struct cnxctx * listener_sctp;
+#endif /* DISABLE_SCTP */
+	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 );
+	
+	/* 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_generate2( fd_g_config->cnf_sec_data.dh_cache, GNUTLS_DEFAULT_DHBITS), );
+	CHECK( GNUTLS_E_SUCCESS, ret );
+	
+	/* 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);
+	}
+	
+	/* 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 1
+		/* For debug: dump the object */
+		fd_log_debug("Dumping CER\n");
+		fd_msg_dump_walk(0, cer);
+		#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, 0, 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, 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, 0));
+		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);
+	}
+		
+#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, 0, 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, 0));
+		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, 0));
+		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, 0));
+		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, 0, 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, 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);
+		
+		/* And the supposed reply */
+		CHECK( 0, fd_cnx_send(client_side, cer_buf, cer_sz, 0));
+		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, 0, handshake_thr, &hf) );
+		CHECK( 0, fd_cnx_handshake(server_side, GNUTLS_SERVER, 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, 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);
+
+			CHECK( 0, fd_cnx_send(client_side, cer_buf, cer_sz, 0));
+			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, 0, 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));
+		
+		/* 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, 0, 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, 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);
+		
+		/* And the supposed reply */
+		CHECK( 0, fd_cnx_send(client_side, cer_buf, cer_sz, 0));
+		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, 0, handshake_thr, &hf) );
+		CHECK( 0, fd_cnx_handshake(server_side, GNUTLS_SERVER, 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, 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);
+
+			CHECK( 0, fd_cnx_send(client_side, cer_buf, cer_sz, 0));
+			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, 0, 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, 0, 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, 0, handshake_thr, &hf) );
+		CHECK( 0, fd_cnx_handshake(server_side, GNUTLS_SERVER, 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, 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);
+
+			CHECK( 0, fd_cnx_send(client_side, cer_buf, cer_sz, 0));
+			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, 0, 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 (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));
+		
+		/* 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, 0, 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, 0, handshake_thr, &hf) );
+		CHECK( 0, fd_cnx_handshake(server_side, GNUTLS_SERVER, 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, 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);
+
+			CHECK( 0, fd_cnx_send(client_side, cer_buf, cer_sz, 0));
+			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, 0, 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
+	{
+		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, 0, 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, 0, handshake_thr, &hf) );
+		CHECK( 0, fd_cnx_handshake(server_side, GNUTLS_SERVER, 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, 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);
+
+			CHECK( 0, fd_cnx_send(client_side, cer_buf, cer_sz, 0));
+			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, 0, 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, 0, 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, 0, handshake_thr, &hf) );
+		CHECK( 0, fd_cnx_handshake(server_side, GNUTLS_SERVER, 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, 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);
+
+			CHECK( 0, fd_cnx_send(client_side, cer_buf, cer_sz, 0));
+			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, 0, 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, 0, 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, 0, handshake_thr, &hf) );
+		CHECK( EINVAL, fd_cnx_handshake(server_side, GNUTLS_SERVER, NULL, NULL) );
+		CHECK( 0, pthread_join(thr, NULL) );
+		
+		/* Now close the connection */
+		CHECK( 0, pthread_create(&thr, 0, 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);
+	}
+	
+	/* Same in SCTP */
+#ifndef DISABLE_SCTP
+	{
+		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, 0, 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, 0, handshake_thr, &hf) );
+		CHECK( EINVAL, fd_cnx_handshake(server_side, GNUTLS_SERVER, NULL, NULL) );
+		CHECK( 0, pthread_join(thr, NULL) );
+		
+		/* Now close the connection */
+		CHECK( 0, pthread_create(&thr, 0, 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 */
+	
+	/* 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, 0, 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, 0, handshake_thr, &hf) );
+		CHECK( EINVAL, fd_cnx_handshake(server_side, GNUTLS_SERVER, NULL, NULL) );
+		CHECK( 0, pthread_join(thr, NULL) );
+		
+		/* Now close the connection */
+		CHECK( 0, pthread_create(&thr, 0, 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);
+	}
+	
+	/* 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, 0, 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, 0, handshake_thr, &hf) );
+		CHECK( 0, fd_cnx_handshake(server_side, GNUTLS_SERVER, NULL, NULL) );
+		CHECK( 0, pthread_join(thr, NULL) );
+		CHECK( 0, hf.ret );
+		
+		/* Now close the connection */
+		CHECK( 0, pthread_create(&thr, 0, 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, 0, 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, 0, handshake_thr, &hf) );
+		CHECK( EINVAL, fd_cnx_handshake(server_side, GNUTLS_SERVER, NULL, NULL) );
+		CHECK( 0, pthread_join(thr, NULL) );
+		
+		/* Now close the connection */
+		CHECK( 0, pthread_create(&thr, 0, 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);
+	}
+	
+	/* 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, 0, 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, 0, handshake_thr, &hf) );
+		CHECK( 0, fd_cnx_handshake(server_side, GNUTLS_SERVER, 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, 0));
+		CHECK( 0, fd_fifo_new(&myfifo) );
+		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);
+		fd_event_destroy(&myfifo, free);
+		
+		/* Now close the connection */
+		CHECK( 0, pthread_create(&thr, 0, 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
+	/* And re-test with a SCTP connection */
+	{
+		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, 0, 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, 0, handshake_thr, &hf) );
+		CHECK( 0, fd_cnx_handshake(server_side, GNUTLS_SERVER, 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, 0));
+		CHECK( 0, fd_fifo_new(&myfifo) );
+		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, 0, 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, 0, 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, 0, 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();
+} 
+	
Index: tests/testdict.c
===================================================================
--- tests/testdict.c	(revision 658)
+++ tests/testdict.c	(revision 658)
@@ -0,0 +1,135 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@nict.go.jp>							 *
+*													 *
+* Copyright (c) 2010, 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 );
+	}
+
+	/* That's all for the tests yet */
+	PASSTEST();
+} 
+	
Index: tests/testdisp.c
===================================================================
--- tests/testdisp.c	(revision 658)
+++ tests/testdisp.c	(revision 658)
@@ -0,0 +1,725 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@nict.go.jp>							 *
+*													 *
+* Copyright (c) 2010, 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;
+	enum disp_action action;
+	struct disp_hdl * hdl[NB_CB];
+	struct disp_when when;
+	const char * ec;
+	
+	/* First, initialize the daemon modules */
+	INIT_FD();
+	
+	/* Create a dummy session, we don't use it anyway */
+	CHECK( 0, fd_sess_new( &sess, "test.disp", 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 ) );
+		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 ) );
+		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 ) );
+		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 ) );
+		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 ) );
+		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 ) );
+		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 ) );
+		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 ) );
+		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 ) );
+		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 ) );
+		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 ) );
+		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 ) );
+		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 ) );
+		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 ) );
+		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 ) );
+		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 ) );
+		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 ) );
+		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 ) );
+		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 ) );
+		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 ) );
+		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 ) );
+		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( 12345, fd_msg_dispatch ( &msg, sess, &action, &ec ) );
+		CHECK( 1, cbcalled[0] );
+		CHECK( 1, cbcalled[1] );
+		CHECK( 1, cbcalled[6] );
+		CHECK( 0, cbcalled[2] );
+		CHECK( 0, cbcalled[3] );
+		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_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 ) );
+		CHECK( 1, cbcalled[0] );
+		CHECK( 1, cbcalled[1] );
+		CHECK( 1, cbcalled[8] );
+		CHECK( 0, cbcalled[2] );
+		CHECK( 0, cbcalled[3] );
+		CHECK( NULL, 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_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 ) );
+		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 ) );
+		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 ) );
+		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 ) );
+		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 ) );
+		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 ) );
+		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 ) );
+		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_conf_dump();
+		#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 ) );
+		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();
+} 
+	
Index: tests/testfifo.c
===================================================================
--- tests/testfifo.c	(revision 658)
+++ tests/testfifo.c	(revision 658)
@@ -0,0 +1,438 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@nict.go.jp>							 *
+*													 *
+* Copyright (c) 2010, 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>
+
+/* 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;
+}
+
+
+/* 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;
+		int count;
+		struct msg * msg  = NULL;
+		
+		/* Create the queue */
+		CHECK( 0, fd_fifo_new(&queue) );
+		
+		/* Check the count is 0 */
+		CHECK( 0, fd_fifo_length(queue, &count) );
+		CHECK( 0, count);
+		
+		/* 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( 0, fd_fifo_length(queue, &count) );
+		CHECK( 3, count);
+		
+		/* Retrieve the first message using fd_fifo_get */
+		CHECK( 0, fd_fifo_get(queue, &msg) );
+		CHECK( msg1, msg);
+		CHECK( 0, fd_fifo_length(queue, &count) );
+		CHECK( 2, count);
+		
+		/* 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( 0, fd_fifo_length(queue, &count) );
+		CHECK( 1, count);
+		
+		/* Retrieve the third message using meq_tryget */
+		CHECK( 0, fd_fifo_tryget(queue, &msg) );
+		CHECK( msg3, msg);
+		CHECK( 0, fd_fifo_length(queue, &count) );
+		CHECK( 0, count);
+		
+		/* Check that another meq_tryget does not block */
+		CHECK( EWOULDBLOCK, fd_fifo_tryget(queue, &msg) );
+		CHECK( 0, fd_fifo_length(queue, &count) );
+		CHECK( 0, 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 			 count;
+		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 {
+			/* The local limit is bellow 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) );
+		
+		/* 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, &count) );
+		CHECK( 0, count);
+		
+		/* 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) );
+		
+		/* 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) );
+		
+		/* 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) );
+	}
+	
+	/* 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();
+} 
Index: tests/testlist.c
===================================================================
--- tests/testlist.c	(revision 658)
+++ tests/testlist.c	(revision 658)
@@ -0,0 +1,74 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@nict.go.jp>							 *
+*													 *
+* Copyright (c) 2009, WIDE Project and NICT								 *
+* All rights reserved.											 *
+* 													 *
+* Redistribution and use of this software in source and binary forms, with or without modification, are  *
+* permitted provided that the following conditions are met:						 *
+* 													 *
+* * Redistributions of source code must retain the above 						 *
+*   copyright notice, this list of conditions and the 							 *
+*   following disclaimer.										 *
+*    													 *
+* * Redistributions in binary form must reproduce the above 						 *
+*   copyright notice, this list of conditions and the 							 *
+*   following disclaimer in the documentation and/or other						 *
+*   materials provided with the distribution.								 *
+* 													 *
+* * Neither the name of the WIDE Project or NICT nor the 						 *
+*   names of its contributors may be used to endorse or 						 *
+*   promote products derived from this software without 						 *
+*   specific prior written permission of WIDE Project and 						 *
+*   NICT.												 *
+* 													 *
+* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED *
+* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
+* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR *
+* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 	 *
+* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 	 *
+* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR *
+* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF   *
+* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.								 *
+*********************************************************************************************************/
+
+#include "tests.h"
+
+#define TEST_STR "This is my test string (with extra unused data)"
+#define TEST_STRLEN 22
+
+/* Main test routine */
+int main(int argc, char *argv[])
+{
+	/* First, initialize the daemon modules */
+	INIT_FD();
+	
+	/* Check the hash function */
+	{
+		char buf[30];
+		
+		uint32_t hash = fd_hash(TEST_STR, TEST_STRLEN); /* reference value */
+		
+		/* Check that a hash of a substring / surstring is different */
+		CHECK( 1, hash != fd_hash(TEST_STR, TEST_STRLEN - 1) ? 1 : 0 );
+		CHECK( 1, hash != fd_hash(TEST_STR, TEST_STRLEN + 1) ? 1 : 0 );
+		
+		/* Check alignment of the string is not important */
+		memcpy(buf + 4, TEST_STR, TEST_STRLEN);
+		CHECK( hash, fd_hash(buf + 4, TEST_STRLEN) );
+		
+		memcpy(buf + 3, TEST_STR, TEST_STRLEN);
+		CHECK( hash, fd_hash(buf + 3, TEST_STRLEN) );
+		
+		memcpy(buf + 2, TEST_STR, TEST_STRLEN);
+		CHECK( hash, fd_hash(buf + 2, TEST_STRLEN) );
+		
+		memcpy(buf + 1, TEST_STR, TEST_STRLEN);
+		CHECK( hash, fd_hash(buf + 1, TEST_STRLEN) );
+	}
+
+	/* That's all for the tests yet */
+	PASSTEST();
+} 
+	
Index: tests/testmesg.c
===================================================================
--- tests/testmesg.c	(revision 658)
+++ tests/testmesg.c	(revision 658)
@@ -0,0 +1,1247 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@nict.go.jp>							 *
+*													 *
+* Copyright (c) 2010, 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\n");
+		fd_msg_dump_walk( 0, acr );
+		#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\n");
+		fd_msg_dump_walk(0, pi);
+		fd_log_debug("Dumping dictionary model\n");
+		fd_dict_dump_object(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\n");
+		fd_msg_dump_walk(0, acr);
+		#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_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  * 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_dict_dump_object ( gavp );
+			#endif
+		}
+		#if 0
+		{
+			fd_dict_dump_object ( 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:\n");
+			fd_msg_dump_one(0, avpi);
+			#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:\n");
+			fd_msg_dump_one(0, avpi);
+			#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):\n");
+				fd_msg_dump_one(0, avpi);
+				#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):\n");
+				fd_msg_dump_one(0, avpi);
+				#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):\n");
+			fd_msg_dump_one(0, avpi);
+			#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...'\n");
+				fd_msg_dump_one(0, avpi);
+				#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):\n");
+				fd_msg_dump_one(0, avpi);
+				#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):\n");
+				fd_msg_dump_one(0, avpi);
+				#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'\n");
+				fd_msg_dump_one(0, avpch);
+				#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'\n");
+				fd_msg_dump_one(0, avpch);
+				#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_msg_dump_walk(0, msg);
+			#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_msg_dump_walk(0, msg);
+			#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_msg_dump_walk(0, msg);
+				#endif
+				
+				/* reset */
+				CHECK( 0, fd_msg_free ( msg ) );
+			}
+			
+			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_msg_dump_walk(0, msg);
+			#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 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_msg_dump_walk(0, msg);
+			#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_msg_dump_walk(0, msg);
+			#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();
+} 
+	
Index: tests/tests.h
===================================================================
--- tests/tests.h	(revision 658)
+++ tests/tests.h	(revision 658)
@@ -0,0 +1,197 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@nict.go.jp>							 *
+*													 *
+* Copyright (c) 2010, 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>
+#include <gcrypt.h>
+
+/* Test timeout duration, unless -n is passed on the command line */
+#ifndef TEST_TIMEOUT
+#define TEST_TIMEOUT	30	/* 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... ){				\
+	fprintf(stderr, ## message);			\
+	TRACE_DEBUG(INFO, "Test failed");		\
+	exit(FAIL);					\
+}
+
+/* Define the macro to pass a test */
+#define PASSTEST( ){					\
+	fprintf(stderr, "Test %s passed\n", __FILE__);	\
+	TRACE_DEBUG(INFO, "Test passed");		\
+	exit(PASS);					\
+}
+
+static int test_verbo = 0;
+static struct fd_config conf;
+struct fd_config * fd_g_config = &conf;
+
+/* Define the standard check routines */
+#define CHECK( _val, _assert ){				\
+	if (test_verbo > 0) {				\
+		fprintf(stderr,				\
+			"%s:%-4d: CHECK( " #_assert " == "\
+				#_val " )\n",		\
+			__FILE__, 			\
+			__LINE__);			\
+	}{						\
+	__typeof__ (_val) __ret = (_assert);		\
+	if (__ret != (_val)) {				\
+		FAILTEST( "%s:%d: CHECK FAILED : %s == %lx != %lx\n",	\
+			__FILE__,			\
+			__LINE__,			\
+			#_assert,			\
+			(unsigned long)__ret,		\
+			(unsigned long)(_val));		\
+	}}						\
+}
+
+static pthread_t timeout_thr;
+static void * timeout_catch(void * arg)
+{
+	int sig;
+	sigset_t ss;
+	fd_log_threadname ( "timeout catcher" );
+	
+	sigemptyset(&ss);
+	sigaddset(&ss, SIGALRM);
+	
+	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;
+
+static inline void parse_cmdline(int argc, char * argv[]) {
+	int c;
+	int no_timeout = 0;
+	while ((c = getopt (argc, argv, "dqnf:F:")) != -1) {
+		switch (c) {
+			case 'd':	/* Increase verbosity of debug messages.  */
+				test_verbo++;
+				break;
+				
+			case 'q':	/* Decrease verbosity.  */
+				test_verbo--;
+				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;
+				
+			default:	/* bug: option not considered.  */
+				return;
+		}
+	}
+	fd_g_debug_lvl = (test_verbo > 0) ? (test_verbo - 1) : 0;
+	if (!no_timeout) {
+		alarm(TEST_TIMEOUT);
+		CHECK( 0, pthread_create(&timeout_thr, NULL, timeout_catch, NULL) );
+	}
+}
+ 
+static inline void test_init(int argc, char * argv[])
+{
+	memset(fd_g_config, 0, sizeof(struct fd_config));
+	
+	CHECK( 0, fd_libproto_init() );
+	
+	fd_log_threadname(basename(__FILE__));
+	
+	/* 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());
+	
+	/* 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()  );
+	
+	/* Parse the command line */
+	parse_cmdline(argc, argv);
+	
+	return;
+}
+#define INIT_FD()  test_init(argc, argv);
+
+#endif /* _TESTS_H */
Index: tests/testsctp.c
===================================================================
--- tests/testsctp.c	(revision 658)
+++ tests/testsctp.c	(revision 658)
@@ -0,0 +1,136 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@nict.go.jp>							 *
+*													 *
+* Copyright (c) 2010, 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 */
+	int sock, srvsock, clisock;
+	char buf1[]="abcdef";
+	char *buf2;
+	size_t sz;
+	struct fd_list eps = FD_LIST_INITIALIZER(eps);
+	uint32_t status = 0;
+	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);
+	}
+	
+	/* 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( &clisock, 0, TEST_PORT, &eps ));
+	
+	/* Accept this connection */
+	srvsock = accept(sock, NULL, NULL);
+	
+	/* Send a first message */
+	CHECK( 0, fd_sctp_sendstr(srvsock, 1, (uint8_t *)buf1, sizeof(buf1), &status) );
+	CHECK( 0, status);
+	
+	/* Receive this message */
+redo1:
+	CHECK( 0, fd_sctp_recvmeta(clisock, &str, (uint8_t **)&buf2, &sz, &ev, &status) );
+	if (ev == FDEVP_CNX_EP_CHANGE)
+		goto redo1;
+	CHECK( FDEVP_CNX_MSG_RECV, ev);
+	CHECK( 0, status);
+	CHECK( 1, str);
+	CHECK( sizeof(buf1), sz );
+	CHECK( 0, memcmp(buf1, buf2, sz) );
+	free(buf2); buf2 = NULL;
+	
+	/* Send in the other direction */
+	CHECK( 0, fd_sctp_sendstr(clisock, 2, (uint8_t *)buf1, sizeof(buf1), &status) );
+	CHECK( 0, status);
+	
+	/* Receive this message */
+redo2:
+	CHECK( 0, fd_sctp_recvmeta(srvsock, &str, (uint8_t **)&buf2, &sz, &ev, &status) );
+	if (ev == FDEVP_CNX_EP_CHANGE)
+		goto redo2;
+	CHECK( FDEVP_CNX_MSG_RECV, ev);
+	CHECK( 0, status);
+	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 */
+} 
+	
Index: tests/testsess.c
===================================================================
--- tests/testsess.c	(revision 658)
+++ tests/testsess.c	(revision 658)
@@ -0,0 +1,369 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@nict.go.jp>							 *
+*													 *
+* Copyright (c) 2010, WIDE Project and NICT								 *
+* All rights reserved.											 *
+* 													 *
+* Redistribution and use of this software in source and binary forms, with or without modification, are  *
+* permitted provided that the following conditions are met:						 *
+* 													 *
+* * Redistributions of source code must retain the above 						 *
+*   copyright notice, this list of conditions and the 							 *
+*   following disclaimer.										 *
+*    													 *
+* * Redistributions in binary form must reproduce the above 						 *
+*   copyright notice, this list of conditions and the 							 *
+*   following disclaimer in the documentation and/or other						 *
+*   materials provided with the distribution.								 *
+* 													 *
+* * Neither the name of the WIDE Project or NICT nor the 						 *
+*   names of its contributors may be used to endorse or 						 *
+*   promote products derived from this software without 						 *
+*   specific prior written permission of WIDE Project and 						 *
+*   NICT.												 *
+* 													 *
+* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED *
+* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
+* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR *
+* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 	 *
+* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 	 *
+* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR *
+* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF   *
+* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.								 *
+*********************************************************************************************************/
+
+#include "tests.h"
+
+#define TEST_DIAM_ID 	"testsess.myid"
+#define TEST_OPT	"suffix"
+#define TEST_SID	TEST_DIAM_ID ";1234;5678;" TEST_OPT
+
+#define TEST_EYEC	0x7e57e1ec
+struct mystate {
+	int	eyec;	/* TEST_EYEC */
+	char *  sid; 	/* the session with which the data was registered */
+	int  *  freed;	/* location where to write the freed status */
+	void *  opaque; /* if opaque was provided, this is the value we expect */
+};
+
+static void mycleanup( struct mystate * data, char * sid, void * opaque )
+{
+	/* sanity */
+	CHECK( 1, sid ? 1 : 0 );
+	CHECK( 1, data? 1 : 0 );
+	CHECK( TEST_EYEC, data->eyec );
+	CHECK( 0, strcmp(sid, data->sid) );
+	if (data->freed)
+		*(data->freed) += 1;
+	if (data->opaque) {
+		CHECK( 1, opaque == data->opaque ? 1 : 0 );  
+	}
+	/* Now, free the data */
+	free(data->sid);
+	free(data);
+}
+
+static __inline__ struct mystate * new_state(char * sid, int *freed) 
+{
+	struct mystate *new;
+	new = malloc(sizeof(struct mystate));
+	CHECK( 1, new ? 1 : 0 );
+	memset(new, 0, sizeof(struct mystate));
+	new->eyec = TEST_EYEC;
+	new->sid = strdup(sid);
+	CHECK( 1, new->sid ? 1 : 0 );
+	new->freed = freed;
+	return new;
+}
+
+void * g_opaque = (void *)"test";
+	
+
+/* Main test routine */
+int main(int argc, char *argv[])
+{
+	struct session_handler * hdl1, *hdl2;
+	struct session *sess1, *sess2, *sess3;
+	char *str1, *str2;
+	int new;
+	
+	/* First, initialize the daemon modules */
+	INIT_FD();
+	
+	/* Test functions related to handlers (simple situation) */
+	{
+		void * testptr = NULL;
+		CHECK( 0, fd_sess_handler_create ( &hdl1, mycleanup, NULL ) );
+		CHECK( 0, fd_sess_handler_create ( &hdl2, mycleanup, NULL ) );
+		CHECK( 0, fd_sess_handler_destroy( &hdl2, &testptr ) );
+		CHECK( 1, testptr == NULL ? 1 : 0 );
+		CHECK( 0, fd_sess_handler_create ( &hdl2, mycleanup, g_opaque ) );
+		#if 0
+		fd_sess_dump_hdl(0, hdl1);
+		fd_sess_dump_hdl(0, hdl2);
+		#endif
+	}
+	
+	/* Test Session Id generation (fd_sess_new) */
+	{
+		/* DiamId is provided, not opt */
+		CHECK( 0, fd_sess_new( &sess1, TEST_DIAM_ID, NULL, 0 ) );
+		CHECK( 0, fd_sess_new( &sess2, TEST_DIAM_ID, NULL, 0 ) );
+		#if 0
+		fd_sess_dump(0, sess1);
+		fd_sess_dump(0, sess2);
+		#endif
+		
+		/* Check both string start with the diameter Id, but are different */
+		CHECK( 0, fd_sess_getsid(sess1, &str1) );
+		CHECK( 0, strncmp(str1, TEST_DIAM_ID ";", strlen(TEST_DIAM_ID) + 1) );
+		CHECK( 0, fd_sess_getsid(sess2, &str2) );
+		CHECK( 0, strncmp(str2, TEST_DIAM_ID ";", strlen(TEST_DIAM_ID) + 1) );
+		CHECK( 1, strcmp(str1, str2) ? 1 : 0 );
+		CHECK( 0, fd_sess_destroy( &sess1 ) );
+		CHECK( 0, fd_sess_destroy( &sess2 ) );
+		
+		/* diamId and opt */
+		CHECK( 0, fd_sess_new( &sess1, TEST_DIAM_ID, TEST_OPT, 0 ) );
+		CHECK( 0, fd_sess_new( &sess2, TEST_DIAM_ID, TEST_OPT, strlen(TEST_OPT) - 1 ) );
+		#if 0
+		fd_sess_dump(0, sess1);
+		fd_sess_dump(0, sess2);
+		#endif
+		
+		CHECK( 0, fd_sess_getsid(sess1, &str1) );
+		CHECK( 0, strncmp(str1, TEST_DIAM_ID ";", strlen(TEST_DIAM_ID) + 1) );
+		CHECK( 0, strcmp(str1 + strlen(str1) - strlen(TEST_OPT) - 1, ";" TEST_OPT) );
+		
+		CHECK( 0, fd_sess_getsid(sess2, &str2) );
+		CHECK( 0, strncmp(str2, TEST_DIAM_ID ";", strlen(TEST_DIAM_ID) + 1) );
+		CHECK( 0, strncmp(str2 + strlen(str2) - strlen(TEST_OPT), ";" TEST_OPT, strlen(TEST_OPT)) );
+		
+		CHECK( 1, strcmp(str1, str2) ? 1 : 0 );
+		CHECK( 0, fd_sess_destroy( &sess1 ) );
+		CHECK( 0, fd_sess_destroy( &sess2 ) );
+		
+		/* Now, only opt is provided */
+		CHECK( 0, fd_sess_new( &sess1, NULL, TEST_SID, 0 ) );
+		CHECK( EALREADY, fd_sess_new( &sess2, NULL, TEST_SID, 0 ) );
+		CHECK( sess2, sess1 );
+		CHECK( EALREADY, fd_sess_new( &sess3, NULL, TEST_SID, strlen(TEST_SID) ) );
+		CHECK( sess3, sess1 );
+		CHECK( 0, fd_sess_new( &sess2, NULL, TEST_SID, strlen(TEST_SID) - 1 ) );
+		#if 0
+		fd_sess_dump(0, sess1);
+		fd_sess_dump(0, sess2);
+		#endif
+		CHECK( 0, fd_sess_getsid(sess1, &str1) );
+		CHECK( 0, fd_sess_getsid(sess2, &str2) );
+		CHECK( 0, strncmp( str1, str2, strlen(TEST_SID) - 1 ) );
+		CHECK( 0, strcmp( str1, TEST_SID ) );
+		
+		CHECK( 0, fd_sess_destroy( &sess2 ) );
+		CHECK( 0, fd_sess_destroy( &sess1 ) );
+	}
+		
+	/* Test fd_sess_fromsid */
+	{
+		CHECK( 0, fd_sess_fromsid( TEST_SID, strlen(TEST_SID), &sess1, &new ) );
+		CHECK( 1, new ? 1 : 0 );
+		
+		CHECK( 0, fd_sess_fromsid( TEST_SID, strlen(TEST_SID), &sess2, &new ) );
+		CHECK( 0, new );
+		CHECK( sess1, sess2 );
+		
+		CHECK( 0, fd_sess_fromsid( TEST_SID, strlen(TEST_SID), &sess3, NULL ) );
+		CHECK( sess1, sess3 );
+		
+		CHECK( 0, fd_sess_destroy( &sess1 ) );
+	}
+	
+	/* Test fd_sess_reclaim */
+	{
+		struct mystate *tms;
+		
+		CHECK( 0, fd_sess_fromsid( TEST_SID, strlen(TEST_SID), &sess1, &new ) );
+		CHECK( 1, new ? 1 : 0 );
+		
+		CHECK( 0, fd_sess_reclaim( &sess1 ) );
+		CHECK( NULL, sess1 );
+		
+		CHECK( 0, fd_sess_fromsid( TEST_SID, strlen(TEST_SID), &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, strlen(TEST_SID), &sess1, &new ) );
+		CHECK( 0, new );
+		
+		CHECK( 0, fd_sess_destroy( &sess1 ) );
+		
+		CHECK( 0, fd_sess_fromsid( TEST_SID, strlen(TEST_SID), &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, strlen(TEST_SID), &sess1, &new ) );
+		CHECK( 1, new ? 1 : 0 );
+		
+		CHECK( 0, clock_gettime(CLOCK_REALTIME, &timeout) );
+		CHECK( 0, fd_sess_settimeout( sess1, &timeout) );
+		timeout.tv_sec = 0;
+		timeout.tv_nsec= 50000000; /* 50 ms */
+		CHECK( 0, nanosleep(&timeout, NULL) );
+		
+		CHECK( 0, fd_sess_fromsid( TEST_SID, strlen(TEST_SID), &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, NULL, 0 ) );
+		
+		/* We don't really have away 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 mystate * ms[6], *tms;
+		int freed[6];
+		struct timespec timeout;
+		void * testptr = NULL;
+		
+		/* Create three sessions */
+		CHECK( 0, fd_sess_new( &sess1, TEST_DIAM_ID, NULL, 0 ) );
+		CHECK( 0, fd_sess_new( &sess2, TEST_DIAM_ID, NULL, 0 ) );
+		CHECK( 0, fd_sess_new( &sess3, TEST_DIAM_ID, NULL, 0 ) );
+		
+		/* Create 2 states */
+		CHECK( 0, fd_sess_getsid(sess1, &str1) );
+		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_sess_dump(0, sess1);
+		#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) );
+		ms[0] = new_state(str1, &freed[0]);
+		ms[1] = new_state(str1, &freed[1]);
+		CHECK( 0, fd_sess_getsid(sess2, &str1) );
+		ms[2] = new_state(str1, &freed[2]);
+		ms[3] = new_state(str1, &freed[3]);
+		CHECK( 0, fd_sess_getsid(sess3, &str1) );
+		ms[4] = new_state(str1, &freed[4]);
+		ms[5] = new_state(str1, &freed[5]);
+		ms[5]->opaque = g_opaque;
+		str2 = strdup(str1);
+		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_sess_dump(0, sess1);
+		fd_sess_dump(0, sess2);
+		fd_sess_dump(0, sess3);
+		#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_sess_dump(0, sess1);
+		fd_sess_dump(0, sess2);
+		#endif
+		
+		/* Create again session 3, check that no data is associated to it */
+		CHECK( 0, fd_sess_fromsid( str2, strlen(str2), &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_sess_dump(0, sess1);
+		fd_sess_dump(0, sess2);
+		#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( 0, fd_sess_getsid(sess1, &str1) );
+		mycleanup(tms, str1, NULL);
+	}
+	
+	
+	/* That's all for the tests yet */
+	PASSTEST();
+} 
+	
