changeset 34:0e2b57789361

Backup for the WE, some warnings remaining
author Sebastien Decugis <sdecugis@nict.go.jp>
date Fri, 30 Oct 2009 17:23:06 +0900
parents e6fcdf12b9a0
children 6486e97f56ae
files freeDiameter/CMakeLists.txt freeDiameter/fD.h freeDiameter/messages.c freeDiameter/p_out.c freeDiameter/p_psm.c freeDiameter/p_sr.c freeDiameter/peers.c include/freeDiameter/freeDiameter.h include/freeDiameter/libfreeDiameter.h libfreeDiameter/messages.c
diffstat 10 files changed, 517 insertions(+), 122 deletions(-) [+]
line wrap: on
line diff
--- a/freeDiameter/CMakeLists.txt	Thu Oct 29 18:05:45 2009 +0900
+++ b/freeDiameter/CMakeLists.txt	Fri Oct 30 17:23:06 2009 +0900
@@ -22,6 +22,7 @@
 	p_expiry.c
 	p_out.c
 	p_psm.c
+	p_sr.c
 	server.c
 	tcp.c
 	)
--- a/freeDiameter/fD.h	Thu Oct 29 18:05:45 2009 +0900
+++ b/freeDiameter/fD.h	Fri Oct 30 17:23:06 2009 +0900
@@ -92,6 +92,12 @@
 /* Create all the dictionary objects defined in the Diameter base RFC. */
 int fd_dict_base_protocol(struct dictionary * dict);
 
+/* Sentinel for the sent requests list */
+struct sr_list {
+	struct fd_list 	srs;
+	pthread_mutex_t	mtx;
+};
+
 /* Peers */
 struct fd_peer { /* The "real" definition of the peer structure */
 	
@@ -136,7 +142,7 @@
 	uint32_t	 p_hbh;
 	
 	/* Sent requests (for fallback), list of struct sentreq ordered by hbh */
-	struct fd_list	 p_sentreq;
+	struct sr_list	 p_sr;
 	
 	/* connection context: socket and related information */
 	struct cnxctx	*p_cnxctx;
@@ -203,12 +209,6 @@
 	int  		  validate;	/* The peer is new, it must be validated (by an extension) or error CEA to be sent */
 };
 
-/* Structure to store a sent request */
-struct sentreq {
-	struct fd_list	chain; 	/* the "o" field points directly to the hop-by-hop of the request (uint32_t *)  */
-	struct msg	*req;	/* A request that was sent and not yet answered. */
-};
-
 
 /* Functions */
 int  fd_peer_fini();
@@ -219,6 +219,7 @@
 int fd_peer_handle_newCER( struct msg ** cer, struct cnxctx ** cnx );
 /* fd_peer_add declared in freeDiameter.h */
 int fd_peer_validate( struct fd_peer * peer );
+void fd_peer_failover_msg(struct fd_peer * peer);
 
 /* Peer expiry */
 int fd_p_expi_init(void);
@@ -236,6 +237,11 @@
 int fd_out_start(struct fd_peer * peer);
 int fd_out_stop(struct fd_peer * peer);
 
+/* Peer sent requests cache */
+int fd_p_sr_store(struct sr_list * srlist, struct msg **req, uint32_t *hbhloc);
+int fd_p_sr_fetch(struct sr_list * srlist, uint32_t hbh, struct msg **req);
+void fd_p_sr_failover(struct sr_list * srlist);
+
 /* Active peers -- routing process should only ever take the read lock, the write lock is managed by PSMs */
 extern struct fd_list fd_g_activ_peers;
 extern pthread_rwlock_t fd_g_activ_peers_rw; /* protect the list */
--- a/freeDiameter/messages.c	Thu Oct 29 18:05:45 2009 +0900
+++ b/freeDiameter/messages.c	Fri Oct 30 17:23:06 2009 +0900
@@ -264,3 +264,43 @@
 	return 0;
 }
 
+/* Parse a message against our dictionary, and in case of error log and eventually build the error reply -- returns the parsing status */
+int fd_msg_parse_or_error( struct msg ** msg )
+{
+	int ret = 0;
+	struct msg * m;
+	struct msg_hdr * hdr = NULL;
+	struct fd_pei	pei;
+	
+	TRACE_ENTRY("%p", msg);
+	
+	CHECK_PARAMS(msg && *msg);
+	m = *msg;
+	
+	/* Parse the message against our dictionary */
+	ret = fd_msg_parse_rules ( m, fd_g_config->cnf_dict, &pei);
+	if (ret != EBADMSG)
+		return ret;
+	
+	fd_log_debug("The following message does not comply to the dictionary and rules (%s):\n", pei.pei_errcode);
+	fd_msg_dump_walk(NONE, m);
+	
+	/* Now create an answer error if the message is a query */
+	CHECK_FCT( fd_msg_hdr(m, &hdr) );
+	
+	if (hdr->msg_flags & CMD_FLAG_REQUEST) {
+		
+		/* Create the error message */
+		CHECK_FCT( fd_msg_new_answer_from_req ( fd_g_config->cnf_dict, msg, pei.pei_protoerr ? MSGFL_ANSW_ERROR : 0 ) );
+		
+		/* Set the error code */
+		CHECK_FCT( fd_msg_rescode_set(*msg, pei.pei_errcode, pei.pei_message, pei.pei_avp, 1 ) );
+		
+	} else {
+		/* Just discard */
+		CHECK_FCT( fd_msg_free( m ) );
+		*msg = NULL;
+	}
+	
+	return ret;
+}
--- a/freeDiameter/p_out.c	Thu Oct 29 18:05:45 2009 +0900
+++ b/freeDiameter/p_out.c	Fri Oct 30 17:23:06 2009 +0900
@@ -36,29 +36,100 @@
 #include "fD.h"
 
 /* Alloc a new hbh for requests, bufferize the message and send on the connection, save in sentreq if provided */
-static int do_send(struct msg ** msg, struct cnxctx * cnx, uint32_t * hbh, struct fd_list * sentreq)
+static int do_send(struct msg ** msg, struct cnxctx * cnx, uint32_t * hbh, struct sr_list * srl)
 {
-	TRACE_ENTRY("%p %p %p %p", msg, cnx, hbh, sentreq);
+	struct msg_hdr * hdr;
+	int msg_is_a_req;
+	uint8_t * buf;
+	size_t sz;
+	int ret;
+	
+	TRACE_ENTRY("%p %p %p %p", msg, cnx, hbh, srl);
+	
+	/* Retrieve the message header */
+	CHECK_FCT( fd_msg_hdr(*msg, &hdr) );
+	
+	msg_is_a_req = (hdr->msg_flags & CMD_FLAG_REQUEST);
+	if (msg_is_a_req) {
+		CHECK_PARAMS(hbh && srl);
+		/* Alloc the hop-by-hop id and increment the value for next message */
+		hdr->msg_hbhid = *hbh;
+		*hbh = hdr->msg_hbhid + 1;
+	}
+	
+	/* Create the message buffer */
+	CHECK_FCT(fd_msg_bufferize( *msg, &buf, &sz ));
 	
-	TODO("If message is a request");
-		TODO("Alloc new *hbh");
+	/* Send the message */
+	pthread_cleanup_push( free, buf );
+	CHECK_FCT_DO( ret = fd_cnx_send(cnx, buf, sz), { free(buf); return ret; } );
+	pthread_cleanup_pop(1);
 	
-	TODO("Bufferize the message, send it");
+	/* Save a request */
+	if (msg_is_a_req) {
+		CHECK_FCT_DO( fd_p_sr_store(srl, msg, &hdr->msg_hbhid),
+			{
+				fd_log_debug("The following request was sent successfully but not saved locally:\n" );
+				fd_log_debug("	(as a result the matching answer will be discarded)\n" );
+				fd_msg_dump_walk(NONE, *msg);
+			} );
+				
+	}
 	
-	TODO("Save in sentreq or free")
+	/* Free answers and unsaved requests */
+	if (*msg) {
+		CHECK_FCT( fd_msg_free(*msg) );
+		*msg = NULL;
+	}
 	
-	return ENOTSUP;
+	return 0;
+}
+
+static void cleanup_requeue(void * arg)
+{
+	struct msg *msg = arg;
+	CHECK_FCT_DO(fd_fifo_post(fd_g_outgoing, &msg),
+			CHECK_FCT_DO(fd_msg_free(msg), /* What can we do more? */));
 }
 
 /* The code of the "out" thread */
 static void * out_thr(void * arg)
 {
-	TODO("Pick next message in peer->p_tosend");
-	TODO("do_send, log errors");
-	TODO("In case of cancellation, requeue the message");
-	return NULL;
+	struct fd_peer * peer = arg;
+	ASSERT( CHECK_PEER(peer) );
+	
+	/* Set the thread name */
+	{
+		char buf[48];
+		sprintf(buf, "OUT/%.*s", sizeof(buf) - 5, peer->p_hdr.info.pi_diamid);
+		fd_log_threadname ( buf );
+	}
+	
+	/* Loop until cancelation */
+	while (1) {
+		struct msg * msg;
+		
+		/* Retrieve next message to send */
+		CHECK_FCT_DO( fd_fifo_get(peer->p_tosend, &msg), goto error );
+		
+		/* Now if we are cancelled, we requeue this message */
+		pthread_cleanup_push(cleanup_requeue, msg);
+		
+		/* Send the message, log any error */
+		CHECK_FCT_DO( do_send(&msg, peer->p_cnxctx, &peer->p_hbh, &peer->p_sr),
+			{
+				fd_log_debug("An error occurred while sending this message, it is lost:\n");
+				fd_msg_dump_walk(NONE, msg);
+				fd_msg_free(msg);
+			} );
+			
+		/* Loop */
+		pthread_cleanup_pop(0);
+	}
+	
 error:
-	TODO(" Send an event to the peer ");
+	/* It is not really a connection error, but the effect is the same, we are not able to send anymore message */
+	CHECK_FCT_DO( fd_event_send(peer->p_events, FDEVP_CNX_ERROR, 0, NULL), /* What do we do if it fails? */ );
 	return NULL;
 }
 
@@ -83,7 +154,7 @@
 			cnx = peer->p_cnxctx;
 
 		/* Do send the message */
-		CHECK_FCT( do_send(msg, cnx, hbh, peer ? &peer->p_sentreq : NULL) );
+		CHECK_FCT( do_send(msg, cnx, hbh, peer ? &peer->p_sr : NULL) );
 	}
 	
 	return 0;
--- a/freeDiameter/p_psm.c	Thu Oct 29 18:05:45 2009 +0900
+++ b/freeDiameter/p_psm.c	Fri Oct 30 17:23:06 2009 +0900
@@ -83,32 +83,64 @@
 /*                 Manage the list of active peers                      */
 /************************************************************************/
 
-
 /* Enter/leave OPEN state */
 static int enter_open_state(struct fd_peer * peer)
 {
+	struct fd_list * li;
+	CHECK_PARAMS( FD_IS_LIST_EMPTY(&peer->p_actives) );
+	
+	/* Callback registered by the credential validator (fd_peer_validate_register) */
+	if (peer->p_cb2) {
+		CHECK_FCT_DO( (*peer->p_cb2)(&peer->p_hdr.info),
+			{
+				TRACE_DEBUG(FULL, "Validation failed, moving to state CLOSING");
+				peer->p_hdr.info.pi_state = STATE_CLOSING;
+				fd_psm_terminate(peer);
+			} );
+		peer->p_cb2 = NULL;
+		return 0;
+	}
+	/* Insert in the active peers list */
 	CHECK_POSIX( pthread_rwlock_wrlock(&fd_g_activ_peers_rw) );
-	TODO(" insert in fd_g_activ_peers ");
+	for (li = fd_g_activ_peers.next; li != &fd_g_activ_peers; li = li->next) {
+		struct fd_peer * next_p = (struct fd_peer *)li->o;
+		int cmp = strcmp(peer->p_hdr.info.pi_diamid, next_p->p_hdr.info.pi_diamid);
+		if (cmp < 0)
+			break;
+	}
+	fd_list_insert_before(li, &peer->p_actives);
+	CHECK_POSIX( pthread_rwlock_unlock(&fd_g_activ_peers_rw) );
 	
-	CHECK_POSIX( pthread_rwlock_unlock(&fd_g_activ_peers_rw) );
+	/* Callback registered when the peer was added, by fd_peer_add */
+	if (peer->p_cb) {
+		TRACE_DEBUG(FULL, "Calling add callback for peer %s", peer->p_hdr.info.pi_diamid);
+		(*peer->p_cb)(&peer->p_hdr.info, peer->p_cb_data);
+		peer->p_cb = NULL;
+		peer->p_cb_data = NULL;
+	}
 	
 	/* Start the thread to handle outgoing messages */
 	CHECK_FCT( fd_out_start(peer) );
 	
-	return ENOTSUP;
+	return 0;
 }
 static int leave_open_state(struct fd_peer * peer)
 {
-	TODO("Remove from active list");
+	/* Remove from active peers list */
+	CHECK_POSIX( pthread_rwlock_wrlock(&fd_g_activ_peers_rw) );
+	fd_list_unlink( &peer->p_actives );
+	CHECK_POSIX( pthread_rwlock_unlock(&fd_g_activ_peers_rw) );
 	
 	/* Stop the "out" thread */
 	CHECK_FCT( fd_out_stop(peer) );
 	
-	TODO("Failover pending messages: requeue in global structures");
+	/* Failover the messages */
+	fd_peer_failover_msg(peer);
 	
-	return ENOTSUP;
+	return 0;
 }
 
+
 /************************************************************************/
 /*                      Helpers for state changes                       */
 /************************************************************************/
@@ -164,7 +196,7 @@
 	
 	peer->p_psm_timer.tv_sec += delay;
 	
-#if 0
+#ifdef SLOW_PSM
 	/* temporary for debug */
 	peer->p_psm_timer.tv_sec += 10;
 #endif
@@ -187,7 +219,7 @@
 static void * p_psm_th( void * arg )
 {
 	struct fd_peer * peer = (struct fd_peer *)arg;
-	int created_started = started;
+	int created_started = started ? 1 : 0;
 	int event;
 	size_t ev_sz;
 	void * ev_data;
@@ -213,7 +245,7 @@
 	if (peer->p_flags.pf_responder) {
 		psm_next_timeout(peer, 0, INCNX_TIMEOUT);
 	} else {
-		psm_next_timeout(peer, created_started ? 0 : 1, 0);
+		psm_next_timeout(peer, created_started, 0);
 	}
 	
 psm_loop:
@@ -237,15 +269,6 @@
 		goto psm_loop;
 	}
 
-	/* Call the extension callback if needed */
-	if (peer->p_cb) {
-		/* Check if we must call it */
-			/*  */
-		/* OK */
-		TODO("Call CB");
-		TODO("Clear CB");
-	}
-
 	/* Handle the (easy) debug event now */
 	if (event == FDEVP_DUMP_ALL) {
 		fd_peer_dump(peer, ANNOYING);
@@ -276,8 +299,66 @@
 	
 	/* A message was received */
 	if (event == FDEVP_CNX_MSG_RECV) {
-		TODO("Parse the buffer into a message");
-		/* parse_and_get_local_ccode */
+		struct msg * msg = NULL;
+		struct msg_hdr * hdr;
+		
+		/* Parse the received buffer */
+		CHECK_FCT_DO( fd_msg_parse_buffer( (void *)&ev_data, ev_sz, &msg), 
+			{
+				fd_log_debug("Received invalid data from peer '%s', closing the connection\n", peer->p_hdr.info.pi_diamid);
+				CHECK_FCT_DO( fd_event_send(peer->p_events, FDEVP_CNX_ERROR, 0, NULL), goto psm_end );
+				goto psm_loop;
+			} );
+		
+		TRACE_DEBUG(FULL, "Received this message from '%s':", peer->p_hdr.info.pi_diamid);
+		fd_msg_dump_walk(FULL, msg);
+	
+		/* Extract the header */
+		CHECK_FCT_DO( fd_msg_hdr(msg, &hdr), goto psm_end );
+		
+		/* If it is an answer, associate with the request */
+		if (!(hdr->msg_flags & CMD_FLAG_REQUEST)) {
+			struct msg * req;
+			/* Search matching request (same hbhid) */
+			CHECK_FCT_DO( fd_p_sr_fetch(&peer->p_sr, hdr->msg_hbhid, &req), goto psm_end );
+			if (req == NULL) {
+				fd_log_debug("Received a Diameter answer message with no corresponding sent request, discarding...\n");
+				fd_msg_dump_walk(NONE, msg);
+				fd_msg_free(msg);
+				goto psm_loop;
+			}
+			
+			/* Associate */
+			CHECK_FCT_DO( fd_msg_answ_associate( msg, req ), goto psm_end );
+		}
+		
+		/* We received a valid message, update the expiry timer */
+		CHECK_FCT_DO( fd_p_expi_update(peer), goto psm_end );
+
+		/* Now handle non-link-local messages */
+		if (fd_msg_is_routable(msg)) {
+			/* If we are not in OPEN state, discard the message */
+			if (peer->p_hdr.info.pi_state != STATE_OPEN) {
+				fd_log_debug("Received a routable message while not in OPEN state from peer '%s', discarded.\n", peer->p_hdr.info.pi_diamid);
+				fd_msg_dump_walk(NONE, msg);
+				fd_msg_free(msg);
+			} else {
+				/* Set the message source and add the Route-Record */
+				CHECK_FCT_DO( fd_msg_source_set( msg, peer->p_hdr.info.pi_diamid, 1, fd_g_config->cnf_dict ), goto psm_end);
+
+				/* Requeue to the global incoming queue */
+				CHECK_FCT_DO(fd_fifo_post(fd_g_incoming, &msg), goto psm_end );
+				
+				/* Update the peer timer */
+				if (!peer->p_flags.pf_dw_pending) {
+					psm_next_timeout(peer, 1, peer->p_hdr.info.pi_twtimer ?: fd_g_config->cnf_timer_tw);
+				}
+			}
+			goto psm_loop;
+		}
+		
+		/* Link-local message: They must be understood by our dictionary */
+		
 		TODO("Check if it is a local message (CER, DWR, ...)");
 		TODO("If not, check we are in OPEN state");
 		TODO("Update expiry timer if needed");
@@ -318,6 +399,7 @@
 		switch (peer->p_hdr.info.pi_state) {
 			case STATE_CLOSED:
 				TODO("Handle the CER, validate the peer if needed (and set expiry), set the alt_fifo in the connection, reply a CEA, eventually handshake, move to OPEN or REOPEN state");
+				/* In case of error : DIAMETER_UNKNOWN_PEER */
 				break;
 				
 			case STATE_WAITCNXACK:
@@ -352,7 +434,7 @@
 	}
 	
 	goto psm_loop;
-	
+
 psm_end:
 	pthread_cleanup_pop(1); /* set STATE_ZOMBIE */
 	peer->p_psm = (pthread_t)NULL;
@@ -397,12 +479,25 @@
 void fd_psm_abord(struct fd_peer * peer )
 {
 	TRACE_ENTRY("%p", peer);
-	TODO("Cancel PSM thread");
-	TODO("Cancel OUT thread");
-	TODO("Cleanup the peer connection object");
-	TODO("Cleanup the message queues (requeue)");
-	TODO("Call p_cb with NULL parameter if needed");
+	
+	/* Cancel PSM thread */
+	CHECK_FCT_DO( fd_thr_term(&peer->p_psm), /* continue */ );
+	
+	/* Cancel the OUT thread */
+	CHECK_FCT_DO( fd_out_stop(peer), /* continue */ );
 	
+	/* Cleanup the connection */
+	if (peer->p_cnxctx) {
+		fd_cnx_destroy(peer->p_cnxctx);
+	}
+	
+	/* Failover the messages */
+	fd_peer_failover_msg(peer);
+	
+	/* Empty the events list, this might leak some memory, but we only do it on exit, so... */
+	fd_event_destroy(&peer->p_events, free);
+	
+	/* More cleanups are performed in fd_peer_free */
 	return;
 }
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/freeDiameter/p_sr.c	Fri Oct 30 17:23:06 2009 +0900
@@ -0,0 +1,145 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@nict.go.jp>							 *
+*													 *
+* Copyright (c) 2009, WIDE Project and NICT								 *
+* All rights reserved.											 *
+* 													 *
+* Redistribution and use of this software in source and binary forms, with or without modification, are  *
+* permitted provided that the following conditions are met:						 *
+* 													 *
+* * Redistributions of source code must retain the above 						 *
+*   copyright notice, this list of conditions and the 							 *
+*   following disclaimer.										 *
+*    													 *
+* * Redistributions in binary form must reproduce the above 						 *
+*   copyright notice, this list of conditions and the 							 *
+*   following disclaimer in the documentation and/or other						 *
+*   materials provided with the distribution.								 *
+* 													 *
+* * Neither the name of the WIDE Project or NICT nor the 						 *
+*   names of its contributors may be used to endorse or 						 *
+*   promote products derived from this software without 						 *
+*   specific prior written permission of WIDE Project and 						 *
+*   NICT.												 *
+* 													 *
+* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED *
+* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
+* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR *
+* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 	 *
+* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 	 *
+* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR *
+* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF   *
+* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.								 *
+*********************************************************************************************************/
+
+#include "fD.h"
+
+/* Structure to store a sent request */
+struct sentreq {
+	struct fd_list	chain; 	/* the "o" field points directly to the hop-by-hop of the request (uint32_t *)  */
+	struct msg	*req;	/* A request that was sent and not yet answered. */
+};
+
+/* Find an element in the list, or the following one */
+static struct fd_list * find_or_next(struct fd_list * srlist, uint32_t hbh, int * match)
+{
+	struct fd_list * li;
+	*match = 0;
+	for (li = srlist->next; li != srlist; li = li->next) {
+		uint32_t * nexthbh = li->o;
+		if (*nexthbh < hbh)
+			continue;
+		if (*nexthbh == hbh)
+			*match = 1;
+		break;
+	}
+	return li;
+}
+
+/* Store a new sent request */
+int fd_p_sr_store(struct sr_list * srlist, struct msg **req, uint32_t *hbhloc)
+{
+	struct sentreq * sr;
+	struct fd_list * next;
+	int match;
+	
+	TRACE_ENTRY("%p %p %p", srlist, req, hbhloc);
+	CHECK_PARAMS(srlist && req && *req && hbhloc);
+	
+	CHECK_MALLOC( sr = malloc(sizeof(struct sentreq)) );
+	memset(sr, 0, sizeof(struct sentreq));
+	fd_list_init(&sr->chain, hbhloc);
+	sr->req = *req;
+	
+	/* Search the place in the list */
+	CHECK_POSIX( pthread_mutex_lock(&srlist->mtx) );
+	next = find_or_next(&srlist->srs, *hbhloc, &match);
+	if (match) {
+		TRACE_DEBUG(INFO, "A request with the same hop-by-hop Id was already sent: error");
+		free(sr);
+		CHECK_POSIX_DO( pthread_mutex_unlock(&srlist->mtx), /* ignore */ );
+		return EINVAL;
+	}
+	
+	/* Save in the list */
+	*req = NULL;
+	fd_list_insert_before(next, &sr->chain);
+	CHECK_POSIX( pthread_mutex_unlock(&srlist->mtx) );
+	return 0;
+}
+
+/* Fetch a request by hbh */
+int fd_p_sr_fetch(struct sr_list * srlist, uint32_t hbh, struct msg **req)
+{
+	struct sentreq * sr;
+	int match;
+	
+	TRACE_ENTRY("%p %x %p", srlist, hbh, req);
+	CHECK_PARAMS(srlist && req);
+	
+	/* Search the request in the list */
+	CHECK_POSIX( pthread_mutex_lock(&srlist->mtx) );
+	sr = (struct sentreq *)find_or_next(&srlist->srs, hbh, &match);
+	if (!match) {
+		TRACE_DEBUG(INFO, "There is no saved request with this hop-by-hop id");
+		*req = NULL;
+	} else {
+		/* Unlink */
+		fd_list_unlink(&sr->chain);
+		*req = sr->req;
+		free(sr);
+	}
+	CHECK_POSIX( pthread_mutex_unlock(&srlist->mtx) );
+
+	/* Done */
+	return 0;
+}
+
+/* Failover requests (free or requeue routables) */
+void fd_p_sr_failover(struct sr_list * srlist)
+{
+	CHECK_POSIX_DO( pthread_mutex_lock(&srlist->mtx), /* continue anyway */ );
+	while (!FD_IS_LIST_EMPTY(&srlist->srs)) {
+		struct sentreq * sr = (struct sentreq *)(srlist->srs.next);
+		fd_list_unlink(&sr->chain);
+		if (fd_msg_is_routable(sr->req)) {
+			struct msg_hdr * hdr = NULL;
+			
+			/* Set the 'T' flag */
+			CHECK_FCT_DO(fd_msg_hdr(sr->req, &hdr), /* continue */);
+			if (hdr)
+				hdr->msg_flags |= CMD_FLAG_RETRANSMIT;
+			
+			/* Requeue for sending to another peer */
+			CHECK_FCT_DO(fd_fifo_post(fd_g_outgoing, &sr->req),
+					CHECK_FCT_DO(fd_msg_free(sr->req), /* What can we do more? */));
+		} else {
+			/* Just free the request... */
+			CHECK_FCT_DO(fd_msg_free(sr->req), /* Ignore */);
+		}
+		free(sr);
+	}
+	CHECK_POSIX_DO( pthread_mutex_unlock(&srlist->mtx), /* continue anyway */ );
+}
+
--- a/freeDiameter/peers.c	Thu Oct 29 18:05:45 2009 +0900
+++ b/freeDiameter/peers.c	Fri Oct 30 17:23:06 2009 +0900
@@ -77,7 +77,8 @@
 	p->p_hbh = lrand48();
 	CHECK_FCT( fd_fifo_new(&p->p_events) );
 	CHECK_FCT( fd_fifo_new(&p->p_tosend) );
-	fd_list_init(&p->p_sentreq, p);
+	fd_list_init(&p->p_sr.srs, p);
+	CHECK_POSIX( pthread_mutex_init(&p->p_sr.mtx, NULL) );
 	
 	return 0;
 }
@@ -179,6 +180,27 @@
 		free(__li);						\
 	}
 
+/* Empty the lists of p_tosend and p_sentreq messages */
+void fd_peer_failover_msg(struct fd_peer * peer)
+{
+	struct msg *m;
+	TRACE_ENTRY("%p", peer);
+	CHECK_PARAMS_DO(CHECK_PEER(peer), return);
+	
+	/* Requeue all messages in the "out" queue */
+	while ( fd_fifo_tryget(peer->p_tosend, &m) == 0 ) {
+		CHECK_FCT_DO(fd_fifo_post(fd_g_outgoing, &m), 
+				/* fallback: destroy the message */
+				CHECK_FCT_DO(fd_msg_free(m), /* What can we do more? */));
+	}
+	
+	/* Requeue all routable sent requests */
+	fd_p_sr_failover(&peer->p_sr);
+	
+	/* Done */
+	return;
+}
+
 /* Destroy a structure once all cleanups have been performed */
 int fd_peer_free(struct fd_peer ** ptr)
 {
@@ -196,7 +218,7 @@
 	free_null(p->p_hdr.info.pi_diamid); 
 	free_null(p->p_hdr.info.pi_realm); 
 	free_list( &p->p_hdr.info.pi_endpoints );
-	/* Assume the security data is already freed */
+	TODO("Free the security data if any ?");
 	free_null(p->p_hdr.info.pi_prodname);
 	free_list( &p->p_hdr.info.pi_apps );
 	
@@ -213,31 +235,22 @@
 	CHECK_FCT( fd_fifo_del(&p->p_events) );
 	
 	CHECK_FCT( fd_thr_term(&p->p_outthr) );
-	while ( fd_fifo_tryget(p->p_tosend, &t) == 0 ) {
-		struct msg * m = t;
-		TRACE_DEBUG(FULL, "Found message %p in outgoing queue of peer %p being destroyed, requeue", m, p);
-		/* We simply requeue in global, the routing thread will re-handle it. */
-		CHECK_FCT(fd_fifo_post(fd_g_outgoing, &m));
-	}
-	CHECK_FCT( fd_fifo_del(&p->p_tosend) );
-	
-	while (!FD_IS_LIST_EMPTY(&p->p_sentreq)) {
-		struct sentreq * sr = (struct sentreq *)(p->p_sentreq.next);
-		fd_list_unlink(&sr->chain);
-		TRACE_DEBUG(FULL, "Found message %p in list of sent requests to peer %p being destroyed, requeue (fallback)", sr->req, p);
-		CHECK_FCT(fd_fifo_post(fd_g_outgoing, &sr->req));
-		free(sr);
-	}
 	
 	if (p->p_cnxctx) {
 		fd_cnx_destroy(p->p_cnxctx);
 	}
 	
+	/* Requeue any remaining message into global structures if possible */
+	fd_peer_failover_msg(p);
+	CHECK_FCT_DO( fd_fifo_del(&p->p_tosend), /* continue */ );
+	CHECK_POSIX_DO( pthread_mutex_destroy(&p->p_sr.mtx), /* continue */);
+	
+	/* If the callback is still around... */
 	if (p->p_cb)
 		(*p->p_cb)(NULL, p->p_cb_data);
 	
+	/* Free the structure */
 	free(p);
-	
 	return 0;
 }
 
--- a/include/freeDiameter/freeDiameter.h	Thu Oct 29 18:05:45 2009 +0900
+++ b/include/freeDiameter/freeDiameter.h	Fri Oct 30 17:23:06 2009 +0900
@@ -491,6 +491,8 @@
 /* Add Origin-Host, Origin-Realm, (if osi) Origin-State-Id AVPS at the end of the message */
 int fd_msg_add_origin ( struct msg * msg, int osi ); 
 
+/* Parse a message against our dictionary, and in case of error log and eventually build the error reply (on return and EBADMSG, *msg == NULL or *msg is the error message ready to send) */
+int fd_msg_parse_or_error( struct msg ** msg );
 
 
 /***************************************/
--- a/include/freeDiameter/libfreeDiameter.h	Thu Oct 29 18:05:45 2009 +0900
+++ b/include/freeDiameter/libfreeDiameter.h	Fri Oct 30 17:23:06 2009 +0900
@@ -1989,8 +1989,8 @@
  *  0      	: Operation complete.
  *  !0      	: an error occurred.
  */
-int fd_msg_source_set( struct msg * msg, char * diamid, uint32_t hash, int add_rr, struct dictionary * dict );
-int fd_msg_source_get( struct msg * msg, char ** diamid, uint32_t *hash );
+int fd_msg_source_set( struct msg * msg, char * diamid, int add_rr, struct dictionary * dict );
+int fd_msg_source_get( struct msg * msg, char ** diamid );
 
 /*
  * FUNCTION:	fd_msg_eteid_get
@@ -2137,13 +2137,21 @@
  */
 int fd_msg_parse_dict ( msg_or_avp * object, struct dictionary * dict );
 
+/* Parsing Error Information structure */
+struct fd_pei {
+	char *		pei_errcode;	/* name of the error code to use */
+	struct avp *	pei_avp;	/* pointer to invalid or missing AVP (to be freed) */
+	char *		pei_message;	/* Overwrite default message if needed */
+	int		pei_protoerr; 	/* do we set the 'E' bit in the error message ? */
+};
+
 /*
  * FUNCTION:	fd_msg_parse_rules
  *
  * PARAMETERS:
  *  object	: A msg or grouped avp object that must be verified.
  *  dict	: The dictionary containing the rules definitions.
- *  rule	: If not NULL, the first conflicting rule will be saved here if a conflict is found.
+ *  error_info	: If not NULL, the first problem information will be saved here.
  *
  * DESCRIPTION: 
  *   Check that the children of the object do not conflict with the dictionary rules (ABNF compliance).
@@ -2154,7 +2162,8 @@
  *  EINVAL 	: The msg or avp object is invalid for this operation.
  *  ENOMEM	: Unable to allocate enough memory to complete the operation.
  */
-int fd_msg_parse_rules ( msg_or_avp * object, struct dictionary * dict, struct dict_object ** rule);
+int fd_msg_parse_rules ( msg_or_avp * object, struct dictionary * dict, struct fd_pei *error_info);
+
 
 
 /*
--- a/libfreeDiameter/messages.c	Thu Oct 29 18:05:45 2009 +0900
+++ b/libfreeDiameter/messages.c	Fri Oct 30 17:23:06 2009 +0900
@@ -123,7 +123,6 @@
 			void * data;
 		}		 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 */
-	uint32_t		 msg_src_hash;		/* Hash of the msg_src_id value */
 };
 
 /* Macro to compute the message header size */
@@ -667,8 +666,8 @@
 		msg->msg_public.msg_hbhid,
 		msg->msg_public.msg_eteid
 		);
-	fd_log_debug(INOBJHDR "intern: rwb:%p rt:%d cb:%p(%p) qry:%p h:%x src:%s\n", 
-			INOBJHDRVAL, msg->msg_rawbuffer, msg->msg_routable, msg->msg_cb.fct, msg->msg_cb.data, msg->msg_query, msg->msg_src_hash, msg->msg_src_id?:"(nil)");
+	fd_log_debug(INOBJHDR "intern: rwb:%p rt:%d cb:%p(%p) qry:%p src:%s\n", 
+			INOBJHDRVAL, msg->msg_rawbuffer, msg->msg_routable, msg->msg_cb.fct, msg->msg_cb.data, msg->msg_query, msg->msg_src_id?:"(nil)");
 }
 
 #define DUMP_VALUE(_format, _parms...)   fd_log_debug(INOBJHDR "value : t:'%s' v:'" _format "'\n", INOBJHDRVAL, typename, ## _parms);
@@ -1076,9 +1075,9 @@
 }
 
 /* Associate source peer */
-int fd_msg_source_set( struct msg * msg, char * diamid, uint32_t hash, int add_rr, struct dictionary * dict )
+int fd_msg_source_set( struct msg * msg, char * diamid, int add_rr, struct dictionary * dict )
 {
-	TRACE_ENTRY( "%p %p %x %d %p", msg, diamid, hash, add_rr, dict);
+	TRACE_ENTRY( "%p %p %d %p", msg, diamid, add_rr, dict);
 	
 	/* Check we received a valid message */
 	CHECK_PARAMS( CHECK_MSG(msg) && dict );
@@ -1088,13 +1087,11 @@
 	
 	/* If the request is to cleanup the source, we are done */
 	if (diamid == NULL) {
-		msg->msg_src_hash = 0;
 		return 0;
 	}
 	
 	/* Otherwise save the new informations */
 	CHECK_MALLOC( msg->msg_src_id = strdup(diamid) );
-	msg->msg_src_hash = hash;
 	
 	if (add_rr) {
 		struct dict_object 	*avp_rr_model;
@@ -1122,9 +1119,9 @@
 	return 0;
 }
 
-int fd_msg_source_get( struct msg * msg, char ** diamid, uint32_t *hash )
+int fd_msg_source_get( struct msg * msg, char ** diamid )
 {
-	TRACE_ENTRY( "%p %p %p", msg, diamid, hash);
+	TRACE_ENTRY( "%p %p", msg, diamid);
 	
 	/* Check we received valid parameters */
 	CHECK_PARAMS( CHECK_MSG(msg) );
@@ -1132,8 +1129,6 @@
 	
 	/* Copy the informations */
 	*diamid = msg->msg_src_id;
-	if (hash)
-		*hash = msg->msg_src_hash;
 	
 	/* done */
 	return 0;
@@ -1862,24 +1857,32 @@
 
 /* We use this structure as parameter for the next function */
 struct parserules_data {
-	struct fd_list     * sentinel;  /* Sentinel of the list of children AVP */
-	struct dict_object * ruleavp;   /* If the rule conflicts, save the rule_avp here (we don't have direct access to the rule but it can be searched) */
+	struct fd_list  * sentinel;  	/* Sentinel of the list of children AVP */
+	struct fd_pei 	* pei;   	/* If the rule conflicts, save the error here */
 };
 
+/* Create an empty AVP of a given model (to use in Failed-AVP) */
+static struct avp * empty_avp(struct dict_object * model_avp)
+{
+	TODO("Create the AVP instance and set a 0 value");
+	return NULL;
+}
+
 /* Check that a list of AVPs is compliant with a given rule -- will be iterated on the list of rules */
 static int parserules_check_one_rule(void * data, struct dict_rule_data *rule)
 {
-	int ret = 0, count, first, last, min;
-	struct parserules_data * pr_data = (struct parserules_data *) data;
+	int count, first, last, min;
+	struct parserules_data * pr_data = data;
 	
 	TRACE_ENTRY("%p %p", data, rule);
 	
-	/* Get statistics of the AVP concerned by this rule in the message instance */
+	/* Get statistics of the AVP concerned by this rule in the parent instance */
 	parserules_stat_avps( rule->rule_avp, pr_data->sentinel, &count, &first, &last);
 	
 	if (TRACE_BOOL(ANNOYING))
 	{
 		struct dict_avp_data avpdata;
+		int ret;
 		ret = fd_dict_getval(rule->rule_avp, &avpdata);
 		
 		TRACE_DEBUG(ANNOYING, "Checking rule: p:%d(%d) m/M:%2d/%2d. Counted %d (first: %d, last:%d) of AVP '%s'", 
@@ -1895,7 +1898,6 @@
 	}
 	
 	/* Now check the rule is not conflicting */
-	ret = 0;
 	
 	/* Check the "min" value */
 	if ((min = rule->rule_min) == -1) {
@@ -1906,15 +1908,24 @@
 	}
 	if (count < min) {
 		TRACE_DEBUG(INFO, "Conflicting rule: the number of occurences (%d) is < the rule min (%d).", count, min);
-		ret = EBADMSG;
-		goto end;
+		if (pr_data->pei) {
+			pr_data->pei->pei_errcode = "DIAMETER_MISSING_AVP";
+			pr_data->pei->pei_avp = empty_avp(rule->rule_avp);
+		}
+		return EBADMSG;
 	}
 	
 	/* Check the "max" value */
 	if ((rule->rule_max != -1) && (count > rule->rule_max)) {
 		TRACE_DEBUG(INFO, "Conflicting rule: the number of occurences (%d) is > the rule max (%d).", count, rule->rule_max);
-		ret = EBADMSG;
-		goto end;
+		if (pr_data->pei) {
+			if (rule->rule_max == 0)
+				pr_data->pei->pei_errcode = "DIAMETER_AVP_NOT_ALLOWED";
+			else
+				pr_data->pei->pei_errcode = "DIAMETER_AVP_OCCURS_TOO_MANY_TIMES";
+			pr_data->pei->pei_avp = empty_avp(rule->rule_avp); /* Well we are supposed to return the (max + 1)th instance of the AVP instead... Pfff... */ TODO("Improve...");
+		}
+		return EBADMSG;
 	}
 		
 	/* Check the position and order (if relevant) */
@@ -1928,8 +1939,12 @@
 			/* Since "0*1<fixed>" is a valid rule specifier, we only reject cases where the AVP appears *after* its fixed position */
 			if (first > rule->rule_order) {
 				TRACE_DEBUG(INFO, "Conflicting rule: the FIXED_HEAD AVP appears first in (%d) position, the rule requires (%d).", first, rule->rule_order);
-				ret = EBADMSG;
-				goto end;
+				if (pr_data->pei) {
+					pr_data->pei->pei_errcode = "DIAMETER_MISSING_AVP";
+					pr_data->pei->pei_message = "AVP was not in its fixed position";
+					pr_data->pei->pei_avp = empty_avp(rule->rule_avp);
+				}
+				return EBADMSG;
 			}
 			break;
 	
@@ -1937,34 +1952,32 @@
 			/* Since "0*1<fixed>" is a valid rule specifier, we only reject cases where the AVP appears *before* its fixed position */
 			if (last > rule->rule_order) {	/* We have a ">" here because we count in reverse order (i.e. from the end) */
 				TRACE_DEBUG(INFO, "Conflicting rule: the FIXED_TAIL AVP appears last in (%d) position, the rule requires (%d).", last, rule->rule_order);
-				ret = EBADMSG;
-				goto end;
+				if (pr_data->pei) {
+					pr_data->pei->pei_errcode = "DIAMETER_MISSING_AVP";
+					pr_data->pei->pei_message = "AVP was not in its fixed position";
+					pr_data->pei->pei_avp = empty_avp(rule->rule_avp);
+				}
+				return EBADMSG;
 			}
 			break;
 		
 		default:
 			/* What is this position ??? */
 			ASSERT(0);
-			ret = ENOTSUP;
+			return ENOTSUP;
 	}
 	
 	/* We've checked all the parameters */
-end:
-	if (ret == EBADMSG) {
-		pr_data->ruleavp = rule->rule_avp;
-	}
-
-	return ret;
+	return 0;
 }
 
 /* Check the rules recursively */
-static int parserules_do ( struct dictionary * dict, msg_or_avp * object, struct dict_object ** conflict_rule, int mandatory)
+static int parserules_do ( struct dictionary * dict, msg_or_avp * object, struct fd_pei *error_info, int mandatory)
 {
-	int ret = 0;
 	struct parserules_data data;
 	struct dict_object * model = NULL;
 	
-	TRACE_ENTRY("%p %p %p %d", dict, object, conflict_rule, mandatory);
+	TRACE_ENTRY("%p %p %p %d", dict, object, error_info, mandatory);
 	
 	/* object has already been checked and dict-parsed when we are called. */
 	
@@ -1980,6 +1993,10 @@
 			/* Commands MUST be supported in the dictionary */
 			if (model == NULL) {
 				TRACE_DEBUG(INFO, "Message with no dictionary model. EBADMSG");
+				if (error_info) {
+					error_info->pei_errcode = "DIAMETER_COMMAND_UNSUPPORTED";
+					error_info->pei_protoerr = 1;
+				}
 				return EBADMSG;
 			}
 		}
@@ -1989,6 +2006,10 @@
 			if ( mandatory && (_A(object)->avp_public.avp_flags & AVP_FLAG_MANDATORY)) {
 				/* Return an error in this case */
 				TRACE_DEBUG(INFO, "Mandatory AVP with no dictionary model. EBADMSG");
+				if (error_info) {
+					error_info->pei_errcode = "DIAMETER_AVP_UNSUPPORTED";
+					error_info->pei_avp = object;
+				}
 				return EBADMSG;
 			} else {
 				/* We don't know any rule for this object, so assume OK */
@@ -2018,38 +2039,30 @@
 		   || (mandatory && (_A(object)->avp_public.avp_flags & AVP_FLAG_MANDATORY)) )
 			is_child_mand = 1;
 		for (ch = _C(object)->children.next; ch != &_C(object)->children; ch = ch->next) {
-			CHECK_FCT(  parserules_do ( dict, _C(ch->o), conflict_rule, is_child_mand )  );
+			CHECK_FCT(  parserules_do ( dict, _C(ch->o), error_info, is_child_mand )  );
 		}
 	}
 
 	/* Now check all rules of this object */
 	data.sentinel = &_C(object)->children;
-	data.ruleavp  = NULL;
-	ret = fd_dict_iterate_rules ( model, &data, parserules_check_one_rule );
+	data.pei  = error_info;
+	CHECK_FCT( fd_dict_iterate_rules ( model, &data, parserules_check_one_rule ) );
 	
-	/* Save the reference to the eventual conflicting rule; otherwise set to NULL */
-	if (conflict_rule && data.ruleavp) {
-		/* data.ruleavp contains the AVP, and model is the parent */
-		struct dict_object * rule = NULL;
-		struct dict_rule_request req = { model, data.ruleavp };
-		
-		CHECK_FCT_DO( fd_dict_search ( dict, DICT_RULE, RULE_BY_AVP_AND_PARENT, &req, &rule, ENOENT),  rule = NULL );
-		
-		*conflict_rule = rule;
-	}
-	
-	return ret;
+	return 0;
 }
 
-int fd_msg_parse_rules ( msg_or_avp * object, struct dictionary * dict, struct dict_object ** rule)
+int fd_msg_parse_rules ( msg_or_avp * object, struct dictionary * dict, struct fd_pei *error_info)
 {
-	TRACE_ENTRY("%p %p", object, rule);
+	TRACE_ENTRY("%p %p %p", object, dict, error_info);
 	
 	/* Resolve the dictionary objects when missing. This also validates the object. */
 	CHECK_FCT(  fd_msg_parse_dict ( object, dict )  );
 	
+	if (error_info)
+		memset(error_info, 0, sizeof(struct fd_pei));
+	
 	/* Call the recursive function */
-	return parserules_do ( dict, object, rule, 1 ) ;
+	return parserules_do ( dict, object, error_info, 1 ) ;
 }
 
 /***************************************************************************************************************/
"Welcome to our mercurial repository"