Index: libfdproto/CMakeLists.txt
===================================================================
--- libfdproto/CMakeLists.txt	(revision 689)
+++ libfdproto/CMakeLists.txt	(revision 706)
@@ -13,4 +13,5 @@
 	messages.c
 	msg_log.c
+	ostr.c
 	rt_data.c
 	sessions.c
Index: libfdproto/dictionary.c
===================================================================
--- libfdproto/dictionary.c	(revision 687)
+++ libfdproto/dictionary.c	(revision 706)
@@ -63,12 +63,14 @@
 	
 	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;
+		struct dict_vendor_data		vendor;		/* datastr_len = strlen(vendor_name) */
+		struct dict_application_data	application;	/* datastr_len = strlen(application_name) */
+		struct dict_type_data		type;		/* datastr_len = strlen(type_name) */
+		struct dict_enumval_data	enumval;	/* datastr_len = strlen(enum_name) */
+		struct dict_avp_data		avp;		/* datastr_len = strlen(avp_name) */
+		struct dict_cmd_data		cmd;		/* datastr_len = strlen(cmd_name) */
+		struct dict_rule_data		rule;		/* datastr_len = 0 */
 	} data;				/* The data of this object */
+	
+	size_t			datastr_len; /* cached length of the string inside the data. Saved when the object is created. */
 	
 	struct dict_object *	parent; /* The parent of this object, if any */
@@ -88,5 +90,5 @@
 	 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.
+	 list[2]: sentinel for the list of AVPs from this vendor, ordered by AVP name (fd_os_cmp).
 	 
 	 => APPLICATIONS:
@@ -97,9 +99,9 @@
 	 => 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[1]: sentinel for the type_enum list of this type, ordered by their constant name (fd_os_cmp).
 	 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[0]: list of the contants for a given type, ordered by the constant name (fd_os_cmp). 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
@@ -107,14 +109,14 @@
 	 => 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[1]: list of the AVP from a given vendor, ordered by avp name (fd_os_cmp). 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[0]: list of the commands, ordered by their names (fd_os_cmp). 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[0]: list of the rules for a given (grouped) AVP or Command, ordered by the AVP vendor & code to which they refer. sentinel is list[2] of a command or (grouped) avp.
 	 list[1]: not used
 	 list[2]: not used.
@@ -223,8 +225,8 @@
 /* Functions to manage the objects creation and destruction. */
 
-/* Duplicate a string inplace */
-#define DUP_string( str ) {				\
-	char * __str = (str);				\
-	CHECK_MALLOC( (str) = strdup(__str) );		\
+/* Duplicate a string inplace, save its length */
+#define DUP_string_len( str, plen ) {		\
+	*(plen) = strlen((str));		\
+	str = os0dup( str, *(plen));		\
 }
 	
@@ -259,5 +261,5 @@
 
 /* Initialize the "data" part of an object */
-static int init_object_data(void * dest, void * source, enum dict_object_type type)
+static int init_object_data(struct dict_object * dest, void * source, enum dict_object_type type)
 {
 	TRACE_ENTRY("%p %p %d", dest, source, type);
@@ -265,5 +267,5 @@
 	
 	/* Generic: copy the full data structure */	
-	memcpy( dest, source, dict_obj_info[type].datasize );
+	memcpy( &dest->data, source, dict_obj_info[type].datasize );
 	
 	/* Then strings must be duplicated, not copied */
@@ -271,25 +273,25 @@
 	switch (type) {
 		case DICT_VENDOR:
-			DUP_string( ((struct dict_vendor_data *)dest)->vendor_name );
+			DUP_string_len( dest->data.vendor.vendor_name, &dest->datastr_len );
 			break;
 		
 		case DICT_APPLICATION:
-			DUP_string( ((struct dict_application_data *)dest)->application_name );
+			DUP_string_len( dest->data.application.application_name, &dest->datastr_len );
 			break;
 			
 		case DICT_TYPE:
-			DUP_string( ((struct dict_type_data *)dest)->type_name );
+			DUP_string_len( dest->data.type.type_name, &dest->datastr_len );
 			break;
 			
 		case DICT_ENUMVAL:
-			DUP_string( ((struct dict_enumval_data *)dest)->enum_name );
+			DUP_string_len( dest->data.enumval.enum_name, &dest->datastr_len );
 			break;
 
 		case DICT_AVP:
-			DUP_string( ((struct dict_avp_data *)dest)->avp_name );
+			DUP_string_len( dest->data.avp.avp_name, &dest->datastr_len );
 			break;
 			
 		case DICT_COMMAND:
-			DUP_string( ((struct dict_cmd_data *)dest)->cmd_name );
+			DUP_string_len( dest->data.cmd.cmd_name, &dest->datastr_len );
 			break;
 		
@@ -456,5 +458,5 @@
 	TRACE_ENTRY("%p %p", o1, o2);
 	
-	return strcmp( o1->data.type.type_name, o2->data.type.type_name );
+	return fd_os_cmp( o1->data.type.type_name, o1->datastr_len, o2->data.type.type_name, o2->datastr_len );
 }
 
@@ -464,5 +466,5 @@
 	TRACE_ENTRY("%p %p", o1, o2);
 	
-	return strcmp( o1->data.enumval.enum_name, o2->data.enumval.enum_name );
+	return fd_os_cmp( o1->data.enumval.enum_name, o1->datastr_len, o2->data.enumval.enum_name, o2->datastr_len );
 }
 
@@ -470,7 +472,4 @@
 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);
 	
@@ -478,9 +477,6 @@
 	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));
+			return fd_os_cmp( o1->data.enumval.enum_value.os.data, o1->data.enumval.enum_value.os.len, 
+					  o2->data.enumval.enum_value.os.data, o2->data.enumval.enum_value.os.len);
 		
 		case AVP_TYPE_INTEGER32:
@@ -522,5 +518,5 @@
 	TRACE_ENTRY("%p %p", o1, o2);
 	
-	return strcmp( o1->data.avp.avp_name, o2->data.avp.avp_name );
+	return fd_os_cmp( o1->data.avp.avp_name, o1->datastr_len, o2->data.avp.avp_name, o2->datastr_len );
 }
 
@@ -530,5 +526,5 @@
 	TRACE_ENTRY("%p %p", o1, o2);
 	
-	return strcmp( o1->data.cmd.cmd_name, o2->data.cmd.cmd_name );
+	return fd_os_cmp( o1->data.cmd.cmd_name, o1->datastr_len, o2->data.cmd.cmd_name, o2->datastr_len );
 }
 
@@ -555,5 +551,5 @@
 
 /* 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 )
+static int order_rule_by_avpvc ( struct dict_object *o1, struct dict_object *o2 )
 {
 	TRACE_ENTRY("%p %p", o1, o2);
@@ -590,11 +586,14 @@
 
 /* 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;						\
+/* it is expected that object->datastr_len is the length of the datafield parameter */
+#define SEARCH_os0_l( str, len, sentinel, datafield, isindex ) {		\
+	char *  __str = (char *) (str);						\
+	size_t __strlen = (size_t)(len);					\
 	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 );		\
+		__cmp = fd_os_cmp(__str, __strlen,				\
+			_O(__li->o)->data. datafield, _O(__li->o)->datastr_len);\
 		if (__cmp == 0) {						\
 			if (result)						\
@@ -611,25 +610,26 @@
 }
 
-/* For search of octetstrings in lists (not \0 terminated). */
-#define SEARCH_ocstring( ostr, length, sentinel, osdatafield, isindex ) {	\
-	unsigned char * __ostr = (unsigned char *) ostr;			\
+/* When len is not provided */
+#define SEARCH_os0( str, sentinel, datafield, isindex ) {			\
+	char *  _str = (char *) (str);						\
+	size_t  _strlen = strlen(_str);						\
+	SEARCH_os0_l( _str, _strlen, sentinel, datafield, isindex );		\
+}
+
+
+/* For search of octetstrings in lists. */
+#define SEARCH_os(  str, strlen, sentinel, osdatafield, isindex ) {		\
+	uint8_t * __str = (uint8_t *) (str);					\
+	size_t __strlen = (size_t)(strlen);					\
 	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); 	\
-		}								\
+	for  (__li = (sentinel)->next; __li != (sentinel); __li = __li->next) {	\
+		__cmp = fd_os_cmp(__str, __strlen,				\
+			_O(__li->o)->data. osdatafield .data,			\
+			_O(__li->o)->data. osdatafield .len);			\
 		if (__cmp == 0) {						\
 			if (result)						\
-				*result = _O(__li->next->o);			\
+				*result = _O(__li->o);				\
 			goto end;						\
 		}								\
@@ -644,15 +644,17 @@
 
 /* For search of AVP name in rule lists. */
-#define SEARCH_ruleavpname( str, sentinel ) {					\
-	char * __str = (char *) str;						\
+#define SEARCH_ruleavpname( str, strlen, sentinel ) {				\
+	char * __str = (char *) (str);						\
+	size_t __strlen = (size_t) (strlen);					\
 	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);\
+	for  (__li = (sentinel)->next; __li != (sentinel); __li = __li->next) {	\
+		__cmp = fd_os_cmp(__str, __strlen, 				\
+		  	_O(__li->o)->data.rule.rule_avp->data.avp.avp_name,	\
+			_O(__li->o)->data.rule.rule_avp->datastr_len);		\
 		if (__cmp == 0) {						\
 			if (result)						\
-				*result = _O(__li->next->o);			\
+				*result = _O(__li->o);				\
 			goto end;						\
 		}								\
@@ -677,9 +679,9 @@
 		goto end;							\
 	}									\
-	for  (__li = (sentinel); __li->next != (sentinel); __li = __li->next) {	\
-		__cmp= ORDER_scalar(value, _O(__li->next->o)->data. datafield );\
+	for  (__li = (sentinel)->next; __li != (sentinel); __li = __li->next) {	\
+		__cmp= ORDER_scalar(value, _O(__li->o)->data. datafield );	\
 		if (__cmp == 0) {						\
 			if (result)						\
-				*result = _O(__li->next->o);			\
+				*result = _O(__li->o);				\
 			goto end;						\
 		}								\
@@ -698,13 +700,11 @@
 	struct fd_list * __li;							\
 	ret = 0;								\
-	for  (	  __li = (sentinel);	 					\
-		  __li->next != (sentinel); 					\
-		  __li = __li->next) {						\
+	for  (__li = (sentinel)->next; __li != (sentinel); __li = __li->next) {	\
 		__cmp = ORDER_scalar(value, 					\
-				_O(__li->next->o)->data.cmd.cmd_code );		\
+				_O(__li->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;	\
+			__mask = _O(__li->o)->data.cmd.cmd_flag_mask;		\
+			__val  = _O(__li->o)->data.cmd.cmd_flag_val;		\
 			if ( ! (__mask & CMD_FLAG_REQUEST) )			\
 				continue;					\
@@ -712,5 +712,5 @@
 				continue;					\
 			if (result)						\
-				*result = _O(__li->next->o);			\
+				*result = _O(__li->o);				\
 			goto end;						\
 		}								\
@@ -739,5 +739,5 @@
 		case VENDOR_BY_NAME:
 			/* "what" is a vendor name */
-			SEARCH_string( what, &dict->dict_vendors.list[0], vendor.vendor_name, 0);
+			SEARCH_os0( what, &dict->dict_vendors.list[0], vendor.vendor_name, 0);
 			break;
 			
@@ -771,5 +771,5 @@
 		case APPLICATION_BY_NAME:
 			/* "what" is an application name */
-			SEARCH_string( what, &dict->dict_applications.list[0], application.application_name, 0);
+			SEARCH_os0( what, &dict->dict_applications.list[0], application.application_name, 0);
 			break;
 			
@@ -801,5 +801,5 @@
 		case TYPE_BY_NAME:
 			/* "what" is a type name */
-			SEARCH_string( what, &dict->dict_types, type.type_name, 1);
+			SEARCH_os0( what, &dict->dict_types, type.type_name, 1);
 			break;
 			
@@ -850,10 +850,10 @@
 				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 );
+					SEARCH_os0(  _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, 
+							SEARCH_os(	 _what->search.enum_value.os.data, 
 									 _what->search.enum_value.os.len, 
 									 &parent->list[2], 
@@ -946,5 +946,5 @@
 		case AVP_BY_NAME:
 			/* "what" is the AVP name, vendor 0 */
-			SEARCH_string( what, &dict->dict_vendors.list[2], avp.avp_name, 1);
+			SEARCH_os0( what, &dict->dict_vendors.list[2], avp.avp_name, 1);
 			break;
 			
@@ -969,8 +969,8 @@
 				/* 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);
+					SEARCH_os0( _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 );
+					SEARCH_scalar( _what->avp_code, &vendor->list[1], avp.avp_code, 1, (struct dict_object *)NULL );
 				}
 			}
@@ -980,11 +980,12 @@
 			{
 				struct fd_list * li;
+				size_t wl = strlen((char *)what);
 				
 				/* First, search for vendor 0 */
-				SEARCH_string( what, &dict->dict_vendors.list[2], avp.avp_name, 1);
+				SEARCH_os0_l( what, wl, &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);
+					SEARCH_os0_l( what, wl, &_O(li->o)->list[2], avp.avp_name, 1);
 				}
 			}
@@ -1008,5 +1009,5 @@
 		case CMD_BY_NAME:
 			/* "what" is a command name */
-			SEARCH_string( what, &dict->dict_cmd_name, cmd.cmd_name, 1);
+			SEARCH_os0( what, &dict->dict_cmd_name, cmd.cmd_name, 1);
 			break;
 			
@@ -1096,5 +1097,5 @@
 				
 				/* Perform the search */
-				SEARCH_ruleavpname( avp->data.avp.avp_name, &parent->list[2]);
+				SEARCH_ruleavpname( avp->data.avp.avp_name, avp->datastr_len, &parent->list[2]);
 				
 			}
@@ -1274,21 +1275,21 @@
 	dump_object( &dict->dict_vendors, 0, 3, 0 );
 	
-	fd_log_debug("###### Dumping applications #######\n");
+	fd_log_debug("######          Dumping applications           #######\n");
 
 	dump_object( &dict->dict_applications, 0, 1, 0 );
 	
-	fd_log_debug("###### Dumping types #######\n");
+	fd_log_debug("######             Dumping types               #######\n");
 
 	dump_list( &dict->dict_types, 0, 2, 0 );
 	
-	fd_log_debug("###### Dumping commands per name #######\n");
+	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");
+	fd_log_debug("######   Dumping commands per code and flags   #######\n");
 
 	dump_list( &dict->dict_cmd_code, 0, 0, 0 );
 	
-	fd_log_debug("###### Statistics #######\n");
+	fd_log_debug("######             Statistics                  #######\n");
 
 	for (i=1; i<=DICT_TYPE_MAX; i++)
@@ -1561,5 +1562,5 @@
 	/* Initialize the data of the new object */
 	init_object(new, type);
-	init_object_data(&new->data, data, type);
+	init_object_data(new, data, type);
 	new->dico = dict;
 	new->parent = parent;
@@ -1632,5 +1633,5 @@
 		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 );
+			ret = fd_list_insert_ordered ( &parent->list[2], &new->list[0], (int (*)(void*, void *))order_rule_by_avpvc, (void **)&locref );
 			if (ret)
 				goto error_unlock;
@@ -1659,6 +1660,7 @@
 		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)) {
+				/* if we are here, it means the two vendors id are identical */
+				if (fd_os_cmp(locref->data.vendor.vendor_name, locref->datastr_len, 
+						new->data.vendor.vendor_name, new->datastr_len)) {
 					TRACE_DEBUG(FULL, "Conflicting vendor name");
 					break;
@@ -1670,5 +1672,6 @@
 			case DICT_APPLICATION:
 				/* got same id */
-				if (strcmp(locref->data.application.application_name, new->data.application.application_name)) {
+				if (fd_os_cmp(locref->data.application.application_name, locref->datastr_len, 
+						new->data.application.application_name, new->datastr_len)) {
 					TRACE_DEBUG(FULL, "Conflicting application name");
 					break;
@@ -1763,5 +1766,5 @@
 
 			case DICT_RULE:
-				/* Both rules point to the same AVPs */
+				/* Both rules point to the same AVPs (code & vendor) */
 				if (locref->data.rule.rule_position != new->data.rule.rule_position) {
 					TRACE_DEBUG(FULL, "Conflicting rule position");
@@ -1847,5 +1850,5 @@
 	struct dictionary * new = NULL;
 	
-	TRACE_ENTRY("");
+	TRACE_ENTRY("%p", dict);
 	
 	/* Sanity checks */
@@ -1865,5 +1868,7 @@
 	/* Initialize the sentinel for vendors and AVP lists */
 	init_object( &new->dict_vendors, DICT_VENDOR );
-	new->dict_vendors.data.vendor.vendor_name = "(no vendor)";
+	#define NO_VENDOR_NAME	"(no vendor)"
+	new->dict_vendors.data.vendor.vendor_name = NO_VENDOR_NAME;
+	new->dict_vendors.datastr_len = CONSTSTRLEN(NO_VENDOR_NAME);
 	new->dict_vendors.list[0].o = NULL; /* overwrite since element is also sentinel for this list. */
 	new->dict_vendors.dico = new;
@@ -1871,5 +1876,7 @@
 	/* Initialize the sentinel for applications */
 	init_object( &new->dict_applications, DICT_APPLICATION );
-	new->dict_applications.data.application.application_name = "Diameter Common Messages";
+	#define APPLICATION_0_NAME	"Diameter Common Messages"
+	new->dict_applications.data.application.application_name = APPLICATION_0_NAME;
+	new->dict_applications.datastr_len = CONSTSTRLEN(APPLICATION_0_NAME);
 	new->dict_applications.list[0].o = NULL; /* overwrite since since element is also sentinel for this list. */
 	new->dict_applications.dico = new;
@@ -1884,5 +1891,7 @@
 	/* Initialize the error command object */
 	init_object( &new->dict_cmd_error, DICT_COMMAND );
-	new->dict_cmd_error.data.cmd.cmd_name="(generic error format)";
+	#define GENERIC_ERROR_NAME	"(generic error format)"
+	new->dict_cmd_error.data.cmd.cmd_name = GENERIC_ERROR_NAME;
+	new->dict_cmd_error.datastr_len = CONSTSTRLEN(GENERIC_ERROR_NAME);
 	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;
Index: libfdproto/fdproto-internal.h
===================================================================
--- libfdproto/fdproto-internal.h	(revision 689)
+++ libfdproto/fdproto-internal.h	(revision 706)
@@ -65,5 +65,5 @@
 
 /* Messages / sessions API */
-int fd_sess_fromsid_msg ( unsigned char * sid, size_t len, struct session ** session, int * new);
+int fd_sess_fromsid_msg ( uint8_t * 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 );
Index: libfdproto/init.c
===================================================================
--- libfdproto/init.c	(revision 693)
+++ libfdproto/init.c	(revision 706)
@@ -35,7 +35,4 @@
 
 #include "fdproto-internal.h"
-
-/* Only for CPU cache flush */
-pthread_mutex_t fd_cpu_mtx_dummy = PTHREAD_MUTEX_INITIALIZER;
 
 /* function to free the threadnames */
Index: libfdproto/lists.c
===================================================================
--- libfdproto/lists.c	(revision 662)
+++ libfdproto/lists.c	(revision 706)
@@ -163,128 +163,2 @@
 
 
-/********************************************************************************************************/
-/* 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 687)
+++ libfdproto/log.c	(revision 706)
@@ -83,6 +83,12 @@
 	/* First, check if a value is already assigned to the current thread */
 	val = pthread_getspecific(fd_log_thname);
+	if (TRACE_BOOL(ANNOYING)) {
+		if (val) {
+			fd_log_debug("(Thread '%s' renamed to '%s')\n", (char *)val, name);
+		} else {
+			fd_log_debug("(Thread %p named '%s')\n", pthread_self(), name?:"(nil)");
+		}
+	}
 	if (val != NULL) {
-		TRACE_DEBUG(FULL, "Freeing old thread name: %s", val);
 		free(val);
 	}
Index: libfdproto/messages.c
===================================================================
--- libfdproto/messages.c	(revision 704)
+++ libfdproto/messages.c	(revision 706)
@@ -126,5 +126,6 @@
 			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 */
+	DiamId_t		 msg_src_id;		/* Diameter Id of the peer this message was received from. This string is malloc'd and must be freed */
+	size_t			 msg_src_id_len;	/* cached length of this string */
 };
 
@@ -332,13 +333,14 @@
 	if (sess && dict) {
 		struct dict_object * sess_id_avp;
-		char * sid;
+		os0_t sid;
+		size_t sidlen;
 		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_sess_getsid ( sess, &sid, &sidlen ) );
 		CHECK_FCT( fd_msg_avp_new ( sess_id_avp, 0, &avp ) );
-		val.os.data = (unsigned char *)sid;
-		val.os.len  = strlen(sid);
+		val.os.data = sid;
+		val.os.len  = sidlen;
 		CHECK_FCT( fd_msg_avp_setvalue( avp, &val ) );
 		CHECK_FCT( fd_msg_avp_add( ans, MSG_BRW_FIRST_CHILD, avp ) );
@@ -703,6 +705,6 @@
 		msg->msg_public.msg_eteid
 		);
-	fd_log_debug_fstr(fstr, 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)");
+	fd_log_debug_fstr(fstr, INOBJHDR "intern: rwb:%p rt:%d cb:%p(%p) qry:%p asso:%d sess:%p src:%s(%zd)\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)", msg->msg_src_id_len);
 }
 
@@ -1012,8 +1014,13 @@
 }
 
+/* cache the dictionary model for next function to avoid re-searching at every incoming message */
+static struct dict_object *cached_avp_rr_model = NULL;
+static struct dictionary  *cached_avp_rr_dict  = NULL;
+static pthread_mutex_t     cached_avp_rr_lock = PTHREAD_MUTEX_INITIALIZER;
+
 /* 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);
+int fd_msg_source_set( struct msg * msg, DiamId_t diamid, size_t diamidlen, int add_rr, struct dictionary * dict )
+{
+	TRACE_ENTRY( "%p %p %zd %d %p", msg, diamid, diamidlen, add_rr, dict);
 	
 	/* Check we received a valid message */
@@ -1021,5 +1028,5 @@
 	
 	/* Cleanup any previous source */
-	free(msg->msg_src_id); msg->msg_src_id = NULL;
+	free(msg->msg_src_id); msg->msg_src_id = NULL; msg->msg_src_id_len = 0;
 	
 	/* If the request is to cleanup the source, we are done */
@@ -1029,14 +1036,32 @@
 	
 	/* Otherwise save the new informations */
-	CHECK_MALLOC( msg->msg_src_id = strdup(diamid) );
+	CHECK_MALLOC( msg->msg_src_id = os0dup(diamid, diamidlen) );
+	msg->msg_src_id_len = diamidlen;
+	
 	
 	if (add_rr) {
-		struct dict_object 	*avp_rr_model;
+		struct dict_object 	*avp_rr_model = NULL;
 		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) );
+		/* Lock the cached values */
+		CHECK_POSIX( pthread_mutex_lock(&cached_avp_rr_lock) );
+		if (cached_avp_rr_dict == dict) {
+			avp_rr_model = cached_avp_rr_model;
+		}
+		CHECK_POSIX( pthread_mutex_unlock(&cached_avp_rr_lock) );
+		
+		/* If it was not cached */
+		if (!avp_rr_model) {
+			/* 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) );
+			
+			/* Now cache this result */
+			CHECK_POSIX( pthread_mutex_lock(&cached_avp_rr_lock) );
+			cached_avp_rr_dict  = dict;
+			cached_avp_rr_model = avp_rr_model;
+			CHECK_POSIX( pthread_mutex_unlock(&cached_avp_rr_lock) );
+		}
 		
 		/* Create the AVP with this model */
@@ -1045,6 +1070,6 @@
 		/* Set the AVP value with the diameter id */
 		memset(&val, 0, sizeof(val));
-		val.os.data = (unsigned char *)diamid;
-		val.os.len  = strlen(diamid);
+		val.os.data = (uint8_t *)diamid;
+		val.os.len  = diamidlen;
 		CHECK_FCT( fd_msg_avp_setvalue( avp, &val ) );
 
@@ -1057,7 +1082,7 @@
 }
 
-int fd_msg_source_get( struct msg * msg, char ** diamid )
-{
-	TRACE_ENTRY( "%p %p", msg, diamid);
+int fd_msg_source_get( struct msg * msg, DiamId_t* diamid, size_t * diamidlen )
+{
+	TRACE_ENTRY( "%p %p %p", msg, diamid, diamidlen);
 	
 	/* Check we received valid parameters */
@@ -1067,4 +1092,7 @@
 	/* Copy the informations */
 	*diamid = msg->msg_src_id;
+	
+	if (diamidlen)
+		*diamidlen = msg->msg_src_id_len;
 	
 	/* done */
@@ -2225,5 +2253,5 @@
 
 /* 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)
+int fd_msg_dispatch ( struct msg ** msg, struct session * session, enum disp_action *action, char ** error_code)
 {
 	struct dictionary  * dict;
Index: libfdproto/ostr.c
===================================================================
--- libfdproto/ostr.c	(revision 706)
+++ libfdproto/ostr.c	(revision 706)
@@ -0,0 +1,267 @@
+/*********************************************************************************************************
+* 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 "fdproto-internal.h"
+
+/* Similar to strdup with (must be verified) os0_t */
+os0_t os0dup_int(os0_t s, size_t l) {
+	os0_t r;
+	CHECK_MALLOC_DO( r = calloc(l+1, 1), return NULL );
+	memcpy(r, s, l); /* this might be faster than a strcpy or strdup because it can work with 32 or 64b blocks */
+	return r;
+}
+
+/* case sensitive comparison, fast */
+int fd_os_cmp_int(uint8_t * os1, size_t os1sz, uint8_t * os2, size_t os2sz)
+{
+	ASSERT( os1 && os2);
+	if (os1sz < os2sz)
+		return -1;
+	if (os1sz > os2sz)
+		return 1;
+	return memcmp(os1, os2, os1sz);
+}
+
+/* a local version of tolower() that does not depend on LC_CTYPE locale */
+static inline uint8_t asciitolower(uint8_t a)
+{
+	if ((a >= 'A') && (a <= 'Z'))
+		return a + 32 /* == 'a' - 'A' */;
+	return a;
+}
+
+/* a little less sensitive to case, slower. */
+int fd_os_almostcasecmp_int(uint8_t * os1, size_t os1sz, uint8_t * os2, size_t os2sz)
+{
+	int i;
+	ASSERT( os1 && os2);
+	if (os1sz < os2sz)
+		return -1;
+	if (os1sz > os2sz)
+		return 1;
+	
+	for (i = 0; i < os1sz; i++) {
+		if (os1[i] == os2[i])
+			continue;
+		
+		if (asciitolower(os1[i]) == asciitolower(os2[i])) 
+			continue;
+		
+		return os1[i] < os2[i] ? -1 : 1;
+	}
+	
+	return 0;
+}
+
+/* Check if the string contains only ASCII */
+int fd_os_is_valid_DiameterIdentity(uint8_t * os, size_t ossz)
+{
+	int i;
+	
+	/* Allow letters, digits, hyphen, dot */
+	for (i=0; i < ossz; i++) {
+		if (os[i] > 'z')
+			break;
+		if (os[i] >= 'a')
+			continue;
+		if ((os[i] >= 'A') && (os[i] <= 'Z'))
+			continue;
+		if ((os[i] == '-') || (os[i] == '.'))
+			continue;
+		if ((os[i] >= '0') && (os[i] <= '9'))
+			continue;
+		break;
+	}
+	if (i < ossz) {
+		TRACE_DEBUG(INFO, "Invalid character '%c' in DiameterIdentity '%.*s'", os[i], ossz, os);
+		return 0;
+	}
+	return 1;
+}
+
+/* The following function validates a string as a Diameter Identity or applies the IDNA transformation on it */
+int fd_os_validate_DiameterIdentity(char ** id, size_t * outsz, int memory /* 0: *id can be realloc'd. 1: *id must be malloc'd on output (was static) */)
+{
+	TRACE_ENTRY("%p %p", id, outsz);
+	
+	*outsz = strlen(*id);
+	
+	if (!fd_os_is_valid_DiameterIdentity((os0_t)*id, *outsz)) {
+		char buf[HOST_NAME_MAX];
+		
+		TODO("Stringprep in into buf");
+		TRACE_DEBUG(INFO, "The string '%s' is not a valid DiameterIdentity, it was changed to '%s'", *id, buf);
+		TODO("Realloc *id if !memory");
+		/* copy buf */
+		/* update the size */
+		return ENOTSUP;
+	} else {
+		if (memory == 1) {
+			CHECK_MALLOC( *id = os0dup(*id, *outsz) );
+		}
+	}
+	return 0;
+}
+
+
+
+
+
+/********************************************************************************************************/
+/* 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_os_hash ( uint8_t * string, size_t len )
+{
+	uint32_t hash = len;
+	uint8_t * 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/rt_data.c
===================================================================
--- libfdproto/rt_data.c	(revision 662)
+++ libfdproto/rt_data.c	(revision 706)
@@ -50,7 +50,9 @@
 /* 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 */
+	struct fd_list	chain;	/* link in the list, ordered by nexthop (fd_os_cmp) */
+	DiamId_t 	nexthop;/* the peer the message was sent to */
+	size_t		nexthoplen; /* cached string length */
+	DiamId_t	erh;	/* the origin of the error */
+	size_t		erhlen; /* cached string length */
 	uint32_t	code;	/* the error code */
 };
@@ -107,17 +109,17 @@
 }
 
-/* Add a peer to the candidates list */
-int  fd_rtd_candidate_add(struct rt_data * rtd, char * peerid, char * realm)
+/* Add a peer to the candidates list. The source is our local peer list, so no need to care for the case here. */
+int  fd_rtd_candidate_add(struct rt_data * rtd, DiamId_t peerid, size_t peeridlen, DiamId_t realm, size_t realmlen)
 {
 	struct fd_list * prev;
 	struct rtd_candidate * new;
 	
-	TRACE_ENTRY("%p %p", rtd, peerid);
-	CHECK_PARAMS(rtd && peerid);
+	TRACE_ENTRY("%p %p %zd %p %zd", rtd, peerid, peeridlen, realm, realmlen);
+	CHECK_PARAMS(rtd && peerid && peeridlen);
 	
 	/* 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);
+		int cmp = fd_os_cmp(peerid, peeridlen, cp->diamid, cp->diamidlen);
 		if (cmp > 0)
 			break;
@@ -127,10 +129,16 @@
 	}
 	
+	/* Create the new entry */
 	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) );
-	
+	CHECK_MALLOC( new->diamid = os0dup(peerid, peeridlen) )
+	new->diamidlen = peeridlen;
+	if (realm) {
+		CHECK_MALLOC( new->realm = os0dup(realm, realmlen) )
+		new->realmlen = realmlen;
+	}
+	
+	/* insert in the list at the correct position */
 	fd_list_insert_after(prev, &new->chain);
 	
@@ -138,28 +146,20 @@
 }
 
-/* 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 */)
+/* Remove a peer from the candidates (if it is found). Case insensitive search since the names are received from other peers */
+void fd_rtd_candidate_del(struct rt_data * rtd, uint8_t * id, size_t idsz)
 {
 	struct fd_list * li;
 	
-	TRACE_ENTRY("%p %p %zd", rtd, peerid, sz);
-	CHECK_PARAMS_DO( rtd && peerid , return );
+	TRACE_ENTRY("%p %p %zd", rtd, id, idsz);
+	CHECK_PARAMS_DO( rtd && id && idsz, return );
+	
+	if (!fd_os_is_valid_DiameterIdentity(id, idsz))
+		/* it cannot be in the list */
+		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);
-		}
+		
+		int cmp = fd_os_almostcasecmp(id, idsz, c->diamid, c->diamidlen);
 		
 		if (!cmp) {
@@ -175,5 +175,5 @@
 			continue;
 		
-		/* The list is ordered only if not extracted */
+		/* The list is guaranteed to be ordered only if not extracted */
 		if (! rtd->extracted)
 			break;
@@ -183,17 +183,18 @@
 }
 
-/* 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)
+/* If a peer returned a protocol error for this message, save it so that we don't try to send it there again.
+ Case insensitive search since the names are received from other peers*/
+int  fd_rtd_error_add(struct rt_data * rtd, DiamId_t sentto, size_t senttolen, 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 */
+	TRACE_ENTRY("%p %p %zd %p %zd %u", rtd, sentto, senttolen, origin, originsz, rcode);
+	CHECK_PARAMS( rtd && sentto && senttolen ); /* 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);
+		int cmp = fd_os_cmp(sentto, senttolen, e->nexthop, e->nexthoplen);
 		if (cmp > 0)
 			continue;
@@ -203,5 +204,6 @@
 	}
 	
-	/* If we already had this entry, we should not have sent the message again to this peer... anyway... */
+	/* If we already had this entry, we should not have sent the message again to this peer... anyway, let's close our eyes. */
+	/* in the normal case, we save the error */
 	if (!match) {
 		/* Add a new entry in the error list */
@@ -210,10 +212,22 @@
 		memset(new, 0, sizeof(struct rtd_error));
 		fd_list_init(&new->chain, NULL);
-		CHECK_MALLOC(new->nexthop = strdup(sentto));
+
+		CHECK_MALLOC(new->nexthop = os0dup(sentto, senttolen));
+		new->nexthoplen = senttolen;
+		
 		if (origin) {
-			CHECK_MALLOC( new->erh = malloc(originsz + 1) );
-			memcpy(new->erh, origin, originsz);
-			new->erh[originsz] = '\0';
+			if (!originsz) {
+				originsz=strlen((char *)origin);
+			} else {
+				if (!fd_os_is_valid_DiameterIdentity(origin, originsz)){
+					TRACE_DEBUG(FULL, "Received error %d from peer with invalid Origin-Host AVP, not saved", rcode);
+					origin = NULL;
+					goto after_origin;
+				}
+			}
+			CHECK_MALLOC( new->erh = (DiamId_t)os0dup(origin, originsz) );
+			new->erhlen = originsz;
 		}
+after_origin:
 		new->code = rcode;
 		fd_list_insert_before(li, &new->chain);
@@ -221,7 +235,7 @@
 	
 	/* Finally, remove this (these) peers from the candidate list */
-	fd_rtd_candidate_del(rtd, sentto, 0);
+	fd_rtd_candidate_del(rtd, (os0_t)sentto, senttolen);
 	if (origin)
-		fd_rtd_candidate_del(rtd, (char *)origin, originsz);
+		fd_rtd_candidate_del(rtd, origin, originsz);
 	
 	/* Done! */
@@ -274,5 +288,5 @@
 			fd_list_move_end(candidates, &highest);
 			
-			/* And the new high score is this reset */
+			/* And the new high score is set */
 			hs = c->score;
 		}
Index: libfdproto/sessions.c
===================================================================
--- libfdproto/sessions.c	(revision 691)
+++ libfdproto/sessions.c	(revision 706)
@@ -69,5 +69,5 @@
 	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 		(*cleanup)(session_state *, os0_t, void *); /* The cleanup function to be called for cleaning a state */
 	void             *opaque; /* a value that is passed as is to the cleanup callback */
 };
@@ -84,5 +84,5 @@
 	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 */
+		os0_t 			 sid;	/* For deleted state, the sid of the session it belong to */
 	};
 };
@@ -92,5 +92,6 @@
 	int 		eyec;	/* Eyecatcher, SI_EYEC */
 	
-	char *		sid;	/* The \0-terminated Session-Id */
+	os0_t		sid;	/* The \0-terminated Session-Id */
+	size_t		sidlen; /* cached length of sid */
 	uint32_t	hash;	/* computed hash of sid */
 	struct fd_list	chain_h;/* chaining in the hash table of sessions. */
@@ -102,9 +103,10 @@
 	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 */
+	int		is_destroyed; /* boolean telling if fd_sess_detroy has been called on this */
 };
 
 /* Sessions hash table, to allow fast sid to session retrieval */
 static struct {
-	struct fd_list	sentinel;	/* sentinel element for this sublist */
+	struct fd_list	sentinel;	/* sentinel element for this sublist. The sublist is ordered by hash value, then fd_os_cmp(sid). */
 	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 ] ;
@@ -132,10 +134,10 @@
 /********************************************************************************************************/
 
-/* 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)
+/* Initialize a session object. It is not linked now. sid must be already malloc'ed. The hash has already been computed. */
+static struct session * new_session(os0_t sid, size_t sidlen, uint32_t hash)
 {
 	struct session * sess;
 	
-	TRACE_ENTRY("%p %d", sid, sidlen);
+	TRACE_ENTRY("%p %zd", sid, sidlen);
 	CHECK_PARAMS_DO( sid && sidlen, return NULL );
 	
@@ -146,5 +148,6 @@
 	
 	sess->sid  = sid;
-	sess->hash = fd_hash(sid, sidlen);
+	sess->sidlen = sidlen;
+	sess->hash = hash;
 	fd_list_init(&sess->chain_h, sess);
 	
@@ -157,4 +160,15 @@
 	
 	return sess;
+}
+
+/* destroy the session object. It should really be already unlinked... */
+static void del_session(struct session * s)
+{
+	ASSERT(FD_IS_LIST_EMPTY(&s->states));
+	free(s->sid);
+	fd_list_unlink(&s->chain_h);
+	fd_list_unlink(&s->expire);
+	CHECK_POSIX_DO( pthread_mutex_destroy(&s->stlock), /* continue */ );
+	free(s);
 }
 	
@@ -250,9 +264,12 @@
 	TRACE_ENTRY("");
 	CHECK_FCT_DO( fd_thr_term(&exp_thr), /* continue */ );
+	
+	/* Destroy all sessions in the hash table, and the hash table itself? -- How to do it without a race condition ? */
+	
 	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 )
+int fd_sess_handler_create_internal ( struct session_handler ** handler, void (*cleanup)(session_state *, os0_t, void *), void * opaque )
 {
 	struct session_handler *new;
@@ -299,9 +316,9 @@
 		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) {
+		for (li_si = sess_hash[i].sentinel.next; li_si != &sess_hash[i].sentinel; li_si = li_si->next) { /* for each session in the hash line */
 			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) {
+			for (li_st = sess->states.next; li_st != &sess->states; li_st = li_st->next) { /* for each state in this session */
 				struct state * st = (struct state *)(li_st->o);
 				/* The list is ordered */
@@ -311,5 +328,5 @@
 					/* 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) );
+					st->sid = sess->sid;
 					fd_list_insert_before(&deleted_states, &st->chain);
 				}
@@ -326,5 +343,4 @@
 		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);
@@ -343,35 +359,49 @@
 
 /* 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;
+int fd_sess_new ( struct session ** session, DiamId_t diamid, size_t diamidlen, uint8_t * opt, size_t optlen )
+{
+	os0_t  sid = NULL;
 	size_t sidlen;
+	uint32_t hash;
 	struct session * sess;
 	struct fd_list * li;
 	int found = 0;
-	
-	TRACE_ENTRY("%p %p %p %d", session, diamId, opt, optlen);
-	CHECK_PARAMS( session && (diamId || opt) );
-	
+	int ret = 0;
+	
+	TRACE_ENTRY("%p %p %zd %p %zd", session, diamid, diamidlen, opt, optlen);
+	CHECK_PARAMS( session && (diamid || opt) );
+
+	if (diamid) {	
+		if (!diamidlen) {
+			diamidlen = strlen(diamid);
+		} 
+		/* We check if the string is a valid DiameterIdentity */
+		CHECK_PARAMS( fd_os_is_valid_DiameterIdentity((uint8_t *)diamid, diamidlen) );
+	} else {
+		diamidlen = 0;
+	}
+	if (opt) {	
+		if (!optlen) {
+			optlen = strlen((char *)opt);
+		} else {
+			CHECK_PARAMS( fd_os_is_valid_os0(opt, optlen) );
+		}
+	} else {
+		optlen = 0;
+	}
+		
 	/* Ok, first create the identifier for the string */
-	if (diamId == NULL) {
+	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);
-		}
+		CHECK_MALLOC( sid = os0dup(opt, optlen) );
+		sidlen = optlen;
 	} else {
 		uint32_t sid_h_cpy;
 		uint32_t sid_l_cpy;
 		/* "<diamId>;<high32>;<low32>[;opt]" */
-		sidlen = strlen(diamId);
+		sidlen = diamidlen;
 		sidlen += 22; /* max size of ';<high32>;<low32>' */
 		if (opt)
-			sidlen += 1 + (optlen ?: strlen(opt)) ;
+			sidlen += 1 + optlen; /* ';opt' */
 		sidlen++; /* space for the final \0 also */
 		CHECK_MALLOC( sid = malloc(sidlen) );
@@ -385,32 +415,27 @@
 		
 		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);
+			sidlen = snprintf((char*)sid, sidlen, "%.*s;%u;%u;%.*s", (int)diamidlen, diamid, sid_h_cpy, sid_l_cpy, (int)optlen, opt);
 		} else {
-			sidlen = snprintf(sid, sidlen, "%s;%u;%u", diamId, sid_h_cpy, sid_l_cpy);
+			sidlen = snprintf((char*)sid, sidlen, "%.*s;%u;%u", (int)diamidlen, 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) );
+	hash = fd_os_hash(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) {
+	CHECK_POSIX( pthread_mutex_lock( H_LOCK(hash) ) );
+	pthread_cleanup_push( fd_cleanup_mutex, H_LOCK(hash) );
+	
+	for (li = H_LIST(hash)->next; li != H_LIST(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)
+		if (s->hash < hash)
 			continue;
-		if (s->hash > sess->hash)
+		if (s->hash > hash)
 			break;
 		
-		cmp = strcasecmp(s->sid, sess->sid);
+		cmp = fd_os_cmp(s->sid, s->sidlen, sid, sidlen);
 		if (cmp < 0)
 			continue;
@@ -424,49 +449,57 @@
 	}
 	
-	/* If the session did not exist, we can link it in global tables */
+	/* If the session did not exist, we can create it & link it in global tables */
 	if (!found) {
+		CHECK_MALLOC_DO(sess = new_session(sid, sidlen, hash),
+			{
+				ret = ENOMEM;
+				goto out;
+			} );
+	
 		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;
+	} else {
+		/* it was found: was it previously destroyed? */
+		if ((*session)->is_destroyed == 0) {
+			ret = EALREADY;
+			goto out;
+		} else {
+			/* the session was marked destroyed, let's re-activate it. */
+			TODO("Re-creating a deleted session. Should investigate if this can lead to an issue... (need more feedback)");
+			sess = *session;
+			
+			/* update the expiry time */
+			CHECK_SYS_DO( clock_gettime(CLOCK_REALTIME, &sess->timeout), { ASSERT(0); } );
+			sess->timeout.tv_sec += SESS_DEFAULT_LIFETIME;
 		}
-		fd_list_insert_after( li, &sess->expire );
-
-		/* We added a new expiring element, we must signal */
-		if (li == &exp_sentinel) {
-			CHECK_POSIX_DO( pthread_cond_signal(&exp_cond), { ASSERT(0); } ); /* if it fails, we might not pop the cleanup handlers, but this should not happen -- and we'd have a serious problem otherwise */
-		}
-		
-		#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_DO( pthread_mutex_unlock( &exp_lock ), { ASSERT(0); } ); /* if it fails, we might not pop the cleanup handler, but this should not happen -- and we'd have a serious problem otherwise */
-	}
-	
+	}
+		
+	/* We must 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_DO( pthread_cond_signal(&exp_cond), { ASSERT(0); } ); /* if it fails, we might not pop the cleanup handlers, but this should not happen -- and we'd have a serious problem otherwise */
+	}
+
+	/* We're done with the locked part */
 	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;
-	}
+	CHECK_POSIX_DO( pthread_mutex_unlock( &exp_lock ), { ASSERT(0); } ); /* if it fails, we might not pop the cleanup handler, but this should not happen -- and we'd have a serious problem otherwise */
+
+out:
+	;	
+	pthread_cleanup_pop(0);
+	CHECK_POSIX( pthread_mutex_unlock( H_LOCK(hash) ) );
+	
+	if (ret) /* in case of error */
+		return ret;
 	
 	*session = sess;
@@ -475,5 +508,5 @@
 
 /* Find or create a session */
-int fd_sess_fromsid ( char * sid, size_t len, struct session ** session, int * new)
+int fd_sess_fromsid ( uint8_t * sid, size_t len, struct session ** session, int * new)
 {
 	int ret;
@@ -482,6 +515,10 @@
 	CHECK_PARAMS( sid && session );
 	
+	if (!fd_os_is_valid_os0(sid,len)) {
+		TRACE_DEBUG(INFO, "Warning: a Session-Id value contains \\0 chars... (len:%zd, begin:'%.*s')\n => Debug messages may be truncated.", len, len, sid);
+	}
+	
 	/* All the work is done in sess_new */
-	ret = fd_sess_new ( session, NULL, sid, len );
+	ret = fd_sess_new ( session, NULL, 0, sid, len );
 	switch (ret) {
 		case 0:
@@ -500,5 +537,5 @@
 
 /* Get the sid of a session */
-int fd_sess_getsid ( struct session * session, char ** sid )
+int fd_sess_getsid ( struct session * session, os0_t * sid, size_t * sidlen )
 {
 	TRACE_ENTRY("%p %p", session, sid);
@@ -507,4 +544,6 @@
 	
 	*sid = session->sid;
+	if (sidlen)
+		*sidlen = session->sidlen;
 	
 	return 0;
@@ -543,15 +582,4 @@
 	}
 
-	#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);
@@ -561,8 +589,14 @@
 }
 
-/* Destroy a session immediatly */
+/* Destroy the states associated to a session, and mark it destroyed. */
 int fd_sess_destroy ( struct session ** session )
 {
 	struct session * sess;
+	int destroy_now;
+	os0_t sid;
+	int ret = 0;
+	
+	/* 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 );
 	
 	TRACE_ENTRY("%p", session);
@@ -572,27 +606,52 @@
 	*session = NULL;
 	
-	/* Unlink and invalidate */
+	/* Lock the hash line */
 	CHECK_POSIX( pthread_mutex_lock( H_LOCK(sess->hash) ) );
 	pthread_cleanup_push( fd_cleanup_mutex, H_LOCK(sess->hash) );
+	
+	/* Unlink from the expiry list */
 	CHECK_POSIX_DO( pthread_mutex_lock( &exp_lock ), { ASSERT(0); /* otherwise cleanup handler is not pop'd */ } );
-	fd_list_unlink( &sess->chain_h );
 	fd_list_unlink( &sess->expire ); /* no need to signal the condition here */
-	sess->eyec = 0xdead;
 	CHECK_POSIX_DO( pthread_mutex_unlock( &exp_lock ), { ASSERT(0); /* otherwise cleanup handler is not pop'd */ } );
-	pthread_cleanup_pop(0);
-	CHECK_POSIX( 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) */
+	
+	/* Now move all states associated to this session into deleted_states */
+	CHECK_POSIX_DO( pthread_mutex_lock( &sess->stlock ), { ASSERT(0); /* otherwise cleanup handler is not pop'd */ } );
 	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);
+		fd_list_insert_before(&deleted_states, &st->chain);
+	}
+	CHECK_POSIX_DO( pthread_mutex_unlock( &sess->stlock ), { ASSERT(0); /* otherwise cleanup handler is not pop'd */ } );
+	
+	/* Mark the session as destroyed */
+	destroy_now = (sess->msg_cnt == 0);
+	if (destroy_now) {
+		fd_list_unlink( &sess->chain_h );
+		sid = sess->sid;
+	} else {
+		sess->is_destroyed = 1;
+		CHECK_MALLOC_DO( sid = os0dup(sess->sid, sess->sidlen), ret = ENOMEM );
+	}
+	pthread_cleanup_pop(0);
+	CHECK_POSIX( pthread_mutex_unlock( H_LOCK(sess->hash) ) );
+	
+	if (ret)
+		return ret;
+	
+	/* Now, really delete the states */
+	while (!FD_IS_LIST_EMPTY(&deleted_states)) {
+		struct state * st = (struct state *)(deleted_states.next->o);
+		fd_list_unlink(&st->chain);
+		TRACE_DEBUG(FULL, "Calling handler %p cleanup for state %p registered with session '%s'", st->hdl, st, sid);
+		(*st->hdl->cleanup)(st->state, sid, st->hdl->opaque);
 		free(st);
 	}
 	
-	/* Finally, destroy the session itself */
-	free(sess->sid);
-	free(sess);
+	/* Finally, destroy the session itself, if it is not referrenced by any message anymore */
+	if (destroy_now) {
+		del_session(sess);
+	} else {
+		free(sid);
+	}
 	
 	return 0;
@@ -604,4 +663,5 @@
 	struct session * sess;
 	uint32_t hash;
+	int destroy_now = 0;
 	
 	TRACE_ENTRY("%p", session);
@@ -612,17 +672,31 @@
 	*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( H_LOCK(hash) ) );
+	pthread_cleanup_push( fd_cleanup_mutex, H_LOCK(hash) );
+	CHECK_POSIX_DO( pthread_mutex_lock( &sess->stlock ), { ASSERT(0); /* otherwise, cleanup not poped on FreeBSD */ } );
+	pthread_cleanup_push( fd_cleanup_mutex, &sess->stlock );
 	CHECK_POSIX_DO( pthread_mutex_lock( &exp_lock ), { ASSERT(0); /* otherwise, cleanup not poped on FreeBSD */ } );
+	
+	/* We only do something if the states list is empty */
 	if (FD_IS_LIST_EMPTY(&sess->states)) {
-		fd_list_unlink( &sess->chain_h );
+		/* In this case, we do as in destroy */
 		fd_list_unlink( &sess->expire );
-		sess->eyec = 0xdead;
-		free(sess->sid);
-		free(sess);
-	}
+		destroy_now = (sess->msg_cnt == 0);
+		if (destroy_now) {
+			fd_list_unlink(&sess->chain_h);
+		} else {
+			/* just mark it as destroyed, it will be freed when the last message stops referencing it */
+			sess->is_destroyed = 1;
+		}
+	}
+	
 	CHECK_POSIX_DO( pthread_mutex_unlock( &exp_lock ), { ASSERT(0); /* otherwise, cleanup not poped on FreeBSD */ } );
 	pthread_cleanup_pop(0);
+	CHECK_POSIX_DO( pthread_mutex_unlock( &sess->stlock ), { ASSERT(0); /* otherwise, cleanup not poped on FreeBSD */ } );
+	pthread_cleanup_pop(0);
 	CHECK_POSIX( pthread_mutex_unlock( H_LOCK(hash) ) );
+	
+	if (destroy_now)
+		del_session(sess);
 	
 	return 0;
@@ -638,5 +712,5 @@
 	
 	TRACE_ENTRY("%p %p %p", handler, session, state);
-	CHECK_PARAMS( handler && VALIDATE_SH(handler) && session && VALIDATE_SI(session) && state );
+	CHECK_PARAMS( handler && VALIDATE_SH(handler) && session && VALIDATE_SI(session) && (!session->is_destroyed) && state );
 	
 	/* Lock the session state list */
@@ -720,5 +794,5 @@
 
 /* For the messages module */
-int fd_sess_fromsid_msg ( unsigned char * sid, size_t len, struct session ** session, int * new)
+int fd_sess_fromsid_msg ( uint8_t * sid, size_t len, struct session ** session, int * new)
 {
 	TRACE_ENTRY("%p %zd %p %p", sid, len, session, new);
@@ -726,5 +800,5 @@
 	
 	/* Get the session object */
-	CHECK_FCT( fd_sess_fromsid ( (char *) sid, len, session, new) );
+	CHECK_FCT( fd_sess_fromsid ( sid, len, session, new) );
 	
 	/* Increase count */
@@ -786,5 +860,5 @@
 	} else {
 		
-		fd_log_debug("\t  %*s  sid '%s', hash %x\n", level, "", session->sid, session->hash);
+		fd_log_debug("\t  %*s  sid '%s'(%zd), hash %x\n", level, "", session->sid, session->sidlen, session->hash);
 
 		strftime(buf, sizeof(buf), "%D,%T", localtime_r( &session->timeout.tv_sec , &tm ));
