changeset 706:4ffbc9f1e922

Large UNTESTED commit with the following changes: * Improved DiameterIdentity handling (esp. interationalization issues), and improve efficiency of some string operations in peers, sessions, and dictionary modules (closes #7) * Cleanup in the session module to free only unreferenced sessions (#16) * Removed fd_cpu_flush_cache(), replaced by more robust alternatives. * Improved peer state machine algorithm to counter SCTP multistream race condition.
author Sebastien Decugis <sdecugis@nict.go.jp>
date Wed, 09 Feb 2011 15:26:58 +0900
parents f0cb8f465763
children e387d5c6b6f5
files contrib/debian/changelog doc/dbg_interactive.py.sample doc/freediameter.conf.sample extensions/app_radgw/rgw_clients.c extensions/app_radgw/rgw_common.h extensions/app_radgw/rgw_servers.c extensions/app_radgw/rgwx_acct.c extensions/app_radgw/rgwx_auth.c extensions/app_radgw/rgwx_debug.c extensions/app_radgw/rgwx_sip.c extensions/app_sip/registrationtermination.c extensions/dbg_interactive/dbg_interactive.i extensions/dbg_interactive/messages.i extensions/dbg_interactive/peers.i extensions/dbg_interactive/routing.i extensions/dbg_interactive/sessions.i extensions/test_app/ta_bench.c extensions/test_app/ta_cli.c extensions/test_netemul/tne_process.c extensions/test_sip/locationinfo.c extensions/test_sip/locationinfosl.c extensions/test_sip/serverassignment.c extensions/test_sip/userauthorization.c include/freeDiameter/libfdcore.h include/freeDiameter/libfdproto.h libfdcore/cnxctx.c libfdcore/cnxctx.h libfdcore/config.c libfdcore/core.c libfdcore/extensions.c libfdcore/fdcore-internal.h libfdcore/fdd.y libfdcore/messages.c libfdcore/p_ce.c libfdcore/p_cnx.c libfdcore/p_dp.c libfdcore/p_dw.c libfdcore/p_expiry.c libfdcore/p_out.c libfdcore/p_psm.c libfdcore/p_sr.c libfdcore/peers.c libfdcore/routing_dispatch.c libfdcore/sctp.c libfdcore/sctps.c libfdcore/server.c libfdproto/CMakeLists.txt libfdproto/dictionary.c libfdproto/fdproto-internal.h libfdproto/init.c libfdproto/lists.c libfdproto/log.c libfdproto/messages.c libfdproto/ostr.c libfdproto/rt_data.c libfdproto/sessions.c tests/CMakeLists.txt tests/testdisp.c tests/testlist.c tests/testostr.c tests/testsctp.c tests/testsess.c
diffstat 62 files changed, 2246 insertions(+), 1408 deletions(-) [+]
line wrap: on
line diff
--- a/contrib/debian/changelog	Mon Jan 31 17:22:21 2011 +0900
+++ b/contrib/debian/changelog	Wed Feb 09 15:26:58 2011 +0900
@@ -4,7 +4,7 @@
     framework now contained in the libfdcore library.
     libfreeDiameter renamed as libfdproto.
     This closes #15.
-  * API version bumped to version 4 accordingly.
+  * API version bumped to version 4.
   * Improved fd_dict_new() when the same object already exists.
   * Improvements to dict_legacy_xml extension.
   * Cleanups in links to shared libraries
@@ -12,8 +12,15 @@
     to RADIUS (closes #25)
   * Allow default signals such as SIGTSTP (ctrl-z) to be used with
     freeDiameterd
+  * Improved DiameterIdentity handling (esp. interationalization issues), 
+    and improve efficiency of some string operations in peers, sessions, 
+    and dictionary modules (closes #7)
+  * Cleanup in the session module to free only unreferenced sessions (#16)
+  * Removed fd_cpu_flush_cache(), replaced by more robust alternatives.
+  * Improved peer state machine algorithm to counter SCTP multistream race 
+    condition.
 
- -- Sebastien Decugis <sdecugis@nict.go.jp>  Tue, 18 Jan 2011 15:13:00 +0900
+ -- Sebastien Decugis <sdecugis@nict.go.jp>  Mon, 07 Feb 2011 17:24:20 +0900
 
 freediameter (1.0.4) UNRELEASED; urgency=low
 
--- a/doc/dbg_interactive.py.sample	Mon Jan 31 17:22:21 2011 +0900
+++ b/doc/dbg_interactive.py.sample	Wed Feb 09 15:26:58 2011 +0900
@@ -104,7 +104,7 @@
 
 ############# Hash ############
 
-hex(fd_hash("hello world"))	# It accepts binary data
+hex(fd_os_hash("hello world"))	# It accepts binary data
 
 
 ############# Dictionary ############
--- a/doc/freediameter.conf.sample	Mon Jan 31 17:22:21 2011 +0900
+++ b/doc/freediameter.conf.sample	Wed Feb 09 15:26:58 2011 +0900
@@ -210,6 +210,7 @@
 #  ConnectTo = "202.249.37.5";
 #  ConnectTo = "2001:200:903:2::202:1";
 #  TLS_Prio = "NORMAL";
+#  Realm = "realm.net"; # Reject the peer if it does not advertise this realm.
 # Examples:
 #ConnectPeer = "aaa.wide.ad.jp";
 #ConnectPeer = "old.diameter.serv" { TcTimer = 60; TLS_old_method; No_SCTP; } ;
--- a/extensions/app_radgw/rgw_clients.c	Mon Jan 31 17:22:21 2011 +0900
+++ b/extensions/app_radgw/rgw_clients.c	Wed Feb 09 15:26:58 2011 +0900
@@ -85,10 +85,14 @@
 	/* The FQDN, realm, and optional aliases */
 	int			 is_local; /* true if the RADIUS client runs on the same host -- we use Diameter Identity in that case */
 	enum rgw_cli_type 	 type; /* is it a proxy ? */
-	char 			*fqdn;
+	DiamId_t		 fqdn; /* malloc'd here */
 	size_t			 fqdn_len;
-	char			*realm;
-	char			**aliases;
+	DiamId_t		 realm; /* references another string, do not free */
+	size_t			 realm_len;
+	struct {
+		os0_t		 name;
+		size_t		 len;
+	}			*aliases; /* Received aliases */
 	size_t			 aliases_nb;
 	
 	/* The secret key data. */
@@ -209,7 +213,8 @@
 static int client_create(struct rgw_client ** res, struct sockaddr ** ip_port, unsigned char ** key, size_t keylen, enum rgw_cli_type type )
 {
 	struct rgw_client *tmp = NULL;
-	char buf[255];
+	DiamId_t fqdn;
+	size_t fqdn_len;
 	int ret, i;
 	int loc = 0;
 	
@@ -219,6 +224,7 @@
 		/* The client is local */
 		loc = 1;
 	} else {
+		char buf[255];
 	
 		/* Search FQDN for the client */
 		ret = getnameinfo( *ip_port, sizeof(struct sockaddr_storage), &buf[0], sizeof(buf), NULL, 0, 0 );
@@ -226,6 +232,12 @@
 			TRACE_DEBUG(INFO, "Unable to resolve peer name: %s", gai_strerror(ret));
 			return EINVAL;
 		}
+		fqdn = &buf[0];
+		CHECK_FCT_DO( ret = fd_os_validate_DiameterIdentity(&fqdn, &fqdn_len, 1),
+			{
+				TRACE_DEBUG(INFO, "Unable to use resolved peer name '%s' as DiameterIdentity: %s", buf, strerror(ret));
+				return ret;
+			} );
 	}
 	
 	/* Create the new object */
@@ -245,14 +257,19 @@
 		tmp->is_local = 1;
 	} else {
 		/* Copy the fqdn */
-		CHECK_MALLOC( tmp->fqdn = strdup(buf) );
-		tmp->fqdn_len = strlen(tmp->fqdn);
+		tmp->fqdn = fqdn;
+		tmp->fqdn_len = fqdn_len;
+
 		/* Find an appropriate realm */
-		tmp->realm = strchr(tmp->fqdn, '.');
-		if (tmp->realm)
+		tmp->realm = strchr(fqdn, '.');
+		if (tmp->realm) {
 			tmp->realm += 1;
-		if ((!tmp->realm) || (*tmp->realm == '\0')) /* in case the fqdn was "localhost." for example, if it is possible... */
+			tmp->realm_len = tmp->fqdn_len - (tmp->realm - fqdn);
+		}
+		if ((!tmp->realm) || (*tmp->realm == '\0')) { /* in case the fqdn was "localhost." for example, if it is possible... */
 			tmp->realm = fd_g_config->cnf_diamrlm;
+			tmp->realm_len = fd_g_config->cnf_diamrlm_len;
+		}
 	}
 	
 	/* move the sa info reference */
@@ -281,7 +298,7 @@
 		
 		/* Free the data */
 		for (idx = 0; idx < client->aliases_nb; idx++)
-			free(client->aliases[idx]);
+			free(client->aliases[idx].name);
 		free(client->aliases);
 		free(client->fqdn);
 		free(client->sa);
@@ -531,9 +548,10 @@
 	int idx;
 	int valid_nas_info = 0;
 	struct radius_attr_hdr *nas_ip = NULL, *nas_ip6 = NULL, *nas_id = NULL;
-	char * oh_str = NULL;
-	char * or_str = NULL;
-	char * rr_str = NULL;
+	size_t nas_id_len;
+	char * oh_str = NULL; size_t oh_strlen; int oh_free = 0;
+	char * or_str = NULL; size_t or_strlen;
+	char * rr_str = NULL; size_t rr_strlen;
 	char buf[REVERSE_DNS_SIZE_MAX]; /* to store DNS lookups results */
 	
 	struct avp *avp = NULL;
@@ -554,6 +572,7 @@
 			
 		if ((attr->type == RADIUS_ATTR_NAS_IDENTIFIER) && (attr_len > 0)) {
 			nas_id = attr;
+			nas_id_len = attr_len;
 			continue;
 		}
 			
@@ -567,7 +586,7 @@
 		TRACE_DEBUG(FULL, "The message does not contain any NAS identification attribute.");
 		
 		/* Get information on this peer */
-		CHECK_FCT( rgw_clients_get_origin(cli, &oh_str, &or_str) );
+		CHECK_FCT( rgw_clients_get_origin(cli, &oh_str, &oh_strlen, &or_str, &or_strlen) );
 		
 		goto diameter;
 	}
@@ -618,12 +637,14 @@
 				TRACE_DEBUG(INFO, "Message received with a NAS-IP-Address or NAS-IPv6-Address different \nfrom the sender's. Please configure as Proxy if this is expected.\n Message discarded.");
 				return EINVAL;
 			} else {
+				int ret;
+				sSS ss;
 				/* the peer is configured as a proxy, or running on localhost, so accept the message */
-				sSS ss;
 				
 				/* In that case, the cli will be stored as Route-Record and the NAS-IP-Address as origin */
 				if (!cli->is_local) {
 					rr_str = cli->fqdn;
+					rr_strlen = cli->fqdn_len;
 				}
 				
 				/* We must DNS-reverse the NAS-IP*-Address */
@@ -640,25 +661,39 @@
 				CHECK_SYS_DO( getnameinfo( (sSA *)&ss, sSAlen(&ss), &buf[0], sizeof(buf), NULL, 0, NI_NAMEREQD),
 					{
 						if (cli->is_local) {
-							CHECK_FCT( rgw_clients_get_origin(cli, &oh_str, &or_str) );
+							CHECK_FCT( rgw_clients_get_origin(cli, &oh_str, &oh_strlen, &or_str, &or_strlen) );
 							goto diameter;
 						}
 						
 						TRACE_DEBUG(INFO, "The NAS-IP*-Address cannot be DNS reversed in order to create the Origin-Host AVP; rejecting the message (translation is impossible).");
 						return EINVAL;
 					} );
+					
+				oh_str = &buf[0];
+				CHECK_FCT_DO( ret = fd_os_validate_DiameterIdentity(&oh_str, &oh_strlen, 1),
+					{
+						if (cli->is_local) {
+							CHECK_FCT( rgw_clients_get_origin(cli, &oh_str, &oh_strlen, &or_str, &or_strlen) );
+							goto diameter;
+						}
+						
+						TRACE_DEBUG(INFO, "Unable to use resolved client name '%s' as DiameterIdentity: %s", buf, strerror(ret));
+						return ret;
+					} );
+				oh_free = 1;
 				
-				oh_str = &buf[0];
 				or_str = strchr(oh_str, '.');
 				if (or_str) {
 					or_str ++; /* move after the first dot */
 					if (*or_str == '\0')
 						or_str = NULL; /* Discard this realm, we will use the local realm later */
+					else
+						or_strlen = oh_strlen - (or_str - oh_str);
 				}
 			}
 		} else {
 			/* The attribute matches the source address, just use this in origin-host */
-			CHECK_FCT( rgw_clients_get_origin(cli, &oh_str, &or_str) );
+			CHECK_FCT( rgw_clients_get_origin(cli, &oh_str, &oh_strlen, &or_str, &or_strlen) );
 		}
 		
 		goto diameter; /* we ignore the nas_id in that case */
@@ -667,7 +702,7 @@
 	/* We don't have a NAS-IP*-Address attribute if we are here */
 	if (cli->is_local) {
 		/* Simple: we use our own configuration */
-		CHECK_FCT( rgw_clients_get_origin(cli, &oh_str, &or_str) );
+		CHECK_FCT( rgw_clients_get_origin(cli, &oh_str, &oh_strlen, &or_str, &or_strlen) );
 		goto diameter;
 	}
 	
@@ -696,14 +731,14 @@
 		*/
 		
 		/* first, check if the nas_id is the fqdn of the peer or a known alias */
-		if ((cli->fqdn_len == (nas_id->length - sizeof(struct radius_attr_hdr))) 
-		&& (!strncasecmp((char *)(nas_id + 1), cli->fqdn, nas_id->length - sizeof(struct radius_attr_hdr)))) {
+		if (!fd_os_almostcasecmp(nas_id + 1, nas_id_len, 
+						cli->fqdn, cli->fqdn_len)) {
 			TRACE_DEBUG(FULL, "NAS-Identifier contains the fqdn of the client");
 			found = 1;
 		} else {
 			for (idx = 0; idx < cli->aliases_nb; idx++) {
-				if (((nas_id->length - sizeof(struct radius_attr_hdr)) == strlen(cli->aliases[idx])) 
-				&& (!strncasecmp((char *)(nas_id + 1), cli->aliases[idx], nas_id->length - sizeof(struct radius_attr_hdr)))) {
+				if (!fd_os_cmp(nas_id + 1, nas_id_len, 
+						cli->aliases[idx].name, cli->aliases[idx].len)) {
 					TRACE_DEBUG(FULL, "NAS-Identifier valid value found in the cache");
 					found = 1;
 					break;
@@ -713,14 +748,14 @@
 		
 		if (found) {
 			/* The NAS-Identifier matches the source IP */
-			CHECK_FCT( rgw_clients_get_origin(cli, &oh_str, &or_str) );
+			CHECK_FCT( rgw_clients_get_origin(cli, &oh_str, &oh_strlen, &or_str, &or_strlen) );
 
 			goto diameter;
 		}
 		
 		/* Attempt DNS resolution of the identifier */
-		ASSERT( nas_id->length - sizeof(struct radius_attr_hdr) < sizeof(buf) );
-		memcpy(buf, nas_id + 1, nas_id->length - sizeof(struct radius_attr_hdr));
+		ASSERT( nas_id_len < sizeof(buf) );
+		memcpy(buf, nas_id + 1, nas_id_len);
 		buf[nas_id->length - sizeof(struct radius_attr_hdr)] = '\0';
 		
 		/* Now check if this alias is valid for this peer */
@@ -744,44 +779,56 @@
 			if (!found) {
 				if (cli->type == RGW_CLI_NAS) {
 					TRACE_DEBUG(INFO, "The NAS-Identifier value '%.*s' resolves to a different IP than the client's, discarding the message. \nConfigure this client as a Proxy if this message should be valid.", 
-						nas_id->length - sizeof(struct radius_attr_hdr), nas_id + 1);
+						nas_id_len, nas_id + 1);
 					return EINVAL;
 				} else {
 					/* This identifier matches a different IP, assume it is a proxied message */
 					if (!cli->is_local) {
 						rr_str = cli->fqdn;
+						rr_strlen = cli->fqdn_len;
 					}
 					oh_str = &buf[0]; /* The canonname resolved */
+					CHECK_FCT_DO( ret = fd_os_validate_DiameterIdentity(&oh_str, &oh_strlen, 1),
+						{
+							TRACE_DEBUG(INFO, "Unable to use resolved client name '%s' as DiameterIdentity: %s", buf, strerror(ret));
+							return ret;
+						} );
+					oh_free = 1;
 					or_str = strchr(oh_str, '.');
 					if (or_str) {
 						or_str ++; /* move after the first dot */
 						if (*or_str == '\0')
 							or_str = NULL; /* Discard this realm, we will use the local realm later */
+						else
+							or_strlen = oh_strlen - (or_str - oh_str);
 					}
 				}
 			} else {
 				/* It is a valid alias, save it */
-				CHECK_MALLOC( cli->aliases = realloc(cli->aliases, (cli->aliases_nb + 1) * sizeof(char *)) );
-				CHECK_MALLOC( cli->aliases[cli->aliases_nb + 1] = malloc( 1 + nas_id->length - sizeof(struct radius_attr_hdr) ));
-				memcpy( cli->aliases[cli->aliases_nb + 1], nas_id + 1, nas_id->length - sizeof(struct radius_attr_hdr));
-				*(cli->aliases[cli->aliases_nb + 1] + nas_id->length - sizeof(struct radius_attr_hdr)) = '\0';
+				CHECK_MALLOC( cli->aliases = realloc(cli->aliases, (cli->aliases_nb + 1) * sizeof(cli->aliases[0])) );
+				
+				CHECK_MALLOC( cli->aliases[cli->aliases_nb + 1].name = os0dup(nas_id + 1, nas_id_len ) );
+				cli->aliases[cli->aliases_nb + 1].len = nas_id_len;
+
 				cli->aliases_nb ++;
-				TRACE_DEBUG(FULL, "Saved valid alias for client: '%s' -> '%s'", cli->aliases[cli->aliases_nb + 1], cli->fqdn);
-				CHECK_FCT( rgw_clients_get_origin(cli, &oh_str, &or_str) );
+				TRACE_DEBUG(FULL, "Saved valid alias for client: '%.*s' -> '%s'", nas_id_len, nas_id + 1, cli->fqdn);
+				CHECK_FCT( rgw_clients_get_origin(cli, &oh_str, &oh_strlen, &or_str, &or_strlen) );
 			}
 		} else {
 			/* Error resolving the name */
 			TRACE_DEBUG(INFO, "NAS-Identifier '%s' cannot be resolved: %s. Ignoring...", buf, gai_strerror(ret));
 			/* Assume this is a valid identifier for the client */
-			CHECK_FCT( rgw_clients_get_origin(cli, &oh_str, &or_str) );
+			CHECK_FCT( rgw_clients_get_origin(cli, &oh_str, &oh_strlen, &or_str, &or_strlen) );
 		}
 	}
 	
 	/* Now, let's create the empty Diameter message with Origin-Host, -Realm, and Route-Record if needed. */
 diameter:
 	ASSERT(oh_str); /* If it is not defined here, there is a bug... */
-	if (!or_str)
+	if (!or_str) {
 		or_str = fd_g_config->cnf_diamrlm; /* Use local realm in that case */
+		or_strlen = fd_g_config->cnf_diamrlm_len;
+	}
 	
 	/* Create an empty Diameter message so that extensions can store their AVPs */
 	CHECK_FCT(  fd_msg_new ( NULL, MSGFL_ALLOC_ETEID, diam )  );
@@ -790,7 +837,7 @@
 	CHECK_FCT( fd_msg_avp_new ( cache_orig_host, 0, &avp ) );
 	memset(&avp_val, 0, sizeof(avp_val));
 	avp_val.os.data = (unsigned char *)oh_str;
-	avp_val.os.len = strlen(oh_str);
+	avp_val.os.len = oh_strlen;
 	CHECK_FCT( fd_msg_avp_setvalue ( avp, &avp_val ) );
 	CHECK_FCT( fd_msg_avp_add ( *diam, MSG_BRW_LAST_CHILD, avp) );
 	
@@ -798,7 +845,7 @@
 	CHECK_FCT( fd_msg_avp_new ( cache_orig_realm, 0, &avp ) );
 	memset(&avp_val, 0, sizeof(avp_val));
 	avp_val.os.data = (unsigned char *)or_str;
-	avp_val.os.len = strlen(or_str);
+	avp_val.os.len = or_strlen;
 	CHECK_FCT( fd_msg_avp_setvalue ( avp, &avp_val ) );
 	CHECK_FCT( fd_msg_avp_add ( *diam, MSG_BRW_LAST_CHILD, avp) );
 	
@@ -806,28 +853,37 @@
 		CHECK_FCT( fd_msg_avp_new ( cache_route_record, 0, &avp ) );
 		memset(&avp_val, 0, sizeof(avp_val));
 		avp_val.os.data = (unsigned char *)rr_str;
-		avp_val.os.len = strlen(rr_str);
+		avp_val.os.len = rr_strlen;
 		CHECK_FCT( fd_msg_avp_setvalue ( avp, &avp_val ) );
 		CHECK_FCT( fd_msg_avp_add ( *diam, MSG_BRW_LAST_CHILD, avp) );
 	}
 	
+	if (oh_free)
+		free(oh_str);
+	
 	/* Done! */
 	return 0;
 }
 
-int rgw_clients_get_origin(struct rgw_client *cli, char **fqdn, char **realm)
+int rgw_clients_get_origin(struct rgw_client *cli, DiamId_t *fqdn, size_t *fqdnlen, DiamId_t *realm, size_t *realmlen)
 {
-	TRACE_ENTRY("%p %p %p", cli, fqdn, realm);
-	CHECK_PARAMS(cli && fqdn);
+	TRACE_ENTRY("%p %p %p %p %p", cli, fqdn, fqdnlen, realm, realmlen);
+	CHECK_PARAMS(cli && fqdn && fqdnlen);
 	
 	if (cli->is_local) {
 		*fqdn = fd_g_config->cnf_diamid;
+		*fqdnlen = fd_g_config->cnf_diamid_len;
 		if (realm)
 			*realm= fd_g_config->cnf_diamrlm;
+		if (realmlen)
+			*realmlen= fd_g_config->cnf_diamrlm_len;
 	} else {
 		*fqdn = cli->fqdn;
+		*fqdnlen = cli->fqdn_len;
 		if (realm)
 			*realm= cli->realm;
+		if (realmlen)
+			*realmlen= cli->realm_len;
 	}
 		
 	return 0;
--- a/extensions/app_radgw/rgw_common.h	Mon Jan 31 17:22:21 2011 +0900
+++ b/extensions/app_radgw/rgw_common.h	Wed Feb 09 15:26:58 2011 +0900
@@ -58,7 +58,7 @@
 
 int    rgw_clients_getkey(struct rgw_client * cli, unsigned char **key, size_t *key_len);
 char * rgw_clients_id(struct rgw_client *cli);
-int    rgw_clients_get_origin(struct rgw_client *cli, char **fqdn, char **realm);
+int    rgw_clients_get_origin(struct rgw_client *cli, DiamId_t *fqdn, size_t *fqdnlen, DiamId_t *realm, size_t *realmlen);
 
 /* Each plugin must provide the following structure. */
 extern struct rgw_api {
--- a/extensions/app_radgw/rgw_servers.c	Mon Jan 31 17:22:21 2011 +0900
+++ b/extensions/app_radgw/rgw_servers.c	Wed Feb 09 15:26:58 2011 +0900
@@ -147,8 +147,9 @@
 		/* parse the message, loop if message is invalid */
 		CHECK_FCT_DO( rgw_msg_parse(&buf[0], len, &msg), 
 			{
-				char * cliname = NULL;
-				CHECK_FCT_DO( rgw_clients_get_origin(nas_info, &cliname, NULL), );
+				DiamId_t cliname = NULL;
+				size_t clisz;
+				CHECK_FCT_DO( rgw_clients_get_origin(nas_info, &cliname, &clisz, NULL, NULL), );
 				TRACE_DEBUG(INFO, "Discarding invalid RADIUS message from '%s'", cliname);
 				rgw_clients_dispose(&nas_info);
 				continue; 
--- a/extensions/app_radgw/rgwx_acct.c	Mon Jan 31 17:22:21 2011 +0900
+++ b/extensions/app_radgw/rgwx_acct.c	Wed Feb 09 15:26:58 2011 +0900
@@ -305,9 +305,9 @@
 	
 	const char * prefix = "Diameter/";
 	size_t pref_len;
-	uint8_t * si = NULL;
+	os0_t si = NULL;
 	size_t si_len = 0;
-	uint8_t * un = NULL;
+	os0_t un = NULL;
 	size_t un_len = 0;
 	
 	TRACE_ENTRY("%p %p %p %p %p %p", cs, session, rad_req, rad_ans, diam_fw, cli);
@@ -486,7 +486,7 @@
 	
 	/* Create the Session-Id AVP if needed */
 	if (!*session) {
-		CHECK_FCT( fd_sess_fromsid ( (char *)/* cast should be removed later */si, si_len, session, NULL) );
+		CHECK_FCT( fd_sess_fromsid ( si, si_len, session, NULL) );
 		
 		TRACE_DEBUG(FULL, "[acct.rgwx] Translating new accounting message for session '%.*s'...", si_len, si);
 		
@@ -662,13 +662,13 @@
 					char buf[32];
 					char * attr_val, *auth_val;
 					attr_val = (char *)(attr + 1);
-					auth_val = attr_val + strlen(CLASS_AAI_PREFIX);
-					if (	(attr->length > sizeof(struct radius_attr_hdr) + strlen(CLASS_AAI_PREFIX)  )
-						&& (attr->length < sizeof(struct radius_attr_hdr) + strlen(CLASS_AAI_PREFIX) + sizeof(buf))
-						&& ! strncmp(attr_val, CLASS_AAI_PREFIX, strlen(CLASS_AAI_PREFIX))) {
+					auth_val = attr_val + CONSTSTRLEN(CLASS_AAI_PREFIX);
+					if (	(attr->length > sizeof(struct radius_attr_hdr) + CONSTSTRLEN(CLASS_AAI_PREFIX)  )
+						&& (attr->length < sizeof(struct radius_attr_hdr) + CONSTSTRLEN(CLASS_AAI_PREFIX) + sizeof(buf))
+						&& ! strncmp(attr_val, CLASS_AAI_PREFIX, CONSTSTRLEN(CLASS_AAI_PREFIX))) {
 					
 						memset(buf, 0, sizeof(buf));
-						memcpy(buf, auth_val, attr->length - sizeof(struct radius_attr_hdr) - strlen(CLASS_AAI_PREFIX));
+						memcpy(buf, auth_val, attr->length - sizeof(struct radius_attr_hdr) - CONSTSTRLEN(CLASS_AAI_PREFIX));
 						if (sscanf(buf, "%u", &auth_appl) == 1) {
 							TRACE_DEBUG(ANNOYING, "Found Class attribute with '%s' prefix (attr #%d), AAI:%u.", CLASS_AAI_PREFIX, idx, auth_appl);
 						}
@@ -1300,8 +1300,10 @@
 	if (st->send_str) {
 		struct msg * str = NULL;
 		struct msg_hdr * hdr = NULL;
-		char * fqdn;
-		char * realm;
+		DiamId_t fqdn;
+		size_t fqdn_len;
+		DiamId_t realm;
+		size_t realm_len;
 		union avp_value avp_val;
 		
 		/* Create a new STR message */
@@ -1322,13 +1324,13 @@
 		CHECK_FCT( fd_msg_avp_add ( str, MSG_BRW_LAST_CHILD, avp) );
 
 		/* Get information on the NAS */
-		CHECK_FCT( rgw_clients_get_origin(cli, &fqdn, &realm) );
+		CHECK_FCT( rgw_clients_get_origin(cli, &fqdn, &fqdn_len, &realm, &realm_len) );
 
 		/* Add the Origin-Host as next AVP */
 		CHECK_FCT( fd_msg_avp_new ( cs->dict.Origin_Host, 0, &avp ) );
 		memset(&avp_val, 0, sizeof(avp_val));
 		avp_val.os.data = (unsigned char *)fqdn;
-		avp_val.os.len = strlen(fqdn);
+		avp_val.os.len = fqdn_len;
 		CHECK_FCT( fd_msg_avp_setvalue ( avp, &avp_val ) );
 		CHECK_FCT( fd_msg_avp_add ( str, MSG_BRW_LAST_CHILD, avp) );
 
@@ -1336,7 +1338,7 @@
 		CHECK_FCT( fd_msg_avp_new ( cs->dict.Origin_Realm, 0, &avp ) );
 		memset(&avp_val, 0, sizeof(avp_val));
 		avp_val.os.data = (unsigned char *)realm;
-		avp_val.os.len = strlen(realm);
+		avp_val.os.len = realm_len;
 		CHECK_FCT( fd_msg_avp_setvalue ( avp, &avp_val ) );
 		CHECK_FCT( fd_msg_avp_add ( str, MSG_BRW_LAST_CHILD, avp) );
 		
--- a/extensions/app_radgw/rgwx_auth.c	Mon Jan 31 17:22:21 2011 +0900
+++ b/extensions/app_radgw/rgwx_auth.c	Wed Feb 09 15:26:58 2011 +0900
@@ -238,13 +238,13 @@
 	int got_empty_eap = 0;
 	const char * prefix = "Diameter/";
 	size_t pref_len;
-	uint8_t * dh = NULL;
+	os0_t dh = NULL;
 	size_t dh_len = 0;
-	uint8_t * dr = NULL;
+	os0_t dr = NULL;
 	size_t dr_len = 0;
-	uint8_t * si = NULL;
+	os0_t si = NULL;
 	size_t si_len = 0;
-	uint8_t * un = NULL;
+	os0_t un = NULL;
 	size_t un_len = 0;
 	size_t nattr_used = 0;
 	struct avp ** avp_tun = NULL, *avp = NULL;
@@ -292,7 +292,7 @@
         	 to the NAS-IP-Address attribute (preferred if available),
         	 and/or to the NAS-Identifier attribute.  (Note that the RADIUS
         	 NAS-Identifier is not required to be an FQDN.)
-		     -> done in rgw_msg_create_base.
+		     -> done in rgw_clients_create_origin.
 
 	      -  The response MUST have an Origin-AAA-Protocol AVP added,
         	 indicating the protocol of origin of the message.
@@ -452,27 +452,30 @@
 	
 	/* Create the session if it is not already done */
 	if (*session == NULL) {
-		char * sess_str = NULL;
+		os0_t sess_str = NULL;
+		size_t sess_strlen;
 		
 		if (si_len) {
 			/* We already have the Session-Id, just use it */
-			CHECK_FCT( fd_sess_fromsid ( (char *) /* this cast will be removed later */ si, si_len, session, NULL) );
+			CHECK_FCT( fd_sess_fromsid ( si, si_len, session, NULL) );
 		} else {
 			/* Create a new Session-Id string */
 			
-			char * fqdn;
-			char * realm;
+			DiamId_t fqdn;
+			size_t fqdnlen;
+			DiamId_t realm;
+			size_t realmlen;
 			
 			/* Get information on the RADIUS client */
-			CHECK_FCT( rgw_clients_get_origin(cli, &fqdn, &realm) );
+			CHECK_FCT( rgw_clients_get_origin(cli, &fqdn, &fqdnlen, &realm, &realmlen) );
 			
 			/* If we have a user name, create the new session with it */
 			if (un) {
 				int len;
-				/* If not found, create a new Session-Id. The format is: {fqdn;hi32;lo32;username;diamid} */
+				/* If not found, create a new Session-Id. Our format is: {fqdn;hi32;lo32;username;diamid} */
 				CHECK_MALLOC( sess_str = malloc(un_len + 1 /* ';' */ + fd_g_config->cnf_diamid_len + 1 /* '\0' */) );
-				len = sprintf(sess_str, "%.*s;%s", (int)un_len, un, fd_g_config->cnf_diamid);
-				CHECK_FCT( fd_sess_new(session, fqdn, sess_str, len) );
+				len = sprintf((char *)sess_str, "%.*s;%s", (int)un_len, un, fd_g_config->cnf_diamid);
+				CHECK_FCT( fd_sess_new(session, fqdn, fqdnlen, sess_str, len) );
 				free(sess_str);
 			} else {
 				/* We don't have enough information to create the Session-Id, the RADIUS message is probably invalid */
@@ -482,14 +485,14 @@
 		}
 		
 		/* Now, add the Session-Id AVP at beginning of Diameter message */
-		CHECK_FCT( fd_sess_getsid(*session, &sess_str) );
+		CHECK_FCT( fd_sess_getsid(*session, &sess_str, &sess_strlen) );
 		
 		TRACE_DEBUG(FULL, "[auth.rgwx] Translating new message for session '%s'...", sess_str);
 		
 		/* Add the Session-Id AVP as first AVP */
 		CHECK_FCT( fd_msg_avp_new ( cs->dict.Session_Id, 0, &avp ) );
-		value.os.data = (unsigned char *)sess_str;
-		value.os.len = strlen(sess_str);
+		value.os.data = sess_str;
+		value.os.len = sess_strlen;
 		CHECK_FCT( fd_msg_avp_setvalue ( avp, &value ) );
 		CHECK_FCT( fd_msg_avp_add ( *diam_fw, MSG_BRW_FIRST_CHILD, avp) );
 	}
@@ -563,18 +566,18 @@
 			
 			/* This macro converts a RADIUS attribute to a Diameter AVP of type OctetString */
 			#define CONV2DIAM_STR( _dictobj_ )	\
-				CHECK_PARAMS( attr->length >= 2 );						\
+				CHECK_PARAMS( attr->length >= sizeof(struct radius_attr_hdr) );			\
 				/* Create the AVP with the specified dictionary model */			\
 				CHECK_FCT( fd_msg_avp_new ( cs->dict._dictobj_, 0, &avp ) );			\
-				value.os.len = attr->length - 2;						\
-				value.os.data = (unsigned char *)(attr + 1);					\
+				value.os.len = attr->length - sizeof(struct radius_attr_hdr);			\
+				value.os.data = (os0_t)(attr + 1);						\
 				CHECK_FCT( fd_msg_avp_setvalue ( avp, &value ) );				\
 				/* Add the AVP in the Diameter message. */					\
 				CHECK_FCT( fd_msg_avp_add ( *diam_fw, MSG_BRW_LAST_CHILD, avp) );		\
 				
 			/* Same thing, for scalar AVPs of 32 bits */
 			#define CONV2DIAM_32B( _dictobj_ )	\
-				CHECK_PARAMS( attr->length == 6 );						\
+				CHECK_PARAMS( attr->length == sizeof(struct radius_attr_hdr)+sizeof(uint32_t) );\
 				CHECK_FCT( fd_msg_avp_new ( cs->dict._dictobj_, 0, &avp ) );			\
 				{										\
 					uint8_t * v = (uint8_t *)(attr + 1);					\
@@ -588,7 +591,7 @@
 				
 			/* And the 64b version */
 			#define CONV2DIAM_64B( _dictobj_ )	\
-				CHECK_PARAMS( attr->length == 10);						\
+				CHECK_PARAMS( attr->length == sizeof(struct radius_attr_hdr)+sizeof(uint64_t) );\
 				CHECK_FCT( fd_msg_avp_new ( cs->dict._dictobj_, 0, &avp ) );			\
 				{										\
 					uint8_t * v = (uint8_t *)(attr + 1);					\
@@ -608,7 +611,7 @@
 			/*
 			      -  The Destination-Realm AVP is created from the information found
         			 in the RADIUS User-Name attribute.
-				 	-> done in rgw_msg_create_base
+				 	-> done in rgw_clients_create_origin
 			*/
 			case RADIUS_ATTR_USER_NAME:
 				CONV2DIAM_STR( User_Name );
--- a/extensions/app_radgw/rgwx_debug.c	Mon Jan 31 17:22:21 2011 +0900
+++ b/extensions/app_radgw/rgwx_debug.c	Wed Feb 09 15:26:58 2011 +0900
@@ -101,8 +101,9 @@
 	if (!session || ! *session) {
 		fd_log_debug(" Diameter session: NULL pointer\n");
 	} else {
-		char * str;
-		CHECK_FCT( fd_sess_getsid(*session, &str) );
+		os0_t str;
+		size_t str_len;
+		CHECK_FCT( fd_sess_getsid(*session, &str, &str_len) );
 
 		fd_log_debug(" Diameter session: %s\n", str);
 	}
--- a/extensions/app_radgw/rgwx_sip.c	Mon Jan 31 17:22:21 2011 +0900
+++ b/extensions/app_radgw/rgwx_sip.c	Wed Feb 09 15:26:58 2011 +0900
@@ -57,38 +57,38 @@
 
 /* This macro converts a RADIUS attribute to a Diameter AVP of type OctetString */
 #define CONV2DIAM_STR( _dictobj_ )	\
-	CHECK_PARAMS( attr->length >= 2 );						\
+	CHECK_PARAMS( attr->length >= sizeof(struct radius_attr_hdr) );			\
 	/* Create the AVP with the specified dictionary model */			\
 	CHECK_FCT( fd_msg_avp_new ( cs->dict._dictobj_, 0, &avp ) );			\
-	value.os.len = attr->length - 2;						\
-	value.os.data = (unsigned char *)(attr + 1);					\
+	value.os.len = attr->length - sizeof(struct radius_attr_hdr);			\
+	value.os.data = (os0_t)(attr + 1);						\
 	CHECK_FCT( fd_msg_avp_setvalue ( avp, &value ) );				\
 	/* Add the AVP in the Diameter message. */					\
 	CHECK_FCT( fd_msg_avp_add ( *diam_fw, MSG_BRW_LAST_CHILD, avp) );		\
 
 #define CONV2DIAM_STR_AUTH( _dictobj_ )	\
-	CHECK_PARAMS( attr->length >= 2 );						\
+	CHECK_PARAMS( attr->length >= sizeof(struct radius_attr_hdr) );			\
 	/* Create the AVP with the specified dictionary model */			\
 	CHECK_FCT( fd_msg_avp_new ( cs->dict._dictobj_, 0, &avp ) );			\
-	value.os.len = attr->length - 2;						\
-	value.os.data = (unsigned char *)(attr + 1);					\
+	value.os.len = attr->length - sizeof(struct radius_attr_hdr);			\
+	value.os.data = (os0_t)(attr + 1);						\
 	CHECK_FCT( fd_msg_avp_setvalue ( avp, &value ) );				\
 	/* Add the AVP in the Diameter message. */					\
-	CHECK_FCT( fd_msg_avp_add ( auth, MSG_BRW_LAST_CHILD, avp) );		\
+	CHECK_FCT( fd_msg_avp_add ( auth, MSG_BRW_LAST_CHILD, avp) );			\
 		
 /* Same thing, for scalar AVPs of 32 bits */
 #define CONV2DIAM_32B( _dictobj_ )	\
-	CHECK_PARAMS( attr->length == 6 );						\
+	CHECK_PARAMS( attr->length == sizeof(struct radius_attr_hdr)+sizeof(uint32_t) );\
 	CHECK_FCT( fd_msg_avp_new ( cs->dict._dictobj_, 0, &avp ) );			\
 	{										\
 		uint8_t * v = (uint8_t *)(attr + 1);					\
 		value.u32  = (v[0] << 24)						\
-		           | (v[1] << 16)						\
-		           | (v[2] <<  8)						\
-		           |  v[3] ;							\
+			   | (v[1] << 16)						\
+			   | (v[2] <<  8)						\
+			   |  v[3] ;							\
 	}										\
 	CHECK_FCT( fd_msg_avp_setvalue ( avp, &value ) );				\
-	CHECK_FCT( fd_msg_avp_add ( *diam_fw, MSG_BRW_LAST_CHILD, avp) );
+	CHECK_FCT( fd_msg_avp_add ( *diam_fw, MSG_BRW_LAST_CHILD, avp) );		\
 				
 				
 				
@@ -144,26 +144,24 @@
 struct noncechain
 {
 	struct fd_list chain;
-    char * sid;
+    os0_t sid;
     size_t sidlen;
-    char * nonce;
+    os0_t nonce;
     size_t noncelen;
     
 };
 
-static int nonce_add_element(char * nonce, size_t noncelen,char * sid, size_t sidlen, struct rgwp_config * state)
+static int nonce_add_element(os0_t nonce, size_t noncelen, os0_t sid, size_t sidlen, struct rgwp_config * state)
 {
 	CHECK_PARAMS(nonce && state && sid && sidlen && noncelen);
 	
 	noncechain *newelt;
 	CHECK_MALLOC(newelt=malloc(sizeof(noncechain)));
 	
-	CHECK_MALLOC(newelt->nonce=malloc(noncelen));
-	memcpy(newelt->nonce,nonce,noncelen);
+	CHECK_MALLOC(newelt->nonce= os0dup(nonce, noncelen));
 	newelt->noncelen=noncelen;
 	
-	CHECK_MALLOC(newelt->sid=malloc(sidlen));
-	memcpy(newelt->sid,sid,sidlen);
+	CHECK_MALLOC(newelt->sid=os0dup(sid, sidlen));
 	newelt->sidlen=sidlen;
 	
 	fd_list_init(&newelt->chain,NULL);
@@ -197,21 +195,21 @@
 }
 */
 //Retrieve sid from nonce
-static char * nonce_get_sid(char * nonce, size_t noncelen, size_t * sidlen, struct rgwp_config *state)
+static os0_t nonce_get_sid(os0_t nonce, size_t noncelen, size_t * sidlen, struct rgwp_config *state)
 {
 	struct fd_list * li;
-	char *sid=NULL;
+	os0_t sid=NULL;
 	
 	CHECK_PARAMS_DO(nonce && state && noncelen && sidlen, return NULL);
 	*sidlen=0;
 	
-	//**Start mutex
+	// **Start mutex
 	CHECK_POSIX_DO(pthread_mutex_lock(&state->nonce_mutex),); 
 	for(li=state->listnonce.next;li!=&state->listnonce;li=li->next)
 	{
 		noncechain *temp=(noncechain *)li;
 		
-		if(temp->noncelen==noncelen && strncmp(temp->nonce,nonce, noncelen)==0)
+		if (!fd_os_cmp(temp->nonce, temp->noncelen, nonce, noncelen))
 		{
 			fd_list_unlink (li);
 			sid=temp->sid;
@@ -223,13 +221,13 @@
 		
 	}
 	CHECK_POSIX_DO(pthread_mutex_unlock(&state->nonce_mutex),); 
-	//***Stop mutex
+	// ***Stop mutex
 	return sid;
 }
 
 static void nonce_deletelistnonce(struct rgwp_config *state)
 {
-	//**Start mutex
+	// **Start mutex
 	CHECK_POSIX_DO(pthread_mutex_lock(&state->nonce_mutex),); 
 	while(!(FD_IS_LIST_EMPTY(&state->listnonce)) )
 	{
@@ -242,7 +240,7 @@
 		
 	}
 	CHECK_POSIX_DO(pthread_mutex_unlock(&state->nonce_mutex),); 
-	//***Stop mutex
+	// ***Stop mutex
 }
 
 /* Initialize the plugin */
@@ -337,8 +335,9 @@
 	int got_Dcnonce = 0;
 	int got_Dresponse = 0;
 	int got_Dalgorithm = 0;
-	char * sid = NULL;
-	char * un=NULL;
+	os0_t sid = NULL;
+	size_t sidlen;
+	os0_t un=NULL;
 	size_t  un_len;
 	size_t nattr_used = 0;
 	struct avp *auth_data=NULL, *auth=NULL, *avp = NULL;
@@ -351,7 +350,7 @@
 	//We check that session is not already filled
 	if(*session)
 	{
-		TRACE_DEBUG(INFO,"We are not supposed to receive a session in radSIP plugin.");
+		TRACE_DEBUG(INFO,"INTERNAL ERROR: We are not supposed to receive a session in radSIP plugin.");
 		return EINVAL;
 	}
 	
@@ -370,7 +369,7 @@
 				if (attr->length>sizeof(struct radius_attr_hdr)) 
 				{
 					TRACE_DEBUG(ANNOYING, "Found a User-Name attribute: '%.*s'", attr->length- sizeof(struct radius_attr_hdr), (char *)(attr+1));
-					un = (char *)(attr + 1);
+					un = (os0_t)(attr + 1);
 					un_len =attr->length - sizeof(struct radius_attr_hdr);
 				}
 			break;
@@ -395,9 +394,7 @@
 			case RADIUS_ATTR_DIGEST_NONCE:
 				got_Dnonce = 1;
 				
-				size_t sidlen;
-				
-				sid=nonce_get_sid((char *)(attr+1),attr->length-2,&sidlen,cs);
+				sid=nonce_get_sid((os0_t)(attr+1), attr->length - sizeof(struct radius_attr_hdr), &sidlen, cs);
 				if(!sid)
 				{
 					TRACE_DEBUG(INFO,"We haven't found the session.'");
@@ -405,7 +402,7 @@
 				}
 				CHECK_FCT(fd_sess_fromsid (sid, sidlen, session, NULL));
 				free(sid);
-								
+							
 				
 			break;
 			case RADIUS_ATTR_DIGEST_CNONCE:
@@ -431,20 +428,21 @@
 	/* Create the session if it is not already done */
 	if (!*session) {
 		
-		char * fqdn;
-		char * realm;
+		DiamId_t fqdn;
+		size_t fqdn_len;
+		DiamId_t realm;
+		size_t realm_len;
 		
 		
 		
 		
 		/* Get information on the RADIUS client */
-		CHECK_FCT( rgw_clients_get_origin(cli, &fqdn, &realm) );
+		CHECK_FCT( rgw_clients_get_origin(cli, &fqdn, &fqdn_len, &realm, &realm_len) );
 		
-		int len;
 		/* Create a new Session-Id. The format is: {fqdn;hi32;lo32;username;diamid} */
 		CHECK_MALLOC( sid = malloc(un_len + 1 /* ';' */ + fd_g_config->cnf_diamid_len + 1 /* '\0' */) );
-		len = sprintf(sid, "%.*s;%s", (int)un_len, un, fd_g_config->cnf_diamid);
-		CHECK_FCT( fd_sess_new(session, fqdn, sid, len) );
+		sidlen = sprintf((char *)sid, "%.*s;%s", (int)un_len, un, fd_g_config->cnf_diamid);
+		CHECK_FCT( fd_sess_new(session, fqdn, fqdn_len, sid, sidlen) );
 		free(sid);
 	}
 		
@@ -452,21 +450,21 @@
 	CHECK_FCT( fd_msg_avp_new ( cs->dict.Destination_Realm, 0, &avp ) );
 	
 	int i = 0;
-	if (un) {
-		/* Is there an '@' in the user name? We don't care for decorated NAI here */
-		for (i = un_len - 2; i > 0; i--) {
-			if (un[i] == '@') {
-				i++;
-				break;
-			}
+	
+	/* Is there an '@' in the user name? We don't care for decorated NAI here */
+	for (i = un_len - 2; i > 0; i--) {
+		if (un[i] == '@') {
+			i++;
+			break;
 		}
 	}
+
 	if (i == 0) {
 		/* Not found in the User-Name => we use the local domain of this gateway */
-		value.os.data = (unsigned char *)fd_g_config->cnf_diamrlm;
+		value.os.data = (os0_t)fd_g_config->cnf_diamrlm;
 		value.os.len  = fd_g_config->cnf_diamrlm_len;
 	} else {
-		value.os.data = (unsigned char *)(un + i);
+		value.os.data = un + i;
 		value.os.len  = un_len - i;
 	}
 	
@@ -474,14 +472,14 @@
 	CHECK_FCT( fd_msg_avp_add ( *diam_fw, MSG_BRW_FIRST_CHILD, avp) );
 	
 	/* Now, add the Session-Id AVP at beginning of Diameter message */
-	CHECK_FCT( fd_sess_getsid(*session, &sid) );
+	CHECK_FCT( fd_sess_getsid(*session, &sid, &sidlen) );
 	
 	TRACE_DEBUG(FULL, "[sip.rgwx] Translating new message for session '%s'...", sid);
 	
 	/* Add the Session-Id AVP as first AVP */
 	CHECK_FCT( fd_msg_avp_new ( cs->dict.Session_Id, 0, &avp ) );
-	value.os.data = (unsigned char *)sid;
-	value.os.len = strlen(sid);
+	value.os.data = sid;
+	value.os.len = sidlen;
 	CHECK_FCT( fd_msg_avp_setvalue ( avp, &value ) );
 	CHECK_FCT( fd_msg_avp_add ( *diam_fw, MSG_BRW_FIRST_CHILD, avp) );
 	
@@ -551,7 +549,6 @@
 		CHECK_FCT( fd_msg_avp_new ( cs->dict.SIP_Authorization, 0, &auth ) );
 		CHECK_FCT( fd_msg_avp_add ( auth_data, MSG_BRW_LAST_CHILD, auth) );
 	}
-	char * temp=NULL,*sipuri=NULL;
 
 	for (idx = 0; idx < rad_req->attr_used; idx++) 
 	{
@@ -596,31 +593,37 @@
 			if(!got_Drealm)
 			{
 				//We extract Realm from Digest_URI
-				char *realm=NULL;
+				DiamId_t realm=NULL;
+				size_t realm_len;
+				os0_t temp;
+				
+				temp = (os0_t)(attr + 1);
+				
+				for (i=attr->length - sizeof(struct radius_attr_hdr) - 1; i>=0; i--) {
+					if (temp[i] == '@') {
+						realm = (DiamId_t)temp + i + 1;
+						CHECK_FCT_DO( fd_os_validate_DiameterIdentity(&realm, &realm_len, 1),
+							realm = NULL );
+						break;
+					}
+				}
 			
-				CHECK_MALLOC(temp=malloc(attr->length -1));
-				strncpy(temp, (char *)(attr + 1), attr->length -2);
-				temp[attr->length-2] = '\0';
-			
-				realm = strtok( (char *)(temp), "@" );
-				realm = strtok( NULL, "@" );
-				free(temp);
-				temp=NULL;
 				if(realm!=NULL)
 				{
 					CHECK_FCT( fd_msg_avp_new ( cs->dict.Digest_Realm, 0, &avp ) );
-					value.os.data=(unsigned char *)realm;
-					value.os.len=strlen(realm);
+					value.os.data=(os0_t)realm;
+					value.os.len=realm_len;
 					CHECK_FCT( fd_msg_avp_setvalue ( avp, &value ) );
 					CHECK_FCT( fd_msg_avp_add ( auth, MSG_BRW_LAST_CHILD, avp) );
 					
 					//We add SIP-Server-URI AVP because SIP server is registrar (through gateway)
 					CHECK_FCT( fd_msg_avp_new ( cs->dict.SIP_Server_URI, 0, &avp ) );
-					value.os.data=(unsigned char *)realm;
-					value.os.len=strlen(realm);
+					value.os.data=(os0_t)realm;
+					value.os.len=realm_len;
 					CHECK_FCT( fd_msg_avp_setvalue ( avp, &value ) );
 					CHECK_FCT( fd_msg_avp_add ( *diam_fw, MSG_BRW_LAST_CHILD, avp) );
 					
+					free(realm);
 				}
 				else
 				{
@@ -640,20 +643,17 @@
 			
 			//We add SIP-Server-URI AVP because SIP server is registrar (through gateway)
 			CHECK_FCT( fd_msg_avp_new ( cs->dict.SIP_Server_URI, 0, &avp ) );
-			
-			
-			CHECK_MALLOC(temp=malloc(attr->length -1));
-			strncpy(temp, (char *)(attr + 1), attr->length -2);
-			
+			os0_t temp;
+			#define SIP_PREFIX	"sip:"
+			size_t temp_len = attr->length - sizeof(struct radius_attr_hdr) + CONSTSTRLEN(SIP_PREFIX) + 1;
+			CHECK_MALLOC( temp = malloc(temp_len) );
+			temp_len = snprintf((char *)temp, temp_len, SIP_PREFIX "%.*s", attr->length - sizeof(struct radius_attr_hdr), (char *)(attr + 1));
 			
-			CHECK_MALLOC(sipuri=malloc(attr->length +3));
-			strcpy(sipuri,"sip:");
-			strcat(sipuri,(const char *)temp);
-			value.os.data=(unsigned char *)sipuri;
-			value.os.len=attr->length +2;
+			value.os.data=temp;
+			value.os.len=temp_len;
 			
 			free(temp);
-			temp=NULL;
+			
 			CHECK_FCT( fd_msg_avp_setvalue ( avp, &value ) );
 			CHECK_FCT( fd_msg_avp_add ( *diam_fw, MSG_BRW_LAST_CHILD, avp) );
 			break;
@@ -688,11 +688,12 @@
 			if(!got_Dalgorithm)
 			{
 				//[Note 3] If Digest-Algorithm is missing, 'MD5' is assumed.
+				#define DIGEST_ALGO_MD5	"MD5"
 										
 				CHECK_FCT( fd_msg_avp_new ( cs->dict.Digest_Algorithm, 0, &avp ) );			
 										
-				value.os.data = (unsigned char *)"MD5";
-				value.os.len = strlen((const char *)value.os.data);
+				value.os.data = (os0_t)DIGEST_ALGO_MD5;
+				value.os.len = CONSTSTRLEN(DIGEST_ALGO_MD5) - 1;
 				CHECK_FCT( fd_msg_avp_setvalue ( avp, &value ) );					
 				CHECK_FCT( fd_msg_avp_add ( auth, MSG_BRW_LAST_CHILD, avp) );	
 				got_Dalgorithm=1;	
@@ -830,12 +831,11 @@
 					CONV2RAD_STR(DIAM_ATTR_DIGEST_NONCE, ahdr->avp_value->os.data, ahdr->avp_value->os.len, 0);
 					/* Retrieve the request identified which was stored in the session */
 					if (session) {
-						char *sid=NULL;
+						os0_t sid=NULL;
 						size_t sidlen;
-						fd_sess_getsid (session, &sid );
-						sidlen=strlen(sid);
+						fd_sess_getsid (session, &sid, &sidlen );
 						
-						nonce_add_element((char *)ahdr->avp_value->os.data,ahdr->avp_value->os.len, sid,sidlen, cs);
+						nonce_add_element(ahdr->avp_value->os.data, ahdr->avp_value->os.len, sid, sidlen, cs);
 					}
 					break;
 				case DIAM_ATTR_DIGEST_REALM:
--- a/extensions/app_sip/registrationtermination.c	Mon Jan 31 17:22:21 2011 +0900
+++ b/extensions/app_sip/registrationtermination.c	Wed Feb 09 15:26:58 2011 +0900
@@ -146,12 +146,14 @@
 	
 	// Create a new session 
 	{
-		CHECK_FCT( fd_sess_new( &sess, fd_g_config->cnf_diamid, "app_sip", 7 ));
-		char * sid;
-		CHECK_FCT( fd_sess_getsid ( sess, &sid ));
+		#define APP_SIP_SID_OPT  "app_sip"
+		CHECK_FCT( fd_sess_new( &sess, fd_g_config->cnf_diamid, fd_g_config->cnf_diamid_len, (os0_t)APP_SIP_SID_OPT, CONSTSTRLEN(APP_SIP_SID_OPT) ));
+		os0_t sid;
+		size_t sidlen;
+		CHECK_FCT( fd_sess_getsid ( sess, &sid, &sidlen ));
 		CHECK_FCT( fd_msg_avp_new ( sip_dict.Session_Id, 0, &avp ));
-		value.os.data = (uint8_t *)sid;
-		value.os.len  = strlen(sid);
+		value.os.data = sid;
+		value.os.len  = sidlen;
 		CHECK_FCT( fd_msg_avp_setvalue( avp, &value ));
 		CHECK_FCT( fd_msg_avp_add( message, MSG_BRW_FIRST_CHILD, avp ));
 	}
--- a/extensions/dbg_interactive/dbg_interactive.i	Mon Jan 31 17:22:21 2011 +0900
+++ b/extensions/dbg_interactive/dbg_interactive.i	Wed Feb 09 15:26:58 2011 +0900
@@ -151,7 +151,7 @@
 %}
 
 /* Overwrite declaration to apply typemaps */
-int fd_sess_fromsid ( char * STRING, size_t LENGTH, struct session ** OUTPUT, int * BOOL_OUT);
+int fd_sess_fromsid ( unsigned char * STRING, size_t LENGTH, struct session ** OUTPUT, int * BOOL_OUT);
 
 
 /*********************************************************
--- a/extensions/dbg_interactive/messages.i	Mon Jan 31 17:22:21 2011 +0900
+++ b/extensions/dbg_interactive/messages.i	Wed Feb 09 15:26:58 2011 +0900
@@ -340,14 +340,14 @@
 	}
 	
 	/* Get the source */
-	char *source() {
-		char * s = NULL;
-		int ret = fd_msg_source_get($self, &s);
+	%cstring_output_allocate_size(char ** outid, size_t * outlen, /* do not free */);
+	void source(char ** outid, size_t * outlen) {
+		int ret = fd_msg_source_get($self, outid, outlen);
 		if (ret != 0) {
 			DI_ERROR(ret, NULL, NULL);
-			return NULL;
+			return;
 		}
-		return s;
+		return;
 	}
 	
 	/* Get the session */
--- a/extensions/dbg_interactive/peers.i	Mon Jan 31 17:22:21 2011 +0900
+++ b/extensions/dbg_interactive/peers.i	Wed Feb 09 15:26:58 2011 +0900
@@ -96,9 +96,9 @@
 }
 
 %inline %{
-static struct peer_hdr * peer_search(char *diamid) {
+static struct peer_hdr * peer_search(char *STRING, size_t LENGTH) {
 	struct peer_hdr *r = NULL;
-	int ret = fd_peer_getbyid( diamid, &r );
+	int ret = fd_peer_getbyid( STRING, LENGTH, 0, &r );
 	if (ret) {
 		DI_ERROR(ret, NULL, NULL);
 		return NULL;
--- a/extensions/dbg_interactive/routing.i	Mon Jan 31 17:22:21 2011 +0900
+++ b/extensions/dbg_interactive/routing.i	Wed Feb 09 15:26:58 2011 +0900
@@ -54,17 +54,19 @@
 		struct rt_data *r = self;
 		fd_rtd_free(&r);
 	}
-	void add(char * peerid, char * realm) {
-		int ret = fd_rtd_candidate_add($self, peerid, realm);
+	%apply (char *STRING, int LENGTH) { (char * peerid, size_t peeridlen) };
+	%apply (char *STRING, int LENGTH) { (char * realm, size_t realmlen) };
+	void add(char * peerid, size_t peeridlen, char * realm, size_t realmlen) {
+		int ret = fd_rtd_candidate_add($self, peerid, peeridlen, realm, realmlen);
 		if (ret != 0) {
 			DI_ERROR(ret, NULL, NULL);
 		}
 	}
 	void remove(char * STRING, size_t LENGTH) {
-		fd_rtd_candidate_del($self, STRING, LENGTH);
+		fd_rtd_candidate_del($self, (os0_t)STRING, LENGTH);
 	}
-	void error(char * dest, char * STRING, size_t LENGTH, uint32_t rcode) {
-		int ret =  fd_rtd_error_add($self, dest, (uint8_t *)STRING, LENGTH, rcode);
+	void error(char * peerid, size_t peeridlen, char * STRING, size_t LENGTH, uint32_t rcode) {
+		int ret =  fd_rtd_error_add($self, peerid, peeridlen, (os0_t)STRING, LENGTH, rcode);
 		if (ret != 0) {
 			DI_ERROR(ret, NULL, NULL);
 		}
--- a/extensions/dbg_interactive/sessions.i	Mon Jan 31 17:22:21 2011 +0900
+++ b/extensions/dbg_interactive/sessions.i	Wed Feb 09 15:26:58 2011 +0900
@@ -39,7 +39,7 @@
 
 %{
 /* call it (might be called from a different thread than the interpreter, when session times out) */
-static void call_the_python_cleanup_callback(session_state * state, char * sid, void * cb) {
+static void call_the_python_cleanup_callback(session_state * state, os0_t sid, void * cb) {
 	PyObject *result;
 	if (!cb) {
 		fd_log_debug("Internal error: missing callback object!\n");
@@ -99,7 +99,7 @@
 	session() {
 		int ret;
 		struct session * s = NULL;
-		ret = fd_sess_new(&s, fd_g_config->cnf_diamid, "dbg_interactive", sizeof("dbg_interactive"));
+		ret = fd_sess_new(&s, fd_g_config->cnf_diamid, fd_g_config->cnf_diamid_len, (os0_t)"dbg_interactive", CONSTSTRLEN("dbg_interactive"));
 		if (ret != 0) {
 			DI_ERROR(ret, NULL, NULL);
 			return NULL;
@@ -109,7 +109,7 @@
 	session(char * diamid, char * STRING, size_t LENGTH) {
 		int ret;
 		struct session * s = NULL;
-		ret = fd_sess_new(&s, diamid, STRING, LENGTH);
+		ret = fd_sess_new(&s, diamid, 0, (os0_t)STRING, LENGTH);
 		if (ret != 0) {
 			DI_ERROR(ret, NULL, NULL);
 			return NULL;
@@ -119,7 +119,7 @@
 	session(char * STRING, size_t LENGTH) {
 		int ret, n;
 		struct session * s = NULL;
-		ret = fd_sess_fromsid(STRING, LENGTH, &s, &n);
+		ret = fd_sess_fromsid((os0_t)STRING, LENGTH, &s, &n);
 		if (ret != 0) {
 			DI_ERROR(ret, NULL, NULL);
 			return NULL;
@@ -143,15 +143,16 @@
 		}
 		return;
 	}
-	char * getsid() {
+	
+	%cstring_output_allocate_size(char ** outsid, size_t * sidlen, /* do not free */);
+	void getsid(char ** outsid, size_t * sidlen) {
 		int ret;
-		char * sid = NULL;
-		ret = fd_sess_getsid( $self, &sid);
+		ret = fd_sess_getsid( $self, (void *)outsid, sidlen);
 		if (ret != 0) {
 			DI_ERROR(ret, NULL, NULL);
-			return NULL;
+			return;
 		}
-		return sid;
+		return;
 	}
 	void settimeout(long seconds) {
 		struct timespec timeout;
--- a/extensions/test_app/ta_bench.c	Mon Jan 31 17:22:21 2011 +0900
+++ b/extensions/test_app/ta_bench.c	Wed Feb 09 15:26:58 2011 +0900
@@ -129,7 +129,8 @@
 	CHECK_FCT_DO( fd_msg_new( ta_cmd_r, MSGFL_ALLOC_ETEID, &req ), goto out );
 	
 	/* Create a new session */
-	CHECK_FCT_DO( fd_sess_new( &sess, fd_g_config->cnf_diamid, "app_test", 8 ), goto out );
+	#define TEST_APP_SID_OPT  "app_testb"
+	CHECK_FCT_DO( fd_sess_new( &sess, fd_g_config->cnf_diamid, fd_g_config->cnf_diamid_len, (os0_t)TEST_APP_SID_OPT, CONSTSTRLEN(TEST_APP_SID_OPT) ), goto out );
 	
 	/* Create the random value to store with the session */
 	mi = malloc(sizeof(struct ta_mess_info));
@@ -144,11 +145,12 @@
 	
 	/* Session-Id */
 	{
-		char * sid;
-		CHECK_FCT_DO( fd_sess_getsid ( sess, &sid ), goto out );
+		os0_t sid;
+		size_t sidlen;
+		CHECK_FCT_DO( fd_sess_getsid ( sess, &sid, &sidlen ), goto out );
 		CHECK_FCT_DO( fd_msg_avp_new ( ta_sess_id, 0, &avp ), goto out );
-		val.os.data = (uint8_t *)sid;
-		val.os.len  = strlen(sid);
+		val.os.data = sid;
+		val.os.len  = sidlen;
 		CHECK_FCT_DO( fd_msg_avp_setvalue( avp, &val ), goto out );
 		CHECK_FCT_DO( fd_msg_avp_add( req, MSG_BRW_FIRST_CHILD, avp ), goto out );
 		
--- a/extensions/test_app/ta_cli.c	Mon Jan 31 17:22:21 2011 +0900
+++ b/extensions/test_app/ta_cli.c	Wed Feb 09 15:26:58 2011 +0900
@@ -149,7 +149,8 @@
 	CHECK_FCT_DO( fd_msg_new( ta_cmd_r, MSGFL_ALLOC_ETEID, &req ), goto out );
 	
 	/* Create a new session */
-	CHECK_FCT_DO( fd_sess_new( &sess, fd_g_config->cnf_diamid, "app_test", 8 ), goto out );
+	#define TEST_APP_SID_OPT  "app_test"
+	CHECK_FCT_DO( fd_sess_new( &sess, fd_g_config->cnf_diamid, fd_g_config->cnf_diamid_len, (os0_t)TEST_APP_SID_OPT, CONSTSTRLEN(TEST_APP_SID_OPT) ), goto out );
 	
 	/* Create the random value to store with the session */
 	mi = malloc(sizeof(struct ta_mess_info));
@@ -164,11 +165,12 @@
 	
 	/* Session-Id */
 	{
-		char * sid;
-		CHECK_FCT_DO( fd_sess_getsid ( sess, &sid ), goto out );
+		os0_t sid;
+		size_t sidlen;
+		CHECK_FCT_DO( fd_sess_getsid ( sess, &sid, &sidlen ), goto out );
 		CHECK_FCT_DO( fd_msg_avp_new ( ta_sess_id, 0, &avp ), goto out );
-		val.os.data = (uint8_t *)sid;
-		val.os.len  = strlen(sid);
+		val.os.data = sid;
+		val.os.len  = sidlen;
 		CHECK_FCT_DO( fd_msg_avp_setvalue( avp, &val ), goto out );
 		CHECK_FCT_DO( fd_msg_avp_add( req, MSG_BRW_FIRST_CHILD, avp ), goto out );
 		
--- a/extensions/test_netemul/tne_process.c	Mon Jan 31 17:22:21 2011 +0900
+++ b/extensions/test_netemul/tne_process.c	Wed Feb 09 15:26:58 2011 +0900
@@ -87,11 +87,12 @@
 		
 		/* Duplicate eventually, unless deactivated */
 		if (tne_conf.dupl_proba != 0.0) {
-			char * src;
+			DiamId_t src;
+			size_t srclen;
 			/* Pick a random value in [0, 1] */
 			double my_rand = drand48();
 			m = pi->chain.o;
-			CHECK_FCT( fd_msg_source_get(m, &src) );
+			CHECK_FCT( fd_msg_source_get(m, &src, &srclen) );
 			
 			while (my_rand < (double) tne_conf.dupl_proba) {
 				/* create the duplicate */
@@ -104,7 +105,7 @@
 				/* Duplicate the message */
 				CHECK_FCT( fd_msg_bufferize(m, &buf, &len) );
 				CHECK_FCT( fd_msg_parse_buffer(&buf, len, &nm) );
-				CHECK_FCT( fd_msg_source_set(nm, src, 0, NULL) );
+				CHECK_FCT( fd_msg_source_set(nm, src, srclen, 0, NULL) );
 				CHECK_FCT( fd_msg_hdr(nm, &nh) );
 				nh->msg_flags |= CMD_FLAG_RETRANSMIT; /* Add the 'T' flag */
 				TRACE_DEBUG(FULL, "[tne] Duplicated message %p as %p", m, nm);
--- a/extensions/test_sip/locationinfo.c	Mon Jan 31 17:22:21 2011 +0900
+++ b/extensions/test_sip/locationinfo.c	Wed Feb 09 15:26:58 2011 +0900
@@ -61,12 +61,13 @@
 		
 	// Create a new session 
 	{
-		CHECK_FCT( fd_sess_new( &sess, fd_g_config->cnf_diamid, "appsip", 6 ));
-		char * sid;
-		CHECK_FCT( fd_sess_getsid ( sess, &sid ));
+		CHECK_FCT( fd_sess_new( &sess, fd_g_config->cnf_diamid, fd_g_config->cnf_diamid_len, (os0_t)"appsip", 6 ));
+		os0_t sid;
+		size_t sidlen;
+		CHECK_FCT( fd_sess_getsid ( sess, &sid, &sidlen ));
 		CHECK_FCT( fd_msg_avp_new ( sip_dict.Session_Id, 0, &avp ));
-		value.os.data = (uint8_t *)sid;
-		value.os.len  = strlen(sid);
+		value.os.data = sid;
+		value.os.len  = sidlen;
 		CHECK_FCT( fd_msg_avp_setvalue( avp, &value ));
 		CHECK_FCT( fd_msg_avp_add( message, MSG_BRW_FIRST_CHILD, avp ));
 	}
--- a/extensions/test_sip/locationinfosl.c	Mon Jan 31 17:22:21 2011 +0900
+++ b/extensions/test_sip/locationinfosl.c	Wed Feb 09 15:26:58 2011 +0900
@@ -61,12 +61,13 @@
 		
 	// Create a new session 
 	{
-		CHECK_FCT( fd_sess_new( &sess, fd_g_config->cnf_diamid, "appsip", 6 ));
-		char * sid;
-		CHECK_FCT( fd_sess_getsid ( sess, &sid ));
+		CHECK_FCT( fd_sess_new( &sess, fd_g_config->cnf_diamid, fd_g_config->cnf_diamid_len, (os0_t)"appsip", 6 ));
+		os0_t sid;
+		size_t sidlen;
+		CHECK_FCT( fd_sess_getsid ( sess, &sid, &sidlen ));
 		CHECK_FCT( fd_msg_avp_new ( sip_dict.Session_Id, 0, &avp ));
-		value.os.data = (uint8_t *)sid;
-		value.os.len  = strlen(sid);
+		value.os.data = sid;
+		value.os.len  = sidlen;
 		CHECK_FCT( fd_msg_avp_setvalue( avp, &value ));
 		CHECK_FCT( fd_msg_avp_add( message, MSG_BRW_FIRST_CHILD, avp ));
 	}
--- a/extensions/test_sip/serverassignment.c	Mon Jan 31 17:22:21 2011 +0900
+++ b/extensions/test_sip/serverassignment.c	Wed Feb 09 15:26:58 2011 +0900
@@ -70,12 +70,13 @@
 	
 	// Create a new session 
 	{
-		CHECK_FCT( fd_sess_new( &sess, fd_g_config->cnf_diamid, "appsip", 6 ));
-		char * sid;
-		CHECK_FCT( fd_sess_getsid ( sess, &sid ));
+		CHECK_FCT( fd_sess_new( &sess, fd_g_config->cnf_diamid, fd_g_config->cnf_diamid_len, (os0_t)"appsip", 6 ));
+		os0_t sid;
+		size_t sidlen;
+		CHECK_FCT( fd_sess_getsid ( sess, &sid, &sidlen ));
 		CHECK_FCT( fd_msg_avp_new ( sip_dict.Session_Id, 0, &avp ));
-		value.os.data = (uint8_t *)sid;
-		value.os.len  = strlen(sid);
+		value.os.data = sid;
+		value.os.len  = sidlen;
 		CHECK_FCT( fd_msg_avp_setvalue( avp, &value ));
 		CHECK_FCT( fd_msg_avp_add( message, MSG_BRW_FIRST_CHILD, avp ));
 	}
--- a/extensions/test_sip/userauthorization.c	Mon Jan 31 17:22:21 2011 +0900
+++ b/extensions/test_sip/userauthorization.c	Wed Feb 09 15:26:58 2011 +0900
@@ -66,12 +66,13 @@
 	
 	// Create a new session 
 	{
-		CHECK_FCT( fd_sess_new( &sess, fd_g_config->cnf_diamid, "appsip", 6 ));
-		char * sid;
-		CHECK_FCT( fd_sess_getsid ( sess, &sid ));
+		CHECK_FCT( fd_sess_new( &sess, fd_g_config->cnf_diamid, fd_g_config->cnf_diamid_len, (os0_t)"appsip", 6 ));
+		os0_t sid;
+		size_t sidlen;
+		CHECK_FCT( fd_sess_getsid ( sess, &sid, &sidlen ));
 		CHECK_FCT( fd_msg_avp_new ( sip_dict.Session_Id, 0, &avp ));
-		value.os.data = (uint8_t *)sid;
-		value.os.len  = strlen(sid);
+		value.os.data = sid;
+		value.os.len  = sidlen;
 		CHECK_FCT( fd_msg_avp_setvalue( avp, &value ));
 		CHECK_FCT( fd_msg_avp_add( message, MSG_BRW_FIRST_CHILD, avp ));
 	}
--- a/include/freeDiameter/libfdcore.h	Mon Jan 31 17:22:21 2011 +0900
+++ b/include/freeDiameter/libfdcore.h	Wed Feb 09 15:26:58 2011 +0900
@@ -107,9 +107,9 @@
 	
 	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 */
+	DiamId_t  	 cnf_diamid;	/* Diameter Identity of the local peer (FQDN -- ASCII) */
+	size_t		 cnf_diamid_len;/* cached length of the previous string */
+	DiamId_t	 cnf_diamrlm;	/* Diameter realm of the local peer, default to realm part of cnf_diamid */
 	size_t		 cnf_diamrlm_len;/* length of the previous string */
 	
 	unsigned int	 cnf_timer_tc;	/* The value in seconds of the default Tc timer */
@@ -187,6 +187,10 @@
 	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 */
 	
+	/* Ordering issues with multistream & state machine. -- see top of p_psm.c for explanation */
+	STATE_OPEN_NEW, 	/* after CEA is sent, until a new message is received. Force ordering in this state */
+	STATE_CLOSING_GRACE,	/* after DPA is sent or received, give a short delay for messages in the pipe to be received. */
+	
 	/* 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
@@ -204,6 +208,8 @@
 	, "STATE_OPEN_HANDSHAKE"	\
 	, "STATE_SUSPECT"		\
 	, "STATE_REOPEN"		\
+	, "STATE_OPEN_NEW"		\
+	, "STATE_CLOSING_GRACE"		\
 	, "STATE_ZOMBIE"		\
 	};
 extern const char *peer_state_str[];
@@ -236,7 +242,8 @@
 /* Information about a remote peer */
 struct peer_info {
 	
-	char * 		pi_diamid;	/* UTF-8, \0 terminated. The Diameter Identity of the remote peer. */
+	DiamId_t	pi_diamid;	/* (supposedly) UTF-8, \0 terminated. The Diameter Identity of the remote peer. */
+	size_t		pi_diamidlen;	/* cached length of pi_diamid */
 	
 	struct {
 		struct {
@@ -249,7 +256,7 @@
 			
 		}		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. */
+		DiamId_t	pic_realm;	/* If configured, the daemon will check 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) */
@@ -262,13 +269,15 @@
 	
 	struct {
 		
-		enum peer_state	pir_state;	/* Current state of the peer in the state machine. fd_cpu_flush_cache() might be useful before reading. */
+		/* enum peer_state	pir_state; */ 
+		/* Since 1.1.0, read the state with fd_peer_getstate(peer). */
 		
-		char * 		pir_realm;	/* The received realm in CER/CEA. */
+		DiamId_t	pir_realm;	/* The received realm in CER/CEA. */
+		size_t		pir_realmlen;	/* length of the realm */
 		
 		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) */
+		os0_t		pir_prodname;	/* copy of 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) */
@@ -289,7 +298,7 @@
 
 
 struct peer_hdr {
-	struct fd_list	 chain;	/* List of all the peers, ordered by their Diameter Id */
+	struct fd_list	 chain;	/* Link into the list of all the peers, ordered by their Diameter Id (fd_os_cmp) */
 	struct peer_info info;	/* The public data */
 	
 	/* This header is followed by more data in the private peer structure definition */
@@ -338,7 +347,9 @@
  * FUNCTION:	fd_peer_getbyid
  *
  * PARAMETERS:
- *  diamid 	: A \0 terminated string.
+ *  diamid 	: an UTF8 string describing the diameter Id of the peer to seek
+ *  diamidlen	: length of the diamid
+ *  igncase	: perform an almost-case-insensitive search? (slower)
  *  peer	: The peer is stored here if it exists.
  *
  * DESCRIPTION: 
@@ -348,7 +359,22 @@
  *  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 );
+int fd_peer_getbyid( DiamId_t diamid, size_t diamidlen, int igncase, struct peer_hdr ** peer );
+
+/* 
+ * FUNCTION:	fd_peer_get_state
+ *
+ * PARAMETERS:
+ *  peer	: The peer which state to read
+ *
+ * DESCRIPTION: 
+ *   Returns the current state of the peer.
+ *
+ * RETURN VALUE:
+ *  -1  : peer is invalid
+ * >=0	: the state of the peer at the time of reading.
+ */
+int fd_peer_get_state(struct peer_hdr *peer);
 
 /*
  * FUNCTION:	fd_peer_validate_register
@@ -474,6 +500,10 @@
 /* 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 ); 
 
+/* Generate a new Session-Id and add it at the beginning of the message (opt is added at the end of the sid if provided) */
+int fd_msg_new_session( struct msg * msg, os0_t opt, size_t optlen );
+
+
 /* 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 );
 
--- a/include/freeDiameter/libfdproto.h	Mon Jan 31 17:22:21 2011 +0900
+++ b/include/freeDiameter/libfdproto.h	Wed Feb 09 15:26:58 2011 +0900
@@ -44,6 +44,7 @@
  * The file contains the following parts:
  *	DEBUG
  *	MACROS
+ *      OCTET STRINGS
  *	THREADS
  *	LISTS
  *	DICTIONARY
@@ -566,6 +567,64 @@
 #define BUFSIZ 96
 #endif /* BUFSIZ */
 
+/* This gives the length of a const string */
+#define CONSTSTRLEN( str ) (sizeof(str) - 1)
+
+
+/*============================================================*/
+/*                         BINARY STRINGS                     */
+/*============================================================*/
+
+/* Compute a hash value of a binary string. 
+The hash must remain local to this machine, there is no guarantee that same input
+will give same output on a different system (endianness) */
+uint32_t fd_os_hash ( uint8_t * string, size_t len );
+
+/* This type used for binary strings that contain no \0 except as their last character. 
+It means some string operations can be used on it. */
+typedef uint8_t * os0_t;
+
+/* Same as strdup but for os0_t strings */
+os0_t os0dup_int(os0_t s, size_t l);
+#define os0dup( _s, _l)  (void *)os0dup_int((os0_t)(_s), _l)
+
+/* Check that an octet string value can be used as os0_t */
+static __inline__ int fd_os_is_valid_os0(uint8_t * os, size_t oslen) {
+	/* The only situation where it is not valid is when it contains a \0 inside the octet string */
+	return (memchr(os, '\0', oslen) == NULL);
+}
+
+/* The following type denotes a verified DiameterIdentity value (that contains only pure letters, digits, hyphen, dot) */
+typedef char * DiamId_t;
+
+/* Maximum length of a hostname we accept */
+#ifndef HOST_NAME_MAX
+#define HOST_NAME_MAX 512
+#endif /* HOST_NAME_MAX */
+
+/* Check if a binary string contains a valid Diameter Identity value.
+  rfc3588 states explicitely that such a Diameter Identity consists only of ASCII characters. */
+int fd_os_is_valid_DiameterIdentity(uint8_t * os, size_t ossz);
+
+/* This checks a string is a valid DiameterIdentity and applies IDNA transformations otherwise (xn--...) */
+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) */ );
+
+/* Create an order relationship for binary strings (not needed to be \0 terminated). 
+   It does NOT mimic strings relationships so that it is more efficient. It is case sensitive.
+   (the strings are actually first ordered by their lengh, then by their bytes contents)
+   returns: -1 if os1 < os2;  +1 if os1 > os2;  0 if they are equal */
+int fd_os_cmp_int(os0_t os1, size_t os1sz, os0_t os2, size_t os2sz);
+#define fd_os_cmp(_o1, _l1, _o2, _l2)  fd_os_cmp_int((os0_t)(_o1), _l1, (os0_t)(_o2), _l2)
+
+/* A roughly case-insensitive variant, which actually only compares ASCII chars (0-127) in a case-insentitive maneer 
+  -- this should be OK with (old) DNS names. Not sure how it works with IDN...
+  -- it also clearly does not support locales where a lowercase letter uses more space than upper case, such as ß -> ss
+ It is slower than fd_os_cmp... 
+ This function should give the same order as fd_os_cmp, except when it finds 2 strings to be equal.
+ Note that the result is NOT the same as strcasecmp !!!
+ */
+int fd_os_almostcasecmp_int(uint8_t * os1, size_t os1sz, uint8_t * os2, size_t os2sz);
+#define fd_os_almostcasecmp(_o1, _l1, _o2, _l2)  fd_os_almostcasecmp_int((os0_t)(_o1), _l1, (os0_t)(_o2), _l2)
 
 
 /*============================================================*/
@@ -601,13 +660,6 @@
 	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 
@@ -672,14 +724,6 @@
 
 
 
-/*============================================================*/
-/*                          HASH                              */
-/*============================================================*/
-
-/* Compute a hash value of a string (session id, diameter id, ...) */
-uint32_t fd_hash ( char * string, size_t len );
-
-
 
 /*============================================================*/
 /*                        DICTIONARY                          */
@@ -801,13 +845,13 @@
 /* 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 */
+	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_BY_NAME,		/* "what" points to a char * */
 	VENDOR_OF_APPLICATION	/* "what" points to a struct dict_object containing an application (see bellow) */
 };
 
@@ -871,13 +915,13 @@
 /* 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 */
+	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_BY_NAME,		/* "what" points to a char * */
 	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) */
 };
@@ -946,7 +990,7 @@
 	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 */
+	}           os;		/* Storage for an octet string */
 	int32_t     i32;	/* integer 32 */
 	int64_t     i64;	/* integer 64 */
 	uint32_t    u32;	/* unsigned 32 */
@@ -1010,7 +1054,7 @@
 /* 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 */
+	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, FILE * fstr);	/* cb called by fd_msg_dump_one for this type of data (if != NULL), to dump the AVP value in fstr */
@@ -1018,7 +1062,7 @@
 
 /* The criteria for searching a type object in the dictionary */
 enum {
-	TYPE_BY_NAME = 30,		/* "what" points to a string */
+	TYPE_BY_NAME = 30,		/* "what" points to a char * */
 	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. */
 };
@@ -1067,7 +1111,7 @@
 
 /* Type to hold data of named constants for AVP */
 struct dict_enumval_data {
-	char 		*enum_name;	/* The name of this constant */
+	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. */
 };
 
@@ -1079,7 +1123,7 @@
 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;
+	char *			 type_name;
 	
 	/* Search criteria for the constant */
 	struct dict_enumval_data search; /* search.enum_value is used only if search.enum_name == NULL */
@@ -1182,7 +1226,7 @@
 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 */
+	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 */
@@ -1191,7 +1235,7 @@
 /* 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_NAME,		/* "what" points to a char *, 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... */
@@ -1201,7 +1245,7 @@
 struct dict_avp_request {
 	vendor_id_t	 avp_vendor;
 	avp_code_t	 avp_code;
-	char		*avp_name;
+	char *		 avp_name;
 };
 
 
@@ -1309,14 +1353,14 @@
 /* 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 */
+	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_NAME = 60,	/* "what" points to a char * */
 	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. */
@@ -1589,10 +1633,10 @@
  *  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 );
+int fd_sess_handler_create_internal ( struct session_handler ** handler, void (*cleanup)(session_state * state, os0_t 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) )
+	fd_sess_handler_create_internal( (_handler), (void (*)(session_state *, os0_t, void *))(_cleanup), (void *)(_opaque) )
 
 	
 /*
@@ -1620,8 +1664,9 @@
  *
  * 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.
+ *  diamid	  : a Diameter Identity, or NULL.
+ *  diamidlen	  : if diamid is \0-terminated, this can be 0. Otherwise, the length of diamid.
+ *  opt           : Additional string, or NULL. Usage is described bellow.
  *  optlen	  : if opt is \0-terminated, this can be 0. Otherwise, the length of opt.
  *
  * DESCRIPTION: 
@@ -1638,13 +1683,13 @@
  *  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 );
+int fd_sess_new ( struct session ** session, DiamId_t diamid, size_t diamidlen, uint8_t * opt, size_t optlen );
 
 /*
  * FUNCTION:	fd_sess_fromsid
  *
  * PARAMETERS:
- *  sid	  	: pointer to a string containing a Session-Id (UTF-8).
+ *  sid	  	: pointer to a string containing a Session-Id (should be 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.
@@ -1658,26 +1703,26 @@
  *  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);
+int fd_sess_fromsid ( uint8_t * 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.
+ *  sid	  	: On success, the location of the sid 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.
+ *  The returned sid is a \0-terminated binary string which might be UTF-8 (but there is no guarantee in the framework).
  *  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.
+ *  0      	: The sid & len parameters have been updated.
  *  EINVAL 	: A parameter is invalid.
  */
-int fd_sess_getsid ( struct session * session, char ** sid );
+int fd_sess_getsid ( struct session * session, os0_t * sid, size_t * sidlen );
 
 /*
  * FUNCTION:	fd_sess_settimeout
@@ -1710,8 +1755,10 @@
  *  session	: Pointer to a session object.
  *
  * DESCRIPTION: 
- *   Destroys a session an all associated data, if any.
+ *   Destroys all associated states of a session, if any.
  * Equivalent to a session timeout expired, but the effect is immediate.
+ * The session itself is marked as deleted, and will be freed when it is not referenced 
+ * by any message anymore.
  *
  * RETURN VALUE:
  *  0      	: The session no longer exists.
@@ -1726,10 +1773,11 @@
  *  session	: Pointer to a session object.
  *
  * DESCRIPTION: 
- *   Destroys the resources of a session, only if no session_state is associated with it.
+ *   Equivalent to fd_sess_destroy, only if no session_state is associated with the session.
+ *  Otherwise, this function has no effect (except that it sets *session to NULL).
  *
  * RETURN VALUE:
- *  0      	: The session no longer exists.
+ *  0      	: The session was reclaimed.
  *  EINVAL 	: A parameter is invalid.
  */
 int fd_sess_reclaim ( struct session ** session );
@@ -1801,27 +1849,29 @@
 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);
+/* Add a peer to the candidates list. */
+int  fd_rtd_candidate_add(struct rt_data * rtd, DiamId_t peerid, size_t peeridlen, DiamId_t realm, size_t realmlen);
 
-/* 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). The search is case-insensitive. */
+void fd_rtd_candidate_del(struct rt_data * rtd, uint8_t * id, size_t idsz);
 
 /* 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);
+int  fd_rtd_error_add(struct rt_data * rtd, DiamId_t sentto, size_t senttolen, 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) */
+	DiamId_t	diamid;	/* the diameter Id of the peer */
+	size_t		diamidlen; /* cached size of the diamid string */
+	DiamId_t	realm;	/* the diameter realm of the peer */
+	size_t		realmlen; /* cached size of realm */
 	int		score;	/* the current routing score for this peer, see fd_rt_out_register definition for details */
 };
 
-/* Reorder the list of peers */
+/* Reorder the list of peers by score */
 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. */
@@ -2238,7 +2288,7 @@
  *
  * PARAMETERS:
  *  msg		: A msg object.
- *  diamid	: The diameter id of the peer from which this message was received.
+ *  diamid,len	: 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)
  *
@@ -2251,8 +2301,8 @@
  *  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 );
+int fd_msg_source_set( struct msg * msg, DiamId_t diamid, size_t diamidlen, int add_rr, struct dictionary * dict );
+int fd_msg_source_get( struct msg * msg, DiamId_t *diamid, size_t * diamidlen );
 
 /*
  * FUNCTION:	fd_msg_eteid_get
@@ -2369,7 +2419,7 @@
  *  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 );
+int fd_msg_bufferize ( struct msg * msg, uint8_t ** buffer, size_t * len );
 
 /*
  * FUNCTION:	fd_msg_parse_buffer
@@ -2391,7 +2441,7 @@
  *  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 );
+int fd_msg_parse_buffer ( uint8_t ** buffer, size_t buflen, struct msg ** msg );
 
 /* Parsing Error Information structure */
 struct fd_pei {
@@ -2653,6 +2703,7 @@
  *  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
+ *  error_code	: Upon return with action == DISP_ACT_ERROR, contains the error (such as "DIAMETER_UNABLE_TO_COMPLY")
  *
  * DESCRIPTION: 
  *   Call all handlers registered for a given message.
@@ -2665,7 +2716,7 @@
  *  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 );
+int fd_msg_dispatch ( struct msg ** msg, struct session * session, enum disp_action *action, char ** error_code );
 
 
 
--- a/libfdcore/cnxctx.c	Mon Jan 31 17:22:21 2011 +0900
+++ b/libfdcore/cnxctx.c	Wed Feb 09 15:26:58 2011 +0900
@@ -50,8 +50,6 @@
  * 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:
@@ -157,8 +155,7 @@
 #ifdef DISABLE_SCTP
 	TRACE_DEBUG(INFO, "This function should never been called when SCTP is disabled...");
 	ASSERT(0);
-	CHECK_FCT_DO( ENOTSUP, );
-	return NULL;
+	CHECK_FCT_DO( ENOTSUP, return NULL);
 #else /* DISABLE_SCTP */
 	struct cnxctx * cnx = NULL;
 
@@ -248,18 +245,18 @@
 		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);
+		/* Numeric values for debug... */
+		snprintf(cli->cc_id, sizeof(cli->cc_id), "%s from [%s]:%s (%d<-%d)", 
+				IPPROTO_NAME(cli->cc_proto), addrbuf, portbuf, serv->cc_socket, cli->cc_socket);
 		
-		/* Name for log messages */
+		
+		/* ...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));
@@ -333,16 +330,16 @@
 		char portbuf[10];
 		int  rc;
 		
-		/* Numeric values for debug */
+		/* 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);
+		snprintf(cnx->cc_id, sizeof(cnx->cc_id), "TCP to [%s]:%s (%d)", addrbuf, portbuf, cnx->cc_socket);
 		
-		/* Name for log messages */
+		/* ...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));
@@ -355,10 +352,9 @@
 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...");
+	TRACE_DEBUG(INFO, "This function should never be called when SCTP is disabled...");
 	ASSERT(0);
-	CHECK_FCT_DO( ENOTSUP, );
-	return NULL;
+	CHECK_FCT_DO( ENOTSUP, return NULL);
 #else /* DISABLE_SCTP */
 	int sock = 0;
 	struct cnxctx * cnx = NULL;
@@ -415,16 +411,16 @@
 		char portbuf[10];
 		int  rc;
 		
-		/* Numeric values for debug */
+		/* 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);
+		snprintf(cnx->cc_id, sizeof(cnx->cc_id), "SCTP to [%s]:%s (%d)", addrbuf, portbuf, cnx->cc_socket);
 		
-		/* Name for log messages */
+		/* ...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));
@@ -453,20 +449,63 @@
 }
 
 /* Set the hostname to check during handshake */
-void fd_cnx_sethostname(struct cnxctx * conn, char * hn)
+void fd_cnx_sethostname(struct cnxctx * conn, DiamId_t hn)
 {
 	CHECK_PARAMS_DO( conn, return );
 	conn->cc_tls_para.cn = hn;
 }
 
+/* We share a lock with many threads but we hold it only very short time so it is OK */
+static pthread_mutex_t state_lock = PTHREAD_MUTEX_INITIALIZER;
+uint32_t fd_cnx_getstate(struct cnxctx * conn)
+{
+	uint32_t st;
+	CHECK_POSIX_DO( pthread_mutex_lock(&state_lock), { ASSERT(0); } );
+	st = conn->cc_state;
+	CHECK_POSIX_DO( pthread_mutex_unlock(&state_lock), { ASSERT(0); } );
+	return st;
+}
+int  fd_cnx_teststate(struct cnxctx * conn, uint32_t flag)
+{
+	uint32_t st;
+	CHECK_POSIX_DO( pthread_mutex_lock(&state_lock), { ASSERT(0); } );
+	st = conn->cc_state;
+	CHECK_POSIX_DO( pthread_mutex_unlock(&state_lock), { ASSERT(0); } );
+	return st & flag;
+}
+void fd_cnx_addstate(struct cnxctx * conn, uint32_t orstate)
+{
+	CHECK_POSIX_DO( pthread_mutex_lock(&state_lock), { ASSERT(0); } );
+	conn->cc_state |= orstate;
+	CHECK_POSIX_DO( pthread_mutex_unlock(&state_lock), { ASSERT(0); } );
+}
+void fd_cnx_setstate(struct cnxctx * conn, uint32_t abstate)
+{
+	CHECK_POSIX_DO( pthread_mutex_lock(&state_lock), { ASSERT(0); } );
+	conn->cc_state = abstate;
+	CHECK_POSIX_DO( pthread_mutex_unlock(&state_lock), { ASSERT(0); } );
+}
+
+
 /* 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;
+	return fd_cnx_teststate(conn, CC_STATUS_TLS);
 }
 
+/* Return true if the connection supports unordered delivery of messages */
+int fd_cnx_isMultichan(struct cnxctx * conn)
+{
+	CHECK_PARAMS_DO( conn, return 0 );
+	#ifdef DISABLE_SCTP
+	if (conn->cc_proto == IPPROTO_SCTP)
+		return (conn->cc_sctp_para.str_in > 1) || (conn->cc_sctp_para.str_out > 1);
+	#endif /* DISABLE_SCTP */
+	return 0;
+}
+
+
 /* 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)
 {
@@ -507,10 +546,11 @@
 	return conn->cc_remid;
 }
 
-/* Retrieve a list of all IP addresses of the local system from the kernel, using  */
+/* Retrieve a list of all IP addresses of the local system from the kernel, using getifaddrs */
 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) {
@@ -542,22 +582,21 @@
 	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);
+	TRACE_DEBUG(FULL, "Error flag set for socket %d (%s, %s)", conn->cc_socket, conn->cc_id, conn->cc_remid);
 	
 	/* Mark the error */
-	fd_cpu_flush_cache();
-	conn->cc_status |= CC_STATUS_ERROR;
+	fd_cnx_addstate(conn, 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 )))  {
+	if (!fd_cnx_teststate(conn, CC_STATUS_CLOSING | 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;
+		CHECK_FCT_DO( fd_event_send( fd_cnx_target_queue(conn), FDEVP_CNX_ERROR, 0, NULL), goto fatal);
+		fd_cnx_addstate(conn, CC_STATUS_SIGNALED);
 	}
-	fd_cpu_flush_cache();
 	return;
 fatal:
 	/* An unrecoverable error occurred, stop the daemon */
+	ASSERT(0);
 	CHECK_FCT_DO(fd_event_send(fd_g_config->cnf_main_ev, FDEV_TERMINATE, 0, NULL), );	
 }
 
@@ -582,8 +621,7 @@
 	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))
+		if (! fd_cnx_teststate(conn, CC_STATUS_CLOSING ))
 			goto again; /* don't care, just ignore */
 		if (!timedout) {
 			timedout ++; /* allow for one timeout while closing */
@@ -591,11 +629,11 @@
 		}
 	}
 	
-	CHECK_SYS_DO(ret, /* continue */);
-	
 	/* Mark the error */
-	if (ret <= 0)
+	if (ret <= 0) {
+		CHECK_SYS_DO(ret, /* continue, this is only used to log the error here */);
 		fd_cnx_markerror(conn);
+	}
 	
 	return ret;
 }
@@ -609,8 +647,7 @@
 	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))
+		if (! fd_cnx_teststate(conn, CC_STATUS_CLOSING ))
 			goto again; /* don't care, just ignore */
 		if (!timedout) {
 			timedout ++; /* allow for one timeout while closing */
@@ -642,8 +679,8 @@
 	}
 	
 	ASSERT( conn->cc_proto == IPPROTO_TCP );
-	ASSERT( ! (conn->cc_status & CC_STATUS_TLS) );
-	ASSERT( Target_Queue(conn) );
+	ASSERT( ! fd_cnx_teststate(conn, CC_STATUS_TLS ) );
+	ASSERT( fd_cnx_target_queue(conn) );
 	
 	/* Receive from a TCP connection: we have to rebuild the message boundaries */
 	do {
@@ -665,7 +702,7 @@
 		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> */
+		if ((header[0] != DIAMETER_VERSION)	/* defined in <libfdproto.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);
@@ -690,8 +727,7 @@
 		}
 		
 		/* 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? */);
+		CHECK_FCT_DO( fd_event_send( fd_cnx_target_queue(conn), FDEVP_CNX_MSG_RECV, length, newmsg), /* continue or destroy everything? */);
 		
 	} while (conn->cc_loop);
 	
@@ -725,12 +761,11 @@
 	}
 	
 	ASSERT( conn->cc_proto == IPPROTO_SCTP );
-	ASSERT( ! (conn->cc_status & CC_STATUS_TLS) );
-	ASSERT( Target_Queue(conn) );
+	ASSERT( ! fd_cnx_teststate(conn, CC_STATUS_TLS ) );
+	ASSERT( fd_cnx_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 );
+		CHECK_FCT_DO( fd_sctp_recvmeta(conn, NULL, &buf, &bufsz, &event), goto fatal );
 		if (event == FDEVP_CNX_ERROR) {
 			fd_cnx_markerror(conn);
 			goto out;
@@ -741,8 +776,7 @@
 			continue;
 		}
 		
-		fd_cpu_flush_cache();
-		CHECK_FCT_DO( fd_event_send( Target_Queue(conn), event, bufsz, buf), goto fatal );
+		CHECK_FCT_DO( fd_event_send( fd_cnx_target_queue(conn), event, bufsz, buf), goto fatal );
 		
 	} while (conn->cc_loop || (event != FDEVP_CNX_MSG_RECV));
 	
@@ -762,7 +796,7 @@
 {
 	TRACE_ENTRY("%p %i", conn, loop);
 	
-	CHECK_PARAMS( conn && Target_Queue(conn) && (!(conn->cc_status & CC_STATUS_TLS)) && (!conn->cc_loop));
+	CHECK_PARAMS( conn && fd_cnx_target_queue(conn) && (!fd_cnx_teststate(conn, 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 */);
@@ -802,8 +836,7 @@
 		{
 			switch (ret) {
 				case GNUTLS_E_REHANDSHAKE: 
-					fd_cpu_flush_cache();
-					if (!(conn->cc_status & CC_STATUS_CLOSING))
+					if (!fd_cnx_teststate(conn, CC_STATUS_CLOSING))
 						CHECK_GNUTLS_DO( ret = gnutls_handshake(session),
 							{
 								if (TRACE_BOOL(INFO)) {
@@ -814,8 +847,7 @@
 
 				case GNUTLS_E_AGAIN:
 				case GNUTLS_E_INTERRUPTED:
-					fd_cpu_flush_cache();
-					if (!(conn->cc_status & CC_STATUS_CLOSING))
+					if (!fd_cnx_teststate(conn, CC_STATUS_CLOSING))
 						goto again;
 					TRACE_DEBUG(FULL, "Connection is closing, so abord gnutls_record_recv now.");
 					break;
@@ -848,8 +880,7 @@
 		{
 			switch (ret) {
 				case GNUTLS_E_REHANDSHAKE: 
-					fd_cpu_flush_cache();
-					if (!(conn->cc_status & CC_STATUS_CLOSING))
+					if (!fd_cnx_teststate(conn, CC_STATUS_CLOSING))
 						CHECK_GNUTLS_DO( ret = gnutls_handshake(session),
 							{
 								if (TRACE_BOOL(INFO)) {
@@ -860,8 +891,7 @@
 
 				case GNUTLS_E_AGAIN:
 				case GNUTLS_E_INTERRUPTED:
-					fd_cpu_flush_cache();
-					if (!(conn->cc_status & CC_STATUS_CLOSING))
+					if (!fd_cnx_teststate(conn, CC_STATUS_CLOSING))
 						goto again;
 					TRACE_DEBUG(INFO, "Connection is closing, so abord gnutls_record_send now.");
 					break;
@@ -926,8 +956,7 @@
 		}
 		
 		/* 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), 
+		CHECK_FCT_DO( ret = fd_event_send( fd_cnx_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), );
@@ -955,8 +984,8 @@
 		fd_log_threadname ( buf );
 	}
 	
-	ASSERT( conn->cc_status & CC_STATUS_TLS );
-	ASSERT( Target_Queue(conn) );
+	ASSERT( fd_cnx_teststate(conn, CC_STATUS_TLS) );
+	ASSERT( fd_cnx_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 */);
@@ -1214,7 +1243,7 @@
 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) );
+	CHECK_PARAMS( conn && (!fd_cnx_teststate(conn, CC_STATUS_TLS)) && ( (mode == GNUTLS_CLIENT) || (mode == GNUTLS_SERVER) ) && (!conn->cc_loop) );
 
 	/* Save the mode */
 	conn->cc_tls_para.mode = mode;
@@ -1247,9 +1276,8 @@
 	}
 
 	/* 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;
-
+	fd_cnx_addstate(conn, CC_STATUS_TLS);
+	
 	/* Handshake master session */
 	{
 		int ret;
@@ -1298,7 +1326,7 @@
 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 );
+	CHECK_PARAMS( conn && fd_cnx_teststate(conn, 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 );
@@ -1359,16 +1387,29 @@
 	return EINVAL;
 }
 
+/* Where the events are sent */
+struct fifo * fd_cnx_target_queue(struct cnxctx * conn)
+{
+	struct fifo *q;
+	CHECK_POSIX_DO( pthread_mutex_lock(&state_lock), { ASSERT(0); } );
+	q = conn->cc_alt ?: conn->cc_incoming;
+	CHECK_POSIX_DO( pthread_mutex_unlock(&state_lock), { ASSERT(0); } );
+	return q;
+}
+
 /* Set an alternate FIFO list to send FDEVP_CNX_* events to */
 int fd_cnx_recv_setaltfifo(struct cnxctx * conn, struct fifo * alt_fifo)
 {
+	int ret;
 	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 ) );
+	CHECK_POSIX_DO( pthread_mutex_lock(&state_lock), { ASSERT(0); } );
+	CHECK_FCT_DO( ret = fd_fifo_move( conn->cc_incoming, alt_fifo, &conn->cc_alt ), );
+	CHECK_POSIX_DO( pthread_mutex_unlock(&state_lock), { ASSERT(0); } );
 	
-	return 0;
+	return ret;
 }
 
 /* Send function when no multi-stream is involved, or sending on stream #0 (send() always use stream 0)*/
@@ -1378,8 +1419,7 @@
 	size_t sent = 0;
 	TRACE_ENTRY("%p %p %zd", conn, buf, len);
 	do {
-		fd_cpu_flush_cache();
-		if (conn->cc_status & CC_STATUS_TLS) {
+		if (fd_cnx_teststate(conn, 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? */
@@ -1393,14 +1433,14 @@
 	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. */
+/* Send a message -- this is synchronous -- and we assume it's never called by several threads at the same time (on the same conn), 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);
+	CHECK_PARAMS(conn && (conn->cc_socket > 0) && (! fd_cnx_teststate(conn, 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);
+	TRACE_DEBUG(FULL, "Sending %zdb %sdata on connection %s", len, fd_cnx_teststate(conn, CC_STATUS_TLS) ? "TLS-protected ":"", conn->cc_id);
 	
 	switch (conn->cc_proto) {
 		case IPPROTO_TCP:
@@ -1409,32 +1449,6 @@
 		
 #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) );
@@ -1443,18 +1457,18 @@
 			
 				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)))  {
+				if ((conn->cc_sctp_para.str_out > 1) && ((!fd_cnx_teststate(conn, 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);
+					conn->cc_sctp_para.next %= (fd_cnx_teststate(conn, 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; } );
+					if (!fd_cnx_teststate(conn, CC_STATUS_TLS)) {
+						CHECK_FCT_DO( fd_sctp_sendstr(conn, conn->cc_sctp_para.next, buf, len), { fd_cnx_markerror(conn); return ENOTCONN; } );
 					} else {
 						/* push the record to the appropriate session */
 						ssize_t ret;
@@ -1495,24 +1509,23 @@
 	
 	CHECK_PARAMS_DO(conn, return);
 	
-	fd_cpu_flush_cache();
-	conn->cc_status |= CC_STATUS_CLOSING;
+	fd_cnx_addstate(conn, 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) {
+	if (fd_cnx_teststate(conn, CC_STATUS_TLS)) {
 #ifndef DISABLE_SCTP
 		if (conn->cc_sctp_para.pairs > 1) {
-			if (! (conn->cc_status & CC_STATUS_ERROR )) {
+			if (! fd_cnx_teststate(conn, 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 ) ) {
+			if (! fd_cnx_teststate(conn, CC_STATUS_ERROR ) ) {
 				/* and other stream pairs */
 				fd_sctps_bye(conn);
 			}
 
-			if (! (conn->cc_status & CC_STATUS_ERROR ) ) {
+			if (! fd_cnx_teststate(conn, CC_STATUS_ERROR ) ) {
 				/* Now wait for all decipher threads to terminate */
 				fd_sctps_waitthreadsterm(conn);
 			} else {
@@ -1532,13 +1545,13 @@
 
 		} else {
 #endif /* DISABLE_SCTP */
-		/* We are not using the sctps wrapper layer */
-			if (! (conn->cc_status & CC_STATUS_ERROR ) ) {
+		/* We are TLS, but not using the sctps wrapper layer */
+			if (! fd_cnx_teststate(conn, 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 ) ) {
+			if (! fd_cnx_teststate(conn, 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 */  );
@@ -1554,7 +1567,6 @@
 				GNUTLS_TRACE( gnutls_deinit(conn->cc_tls_para.session) );
 				conn->cc_tls_para.session = NULL;
 			}
-		
 #ifndef DISABLE_SCTP
 		}
 #endif /* DISABLE_SCTP */
--- a/libfdcore/cnxctx.h	Mon Jan 31 17:22:21 2011 +0900
+++ b/libfdcore/cnxctx.h	Wed Feb 09 15:26:58 2011 +0900
@@ -47,7 +47,8 @@
 
 	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 */
+	
+	uint32_t	cc_state;	/* True if the object is being destroyed: we don't send events anymore. access with fd_cnx_getstate() */
 	#define 	CC_STATUS_CLOSING	1
 	#define 	CC_STATUS_ERROR		2
 	#define 	CC_STATUS_SIGNALED	4
@@ -58,11 +59,10 @@
 	
 	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 */
+		DiamId_t 			 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;
@@ -83,6 +83,12 @@
 };
 
 void fd_cnx_markerror(struct cnxctx * conn);
+uint32_t fd_cnx_getstate(struct cnxctx * conn);
+int  fd_cnx_teststate(struct cnxctx * conn, uint32_t flag);
+void fd_cnx_addstate(struct cnxctx * conn, uint32_t orstate);
+void fd_cnx_setstate(struct cnxctx * conn, uint32_t abstate);
+struct fifo * fd_cnx_target_queue(struct cnxctx * conn);
+
 
 /* Socket */
 ssize_t fd_cnx_s_recv(struct cnxctx * conn, void *buffer, size_t length);
@@ -108,8 +114,8 @@
 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);
+int fd_sctp_sendstr(struct cnxctx * conn, uint16_t strid, uint8_t * buf, size_t len);
+int fd_sctp_recvmeta(struct cnxctx * conn, uint16_t * strid, uint8_t ** buf, size_t * len, int *event);
 
 /* TLS over SCTP (multi-stream) */
 struct sctps_ctx {
--- a/libfdcore/config.c	Mon Jan 31 17:22:21 2011 +0900
+++ b/libfdcore/config.c	Wed Feb 09 15:26:58 2011 +0900
@@ -145,6 +145,7 @@
 int fd_conf_parse()
 {
 	extern FILE * fddin;
+	char * orig = NULL;
 	
 	/* Attempt to find the configuration file */
 	if (!fd_g_config->cnf_file)
@@ -153,14 +154,22 @@
 	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 );
+		orig = fd_g_config->cnf_file;
+		CHECK_MALLOC( fd_g_config->cnf_file = malloc(strlen(orig) + strlen(DEFAULT_CONF_PATH) + 2) ); /* we will not free it, but not important */
+		sprintf( fd_g_config->cnf_file, DEFAULT_CONF_PATH "/%s", orig );
 		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));
+		if (orig) {
+			fprintf(stderr, "Unable to open configuration file for reading\n"
+					"Tried the following locations:\n"
+					" - %s\n"
+					" - %s\n"
+					"Error: %s\n", orig, fd_g_config->cnf_file, strerror(ret));
+		} else {
+			fprintf(stderr, "Unable to open '%s' for reading: %s\n", fd_g_config->cnf_file, strerror(ret));
+		}
 		return ret;
 	}
 	
@@ -177,11 +186,22 @@
 		return EINVAL;
 	}
 	
+	/* If the CA is not provided, let's use the same file (assuming self-signed certificate) */
+	if (! fd_g_config->cnf_sec_data.ca_file) {
+		CHECK_MALLOC( fd_g_config->cnf_sec_data.ca_file = strdup(fd_g_config->cnf_sec_data.cert_file) );
+		CHECK_GNUTLS_DO( fd_g_config->cnf_sec_data.ca_file_nr += gnutls_certificate_set_x509_trust_file( 
+					fd_g_config->cnf_sec_data.credentials,
+					fd_g_config->cnf_sec_data.ca_file,
+					GNUTLS_X509_FMT_PEM),
+				{ 
+					TRACE_DEBUG(INFO, "Unable to use the local certificate as trusted security anchor (CA), please provide a valid TLS_CA='...' directive.");
+					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;
@@ -201,13 +221,13 @@
 					buf, gai_strerror(ret));
 			return EINVAL;
 		}
-		CHECK_MALLOC( fd_g_config->cnf_diamid = strdup(info->ai_canonname) );
+		fd_g_config->cnf_diamid = info->ai_canonname;
+		CHECK_FCT( fd_os_validate_DiameterIdentity(&fd_g_config->cnf_diamid, &fd_g_config->cnf_diamid_len, 1) );
 		freeaddrinfo(info);
+	} else {
+		CHECK_FCT( fd_os_validate_DiameterIdentity(&fd_g_config->cnf_diamid, &fd_g_config->cnf_diamid_len, 0) );
 	}
 	
-	/* 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;
@@ -219,11 +239,13 @@
 					"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 = start + 1;
+		CHECK_FCT( fd_os_validate_DiameterIdentity(&fd_g_config->cnf_diamrlm, &fd_g_config->cnf_diamrlm_len, 1) );
+	} else {
+		CHECK_FCT( fd_os_validate_DiameterIdentity(&fd_g_config->cnf_diamrlm, &fd_g_config->cnf_diamrlm_len, 0) );
 	}
-	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) {
--- a/libfdcore/core.c	Mon Jan 31 17:22:21 2011 +0900
+++ b/libfdcore/core.c	Wed Feb 09 15:26:58 2011 +0900
@@ -44,7 +44,7 @@
 /* gcrypt functions to support posix threads */
 GCRY_THREAD_OPTION_PTHREAD_IMPL;
 
-/* Signal extensions when the framework is completly initialized */
+/* Signal extensions when the framework is completly initialized (they are waiting in fd_core_waitstartcomplete()) */
 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;
@@ -61,6 +61,8 @@
 
 /* Thread that process incoming events on the main queue -- and terminates the framework when requested */
 static pthread_t core_runner = (pthread_t)NULL;
+
+/* How the thread is terminated */
 enum core_mode {
 	CORE_MODE_EVENTS,
 	CORE_MODE_IMMEDIATE
--- a/libfdcore/extensions.c	Mon Jan 31 17:22:21 2011 +0900
+++ b/libfdcore/extensions.c	Wed Feb 09 15:26:58 2011 +0900
@@ -56,7 +56,7 @@
 {
 	struct fd_ext_info * new;
 	
-	TRACE_ENTRY("%p(%s) %p(%s)", filename, filename?filename:"", conffile, conffile?conffile:"");
+	TRACE_ENTRY("%p %p", filename, conffile);
 	
 	/* Check the filename is valid */
 	CHECK_PARAMS( filename );
@@ -105,7 +105,7 @@
 #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 */
+		/* We resolve symbols 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) {
--- a/libfdcore/fdcore-internal.h	Mon Jan 31 17:22:21 2011 +0900
+++ b/libfdcore/fdcore-internal.h	Wed Feb 09 15:26:58 2011 +0900
@@ -137,6 +137,10 @@
 	/* Origin of this peer object, for debug */
 	char		*p_dbgorig;
 	
+	/* State of the peer, and its lock */
+	enum peer_state	 p_state;
+	pthread_mutex_t  p_state_mtx;
+	
 	/* 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 */
@@ -146,6 +150,7 @@
 	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_localterm	: 1;	/* If the latest DPR/DPA was initiated from this side */
 		
 		unsigned pf_dw_pending 	: 1;	/* A DWR message was sent and not answered yet */
 		
@@ -192,6 +197,9 @@
 #define CHECK_PEER( _p ) \
 	(((_p) != NULL) && (((struct fd_peer *)(_p))->p_eyec == EYEC_PEER))
 
+#define fd_peer_getstate(peer)  fd_peer_get_state((struct peer_hdr *)(peer))
+
+
 /* Events codespace for struct fd_peer->p_events */
 enum {
 	/* Dump all info about this peer in the debug log */
@@ -307,6 +315,7 @@
 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);
+int fd_p_dp_newdelay(struct fd_peer * peer);
 
 /* 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;
@@ -326,11 +335,12 @@
 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);
+void		fd_cnx_sethostname(struct cnxctx * conn, DiamId_t 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_isMultichan(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);
@@ -342,6 +352,5 @@
 
 /* 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 */
--- a/libfdcore/fdd.y	Mon Jan 31 17:22:21 2011 +0900
+++ b/libfdcore/fdd.y	Wed Feb 09 15:26:58 2011 +0900
@@ -235,7 +235,7 @@
 
 appservthreads:		APPSERVTHREADS '=' INTEGER ';'
 			{
-				CHECK_PARAMS_DO( ($3 > 0) && ($3 < 1024),
+				CHECK_PARAMS_DO( ($3 > 0) && ($3 < 256),
 					{ yyerror (&yylloc, conf, "Invalid value"); YYERROR; } );
 				conf->cnf_dispthr = (uint16_t)$3;
 			}
--- a/libfdcore/messages.c	Mon Jan 31 17:22:21 2011 +0900
+++ b/libfdcore/messages.c	Wed Feb 09 15:26:58 2011 +0900
@@ -35,6 +35,7 @@
 
 #include "fdcore-internal.h"
 
+static struct dict_object * dict_avp_SI  = NULL; /* Session-Id */
 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 */
@@ -53,6 +54,7 @@
 	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, "Session-Id", 	&dict_avp_SI , ENOENT)  );
 	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)  );
@@ -88,7 +90,7 @@
 	
 	/* Set its value */
 	memset(&val, 0, sizeof(val));
-	val.os.data = (unsigned char *)fd_g_config->cnf_diamid;
+	val.os.data = (os0_t)fd_g_config->cnf_diamid;
 	val.os.len  = fd_g_config->cnf_diamid_len;
 	CHECK_FCT( fd_msg_avp_setvalue( avp_OH, &val ) );
 	
@@ -101,7 +103,7 @@
 	
 	/* Set its value */
 	memset(&val, 0, sizeof(val));
-	val.os.data = (unsigned char *)fd_g_config->cnf_diamrlm;
+	val.os.data = (os0_t)fd_g_config->cnf_diamrlm;
 	val.os.len  = fd_g_config->cnf_diamrlm_len;
 	CHECK_FCT( fd_msg_avp_setvalue( avp_OR, &val ) );
 	
@@ -124,6 +126,43 @@
 	return 0;
 }
 
+/* Create a new Session-Id and add at the beginning of the message. */
+int fd_msg_new_session( struct msg * msg, os0_t opt, size_t optlen )
+{
+	union avp_value val;
+	struct avp * avp  = NULL;
+	struct session * sess = NULL;
+	os0_t sid;
+	size_t sidlen;
+	
+	TRACE_ENTRY("%p %p %zd", msg, opt, optlen);
+	CHECK_PARAMS(  msg  );
+	
+	/* Check there is not already a session in the message */
+	CHECK_FCT( fd_msg_sess_get(fd_g_config->cnf_dict, msg, &sess, NULL) );
+	CHECK_PARAMS( sess == NULL );
+	
+	/* Ok, now create the session */
+	CHECK_FCT( fd_sess_new ( &sess, fd_g_config->cnf_diamid, fd_g_config->cnf_diamid_len, opt, optlen ) );
+	CHECK_FCT( fd_sess_getsid( sess, &sid, &sidlen) );
+	
+	/* Create an AVP to hold it */
+	CHECK_FCT( fd_msg_avp_new( dict_avp_SI, 0, &avp ) );
+	
+	/* Set its value */
+	memset(&val, 0, sizeof(val));
+	val.os.data = sid;
+	val.os.len  = sidlen;
+	CHECK_FCT( fd_msg_avp_setvalue( avp, &val ) );
+	
+	/* Add it to the message */
+	CHECK_FCT( fd_msg_avp_add( msg, MSG_BRW_FIRST_CHILD, avp ) );
+	
+	/* Done! */
+	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 )
 {
@@ -146,7 +185,7 @@
 		struct dict_enumval_request req;
 		memset(&req, 0, sizeof(struct dict_enumval_request));
 		
-		/* First, get the enumerated type of the Result-Code AVP */
+		/* First, get the enumerated type of the Result-Code AVP (this is fast, no need to cache the object) */
 		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 */
@@ -183,7 +222,7 @@
 
 		/* Set its value */
 		memset(&val, 0, sizeof(val));
-		val.os.data = (unsigned char *)fd_g_config->cnf_diamid;
+		val.os.data = (uint8_t *)fd_g_config->cnf_diamid;
 		val.os.len  = fd_g_config->cnf_diamid_len;
 		CHECK_FCT( fd_msg_avp_setvalue( avp_ERH, &val ) );
 
@@ -245,10 +284,10 @@
 		memset(&val, 0, sizeof(val));
 		
 		if (errormsg) {
-			val.os.data = (unsigned char *)errormsg;
+			val.os.data = (uint8_t *)errormsg;
 			val.os.len  = strlen(errormsg);
 		} else {
-			val.os.data = (unsigned char *)rescode;
+			val.os.data = (uint8_t *)rescode;
 			val.os.len  = strlen(rescode);
 		}
 		CHECK_FCT( fd_msg_avp_setvalue( avp_EM, &val ) );
@@ -310,7 +349,7 @@
 	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;
+		return ret; /* 0 or another error */
 	
 	TRACE_DEBUG(INFO, "A message does not comply to the dictionary and/or rules (%s)", pei.pei_errcode);
 	fd_msg_dump_walk(FULL, m);
--- a/libfdcore/p_ce.c	Mon Jan 31 17:22:21 2011 +0900
+++ b/libfdcore/p_ce.c	Wed Feb 09 15:26:58 2011 +0900
@@ -37,13 +37,6 @@
 
 /* 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)
 {
@@ -87,7 +80,7 @@
 	}
 }
 
-/* Election: compare the Diameter Ids, return true if the election is won */
+/* Election: compare the Diameter Ids by lexical order, 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);
@@ -244,26 +237,28 @@
 /* Remove any information saved from a previous CER/CEA exchange */
 static void cleanup_remote_CE_info(struct fd_peer * peer)
 {
+	/* free linked information */
 	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);
 	}
+	/* note: pir_cert_list needs not be freed (belongs to gnutls) */
 	
-	fd_ep_clearflags( &peer->p_hdr.info.pi_endpoints, EP_FL_ADV /* Remove previously advertised endpoints */ );
+	/* cleanup the area */
+	memset(&peer->p_hdr.info.runtime, 0, sizeof(peer->p_hdr.info.runtime));
+	
+	/* reinit the list */
+	fd_list_init(&peer->p_hdr.info.runtime.pir_apps, peer);
+
+	/* Remove previously advertised endpoints */
+	fd_ep_clearflags( &peer->p_hdr.info.pi_endpoints, EP_FL_ADV );
 }
 
 /* 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)
+static int save_remote_CE_info(struct msg * msg, struct fd_peer * peer, struct fd_pei * error, uint32_t *rc)
 {
 	struct avp * avp = NULL;
 	
@@ -308,11 +303,13 @@
 				}
 				
 				/* 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)) {
+				if (fd_os_almostcasecmp(hdr->avp_value->os.data, hdr->avp_value->os.len, 
+							peer->p_hdr.info.pi_diamid, peer->p_hdr.info.pi_diamidlen)) {
 					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";
+					error->pei_errcode = "ER_DIAMETER_AVP_NOT_ALLOWED";
+					error->pei_message = "Your Origin-Host value does not match my configuration.";
+					error->pei_avp = avp;
 					return EINVAL;
 				}
 
@@ -329,13 +326,24 @@
 				
 				/* 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;
+					TRACE_DEBUG(INFO, "Multiple instances of the Origin-Realm AVP");
+					error->pei_errcode = "ER_DIAMETER_AVP_OCCURS_TOO_MANY_TIMES";
+					error->pei_message = "I found several Origin-Realm AVPs";
+					error->pei_avp = avp;
+					return EINVAL;
 				}
 				
-				/* 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);
+				/* If the octet string contains a \0 */
+				if (!fd_os_is_valid_DiameterIdentity(hdr->avp_value->os.data, hdr->avp_value->os.len)) {
+					error->pei_errcode = "ER_DIAMETER_INVALID_AVP_VALUE";
+					error->pei_message = "Your Origin-Realm contains invalid characters.";
+					error->pei_avp = avp;
+					return EINVAL;
+				}
+				
+				/* Save the value */
+				CHECK_MALLOC(  peer->p_hdr.info.runtime.pir_realm = os0dup( hdr->avp_value->os.data, hdr->avp_value->os.len )  );
+				peer->p_hdr.info.runtime.pir_realmlen = hdr->avp_value->os.len;
 				break;
 
 			case AC_HOST_IP_ADDRESS: /* Host-IP-Address */
@@ -351,7 +359,13 @@
 
 					/* Get the sockaddr value */
 					memset(&ss, 0, sizeof(ss));
-					CHECK_FCT( fd_msg_avp_value_interpret( avp, &ss) );
+					CHECK_FCT_DO( fd_msg_avp_value_interpret( avp, &ss),
+						{
+							/* in case of error, assume the AVP value was wrong */
+							error->pei_errcode = "ER_DIAMETER_INVALID_AVP_VALUE";
+							error->pei_avp = avp;
+							return EINVAL;
+						} );
 
 					/* 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 ) );
@@ -369,8 +383,11 @@
 				
 				/* 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;
+					TRACE_DEBUG(INFO, "Multiple instances of the Vendor-Id AVP");
+					error->pei_errcode = "ER_DIAMETER_AVP_OCCURS_TOO_MANY_TIMES";
+					error->pei_message = "I found several Vendor-Id AVPs";
+					error->pei_avp = avp;
+					return EINVAL;
 				}
 				
 				peer->p_hdr.info.runtime.pir_vendorid = hdr->avp_value->u32;
@@ -387,8 +404,11 @@
 				
 				/* 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;
+					TRACE_DEBUG(INFO, "Multiple instances of the Product-Name AVP");
+					error->pei_errcode = "ER_DIAMETER_AVP_OCCURS_TOO_MANY_TIMES";
+					error->pei_message = "I found several Product-Name AVPs";
+					error->pei_avp = avp;
+					return EINVAL;
 				}
 
 				CHECK_MALLOC( peer->p_hdr.info.runtime.pir_prodname = calloc( hdr->avp_value->os.len + 1, 1 )  );
@@ -406,8 +426,11 @@
 				
 				/* 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;
+					TRACE_DEBUG(INFO, "Multiple instances of the Origin-State-Id AVP");
+					error->pei_errcode = "ER_DIAMETER_AVP_OCCURS_TOO_MANY_TIMES";
+					error->pei_message = "I found several Origin-State-Id AVPs";
+					error->pei_avp = avp;
+					return EINVAL;
 				}
 				
 				peer->p_hdr.info.runtime.pir_orstate = hdr->avp_value->u32;
@@ -422,7 +445,10 @@
 					goto next;
 				}
 				
-				TRACE_DEBUG(FULL, "'%s' supports a subset of vendor %d features.", peer->p_hdr.info.pi_diamid, hdr->avp_value->u32);
+				TRACE_DEBUG(FULL, "'%s' claims support for a subset of vendor %d features.", peer->p_hdr.info.pi_diamid, hdr->avp_value->u32);
+				/* not that it makes a difference for us... 
+				 -- if an application actually needs this info, we could save it somewhere.
+				*/
 				break;
 
 			case AC_VENDOR_SPECIFIC_APPLICATION_ID: /* Vendor-Specific-Application-Id (grouped)*/
@@ -476,6 +502,9 @@
 					if (auth + acct != 1) {
 						TRACE_DEBUG(FULL, "Invalid Vendor-Specific-Application-Id AVP received, ignored");
 						fd_msg_dump_one(FULL, avp);
+						error->pei_errcode = "ER_DIAMETER_INVALID_AVP_VALUE";
+						error->pei_avp = avp;
+						return EINVAL;
 					} else {
 						/* Add an entry in the list */
 						CHECK_FCT( fd_app_merge(&peer->p_hdr.info.runtime.pir_apps, aid, vid, auth, acct) );
@@ -509,9 +538,9 @@
 				}
 				
 				if (hdr->avp_value->u32 == AI_RELAY) {
+					/* Not clear if the relay application can be inside this AVP... */
 					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;
@@ -536,7 +565,12 @@
 					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 */
+				if (hdr->avp_value->u32 >= 32 ) {
+					error->pei_errcode = "ER_DIAMETER_INVALID_AVP_VALUE";
+					error->pei_message = "I don't support this Inband-Security-Id value (yet).";
+					error->pei_avp = avp;
+					return EINVAL;
+				}
 				peer->p_hdr.info.runtime.pir_isi |= (1 << hdr->avp_value->u32);
 				break;
 		}
@@ -558,9 +592,9 @@
 	/* 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 ? */
+	/* Do we need Inband-Security-Id AVPs ? If we're already using TLS, we don't... */
 	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_none = peer->p_hdr.info.config.pic_flags.sec & PI_SEC_NONE; /* we add it even if the peer does not use the old mechanism, it is impossible to distinguish */
 		isi_tls  = peer->p_hdr.info.config.pic_flags.sec & PI_SEC_TLS_OLD;
 	}
 	
@@ -586,16 +620,17 @@
 }
 
 /* Reject an incoming connection attempt */
-static void receiver_reject(struct cnxctx * recv_cnx, struct msg ** cer, char * rescode, char * errormsg)
+static void receiver_reject(struct cnxctx ** recv_cnx, struct msg ** cer, struct fd_pei * error)
 {
 	/* 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 );
+	CHECK_FCT_DO( fd_msg_rescode_set(*cer, error->pei_errcode, error->pei_message, error->pei_avp, 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);
+	fd_cnx_destroy(*recv_cnx);
+	*recv_cnx = NULL;
 	if (*cer) {
 		fd_msg_log(FD_MSG_LOG_DROPPED, *cer, "An error occurred while rejecting a CER.");
 		fd_msg_free(*cer);
@@ -613,8 +648,7 @@
 	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 (fd_peer_getstate(peer) == STATE_WAITCNXACK_ELEC) {
 		if (election_result(peer)) {
 			/* Close initiator connection */
 			fd_cnx_destroy(initiator);
@@ -623,10 +657,12 @@
 			CHECK_FCT( fd_p_ce_process_receiver(peer) );
 
 		} else {
+			struct fd_pei pei;
+			memset(&pei, 0, sizeof(pei));
+			pei.pei_errcode = "ELECTION_LOST";
 
 			/* Answer an ELECTION LOST to the receiver side */
-			receiver_reject(peer->p_receiver, &peer->p_cer, "ELECTION_LOST", NULL);
-			peer->p_receiver = NULL;
+			receiver_reject(&peer->p_receiver, &peer->p_cer, &pei);
 			CHECK_FCT( to_waitcea(peer, initiator) );
 		}
 	} else {
@@ -640,8 +676,10 @@
 /* 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;
+	int st;
+	struct fd_pei pei;
+	
 	TRACE_ENTRY("%p %p", msg, peer);
 	CHECK_PARAMS( msg && *msg && CHECK_PEER(peer) );
 	
@@ -655,17 +693,16 @@
 		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 ) );
+		CHECK_FCT( fd_msg_rescode_set(*msg, "ER_DIAMETER_UNABLE_TO_COMPLY", "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 (req || ((st = fd_peer_getstate(peer)) != STATE_WAITCEA)) {
 		if (*msg) {
-			fd_msg_log( FD_MSG_LOG_DROPPED, *msg, "Received CER/CEA while in '%s' state.\n", STATE_STR(peer->p_hdr.info.runtime.pir_state));
+			fd_msg_log( FD_MSG_LOG_DROPPED, *msg, "Received CER/CEA while in '%s' state.\n", STATE_STR(st));
 			CHECK_FCT_DO( fd_msg_free(*msg), /* continue */);
 			*msg = NULL;
 		}
@@ -673,8 +710,10 @@
 		return 0;
 	}
 	
+	memset(&pei, 0, sizeof(pei));
+	
 	/* Save info from the CEA into the peer */
-	CHECK_FCT_DO( save_remote_CE_info(*msg, peer, &ec, &rc), goto cleanup );
+	CHECK_FCT_DO( save_remote_CE_info(*msg, peer, &pei, &rc), goto cleanup );
 	
 	/* Dispose of the message, we don't need it anymore */
 	CHECK_FCT_DO( fd_msg_free(*msg), /* continue */ );
@@ -700,7 +739,7 @@
 		
 		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);
+			TRACE_DEBUG(INFO, "Peer %s replied a CEA with Result-Code %d, aborting connection attempts.", peer->p_hdr.info.pi_diamid, rc);
 			return EINVAL;
 	}
 	
@@ -750,13 +789,14 @@
 	return 0;
 }
 
-/* Handle the receiver side to go to OPEN state (any election is resolved) */
+/* Handle the receiver side to go to OPEN or OPEN_NEW state (any election is resolved) */
 int fd_p_ce_process_receiver(struct fd_peer * peer)
 {
-	char * ec = NULL;
+	struct fd_pei pei;
 	struct msg * msg = NULL;
 	int isi = 0;
 	int fatal = 0;
+	int tls_sync=0;
 	
 	TRACE_ENTRY("%p", peer);
 	
@@ -764,15 +804,27 @@
 	msg = peer->p_cer;
 	peer->p_cer = NULL;
 	
+	memset(&pei, 0, sizeof(pei));
+	
 	/* Parse the content of the received CER */
-	CHECK_FCT_DO( save_remote_CE_info(msg, peer, &ec, NULL), goto error_abort );
+	CHECK_FCT_DO( save_remote_CE_info(msg, peer, &pei, NULL), goto error_abort );
+	
+	/* Validate the realm if needed */
+	if (peer->p_hdr.info.config.pic_realm) {
+		size_t len = strlen(peer->p_hdr.info.config.pic_realm);
+		if (fd_os_almostcasecmp(peer->p_hdr.info.config.pic_realm, len, peer->p_hdr.info.runtime.pir_realm, peer->p_hdr.info.runtime.pir_realmlen)) {
+			TRACE_DEBUG(INFO, "Rejected CER from peer '%s', realm mismatch with configured value (returning DIAMETER_UNKNOWN_PEER).\n", peer->p_hdr.info.pi_diamid);
+			pei.pei_errcode = "DIAMETER_UNKNOWN_PEER"; /* maybe AVP_NOT_ALLOWED would be better fit? */
+			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";
+			pei.pei_errcode = "DIAMETER_UNKNOWN_PEER";
 			goto error_abort;
 		}
 		CHECK_FCT( res );
@@ -784,7 +836,7 @@
 		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";
+			pei.pei_errcode = "DIAMETER_NO_COMMON_APPLICATION";
 			fatal = 1;
 			goto error_abort;
 		}
@@ -834,7 +886,7 @@
 		/* 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";
+			pei.pei_errcode = "DIAMETER_NO_COMMON_SECURITY";
 			fatal = 1;
 			goto error_abort;
 		}
@@ -848,11 +900,7 @@
 	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) {
@@ -877,7 +925,7 @@
 					return 0;
 				}  );
 		}
-		
+		tls_sync = 1;
 	} else {
 		if ( ! fd_cnx_getTLS(peer->p_cnxctx) ) {
 			TRACE_DEBUG(INFO, "No TLS protection negotiated with peer '%s'.", peer->p_hdr.info.pi_diamid);
@@ -890,22 +938,23 @@
 		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);
+		if ((!tls_sync) && (fd_cnx_isMultichan(peer->p_cnxctx))) {
+			fd_psm_change_state(peer, STATE_OPEN_NEW );
+			/* send DWR */
+			CHECK_FCT( fd_p_dw_timeout(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 */ );
+	if (pei.pei_errcode) {
+		/* Send the error */
+		receiver_reject(&peer->p_cnxctx, &msg, &pei);
 	}
 	
 cleanup:
@@ -924,8 +973,11 @@
 /* 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) {
+	struct fd_pei pei;
+	int cur_state = fd_peer_getstate(peer);
+	memset(&pei, 0, sizeof(pei));
+	
+	switch (cur_state) {
 		case STATE_CLOSED:
 			peer->p_receiver = *cnx;
 			*cnx = NULL;
@@ -959,14 +1011,16 @@
 			} 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;
+				pei.pei_errcode = "ELECTION_LOST";
+				pei.pei_message = "Please answer my CER instead, you won the election.";
+				receiver_reject(cnx, msg, &pei);
 			}
 			break;
 
 		default:
-			receiver_reject(*cnx, msg, "DIAMETER_UNABLE_TO_COMPLY", "Invalid state to receive a new connection attempt");
-			*cnx = NULL;
+			pei.pei_errcode = "DIAMETER_UNABLE_TO_COMPLY"; /* INVALID COMMAND? in case of Capabilities-Updates? */
+			pei.pei_message = "Invalid state to receive a new connection attempt.";
+			receiver_reject(cnx, msg, &pei);
 	}
 				
 	return 0;
--- a/libfdcore/p_cnx.c	Mon Jan 31 17:22:21 2011 +0900
+++ b/libfdcore/p_cnx.c	Wed Feb 09 15:26:58 2011 +0900
@@ -35,6 +35,9 @@
 
 #include "fdcore-internal.h"
 
+
+/* TODO: change the behavior to handle properly forced ordering at beginning & end of OPEN state */
+
 /* This file contains code used by a peer state machine to initiate a connection to remote peer */
 
 struct next_conn {
@@ -86,7 +89,7 @@
 		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));
+			TRACE_DEBUG(INFO, "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;
@@ -122,7 +125,8 @@
 	
 	/* 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);
+		TRACE_DEBUG(INFO, "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;
 	}
@@ -218,7 +222,7 @@
 	/* Set the thread name */
 	{
 		char buf[48];
-		sprintf(buf, "ConnTo:%.*s", (int)(sizeof(buf)) - 8, peer->p_hdr.info.pi_diamid);
+		snprintf(buf, sizeof(buf), "ConnTo:%s", peer->p_hdr.info.pi_diamid);
 		fd_log_threadname ( buf );
 	}
 	
@@ -246,7 +250,8 @@
 				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);
+				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 */
 		}
@@ -259,7 +264,7 @@
 		
 		pthread_testcancel();
 		
-	} while (!cnx); /* and until cancellation */
+	} while (!cnx); /* and until cancellation or all addresses attempted without success */
 	
 	/* Now, we have an established connection in cnx */
 	
@@ -273,7 +278,7 @@
 		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);
+				TRACE_DEBUG(INFO, "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);
--- a/libfdcore/p_dp.c	Mon Jan 31 17:22:21 2011 +0900
+++ b/libfdcore/p_dp.c	Wed Feb 09 15:26:58 2011 +0900
@@ -37,19 +37,39 @@
 
 /* This file contains code to handle Disconnect Peer messages (DPR and DPA) */
 
+/* Delay to use before next reconnect attempt */
+int fd_p_dp_newdelay(struct fd_peer * peer) 
+{
+	int delay = peer->p_hdr.info.config.pic_tctimer ?: fd_g_config->cnf_timer_tc;
+	
+	switch (peer->p_hdr.info.runtime.pir_lastDC) {
+		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;
+	}
+	return delay;
+}
+
 /* 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 */
+		/* We received a DPR, save the Disconnect-Cause and go to CLOSING_GRACE or 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) {
@@ -59,30 +79,20 @@
 				ASSERT(0); /* To check if this really happens, and understand why... */
 			}
 
+			/* save the cause */
 			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_object * dictobj;
 				struct dict_enumval_request er;
 				memset(&er, 0, sizeof(er));
+				
+				/* prepare the request */
 				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;
+				
+				/* Search the enum value */
 				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 ) );
@@ -99,30 +109,43 @@
 		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...) */
+		/* Move to CLOSING state to failover outgoing messages (and avoid failing over 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);
-		
+		if (fd_cnx_isMultichan(peer->p_cnxctx)) {
+			/* There is a possibililty that messages are still in the pipe coming here, so let's grace for 1 second */
+			CHECK_FCT( fd_psm_change_state(peer, STATE_CLOSING_GRACE) );
+			fd_psm_next_timeout(peer, 0, 1);
+			
+		} else {
+			/* 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, fd_p_dp_newdelay(peer));
+		}
 	} 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));
+		int curstate = fd_peer_getstate(peer);
+		if (curstate != STATE_CLOSING) {
+			TRACE_DEBUG(INFO, "Ignoring DPA received in state %s", STATE_STR(curstate));
 		}
 			
 		/* 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 */
+		
+		/* TODO("Control Result-Code in the DPA") */
 		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 */
+		if (fd_cnx_isMultichan(peer->p_cnxctx)) {
+			CHECK_FCT( fd_psm_change_state(peer, STATE_CLOSING_GRACE) );
+			fd_psm_next_timeout(peer, 0, 1);
+			peer->p_flags.pf_localterm = 1;
+		}
+		/* otherwise, return in CLOSING state, the psm will handle it */
 	}
 	
 	return 0;
@@ -152,7 +175,7 @@
 	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_DO( fd_dict_search( fd_g_config->cnf_dict, DICT_ENUMVAL, ENUMVAL_BY_STRUCT, &er, &dictobj, ENOENT ), { ASSERT(0); /* internal error: unknown reason */ }  );
 	CHECK_FCT( fd_dict_getval( dictobj, &er.search ) );
 	
 	/* Set the value in the AVP */
--- a/libfdcore/p_dw.c	Mon Jan 31 17:22:21 2011 +0900
+++ b/libfdcore/p_dw.c	Wed Feb 09 15:26:58 2011 +0900
@@ -37,16 +37,17 @@
 
 /* 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)
+/* Check the value of Origin-State-Id is consistent in a DWR or DWA -- we return an error otherwise */
+static int 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 );
+	CHECK_FCT( fd_msg_search_avp ( msg, fd_dict_avp_OSI, &osi ) );
 	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  );
+		CHECK_FCT(  fd_msg_avp_hdr( osi, &hdr )  );
 		if (hdr->avp_value == NULL) {
 			/* This is a sanity check */
 			TRACE_DEBUG(NONE, "BUG: Unset value in Origin-State-Id in DWR / DWA");
@@ -55,12 +56,14 @@
 		}
 
 		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", 
+			TRACE_DEBUG(INFO, "Received a new Origin-State-Id from peer '%s'! (%x -> %x); resetting the connection.\n", 
 				peer->p_hdr.info.pi_diamid, 
-				hdr->avp_value->u32,
-				peer->p_hdr.info.runtime.pir_orstate );
+				peer->p_hdr.info.runtime.pir_orstate,
+				hdr->avp_value->u32 );
+			return EINVAL;
 		}
 	}
+	return 0;
 }
 
 /* Create and send a DWR */
@@ -91,7 +94,7 @@
 	TRACE_ENTRY("%p %d %p", msg, req, peer);
 	
 	/* Check the value of OSI for information */
-	check_state_id(*msg, peer);
+	CHECK_FCT( check_state_id(*msg, peer) );
 	
 	if (req) {
 		/* If we receive a DWR, send back a DWA */
@@ -101,7 +104,7 @@
 		CHECK_FCT( fd_out_send( msg, peer->p_cnxctx, peer, FD_CNX_ORDERED) );
 		
 	} else {
-		/* Just discard the DWA */
+		/* Discard the DWA */
 		CHECK_FCT_DO( fd_msg_free(*msg), /* continue */ );
 		*msg = NULL;
 		
@@ -122,8 +125,7 @@
 	}
 	
 	/* If we are in REOPEN state, increment the counter */
-	fd_cpu_flush_cache();
-	if (peer->p_hdr.info.runtime.pir_state == STATE_REOPEN) {
+	if (fd_peer_getstate(peer) == STATE_REOPEN) {
 		peer->p_flags.pf_reopen_cnt += 1;
 		
 		if (peer->p_flags.pf_reopen_cnt) {
--- a/libfdcore/p_expiry.c	Mon Jan 31 17:22:21 2011 +0900
+++ b/libfdcore/p_expiry.c	Wed Feb 09 15:26:58 2011 +0900
@@ -58,10 +58,9 @@
 		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;
+			struct fd_peer * peer = (struct fd_peer *)li->o;
 			
-			fd_cpu_flush_cache();
-			if (peer->p_hdr.info.runtime.pir_state != STATE_ZOMBIE)
+			if (fd_peer_getstate(peer) != STATE_ZOMBIE)
 				continue;
 			
 			if (peer->p_hdr.info.config.pic_flags.persist == PI_PRST_ALWAYS)
@@ -77,7 +76,7 @@
 		
 		/* Now delete peers that are in the purge list */
 		while (!FD_IS_LIST_EMPTY(&purge)) {
-			struct fd_peer * peer = (struct fd_peer *)(purge.next);
+			struct fd_peer * peer = (struct fd_peer *)(purge.next->o);
 			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 ? */ );
@@ -104,7 +103,7 @@
 		struct timespec	now;
 		struct fd_peer * first;
 		
-		/* Check if there are expiring sessions available */
+		/* Check if there are expiring peers 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 ), { ASSERT(0); } );
@@ -182,7 +181,7 @@
 		struct fd_list * li;
 		
 		/* update the p_exp_timer value */
-		CHECK_SYS(  clock_gettime(CLOCK_REALTIME, &peer->p_exp_timer)  );
+		CHECK_SYS_DO(  clock_gettime(CLOCK_REALTIME, &peer->p_exp_timer), { ASSERT(0); }  );
 		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) */
--- a/libfdcore/p_out.c	Mon Jan 31 17:22:21 2011 +0900
+++ b/libfdcore/p_out.c	Wed Feb 09 15:26:58 2011 +0900
@@ -108,7 +108,7 @@
 	/* Set the thread name */
 	{
 		char buf[48];
-		sprintf(buf, "OUT/%.*s", (int)sizeof(buf) - 5, peer->p_hdr.info.pi_diamid);
+		snprintf(buf, sizeof(buf), "OUT/%s", peer->p_hdr.info.pi_diamid);
 		fd_log_threadname ( buf );
 	}
 	
@@ -148,8 +148,7 @@
 	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)) {
+	if (fd_peer_getstate(peer) == STATE_OPEN) {
 		/* Normal case: just queue for the out thread to pick it up */
 		CHECK_FCT( fd_fifo_post(peer->p_tosend, msg) );
 		
--- a/libfdcore/p_psm.c	Mon Jan 31 17:22:21 2011 +0900
+++ b/libfdcore/p_psm.c	Wed Feb 09 15:26:58 2011 +0900
@@ -35,6 +35,73 @@
 
 #include "fdcore-internal.h"
 
+/*
+This file implement a Peer State Machine which is a mix of:
+ - the state machine described in rfc3588bis
+ - the state machine described in rfc3539#section-3.4
+ - the following observations.
+ 
+The delivery of Diameter messages must not always be unordered: order is important at
+begining and end of a connection lifetime. It means we need agility to
+switch between "ordering enforced" and "ordering not enforced to counter
+HotLB" modes of operation.
+
+The connection state machine represented in RFC3588 (and rfc3588bis) is
+incomplete, because it lacks the SUSPECT state and the 3 DWR/DWA
+exchanges (section 5.1) when the peer recovers from this state.
+Personnally I don't see the rationale for exchanging 3 messages (why 3?)
+but, if we require at least 1 DWR/DWA exchange to be always performed
+after the CER/CEA exchange (and initiated by the peer that sent the
+CEA), we have a simple way to deal with our ordering problem, as resumed
+bellow. Peers are: [i]nitiator, [r]esponder.
+ (1) [i] SCTP connection attempt.
+ (2) [r] accept the connection.
+ (3) [i,r] (if secure port) DTLS handshake, close on failure.
+ (4) [i] Send CER
+ (5) [r] Receive CER, send CEA using stream 0, flag "unordered" cleared.
+       [r] Immediately send a DWR after the CEA, also using stream 0,
+flag "unordered" cleared.
+       [r] Move to STATE_OPEN_NEW state -- equivalent to OPEN except
+that all messages are sent ordered at the moment.
+ (6) [i] receive CEA, move to OPEN state. All messages can be sent
+unordered in OPEN state.
+       [i] As per normal operation, reply with DWA to the DWR.
+ (7) [r] Upon reception of the DWA, move to OPEN state, messages can be
+sent unordered from this point.
+
+Note about (5) and (6): if the Diameter Identity received in CER or CEA
+does not match the credentials from the certificate presented during
+DTLS handshake, we may need to specify a path of clean disconnection
+(not blocking the remote peer waiting for something).
+
+This proposed mechanism removes the problem of application messages
+received before the CEA by the initiator. Note that if the "old" inband
+TLS handshake is used, this handshake plays the same synchronization
+role than the new DWR/DWA, which becomes useless.
+
+
+The other time where ordering is important is by the end of connection
+lifetime, when one peer is shutting down the link for some reason
+(reboot, overload, no activity, etc...). In case of unordered delivery,
+we may have:
+- peer A sends an application message followed by a DPR. Peer B receives
+the DPR first and tears down the connection. Application message is lost.
+- Peer B sends an application message, then receives a DPR and answers a
+DPA. Peer A receives the DPA before the application message. The
+application message is lost.
+
+This situation is actually quite possible because DPR/DPA messages are
+very short, while application messages can be quite large. Therefore,
+they require much more time to deliver.
+
+I really cannot see a way to counter this effect by using the ordering
+of the messages, except by applying a timer (state STATE_CLOSING_GRACE).
+
+However, this problem must be balanced with the fact that the message
+that is lost will be in many cases sent again as the failover mechanism
+specifies.
+*/
+
 /* The actual declaration of peer_state_str */
 DECLARE_STATE_STR();
 
@@ -100,11 +167,13 @@
 		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);
+		int cmp = fd_os_cmp(peer->p_hdr.info.pi_diamid, peer->p_hdr.info.pi_diamidlen, 
+					next_p->p_hdr.info.pi_diamid, next_p->p_hdr.info.pi_diamidlen);
 		if (cmp < 0)
 			break;
 	}
@@ -114,7 +183,7 @@
 	/* 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)(&peer->p_hdr.info, peer->p_cb_data); /* TODO: do this in a separate detached thread? */
 		peer->p_cb = NULL;
 		peer->p_cb_data = NULL;
 	}
@@ -177,6 +246,23 @@
 	}
 }
 
+/* Read state */
+int fd_peer_get_state(struct peer_hdr *peer)
+{
+	int ret;
+	
+	struct fd_peer * p = (struct fd_peer *)peer;
+	
+	if (!CHECK_PEER(p))
+		return -1;
+	
+	CHECK_POSIX_DO( pthread_mutex_lock(&p->p_state_mtx), return -1 );
+	ret = p->p_state;
+	CHECK_POSIX_DO( pthread_mutex_unlock(&p->p_state_mtx), return -1 );
+	
+	return ret;
+}
+
 
 /* Change state */
 int fd_psm_change_state(struct fd_peer * peer, int new_state)
@@ -185,8 +271,8 @@
 	
 	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;
+	
+	old = fd_peer_getstate(peer);
 	if (old == new_state)
 		return 0;
 	
@@ -195,8 +281,10 @@
 			STATE_STR(new_state),
 			peer->p_hdr.info.pi_diamid);
 	
-	peer->p_hdr.info.runtime.pir_state = new_state;
-	fd_cpu_flush_cache();
+	
+	CHECK_POSIX( pthread_mutex_lock(&peer->p_state_mtx) );
+	peer->p_state = new_state;
+	CHECK_POSIX( pthread_mutex_unlock(&peer->p_state_mtx) );
 	
 	if (old == STATE_OPEN) {
 		CHECK_FCT( leave_open_state(peer) );
@@ -254,8 +342,7 @@
 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) {
+	if (fd_peer_getstate(peer) != STATE_ZOMBIE) {
 		CHECK_FCT_DO( fd_psm_change_state(peer, STATE_CLOSED), /* continue */ );
 	}
 	
@@ -284,8 +371,9 @@
 {
 	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();
+	CHECK_POSIX_DO( pthread_mutex_lock(&peer->p_state_mtx), );
+	peer->p_state = STATE_ZOMBIE;
+	CHECK_POSIX_DO( pthread_mutex_unlock(&peer->p_state_mtx), );
 	return;
 }
 
@@ -297,6 +385,7 @@
 	int event;
 	size_t ev_sz;
 	void * ev_data;
+	int cur_state;
 	
 	CHECK_PARAMS_DO( CHECK_PEER(peer), ASSERT(0) );
 	
@@ -305,12 +394,14 @@
 	/* Set the thread name */
 	{
 		char buf[48];
-		sprintf(buf, "PSM/%.*s", (int)sizeof(buf) - 5, peer->p_hdr.info.pi_diamid);
+		snprintf(buf, sizeof(buf), "PSM/%s", 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;
+	CHECK_POSIX_DO( pthread_mutex_lock(&peer->p_state_mtx), goto psm_end );
+	peer->p_state = STATE_CLOSED;
+	CHECK_POSIX_DO( pthread_mutex_unlock(&peer->p_state_mtx), goto psm_end );
 
 	/* Wait that the PSM are authorized to start in the daemon */
 	CHECK_FCT_DO( fd_psm_waitstart(), goto psm_end );
@@ -325,23 +416,29 @@
 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));
+			peer->p_hdr.info.pi_diamid, STATE_STR(fd_peer_getstate(peer)));
 	CHECK_FCT_DO( fd_event_timedget(peer->p_events, &peer->p_psm_timer, FDEVP_PSM_TIMEOUT, &event, &ev_sz, &ev_data), goto psm_end );
+	
+	cur_state = fd_peer_getstate(peer);
+	if (cur_state == -1)
+		goto psm_end;
+	
 	TRACE_DEBUG(FULL, "'%s'\t<-- '%s'\t(%p,%zd)\t'%s'",
-			STATE_STR(peer->p_hdr.info.runtime.pir_state),
+			STATE_STR(cur_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 */
+	ASSERT( cur_state != STATE_NEW );
+	ASSERT( cur_state != STATE_ZOMBIE );
+	ASSERT( cur_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);
+		ASSERT(0); /* we should investigate this situation */
 		goto psm_loop;
 	}
 
@@ -353,15 +450,17 @@
 
 	/* Requests to terminate the peer object */
 	if (event == FDEVP_TERMINATE) {
-		switch (peer->p_hdr.info.runtime.pir_state) {
+		switch (cur_state) {
 			case STATE_OPEN:
+			case STATE_OPEN_NEW:
 			case STATE_REOPEN:
-				/* We cannot just close the conenction, we have to send a DPR first */
+				/* We cannot just close the connection, 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_CLOSING_GRACE:
 			case STATE_WAITCNXACK:
 			case STATE_WAITCNXACK_ELEC:
 			case STATE_WAITCEA:
@@ -379,13 +478,6 @@
 		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), 
 			{
@@ -395,8 +487,16 @@
 				goto psm_loop;
 			} );
 		
+		/* If the current state does not allow receiving messages, just drop it */
+		if (cur_state == STATE_CLOSED) {
+			/* In such case, just discard the message */
+			fd_msg_log( FD_MSG_LOG_DROPPED, msg, "Purged from peer '%s''s queue (CLOSED state).", peer->p_hdr.info.pi_diamid );
+			fd_msg_free(msg);
+			goto psm_loop;
+		}
+		
 		/* Log incoming message */
-		fd_msg_log( FD_MSG_LOG_RECEIVED, msg, "Received %zdb from '%s'", ev_sz, peer->p_hdr.info.pi_diamid );
+		fd_msg_log( FD_MSG_LOG_RECEIVED, msg, "Received %zdb from '%s' (%s)", ev_sz, peer->p_hdr.info.pi_diamid, STATE_STR(cur_state) );
 	
 		/* Extract the header */
 		CHECK_FCT_DO( fd_msg_hdr(msg, &hdr), goto psm_end );
@@ -416,27 +516,34 @@
 			CHECK_FCT_DO( fd_msg_answ_associate( msg, req ), goto psm_end );
 		}
 		
+		if (cur_state == STATE_OPEN_NEW) {
+			/* OK, we have received something, so the connection is supposedly now in OPEN state at the remote site */
+			fd_psm_change_state(peer, STATE_OPEN );
+		}
+		
 		/* Now handle non-link-local messages */
 		if (fd_msg_is_routable(msg)) {
-			switch (peer->p_hdr.info.runtime.pir_state) {
+			switch (cur_state) {
 				/* To maximize compatibility -- should not be a security issue here */
 				case STATE_REOPEN:
 				case STATE_SUSPECT:
 				case STATE_CLOSING:
+				case STATE_CLOSING_GRACE:
 					TRACE_DEBUG(FULL, "Accepted a message while not in OPEN state... ");
 				/* The standard situation : */
+				case STATE_OPEN_NEW:
 				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);
+					CHECK_FCT_DO( fd_msg_source_set( msg, peer->p_hdr.info.pi_diamid, peer->p_hdr.info.pi_diamidlen, 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)) {
+					if ((cur_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;
@@ -448,7 +555,7 @@
 				case STATE_CLOSED:
 				default:
 					/* In such case, just discard the message */
-					fd_msg_log( FD_MSG_LOG_DROPPED, msg, "Received from peer '%s' while connection was not in OPEN state.", peer->p_hdr.info.pi_diamid );
+					fd_msg_log( FD_MSG_LOG_DROPPED, msg, "Received from peer '%s' while connection was not in state %s.", peer->p_hdr.info.pi_diamid, STATE_STR(cur_state) );
 					fd_msg_free(msg);
 			}
 			goto psm_loop;
@@ -484,8 +591,9 @@
 			
 			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)
+				if (fd_peer_getstate(peer) == STATE_CLOSING)
 					goto psm_end;
+
 				break;
 			
 			case CC_DEVICE_WATCHDOG:
@@ -493,7 +601,7 @@
 				break;
 			
 			default:
-				/* Unknown / unexpected / invalid message */
+				/* Unknown / unexpected / invalid message -- but validated by our dictionary */
 				TRACE_DEBUG(INFO, "Invalid non-routable command received: %u.", hdr->msg_code);
 				if (hdr->msg_flags & CMD_FLAG_REQUEST) {
 					do {
@@ -501,14 +609,14 @@
 						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 );
+						CHECK_FCT_DO( fd_msg_rescode_set(msg, "DIAMETER_COMMAND_UNSUPPORTED", "Or maybe the P-bit or application Id are erroneous.", 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");
+					TRACE_DEBUG(INFO, "Received answer with erroneous 'is_routable' result...");
 				}
 				
 				/* Cleanup the message if not done */
@@ -530,7 +638,7 @@
 	
 	/* The connection object is broken */
 	if (event == FDEVP_CNX_ERROR) {
-		switch (peer->p_hdr.info.runtime.pir_state) {
+		switch (cur_state) {
 			case STATE_WAITCNXACK_ELEC:
 				/* Abort the initiating side */
 				fd_p_cnx_abort(peer, 0);
@@ -540,6 +648,7 @@
 			
 			case STATE_WAITCEA:
 			case STATE_OPEN:
+			case STATE_OPEN_NEW:
 			case STATE_REOPEN:
 			case STATE_WAITCNXACK:
 			case STATE_SUSPECT:
@@ -557,6 +666,15 @@
 				/* We sent a DPR so we are terminating, do not wait for DPA */
 				goto psm_end;
 				
+			case STATE_CLOSING_GRACE:
+				if (peer->p_flags.pf_localterm) /* initiated here */
+					goto psm_end;
+				
+				fd_psm_cleanup(peer, 0);
+				
+				/* Reset the timer for next connection attempt */
+				fd_psm_next_timeout(peer, 1, fd_p_dp_newdelay(peer));
+				goto psm_loop;
 		}
 		goto psm_loop;
 	}
@@ -615,7 +733,7 @@
 		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) {
+		switch (cur_state) {
 			case STATE_WAITCNXACK_ELEC:
 			case STATE_WAITCNXACK:
 				fd_p_ce_handle_newcnx(peer, cnx);
@@ -623,7 +741,7 @@
 				
 			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));
+				TRACE_DEBUG(FULL, "Connection attempt successful but current state is %s, closing... (too slow?)", STATE_STR(cur_state));
 				fd_cnx_destroy(cnx);
 		}
 		
@@ -637,7 +755,7 @@
 		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) {
+		switch (cur_state) {
 			case STATE_WAITCNXACK_ELEC:
 				/* Abort the initiating side */
 				fd_p_cnx_abort(peer, 0);
@@ -652,7 +770,7 @@
 				
 			default:
 				/* Just ignore */
-				TRACE_DEBUG(FULL, "Connection attempt failed but current state is %s, ignoring...", STATE_STR(peer->p_hdr.info.runtime.pir_state));
+				TRACE_DEBUG(FULL, "Connection attempt failed but current state is %s, ignoring...", STATE_STR(cur_state));
 		}
 		
 		goto psm_loop;
@@ -660,9 +778,10 @@
 	
 	/* The timeout for the current state has been reached */
 	if (event == FDEVP_PSM_TIMEOUT) {
-		switch (peer->p_hdr.info.runtime.pir_state) {
+		switch (cur_state) {
 			case STATE_OPEN:
 			case STATE_REOPEN:
+			case STATE_OPEN_NEW:
 				CHECK_FCT_DO( fd_p_dw_timeout(peer), goto psm_end );
 				goto psm_loop;
 				
@@ -675,7 +794,6 @@
 			case STATE_SUSPECT:
 				/* Mark the connection problem */
 				peer->p_flags.pf_cnx_pb = 1;
-				
 			case STATE_CLOSING:
 			case STATE_WAITCNXACK:
 			case STATE_WAITCEA:
@@ -683,6 +801,16 @@
 				fd_psm_next_timeout(peer, 1, peer->p_hdr.info.config.pic_tctimer ?: fd_g_config->cnf_timer_tc);
 				goto psm_reset;
 				
+			case STATE_CLOSING_GRACE:
+				/* The grace period is completed, now close */
+				if (peer->p_flags.pf_localterm)
+					goto psm_end;
+				
+				fd_psm_cleanup(peer, 0);
+				/* Reset the timer for next connection attempt */
+				fd_psm_next_timeout(peer, 1, fd_p_dp_newdelay(peer));
+				goto psm_loop;
+				
 			case STATE_WAITCNXACK_ELEC:
 				/* Abort the initiating side */
 				fd_p_cnx_abort(peer, 0);
@@ -696,7 +824,7 @@
 	}
 	
 	/* 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));
+	TRACE_DEBUG(INFO, "Missing handler in PSM for '%s'\t<-- '%s'", STATE_STR(cur_state), fd_pev_str(event));
 psm_reset:
 	if (peer->p_flags.pf_delete)
 		goto psm_end;
@@ -706,10 +834,9 @@
 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),
+			STATE_STR(fd_peer_getstate(peer)),
 			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;
@@ -725,7 +852,7 @@
 	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) );
+	CHECK_PARAMS( fd_peer_getstate(peer) == STATE_NEW );
 	
 	/* Create the FIFO for events */
 	CHECK_FCT( fd_fifo_new(&peer->p_events) );
@@ -743,8 +870,7 @@
 	TRACE_ENTRY("%p", peer);
 	CHECK_PARAMS( CHECK_PEER(peer) );
 	
-	fd_cpu_flush_cache();
-	if (peer->p_hdr.info.runtime.pir_state != STATE_ZOMBIE) {
+	if (fd_peer_getstate(peer) != 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);
--- a/libfdcore/p_sr.c	Mon Jan 31 17:22:21 2011 +0900
+++ b/libfdcore/p_sr.c	Wed Feb 09 15:26:58 2011 +0900
@@ -35,10 +35,6 @@
 
 #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 *)  */
@@ -68,17 +64,23 @@
 {
 	struct fd_list * li;
 	struct timespec now;
-	if (!TRACE_BOOL(SR_DEBUG_LVL))
+	
+	if (!TRACE_BOOL(ANNOYING))
 		return;
+	
+	fd_log_debug("%sSentReq list @%p:\n", text, srlist);
+	
 	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, 
+		
+		fd_log_debug(" - Next req (hbh:%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);
+		
+		fd_msg_dump_one(ANNOYING + 1, sr->req);
 	}
 }
 
@@ -116,7 +118,7 @@
 	return NULL;
 }
 
-/* thread that handles messages expiring. The thread is started / cancelled only when needed */
+/* thread that handles messages expiring. The thread is started only when needed */
 static void * sr_expiry_th(void * arg) {
 	struct sr_list * srlist = arg;
 	struct msg * expired_req;
@@ -128,7 +130,7 @@
 	/* 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);
+		snprintf(buf, sizeof(buf), "ReqExp/%s", ((struct fd_peer *)(srlist->exp.o))->p_hdr.info.pi_diamid);
 		fd_log_threadname ( buf );
 	}
 	
@@ -312,7 +314,7 @@
 				});
 		} else {
 			/* Just free the request. */
-			fd_msg_log( FD_MSG_LOG_DROPPED, sr->req, "Local message discarded during failover" );
+			fd_msg_log( FD_MSG_LOG_DROPPED, sr->req, "Sent & unanswered local message discarded during failover." );
 			CHECK_FCT_DO(fd_msg_free(sr->req), /* Ignore */);
 		}
 		free(sr);
--- a/libfdcore/peers.c	Mon Jan 31 17:22:21 2011 +0900
+++ b/libfdcore/peers.c	Wed Feb 09 15:26:58 2011 +0900
@@ -72,6 +72,8 @@
 	fd_list_init(&p->p_hdr.info.runtime.pir_apps, p);
 	
 	p->p_eyec = EYEC_PEER;
+	CHECK_POSIX( pthread_mutex_init(&p->p_state_mtx, NULL) );
+	
 	fd_list_init(&p->p_actives, p);
 	fd_list_init(&p->p_expiry, p);
 	CHECK_FCT( fd_fifo_new(&p->p_tosend) );
@@ -93,22 +95,32 @@
 	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);
 	
+	if (info->config.pic_realm) {
+		if (!fd_os_is_valid_DiameterIdentity((os0_t)info->config.pic_realm, strlen(info->config.pic_realm))) {
+			TRACE_DEBUG(INFO, "'%s' is not a valid DiameterIdentity.", info->config.pic_realm);
+			return EINVAL;
+		}
+	}
+	
 	/* 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) );
+	p->p_hdr.info.pi_diamid = info->pi_diamid;
+	CHECK_FCT( fd_os_validate_DiameterIdentity(&p->p_hdr.info.pi_diamid, &p->p_hdr.info.pi_diamidlen, 1) );
 	
 	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) );
+		CHECK_MALLOC( p->p_hdr.info.config.pic_priority = strdup(info->config.pic_priority) );
 	}
 	
 	/* Move the list of endpoints into the peer */
@@ -123,7 +135,7 @@
 	if (orig_dbg) {
 		CHECK_MALLOC( p->p_dbgorig = strdup(orig_dbg) );
 	} else {
-		CHECK_MALLOC( p->p_dbgorig = strdup("unknown") );
+		CHECK_MALLOC( p->p_dbgorig = strdup("unspecified") );
 	}
 	p->p_cb = cb;
 	p->p_cb_data = cb_data;
@@ -133,7 +145,8 @@
 	
 	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 );
+		int cmp = fd_os_almostcasecmp( p->p_hdr.info.pi_diamid, p->p_hdr.info.pi_diamidlen, 
+						next->p_hdr.info.pi_diamid, next->p_hdr.info.pi_diamidlen );
 		if (cmp > 0)
 			continue;
 		if (cmp == 0)
@@ -161,12 +174,11 @@
 }
 
 /* Search for a peer */
-int fd_peer_getbyid( char * diamid, struct peer_hdr ** peer )
+int fd_peer_getbyid( DiamId_t diamid, size_t diamidlen, int igncase, struct peer_hdr ** peer )
 {
 	struct fd_list * li;
-	
-	TRACE_ENTRY("%p %p", diamid, peer);
-	CHECK_PARAMS( diamid && peer );
+	TRACE_ENTRY("%p %zd %d %p", diamid, diamidlen, igncase, peer);
+	CHECK_PARAMS( diamid && diamidlen && peer );
 	
 	*peer = NULL;
 	
@@ -174,7 +186,11 @@
 	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 );
+		int cmp;
+		if (igncase)
+			cmp = fd_os_almostcasecmp( diamid, diamidlen, next->p_hdr.info.pi_diamid, next->p_hdr.info.pi_diamidlen );
+		else
+			cmp = fd_os_cmp( diamid, diamidlen, next->p_hdr.info.pi_diamid, next->p_hdr.info.pi_diamidlen );
 		if (cmp > 0)
 			continue;
 		if (cmp == 0)
@@ -254,6 +270,7 @@
 	fd_list_unlink(&p->p_actives);
 	
 	CHECK_FCT_DO( fd_fifo_del(&p->p_tosend), /* continue */ );
+	CHECK_POSIX_DO( pthread_mutex_destroy(&p->p_state_mtx), /* continue */);
 	CHECK_POSIX_DO( pthread_mutex_destroy(&p->p_sr.mtx), /* continue */);
 	CHECK_POSIX_DO( pthread_cond_destroy(&p->p_sr.cnd), /* continue */);
 	
@@ -282,10 +299,9 @@
 	
 	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;
+		struct fd_peer * peer = (struct fd_peer *)li->o;
 		
-		fd_cpu_flush_cache();
-		if (peer->p_hdr.info.runtime.pir_state != STATE_ZOMBIE) {
+		if (fd_peer_getstate(peer) != STATE_ZOMBIE) {
 			CHECK_FCT_DO( fd_psm_terminate(peer, "REBOOTING"), /* continue */ );
 		} else {
 			li = li->prev; /* to avoid breaking the loop */
@@ -306,14 +322,13 @@
 	while ((!list_empty) && (TS_IS_INFERIOR(&now, &wait_until))) {
 		
 		/* Allow the PSM(s) to execute */
-		sched_yield();
+		usleep(100000);
 		
 		/* 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) {
+			struct fd_peer * peer = (struct fd_peer *)li->o;
+			if (fd_peer_getstate(peer) == 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);
@@ -328,7 +343,7 @@
 		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);
+			struct fd_peer * peer = (struct fd_peer *)(fd_g_peers.next->o);
 			fd_psm_abord(peer);
 			fd_list_unlink(&peer->p_hdr.chain);
 			fd_list_insert_before(&purge, &peer->p_hdr.chain);
@@ -338,7 +353,7 @@
 	
 	/* Free memory objects of all peers */
 	while (!FD_IS_LIST_EMPTY(&purge)) {
-		struct fd_peer * peer = (struct fd_peer *)(purge.next);
+		struct fd_peer * peer = (struct fd_peer *)(purge.next->o);
 		fd_list_unlink(&peer->p_hdr.chain);
 		fd_peer_free(&peer);
 	}
@@ -363,9 +378,9 @@
 		return;
 	}
 
-	fd_log_debug(">  %s\t%s", STATE_STR(peer->p_hdr.info.runtime.pir_state), peer->p_hdr.info.pi_diamid);
+	fd_log_debug(">  %s\t%s", STATE_STR(fd_peer_getstate(peer)), peer->p_hdr.info.pi_diamid);
 	if (details > INFO) {
-		fd_log_debug("\t(rlm:%s)", peer->p_hdr.info.runtime.pir_realm ?: "(unknown)");
+		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);
 	}
@@ -397,19 +412,20 @@
 	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;
+		struct fd_peer * np = (struct fd_peer *)li->o;
 		fd_peer_dump(np, details);
 	}
 	
 	CHECK_FCT_DO( pthread_rwlock_unlock(&fd_g_peers_rw), /* continue */ );
 }
 
+static struct dict_object *avp_oh_model = NULL;
+static pthread_mutex_t cache_avp_lock = PTHREAD_MUTEX_INITIALIZER;
+
 /* 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;
@@ -423,20 +439,40 @@
 	
 	msg = *cer; 
 	
+	/* If needed, resolve the dictioanry model for Origin-Host */
+	CHECK_POSIX( pthread_mutex_lock(&cache_avp_lock) );
+	if (!avp_oh_model) {
+		avp_code_t code = AC_ORIGIN_HOST;
+		int ret;
+		CHECK_FCT_DO( ret = fd_dict_search ( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_CODE, &code, &avp_oh_model, ENOENT),
+			{ CHECK_POSIX( pthread_mutex_unlock(&cache_avp_lock) ); return ret; } );
+	}
+	CHECK_POSIX( pthread_mutex_unlock(&cache_avp_lock) );
+	
 	/* 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 ) );
+	ASSERT(avp_oh); /* otherwise it should not have passed rules validation, right? */
 	CHECK_FCT( fd_msg_avp_hdr ( avp_oh, &avp_hdr ) );
 	
+	/* First, check if the Origin-Host value  */
+	if (!fd_os_is_valid_DiameterIdentity(avp_hdr->avp_value->os.data, avp_hdr->avp_value->os.len)) {
+		TRACE_DEBUG(INFO, "Received new CER with invalid \\0 in its Origin-Host");
+		CHECK_FCT( fd_msg_new_answer_from_req ( fd_g_config->cnf_dict, cer, MSGFL_ANSW_ERROR ) );
+		CHECK_FCT( fd_msg_rescode_set(*cer, "ER_DIAMETER_INVALID_AVP_VALUE", 
+							"Your Origin-Host contains invalid characters.", avp_oh, 1 ) );
+		CHECK_FCT( fd_out_send(cer, *cnx, NULL, FD_CNX_ORDERED) );
+		return EINVAL;
+	}
+	
 	/* 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 );
+		int cmp;
+		peer = (struct fd_peer *)li->o;
+		cmp = fd_os_almostcasecmp( avp_hdr->avp_value->os.data, avp_hdr->avp_value->os.len, peer->p_hdr.info.pi_diamid, peer->p_hdr.info.pi_diamidlen );
 		if (cmp > 0)
 			continue;
 		if (cmp == 0)
@@ -450,9 +486,9 @@
 		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_hdr.info.pi_diamid = os0dup(avp_hdr->avp_value->os.data, avp_hdr->avp_value->os.len), 
+			{ ret = ENOMEM; goto out; } );
+		peer->p_hdr.info.pi_diamidlen = avp_hdr->avp_value->os.len;
 		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;
@@ -469,16 +505,18 @@
 		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) {
+		if (fd_peer_getstate(peer) == 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_POSIX_DO( pthread_mutex_lock(&peer->p_state_mtx), );
+			peer->p_state = STATE_NEW;
+			CHECK_POSIX_DO( pthread_mutex_unlock(&peer->p_state_mtx), );
+			peer->p_flags.pf_localterm = 0;
 			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));
--- a/libfdcore/routing_dispatch.c	Mon Jan 31 17:22:21 2011 +0900
+++ b/libfdcore/routing_dispatch.c	Wed Feb 09 15:26:58 2011 +0900
@@ -207,8 +207,8 @@
 		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_FCT( fd_peer_getbyid( c->diamid, c->diamidlen, 0, (void *)&peer ) );
+		if (peer && !peer->p_hdr.info.runtime.pir_relay) {
 			/* 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)
@@ -263,23 +263,23 @@
 	/* 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;
+		
+	    #if 0 /* this is actually useless since the sending process will also ensure that the peer is still available */
 		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;
-				}
+		/* Since the candidates list comes from the peers list, we do not have any issue with upper/lower case to find the peer object */
+		CHECK_FCT( fd_peer_getbyid( c->diamid, c->diamidlen, 0, (void *)&peer ) );
+		if (!peer)
+			continue; /* it has been deleted since the candidate list was generated; avoid sending to this one in that case. */
+	    #endif /* 0 */
+		
+		/* In the AVPs, the value comes from the network, so let's be case permissive */
+		if (dh && !fd_os_almostcasecmp(dh->os.data, dh->os.len, c->diamid, c->diamidlen) ) {
+			/* The candidate is the Destination-Host */
+			c->score += FD_SCORE_FINALDEST;
+		} else {
+			if (dr && !fd_os_almostcasecmp(dr->os.data, dr->os.len, c->realm, c->realmlen) ) {
+				/* The candidate's realm matchs the Destination-Realm */
+				c->score += FD_SCORE_REALM;
 			}
 		}
 	}
@@ -297,8 +297,10 @@
 	int i;
 	
 	TRACE_ENTRY("%p %p %p", un, excl_idx, at_idx);
-	CHECK_PARAMS_DO( un && excl_idx, return );
+	CHECK_PARAMS_DO( un && excl_idx && at_idx, return );
+	
 	*excl_idx = 0;
+	*at_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++) {
@@ -306,67 +308,47 @@
 		if ( un->os.data[i] == (unsigned char) '!' ) {
 			if (!*excl_idx)
 				*excl_idx = i;
-			if (!at_idx)
-				return;
+			continue;
 		}
 		/* If we reach the realm part, we can stop */
 		if ( un->os.data[i] == (unsigned char) '@' ) {
-			if (at_idx)
-				*at_idx = i;
+			*at_idx = i;
 			break;
 		}
+		/* Stop if we find a \0 in the middle */
+		if ( un->os.data[i] == 0 ) {
+			return;
+		}
 		/* 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)
+/* Create new User-Name and Destination-Realm values */
+static int process_decorated_NAI(int * was_nai, union avp_value * un, union avp_value * dr)
 {
-	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;
+	int at_idx, sep_idx;
 	unsigned char * old_un;
-	TRACE_ENTRY("%p %p", un, dr);
-	CHECK_PARAMS(un && dr);
+	TRACE_ENTRY("%p %p %p", was_nai, un, dr);
+	CHECK_PARAMS(was_nai && 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));
+	if ((!sep_idx) || (sep_idx > at_idx) || !fd_os_is_valid_DiameterIdentity(old_un, sep_idx /* this is the new realm part */)) {
+		*was_nai = 0;
+		return 0;
+	}
+	
+	*was_nai = 1;
 	
 	/* Create the new User-Name value */
 	CHECK_MALLOC( un->os.data = malloc( at_idx ) );
@@ -389,6 +371,7 @@
 	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)
 {
@@ -397,15 +380,16 @@
 
 	/* Get the source of the message */
 	{
-		char * id;
-		CHECK_FCT( fd_msg_source_get( *pmsg, &id ) );
+		DiamId_t id;
+		size_t   idlen;
+		CHECK_FCT( fd_msg_source_get( *pmsg, &id, &idlen ) );
 		
 		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 ) );
+			CHECK_FCT( fd_peer_getbyid( id, idlen, 0, (void *)&peer ) );
 
 			if (!peer) {
 				fd_msg_log(FD_MSG_LOG_DROPPED, *pmsg, "Unable to send error '%s' to deleted peer '%s' in reply to this message.", error_code, id);
@@ -438,7 +422,6 @@
 /*         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)
 {
@@ -446,8 +429,8 @@
 	int is_req = 0, ret;
 	struct session * sess;
 	enum disp_action action;
-	const char * ec = NULL;
-	const char * em = NULL;
+	char * ec = NULL;
+	char * em = NULL;
 
 	/* Read the message header */
 	CHECK_FCT( fd_msg_hdr(*pmsg, &hdr) );
@@ -459,7 +442,7 @@
 	/* 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 */
+			/* in case of error */
 			if ((ret == EBADMSG) && (*pmsg != NULL)) {
 				/* msg now contains the answer message to send back */
 				CHECK_FCT( fd_fifo_post(fd_g_outgoing, pmsg) );
@@ -511,7 +494,7 @@
 				/* 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";
-			
+				/* and continue as if an error occurred... */
 			case DISP_ACT_ERROR:
 				/* We have a problem with delivering the message */
 				if (ec == NULL) {
@@ -527,7 +510,7 @@
 				
 				/* 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 ) );
+				CHECK_FCT( fd_msg_rescode_set(*pmsg, ec, em, NULL, 1 ) );
 				
 			case DISP_ACT_SEND:
 				/* Now, send the message */
@@ -544,7 +527,8 @@
 	struct msg_hdr * hdr;
 	int is_req = 0;
 	int is_err = 0;
-	char * qry_src = NULL;
+	DiamId_t qry_src = NULL;
+	size_t   qry_src_len = 0;
 
 	/* Read the message header */
 	CHECK_FCT( fd_msg_hdr(*pmsg, &hdr) );
@@ -571,7 +555,8 @@
 
 		/* 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");
+			TRACE_DEBUG(INFO, "Received a routable message with application id 0 or " _stringize(AI_RELAY) " (relay),\n"
+					  " returning DIAMETER_APPLICATION_UNSUPPORTED");
 			CHECK_FCT( return_error( pmsg, "DIAMETER_APPLICATION_UNSUPPORTED", "Routable message with application id 0 or relay", NULL) );
 			return 0;
 		} else {
@@ -586,6 +571,9 @@
 			struct avp_hdr * ahdr;
 			struct fd_pei error_info;
 			int ret;
+			
+			memset(&error_info, 0, sizeof(struct fd_pei)); 
+			
 			CHECK_FCT(  fd_msg_avp_hdr( avp, &ahdr )  );
 
 			if (! (ahdr->avp_flags & AVP_FLAG_VENDOR)) {
@@ -603,11 +591,10 @@
 							} );
 						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;
+						if (!fd_os_almostcasecmp(ahdr->avp_value->os.data, ahdr->avp_value->os.len, fd_g_config->cnf_diamid, fd_g_config->cnf_diamid_len)) {
+							is_dest_host = YES;
 						} else {
-							is_dest_host = (strncasecmp(fd_g_config->cnf_diamid, (char *)ahdr->avp_value->os.data, fd_g_config->cnf_diamid_len) 
-										? NO : YES);
+							is_dest_host = NO;
 						}
 						break;
 
@@ -625,14 +612,14 @@
 						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;
+						if (!fd_os_almostcasecmp(dr_val->os.data, dr_val->os.len, fd_g_config->cnf_diamrlm, fd_g_config->cnf_diamrlm_len)) {
+							is_dest_realm = YES;
 						} else {
-							is_dest_realm = (strncasecmp(fd_g_config->cnf_diamrlm, (char *)ahdr->avp_value->os.data, fd_g_config->cnf_diamrlm_len) 
-										? NO : YES);
+							is_dest_realm = NO;
 						}
 						break;
 
+					/* we also use User-Name for decorated NAI */
 					case AC_USER_NAME:
 						/* Parse this AVP */
 						CHECK_FCT_DO( ret = fd_msg_parse_dict ( avp, fd_g_config->cnf_dict, &error_info ),
@@ -651,6 +638,7 @@
 				}
 			}
 
+			/* Stop when we found all 3 AVPs -- they are supposed to be at the beginning of the message, so this should be fast */
 			if ((is_dest_host != UNKNOWN) && (is_dest_realm != UNKNOWN) && un)
 				break;
 
@@ -681,22 +669,23 @@
 		/* 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) );
+				CHECK_FCT( return_error( pmsg, "DIAMETER_UNABLE_TO_DELIVER", "I am not a Diameter agent", NULL) );
 				return 0;
 			}
 		} else {
 		/* Destination-Host was not set, and Destination-Realm is matching : we may handle or pass to a fellow peer */
+			int is_nai = 0;
 
 			/* 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;
-					} );
-
+			/* Handle the decorated NAI */
+			CHECK_FCT_DO( process_decorated_NAI(&is_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;
+				} );
+				
+			if (is_nai) {
 				/* We have transformed the AVP, now submit it again in the queue */
 				CHECK_FCT(fd_fifo_post(fd_g_incoming, pmsg) );
 				return 0;
@@ -723,7 +712,7 @@
 
 		/* Retrieve the corresponding query and its origin */
 		CHECK_FCT( fd_msg_answ_getq( *pmsg, &qry ) );
-		CHECK_FCT( fd_msg_source_get( qry, &qry_src ) );
+		CHECK_FCT( fd_msg_source_get( qry, &qry_src, &qry_src_len ) );
 
 		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. */
@@ -732,6 +721,7 @@
 		}
 
 		/* From that point, for answers, we will call the registered callbacks, then pass it to the dispatch module or forward it */
+		TODO("Callback for answers with a Redirect code?");
 	}
 
 	/* Call all registered callbacks for this message */
@@ -799,20 +789,20 @@
 	/* For answers, the routing is very easy */
 	if ( ! is_req ) {
 		struct msg * qry;
-		char * qry_src = NULL;
+		DiamId_t qry_src = NULL;
+		size_t qry_src_len = 0;
 		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 ) );
+		CHECK_FCT( fd_msg_source_get( qry, &qry_src, &qry_src_len ) );
 
 		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)) {
+		CHECK_FCT( fd_peer_getbyid( qry_src, qry_src_len, 0, (void *) &peer ) );
+		if (fd_peer_getstate(peer) != STATE_OPEN) {
 			fd_msg_log( FD_MSG_LOG_DROPPED, *pmsg, "Unable to forward answer to deleted / closed peer '%s'.", qry_src);
 			fd_msg_free(*pmsg);
 			*pmsg = NULL;
@@ -843,7 +833,12 @@
 		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_DO( ret = fd_rtd_candidate_add(rtd, 
+							p->p_hdr.info.pi_diamid, 
+							p->p_hdr.info.pi_diamidlen, 
+							p->p_hdr.info.runtime.pir_realm,
+							p->p_hdr.info.runtime.pir_realmlen), 
+				{ CHECK_FCT_DO( pthread_rwlock_unlock(&fd_g_activ_peers_rw), ); return ret; } );
 		}
 		CHECK_FCT( pthread_rwlock_unlock(&fd_g_activ_peers_rw) );
 
@@ -866,8 +861,8 @@
 						}
 					} );
 				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);
+				/* Remove this value from the list. We don't need to pay special attention to the contents here. */
+				fd_rtd_candidate_del(rtd, ahdr->avp_value->os.data, ahdr->avp_value->os.len);
 			}
 
 			/* Go to next AVP */
@@ -875,7 +870,7 @@
 		}
 	}
 
-	/* 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 ? */
+	/* 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 ? -- TODO */
 
 	/* Ok, we have our list in rtd now, let's (re)initialize the scores */
 	fd_rtd_candidate_extract(rtd, &candidates, FD_SCORE_INI);
@@ -927,10 +922,9 @@
 			break;
 
 		/* Search for the peer */
-		CHECK_FCT( fd_peer_getbyid( c->diamid, (void *)&peer ) );
+		CHECK_FCT( fd_peer_getbyid( c->diamid, c->diamidlen, 0, (void *)&peer ) );
 
-		fd_cpu_flush_cache();
-		if (peer && (peer->p_hdr.info.runtime.pir_state == STATE_OPEN)) {
+		if (fd_peer_getstate(peer) == STATE_OPEN) {
 			/* Send to this one */
 			CHECK_FCT_DO( fd_out_send(pmsg, NULL, peer, 0), continue );
 			
@@ -963,14 +957,15 @@
 
 /* Control of the threads */
 static enum { RUN = 0, STOP = 1 } order_val = RUN;
-static pthread_mutex_t order_lock = PTHREAD_MUTEX_INITIALIZER;
+static pthread_mutex_t order_state_lock = PTHREAD_MUTEX_INITIALIZER;
 
 /* Threads report their status */
 enum thread_state { NOTRUNNING = 0, RUNNING = 1 };
 static void cleanup_state(void * state_loc)
 {
-	if (state_loc)
-		*(enum thread_state *)state_loc = NOTRUNNING;
+	CHECK_POSIX_DO( pthread_mutex_lock(&order_state_lock), );
+	*(enum thread_state *)state_loc = NOTRUNNING;
+	CHECK_POSIX_DO( pthread_mutex_unlock(&order_state_lock), );
 }
 
 /* This is the common thread code (same for routing and dispatching) */
@@ -990,8 +985,9 @@
 	pthread_cleanup_push( cleanup_state, arg );
 	
 	/* Mark the thread running */
+	CHECK_POSIX_DO( pthread_mutex_lock(&order_state_lock), );
 	*(enum thread_state *)arg = RUNNING;
-	fd_cpu_flush_cache();
+	CHECK_POSIX_DO( pthread_mutex_unlock(&order_state_lock), );
 	
 	do {
 		struct msg * msg;
@@ -999,9 +995,9 @@
 		/* Test the current order */
 		{
 			int must_stop;
-			CHECK_POSIX_DO( pthread_mutex_lock(&order_lock), { ASSERT(0); } ); /* we lock to flush the caches */
+			CHECK_POSIX_DO( pthread_mutex_lock(&order_state_lock), { ASSERT(0); } ); /* we lock to flush the caches */
 			must_stop = (order_val == STOP);
-			CHECK_POSIX_DO( pthread_mutex_unlock(&order_lock), { ASSERT(0); } );
+			CHECK_POSIX_DO( pthread_mutex_unlock(&order_state_lock), { ASSERT(0); } );
 			if (must_stop)
 				goto end;
 			
@@ -1013,7 +1009,15 @@
 		/* Get the next message from the queue */
 		{
 			int ret;
-			ret = fd_fifo_get ( queue, &msg );
+			struct timespec ts;
+			
+			CHECK_SYS_DO( clock_gettime(CLOCK_REALTIME, &ts), goto fatal_error );
+			ts.tv_sec += 1;
+			
+			ret = fd_fifo_timedget ( queue, &msg, &ts );
+			if (ret == ETIMEDOUT)
+				/* loop, check if the thread must stop now */
+				continue;
 			if (ret == EPIPE)
 				/* The queue was destroyed, we are probably exiting */
 				goto end;
@@ -1099,15 +1103,18 @@
 	/* 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 ) );
+	
+	TODO("built-in callbacks for Redirect messages?");
+	
 	return 0;
 }
 
 /* Ask the thread to terminate after next iteration */
 int fd_rtdisp_cleanstop(void)
 {
-	CHECK_POSIX( pthread_mutex_lock(&order_lock) );
+	CHECK_POSIX_DO( pthread_mutex_lock(&order_state_lock), );
 	order_val = STOP;
-	CHECK_POSIX( pthread_mutex_unlock(&order_lock) );
+	CHECK_POSIX_DO( pthread_mutex_unlock(&order_state_lock), );
 
 	return 0;
 }
@@ -1116,10 +1123,15 @@
 {
 	TRACE_ENTRY("%p %p", st, thr);
 	CHECK_PARAMS_DO(st && thr, return);
+	int terminated;
+	
+	CHECK_POSIX_DO( pthread_mutex_lock(&order_state_lock), );
+	terminated = (*st == NOTRUNNING);
+	CHECK_POSIX_DO( pthread_mutex_unlock(&order_state_lock), );
+	
 
 	/* Wait for a second for the thread to complete, by monitoring my_state */
-	fd_cpu_flush_cache();
-	if (*st != NOTRUNNING) {
+	if (!terminated) {
 		TRACE_DEBUG(INFO, "Waiting for the %s thread to have a chance to terminate", th_name);
 		do {
 			struct timespec	 ts, ts_final;
@@ -1130,8 +1142,11 @@
 			ts_final.tv_nsec = ts.tv_nsec;
 			
 			while (TS_IS_INFERIOR( &ts, &ts_final )) {
-				fd_cpu_flush_cache();
-				if (*st == NOTRUNNING)
+			
+				CHECK_POSIX_DO( pthread_mutex_lock(&order_state_lock), );
+				terminated = (*st == NOTRUNNING);
+				CHECK_POSIX_DO( pthread_mutex_unlock(&order_state_lock), );
+				if (terminated)
 					break;
 				
 				usleep(100000);
--- a/libfdcore/sctp.c	Mon Jan 31 17:22:21 2011 +0900
+++ b/libfdcore/sctp.c	Wed Feb 09 15:26:58 2011 +0900
@@ -998,7 +998,7 @@
 }
 
 /* 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)
+int fd_sctp_sendstr(struct cnxctx * conn, uint16_t strid, uint8_t * buf, size_t len)
 {
 	struct msghdr mhdr;
 	struct iovec  iov;
@@ -1008,8 +1008,8 @@
 	ssize_t ret;
 	int timedout = 0;
 	
-	TRACE_ENTRY("%d %hu %p %zd %p", sock, strid, buf, len, cc_status);
-	CHECK_PARAMS(cc_status);
+	TRACE_ENTRY("%p %hu %p %zd", conn, strid, buf, len);
+	CHECK_PARAMS(conn && buf && len);
 	
 	memset(&mhdr, 0, sizeof(mhdr));
 	memset(&iov,  0, sizeof(iov));
@@ -1036,12 +1036,12 @@
 	mhdr.msg_control    = anci;
 	mhdr.msg_controllen = sizeof(anci);
 	
-	TRACE_DEBUG(FULL, "Sending %db data on stream %hu of socket %d", len, strid, sock);
+	TRACE_DEBUG(FULL, "Sending %db data on stream %hu of socket %d", len, strid, conn->cc_socket);
 again:	
-	ret = sendmsg(sock, &mhdr, 0);
+	ret = sendmsg(conn->cc_socket, &mhdr, 0);
 	/* Handle special case of timeout */
 	if ((ret < 0) && (errno == EAGAIN)) {
-		if (!(*cc_status & CC_STATUS_CLOSING))
+		if (! fd_cnx_teststate(conn, CC_STATUS_CLOSING ))
 			goto again; /* don't care, just ignore */
 		if (!timedout) {
 			timedout ++; /* allow for one timeout while closing */
@@ -1056,7 +1056,7 @@
 }
 
 /* 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)
+int fd_sctp_recvmeta(struct cnxctx * conn, uint16_t * strid, uint8_t ** buf, size_t * len, int *event)
 {
 	ssize_t 		 ret = 0;
 	struct msghdr 		 mhdr;
@@ -1067,8 +1067,8 @@
 	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 );
+	TRACE_ENTRY("%p %p %p %p %p", conn, strid, buf, len, event);
+	CHECK_PARAMS( conn && buf && len && event );
 	
 	/* Cleanup out parameters */
 	*buf = NULL;
@@ -1097,12 +1097,12 @@
 	/* Receive data from the socket */
 again:
 	pthread_cleanup_push(free, data);
-	ret = recvmsg(sock, &mhdr, 0);
+	ret = recvmsg(conn->cc_socket, &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))
+		if (! fd_cnx_teststate(conn, CC_STATUS_CLOSING ))
 			goto again; /* don't care, just ignore */
 		if (!timedout) {
 			timedout ++; /* allow for one timeout while closing */
@@ -1131,7 +1131,7 @@
 	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);
+		TRACE_DEBUG(FULL, "Received %db data of notification on socket %d", datasize, conn->cc_socket);
 	
 		switch (notif->sn_header.sn_type) {
 			
@@ -1225,9 +1225,9 @@
 
 			*strid = sndrcv->sinfo_stream;
 		}
-		TRACE_DEBUG(FULL, "Received %db data on socket %d, stream %hu", datasize, sock, *strid);
+		TRACE_DEBUG(FULL, "Received %db data on socket %d, stream %hu", datasize, conn->cc_socket, *strid);
 	} else {
-		TRACE_DEBUG(FULL, "Received %db data on socket %d (stream ignored)", datasize, sock);
+		TRACE_DEBUG(FULL, "Received %db data on socket %d (stream ignored)", datasize, conn->cc_socket);
 	}
 	
 	return 0;
--- a/libfdcore/sctps.c	Mon Jan 31 17:22:21 2011 +0900
+++ b/libfdcore/sctps.c	Wed Feb 09 15:26:58 2011 +0900
@@ -56,7 +56,7 @@
 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.
 */
 
-
+/* TODO: change this whole wrapper to DTLS which should not require many different threads */
 
 /*************************************************************/
 /*                      threads                              */
@@ -82,12 +82,11 @@
 	}
 	
 	ASSERT( conn->cc_proto == IPPROTO_SCTP );
-	ASSERT( Target_Queue(conn) );
+	ASSERT( fd_cnx_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 );
+		CHECK_FCT_DO( fd_sctp_recvmeta(conn, &strid, &buf, &bufsz, &event), 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 */
@@ -101,8 +100,7 @@
 				
 			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 );
+				CHECK_FCT_DO( fd_event_send( fd_cnx_target_queue(conn), event, bufsz, buf), goto fatal );
 				break;
 			
 			case FDEVP_CNX_ERROR:
@@ -143,7 +141,7 @@
 	TRACE_ENTRY("%p", arg);
 	CHECK_PARAMS_DO(ctx && ctx->raw_recv && ctx->parent, goto error);
 	cnx = ctx->parent;
-	ASSERT( Target_Queue(cnx) );
+	ASSERT( fd_cnx_target_queue(cnx) );
 	
 	/* Set the thread name */
 	{
@@ -172,8 +170,7 @@
 	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 );
+	CHECK_FCT_DO( fd_sctp_sendstr(ctx->parent, ctx->strid, (uint8_t *)data, len), /* errno is already set */ return -1 );
 	
 	return len;
 }
@@ -305,15 +302,7 @@
 		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 );
-		
+		cmp = fd_os_cmp(key.data, key.size, sr->key.data, sr->key.size);
 		if (cmp > 0)
 			continue;
 		
@@ -626,8 +615,7 @@
 	
 	/* 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)) {
+		if ( ! fd_cnx_teststate(conn, CC_STATUS_ERROR)) {
 			CHECK_GNUTLS_DO( gnutls_bye(conn->cc_sctps_data.array[i].session, GNUTLS_SHUT_WR), fd_cnx_markerror(conn) );
 		}
 	}
--- a/libfdcore/server.c	Mon Jan 31 17:22:21 2011 +0900
+++ b/libfdcore/server.c	Wed Feb 09 15:26:58 2011 +0900
@@ -40,6 +40,13 @@
 static 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 framework thread. */
 
+enum s_state {
+	NOT_CREATED=0,
+	RUNNING,
+	TERMINATED,
+	ERROR	/* an error occurred, this is not a valid status */
+};
+
 /* Servers information */
 struct server {
 	struct fd_list	chain;		/* link in the FD_SERVERS list */
@@ -49,12 +56,13 @@
 	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 */
+	enum s_state	state;		/* state of the thread */
 	
 	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 */
@@ -64,6 +72,26 @@
 };
 
 
+
+/* Micro functions to read/change the status thread-safely */
+static pthread_mutex_t s_lock = PTHREAD_MUTEX_INITIALIZER;
+static enum s_state get_status(struct server * s)
+{
+	enum s_state r;
+	CHECK_POSIX_DO( pthread_mutex_lock(&s_lock), return ERROR );
+	r = s->state;
+	CHECK_POSIX_DO( pthread_mutex_unlock(&s_lock), return ERROR );
+	return r;
+}
+static void set_status(struct server * s, enum s_state st)
+{
+	CHECK_POSIX_DO( pthread_mutex_lock(&s_lock), return );
+	s->state = st;
+	CHECK_POSIX_DO( pthread_mutex_unlock(&s_lock), return );
+}
+	
+
+
 /* Dump all servers information */
 void fd_servers_dump()
 {
@@ -72,14 +100,14 @@
 	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();
+		enum s_state st = get_status(s);
 		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" :
+				(st == NOT_CREATED) ? "Thread not created" :
+				((st == RUNNING) ? "Thread running" :
+				((st == TERMINATED) ? "Thread terminated" :
 							  "Thread status unknown")));
 		/* Dump the client list of this server */
 		CHECK_POSIX_DO( pthread_mutex_lock(&s->clients_mtx), );
@@ -191,8 +219,7 @@
 	
 	CHECK_PARAMS_DO(s, goto error);
 	fd_log_threadname ( fd_cnx_getid(s->conn) );
-	s->status = 1;
-	fd_cpu_flush_cache();
+	set_status(s, RUNNING);
 	
 	/* Accept incoming connections */
 	CHECK_FCT_DO( fd_cnx_serv_listen(s->conn), goto error );
@@ -224,7 +251,7 @@
 	
 error:	
 	if (s)
-		s->status = 2;
+		set_status(s, TERMINATED);
 	/* 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), );
@@ -265,16 +292,20 @@
 #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 ) );
+		if (fd_g_config->cnf_port) {
+			CHECK_MALLOC( s = new_serv(IPPROTO_SCTP, 0) );
+			CHECK_MALLOC( s->conn = fd_cnx_serv_sctp(fd_g_config->cnf_port, empty_conf_ep ? 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 ) );
+		if (fd_g_config->cnf_port_tls) {
+			CHECK_MALLOC( s = new_serv(IPPROTO_SCTP, 1) );
+			CHECK_MALLOC( s->conn = fd_cnx_serv_sctp(fd_g_config->cnf_port_tls, empty_conf_ep ? 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 */
 	}
@@ -286,28 +317,37 @@
 			/* 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 ) );
+				if (fd_g_config->cnf_port) {
+					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 ) );
+				if (fd_g_config->cnf_port_tls) {
+					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 ) );
+				if (fd_g_config->cnf_port) {
+					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 ) );
+				}
+
+				if (fd_g_config->cnf_port_tls) {
+					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 */
@@ -322,24 +362,30 @@
 				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 ) );
+				if (fd_g_config->cnf_port) {
+					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 ) );
+				if (fd_g_config->cnf_port_tls) {
+					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)) {
+	/* Now, if we had an empty list of local adresses (no address configured), try to read the real addresses from the kernel */
+	if (empty_conf_ep) {
 		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.");
+			TRACE_DEBUG(INFO, "Unable to find the address(es) of the local system.\n" 
+					"Please use \"ListenOn\" parameter in the configuration.\n"
+					"This information is required to generate the CER/CEA messages.\n");
 			return EINVAL;
 		}
 	}
--- a/libfdproto/CMakeLists.txt	Mon Jan 31 17:22:21 2011 +0900
+++ b/libfdproto/CMakeLists.txt	Wed Feb 09 15:26:58 2011 +0900
@@ -12,6 +12,7 @@
 	log.c
 	messages.c
 	msg_log.c
+	ostr.c
 	rt_data.c
 	sessions.c
 	)
--- a/libfdproto/dictionary.c	Mon Jan 31 17:22:21 2011 +0900
+++ b/libfdproto/dictionary.c	Wed Feb 09 15:26:58 2011 +0900
@@ -62,15 +62,17 @@
 	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;
+		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 */
 	
 	struct fd_list		list[NB_LISTS_PER_OBJ];/* used to chain objects.*/
@@ -87,7 +89,7 @@
 	 => 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.
+	 list[2]: sentinel for the list of AVPs from this vendor, ordered by AVP name (fd_os_cmp).
 	 
 	 => APPLICATIONS:
 	 list[0]: list of the applications, ordered by their id. The sentinel is g_dict_applications (application with id 0)
@@ -96,26 +98,26 @@
 	 
 	 => 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
 	 
 	 => 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.
 	 
@@ -222,10 +224,10 @@
 
 /* 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));		\
 }
 	
 /* Initialize an object */
@@ -258,39 +260,39 @@
 }
 
 /* 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);
 	CHECK_PARAMS( dest && source && CHECK_TYPE(type) );
 	
 	/* 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 */
 	/* 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 );
+			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;
 		
 		default:
@@ -455,7 +457,7 @@
 {
 	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 );
 }
 
 /* Compare two type_enum objects by their names (checks already performed) */
@@ -463,25 +465,19 @@
 {
 	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 );
 }
 
 /* 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));
+			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:
 			return ORDER_scalar( o1->data.enumval.enum_value.i32, o2->data.enumval.enum_value.i32 );
@@ -521,7 +517,7 @@
 {
 	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 );
 }
 
 /* Compare two command objects by their names (checks already performed) */
@@ -529,7 +525,7 @@
 {
 	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 );
 }
 
 /* Compare two command objects by their codes and flags (request or answer) (checks already performed) */
@@ -554,7 +550,7 @@
 }
 
 /* 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);
 	
@@ -589,13 +585,16 @@
 }
 
 /* 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)						\
 				*result = _O(__li->o);				\
@@ -610,27 +609,28 @@
 		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;			\
+/* 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;						\
 		}								\
 		if ((isindex) && (__cmp < 0))					\
@@ -643,17 +643,19 @@
 }
 
 /* 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;						\
 		}								\
 		if (__cmp < 0)							\
@@ -676,11 +678,11 @@
 			*result = _O(defaultobj);				\
 		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;						\
 		}								\
 		if ((isindex) && (__cmp < 0))					\
@@ -697,21 +699,19 @@
 	int __cmp;								\
 	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;					\
 			if ( ( __val & CMD_FLAG_REQUEST ) != R_flag_val )	\
 				continue;					\
 			if (result)						\
-				*result = _O(__li->next->o);			\
+				*result = _O(__li->o);				\
 			goto end;						\
 		}								\
 		if (__cmp < 0)							\
@@ -738,7 +738,7 @@
 				
 		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;
 			
 		case VENDOR_OF_APPLICATION:
@@ -770,7 +770,7 @@
 				
 		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;
 			
 		case APPLICATION_OF_TYPE:
@@ -800,7 +800,7 @@
 	switch (criteria) {
 		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;
 			
 		case TYPE_OF_ENUMVAL:
@@ -849,12 +849,12 @@
 				
 				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], 
 									 enumval.enum_value.os , 
@@ -945,7 +945,7 @@
 				
 		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;
 			
 		case AVP_BY_CODE_AND_VENDOR:
@@ -968,10 +968,10 @@
 				
 				/* 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 );
 				}
 			}
 			break;
@@ -979,13 +979,14 @@
 		case AVP_BY_NAME_ALL_VENDORS:
 			{
 				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);
 				}
 			}
 			break;
@@ -1007,7 +1008,7 @@
 	switch (criteria) {
 		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;
 			
 		case CMD_BY_CODE_R:
@@ -1095,7 +1096,7 @@
 				CHECK_PARAMS( verify_object(avp) && (avp->type == DICT_AVP) );
 				
 				/* 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]);
 				
 			}
 			break;
@@ -1273,23 +1274,23 @@
 	
 	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++)
 		fd_log_debug(" %5d objects of type %s\n", dict->dict_count[i], dict_obj_info[i].name);
@@ -1560,7 +1561,7 @@
 	
 	/* 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;
 	
@@ -1631,7 +1632,7 @@
 		
 		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;
 			break;
@@ -1658,8 +1659,9 @@
 		/* 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)) {
+				/* 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;
 				}
@@ -1669,7 +1671,8 @@
 
 			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;
 				}
@@ -1762,7 +1765,7 @@
 				break;
 
 			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");
 					break;
@@ -1846,7 +1849,7 @@
 {
 	struct dictionary * new = NULL;
 	
-	TRACE_ENTRY("");
+	TRACE_ENTRY("%p", dict);
 	
 	/* Sanity checks */
 	ASSERT( (sizeof(type_base_name) / sizeof(type_base_name[0])) == (AVP_TYPE_MAX + 1) );
@@ -1864,13 +1867,17 @@
 	
 	/* 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;
 	
 	/* 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;
 			
@@ -1883,7 +1890,9 @@
 	
 	/* 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;
 	new->dict_cmd_error.dico = new;
--- a/libfdproto/fdproto-internal.h	Mon Jan 31 17:22:21 2011 +0900
+++ b/libfdproto/fdproto-internal.h	Wed Feb 09 15:26:58 2011 +0900
@@ -64,7 +64,7 @@
 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_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 );
 
--- a/libfdproto/init.c	Mon Jan 31 17:22:21 2011 +0900
+++ b/libfdproto/init.c	Wed Feb 09 15:26:58 2011 +0900
@@ -35,9 +35,6 @@
 
 #include "fdproto-internal.h"
 
-/* Only for CPU cache flush */
-pthread_mutex_t fd_cpu_mtx_dummy = PTHREAD_MUTEX_INITIALIZER;
-
 /* function to free the threadnames */
 static void freelogstr(void * str) {
 	if (TRACE_BOOL(ANNOYING)) {
--- a/libfdproto/lists.c	Mon Jan 31 17:22:21 2011 +0900
+++ b/libfdproto/lists.c	Wed Feb 09 15:26:58 2011 +0900
@@ -162,129 +162,3 @@
 }
 
 
-/********************************************************************************************************/
-/* 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;
-} 
--- a/libfdproto/log.c	Mon Jan 31 17:22:21 2011 +0900
+++ b/libfdproto/log.c	Wed Feb 09 15:26:58 2011 +0900
@@ -82,8 +82,14 @@
 	
 	/* 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);
 	}
 	
--- a/libfdproto/messages.c	Mon Jan 31 17:22:21 2011 +0900
+++ b/libfdproto/messages.c	Wed Feb 09 15:26:58 2011 +0900
@@ -125,7 +125,8 @@
 			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 */
+	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 */
 };
 
 /* Macro to compute the message header size */
@@ -331,15 +332,16 @@
 	/* Add the Session-Id AVP if session is known */
 	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 ) );
 		ans->msg_sess = sess;
@@ -702,8 +704,8 @@
 		msg->msg_public.msg_hbhid,
 		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);
 }
 
 /* Dump an avp object */
@@ -1011,16 +1013,21 @@
 	return (msg->msg_routable == 1) ? 1 : 0;
 }
 
+/* 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 )
+int fd_msg_source_set( struct msg * msg, DiamId_t diamid, size_t diamidlen, int add_rr, struct dictionary * dict )
 {
-	TRACE_ENTRY( "%p %p %d %p", msg, diamid, add_rr, dict);
+	TRACE_ENTRY( "%p %p %zd %d %p", msg, diamid, diamidlen, 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;
+	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 */
 	if (diamid == NULL) {
@@ -1028,24 +1035,42 @@
 	}
 	
 	/* 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 */
 		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);
+		val.os.data = (uint8_t *)diamid;
+		val.os.len  = diamidlen;
 		CHECK_FCT( fd_msg_avp_setvalue( avp, &val ) );
 
 		/* Add the AVP in the message */
@@ -1056,9 +1081,9 @@
 	return 0;
 }
 
-int fd_msg_source_get( struct msg * msg, char ** diamid )
+int fd_msg_source_get( struct msg * msg, DiamId_t* diamid, size_t * diamidlen )
 {
-	TRACE_ENTRY( "%p %p", msg, diamid);
+	TRACE_ENTRY( "%p %p %p", msg, diamid, diamidlen);
 	
 	/* Check we received valid parameters */
 	CHECK_PARAMS( CHECK_MSG(msg) );
@@ -1067,6 +1092,9 @@
 	/* Copy the informations */
 	*diamid = msg->msg_src_id;
 	
+	if (diamidlen)
+		*diamidlen = msg->msg_src_id_len;
+	
 	/* done */
 	return 0;
 }
@@ -2224,7 +2252,7 @@
 		goto out;
 
 /* 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;
 	struct dict_object * app;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libfdproto/ostr.c	Wed Feb 09 15:26:58 2011 +0900
@@ -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;
+} 
+
--- a/libfdproto/rt_data.c	Mon Jan 31 17:22:21 2011 +0900
+++ b/libfdproto/rt_data.c	Wed Feb 09 15:26:58 2011 +0900
@@ -49,9 +49,11 @@
 
 /* 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 */
 };
 
@@ -106,19 +108,19 @@
 	return;
 }
 
-/* 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;
 		if (cmp == 0)
@@ -126,41 +128,39 @@
 			return 0;
 	}
 	
+	/* 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);
 	
 	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 */)
+/* 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) {
 			/* Found it! Remove it */
@@ -174,7 +174,7 @@
 		if (cmp > 0)
 			continue;
 		
-		/* The list is ordered only if not extracted */
+		/* The list is guaranteed to be ordered only if not extracted */
 		if (! rtd->extracted)
 			break;
 	}
@@ -182,19 +182,20 @@
 	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)
+/* 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;
 		if (!cmp)
@@ -202,27 +203,40 @@
 		break;
 	}
 	
-	/* 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 */
 		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));
+
+		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);
 	}
 	
 	/* 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! */
 	return 0;
@@ -273,7 +287,7 @@
 			/* 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 */
+			/* And the new high score is set */
 			hs = c->score;
 		}
 		
--- a/libfdproto/sessions.c	Mon Jan 31 17:22:21 2011 +0900
+++ b/libfdproto/sessions.c	Wed Feb 09 15:26:58 2011 +0900
@@ -68,7 +68,7 @@
 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 		(*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 */
 };
 
@@ -83,7 +83,7 @@
 	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 */
+		os0_t 			 sid;	/* For deleted state, the sid of the session it belong to */
 	};
 };
 
@@ -91,7 +91,8 @@
 struct session {
 	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. */
 	
@@ -101,11 +102,12 @@
 	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 */
+	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 ] ;
 #define H_MASK( __hash ) ((__hash) & (( 1 << SESS_HASH_SIZE ) - 1))
@@ -131,12 +133,12 @@
 
 /********************************************************************************************************/
 
-/* 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 );
 	
 	CHECK_MALLOC_DO( sess = malloc(sizeof(struct session)), return NULL );
@@ -145,7 +147,8 @@
 	sess->eyec = SI_EYEC;
 	
 	sess->sid  = sid;
-	sess->hash = fd_hash(sid, sidlen);
+	sess->sidlen = sidlen;
+	sess->hash = hash;
 	fd_list_init(&sess->chain_h, sess);
 	
 	CHECK_SYS_DO( clock_gettime(CLOCK_REALTIME, &sess->timeout), return NULL );
@@ -157,6 +160,17 @@
 	
 	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);
+}
 	
 /* The expiry thread */
 static void * exp_fct(void * arg)
@@ -249,11 +263,14 @@
 {
 	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;
 	
@@ -298,11 +315,11 @@
 		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) {
+		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 */
 				if (st->hdl->id < del->id)
@@ -310,7 +327,7 @@
 				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) );
+					st->sid = sess->sid;
 					fd_list_insert_before(&deleted_states, &st->chain);
 				}
 				break;
@@ -325,7 +342,6 @@
 		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);
 	}
@@ -342,37 +358,51 @@
 
 
 /* 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 )
+int fd_sess_new ( struct session ** session, DiamId_t diamid, size_t diamidlen, uint8_t * opt, size_t optlen )
 {
-	char * sid = NULL;
+	os0_t  sid = NULL;
 	size_t sidlen;
+	uint32_t hash;
 	struct session * sess;
 	struct fd_list * li;
 	int found = 0;
+	int ret = 0;
 	
-	TRACE_ENTRY("%p %p %p %d", session, diamId, opt, optlen);
-	CHECK_PARAMS( session && (diamId || opt) );
-	
+	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) );
 		
@@ -384,34 +414,29 @@
 		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);
+			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) );
+	CHECK_POSIX( pthread_mutex_lock( H_LOCK(hash) ) );
+	pthread_cleanup_push( fd_cleanup_mutex, H_LOCK(hash) );
 	
-	for (li = H_LIST(sess->hash)->next; li != H_LIST(sess->hash); li = li->next) {
+	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;
 		if (cmp > 0)
@@ -423,66 +448,78 @@
 		break;
 	}
 	
-	/* 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) ) );
+	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 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;
-	}
+	if (ret) /* in case of error */
+		return ret;
 	
 	*session = sess;
 	return 0;
 }
 
 /* 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;
 	
 	TRACE_ENTRY("%p %d %p %p", sid, len, session, new);
 	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:
 		case EALREADY:
@@ -499,13 +536,15 @@
 }
 
 /* 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);
 	
 	CHECK_PARAMS( VALIDATE_SI(session) && sid );
 	
 	*sid = session->sid;
+	if (sidlen)
+		*sidlen = session->sidlen;
 	
 	return 0;
 }
@@ -542,17 +581,6 @@
 		CHECK_POSIX_DO( pthread_cond_signal(&exp_cond), { ASSERT(0); /* so that we don't have a pending cancellation handler */ } );
 	}
 
-	#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 ) );
@@ -560,10 +588,16 @@
 	return 0;
 }
 
-/* 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);
 	CHECK_PARAMS( session && VALIDATE_SI(*session) );
@@ -571,29 +605,54 @@
 	sess = *session;
 	*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 */ } );
+	
+	/* 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);
+		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) ) );
 	
-	/* 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);
+	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 registered with session '%s'", st->hdl, sess->sid);
-		(*st->hdl->cleanup)(st->state, sess->sid, st->hdl->opaque);
+		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;
 }
@@ -603,6 +662,7 @@
 {
 	struct session * sess;
 	uint32_t hash;
+	int destroy_now = 0;
 	
 	TRACE_ENTRY("%p", session);
 	CHECK_PARAMS( session && VALIDATE_SI(*session) );
@@ -611,20 +671,34 @@
 	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( 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;
 }
 
@@ -637,7 +711,7 @@
 	int ret = 0;
 	
 	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 */
 	CHECK_POSIX( pthread_mutex_lock(&session->stlock) );
@@ -719,13 +793,13 @@
 }
 
 /* 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);
 	CHECK_PARAMS( sid && len && session );
 	
 	/* 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 */
 	CHECK_FCT( fd_sess_ref_msg ( *session ) );
@@ -785,7 +859,7 @@
 		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);
+		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 ));
 		fd_log_debug("\t  %*s  timeout %s.%09ld\n", level, "", buf, session->timeout.tv_nsec);
--- a/tests/CMakeLists.txt	Mon Jan 31 17:22:21 2011 +0900
+++ b/tests/CMakeLists.txt	Wed Feb 09 15:26:58 2011 +0900
@@ -12,7 +12,7 @@
 # List the test cases
 SET(TEST_LIST
 	testsctp
-	testlist
+	testostr
 	testdict
 	testmesg
 	testfifo
--- a/tests/testdisp.c	Mon Jan 31 17:22:21 2011 +0900
+++ b/tests/testdisp.c	Wed Feb 09 15:26:58 2011 +0900
@@ -109,13 +109,14 @@
 	enum disp_action action;
 	struct disp_hdl * hdl[NB_CB];
 	struct disp_when when;
-	const char * ec;
+	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 ) );
+	#define DUMMY_SID "test.disp"
+	CHECK( 0, fd_sess_new( &sess, DUMMY_SID, CONSTSTRLEN(DUMMY_SID), NULL, 0 ) );
 	
 	memset(&when, 0xff, sizeof(when)); /* check that we don't use un-initialized parts */
 	
--- a/tests/testlist.c	Mon Jan 31 17:22:21 2011 +0900
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,74 +0,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 "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();
-} 
-	
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/testostr.c	Wed Feb 09 15:26:58 2011 +0900
@@ -0,0 +1,73 @@
+/*********************************************************************************************************
+* 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 "tests.h"
+
+#define TEST_STR (os0_t)"This is my test string (with extra unused data)"
+
+/* Main test routine */
+int main(int argc, char *argv[])
+{
+	/* First, initialize the daemon modules */
+	INIT_FD();
+	
+	/* Check the hash function */
+	{
+		uint8_t buf[30];
+		
+		uint32_t hash = fd_os_hash(TEST_STR, CONSTSTRLEN(TEST_STR)); /* reference value */
+		
+		/* Check that a hash of a substring / surstring is different */
+		CHECK( 1, hash != fd_os_hash(TEST_STR, CONSTSTRLEN(TEST_STR) - 1) ? 1 : 0 );
+		CHECK( 1, hash != fd_os_hash(TEST_STR, CONSTSTRLEN(TEST_STR) + 1) ? 1 : 0 );
+		
+		/* Check alignment of the string is not important */
+		memcpy(buf + 4, TEST_STR, CONSTSTRLEN(TEST_STR));
+		CHECK( hash, fd_os_hash(buf + 4, CONSTSTRLEN(TEST_STR)) );
+		
+		memcpy(buf + 3, TEST_STR, CONSTSTRLEN(TEST_STR));
+		CHECK( hash, fd_os_hash(buf + 3, CONSTSTRLEN(TEST_STR)) );
+		
+		memcpy(buf + 2, TEST_STR, CONSTSTRLEN(TEST_STR));
+		CHECK( hash, fd_os_hash(buf + 2, CONSTSTRLEN(TEST_STR)) );
+		
+		memcpy(buf + 1, TEST_STR, CONSTSTRLEN(TEST_STR));
+		CHECK( hash, fd_os_hash(buf + 1, CONSTSTRLEN(TEST_STR)) );
+	}
+
+	/* That's all for the tests yet */
+	PASSTEST();
+} 
+	
--- a/tests/testsctp.c	Mon Jan 31 17:22:21 2011 +0900
+++ b/tests/testsctp.c	Wed Feb 09 15:26:58 2011 +0900
@@ -55,12 +55,12 @@
 	/* In this case, we don't perform this simple test */
 	PASSTEST();
 #else /* DISABLE_SCTP */
-	int sock, srvsock, clisock;
+	struct cnxctx cli, srv; /* we use only their cc_socket & cc_state */
+	int sock;
 	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;
 	
@@ -79,6 +79,9 @@
 		freeaddrinfo(ai);
 	}
 	
+	memset(&cli, 0, sizeof(cli));
+	memset(&srv, 0, sizeof(srv));
+	
 	/* First, initialize the daemon modules */
 	INIT_FD();
 	
@@ -92,38 +95,38 @@
 	CHECK( 0, fd_sctp_listen( sock ));
 	
 	/* Now, create the client socket */
-	CHECK( 0, fd_sctp_client( &clisock, 0, TEST_PORT, &eps ));
+	CHECK( 0, fd_sctp_client( &cli.cc_socket, 0, TEST_PORT, &eps ));
 	
 	/* Accept this connection */
-	srvsock = accept(sock, NULL, NULL);
+	srv.cc_socket = accept(sock, NULL, NULL);
 	
 	/* Send a first message */
-	CHECK( 0, fd_sctp_sendstr(srvsock, 1, (uint8_t *)buf1, sizeof(buf1), &status) );
-	CHECK( 0, status);
+	CHECK( 0, fd_sctp_sendstr(&srv, 1, (uint8_t *)buf1, sizeof(buf1) ) );
+	CHECK( 0, srv.cc_state);
 	
 	/* Receive this message */
 redo1:
-	CHECK( 0, fd_sctp_recvmeta(clisock, &str, (uint8_t **)&buf2, &sz, &ev, &status) );
+	CHECK( 0, fd_sctp_recvmeta(&cli, &str, (uint8_t **)&buf2, &sz, &ev) );
 	if (ev == FDEVP_CNX_EP_CHANGE)
 		goto redo1;
 	CHECK( FDEVP_CNX_MSG_RECV, ev);
-	CHECK( 0, status);
+	CHECK( 0, cli.cc_state);
 	CHECK( 1, str);
 	CHECK( sizeof(buf1), sz );
 	CHECK( 0, memcmp(buf1, buf2, sz) );
 	free(buf2); buf2 = NULL;
 	
 	/* Send in the other direction */
-	CHECK( 0, fd_sctp_sendstr(clisock, 2, (uint8_t *)buf1, sizeof(buf1), &status) );
-	CHECK( 0, status);
+	CHECK( 0, fd_sctp_sendstr(&cli, 2, (uint8_t *)buf1, sizeof(buf1)) );
+	CHECK( 0, cli.cc_state);
 	
 	/* Receive this message */
 redo2:
-	CHECK( 0, fd_sctp_recvmeta(srvsock, &str, (uint8_t **)&buf2, &sz, &ev, &status) );
+	CHECK( 0, fd_sctp_recvmeta(&srv, &str, (uint8_t **)&buf2, &sz, &ev) );
 	if (ev == FDEVP_CNX_EP_CHANGE)
 		goto redo2;
 	CHECK( FDEVP_CNX_MSG_RECV, ev);
-	CHECK( 0, status);
+	CHECK( 0, srv.cc_state);
 	CHECK( 2, str);
 	CHECK( sizeof(buf1), sz );
 	CHECK( 0, memcmp(buf1, buf2, sz) );
--- a/tests/testsess.c	Mon Jan 31 17:22:21 2011 +0900
+++ b/tests/testsess.c	Wed Feb 09 15:26:58 2011 +0900
@@ -36,24 +36,26 @@
 #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_OPT_IN	"suffix"
+#define TEST_OPT	(os0_t)TEST_OPT_IN
+#define TEST_SID_IN	TEST_DIAM_ID ";1234;5678;" TEST_OPT_IN
+#define TEST_SID	(os0_t)TEST_SID_IN
 
 #define TEST_EYEC	0x7e57e1ec
 struct mystate {
 	int	eyec;	/* TEST_EYEC */
-	char *  sid; 	/* the session with which the data was registered */
+	os0_t   sid; 	/* the session with which the data was registered */
 	int  *  freed;	/* location where to write the freed status */
 	void *  opaque; /* if opaque was provided, this is the value we expect */
 };
 
-static void mycleanup( struct mystate * data, char * sid, void * opaque )
+static void mycleanup( struct mystate * data, os0_t sid, void * opaque )
 {
 	/* sanity */
 	CHECK( 1, sid ? 1 : 0 );
 	CHECK( 1, data? 1 : 0 );
 	CHECK( TEST_EYEC, data->eyec );
-	CHECK( 0, strcmp(sid, data->sid) );
+	CHECK( 0, strcmp((char *)sid, (char *)data->sid) );
 	if (data->freed)
 		*(data->freed) += 1;
 	if (data->opaque) {
@@ -64,20 +66,25 @@
 	free(data);
 }
 
-static __inline__ struct mystate * new_state(char * sid, int *freed) 
+static __inline__ struct mystate * new_state(os0_t 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);
+	new->sid = os0dup(sid, strlen((char *)sid));
 	CHECK( 1, new->sid ? 1 : 0 );
 	new->freed = freed;
 	return new;
 }
 
 void * g_opaque = (void *)"test";
+
+/* Avoid a lot of casts */
+#define strlen(s) strlen((char *)s)
+#define strncmp(s1,s2,l) strncmp((char *)s1, (char *)s2, l)
+#define strcmp(s1,s2) strcmp((char *)s1, (char *)s2)
 	
 
 /* Main test routine */
@@ -85,7 +92,8 @@
 {
 	struct session_handler * hdl1, *hdl2;
 	struct session *sess1, *sess2, *sess3;
-	char *str1, *str2;
+	os0_t str1, str2;
+	size_t str1len, str2len;
 	int new;
 	
 	/* First, initialize the daemon modules */
@@ -108,57 +116,59 @@
 	/* 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 ) );
+		CHECK( 0, fd_sess_new( &sess1, TEST_DIAM_ID, CONSTSTRLEN(TEST_DIAM_ID), NULL, 0 ) );
+		CHECK( 0, fd_sess_new( &sess2, TEST_DIAM_ID, CONSTSTRLEN(TEST_DIAM_ID), NULL, 0 ) );
 		#if 0
 		fd_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( 0, fd_sess_getsid(sess1, &str1, &str1len) );
+		CHECK( 1, (strlen(str1) == str1len) ? 1 : 0 );
+		CHECK( 0, strncmp(str1, TEST_DIAM_ID ";", CONSTSTRLEN(TEST_DIAM_ID) + 1) );
+		CHECK( 0, fd_sess_getsid(sess2, &str2, &str2len) );
+		CHECK( 0, strncmp(str2, TEST_DIAM_ID ";", CONSTSTRLEN(TEST_DIAM_ID) + 1) );
 		CHECK( 1, strcmp(str1, str2) ? 1 : 0 );
 		CHECK( 0, fd_sess_destroy( &sess1 ) );
 		CHECK( 0, fd_sess_destroy( &sess2 ) );
 		
 		/* diamId and opt */
-		CHECK( 0, fd_sess_new( &sess1, TEST_DIAM_ID, TEST_OPT, 0 ) );
-		CHECK( 0, fd_sess_new( &sess2, TEST_DIAM_ID, TEST_OPT, strlen(TEST_OPT) - 1 ) );
+		CHECK( 0, fd_sess_new( &sess1, TEST_DIAM_ID, 0, TEST_OPT, 0 ) );
+		CHECK( 0, fd_sess_new( &sess2, TEST_DIAM_ID, CONSTSTRLEN(TEST_DIAM_ID), TEST_OPT, CONSTSTRLEN(TEST_OPT_IN) - 1 ) );
 		#if 0
 		fd_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(sess1, &str1, &str1len) );
+		CHECK( 0, strncmp(str1, TEST_DIAM_ID ";", CONSTSTRLEN(TEST_DIAM_ID) + 1) );
+		CHECK( 0, strcmp(str1 + str1len - CONSTSTRLEN(TEST_OPT_IN) - 1, ";" TEST_OPT_IN) );
 		
-		CHECK( 0, fd_sess_getsid(sess2, &str2) );
-		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( 0, fd_sess_getsid(sess2, &str2, &str2len) );
+		CHECK( 0, strncmp(str2, TEST_DIAM_ID ";", CONSTSTRLEN(TEST_DIAM_ID) + 1) );
+		CHECK( 0, strncmp(str2 + str2len - CONSTSTRLEN(TEST_OPT_IN), ";" TEST_OPT_IN, CONSTSTRLEN(TEST_OPT_IN)) );
 		
 		CHECK( 1, strcmp(str1, str2) ? 1 : 0 );
 		CHECK( 0, fd_sess_destroy( &sess1 ) );
 		CHECK( 0, fd_sess_destroy( &sess2 ) );
 		
 		/* Now, only opt is provided */
-		CHECK( 0, fd_sess_new( &sess1, NULL, TEST_SID, 0 ) );
-		CHECK( EALREADY, fd_sess_new( &sess2, NULL, TEST_SID, 0 ) );
+		CHECK( 0, fd_sess_new( &sess1, NULL, 0, TEST_SID, 0 ) );
+		CHECK( EALREADY, fd_sess_new( &sess2, NULL, 0, TEST_SID, 0 ) );
 		CHECK( sess2, sess1 );
-		CHECK( EALREADY, fd_sess_new( &sess3, NULL, TEST_SID, strlen(TEST_SID) ) );
+		CHECK( EALREADY, fd_sess_new( &sess3, NULL, 0, TEST_SID, CONSTSTRLEN(TEST_SID_IN) ) );
 		CHECK( sess3, sess1 );
-		CHECK( 0, fd_sess_new( &sess2, NULL, TEST_SID, strlen(TEST_SID) - 1 ) );
+		CHECK( 0, fd_sess_new( &sess2, NULL, 0, TEST_SID, CONSTSTRLEN(TEST_SID_IN) - 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, fd_sess_getsid(sess1, &str1, &str1len) );
+		CHECK( 0, fd_sess_getsid(sess2, &str2, &str2len) );
+		CHECK( 0, strncmp( str1, str2, CONSTSTRLEN(TEST_SID_IN) - 1 ) );
 		CHECK( 0, strcmp( str1, TEST_SID ) );
+		CHECK( 1, strcmp( str1, str2 ) ? 1 : 0 );
 		
 		CHECK( 0, fd_sess_destroy( &sess2 ) );
 		CHECK( 0, fd_sess_destroy( &sess1 ) );
@@ -166,14 +176,14 @@
 		
 	/* Test fd_sess_fromsid */
 	{
-		CHECK( 0, fd_sess_fromsid( TEST_SID, strlen(TEST_SID), &sess1, &new ) );
+		CHECK( 0, fd_sess_fromsid( TEST_SID, CONSTSTRLEN(TEST_SID_IN), &sess1, &new ) );
 		CHECK( 1, new ? 1 : 0 );
 		
-		CHECK( 0, fd_sess_fromsid( TEST_SID, strlen(TEST_SID), &sess2, &new ) );
+		CHECK( 0, fd_sess_fromsid( TEST_SID, CONSTSTRLEN(TEST_SID_IN), &sess2, &new ) );
 		CHECK( 0, new );
 		CHECK( sess1, sess2 );
 		
-		CHECK( 0, fd_sess_fromsid( TEST_SID, strlen(TEST_SID), &sess3, NULL ) );
+		CHECK( 0, fd_sess_fromsid( TEST_SID, CONSTSTRLEN(TEST_SID_IN), &sess3, NULL ) );
 		CHECK( sess1, sess3 );
 		
 		CHECK( 0, fd_sess_destroy( &sess1 ) );
@@ -183,13 +193,13 @@
 	{
 		struct mystate *tms;
 		
-		CHECK( 0, fd_sess_fromsid( TEST_SID, strlen(TEST_SID), &sess1, &new ) );
+		CHECK( 0, fd_sess_fromsid( TEST_SID, CONSTSTRLEN(TEST_SID_IN), &sess1, &new ) );
 		CHECK( 1, new ? 1 : 0 );
 		
 		CHECK( 0, fd_sess_reclaim( &sess1 ) );
 		CHECK( NULL, sess1 );
 		
-		CHECK( 0, fd_sess_fromsid( TEST_SID, strlen(TEST_SID), &sess1, &new ) );
+		CHECK( 0, fd_sess_fromsid( TEST_SID, CONSTSTRLEN(TEST_SID_IN), &sess1, &new ) );
 		CHECK( 1, new ? 1 : 0 );
 		
 		tms = new_state(TEST_SID, NULL);
@@ -198,12 +208,12 @@
 		CHECK( 0, fd_sess_reclaim( &sess1 ) );
 		CHECK( NULL, sess1 );
 		
-		CHECK( 0, fd_sess_fromsid( TEST_SID, strlen(TEST_SID), &sess1, &new ) );
+		CHECK( 0, fd_sess_fromsid( TEST_SID, CONSTSTRLEN(TEST_SID_IN), &sess1, &new ) );
 		CHECK( 0, new );
 		
 		CHECK( 0, fd_sess_destroy( &sess1 ) );
 		
-		CHECK( 0, fd_sess_fromsid( TEST_SID, strlen(TEST_SID), &sess1, &new ) );
+		CHECK( 0, fd_sess_fromsid( TEST_SID, CONSTSTRLEN(TEST_SID_IN), &sess1, &new ) );
 		CHECK( 1, new ? 1 : 0 );
 		
 		CHECK( 0, fd_sess_destroy( &sess1 ) );
@@ -213,16 +223,16 @@
 	{
 		struct timespec timeout;
 		
-		CHECK( 0, fd_sess_fromsid( TEST_SID, strlen(TEST_SID), &sess1, &new ) );
+		CHECK( 0, fd_sess_fromsid( TEST_SID, CONSTSTRLEN(TEST_SID_IN), &sess1, &new ) );
 		CHECK( 1, new ? 1 : 0 );
 		
 		CHECK( 0, clock_gettime(CLOCK_REALTIME, &timeout) );
-		CHECK( 0, fd_sess_settimeout( sess1, &timeout) );
+		CHECK( 0, fd_sess_settimeout( sess1, &timeout) ); /* expire now */
 		timeout.tv_sec = 0;
 		timeout.tv_nsec= 50000000; /* 50 ms */
 		CHECK( 0, nanosleep(&timeout, NULL) );
 		
-		CHECK( 0, fd_sess_fromsid( TEST_SID, strlen(TEST_SID), &sess1, &new ) );
+		CHECK( 0, fd_sess_fromsid( TEST_SID, CONSTSTRLEN(TEST_SID_IN), &sess1, &new ) );
 		CHECK( 1, new ? 1 : 0 );
 		
 		CHECK( 0, clock_gettime(CLOCK_REALTIME, &timeout) );
@@ -230,9 +240,9 @@
 		CHECK( 0, fd_sess_settimeout( sess1, &timeout) );
 		
 		/* Create a second session */
-		CHECK( 0, fd_sess_new( &sess2, TEST_DIAM_ID, NULL, 0 ) );
+		CHECK( 0, fd_sess_new( &sess2, TEST_DIAM_ID, 0, NULL, 0 ) );
 		
-		/* We don't really have away to verify the expiry list is in proper order automatically here... */
+		/* We don't really have a way to verify the expiry list is in proper order automatically here... */
 		
 		CHECK( 0, fd_sess_destroy( &sess2 ) );
 		CHECK( 0, fd_sess_destroy( &sess1 ) );
@@ -247,12 +257,12 @@
 		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 ) );
+		CHECK( 0, fd_sess_new( &sess1, TEST_DIAM_ID, 0, NULL, 0 ) );
+		CHECK( 0, fd_sess_new( &sess2, TEST_DIAM_ID, 0, NULL, 0 ) );
+		CHECK( 0, fd_sess_new( &sess3, TEST_DIAM_ID, 0, NULL, 0 ) );
 		
 		/* Create 2 states */
-		CHECK( 0, fd_sess_getsid(sess1, &str1) );
+		CHECK( 0, fd_sess_getsid(sess1, &str1, &str1len) );
 		freed[0] = 0;
 		ms[0] = new_state(str1, &freed[0]);
 		ms[1] = new_state(str1, NULL);
@@ -280,17 +290,17 @@
 		
 		/* Now create 6 states */
 		memset(&freed[0], 0, sizeof(freed));
-		CHECK( 0, fd_sess_getsid(sess1, &str1) );
+		CHECK( 0, fd_sess_getsid(sess1, &str1, &str1len) );
 		ms[0] = new_state(str1, &freed[0]);
 		ms[1] = new_state(str1, &freed[1]);
-		CHECK( 0, fd_sess_getsid(sess2, &str1) );
+		CHECK( 0, fd_sess_getsid(sess2, &str1, &str1len) );
 		ms[2] = new_state(str1, &freed[2]);
 		ms[3] = new_state(str1, &freed[3]);
-		CHECK( 0, fd_sess_getsid(sess3, &str1) );
+		CHECK( 0, fd_sess_getsid(sess3, &str1, &str1len) );
 		ms[4] = new_state(str1, &freed[4]);
 		ms[5] = new_state(str1, &freed[5]);
 		ms[5]->opaque = g_opaque;
-		str2 = strdup(str1);
+		str2 = os0dup(str1, str1len);
 		CHECK( 1, str2 ? 1 : 0 );
 		
 		/* Store the six states */
@@ -332,7 +342,7 @@
 		#endif
 		
 		/* Create again session 3, check that no data is associated to it */
-		CHECK( 0, fd_sess_fromsid( str2, strlen(str2), &sess3, &new ) );
+		CHECK( 0, fd_sess_fromsid( str2, str1len, &sess3, &new ) );
 		CHECK( 1, new ? 1 : 0 );
 		CHECK( 0, fd_sess_state_retrieve( hdl1, sess3, &tms ) );
 		CHECK( NULL, tms );
@@ -358,10 +368,12 @@
 		
 		/* Check the last data can still be retrieved */
 		CHECK( 0, fd_sess_state_retrieve( hdl1, sess1, &tms ) );
-		CHECK( 0, fd_sess_getsid(sess1, &str1) );
+		CHECK( 1, tms ? 1 : 0);
+		CHECK( 0, fd_sess_getsid(sess1, &str1, &str1len) );
 		mycleanup(tms, str1, NULL);
 	}
 	
+	/* TODO: add tests on messages referencing sessions */
 	
 	/* That's all for the tests yet */
 	PASSTEST();
"Welcome to our mercurial repository"