Index: libfdcore/routing_dispatch.c
===================================================================
--- libfdcore/routing_dispatch.c	(revision 691)
+++ libfdcore/routing_dispatch.c	(revision 706)
@@ -208,6 +208,6 @@
 		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) );
@@ -264,21 +264,21 @@
 	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;
 			}
 		}
@@ -298,6 +298,8 @@
 	
 	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 '.' ? */
@@ -307,12 +309,14 @@
 			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 */
@@ -321,17 +325,4 @@
 			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;
-		}
 	}
 	
@@ -340,25 +331,11 @@
 
 /* Test if a User-Name AVP contains a Decorated NAI -- RFC4282, RFC5729 */
-static int is_decorated_NAI(union avp_value * un)
-{
-	int i;
-	TRACE_ENTRY("%p", un);
-	
-	/* If there was no User-Name, we return false */
-	if (un == NULL)
-		return 0;
-	
-	nai_get_indexes(un, &i, NULL);
-	
-	return i;
-}
-
 /* Create new User-Name and Destination-Realm values */
-static int process_decorated_NAI(union avp_value * un, union avp_value * dr)
-{
-	int at_idx = 0, sep_idx = 0;
+static int process_decorated_NAI(int * was_nai, union avp_value * un, union avp_value * dr)
+{
+	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' */
@@ -367,5 +344,10 @@
 	/* 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 */
@@ -390,4 +372,5 @@
 }
 
+
 /* 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)
@@ -398,6 +381,7 @@
 	/* 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) {
@@ -406,5 +390,5 @@
 		
 			/* 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) {
@@ -439,5 +423,4 @@
 /****************************************************************************/
 
-/* These are the functions of each threads: dispatch & routing */
 /* The DISPATCH message processing */
 static int msg_dispatch(struct msg ** pmsg)
@@ -447,6 +430,6 @@
 	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 */
@@ -460,5 +443,5 @@
 	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 */
@@ -512,5 +495,5 @@
 				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 */
@@ -528,5 +511,5 @@
 				/* 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:
@@ -545,5 +528,6 @@
 	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 */
@@ -572,5 +556,6 @@
 		/* 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;
@@ -587,4 +572,7 @@
 			struct fd_pei error_info;
 			int ret;
+			
+			memset(&error_info, 0, sizeof(struct fd_pei)); 
+			
 			CHECK_FCT(  fd_msg_avp_hdr( avp, &ahdr )  );
 
@@ -604,9 +592,8 @@
 						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) {
+						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 = NO;
-						} else {
-							is_dest_host = (strncasecmp(fd_g_config->cnf_diamid, (char *)ahdr->avp_value->os.data, fd_g_config->cnf_diamid_len) 
-										? NO : YES);
 						}
 						break;
@@ -626,12 +613,12 @@
 						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) {
+						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 = NO;
-						} else {
-							is_dest_realm = (strncasecmp(fd_g_config->cnf_diamrlm, (char *)ahdr->avp_value->os.data, fd_g_config->cnf_diamrlm_len) 
-										? NO : YES);
 						}
 						break;
 
+					/* we also use User-Name for decorated NAI */
 					case AC_USER_NAME:
 						/* Parse this AVP */
@@ -652,4 +639,5 @@
 			}
 
+			/* 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;
@@ -682,20 +670,21 @@
 		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) );
@@ -724,5 +713,5 @@
 		/* 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)) {
@@ -733,4 +722,5 @@
 
 		/* 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?");
 	}
 
@@ -800,5 +790,6 @@
 	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;
@@ -806,12 +797,11 @@
 		/* 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);
@@ -844,5 +834,10 @@
 		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) );
@@ -867,6 +862,6 @@
 					} );
 				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);
 			}
 
@@ -876,5 +871,5 @@
 	}
 
-	/* 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 */
@@ -928,8 +923,7 @@
 
 		/* Search for the peer */
-		CHECK_FCT( fd_peer_getbyid( c->diamid, (void *)&peer ) );
-
-		fd_cpu_flush_cache();
-		if (peer && (peer->p_hdr.info.runtime.pir_state == STATE_OPEN)) {
+		CHECK_FCT( fd_peer_getbyid( c->diamid, c->diamidlen, 0, (void *)&peer ) );
+
+		if (fd_peer_getstate(peer) == STATE_OPEN) {
 			/* Send to this one */
 			CHECK_FCT_DO( fd_out_send(pmsg, NULL, peer, 0), continue );
@@ -964,5 +958,5 @@
 /* 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 */
@@ -970,6 +964,7 @@
 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), );
 }
 
@@ -991,6 +986,7 @@
 	
 	/* 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 {
@@ -1000,7 +996,7 @@
 		{
 			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;
@@ -1014,5 +1010,13 @@
 		{
 			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 */
@@ -1100,4 +1104,7 @@
 	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;
 }
@@ -1106,7 +1113,7 @@
 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;
@@ -1117,8 +1124,13 @@
 	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 {
@@ -1131,6 +1143,9 @@
 			
 			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;
 				
