changeset 1113:eb4ce68b6e5c

Added calls to remaining hooks
author Sebastien Decugis <sdecugis@freediameter.net>
date Mon, 13 May 2013 19:17:13 +0800
parents d87cee14b051
children 5da2ea1585a1
files extensions/dbg_interactive/endpoints.i extensions/dbg_monitor/dbg_monitor.c include/freeDiameter/libfdcore.h include/freeDiameter/libfdproto.h libfdcore/config.c libfdcore/endpoints.c libfdcore/extensions.c libfdcore/messages.c libfdcore/p_ce.c libfdcore/p_out.c libfdcore/p_psm.c libfdcore/p_sr.c libfdcore/peers.c libfdcore/queues.c libfdcore/routing_dispatch.c libfdcore/server.c libfdproto/dispatch.c libfdproto/fdproto-internal.h libfdproto/messages.c tests/testdisp.c
diffstat 20 files changed, 223 insertions(+), 134 deletions(-) [+]
line wrap: on
line diff
--- a/extensions/dbg_interactive/endpoints.i	Mon May 13 18:50:26 2013 +0800
+++ b/extensions/dbg_interactive/endpoints.i	Mon May 13 19:17:13 2013 +0800
@@ -127,7 +127,7 @@
 	void dump() {
 		char * buf = NULL;
 		size_t len;
-		printf("%s", fd_ep_dump_one(&buf, &len, NULL, $self));
+		printf("%s", fd_ep_dump_one(&buf, &len, NULL, 1, $self));
 		free(buf);
 	}
 }
--- a/extensions/dbg_monitor/dbg_monitor.c	Mon May 13 18:50:26 2013 +0800
+++ b/extensions/dbg_monitor/dbg_monitor.c	Mon May 13 19:17:13 2013 +0800
@@ -125,7 +125,7 @@
 		CHECK_FCT_DO( pthread_rwlock_unlock(&fd_g_peers_rw), /* continue */ );
 		
 		TRACE_DEBUG(INFO, "[dbg_monitor] Dumping servers information");
-		TRACE_DEBUG(INFO, "%s", fd_servers_dump(&buf, &len, NULL));
+		TRACE_DEBUG(INFO, "%s", fd_servers_dump(&buf, &len, NULL, 1));
 		
 		sleep(1);
 	}
--- a/include/freeDiameter/libfdcore.h	Mon May 13 18:50:26 2013 +0800
+++ b/include/freeDiameter/libfdcore.h	Mon May 13 19:17:13 2013 +0800
@@ -813,7 +813,7 @@
 /* The "old" FD_EV_DUMP_* events are replaced with direct calls to the following dump functions */
 DECLARE_FD_DUMP_PROTOTYPE(fd_conf_dump);
 DECLARE_FD_DUMP_PROTOTYPE(fd_ext_dump);
-DECLARE_FD_DUMP_PROTOTYPE(fd_servers_dump);
+DECLARE_FD_DUMP_PROTOTYPE(fd_servers_dump, int details);
 #endif /* SWIG */
 DECLARE_FD_DUMP_PROTOTYPE(fd_peer_dump_list, int details);
 DECLARE_FD_DUMP_PROTOTYPE(fd_peer_dump, struct peer_hdr * p, int details);
@@ -852,8 +852,8 @@
 int fd_ep_filter_family( struct fd_list * list, int af );
 int fd_ep_filter_list( struct fd_list * list, struct fd_list * exclude_list );
 int fd_ep_clearflags( struct fd_list * list, uint32_t flags );
-DECLARE_FD_DUMP_PROTOTYPE(fd_ep_dump_one, struct fd_endpoint * ep );
-DECLARE_FD_DUMP_PROTOTYPE(fd_ep_dump, int indent, struct fd_list * eps  );
+DECLARE_FD_DUMP_PROTOTYPE(fd_ep_dump_one, int preamble, struct fd_endpoint * ep );
+DECLARE_FD_DUMP_PROTOTYPE(fd_ep_dump, int preamble, int indent, struct fd_list * eps  );
 
 
 /*============================================================*/
@@ -935,7 +935,7 @@
 		 */
 	
 	HOOK_MESSAGE_LOCAL,
-		/* Hook called when a request message has been created locally and is being sent.
+		/* Hook called when a request message has been created locally by an extension and is being sent.
 		 - {msg} points to the message.
 		 - {peer} is NULL
 		 - {other} is NULL
--- a/include/freeDiameter/libfdproto.h	Mon May 13 18:50:26 2013 +0800
+++ b/include/freeDiameter/libfdproto.h	Mon May 13 19:17:13 2013 +0800
@@ -2921,6 +2921,8 @@
  *  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")
+ *  drop_reason : if set on return, the message must be freed for this reason.
+ *  drop_msg    : if drop_reason is set, this points to the message to be freed while *msg is NULL.
  *
  * DESCRIPTION: 
  *   Call all handlers registered for a given message.
@@ -2933,7 +2935,7 @@
  *  EINVAL 	: A parameter is invalid.
  *  (other errors)
  */
-int fd_msg_dispatch ( struct msg ** msg, struct session * session, enum disp_action *action, char ** error_code );
+int fd_msg_dispatch ( struct msg ** msg, struct session * session, enum disp_action *action, char ** error_code, char ** drop_reason, struct msg ** drop_msg );
 
 
 
--- a/libfdcore/config.c	Mon May 13 18:50:26 2013 +0800
+++ b/libfdcore/config.c	Mon May 13 19:17:13 2013 +0800
@@ -84,7 +84,7 @@
 {
 	FD_DUMP_HANDLE_OFFSET();
 	
-	CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "{freeDiameter configuration}(@%p): \n", fd_g_config), return NULL);	
+	CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "freeDiameter configuration:\n", fd_g_config), return NULL);	
 	CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "  Default trace level .... : %+d\n", fd_g_debug_lvl), return NULL);
 	CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "  Configuration file ..... : %s\n", fd_g_config->cnf_file), return NULL);
 	CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "  Diameter Identity ...... : %s (l:%Zi)\n", fd_g_config->cnf_diamid, fd_g_config->cnf_diamid_len), return NULL);
@@ -98,17 +98,17 @@
 	if (FD_IS_LIST_EMPTY(&fd_g_config->cnf_endpoints)) {
 		CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "  Local endpoints ........ : Default (use all available)\n"), return NULL);
 	} else {
-		CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "  Local endpoints ........ : \n"), return NULL);
-		CHECK_MALLOC_DO( fd_ep_dump( FD_DUMP_STD_PARAMS, 29, &fd_g_config->cnf_endpoints ), return NULL);
+		CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "  Local endpoints ........ : "), return NULL);
+		CHECK_MALLOC_DO( fd_ep_dump( FD_DUMP_STD_PARAMS, 0, 0, &fd_g_config->cnf_endpoints ), return NULL);
 	}
 	if (FD_IS_LIST_EMPTY(&fd_g_config->cnf_apps)) {
-		CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "  Local applications ..... : (none)\n"), return NULL);
+		CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "  Local applications ..... : (none)"), return NULL);
 	} else {
 		struct fd_list * li = fd_g_config->cnf_apps.next;
-		CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "  Local applications ..... : \n"), return NULL);
+		CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "  Local applications ..... : "), return NULL);
 		while (li != &fd_g_config->cnf_apps) {
 			struct fd_app * app = (struct fd_app *)li;
-			CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "                             App: %u\t%s%s\tVnd: %u\n", 
+			CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "App: %u,%s%s,Vnd:%u\t", 
 					app->appid,
 					app->flags.auth ? "Au" : "--",
 					app->flags.acct ? "Ac" : "--",
@@ -117,7 +117,7 @@
 		}
 	}
 	
-	CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "  Flags : - IP ........... : %s\n", fd_g_config->cnf_flags.no_ip4 ? "DISABLED" : "Enabled"), return NULL);
+	CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "\n  Flags : - IP ........... : %s\n", fd_g_config->cnf_flags.no_ip4 ? "DISABLED" : "Enabled"), return NULL);
 	CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "          - IPv6 ......... : %s\n", fd_g_config->cnf_flags.no_ip6 ? "DISABLED" : "Enabled"), return NULL);
 	CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "          - Relay app .... : %s\n", fd_g_config->cnf_flags.no_fwd ? "DISABLED" : "Enabled"), return NULL);
 	CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "          - TCP .......... : %s\n", fd_g_config->cnf_flags.no_tcp ? "DISABLED" : "Enabled"), return NULL);
@@ -587,7 +587,7 @@
 		free(dhparams.data);
 		
 	} else {
-		TRACE_DEBUG(INFO, "Generating fresh Diffie-Hellman parameters of size %d (this takes some time)... ", fd_g_config->cnf_sec_data.dh_bits ?: GNUTLS_DEFAULT_DHBITS);
+		LOG_D( "Generating fresh Diffie-Hellman parameters of size %d (this takes some time)... ", fd_g_config->cnf_sec_data.dh_bits ?: GNUTLS_DEFAULT_DHBITS);
 		CHECK_GNUTLS_DO( gnutls_dh_params_generate2( 
 					fd_g_config->cnf_sec_data.dh_cache,
 					fd_g_config->cnf_sec_data.dh_bits ?: GNUTLS_DEFAULT_DHBITS),
--- a/libfdcore/endpoints.c	Mon May 13 18:50:26 2013 +0800
+++ b/libfdcore/endpoints.c	Mon May 13 19:17:13 2013 +0800
@@ -284,11 +284,13 @@
 	return 0;
 }
 
-DECLARE_FD_DUMP_PROTOTYPE(fd_ep_dump_one, struct fd_endpoint * ep )
+DECLARE_FD_DUMP_PROTOTYPE(fd_ep_dump_one, int preamble, struct fd_endpoint * ep )
 {
 	FD_DUMP_HANDLE_OFFSET();
 	
-	CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "{ep}(@%p): ", ep), return NULL);
+	if (preamble) {
+		CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "{ep}(@%p): ", ep), return NULL);
+	}
 	
 	if (!ep) {
 		CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "INVALID/NULL"), return NULL);
@@ -296,27 +298,33 @@
 	}
 	
 	CHECK_MALLOC_DO( fd_sa_dump_node_serv( FD_DUMP_STD_PARAMS, &ep->sa, NI_NUMERICHOST | NI_NUMERICSERV ), return NULL);
-	CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, " {%s%s%s%s%s}",
-			(ep->flags & EP_FL_CONF) 	? "C" : "-",
-			(ep->flags & EP_FL_DISC) 	? "D" : "-",
-			(ep->flags & EP_FL_ADV) 	? "A" : "-",
-			(ep->flags & EP_FL_LL) 		? "L" : "-",
-			(ep->flags & EP_FL_PRIMARY) 	? "P" : "-"), return NULL);
+	CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "{%s%s%s%s%s}",
+				(ep->flags & EP_FL_CONF) 	? "C" : "-",
+				(ep->flags & EP_FL_DISC) 	? "D" : "-",
+				(ep->flags & EP_FL_ADV) 	? "A" : "-",
+				(ep->flags & EP_FL_LL) 		? "L" : "-",
+				(ep->flags & EP_FL_PRIMARY) 	? "P" : "-"), return NULL);
 	return *buf;
 }
 
-DECLARE_FD_DUMP_PROTOTYPE(fd_ep_dump, int indent, struct fd_list * eps  )
+DECLARE_FD_DUMP_PROTOTYPE(fd_ep_dump, int preamble, int indent, struct fd_list * eps  )
 {
 	struct fd_list * li;
 	
 	FD_DUMP_HANDLE_OFFSET();
 	
-	CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "%*s{eps}(@%p):", indent, "", eps), return NULL);
+	if (preamble) {
+		CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "%*s{eps}(@%p):", indent, "", eps), return NULL);
+	}
 	if (eps) {
 		for (li = eps->next; li != eps; li = li->next) {
 			struct fd_endpoint * ep = (struct fd_endpoint *)li;
-			CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "\n%*s", indent+1, ""), return NULL);
-			CHECK_MALLOC_DO( fd_ep_dump_one( FD_DUMP_STD_PARAMS, ep ), return NULL);
+			if (preamble) {
+				CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "\n%*s", indent+1, ""), return NULL);
+			} else if (li->prev != eps) {
+				CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "\t"), return NULL);
+			}
+			CHECK_MALLOC_DO( fd_ep_dump_one( FD_DUMP_STD_PARAMS, preamble, ep ), return NULL);
 		}
 	}
 }
--- a/libfdcore/extensions.c	Mon May 13 18:50:26 2013 +0800
+++ b/libfdcore/extensions.c	Mon May 13 19:17:13 2013 +0800
@@ -150,7 +150,7 @@
 	for (li = ext_list.next; li != &ext_list; li = li->next)
 	{
 		struct fd_ext_info * ext = (struct fd_ext_info *)li;
-		TRACE_DEBUG (INFO, "Loading : %s", ext->filename);
+		LOG_D( "Loading : %s", ext->filename);
 		
 		/* Load the extension */
 #ifndef DEBUG
@@ -161,7 +161,7 @@
 #endif /* DEBUG */
 		if (ext->handler == NULL) {
 			/* An error occured */
-			TRACE_ERROR("Loading of extension %s failed: %s", ext->filename, dlerror());
+			LOG_F("Loading of extension %s failed: %s", ext->filename, dlerror());
 			#ifdef DEBUG
 			ext->handler = dlopen(ext->filename, RTLD_LAZY | RTLD_GLOBAL);
 			if (ext->handler) {
@@ -207,7 +207,7 @@
 		/* Proceed to the next extension */
 	}
 
-	TRACE_DEBUG (INFO, "All extensions loaded.");
+	LOG_N("All extensions loaded.");
 	
 	/* We have finished. */
 	return 0;
--- a/libfdcore/messages.c	Mon May 13 18:50:26 2013 +0800
+++ b/libfdcore/messages.c	Mon May 13 19:17:13 2013 +0800
@@ -313,19 +313,36 @@
 	return 0;
 }
 
+static int fd_msg_send_int( struct msg ** pmsg, void (*anscb)(void *, struct msg **), void * data, void (*expirecb)(void *, DiamId_t, size_t, struct msg **), const struct timespec *timeout )
+{
+	struct msg_hdr *hdr;
+	DiamId_t diamid;
+	size_t diamidlen;
+	
+	/* Save the callback in the message, with the timeout */
+	CHECK_FCT(  fd_msg_anscb_associate( *pmsg, anscb, data, expirecb, timeout )  );
+	
+	/* If this is a new request, call the HOOK_MESSAGE_LOCAL hook */
+	if ( (fd_msg_hdr(*pmsg, &hdr) == 0)
+	 &&  (hdr->msg_flags & CMD_FLAG_REQUEST)
+	 &&  (fd_msg_source_get(*pmsg, &diamid, &diamidlen) == 0)
+	 &&  (diamid == NULL)) {
+		fd_hook_call(HOOK_MESSAGE_LOCAL, *pmsg, NULL, NULL, fd_msg_pmdl_get(*pmsg));
+	}
+		
+	/* Post the message in the outgoing queue */
+	CHECK_FCT( fd_fifo_post(fd_g_outgoing, pmsg) );
+	
+	return 0;
+}
+
 /* Send a message and optionaly register a callback for an answer */
 int fd_msg_send ( struct msg ** pmsg, void (*anscb)(void *, struct msg **), void * data )
 {
 	TRACE_ENTRY("%p %p %p", pmsg, anscb, data);
 	CHECK_PARAMS( pmsg );
 	
-	/* Save the callback in the message */
-	CHECK_FCT(  fd_msg_anscb_associate( *pmsg, anscb, data, NULL, NULL /* we should maybe use a safeguard here like 1 hour or so? */ )  );
-	
-	/* Post the message in the outgoing queue */
-	CHECK_FCT( fd_fifo_post(fd_g_outgoing, pmsg) );
-	
-	return 0;
+	return fd_msg_send_int(pmsg, anscb, data, NULL, NULL);
 }
 
 /* The variation of the same function with a timeout callback */
@@ -334,13 +351,7 @@
 	TRACE_ENTRY("%p %p %p %p %p", pmsg, anscb, data, expirecb, timeout);
 	CHECK_PARAMS( pmsg && expirecb && timeout );
 	
-	/* Save the callback in the message, with the timeout */
-	CHECK_FCT(  fd_msg_anscb_associate( *pmsg, anscb, data, expirecb, timeout )  );
-	
-	/* Post the message in the outgoing queue */
-	CHECK_FCT( fd_fifo_post(fd_g_outgoing, pmsg) );
-	
-	return 0;
+	return fd_msg_send_int(pmsg, anscb, data, expirecb, timeout);
 }
 
 
--- a/libfdcore/p_ce.c	Mon May 13 18:50:26 2013 +0800
+++ b/libfdcore/p_ce.c	Mon May 13 19:17:13 2013 +0800
@@ -649,7 +649,7 @@
 	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_hook_call(HOOK_MESSAGE_DROPPED, *cer, NULL, "An error occurred while rejecting this CER.", fd_msg_pmdl_get(*cer));
 		fd_msg_free(*cer);
 		*cer = NULL;
 	}
--- a/libfdcore/p_out.c	Mon May 13 18:50:26 2013 +0800
+++ b/libfdcore/p_out.c	Mon May 13 19:17:13 2013 +0800
@@ -97,7 +97,7 @@
 	struct msg *msg = arg;
 	CHECK_FCT_DO(fd_fifo_post(fd_g_outgoing, &msg),
 		{
-			//fd_msg_log( FD_MSG_LOG_DROPPED, msg, "An error occurred while attempting to requeue this message during cancellation of the sending function");
+			fd_hook_call(HOOK_MESSAGE_DROPPED, msg, NULL, "An error occurred while attempting to requeue this message during cancellation of the sending function", fd_msg_pmdl_get(msg));
 			CHECK_FCT_DO(fd_msg_free(msg), /* What can we do more? */);
 		} );
 }
@@ -130,7 +130,9 @@
 		CHECK_FCT_DO( ret = do_send(&msg, 0, peer->p_cnxctx, &peer->p_hbh, peer),
 			{
 				if (msg) {
-					//fd_msg_log( FD_MSG_LOG_DROPPED, msg, "Internal error: Problem while sending (%s)", strerror(ret) );
+					char buf[256];
+					snprintf(buf, sizeof(buf), "Error while sending this message: s", strerror(ret));
+					fd_hook_call(HOOK_MESSAGE_DROPPED, msg, NULL, buf, fd_msg_pmdl_get(msg));
 					fd_msg_free(msg);
 				}
 			} );
@@ -182,7 +184,9 @@
 		CHECK_FCT_DO( ret = do_send(msg, flags, cnx, hbh, peer),
 			{
 				if (msg) {
-					//fd_msg_log( FD_MSG_LOG_DROPPED, *msg, "Internal error: Problem while sending (%s)", strerror(ret) );
+					char buf[256];
+					snprintf(buf, sizeof(buf), "Error while sending this message: s", strerror(ret));
+					fd_hook_call(HOOK_MESSAGE_DROPPED, *msg, NULL, buf, fd_msg_pmdl_get(*msg));
 					fd_msg_free(*msg);
 					*msg = NULL;
 				}
--- a/libfdcore/p_psm.c	Mon May 13 18:50:26 2013 +0800
+++ b/libfdcore/p_psm.c	Mon May 13 19:17:13 2013 +0800
@@ -240,7 +240,7 @@
 			
 			case FDEVP_CNX_INCOMING: {
 				struct cnx_incoming * evd = ev->data;
-				//fd_msg_log( FD_MSG_LOG_DROPPED, evd->cer, "Message discarded while cleaning peer state machine queue." );
+				fd_hook_call(HOOK_MESSAGE_DROPPED, evd->cer, NULL, "Message discarded while cleaning peer state machine queue.", fd_msg_pmdl_get(evd->cer));
 				CHECK_FCT_DO( fd_msg_free(evd->cer), /* continue */);
 				fd_cnx_destroy(evd->cnx);
 			}
@@ -576,6 +576,7 @@
 					/* In such case, just discard the message */
 					char buf[128];
 					snprintf(buf, sizeof(buf), "Received while peer state machine was in state %s.", STATE_STR(cur_state));
+					LOG_E("%s",buf); 
 					fd_hook_call(HOOK_MESSAGE_DROPPED, msg, peer, buf, fd_msg_pmdl_get(msg));
 					fd_msg_free(msg);
 				}
@@ -589,8 +590,11 @@
 			int ret = fd_msg_parse_or_error( &msg, &error );
 			if (ret != EBADMSG) {
 				CHECK_FCT_DO( ret, 
-					{ 
-						LOG_E("%s: An unexpected error occurred while parsing a link-local message", peer->p_hdr.info.pi_diamid); 
+					{
+						char buf[256];
+						snprintf(buf, sizeof(buf), "%s: An unexpected error occurred while parsing a link-local message", peer->p_hdr.info.pi_diamid); 
+						LOG_E("%s",buf); 
+						fd_hook_call(HOOK_MESSAGE_DROPPED, msg, peer, buf, fd_msg_pmdl_get(msg));
 						fd_msg_free(msg); 
 						goto psm_end; 
 					} );
@@ -599,13 +603,18 @@
 					/* Send the error back to the peer */
 					CHECK_FCT_DO( ret = fd_out_send(&error, NULL, peer, FD_CNX_ORDERED),  );
 					if (error) {
+						char buf[256];
 						/* Only if an error occurred & the message was not saved / dumped */
-						LOG_E("%s: error sending a message", peer->p_hdr.info.pi_diamid); 
+						snprintf(buf, sizeof(buf), "%s: error sending a message", peer->p_hdr.info.pi_diamid); 
+						LOG_E("%s",buf); 
+						fd_hook_call(HOOK_MESSAGE_DROPPED, error, peer, buf, fd_msg_pmdl_get(error));
 						CHECK_FCT_DO( fd_msg_free(error), goto psm_end);
 					}
 				} else {
+					char buf[256];
 					/* We received an invalid answer, let's disconnect */
-					LOG_E("%s: Received invalid answer to Base protocol message, disconnecting...", peer->p_hdr.info.pi_diamid);
+					snprintf(buf, sizeof(buf), "%s: Received invalid answer to Base protocol message, disconnecting...", peer->p_hdr.info.pi_diamid);
+					LOG_E("%s",buf); 
 					CHECK_FCT_DO( fd_msg_free(msg), goto psm_end);
 					CHECK_FCT_DO( fd_event_send(peer->p_events, FDEVP_CNX_ERROR, 0, NULL), goto psm_reset );
 				}
@@ -656,7 +665,10 @@
 				
 				/* Cleanup the message if not done */
 				if (msg) {
-					//fd_msg_log( FD_MSG_LOG_DROPPED, msg, "Received un-handled non-routable command from peer '%s'.", peer->p_hdr.info.pi_diamid );
+					char buf[256];
+					snprintf(buf, sizeof(buf), "Received un-handled non-routable command from peer '%s'.", peer->p_hdr.info.pi_diamid);
+					LOG_E("%s",buf);
+					fd_hook_call(HOOK_MESSAGE_DROPPED, msg, NULL, buf, fd_msg_pmdl_get(msg));
 					CHECK_FCT_DO( fd_msg_free(msg), /* continue */);
 					msg = NULL;
 				}
@@ -664,7 +676,10 @@
 		
 		/* At this point the message must have been fully handled already */
 		if (msg) {
-			//fd_msg_log( FD_MSG_LOG_DROPPED, msg, "Internal error ('%s'): unhandled message.", peer->p_hdr.info.pi_diamid );
+			char buf[256];
+			snprintf(buf, sizeof(buf), "Internal error ('%s'): unhandled message.", peer->p_hdr.info.pi_diamid);
+			LOG_E("%s",buf);
+			fd_hook_call(HOOK_MESSAGE_DROPPED, msg, NULL, buf, fd_msg_pmdl_get(msg));
 			fd_msg_free(msg);
 		}
 		
@@ -731,7 +746,7 @@
 		{
 			char * buf = NULL;
 			size_t len = 0;
-			LOG_D("New remote endpoint(s): %s",  fd_ep_dump(&buf, &len, NULL, 6, &peer->p_hdr.info.pi_endpoints) ?: "error");
+			LOG_D("New remote endpoint(s): %s",  fd_ep_dump(&buf, &len, NULL, 0, 0, &peer->p_hdr.info.pi_endpoints) ?: "error");
 			free(buf);
 		}
 		
--- a/libfdcore/p_sr.c	Mon May 13 18:50:26 2013 +0800
+++ b/libfdcore/p_sr.c	Mon May 13 19:17:13 2013 +0800
@@ -118,7 +118,7 @@
 	
 	/* If the callback did not dispose of the message, do it now */
 	if (ed->request) {
-		//fd_msg_log(FD_MSG_LOG_DROPPED, ed->request, "Expiration period completed without an answer, and the expiry callback did not dispose of the message.");
+		fd_hook_call(HOOK_MESSAGE_DROPPED, ed->request, NULL, "Expiration period completed without an answer, and the expiry callback did not dispose of the message.", fd_msg_pmdl_get(ed->request));
 		CHECK_FCT_DO( fd_msg_free(ed->request), /* ignore */ );
 	}
 	
@@ -322,15 +322,19 @@
 			if (hdr)
 				hdr->msg_flags |= CMD_FLAG_RETRANSMIT;
 			
+			fd_hook_call(HOOK_MESSAGE_FAILOVER, sr->req, (struct fd_peer *)srlist->srs.o, NULL, fd_msg_pmdl_get(sr->req));
+			
 			/* Requeue for sending to another peer */
 			CHECK_FCT_DO( ret = fd_fifo_post(fd_g_outgoing, &sr->req),
 				{
-					//fd_msg_log( FD_MSG_LOG_DROPPED, sr->req, "Internal error: error while requeuing during failover: %s", strerror(ret) );
+					char buf[256];
+					snprintf(buf, sizeof(buf), "Internal error: error while requeuing during failover: %s", strerror(ret));
+					fd_hook_call(HOOK_MESSAGE_DROPPED, sr->req, NULL, buf, fd_msg_pmdl_get(sr->req));
 					CHECK_FCT_DO(fd_msg_free(sr->req), /* What can we do more? */)
 				});
 		} else {
 			/* Just free the request. */
-			//fd_msg_log( FD_MSG_LOG_DROPPED, sr->req, "Sent & unanswered local message discarded during failover." );
+			/* fd_hook_call(HOOK_MESSAGE_DROPPED, sr->req, NULL, "Sent & unanswered local message discarded during failover.", fd_msg_pmdl_get(sr->req)); */
 			CHECK_FCT_DO(fd_msg_free(sr->req), /* Ignore */);
 		}
 		free(sr);
--- a/libfdcore/peers.c	Mon May 13 18:50:26 2013 +0800
+++ b/libfdcore/peers.c	Mon May 13 19:17:13 2013 +0800
@@ -241,10 +241,11 @@
 	
 	/* Requeue all messages in the "out" queue */
 	while ( fd_fifo_tryget(peer->p_tosend, &m) == 0 ) {
+		fd_hook_call(HOOK_MESSAGE_FAILOVER, m, peer, NULL, fd_msg_pmdl_get(m));
 		CHECK_FCT_DO(fd_fifo_post(fd_g_outgoing, &m), 
 			{
 				/* fallback: destroy the message */
-				//fd_msg_log(FD_MSG_LOG_DROPPED, m, "Internal error: unable to requeue this message during failover process");
+				fd_hook_call(HOOK_MESSAGE_DROPPED, m, NULL, "Internal error: unable to requeue this message during failover process", fd_msg_pmdl_get(m));
 				CHECK_FCT_DO(fd_msg_free(m), /* What can we do more? */)
 			} );
 	}
--- a/libfdcore/queues.c	Mon May 13 18:50:26 2013 +0800
+++ b/libfdcore/queues.c	Mon May 13 19:17:13 2013 +0800
@@ -73,7 +73,7 @@
 		CHECK_FCT(ret);
 		
 		/* We got one! */
-		//fd_msg_log( FD_MSG_LOG_DROPPED, msg, "Message lost because framework is terminating." );
+		fd_hook_call(HOOK_MESSAGE_DROPPED, msg, NULL, "Message lost because framework is terminating.", fd_msg_pmdl_get(msg));
 		fd_msg_free(msg);
 	}
 	
--- a/libfdcore/routing_dispatch.c	Mon May 13 18:50:26 2013 +0800
+++ b/libfdcore/routing_dispatch.c	Mon May 13 19:17:13 2013 +0800
@@ -392,7 +392,9 @@
 			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);
+				char buf[256];
+				snprintf(buf, sizeof(buf), "Unable to send error '%s' to deleted peer '%s' in reply to this message.", error_code, id);
+				fd_hook_call(HOOK_MESSAGE_DROPPED, *pmsg, NULL, buf, fd_msg_pmdl_get(*pmsg));
 				fd_msg_free(*pmsg);
 				*pmsg = NULL;
 				return 0;
@@ -495,7 +497,7 @@
 	CHECK_FCT( fd_msg_sess_get(fd_g_config->cnf_dict, msgptr, &sess, NULL) );
 
 	/* Now, call any callback registered for the message */
-	CHECK_FCT( fd_msg_dispatch ( &msgptr, sess, &action, &ec) );
+	CHECK_FCT( fd_msg_dispatch ( &msgptr, sess, &action, &ec, &em, &error) );
 
 	/* Now, act depending on msg and action and ec */
 	if (msgptr) {
@@ -504,6 +506,7 @@
 				/* No callback has handled the message, let's reply with a generic error or relay it */
 				if (!fd_g_config->cnf_flags.no_fwd) {
 					/* requeue to fd_g_outgoing */
+					fd_hook_call(HOOK_MESSAGE_ROUTING_FORWARD, msgptr, NULL, NULL, fd_msg_pmdl_get(msgptr));
 					CHECK_FCT( fd_fifo_post(fd_g_outgoing, &msgptr) );
 					break;
 				}
@@ -518,7 +521,7 @@
 				}
 				
 				if (!is_req) {
-					//fd_msg_log( FD_MSG_LOG_DROPPED, msgptr,  "Internal error: Answer received to locally issued request, but not handled by any handler.");
+					fd_hook_call(HOOK_MESSAGE_DROPPED, msgptr, NULL, "Internal error: Answer received to locally issued request, but not handled by any handler.", fd_msg_pmdl_get(msgptr));
 					fd_msg_free(msgptr);
 					break;
 				}
@@ -531,6 +534,9 @@
 				/* Now, send the message */
 				CHECK_FCT( fd_fifo_post(fd_g_outgoing, &msgptr) );
 		}
+	} else if (em) {
+		fd_hook_call(HOOK_MESSAGE_DROPPED, error, NULL, em, fd_msg_pmdl_get(error));
+		fd_msg_free(error);
 	}
 	
 	/* We're done with dispatching this message */
@@ -554,6 +560,7 @@
 
 	/* Handle incorrect bits */
 	if (is_req && is_err) {
+		fd_hook_call(HOOK_MESSAGE_PARSING_ERROR, msgptr, NULL, "R & E bits were set", fd_msg_pmdl_get(msgptr));
 		CHECK_FCT( return_error( &msgptr, "DIAMETER_INVALID_HDR_BITS", "R & E bits were set", NULL) );
 		return 0;
 	}
@@ -572,8 +579,7 @@
 
 		/* 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 or " _stringize(AI_RELAY) " (relay),"
-					  " returning DIAMETER_APPLICATION_UNSUPPORTED");
+			fd_hook_call(HOOK_MESSAGE_PARSING_ERROR, msgptr, NULL, "Received a routable message with application id 0 or " _stringize(AI_RELAY) " (relay)", fd_msg_pmdl_get(msgptr));
 			CHECK_FCT( return_error( &msgptr, "DIAMETER_APPLICATION_UNSUPPORTED", "Routable message with application id 0 or relay", NULL) );
 			return 0;
 		} else {
@@ -600,9 +606,11 @@
 						CHECK_FCT_DO( ret = fd_msg_parse_dict ( avp, fd_g_config->cnf_dict, &error_info ),
 							{
 								if (error_info.pei_errcode) {
+									fd_hook_call(HOOK_MESSAGE_PARSING_ERROR, msgptr, NULL, error_info.pei_message ?: error_info.pei_errcode, fd_msg_pmdl_get(msgptr));
 									CHECK_FCT( return_error( &msgptr, error_info.pei_errcode, error_info.pei_message, error_info.pei_avp) );
 									return 0;
 								} else {
+									fd_hook_call(HOOK_MESSAGE_PARSING_ERROR, msgptr, NULL, "Unspecified error while parsing Destination-Host AVP", fd_msg_pmdl_get(msgptr));
 									return ret;
 								}
 							} );
@@ -620,9 +628,11 @@
 						CHECK_FCT_DO( ret = fd_msg_parse_dict ( avp, fd_g_config->cnf_dict, &error_info ),
 							{
 								if (error_info.pei_errcode) {
+									fd_hook_call(HOOK_MESSAGE_PARSING_ERROR, msgptr, NULL, error_info.pei_message ?: error_info.pei_errcode, fd_msg_pmdl_get(msgptr));
 									CHECK_FCT( return_error( &msgptr, error_info.pei_errcode, error_info.pei_message, error_info.pei_avp) );
 									return 0;
 								} else {
+									fd_hook_call(HOOK_MESSAGE_PARSING_ERROR, msgptr, NULL, "Unspecified error while parsing Destination-Realm AVP", fd_msg_pmdl_get(msgptr));
 									return ret;
 								}
 							} );
@@ -642,9 +652,11 @@
 						CHECK_FCT_DO( ret = fd_msg_parse_dict ( avp, fd_g_config->cnf_dict, &error_info ),
 							{
 								if (error_info.pei_errcode) {
+									fd_hook_call(HOOK_MESSAGE_PARSING_ERROR, msgptr, NULL, error_info.pei_message ?: error_info.pei_errcode, fd_msg_pmdl_get(msgptr));
 									CHECK_FCT( return_error( &msgptr, error_info.pei_errcode, error_info.pei_message, error_info.pei_avp) );
 									return 0;
 								} else {
+									fd_hook_call(HOOK_MESSAGE_PARSING_ERROR, msgptr, NULL, "Unspecified error while parsing User-Name AVP", fd_msg_pmdl_get(msgptr));
 									return ret;
 								}
 							} );
@@ -667,6 +679,7 @@
 
 		/* Handle the missing routing AVPs first */
 		if ( is_dest_realm == UNKNOWN ) {
+			fd_hook_call(HOOK_MESSAGE_PARSING_ERROR, msgptr, NULL, "Non-routable message not supported (invalid bit ? missing Destination-Realm ?)", fd_msg_pmdl_get(msgptr));
 			CHECK_FCT( return_error( &msgptr, "DIAMETER_COMMAND_UNSUPPORTED", "Non-routable message not supported (invalid bit ? missing Destination-Realm ?)", NULL) );
 			return 0;
 		}
@@ -675,9 +688,11 @@
 		if (is_dest_host == YES) {
 			if (is_local_app == YES) {
 				/* Ok, give the message to the dispatch thread */
+				fd_hook_call(HOOK_MESSAGE_ROUTING_LOCAL, msgptr, NULL, NULL, fd_msg_pmdl_get(msgptr));
 				CHECK_FCT( fd_fifo_post(fd_g_local, &msgptr) );
 			} else {
 				/* We don't support the application, reply an error */
+				fd_hook_call(HOOK_MESSAGE_PARSING_ERROR, msgptr, NULL, "Application unsupported", fd_msg_pmdl_get(msgptr));
 				CHECK_FCT( return_error( &msgptr, "DIAMETER_APPLICATION_UNSUPPORTED", NULL, NULL) );
 			}
 			return 0;
@@ -686,6 +701,7 @@
 		/* 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) {
+				fd_hook_call(HOOK_MESSAGE_ROUTING_ERROR, msgptr, NULL, "Message for another realm/host", fd_msg_pmdl_get(msgptr));
 				CHECK_FCT( return_error( &msgptr, "DIAMETER_UNABLE_TO_DELIVER", "I am not a Diameter agent", NULL) );
 				return 0;
 			}
@@ -699,6 +715,7 @@
 				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 */
+						fd_hook_call(HOOK_MESSAGE_PARSING_ERROR, msgptr, NULL, "Failed to process decorated NAI", fd_msg_pmdl_get(msgptr));
 						CHECK_FCT( return_error( &msgptr, "DIAMETER_INVALID_AVP_VALUE", "Failed to process decorated NAI", un) );
 						return 0;
 					} );
@@ -712,12 +729,14 @@
 
 			if (is_local_app == YES) {
 				/* Handle localy since we are able to */
+				fd_hook_call(HOOK_MESSAGE_ROUTING_LOCAL, msgptr, NULL, NULL, fd_msg_pmdl_get(msgptr));
 				CHECK_FCT(fd_fifo_post(fd_g_local, &msgptr) );
 				return 0;
 			}
 
 			if (fd_g_config->cnf_flags.no_fwd) {
 				/* We return an error */
+				fd_hook_call(HOOK_MESSAGE_ROUTING_ERROR, msgptr, NULL, "Application unsupported", fd_msg_pmdl_get(msgptr));
 				CHECK_FCT( return_error( &msgptr, "DIAMETER_APPLICATION_UNSUPPORTED", NULL, NULL) );
 				return 0;
 			}
@@ -735,6 +754,7 @@
 
 		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. */
+			fd_hook_call(HOOK_MESSAGE_ROUTING_LOCAL, msgptr, NULL, NULL, fd_msg_pmdl_get(msgptr));
 			CHECK_FCT(fd_fifo_post(fd_g_local, &msgptr) );
 			return 0;
 		}
@@ -763,9 +783,13 @@
 			TRACE_DEBUG(ANNOYING, "Calling next FWD callback on %p : %p", msgptr, rh->rt_fwd_cb);
 			CHECK_FCT_DO( ret = (*rh->rt_fwd_cb)(rh->cbdata, &msgptr),
 				{
-					//fd_msg_log( FD_MSG_LOG_DROPPED, msgptr, "Internal error: a FWD routing callback returned an error (%s)", strerror(ret));
+					char buf[256];
+					snprintf(buf, sizeof(buf), "A FWD routing callback returned an error: %s", strerror(ret));
+					fd_hook_call(HOOK_MESSAGE_ROUTING_ERROR, msgptr, NULL, buf, fd_msg_pmdl_get(msgptr));
+					fd_hook_call(HOOK_MESSAGE_DROPPED, msgptr, NULL, buf, fd_msg_pmdl_get(msgptr));
 					fd_msg_free(msgptr);
 					msgptr = NULL;
+					break;
 				} );
 		}
 
@@ -779,8 +803,10 @@
 
 	/* Now pass the message to the next step: either forward to another peer, or dispatch to local extensions */
 	if (is_req || qry_src) {
+		fd_hook_call(HOOK_MESSAGE_ROUTING_FORWARD, msgptr, NULL, NULL, fd_msg_pmdl_get(msgptr));
 		CHECK_FCT(fd_fifo_post(fd_g_outgoing, &msgptr) );
 	} else {
+		fd_hook_call(HOOK_MESSAGE_ROUTING_LOCAL, msgptr, NULL, NULL, fd_msg_pmdl_get(msgptr));
 		CHECK_FCT(fd_fifo_post(fd_g_local, &msgptr) );
 	}
 
@@ -822,7 +848,10 @@
 		/* Find the peer corresponding to this name */
 		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, msgptr, "Unable to forward answer to deleted / closed peer '%s'.", qry_src);
+			char buf[128];
+			snprintf(buf, sizeof(buf), "Unable to forward answer to deleted / closed peer '%s'.", qry_src);
+			fd_hook_call(HOOK_MESSAGE_ROUTING_ERROR, msgptr, NULL, buf, fd_msg_pmdl_get(msgptr));
+			fd_hook_call(HOOK_MESSAGE_DROPPED, msgptr, NULL, buf, fd_msg_pmdl_get(msgptr));
 			fd_msg_free(msgptr);
 			return 0;
 		}
@@ -907,7 +936,10 @@
 			TRACE_DEBUG(ANNOYING, "Calling next OUT callback on %p : %p (prio %d)", msgptr, rh->rt_out_cb, rh->prio);
 			CHECK_FCT_DO( ret = (*rh->rt_out_cb)(rh->cbdata, msgptr, candidates),
 				{
-					//fd_msg_log( FD_MSG_LOG_DROPPED, msgptr, "Internal error: an OUT routing callback returned an error (%s)", strerror(ret));
+					char buf[256];
+					snprintf(buf, sizeof(buf), "An OUT routing callback returned an error: %s", strerror(ret));
+					fd_hook_call(HOOK_MESSAGE_ROUTING_ERROR, msgptr, NULL, buf, fd_msg_pmdl_get(msgptr));
+					fd_hook_call(HOOK_MESSAGE_DROPPED, msgptr, NULL, buf, fd_msg_pmdl_get(msgptr));
 					fd_msg_free(msgptr);
 					msgptr = NULL;
 					break;
@@ -955,7 +987,7 @@
 
 	/* If the message has not been sent, return an error */
 	if (msgptr) {
-		//fd_msg_log( FD_MSG_LOG_NODELIVER, msgptr, "No suitable candidate to route the message to." );
+		fd_hook_call(HOOK_MESSAGE_ROUTING_ERROR, msgptr, NULL, "No remaining suitable candidate to route the message to", fd_msg_pmdl_get(msgptr));
 		return_error( &msgptr, "DIAMETER_UNABLE_TO_DELIVER", "No suitable candidate to route the message to", NULL);
 	}
 
--- a/libfdcore/server.c	Mon May 13 18:50:26 2013 +0800
+++ b/libfdcore/server.c	Mon May 13 19:17:13 2013 +0800
@@ -93,7 +93,7 @@
 
 
 /* Dump all servers information */
-DECLARE_FD_DUMP_PROTOTYPE(fd_servers_dump)
+DECLARE_FD_DUMP_PROTOTYPE(fd_servers_dump, int details)
 {
 	struct fd_list * li, *cli;
 	
@@ -103,24 +103,29 @@
 		struct server * s = (struct server *)li;
 		enum s_state st = get_status(s);
 		
-		CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "{server}(@%p)'%s': %s, %s, %s", s, fd_cnx_getid(s->conn), 
-				IPPROTO_NAME( s->proto ),
-				s->secur ? "Secur" : "NotSecur",
-				(st == NOT_CREATED) ? "Thread not created" :
-				((st == RUNNING) ? "Thread running" :
-				((st == TERMINATED) ? "Thread terminated" :
-							  "Thread status unknown"))), return NULL);
-		/* Dump the client list of this server */
-		CHECK_POSIX_DO( pthread_mutex_lock(&s->clients_mtx), );
-		for (cli = s->clients.next; cli != &s->clients; cli = cli->next) {
-			struct client * c = (struct client *)cli;
-			char bufts[128];
-			CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "\n  {client}(@%p)'%s': to:%s", c, fd_cnx_getid(c->conn), fd_log_time(&c->ts, bufts, sizeof(bufts))), break);
-		}
-		CHECK_POSIX_DO( pthread_mutex_unlock(&s->clients_mtx), );
-		
-		if (li->next != &FD_SERVERS) {
-			CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "\n"), return NULL);
+		if (details) {
+			CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "{server}(@%p)'%s': %s, %s, %s", s, fd_cnx_getid(s->conn), 
+					IPPROTO_NAME( s->proto ),
+					s->secur ? "Secur" : "NotSecur",
+					(st == NOT_CREATED) ? "Thread not created" :
+					((st == RUNNING) ? "Thread running" :
+					((st == TERMINATED) ? "Thread terminated" :
+								  "Thread status unknown"))), return NULL);
+			/* Dump the client list of this server */
+			CHECK_POSIX_DO( pthread_mutex_lock(&s->clients_mtx), );
+			for (cli = s->clients.next; cli != &s->clients; cli = cli->next) {
+				struct client * c = (struct client *)cli;
+				char bufts[128];
+				CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "\n  {client}(@%p)'%s': to:%s", c, fd_cnx_getid(c->conn), fd_log_time(&c->ts, bufts, sizeof(bufts))), break);
+			}
+			CHECK_POSIX_DO( pthread_mutex_unlock(&s->clients_mtx), );
+
+			if (li->next != &FD_SERVERS) {
+				CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "\n"), return NULL);
+			}
+		} else {
+			CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "'%s'(%s,%s)  ", fd_cnx_getid(s->conn), 
+					IPPROTO_NAME( s->proto ), s->secur ? "Secur" : "NotSecur"), return NULL);
 		}
 	}
 	
@@ -445,7 +450,7 @@
 		char * buf = NULL;
 		size_t len = 0, offset = 0;
 		CHECK_MALLOC_DO( fd_dump_extend( &buf, &len, &offset , "Local server address(es): "), );
-		CHECK_MALLOC_DO( fd_ep_dump(  &buf, &len, &offset, 5, &fd_g_config->cnf_endpoints ), );
+		CHECK_MALLOC_DO( fd_ep_dump(  &buf, &len, &offset, 0, 0, &fd_g_config->cnf_endpoints ), );
 		LOG_N("%s", buf ?: "Error dumping addresses");
 		free(buf);
 	}
--- a/libfdproto/dispatch.c	Mon May 13 18:50:26 2013 +0800
+++ b/libfdproto/dispatch.c	Mon May 13 19:17:13 2013 +0800
@@ -67,7 +67,8 @@
 
 /* Call CBs from a given list (any_handlers if cb_list is NULL) -- must have locked fd_disp_lock before */
 int fd_disp_call_cb_int( struct fd_list * cb_list, struct msg ** msg, struct avp *avp, struct session *sess, enum disp_action *action, 
-			struct dict_object * obj_app, struct dict_object * obj_cmd, struct dict_object * obj_avp, struct dict_object * obj_enu)
+			struct dict_object * obj_app, struct dict_object * obj_cmd, struct dict_object * obj_avp, struct dict_object * obj_enu,
+			char ** drop_reason, struct msg ** drop_msg)
 {
 	struct fd_list * senti, *li;
 	int r;
@@ -96,8 +97,8 @@
 		/* We have a match, the cb must be called. */
 		CHECK_FCT_DO( (r = (*hdl->cb)(msg, avp, sess, hdl->opaque, action)),
 			{
-				//fd_msg_log( FD_MSG_LOG_DROPPED, *msg, "Internal error: a DISPATCH callback returned an error (%s)", strerror(r));
-				fd_msg_free(*msg);
+				*drop_reason = "Internal error: a DISPATCH callback returned an error";
+				*drop_msg = *msg;
 				*msg = NULL;
 			}
 		 );
--- a/libfdproto/fdproto-internal.h	Mon May 13 18:50:26 2013 +0800
+++ b/libfdproto/fdproto-internal.h	Mon May 13 19:17:13 2013 +0800
@@ -54,7 +54,8 @@
 int fd_dict_disp_cb(enum dict_object_type type, struct dict_object *obj, struct fd_list ** cb_list);
 DECLARE_FD_DUMP_PROTOTYPE(fd_dict_dump_avp_value, union avp_value *avp_value, struct dict_object * model, int indent, int header);
 int fd_disp_call_cb_int( struct fd_list * cb_list, struct msg ** msg, struct avp *avp, struct session *sess, enum disp_action *action, 
-			struct dict_object * obj_app, struct dict_object * obj_cmd, struct dict_object * obj_avp, struct dict_object * obj_enu);
+			struct dict_object * obj_app, struct dict_object * obj_cmd, struct dict_object * obj_avp, struct dict_object * obj_enu,
+			char ** drop_reason, struct msg ** drop_msg);
 extern pthread_rwlock_t fd_disp_lock;
 
 /* Messages / sessions API */
--- a/libfdproto/messages.c	Mon May 13 18:50:26 2013 +0800
+++ b/libfdproto/messages.c	Mon May 13 19:17:13 2013 +0800
@@ -2669,7 +2669,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, char ** error_code)
+int fd_msg_dispatch ( struct msg ** msg, struct session * session, enum disp_action *action, char ** error_code, char ** drop_reason, struct msg ** drop_msg)
 {
 	struct dictionary  * dict;
 	struct dict_object * app;
@@ -2683,6 +2683,8 @@
 	
 	if (error_code)
 		*error_code = NULL;
+	if (drop_reason)
+		*drop_reason = NULL;
 	*action = DISP_ACT_CONT;
 	
 	/* Take the dispatch lock */
@@ -2690,7 +2692,7 @@
 	pthread_cleanup_push( fd_cleanup_rwlock, &fd_disp_lock );
 	
 	/* First, call the DISP_HOW_ANY callbacks */
-	CHECK_FCT_DO( ret = fd_disp_call_cb_int( NULL, msg, NULL, session, action, NULL, NULL, NULL, NULL ), goto out );
+	CHECK_FCT_DO( ret = fd_disp_call_cb_int( NULL, msg, NULL, session, action, NULL, NULL, NULL, NULL, drop_reason, drop_msg ), goto out );
 
 	TEST_ACTION_STOP();
 	
@@ -2707,8 +2709,8 @@
 				*error_code = "DIAMETER_APPLICATION_UNSUPPORTED";
 			*action = DISP_ACT_ERROR;
 		} else {
-			//fd_msg_log( FD_MSG_LOG_DROPPED, *msg, "Internal error: Received this answer to a local query with an unsupported application %d", (*msg)->msg_public.msg_appl);
-			fd_msg_free(*msg);
+			*drop_reason = "Internal error: Received this answer to a local query with an unsupported application";
+			*drop_msg = *msg;
 			*msg = NULL;
 		}
 		goto out;
@@ -2739,7 +2741,7 @@
 			}
 			
 			/* Call the callbacks */
-			CHECK_FCT_DO( ret = fd_disp_call_cb_int( cb_list, msg, avp, session, action, app, cmd, avp->avp_model, enumval ), goto out );
+			CHECK_FCT_DO( ret = fd_disp_call_cb_int( cb_list, msg, avp, session, action, app, cmd, avp->avp_model, enumval, drop_reason, drop_msg ), goto out );
 			TEST_ACTION_STOP();
 		}
 		/* Go to next AVP */
@@ -2748,12 +2750,12 @@
 		
 	/* Now call command and application callbacks */
 	CHECK_FCT_DO( ret = fd_dict_disp_cb(DICT_COMMAND, cmd, &cb_list), goto out );
-	CHECK_FCT_DO( ret = fd_disp_call_cb_int( cb_list, msg, NULL, session, action, app, cmd, NULL, NULL ), goto out );
+	CHECK_FCT_DO( ret = fd_disp_call_cb_int( cb_list, msg, NULL, session, action, app, cmd, NULL, NULL, drop_reason, drop_msg ), goto out );
 	TEST_ACTION_STOP();
 	
 	if (app) {
 		CHECK_FCT_DO( ret = fd_dict_disp_cb(DICT_APPLICATION, app, &cb_list), goto out );
-		CHECK_FCT_DO( ret = fd_disp_call_cb_int( cb_list, msg, NULL, session, action, app, cmd, NULL, NULL ), goto out );
+		CHECK_FCT_DO( ret = fd_disp_call_cb_int( cb_list, msg, NULL, session, action, app, cmd, NULL, NULL, drop_reason, drop_msg ), goto out );
 		TEST_ACTION_STOP();
 	}
 out:
--- a/tests/testdisp.c	Mon May 13 18:50:26 2013 +0800
+++ b/tests/testdisp.c	Mon May 13 19:17:13 2013 +0800
@@ -105,11 +105,11 @@
 	struct dict_object * cmd1, * cmd2;
 	struct dict_object * avp1, * avp2; /* avp2 is enumerated; they are both unsigned32 types */
 	struct dict_object * enu1, * enu2;
-	struct msg * msg = NULL;
+	struct msg * msg = NULL, *error;
 	enum disp_action action;
 	struct disp_hdl * hdl[NB_CB];
 	struct disp_when when;
-	char * ec;
+	char * ec, *em;
 	
 	/* First, initialize the daemon modules */
 	INIT_FD();
@@ -155,7 +155,7 @@
 		/* Check this handler is called for a message */
 		msg = new_msg( 0, cmd1, avp1, NULL, 0 );
 		memset(cbcalled, 0, sizeof(cbcalled));
-		CHECK( 0, fd_msg_dispatch ( &msg, sess, &action, &ec ) );
+		CHECK( 0, fd_msg_dispatch ( &msg, sess, &action, &ec, &em, &error ) );
 		CHECK( 1, cbcalled[0] );
 		CHECK( DISP_ACT_CONT, action );
 		
@@ -179,7 +179,7 @@
 		/* Check the callbacks are called as appropriate */
 		memset(cbcalled, 0, sizeof(cbcalled));
 		msg = new_msg( 0, cmd1, avp1, NULL, 0 );
-		CHECK( 0, fd_msg_dispatch ( &msg, sess, &action, &ec ) );
+		CHECK( 0, fd_msg_dispatch ( &msg, sess, &action, &ec, &em, &error ) );
 		CHECK( 1, cbcalled[0] );
 		CHECK( 0, cbcalled[1] );
 		CHECK( 0, cbcalled[2] );
@@ -190,7 +190,7 @@
 		
 		memset(cbcalled, 0, sizeof(cbcalled));
 		msg = new_msg( 1, cmd1, avp1, NULL, 0 );
-		CHECK( 0, fd_msg_dispatch ( &msg, sess, &action, &ec ) );
+		CHECK( 0, fd_msg_dispatch ( &msg, sess, &action, &ec, &em, &error ) );
 		CHECK( 1, cbcalled[0] );
 		CHECK( 1, cbcalled[1] );
 		CHECK( 0, cbcalled[2] );
@@ -201,7 +201,7 @@
 		
 		memset(cbcalled, 0, sizeof(cbcalled));
 		msg = new_msg( 2, cmd1, avp1, NULL, 0 );
-		CHECK( 0, fd_msg_dispatch ( &msg, sess, &action, &ec ) );
+		CHECK( 0, fd_msg_dispatch ( &msg, sess, &action, &ec, &em, &error ) );
 		CHECK( 1, cbcalled[0] );
 		CHECK( 0, cbcalled[1] );
 		CHECK( 1, cbcalled[2] );
@@ -235,7 +235,7 @@
 		/* Check the callbacks are called as appropriate */
 		memset(cbcalled, 0, sizeof(cbcalled));
 		msg = new_msg( 0, cmd1, avp1, NULL, 0 );
-		CHECK( 0, fd_msg_dispatch ( &msg, sess, &action, &ec ) );
+		CHECK( 0, fd_msg_dispatch ( &msg, sess, &action, &ec, &em, &error ) );
 		CHECK( 1, cbcalled[0] );
 		CHECK( 1, cbcalled[1] );
 		CHECK( 0, cbcalled[2] );
@@ -245,7 +245,7 @@
 		
 		memset(cbcalled, 0, sizeof(cbcalled));
 		msg = new_msg( 2, cmd1, avp1, NULL, 0 );
-		CHECK( 0, fd_msg_dispatch ( &msg, sess, &action, &ec ) );
+		CHECK( 0, fd_msg_dispatch ( &msg, sess, &action, &ec, &em, &error ) );
 		CHECK( 1, cbcalled[0] );
 		CHECK( 1, cbcalled[1] );
 		CHECK( 1, cbcalled[2] );
@@ -255,7 +255,7 @@
 		
 		memset(cbcalled, 0, sizeof(cbcalled));
 		msg = new_msg( 2, cmd2, avp1, NULL, 0 );
-		CHECK( 0, fd_msg_dispatch ( &msg, sess, &action, &ec ) );
+		CHECK( 0, fd_msg_dispatch ( &msg, sess, &action, &ec, &em, &error ) );
 		CHECK( 1, cbcalled[0] );
 		CHECK( 0, cbcalled[1] );
 		CHECK( 0, cbcalled[2] );
@@ -265,7 +265,7 @@
 		
 		memset(cbcalled, 0, sizeof(cbcalled));
 		msg = new_msg( 1, cmd2, NULL, avp2, 0 );
-		CHECK( 0, fd_msg_dispatch ( &msg, sess, &action, &ec ) );
+		CHECK( 0, fd_msg_dispatch ( &msg, sess, &action, &ec, &em, &error ) );
 		CHECK( 1, cbcalled[0] );
 		CHECK( 0, cbcalled[1] );
 		CHECK( 0, cbcalled[2] );
@@ -315,7 +315,7 @@
 		/* Check the callbacks are called as appropriate */
 		memset(cbcalled, 0, sizeof(cbcalled));
 		msg = new_msg( 0, cmd1, NULL, NULL, 0 );
-		CHECK( 0, fd_msg_dispatch ( &msg, sess, &action, &ec ) );
+		CHECK( 0, fd_msg_dispatch ( &msg, sess, &action, &ec, &em, &error ) );
 		CHECK( 1, cbcalled[0] );
 		CHECK( 0, cbcalled[1] );
 		CHECK( 0, cbcalled[2] );
@@ -327,7 +327,7 @@
 		
 		memset(cbcalled, 0, sizeof(cbcalled));
 		msg = new_msg( 0, cmd1, avp1, NULL, 0 );
-		CHECK( 0, fd_msg_dispatch ( &msg, sess, &action, &ec ) );
+		CHECK( 0, fd_msg_dispatch ( &msg, sess, &action, &ec, &em, &error ) );
 		CHECK( 1, cbcalled[0] );
 		CHECK( 1, cbcalled[1] );
 		CHECK( 1, cbcalled[2] );
@@ -339,7 +339,7 @@
 		
 		memset(cbcalled, 0, sizeof(cbcalled));
 		msg = new_msg( 1, cmd2, avp1, NULL, 0 );
-		CHECK( 0, fd_msg_dispatch ( &msg, sess, &action, &ec ) );
+		CHECK( 0, fd_msg_dispatch ( &msg, sess, &action, &ec, &em, &error ) );
 		CHECK( 1, cbcalled[0] );
 		CHECK( 1, cbcalled[1] );
 		CHECK( 0, cbcalled[2] );
@@ -351,7 +351,7 @@
 		
 		memset(cbcalled, 0, sizeof(cbcalled));
 		msg = new_msg( 1, cmd1, avp1, NULL, 0 );
-		CHECK( 0, fd_msg_dispatch ( &msg, sess, &action, &ec ) );
+		CHECK( 0, fd_msg_dispatch ( &msg, sess, &action, &ec, &em, &error ) );
 		CHECK( 1, cbcalled[0] );
 		CHECK( 1, cbcalled[1] );
 		CHECK( 1, cbcalled[2] );
@@ -363,7 +363,7 @@
 		
 		memset(cbcalled, 0, sizeof(cbcalled));
 		msg = new_msg( 1, cmd1, avp1, avp2, 0 );
-		CHECK( 0, fd_msg_dispatch ( &msg, sess, &action, &ec ) );
+		CHECK( 0, fd_msg_dispatch ( &msg, sess, &action, &ec, &em, &error ) );
 		CHECK( 1, cbcalled[0] );
 		CHECK( 1, cbcalled[1] );
 		CHECK( 1, cbcalled[2] );
@@ -376,7 +376,7 @@
 		
 		memset(cbcalled, 0, sizeof(cbcalled));
 		msg = new_msg( 1, cmd1, NULL, avp2, 1 );
-		CHECK( 0, fd_msg_dispatch ( &msg, sess, &action, &ec ) );
+		CHECK( 0, fd_msg_dispatch ( &msg, sess, &action, &ec, &em, &error ) );
 		CHECK( 1, cbcalled[0] );
 		CHECK( 0, cbcalled[1] );
 		CHECK( 0, cbcalled[2] );
@@ -389,7 +389,7 @@
 		
 		memset(cbcalled, 0, sizeof(cbcalled));
 		msg = new_msg( 1, cmd1, NULL, avp2, 2 );
-		CHECK( 0, fd_msg_dispatch ( &msg, sess, &action, &ec ) );
+		CHECK( 0, fd_msg_dispatch ( &msg, sess, &action, &ec, &em, &error ) );
 		CHECK( 1, cbcalled[0] );
 		CHECK( 0, cbcalled[1] );
 		CHECK( 0, cbcalled[2] );
@@ -436,7 +436,7 @@
 		/* Check the callbacks are called as appropriate */
 		memset(cbcalled, 0, sizeof(cbcalled));
 		msg = new_msg( 0, cmd1, avp1, NULL, 0 );
-		CHECK( 0, fd_msg_dispatch ( &msg, sess, &action, &ec ) );
+		CHECK( 0, fd_msg_dispatch ( &msg, sess, &action, &ec, &em, &error ) );
 		CHECK( 1, cbcalled[0] );
 		CHECK( 0, cbcalled[1] );
 		CHECK( 0, cbcalled[2] );
@@ -446,7 +446,7 @@
 		
 		memset(cbcalled, 0, sizeof(cbcalled));
 		msg = new_msg( 1, cmd2, avp1, avp2, 0 );
-		CHECK( 0, fd_msg_dispatch ( &msg, sess, &action, &ec ) );
+		CHECK( 0, fd_msg_dispatch ( &msg, sess, &action, &ec, &em, &error ) );
 		CHECK( 1, cbcalled[0] );
 		CHECK( 0, cbcalled[1] );
 		CHECK( 0, cbcalled[2] );
@@ -456,7 +456,7 @@
 		
 		memset(cbcalled, 0, sizeof(cbcalled));
 		msg = new_msg( 1, cmd2, avp1, avp2, 1 );
-		CHECK( 0, fd_msg_dispatch ( &msg, sess, &action, &ec ) );
+		CHECK( 0, fd_msg_dispatch ( &msg, sess, &action, &ec, &em, &error ) );
 		CHECK( 1, cbcalled[0] );
 		CHECK( 1, cbcalled[1] );
 		CHECK( 0, cbcalled[2] );
@@ -466,7 +466,7 @@
 		
 		memset(cbcalled, 0, sizeof(cbcalled));
 		msg = new_msg( 1, cmd2, avp1, avp2, 2 );
-		CHECK( 0, fd_msg_dispatch ( &msg, sess, &action, &ec ) );
+		CHECK( 0, fd_msg_dispatch ( &msg, sess, &action, &ec, &em, &error ) );
 		CHECK( 1, cbcalled[0] );
 		CHECK( 0, cbcalled[1] );
 		CHECK( 0, cbcalled[2] );
@@ -476,7 +476,7 @@
 		
 		memset(cbcalled, 0, sizeof(cbcalled));
 		msg = new_msg( 1, cmd1, avp1, avp2, 1 );
-		CHECK( 0, fd_msg_dispatch ( &msg, sess, &action, &ec ) );
+		CHECK( 0, fd_msg_dispatch ( &msg, sess, &action, &ec, &em, &error ) );
 		CHECK( 1, cbcalled[0] );
 		CHECK( 1, cbcalled[1] );
 		CHECK( 1, cbcalled[2] );
@@ -494,7 +494,7 @@
 			CHECK( 0, fd_msg_avp_setvalue ( avp, &value ) );
 			CHECK( 0, fd_msg_avp_add ( msg, MSG_BRW_LAST_CHILD, avp ) );
 		}
-		CHECK( 0, fd_msg_dispatch ( &msg, sess, &action, &ec ) );
+		CHECK( 0, fd_msg_dispatch ( &msg, sess, &action, &ec, &em, &error ) );
 		CHECK( 1, cbcalled[0] );
 		CHECK( 1, cbcalled[1] );
 		CHECK( 0, cbcalled[2] );
@@ -518,13 +518,15 @@
 		
 		memset(cbcalled, 0, sizeof(cbcalled));
 		msg = new_msg( 1, cmd1, avp1, avp2, 1 );
-		CHECK( 0, fd_msg_dispatch ( &msg, sess, &action, &ec ) );
+		CHECK( 0, fd_msg_dispatch ( &msg, sess, &action, &ec, &em, &error ) );
 		CHECK( 1, cbcalled[0] );
 		CHECK( 1, cbcalled[1] );
 		CHECK( 1, cbcalled[6] );
 		CHECK( 0, cbcalled[2] );
 		CHECK( 0, cbcalled[3] );
 		CHECK( 0, msg ? 1 : 0);
+		CHECK( 1, ec ? 1 : 0);
+		CHECK( 0, fd_msg_free( error ) );
 		
 		CHECK( 0, fd_disp_unregister( &hdl[0], NULL ) );
 		CHECK( 0, fd_disp_unregister( &hdl[1], NULL ) );
@@ -540,13 +542,14 @@
 		
 		memset(cbcalled, 0, sizeof(cbcalled));
 		msg = new_msg( 1, cmd1, avp1, avp2, 1 );
-		CHECK( 0, fd_msg_dispatch ( &msg, sess, &action, &ec ) );
+		CHECK( 0, fd_msg_dispatch ( &msg, sess, &action, &ec, &em, &error ) );
 		CHECK( 1, cbcalled[0] );
 		CHECK( 1, cbcalled[1] );
 		CHECK( 1, cbcalled[8] );
 		CHECK( 0, cbcalled[2] );
 		CHECK( 0, cbcalled[3] );
 		CHECK( NULL, msg );
+		CHECK( NULL, ec );
 		
 		CHECK( 0, fd_disp_unregister( &hdl[0], NULL ) );
 		CHECK( 0, fd_disp_unregister( &hdl[1], NULL ) );
@@ -562,7 +565,7 @@
 		
 		memset(cbcalled, 0, sizeof(cbcalled));
 		msg = new_msg( 1, cmd1, avp1, avp2, 1 );
-		CHECK( 0, fd_msg_dispatch ( &msg, sess, &action, &ec ) );
+		CHECK( 0, fd_msg_dispatch ( &msg, sess, &action, &ec, &em, &error ) );
 		CHECK( 1, cbcalled[0] );
 		CHECK( 1, cbcalled[1] );
 		CHECK( 1, cbcalled[9] );
@@ -593,7 +596,7 @@
 		
 		memset(cbcalled, 0, sizeof(cbcalled));
 		msg = new_msg( 2, cmd2, avp1, avp2, 2 );
-		CHECK( 0, fd_msg_dispatch ( &msg, sess, &action, &ec ) );
+		CHECK( 0, fd_msg_dispatch ( &msg, sess, &action, &ec, &em, &error ) );
 		CHECK( 1, cbcalled[0] );
 		CHECK( 1, cbcalled[1] );
 		CHECK( 1, cbcalled[2] );
@@ -605,7 +608,7 @@
 		CHECK( 0, fd_disp_register( cb_9, DISP_HOW_ANY, &when, NULL, &hdl[5] ) );
 		memset(cbcalled, 0, sizeof(cbcalled));
 		msg = new_msg( 2, cmd2, avp1, avp2, 2 );
-		CHECK( 0, fd_msg_dispatch ( &msg, sess, &action, &ec ) );
+		CHECK( 0, fd_msg_dispatch ( &msg, sess, &action, &ec, &em, &error ) );
 		CHECK( 1, cbcalled[0] );
 		CHECK( 0, cbcalled[1] );
 		CHECK( 0, cbcalled[2] );
@@ -618,7 +621,7 @@
 		CHECK( 0, fd_disp_register( cb_9, DISP_HOW_AVP_ENUMVAL, &when, NULL, &hdl[5] ) );
 		memset(cbcalled, 0, sizeof(cbcalled));
 		msg = new_msg( 2, cmd2, avp1, avp2, 2 );
-		CHECK( 0, fd_msg_dispatch ( &msg, sess, &action, &ec ) );
+		CHECK( 0, fd_msg_dispatch ( &msg, sess, &action, &ec, &em, &error ) );
 		CHECK( 1, cbcalled[0] );
 		CHECK( 1, cbcalled[1] );
 		CHECK( 1, cbcalled[2] );
@@ -631,7 +634,7 @@
 		CHECK( 0, fd_disp_register( cb_9, DISP_HOW_AVP, &when, NULL, &hdl[5] ) );
 		memset(cbcalled, 0, sizeof(cbcalled));
 		msg = new_msg( 2, cmd2, avp1, avp2, 2 );
-		CHECK( 0, fd_msg_dispatch ( &msg, sess, &action, &ec ) );
+		CHECK( 0, fd_msg_dispatch ( &msg, sess, &action, &ec, &em, &error ) );
 		CHECK( 1, cbcalled[0] );
 		CHECK( 1, cbcalled[1] );
 		CHECK( 1, cbcalled[2] );
@@ -644,7 +647,7 @@
 		CHECK( 0, fd_disp_register( cb_9, DISP_HOW_CC, &when, NULL, &hdl[5] ) );
 		memset(cbcalled, 0, sizeof(cbcalled));
 		msg = new_msg( 2, cmd2, avp1, avp2, 2 );
-		CHECK( 0, fd_msg_dispatch ( &msg, sess, &action, &ec ) );
+		CHECK( 0, fd_msg_dispatch ( &msg, sess, &action, &ec, &em, &error ) );
 		CHECK( 1, cbcalled[0] );
 		CHECK( 1, cbcalled[1] );
 		CHECK( 1, cbcalled[2] );
@@ -657,7 +660,7 @@
 		CHECK( 0, fd_disp_register( cb_9, DISP_HOW_APPID, &when, NULL, &hdl[5] ) );
 		memset(cbcalled, 0, sizeof(cbcalled));
 		msg = new_msg( 2, cmd2, avp1, avp2, 2 );
-		CHECK( 0, fd_msg_dispatch ( &msg, sess, &action, &ec ) );
+		CHECK( 0, fd_msg_dispatch ( &msg, sess, &action, &ec, &em, &error ) );
 		CHECK( 1, cbcalled[0] );
 		CHECK( 1, cbcalled[1] );
 		CHECK( 1, cbcalled[2] );
@@ -710,7 +713,7 @@
 		/* Check this handler is called for a message */
 		msg = new_msg( 0, cmd1, avp1, NULL, 0 );
 		memset(cbcalled, 0, sizeof(cbcalled));
-		CHECK( 0, fd_msg_dispatch ( &msg, sess, &action, &ec ) );
+		CHECK( 0, fd_msg_dispatch ( &msg, sess, &action, &ec, &em, &error ) );
 		CHECK( 1, cbcalled[0] );
 		CHECK( DISP_ACT_CONT, action );
 		
"Welcome to our mercurial repository"