Initial commit

Change-Id: I6a4444e3c193dae437cd7929f4c39aba7b749efa
diff --git a/libfdcore/CMakeLists.txt b/libfdcore/CMakeLists.txt
new file mode 100644
index 0000000..503437d
--- /dev/null
+++ b/libfdcore/CMakeLists.txt
@@ -0,0 +1,85 @@
+# The subproject name
+Project("freeDiameter core library" C)
+
+# Configuration for newer cmake
+cmake_policy(VERSION 2.6)
+if (POLICY CMP0022)
+	cmake_policy(SET CMP0022 OLD)
+endif (POLICY CMP0022)
+
+# Configuration parser
+BISON_FILE(fdd.y)
+FLEX_FILE(fdd.l)
+SET_SOURCE_FILES_PROPERTIES(lex.fdd.c fdd.tab.c PROPERTIES COMPILE_FLAGS "-I ${CMAKE_CURRENT_SOURCE_DIR}")
+
+# List of source files
+SET(FDCORE_SRC
+	fdcore-internal.h
+	apps.c
+	cnxctx.h
+	config.c
+	core.c
+	cnxctx.c
+	endpoints.c
+	events.c
+	extensions.c
+	fifo_stats.c
+	hooks.c
+	dict_base_proto.c
+	messages.c
+	queues.c
+	peers.c
+	p_ce.c
+	p_cnx.c
+	p_dw.c
+	p_dp.c
+	p_expiry.c
+	p_out.c
+	p_psm.c
+	p_sr.c
+	routing_dispatch.c
+	server.c
+	tcp.c
+	version.c
+	)
+
+IF(NOT DISABLE_SCTP)
+	SET(FDCORE_SRC ${FDCORE_SRC} sctp.c sctp3436.c)
+ENDIF(NOT DISABLE_SCTP)
+
+SET(FDCORE_GEN_SRC
+		lex.fdd.c
+		fdd.tab.c
+		fdd.tab.h
+	)
+	
+# Save the list of files for the tests 
+SET(FDCORE_SRC ${FDCORE_SRC} PARENT_SCOPE)
+SET(FDCORE_GEN_SRC ${FDCORE_GEN_SRC} PARENT_SCOPE)
+
+# Include path
+INCLUDE_DIRECTORIES(${LFDCORE_INCLUDES})
+INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR})
+
+# Build the executable
+ADD_LIBRARY(libfdcore SHARED ${FDCORE_SRC} ${FDCORE_GEN_SRC})
+ADD_DEPENDENCIES(libfdcore version_information)
+
+# Avoid the liblib name, and set the version
+SET_TARGET_PROPERTIES(libfdcore PROPERTIES 
+	OUTPUT_NAME "fdcore"
+	SOVERSION ${FD_PROJECT_VERSION_API}
+	VERSION ${FD_PROJECT_VERSION_MAJOR}.${FD_PROJECT_VERSION_MINOR}.${FD_PROJECT_VERSION_REV}
+	LINK_INTERFACE_LIBRARIES "${LFDCORE_LINK_INTERFACES}")
+
+# The library itself needs other libraries 
+LINK_DIRECTORIES(${CURRENT_BINARY_DIR}/../libfdproto)
+TARGET_LINK_LIBRARIES(libfdcore libfdproto ${LFDCORE_LIBS})
+
+
+####
+## INSTALL section ##
+
+INSTALL(TARGETS libfdcore
+	LIBRARY DESTINATION ${INSTALL_LIBRARY_SUFFIX}
+	COMPONENT freeDiameter-common)
diff --git a/libfdcore/apps.c b/libfdcore/apps.c
new file mode 100644
index 0000000..a0811d9
--- /dev/null
+++ b/libfdcore/apps.c
@@ -0,0 +1,152 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@freediameter.net>							 *
+*													 *
+* Copyright (c) 2011, WIDE Project and NICT								 *
+* All rights reserved.											 *
+* 													 *
+* Redistribution and use of this software in source and binary forms, with or without modification, are  *
+* permitted provided that the following conditions are met:						 *
+* 													 *
+* * Redistributions of source code must retain the above 						 *
+*   copyright notice, this list of conditions and the 							 *
+*   following disclaimer.										 *
+*    													 *
+* * Redistributions in binary form must reproduce the above 						 *
+*   copyright notice, this list of conditions and the 							 *
+*   following disclaimer in the documentation and/or other						 *
+*   materials provided with the distribution.								 *
+* 													 *
+* * Neither the name of the WIDE Project or NICT nor the 						 *
+*   names of its contributors may be used to endorse or 						 *
+*   promote products derived from this software without 						 *
+*   specific prior written permission of WIDE Project and 						 *
+*   NICT.												 *
+* 													 *
+* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED *
+* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
+* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR *
+* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 	 *
+* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 	 *
+* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR *
+* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF   *
+* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.								 *
+*********************************************************************************************************/
+
+#include "fdcore-internal.h"
+
+/* Merge information into a list of apps */
+int fd_app_merge(struct fd_list * list, application_id_t aid, vendor_id_t vid, int auth, int acct)
+{
+	struct fd_list * li;
+	int skip = 0;
+	
+	/* List is ordered by appid. Avoid duplicates */
+	for (li = list; li->next != list; li = li->next) {
+		struct fd_app * na = (struct fd_app *)(li->next);
+		if (na->appid < aid)
+			continue;
+		
+		if (na->appid > aid)
+			break;
+		
+		/* Otherwise, we merge with existing entry -- ignore vendor id in this case */
+		skip = 1;
+		
+		if (auth)
+			na->flags.auth = 1;
+		if (acct)
+			na->flags.acct = 1;
+		break;
+	}
+	
+	if (!skip) {			
+		struct fd_app  * new = NULL;
+
+		CHECK_MALLOC( new = malloc(sizeof(struct fd_app)) );
+		memset(new, 0, sizeof(struct fd_app));
+		fd_list_init(&new->chain, NULL);
+		new->flags.auth = (auth ? 1 : 0);
+		new->flags.acct = (acct ? 1 : 0);
+		new->vndid = vid;
+		new->appid = aid;
+		fd_list_insert_after(li, &new->chain);
+	}
+	
+	return 0;
+}
+
+/* Check if a given application id is in a list */
+int fd_app_check(struct fd_list * list, application_id_t aid, struct fd_app **detail)
+{
+	struct fd_list * li;
+	
+	TRACE_ENTRY("%p %d %p", list, aid, detail);
+	CHECK_PARAMS(list && detail);
+	
+	*detail = NULL;
+	
+	/* Search in the list */
+	for (li = list->next; li != list; li = li->next) {
+		struct fd_app * a = (struct fd_app *)li;
+		if (a->appid < aid)
+			continue;
+		
+		if (a->appid == aid)
+			*detail = a;
+		break;
+	}
+	
+	return 0;
+}
+
+/* Check if two lists have at least one common application */
+int fd_app_check_common(struct fd_list * list1, struct fd_list * list2, int * common_found)
+{
+	struct fd_list * li1, *li2;
+	
+	TRACE_ENTRY("%p %p %p", list1, list2, common_found);
+	CHECK_PARAMS( list1 && list2 && common_found );
+	
+	/* Both lists are ordered, so advance both pointers at the same time */
+	for (li1 = list1->next, li2 = list2->next;  (li1 != list1) && (li2 != list2); ) {
+		struct fd_app * a1 = (struct fd_app *)li1, *a2 = (struct fd_app *)li2;
+		if (a1->appid < a2->appid) {
+			li1 = li1->next;
+			continue;
+		}
+		if (a1->appid > a2->appid) {
+			li2 = li2->next;
+			continue;
+		}
+		/* They are equal, compare the applications */
+		if ((a1->flags.auth && a2->flags.auth) || (a1->flags.acct && a2->flags.acct)) {
+			/* found! */
+			*common_found = 1;
+			return 0;
+		}
+		
+		/* This application is not common, advance both lists */
+		li1 = li1->next;
+		li2 = li2->next;
+	}
+	
+	/* We did not find a common app */
+	*common_found = 0;
+	return 0;
+}
+
+/* Remove the apps from a list */
+int fd_app_empty(struct fd_list * list)
+{
+	TRACE_ENTRY("%p", list);
+	CHECK_PARAMS( list );
+	
+	while (!FD_IS_LIST_EMPTY(list)) {
+		struct fd_list * li = list->next;
+		fd_list_unlink(li);
+		free(li);
+	}
+	
+	return 0;
+}
diff --git a/libfdcore/cnxctx.c b/libfdcore/cnxctx.c
new file mode 100644
index 0000000..1fc336e
--- /dev/null
+++ b/libfdcore/cnxctx.c
@@ -0,0 +1,2045 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@freediameter.net>							 *
+*													 *
+* Copyright (c) 2015, 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 "fdcore-internal.h"
+#include "cnxctx.h"
+
+#include <net/if.h>
+#include <ifaddrs.h> /* for getifaddrs */
+#include <sys/uio.h> /* writev */
+
+/* The maximum size of Diameter message we accept to receive (<= 2^24) to avoid too big mallocs in case of trashed headers */
+#ifndef DIAMETER_MSG_SIZE_MAX
+#define DIAMETER_MSG_SIZE_MAX	65535	/* in bytes */
+#endif /* DIAMETER_MSG_SIZE_MAX */
+
+
+/* Connections contexts (cnxctx) in freeDiameter are wrappers around the sockets and TLS operations .
+ * They are used to hide the details of the processing to the higher layers of the daemon.
+ * They are always oriented on connections (TCP or SCTP), connectionless modes (UDP or SCTP) are not supported.
+ */
+
+/* Lifetime of a cnxctx object:
+ * 1) Creation
+ *    a) a server socket:
+ *       - create the object with fd_cnx_serv_tcp or fd_cnx_serv_sctp
+ *       - start listening incoming connections: fd_cnx_serv_listen
+ *       - accept new clients with fd_cnx_serv_accept.
+ *    b) a client socket:
+ *       - connect to a remote server with fd_cnx_cli_connect
+ *
+ * 2) Initialization
+ *    - if TLS is started first, call fd_cnx_handshake
+ *    - otherwise to receive clear messages, call fd_cnx_start_clear. fd_cnx_handshake can be called later.
+ *
+ * 3) Usage
+ *    - fd_cnx_receive, fd_cnx_send : exchange messages on this connection (send is synchronous, receive is not, but blocking).
+ *    - fd_cnx_recv_setaltfifo : when a message is received, the event is sent to an external fifo list. fd_cnx_receive does not work when the alt_fifo is set.
+ *    - fd_cnx_getid : retrieve a descriptive string for the connection (for debug)
+ *    - fd_cnx_getremoteid : identification of the remote peer (IP address or fqdn)
+ *    - fd_cnx_getcred : get the remote peer TLS credentials, after handshake
+ *
+ * 4) End
+ *    - fd_cnx_destroy
+ */
+
+/*******************************************/
+/*     Creation of a connection object     */
+/*******************************************/
+
+/* Initialize a context structure */
+static struct cnxctx * fd_cnx_init(int full)
+{
+	struct cnxctx * conn = NULL;
+
+	TRACE_ENTRY("%d", full);
+
+	CHECK_MALLOC_DO( conn = malloc(sizeof(struct cnxctx)), return NULL );
+	memset(conn, 0, sizeof(struct cnxctx));
+
+	if (full) {
+		CHECK_FCT_DO( fd_fifo_new ( &conn->cc_incoming, 5 ), return NULL );
+	}
+
+	return conn;
+}
+
+#define CC_ID_HDR "{----} "
+
+/* Create and bind a server socket to the given endpoint and port */
+struct cnxctx * fd_cnx_serv_tcp(uint16_t port, int family, struct fd_endpoint * ep)
+{
+	struct cnxctx * cnx = NULL;
+	sSS dummy;
+	sSA * sa = (sSA *) &dummy;
+
+	TRACE_ENTRY("%hu %d %p", port, family, ep);
+
+	CHECK_PARAMS_DO( port, return NULL );
+	CHECK_PARAMS_DO( ep || family, return NULL );
+	CHECK_PARAMS_DO( (! family) || (family == AF_INET) || (family == AF_INET6), return NULL );
+	CHECK_PARAMS_DO( (! ep) || (ep->ss.ss_family == AF_INET) || (ep->ss.ss_family == AF_INET6), return NULL );
+	CHECK_PARAMS_DO( (! ep) || (!family) || (ep->ss.ss_family == family), return NULL );
+
+	/* The connection object */
+	CHECK_MALLOC_DO( cnx = fd_cnx_init(0), return NULL );
+
+	/* Prepare the socket address information */
+	if (ep) {
+		memcpy(sa, &ep->ss, sizeof(sSS));
+	} else {
+		memset(&dummy, 0, sizeof(dummy));
+		sa->sa_family = family;
+	}
+	if (sa->sa_family == AF_INET) {
+		((sSA4 *)sa)->sin_port = htons(port);
+		cnx->cc_family = AF_INET;
+	} else {
+		((sSA6 *)sa)->sin6_port = htons(port);
+		cnx->cc_family = AF_INET6;
+	}
+
+	/* Create the socket */
+	CHECK_FCT_DO( fd_tcp_create_bind_server( &cnx->cc_socket, sa, sSAlen(sa) ), goto error );
+
+	/* Generate the name for the connection object */
+	{
+		char addrbuf[INET6_ADDRSTRLEN];
+		int  rc;
+		rc = getnameinfo(sa, sSAlen(sa), addrbuf, sizeof(addrbuf), NULL, 0, NI_NUMERICHOST);
+		if (rc)
+			snprintf(addrbuf, sizeof(addrbuf), "[err:%s]", gai_strerror(rc));
+		snprintf(cnx->cc_id, sizeof(cnx->cc_id), CC_ID_HDR "TCP srv [%s]:%hu (%d)", addrbuf, port, cnx->cc_socket);
+	}
+
+	cnx->cc_proto = IPPROTO_TCP;
+
+	return cnx;
+
+error:
+	fd_cnx_destroy(cnx);
+	return NULL;
+}
+
+/* Same function for SCTP, with a list of local endpoints to bind to */
+struct cnxctx * fd_cnx_serv_sctp(uint16_t port, struct fd_list * ep_list)
+{
+#ifdef DISABLE_SCTP
+	TRACE_DEBUG(INFO, "This function should never been called when SCTP is disabled...");
+	ASSERT(0);
+	CHECK_FCT_DO( ENOTSUP, );
+	return NULL;
+#else /* DISABLE_SCTP */
+	struct cnxctx * cnx = NULL;
+
+	TRACE_ENTRY("%hu %p", port, ep_list);
+
+	CHECK_PARAMS_DO( port, return NULL );
+
+	/* The connection object */
+	CHECK_MALLOC_DO( cnx = fd_cnx_init(0), return NULL );
+
+	if (fd_g_config->cnf_flags.no_ip6) {
+		cnx->cc_family = AF_INET;
+	} else {
+		cnx->cc_family = AF_INET6; /* can create socket for both IP and IPv6 */
+	}
+	
+	/* Create the socket */
+	CHECK_FCT_DO( fd_sctp_create_bind_server( &cnx->cc_socket, cnx->cc_family, ep_list, port ), goto error );
+
+	/* Generate the name for the connection object */
+	snprintf(cnx->cc_id, sizeof(cnx->cc_id), CC_ID_HDR "SCTP srv :%hu (%d)", port, cnx->cc_socket);
+
+	cnx->cc_proto = IPPROTO_SCTP;
+
+	return cnx;
+
+error:
+	fd_cnx_destroy(cnx);
+	return NULL;
+#endif /* DISABLE_SCTP */
+}
+
+/* Allow clients to connect on the server socket */
+int fd_cnx_serv_listen(struct cnxctx * conn)
+{
+	CHECK_PARAMS( conn );
+
+	switch (conn->cc_proto) {
+		case IPPROTO_TCP:
+			CHECK_FCT(fd_tcp_listen(conn->cc_socket));
+			break;
+
+#ifndef DISABLE_SCTP
+		case IPPROTO_SCTP:
+			CHECK_FCT(fd_sctp_listen(conn->cc_socket));
+			break;
+#endif /* DISABLE_SCTP */
+
+		default:
+			CHECK_PARAMS(0);
+	}
+
+	return 0;
+}
+
+/* Accept a client (blocking until a new client connects) -- cancelable */
+struct cnxctx * fd_cnx_serv_accept(struct cnxctx * serv)
+{
+	struct cnxctx * cli = NULL;
+	sSS ss;
+	socklen_t ss_len = sizeof(ss);
+	int cli_sock = 0;
+
+	TRACE_ENTRY("%p", serv);
+	CHECK_PARAMS_DO(serv, return NULL);
+	
+	/* Accept the new connection -- this is blocking until new client enters or until cancellation */
+	CHECK_SYS_DO( cli_sock = accept(serv->cc_socket, (sSA *)&ss, &ss_len), return NULL );
+	
+	CHECK_MALLOC_DO( cli = fd_cnx_init(1), { shutdown(cli_sock, SHUT_RDWR); close(cli_sock); return NULL; } );
+	cli->cc_socket = cli_sock;
+	cli->cc_family = serv->cc_family;
+	cli->cc_proto = serv->cc_proto;
+	
+	/* Set the timeout */
+	fd_cnx_s_setto(cli->cc_socket);
+	
+	/* Generate the name for the connection object */
+	{
+		char addrbuf[INET6_ADDRSTRLEN];
+		char portbuf[10];
+		int  rc;
+		
+		rc = getnameinfo((sSA *)&ss, ss_len, addrbuf, sizeof(addrbuf), portbuf, sizeof(portbuf), NI_NUMERICHOST | NI_NUMERICSERV);
+		if (rc) {
+			snprintf(addrbuf, sizeof(addrbuf), "[err:%s]", gai_strerror(rc));
+			portbuf[0] = '\0';
+		}
+		
+		/* Numeric values for debug... */
+		snprintf(cli->cc_id, sizeof(cli->cc_id), CC_ID_HDR "%s from [%s]:%s (%d<-%d)", 
+				IPPROTO_NAME(cli->cc_proto), addrbuf, portbuf, serv->cc_socket, cli->cc_socket);
+		
+		
+		/* ...Name for log messages */
+		rc = getnameinfo((sSA *)&ss, ss_len, cli->cc_remid, sizeof(cli->cc_remid), NULL, 0, 0);
+		if (rc)
+			snprintf(cli->cc_remid, sizeof(cli->cc_remid), "[err:%s]", gai_strerror(rc));
+	}
+	
+	LOG_D("Incoming connection: '%s' <- '%s'   {%s}", fd_cnx_getid(serv), cli->cc_remid, cli->cc_id);
+
+#ifndef DISABLE_SCTP
+	/* SCTP-specific handlings */
+	if (cli->cc_proto == IPPROTO_SCTP) {
+		/* Retrieve the number of streams */
+		CHECK_FCT_DO( fd_sctp_get_str_info( cli->cc_socket, &cli->cc_sctp_para.str_in, &cli->cc_sctp_para.str_out, NULL ), {fd_cnx_destroy(cli); return NULL;} );
+		if (cli->cc_sctp_para.str_out < cli->cc_sctp_para.str_in)
+			cli->cc_sctp_para.pairs = cli->cc_sctp_para.str_out;
+		else
+			cli->cc_sctp_para.pairs = cli->cc_sctp_para.str_in;
+		
+		LOG_A( "%s : client '%s' (SCTP:%d, %d/%d streams)", fd_cnx_getid(serv), fd_cnx_getid(cli), cli->cc_socket, cli->cc_sctp_para.str_in, cli->cc_sctp_para.str_out);
+	}
+#endif /* DISABLE_SCTP */
+
+	return cli;
+}
+
+/* Client side: connect to a remote server -- cancelable */
+struct cnxctx * fd_cnx_cli_connect_tcp(sSA * sa /* contains the port already */, socklen_t addrlen)
+{
+	int sock = 0;
+	struct cnxctx * cnx = NULL;
+	char sa_buf[sSA_DUMP_STRLEN];
+	
+	TRACE_ENTRY("%p %d", sa, addrlen);
+	CHECK_PARAMS_DO( sa && addrlen, return NULL );
+	
+	fd_sa_sdump_numeric(sa_buf, sa);
+	
+	LOG_D("Connecting to TCP %s...", sa_buf);
+	
+	/* Create the socket and connect, which can take some time and/or fail */
+	{
+		int ret = fd_tcp_client( &sock, sa, addrlen );
+		if (ret != 0) {
+			LOG_D("TCP connection to %s failed: %s", sa_buf, strerror(ret));
+			return NULL;
+		}
+	}
+	
+	/* Once the socket is created successfuly, prepare the remaining of the cnx */
+	CHECK_MALLOC_DO( cnx = fd_cnx_init(1), { shutdown(sock, SHUT_RDWR); close(sock); return NULL; } );
+	
+	cnx->cc_socket = sock;
+	cnx->cc_family = sa->sa_family;
+	cnx->cc_proto  = IPPROTO_TCP;
+	
+	/* Set the timeout */
+	fd_cnx_s_setto(cnx->cc_socket);
+	
+	/* Generate the names for the object */
+	{
+		int  rc;
+		
+		snprintf(cnx->cc_id, sizeof(cnx->cc_id), CC_ID_HDR "TCP,#%d->%s", cnx->cc_socket, sa_buf);
+		
+		/* ...Name for log messages */
+		rc = getnameinfo(sa, addrlen, cnx->cc_remid, sizeof(cnx->cc_remid), NULL, 0, 0);
+		if (rc)
+			snprintf(cnx->cc_remid, sizeof(cnx->cc_remid), "[err:%s]", gai_strerror(rc));
+	}
+	
+	LOG_A("TCP connection to %s succeed (socket:%d).", sa_buf, sock);
+	
+	return cnx;
+}
+
+/* Same for SCTP, accepts a list of remote addresses to connect to (see sctp_connectx for how they are used) */
+struct cnxctx * fd_cnx_cli_connect_sctp(int no_ip6, uint16_t port, struct fd_list * list)
+{
+#ifdef DISABLE_SCTP
+	TRACE_DEBUG(INFO, "This function should never be called when SCTP is disabled...");
+	ASSERT(0);
+	CHECK_FCT_DO( ENOTSUP, );
+	return NULL;
+#else /* DISABLE_SCTP */
+	int sock = 0;
+	struct cnxctx * cnx = NULL;
+	char sa_buf[sSA_DUMP_STRLEN];
+	sSS primary;
+	
+	TRACE_ENTRY("%p", list);
+	CHECK_PARAMS_DO( list && !FD_IS_LIST_EMPTY(list), return NULL );
+	
+	fd_sa_sdump_numeric(sa_buf, &((struct fd_endpoint *)(list->next))->sa);
+	
+	LOG_D("Connecting to SCTP %s:%hu...", sa_buf, port);
+	
+	{
+		int ret = fd_sctp_client( &sock, no_ip6, port, list );
+		if (ret != 0) {
+			LOG_D("SCTP connection to [%s,...] failed: %s", sa_buf, strerror(ret));
+			return NULL;
+		}
+	}
+	
+	/* Once the socket is created successfuly, prepare the remaining of the cnx */
+	CHECK_MALLOC_DO( cnx = fd_cnx_init(1), { shutdown(sock, SHUT_RDWR); close(sock); return NULL; } );
+	
+	cnx->cc_socket = sock;
+	cnx->cc_family = no_ip6 ? AF_INET : AF_INET6;
+	cnx->cc_proto  = IPPROTO_SCTP;
+	
+	/* Set the timeout */
+	fd_cnx_s_setto(cnx->cc_socket);
+	
+	/* Retrieve the number of streams and primary address */
+	CHECK_FCT_DO( fd_sctp_get_str_info( sock, &cnx->cc_sctp_para.str_in, &cnx->cc_sctp_para.str_out, &primary ), goto error );
+	if (cnx->cc_sctp_para.str_out < cnx->cc_sctp_para.str_in)
+		cnx->cc_sctp_para.pairs = cnx->cc_sctp_para.str_out;
+	else
+		cnx->cc_sctp_para.pairs = cnx->cc_sctp_para.str_in;
+	
+	fd_sa_sdump_numeric(sa_buf, (sSA *)&primary);
+	
+	/* Generate the names for the object */
+	{
+		int  rc;
+		
+		snprintf(cnx->cc_id, sizeof(cnx->cc_id), CC_ID_HDR "SCTP,#%d->%s", cnx->cc_socket, sa_buf);
+		
+		/* ...Name for log messages */
+		rc = getnameinfo((sSA *)&primary, sSAlen(&primary), cnx->cc_remid, sizeof(cnx->cc_remid), NULL, 0, 0);
+		if (rc)
+			snprintf(cnx->cc_remid, sizeof(cnx->cc_remid), "[err:%s]", gai_strerror(rc));
+	}
+	
+	LOG_A("SCTP connection to %s succeed (socket:%d, %d/%d streams).", sa_buf, sock, cnx->cc_sctp_para.str_in, cnx->cc_sctp_para.str_out);
+	
+	return cnx;
+
+error:
+	fd_cnx_destroy(cnx);
+	return NULL;
+#endif /* DISABLE_SCTP */
+}
+
+/* Return a string describing the connection, for debug */
+char * fd_cnx_getid(struct cnxctx * conn)
+{
+	CHECK_PARAMS_DO( conn, return "" );
+	return conn->cc_id;
+}
+
+/* Return the protocol of a connection */
+int fd_cnx_getproto(struct cnxctx * conn)
+{
+	CHECK_PARAMS_DO( conn, return 0 );
+	return conn->cc_proto;
+}
+
+/* Set the hostname to check during handshake */
+void fd_cnx_sethostname(struct cnxctx * conn, DiamId_t hn)
+{
+	CHECK_PARAMS_DO( conn, return );
+	conn->cc_tls_para.cn = hn;
+}
+
+/* We share a lock with many threads but we hold it only very short time so it is OK */
+static pthread_mutex_t state_lock = PTHREAD_MUTEX_INITIALIZER;
+uint32_t fd_cnx_getstate(struct cnxctx * conn)
+{
+	uint32_t st;
+	CHECK_POSIX_DO( pthread_mutex_lock(&state_lock), { ASSERT(0); } );
+	st = conn->cc_state;
+	CHECK_POSIX_DO( pthread_mutex_unlock(&state_lock), { ASSERT(0); } );
+	return st;
+}
+int  fd_cnx_teststate(struct cnxctx * conn, uint32_t flag)
+{
+	uint32_t st;
+	CHECK_POSIX_DO( pthread_mutex_lock(&state_lock), { ASSERT(0); } );
+	st = conn->cc_state;
+	CHECK_POSIX_DO( pthread_mutex_unlock(&state_lock), { ASSERT(0); } );
+	return st & flag;
+}
+void fd_cnx_update_id(struct cnxctx * conn) {
+	if (conn->cc_state & CC_STATUS_CLOSING)
+		conn->cc_id[1] = 'C';
+	else
+		conn->cc_id[1] = '-';
+	
+	if (conn->cc_state & CC_STATUS_ERROR)
+		conn->cc_id[2] = 'E';
+	else
+		conn->cc_id[2] = '-';
+	
+	if (conn->cc_state & CC_STATUS_SIGNALED)
+		conn->cc_id[3] = 'S';
+	else
+		conn->cc_id[3] = '-';
+	
+	if (conn->cc_state & CC_STATUS_TLS)
+		conn->cc_id[4] = 'T';
+	else
+		conn->cc_id[4] = '-';
+}
+void fd_cnx_addstate(struct cnxctx * conn, uint32_t orstate)
+{
+	CHECK_POSIX_DO( pthread_mutex_lock(&state_lock), { ASSERT(0); } );
+	conn->cc_state |= orstate;
+	fd_cnx_update_id(conn);
+	CHECK_POSIX_DO( pthread_mutex_unlock(&state_lock), { ASSERT(0); } );
+}
+void fd_cnx_setstate(struct cnxctx * conn, uint32_t abstate)
+{
+	CHECK_POSIX_DO( pthread_mutex_lock(&state_lock), { ASSERT(0); } );
+	conn->cc_state = abstate;
+	fd_cnx_update_id(conn);
+	CHECK_POSIX_DO( pthread_mutex_unlock(&state_lock), { ASSERT(0); } );
+}
+
+
+/* Return the TLS state of a connection */
+int fd_cnx_getTLS(struct cnxctx * conn)
+{
+	CHECK_PARAMS_DO( conn, return 0 );
+	return fd_cnx_teststate(conn, CC_STATUS_TLS);
+}
+
+/* Mark the connection to tell if OOO delivery is permitted (only for SCTP) */
+int fd_cnx_unordered_delivery(struct cnxctx * conn, int is_allowed)
+{
+	CHECK_PARAMS( conn );
+	conn->cc_sctp_para.unordered = is_allowed;
+	return 0;
+}
+
+/* Return true if the connection supports unordered delivery of messages */
+int fd_cnx_is_unordered_delivery_supported(struct cnxctx * conn)
+{
+	CHECK_PARAMS_DO( conn, return 0 );
+	#ifndef DISABLE_SCTP
+	if (conn->cc_proto == IPPROTO_SCTP)
+		return (conn->cc_sctp_para.str_out > 1);
+	#endif /* DISABLE_SCTP */
+	return 0;
+}
+
+
+/* Get the list of endpoints (IP addresses) of the local and remote peers on this connection */
+int fd_cnx_getremoteeps(struct cnxctx * conn, struct fd_list * eps)
+{
+	TRACE_ENTRY("%p %p", conn, eps);
+	CHECK_PARAMS(conn && eps);
+	
+	/* Check we have a full connection object, not a listening socket (with no remote) */
+	CHECK_PARAMS( conn->cc_incoming );
+
+	/* Retrieve the peer endpoint(s) of the connection */
+	switch (conn->cc_proto) {
+		case IPPROTO_TCP: {
+			sSS ss;
+			socklen_t sl;
+			CHECK_FCT(fd_tcp_get_remote_ep(conn->cc_socket, &ss, &sl));
+			CHECK_FCT(fd_ep_add_merge( eps, (sSA *)&ss, sl, EP_FL_LL | EP_FL_PRIMARY ));
+		}
+		break;
+
+		#ifndef DISABLE_SCTP
+		case IPPROTO_SCTP: {
+			CHECK_FCT(fd_sctp_get_remote_ep(conn->cc_socket, eps));
+		}
+		break;
+		#endif /* DISABLE_SCTP */
+
+		default:
+			CHECK_PARAMS(0);
+	}
+
+	return 0;
+}
+
+/* Get a string describing the remote peer address (ip address or fqdn) */
+char * fd_cnx_getremoteid(struct cnxctx * conn)
+{
+	CHECK_PARAMS_DO( conn, return "" );
+	return conn->cc_remid;
+}
+
+static int fd_cnx_may_dtls(struct cnxctx * conn);
+
+/* Get a short string representing the connection */
+int fd_cnx_proto_info(struct cnxctx * conn, char * buf, size_t len) 
+{
+	CHECK_PARAMS( conn );
+	
+	if (fd_cnx_teststate(conn, CC_STATUS_TLS)) {
+		snprintf(buf, len, "%s,%s,soc#%d", IPPROTO_NAME(conn->cc_proto), fd_cnx_may_dtls(conn) ? "DTLS" : "TLS", conn->cc_socket);
+	} else {
+		snprintf(buf, len, "%s,soc#%d", IPPROTO_NAME(conn->cc_proto), conn->cc_socket);
+	}
+	
+	return 0;
+}
+
+/* Retrieve a list of all IP addresses of the local system from the kernel, using getifaddrs */
+int fd_cnx_get_local_eps(struct fd_list * list)
+{
+	struct ifaddrs *iflist, *cur;
+	
+	CHECK_SYS(getifaddrs(&iflist));
+	
+	for (cur = iflist; cur != NULL; cur = cur->ifa_next) {
+		if (cur->ifa_flags & IFF_LOOPBACK)
+			continue;
+		
+		if (cur->ifa_addr == NULL) /* may happen with ppp interfaces */
+			continue;
+		
+		if (fd_g_config->cnf_flags.no_ip4 && (cur->ifa_addr->sa_family == AF_INET))
+			continue;
+		
+		if (fd_g_config->cnf_flags.no_ip6 && (cur->ifa_addr->sa_family == AF_INET6))
+			continue;
+		
+		CHECK_FCT(fd_ep_add_merge( list, cur->ifa_addr, sSAlen(cur->ifa_addr), EP_FL_LL ));
+	}
+	
+	freeifaddrs(iflist);
+	
+	return 0;
+}
+
+
+/**************************************/
+/*     Use of a connection object     */
+/**************************************/
+
+/* An error occurred on the socket */
+void fd_cnx_markerror(struct cnxctx * conn)
+{
+	TRACE_ENTRY("%p", conn);
+	CHECK_PARAMS_DO( conn, goto fatal );
+	
+	TRACE_DEBUG(FULL, "Error flag set for socket %d (%s, %s)", conn->cc_socket, conn->cc_id, conn->cc_remid);
+
+	/* Mark the error */
+	fd_cnx_addstate(conn, CC_STATUS_ERROR);
+	
+	/* Report the error if not reported yet, and not closing */
+	if (!fd_cnx_teststate(conn, CC_STATUS_CLOSING | CC_STATUS_SIGNALED ))  {
+		TRACE_DEBUG(FULL, "Sending FDEVP_CNX_ERROR event");
+		CHECK_FCT_DO( fd_event_send( fd_cnx_target_queue(conn), FDEVP_CNX_ERROR, 0, NULL), goto fatal);
+		fd_cnx_addstate(conn, CC_STATUS_SIGNALED);
+	}
+	
+	return;
+fatal:
+	/* An unrecoverable error occurred, stop the daemon */
+	ASSERT(0);
+	CHECK_FCT_DO(fd_core_shutdown(), );	
+}
+
+/* Set the timeout option on the socket */
+void fd_cnx_s_setto(int sock) 
+{
+	struct timeval tv;
+	
+	/* Set a timeout on the socket so that in any case we are not stuck waiting for something */
+	memset(&tv, 0, sizeof(tv));
+	tv.tv_usec = 100000L;	/* 100ms, to react quickly to head-of-the-line blocking. */
+	CHECK_SYS_DO( setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)),  );
+	CHECK_SYS_DO( setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)),  );
+}
+
+
+#ifdef GNUTLS_VERSION_300
+/* The pull_timeout function for gnutls */
+static int fd_cnx_s_select (struct cnxctx * conn, unsigned int ms)
+{
+	fd_set rfds;
+	struct timeval tv;
+	
+	FD_ZERO (&rfds);
+	FD_SET (conn->cc_socket, &rfds);
+	
+	tv.tv_sec = ms / 1000;
+	tv.tv_usec = (ms * 1000) % 1000000;
+	
+	return select (conn->cc_socket + 1, &rfds, NULL, NULL, &tv);
+}		
+#endif /* GNUTLS_VERSION_300 */
+
+/* A recv-like function, taking a cnxctx object instead of socket as entry. We use it to quickly react to timeouts without traversing GNUTLS wrapper each time */
+ssize_t fd_cnx_s_recv(struct cnxctx * conn, void *buffer, size_t length)
+{
+	ssize_t ret = 0;
+	int timedout = 0;
+again:
+	ret = recv(conn->cc_socket, buffer, length, 0);
+	/* Handle special case of timeout / interrupts */
+	if ((ret < 0) && ((errno == EAGAIN) || (errno == EINTR))) {
+		pthread_testcancel();
+		if (! fd_cnx_teststate(conn, CC_STATUS_CLOSING ))
+			goto again; /* don't care, just ignore */
+		if (!timedout) {
+			timedout ++; /* allow for one timeout while closing */
+			goto again;
+		}
+	}
+	
+	/* Mark the error */
+	if (ret <= 0) {
+		CHECK_SYS_DO(ret, /* continue, this is only used to log the error here */);
+		fd_cnx_markerror(conn);
+	}
+	
+	return ret;
+}
+
+/* Send */
+static ssize_t fd_cnx_s_sendv(struct cnxctx * conn, const struct iovec * iov, int iovcnt)
+{
+	ssize_t ret = 0;
+	struct timespec ts, now;
+	CHECK_SYS_DO(  clock_gettime(CLOCK_REALTIME, &ts), return -1 );
+again:
+	ret = writev(conn->cc_socket, iov, iovcnt);
+	/* Handle special case of timeout */
+	if ((ret < 0) && ((errno == EAGAIN) || (errno == EINTR))) {
+		ret = -errno;
+		pthread_testcancel();
+		
+		/* Check how much time we were blocked for this sending. */
+		CHECK_SYS_DO(  clock_gettime(CLOCK_REALTIME, &now), return -1 );
+		if ( ((now.tv_sec - ts.tv_sec) * 1000 + ((now.tv_nsec - ts.tv_nsec) / 1000000L)) > MAX_HOTL_BLOCKING_TIME) {
+			LOG_D("Unable to send any data for %dms, closing the connection", MAX_HOTL_BLOCKING_TIME);
+		} else if (! fd_cnx_teststate(conn, CC_STATUS_CLOSING )) {
+			goto again; /* don't care, just ignore */
+		}
+		
+		/* propagate the error */
+		errno = -ret;
+		ret = -1;
+		CHECK_SYS_DO(ret, /* continue */);
+	}
+	
+	/* Mark the error */
+	if (ret <= 0)
+		fd_cnx_markerror(conn);
+	
+	return ret;
+}
+
+/* Send, for older GNUTLS */
+#ifndef GNUTLS_VERSION_212
+static ssize_t fd_cnx_s_send(struct cnxctx * conn, const void *buffer, size_t length)
+{
+	struct iovec iov;
+	iov.iov_base = (void *)buffer;
+	iov.iov_len  = length;
+	return fd_cnx_s_sendv(conn, &iov, 1);
+}
+#endif /* GNUTLS_VERSION_212 */
+
+#define ALIGNOF(t) ((char *)(&((struct { char c; t _h; } *)0)->_h) - (char *)0)  /* Could use __alignof__(t) on some systems but this is more portable probably */
+#define PMDL_PADDED(len) ( ((len) + ALIGNOF(struct fd_msg_pmdl) - 1) & ~(ALIGNOF(struct fd_msg_pmdl) - 1) )
+
+size_t fd_msg_pmdl_sizewithoverhead(size_t datalen)
+{
+	return PMDL_PADDED(datalen) + sizeof(struct fd_msg_pmdl);
+}
+
+struct fd_msg_pmdl * fd_msg_pmdl_get_inbuf(uint8_t * buf, size_t datalen)
+{
+	return (struct fd_msg_pmdl *)(buf + PMDL_PADDED(datalen));
+} 
+
+static int fd_cnx_init_msg_buffer(uint8_t * buffer, size_t expected_len, struct fd_msg_pmdl ** pmdl)
+{
+	*pmdl = fd_msg_pmdl_get_inbuf(buffer, expected_len);
+	fd_list_init(&(*pmdl)->sentinel, NULL);
+	CHECK_POSIX(pthread_mutex_init(&(*pmdl)->lock, NULL) );
+	return 0;
+}
+
+static uint8_t * fd_cnx_alloc_msg_buffer(size_t expected_len, struct fd_msg_pmdl ** pmdl)
+{
+	uint8_t * ret = NULL;
+	
+	CHECK_MALLOC_DO(  ret = malloc( fd_msg_pmdl_sizewithoverhead(expected_len) ), return NULL );
+	CHECK_FCT_DO( fd_cnx_init_msg_buffer(ret, expected_len, pmdl), {free(ret); return NULL;} );
+	return ret;
+}
+
+#ifndef DISABLE_SCTP /* WE use this function only in SCTP code */
+static uint8_t * fd_cnx_realloc_msg_buffer(uint8_t * buffer, size_t expected_len, struct fd_msg_pmdl ** pmdl)
+{
+	uint8_t * ret = NULL;
+	
+	CHECK_MALLOC_DO(  ret = realloc( buffer, fd_msg_pmdl_sizewithoverhead(expected_len) ), return NULL );
+	CHECK_FCT_DO( fd_cnx_init_msg_buffer(ret, expected_len, pmdl), {free(ret); return NULL;} );
+	return ret;
+}
+#endif /* DISABLE_SCTP */
+
+static void free_rcvdata(void * arg) 
+{
+	struct fd_cnx_rcvdata * data = arg;
+	struct fd_msg_pmdl * pmdl = fd_msg_pmdl_get_inbuf(data->buffer, data->length);
+	(void) pthread_mutex_destroy(&pmdl->lock);
+	free(data->buffer);
+}
+
+/* Receiver thread (TCP & noTLS) : incoming message is directly saved into the target queue */
+static void * rcvthr_notls_tcp(void * arg)
+{
+	struct cnxctx * conn = arg;
+	
+	TRACE_ENTRY("%p", arg);
+	CHECK_PARAMS_DO(conn && (conn->cc_socket > 0), goto out);
+	
+	/* Set the thread name */
+	{
+		char buf[48];
+		snprintf(buf, sizeof(buf), "Receiver (%d) TCP/noTLS)", conn->cc_socket);
+		fd_log_threadname ( buf );
+	}
+	
+	ASSERT( conn->cc_proto == IPPROTO_TCP );
+	ASSERT( ! fd_cnx_teststate(conn, CC_STATUS_TLS ) );
+	ASSERT( fd_cnx_target_queue(conn) );
+	
+	/* Receive from a TCP connection: we have to rebuild the message boundaries */
+	do {
+		uint8_t header[4];
+		struct fd_cnx_rcvdata rcv_data;
+		struct fd_msg_pmdl *pmdl=NULL;
+		ssize_t ret = 0;
+		size_t	received = 0;
+
+		do {
+			ret = fd_cnx_s_recv(conn, &header[received], sizeof(header) - received);
+			if (ret <= 0) {
+				goto out; /* Stop the thread, the event was already sent */
+			}
+
+			received += ret;
+			
+			if (header[0] != DIAMETER_VERSION)
+				break; /* No need to wait for 4 bytes in this case */
+		} while (received < sizeof(header));
+
+		rcv_data.length = ((size_t)header[1] << 16) + ((size_t)header[2] << 8) + (size_t)header[3];
+
+		/* Check the received word is a valid begining of a Diameter message */
+		if ((header[0] != DIAMETER_VERSION)	/* defined in <libfdproto.h> */
+		   || (rcv_data.length > DIAMETER_MSG_SIZE_MAX)) { /* to avoid too big mallocs */
+			/* The message is suspect */
+			LOG_E( "Received suspect header [ver: %d, size: %zd] from '%s', assuming disconnection", (int)header[0], rcv_data.length, conn->cc_remid);
+			fd_cnx_markerror(conn);
+			goto out; /* Stop the thread, the recipient of the event will cleanup */
+		}
+
+		/* Ok, now we can really receive the data */
+		CHECK_MALLOC_DO(  rcv_data.buffer = fd_cnx_alloc_msg_buffer( rcv_data.length, &pmdl ), goto fatal );
+		memcpy(rcv_data.buffer, header, sizeof(header));
+
+		while (received < rcv_data.length) {
+			pthread_cleanup_push(free_rcvdata, &rcv_data); /* In case we are canceled, clean the partialy built buffer */
+			ret = fd_cnx_s_recv(conn, rcv_data.buffer + received, rcv_data.length - received);
+			pthread_cleanup_pop(0);
+
+			if (ret <= 0) {
+				free_rcvdata(&rcv_data);
+				goto out;
+			}
+			received += ret;
+		}
+		
+		fd_hook_call(HOOK_DATA_RECEIVED, NULL, NULL, &rcv_data, pmdl);
+		
+		/* We have received a complete message, pass it to the daemon */
+		CHECK_FCT_DO( fd_event_send( fd_cnx_target_queue(conn), FDEVP_CNX_MSG_RECV, rcv_data.length, rcv_data.buffer), 
+			{ 
+				free_rcvdata(&rcv_data);
+				goto fatal; 
+			} );
+		
+	} while (conn->cc_loop);
+	
+out:
+	TRACE_DEBUG(FULL, "Thread terminated");	
+	return NULL;
+	
+fatal:
+	/* An unrecoverable error occurred, stop the daemon */
+	CHECK_FCT_DO(fd_core_shutdown(), );
+	goto out;
+}
+
+#ifndef DISABLE_SCTP
+/* Receiver thread (SCTP & noTLS) : incoming message is directly saved into cc_incoming, no need to care for the stream ID */
+static void * rcvthr_notls_sctp(void * arg)
+{
+	struct cnxctx * conn = arg;
+	struct fd_cnx_rcvdata rcv_data;
+	int	  event;
+	
+	TRACE_ENTRY("%p", arg);
+	CHECK_PARAMS_DO(conn && (conn->cc_socket > 0), goto fatal);
+	
+	/* Set the thread name */
+	{
+		char buf[48];
+		snprintf(buf, sizeof(buf), "Receiver (%d) SCTP/noTLS)", conn->cc_socket);
+		fd_log_threadname ( buf );
+	}
+	
+	ASSERT( conn->cc_proto == IPPROTO_SCTP );
+	ASSERT( ! fd_cnx_teststate(conn, CC_STATUS_TLS ) );
+	ASSERT( fd_cnx_target_queue(conn) );
+	
+	do {
+		struct fd_msg_pmdl *pmdl=NULL;
+		CHECK_FCT_DO( fd_sctp_recvmeta(conn, NULL, &rcv_data.buffer, &rcv_data.length, &event), goto fatal );
+		if (event == FDEVP_CNX_ERROR) {
+			fd_cnx_markerror(conn);
+			goto out;
+		}
+		
+		if (event == FDEVP_CNX_SHUTDOWN) {
+			/* Just ignore the notification for now, we will get another error later anyway */
+			continue;
+		}
+
+		if (event == FDEVP_CNX_MSG_RECV) {
+			CHECK_MALLOC_DO( rcv_data.buffer = fd_cnx_realloc_msg_buffer(rcv_data.buffer, rcv_data.length, &pmdl), goto fatal );
+			fd_hook_call(HOOK_DATA_RECEIVED, NULL, NULL, &rcv_data, pmdl);
+		}
+		CHECK_FCT_DO( fd_event_send( fd_cnx_target_queue(conn), event, rcv_data.length, rcv_data.buffer), goto fatal );
+		
+	} while (conn->cc_loop || (event != FDEVP_CNX_MSG_RECV));
+	
+out:
+	TRACE_DEBUG(FULL, "Thread terminated");	
+	return NULL;
+
+fatal:
+	/* An unrecoverable error occurred, stop the daemon */
+	CHECK_FCT_DO(fd_core_shutdown(), );
+	goto out;
+}
+#endif /* DISABLE_SCTP */
+
+/* Start receving messages in clear (no TLS) on the connection */
+int fd_cnx_start_clear(struct cnxctx * conn, int loop)
+{
+	TRACE_ENTRY("%p %i", conn, loop);
+	
+	CHECK_PARAMS( conn && fd_cnx_target_queue(conn) && (!fd_cnx_teststate(conn, CC_STATUS_TLS)) && (!conn->cc_loop));
+	
+	/* Release resources in case of a previous call was already made */
+	CHECK_FCT_DO( fd_thr_term(&conn->cc_rcvthr), /* continue */);
+	
+	/* Save the loop request */
+	conn->cc_loop = loop;
+	
+	switch (conn->cc_proto) {
+		case IPPROTO_TCP:
+			/* Start the tcp_notls thread */
+			CHECK_POSIX( pthread_create( &conn->cc_rcvthr, NULL, rcvthr_notls_tcp, conn ) );
+			break;
+#ifndef DISABLE_SCTP
+		case IPPROTO_SCTP:
+			/* Start the tcp_notls thread */
+			CHECK_POSIX( pthread_create( &conn->cc_rcvthr, NULL, rcvthr_notls_sctp, conn ) );
+			break;
+#endif /* DISABLE_SCTP */
+		default:
+			TRACE_DEBUG(INFO, "Unknown protocol: %d", conn->cc_proto);
+			ASSERT(0);
+			return ENOTSUP;
+	}
+			
+	return 0;
+}
+
+
+
+
+/* Returns 0 on error, received data size otherwise (always >= 0). This is not used for DTLS-protected associations. */
+static ssize_t fd_tls_recv_handle_error(struct cnxctx * conn, gnutls_session_t session, void * data, size_t sz)
+{
+	ssize_t ret;
+again:	
+	CHECK_GNUTLS_DO( ret = gnutls_record_recv(session, data, sz), 
+		{
+			switch (ret) {
+				case GNUTLS_E_REHANDSHAKE: 
+					if (!fd_cnx_teststate(conn, CC_STATUS_CLOSING)) {
+						CHECK_GNUTLS_DO( ret = gnutls_handshake(session),
+							{
+								if (TRACE_BOOL(INFO)) {
+									fd_log_debug("TLS re-handshake failed on socket %d (%s) : %s", conn->cc_socket, conn->cc_id, gnutls_strerror(ret));
+								}
+								goto end;
+							} );
+					}
+
+				case GNUTLS_E_AGAIN:
+				case GNUTLS_E_INTERRUPTED:
+					if (!fd_cnx_teststate(conn, CC_STATUS_CLOSING))
+						goto again;
+					TRACE_DEBUG(FULL, "Connection is closing, so abord gnutls_record_recv now.");
+					break;
+
+				case GNUTLS_E_UNEXPECTED_PACKET_LENGTH:
+					/* The connection is closed */
+					TRACE_DEBUG(FULL, "Got 0 size while reading the socket, probably connection closed...");
+					break;
+				
+				default:
+					if (gnutls_error_is_fatal (ret) == 0) {
+						LOG_N("Ignoring non-fatal GNU TLS error: %s", gnutls_strerror (ret));
+						goto again;
+					}
+					LOG_E("Fatal GNUTLS error: %s", gnutls_strerror (ret));
+			}
+		} );
+		
+	if (ret == 0)
+		CHECK_GNUTLS_DO( gnutls_bye(session, GNUTLS_SHUT_RDWR),  );
+	
+end:	
+	if (ret <= 0)
+		fd_cnx_markerror(conn);
+	return ret;
+}
+
+/* Wrapper around gnutls_record_send to handle some error codes. This is also used for DTLS-protected associations */
+static ssize_t fd_tls_send_handle_error(struct cnxctx * conn, gnutls_session_t session, void * data, size_t sz)
+{
+	ssize_t ret;
+	struct timespec ts, now;
+	CHECK_SYS_DO(  clock_gettime(CLOCK_REALTIME, &ts), return -1 );
+again:	
+	CHECK_GNUTLS_DO( ret = gnutls_record_send(session, data, sz),
+		{
+			pthread_testcancel();
+			switch (ret) {
+				case GNUTLS_E_REHANDSHAKE: 
+					if (!fd_cnx_teststate(conn, CC_STATUS_CLOSING)) {
+						CHECK_GNUTLS_DO( ret = gnutls_handshake(session),
+							{
+								if (TRACE_BOOL(INFO)) {
+									fd_log_debug("TLS re-handshake failed on socket %d (%s) : %s", conn->cc_socket, conn->cc_id, gnutls_strerror(ret));
+								}
+								goto end;
+							} );
+					}
+
+				case GNUTLS_E_AGAIN:
+				case GNUTLS_E_INTERRUPTED:
+					CHECK_SYS_DO(  clock_gettime(CLOCK_REALTIME, &now), return -1 );
+					if ( ((now.tv_sec - ts.tv_sec) * 1000 + ((now.tv_nsec - ts.tv_nsec) / 1000000L)) > MAX_HOTL_BLOCKING_TIME) {
+						LOG_D("Unable to send any data for %dms, closing the connection", MAX_HOTL_BLOCKING_TIME);
+					} else if (! fd_cnx_teststate(conn, CC_STATUS_CLOSING )) {
+						goto again;
+					}
+					break;
+
+				default:
+					if (gnutls_error_is_fatal (ret) == 0) {
+						LOG_N("Ignoring non-fatal GNU TLS error: %s", gnutls_strerror (ret));
+						goto again;
+					}
+					LOG_E("Fatal GNUTLS error: %s", gnutls_strerror (ret));
+			}
+		} );
+end:	
+	if (ret <= 0)
+		fd_cnx_markerror(conn);
+		
+	return ret;
+}
+
+
+/* The function that receives TLS data and re-builds a Diameter message -- it exits only on error or cancelation */
+/* 	   For the case of DTLS, since we are not using SCTP_UNORDERED, the messages over a single stream are ordered.
+	   Furthermore, as long as messages are shorter than the MTU [2^14 = 16384 bytes], they are delivered in a single
+	   record, as far as I understand. 
+	   For larger messages, however, it is possible that pieces of messages coming from different streams can get interleaved. 
+	   As a result, we do not use the following function for DTLS reception, because we use the sequence number to rebuild the
+	   messages. */
+int fd_tls_rcvthr_core(struct cnxctx * conn, gnutls_session_t session)
+{
+	/* No guarantee that GnuTLS preserves the message boundaries, so we re-build it as in TCP. */
+	do {
+		uint8_t header[4];
+		struct fd_cnx_rcvdata rcv_data;
+		struct fd_msg_pmdl *pmdl=NULL;
+		ssize_t ret = 0;
+		size_t	received = 0;
+
+		do {
+			ret = fd_tls_recv_handle_error(conn, session, &header[received], sizeof(header) - received);
+			if (ret <= 0) {
+				/* The connection is closed */
+				goto out;
+			}
+			received += ret;
+		} while (received < sizeof(header));
+
+		rcv_data.length = ((size_t)header[1] << 16) + ((size_t)header[2] << 8) + (size_t)header[3];
+
+		/* Check the received word is a valid beginning of a Diameter message */
+		if ((header[0] != DIAMETER_VERSION)	/* defined in <libfreeDiameter.h> */
+		   || (rcv_data.length > DIAMETER_MSG_SIZE_MAX)) { /* to avoid too big mallocs */
+			/* The message is suspect */
+			LOG_E( "Received suspect header [ver: %d, size: %zd] from '%s', assume disconnection", (int)header[0], rcv_data.length, conn->cc_remid);
+			fd_cnx_markerror(conn);
+			goto out;
+		}
+
+		/* Ok, now we can really receive the data */
+		CHECK_MALLOC(  rcv_data.buffer = fd_cnx_alloc_msg_buffer( rcv_data.length, &pmdl ) );
+		memcpy(rcv_data.buffer, header, sizeof(header));
+
+		while (received < rcv_data.length) {
+			pthread_cleanup_push(free_rcvdata, &rcv_data); /* In case we are canceled, clean the partialy built buffer */
+			ret = fd_tls_recv_handle_error(conn, session, rcv_data.buffer + received, rcv_data.length - received);
+			pthread_cleanup_pop(0);
+
+			if (ret <= 0) {
+				free_rcvdata(&rcv_data);
+				goto out;
+			}
+			received += ret;
+		}
+		
+		fd_hook_call(HOOK_DATA_RECEIVED, NULL, NULL, &rcv_data, pmdl);
+		
+		/* We have received a complete message, pass it to the daemon */
+		CHECK_FCT_DO( ret = fd_event_send( fd_cnx_target_queue(conn), FDEVP_CNX_MSG_RECV, rcv_data.length, rcv_data.buffer), 
+			{ 
+				free_rcvdata(&rcv_data);
+				CHECK_FCT_DO(fd_core_shutdown(), );
+				return ret; 
+			} );
+		
+	} while (1);
+	
+out:
+	return ENOTCONN;
+}
+
+/* Receiver thread (TLS & 1 stream SCTP or TCP)  */
+static void * rcvthr_tls_single(void * arg)
+{
+	struct cnxctx * conn = arg;
+	
+	TRACE_ENTRY("%p", arg);
+	CHECK_PARAMS_DO(conn && (conn->cc_socket > 0), return NULL );
+	
+	/* Set the thread name */
+	{
+		char buf[48];
+		snprintf(buf, sizeof(buf), "Receiver (%d) TLS/single stream", conn->cc_socket);
+		fd_log_threadname ( buf );
+	}
+	
+	ASSERT( fd_cnx_teststate(conn, CC_STATUS_TLS) );
+	ASSERT( fd_cnx_target_queue(conn) );
+
+	/* The next function only returns when there is an error on the socket */	
+	CHECK_FCT_DO(fd_tls_rcvthr_core(conn, conn->cc_tls_para.session), /* continue */);
+
+	TRACE_DEBUG(FULL, "Thread terminated");	
+	return NULL;
+}
+
+/* Prepare a gnutls session object for handshake */
+int fd_tls_prepare(gnutls_session_t * session, int mode, int dtls, char * priority, void * alt_creds)
+{
+	if (dtls) {
+		LOG_E("DTLS sessions not yet supported");
+		return ENOTSUP;
+	}
+
+	/* Create the session context */
+	CHECK_GNUTLS_DO( gnutls_init (session, mode), return ENOMEM );
+
+	/* Set the algorithm suite */
+	if (priority) {
+		const char * errorpos;
+		CHECK_GNUTLS_DO( gnutls_priority_set_direct( *session, priority, &errorpos ), 
+			{ TRACE_DEBUG(INFO, "Error in priority string '%s' at position: '%s'", priority, errorpos); return EINVAL; } );
+	} else {
+		CHECK_GNUTLS_DO( gnutls_priority_set( *session, fd_g_config->cnf_sec_data.prio_cache ), return EINVAL );
+	}
+
+	/* Set the credentials of this side of the connection */
+	CHECK_GNUTLS_DO( gnutls_credentials_set (*session, GNUTLS_CRD_CERTIFICATE, alt_creds ?: fd_g_config->cnf_sec_data.credentials), return EINVAL );
+
+	/* Request the remote credentials as well */
+	if (mode == GNUTLS_SERVER) {
+		gnutls_certificate_server_set_request (*session, GNUTLS_CERT_REQUIRE);
+	}
+		
+	return 0;
+}
+
+#ifndef GNUTLS_VERSION_300
+
+/* Verify remote credentials after successful handshake (return 0 if OK, EINVAL otherwise) */
+int fd_tls_verify_credentials(gnutls_session_t session, struct cnxctx * conn, int verbose)
+{
+	int i, ret = 0;
+	unsigned int gtret;
+	const gnutls_datum_t *cert_list;
+	unsigned int cert_list_size;
+	gnutls_x509_crt_t cert;
+	time_t now;
+	
+	TRACE_ENTRY("%p %d", conn, verbose);
+	CHECK_PARAMS(conn);
+	
+	/* Trace the session information -- http://www.gnu.org/software/gnutls/manual/gnutls.html#Obtaining-session-information */
+	#ifdef DEBUG
+	if (verbose) {
+		const char *tmp;
+		gnutls_kx_algorithm_t kx;
+  		gnutls_credentials_type_t cred;
+		
+		LOG_D("TLS Session information for connection '%s':", conn->cc_id);
+
+		/* print the key exchange's algorithm name */
+		GNUTLS_TRACE( kx = gnutls_kx_get (session) );
+		GNUTLS_TRACE( tmp = gnutls_kx_get_name (kx) );
+		LOG_D("\t - Key Exchange: %s", tmp);
+
+		/* Check the authentication type used and switch
+		* to the appropriate. */
+		GNUTLS_TRACE( cred = gnutls_auth_get_type (session) );
+		switch (cred)
+		{
+			case GNUTLS_CRD_IA:
+				LOG_D("\t - TLS/IA session");
+				break;
+
+			case GNUTLS_CRD_PSK:
+				/* This returns NULL in server side. */
+				if (gnutls_psk_client_get_hint (session) != NULL)
+					LOG_D("\t - PSK authentication. PSK hint '%s'",
+						gnutls_psk_client_get_hint (session));
+				/* This returns NULL in client side. */
+				if (gnutls_psk_server_get_username (session) != NULL)
+					LOG_D("\t - PSK authentication. Connected as '%s'",
+						gnutls_psk_server_get_username (session));
+				break;
+
+			case GNUTLS_CRD_ANON:	/* anonymous authentication */
+				LOG_D("\t - Anonymous DH using prime of %d bits",
+					gnutls_dh_get_prime_bits (session));
+				break;
+
+			case GNUTLS_CRD_CERTIFICATE:	/* certificate authentication */
+				/* Check if we have been using ephemeral Diffie-Hellman. */
+				if (kx == GNUTLS_KX_DHE_RSA || kx == GNUTLS_KX_DHE_DSS) {
+					LOG_D("\t - Ephemeral DH using prime of %d bits",
+						gnutls_dh_get_prime_bits (session));
+				}
+				break;
+#ifdef ENABLE_SRP				
+			case GNUTLS_CRD_SRP:
+				LOG_D("\t - SRP session with username %s",
+					gnutls_srp_server_get_username (session));
+				break;
+#endif /* ENABLE_SRP */
+
+			default:
+				fd_log_debug("\t - Different type of credentials for the session (%d).", cred);
+				break;
+
+		}
+
+		/* print the protocol's name (ie TLS 1.0) */
+		tmp = gnutls_protocol_get_name (gnutls_protocol_get_version (session));
+		LOG_D("\t - Protocol: %s", tmp);
+
+		/* print the certificate type of the peer. ie X.509 */
+		tmp = gnutls_certificate_type_get_name (gnutls_certificate_type_get (session));
+		LOG_D("\t - Certificate Type: %s", tmp);
+
+		/* print the compression algorithm (if any) */
+		tmp = gnutls_compression_get_name (gnutls_compression_get (session));
+		LOG_D("\t - Compression: %s", tmp);
+
+		/* print the name of the cipher used. ie 3DES. */
+		tmp = gnutls_cipher_get_name (gnutls_cipher_get (session));
+		LOG_D("\t - Cipher: %s", tmp);
+
+		/* Print the MAC algorithms name. ie SHA1 */
+		tmp = gnutls_mac_get_name (gnutls_mac_get (session));
+		LOG_D("\t - MAC: %s", tmp);
+	}
+	#endif /* DEBUG */
+	
+	/* First, use built-in verification */
+	CHECK_GNUTLS_DO( gnutls_certificate_verify_peers2 (session, &gtret), return EINVAL );
+	if (gtret) {
+		LOG_E("TLS: Remote certificate invalid on socket %d (Remote: '%s')(Connection: '%s') :", conn->cc_socket, conn->cc_remid, conn->cc_id);
+		if (gtret & GNUTLS_CERT_INVALID)
+			LOG_E(" - The certificate is not trusted (unknown CA? expired?)");
+		if (gtret & GNUTLS_CERT_REVOKED)
+			LOG_E(" - The certificate has been revoked.");
+		if (gtret & GNUTLS_CERT_SIGNER_NOT_FOUND)
+			LOG_E(" - The certificate hasn't got a known issuer.");
+		if (gtret & GNUTLS_CERT_SIGNER_NOT_CA)
+			LOG_E(" - The certificate signer is not a CA, or uses version 1, or 3 without basic constraints.");
+		if (gtret & GNUTLS_CERT_INSECURE_ALGORITHM)
+			LOG_E(" - The certificate signature uses a weak algorithm.");
+		return EINVAL;
+	}
+	
+	/* Code from http://www.gnu.org/software/gnutls/manual/gnutls.html#Verifying-peer_0027s-certificate */
+	if (gnutls_certificate_type_get (session) != GNUTLS_CRT_X509) {
+		LOG_E("TLS: Remote peer did not present a certificate, other mechanisms are not supported yet. socket %d (Remote: '%s')(Connection: '%s') :", conn->cc_socket, conn->cc_remid, conn->cc_id);
+		return EINVAL;
+	}
+	
+	GNUTLS_TRACE( cert_list = gnutls_certificate_get_peers (session, &cert_list_size) );
+	if (cert_list == NULL)
+		return EINVAL;
+	
+	now = time(NULL);
+	
+	#ifdef DEBUG
+		char serial[40];
+		char dn[128];
+		size_t size;
+		unsigned int algo, bits;
+		time_t expiration_time, activation_time;
+		
+		LOG_D("TLS Certificate information for connection '%s' (%d certs provided):", conn->cc_id, cert_list_size);
+		for (i = 0; i < cert_list_size; i++)
+		{
+
+			CHECK_GNUTLS_DO( gnutls_x509_crt_init (&cert), return EINVAL);
+			CHECK_GNUTLS_DO( gnutls_x509_crt_import (cert, &cert_list[i], GNUTLS_X509_FMT_DER), return EINVAL);
+		
+			LOG_A(" Certificate %d info:", i);
+
+			GNUTLS_TRACE( expiration_time = gnutls_x509_crt_get_expiration_time (cert) );
+			GNUTLS_TRACE( activation_time = gnutls_x509_crt_get_activation_time (cert) );
+
+			LOG( i ? FD_LOG_ANNOYING : FD_LOG_DEBUG, "\t - Certificate is valid since: %.24s", ctime (&activation_time));
+			LOG( i ? FD_LOG_ANNOYING : FD_LOG_DEBUG, "\t - Certificate expires: %.24s", ctime (&expiration_time));
+
+			/* Print the serial number of the certificate. */
+			size = sizeof (serial);
+			gnutls_x509_crt_get_serial (cert, serial, &size);
+			
+			{
+				int j;
+				char buf[1024];
+				snprintf(buf, sizeof(buf), "\t - Certificate serial number: ");
+				for (j = 0; j < size; j++) {
+					snprintf(buf+strlen(buf), sizeof(buf)-strlen(buf), "%02hhx", serial[j]);
+				}
+				LOG( i ? FD_LOG_ANNOYING : FD_LOG_DEBUG, "%s", buf);
+			}
+
+			/* Extract some of the public key algorithm's parameters */
+			GNUTLS_TRACE( algo = gnutls_x509_crt_get_pk_algorithm (cert, &bits) );
+			LOG( i ? FD_LOG_ANNOYING : FD_LOG_DEBUG, "\t - Certificate public key: %s",
+			      gnutls_pk_algorithm_get_name (algo));
+
+			/* Print the version of the X.509 certificate. */
+			LOG( i ? FD_LOG_ANNOYING : FD_LOG_DEBUG, "\t - Certificate version: #%d",
+			      gnutls_x509_crt_get_version (cert));
+
+			size = sizeof (dn);
+			GNUTLS_TRACE( gnutls_x509_crt_get_dn (cert, dn, &size) );
+			LOG( i ? FD_LOG_ANNOYING : FD_LOG_DEBUG, "\t - DN: %s", dn);
+
+			size = sizeof (dn);
+			GNUTLS_TRACE( gnutls_x509_crt_get_issuer_dn (cert, dn, &size) );
+			LOG( i ? FD_LOG_ANNOYING : FD_LOG_DEBUG, "\t - Issuer's DN: %s", dn);
+
+			GNUTLS_TRACE( gnutls_x509_crt_deinit (cert) );
+		}
+	#endif /* DEBUG */
+
+	/* Check validity of all the certificates */
+	for (i = 0; i < cert_list_size; i++)
+	{
+		time_t deadline;
+		
+		CHECK_GNUTLS_DO( gnutls_x509_crt_init (&cert), return EINVAL);
+		CHECK_GNUTLS_DO( gnutls_x509_crt_import (cert, &cert_list[i], GNUTLS_X509_FMT_DER), return EINVAL);
+		
+		GNUTLS_TRACE( deadline = gnutls_x509_crt_get_expiration_time(cert) );
+		if ((deadline != (time_t)-1) && (deadline < now)) {
+			LOG_E("TLS: Remote certificate invalid on socket %d (Remote: '%s')(Connection: '%s') :", conn->cc_socket, conn->cc_remid, conn->cc_id);
+			LOG_E(" - The certificate %d in the chain is expired", i);
+			ret = EINVAL;
+		}
+		
+		GNUTLS_TRACE( deadline = gnutls_x509_crt_get_activation_time(cert) );
+		if ((deadline != (time_t)-1) && (deadline > now)) {
+			LOG_E("TLS: Remote certificate invalid on socket %d (Remote: '%s')(Connection: '%s') :", conn->cc_socket, conn->cc_remid, conn->cc_id);
+			LOG_E(" - The certificate %d in the chain is not yet activated", i);
+			ret = EINVAL;
+		}
+		
+		if ((i == 0) && (conn->cc_tls_para.cn)) {
+			if (!gnutls_x509_crt_check_hostname (cert, conn->cc_tls_para.cn)) {
+				LOG_E("TLS: Remote certificate invalid on socket %d (Remote: '%s')(Connection: '%s') :", conn->cc_socket, conn->cc_remid, conn->cc_id);
+				LOG_E(" - The certificate hostname does not match '%s'", conn->cc_tls_para.cn);
+				ret = EINVAL;
+			}
+		}
+		
+		GNUTLS_TRACE( gnutls_x509_crt_deinit (cert) );
+	}
+
+	return ret;
+}
+
+#else /* GNUTLS_VERSION_300 */
+
+/* Verify remote credentials DURING handshake (return gnutls status) */
+int fd_tls_verify_credentials_2(gnutls_session_t session)
+{
+	/* inspired from gnutls 3.x guidelines */
+	unsigned int status;
+	const gnutls_datum_t *cert_list = NULL;
+	unsigned int cert_list_size;
+	gnutls_x509_crt_t cert;
+	struct cnxctx * conn;
+	int hostname_verified = 0;
+
+	TRACE_ENTRY("%p", session);
+	
+	/* get the associated connection */
+	conn = gnutls_session_get_ptr (session);
+	
+	/* Trace the session information -- http://www.gnu.org/software/gnutls/manual/gnutls.html#Obtaining-session-information */
+#ifdef DEBUG
+		const char *tmp;
+		gnutls_credentials_type_t cred;
+		gnutls_kx_algorithm_t kx;
+		int dhe, ecdh;
+
+		dhe = ecdh = 0;
+
+		LOG_A("TLS Session information for connection '%s':", conn->cc_id);
+		
+		/* print the key exchange's algorithm name
+		*/
+		GNUTLS_TRACE( kx = gnutls_kx_get (session) );
+		GNUTLS_TRACE( tmp = gnutls_kx_get_name (kx) );
+		LOG_D("\t- Key Exchange: %s", tmp);
+
+		/* Check the authentication type used and switch
+		* to the appropriate.
+		*/
+		GNUTLS_TRACE( cred = gnutls_auth_get_type (session) );
+		switch (cred)
+		{
+			case GNUTLS_CRD_IA:
+				LOG_D("\t - TLS/IA session");
+				break;
+
+
+			#ifdef ENABLE_SRP
+			case GNUTLS_CRD_SRP:
+				LOG_D("\t - SRP session with username %s",
+					gnutls_srp_server_get_username (session));
+				break;
+			#endif
+
+			case GNUTLS_CRD_PSK:
+				/* This returns NULL in server side.
+				*/
+				if (gnutls_psk_client_get_hint (session) != NULL)
+					LOG_D("\t - PSK authentication. PSK hint '%s'",
+						gnutls_psk_client_get_hint (session));
+				/* This returns NULL in client side.
+				*/
+				if (gnutls_psk_server_get_username (session) != NULL)
+					LOG_D("\t - PSK authentication. Connected as '%s'",
+						gnutls_psk_server_get_username (session));
+
+				if (kx == GNUTLS_KX_ECDHE_PSK)
+					ecdh = 1;
+				else if (kx == GNUTLS_KX_DHE_PSK)
+					dhe = 1;
+				break;
+
+			case GNUTLS_CRD_ANON:      /* anonymous authentication */
+				LOG_D("\t - Anonymous DH using prime of %d bits",
+					gnutls_dh_get_prime_bits (session));
+				if (kx == GNUTLS_KX_ANON_ECDH)
+					ecdh = 1;
+				else if (kx == GNUTLS_KX_ANON_DH)
+					dhe = 1;
+				break;
+
+			case GNUTLS_CRD_CERTIFICATE:       /* certificate authentication */
+
+				/* Check if we have been using ephemeral Diffie-Hellman.
+				*/
+				if (kx == GNUTLS_KX_DHE_RSA || kx == GNUTLS_KX_DHE_DSS)
+					dhe = 1;
+				else if (kx == GNUTLS_KX_ECDHE_RSA || kx == GNUTLS_KX_ECDHE_ECDSA)
+					ecdh = 1;
+				
+				/* Now print some info on the remote certificate */
+				if (gnutls_certificate_type_get (session) == GNUTLS_CRT_X509) {
+					gnutls_datum_t cinfo;
+
+					cert_list = gnutls_certificate_get_peers (session, &cert_list_size);
+
+					LOG_D("\t Peer provided %d certificates.", cert_list_size);
+
+					if (cert_list_size > 0)
+					{
+						int ret;
+
+						/* we only print information about the first certificate.
+						*/
+						gnutls_x509_crt_init (&cert);
+
+						gnutls_x509_crt_import (cert, &cert_list[0], GNUTLS_X509_FMT_DER);
+
+												LOG_A("\t Certificate info:");
+
+						/* This is the preferred way of printing short information about
+						 a certificate. */
+
+						ret = gnutls_x509_crt_print (cert, GNUTLS_CRT_PRINT_ONELINE, &cinfo);
+						if (ret == 0)
+						{
+						  LOG_D("\t\t%s", cinfo.data);
+						  gnutls_free (cinfo.data);
+						}
+						
+						if (conn->cc_tls_para.cn) {
+							if (!gnutls_x509_crt_check_hostname (cert, conn->cc_tls_para.cn)) {
+								LOG_E("\tTLS: Remote certificate invalid on socket %d (Remote: '%s')(Connection: '%s') :", conn->cc_socket, conn->cc_remid, conn->cc_id);
+								LOG_E("\t - The certificate hostname does not match '%s'", conn->cc_tls_para.cn);
+								gnutls_x509_crt_deinit (cert);
+								return GNUTLS_E_CERTIFICATE_ERROR;
+							}
+							
+						}
+
+						hostname_verified = 1;
+
+						gnutls_x509_crt_deinit (cert);
+
+					}
+    				}
+				break;
+
+			default:
+				LOG_E("\t - unknown session type (%d)", cred);
+
+		}                           /* switch */
+
+		if (ecdh != 0)
+			LOG_D("\t - Ephemeral ECDH using curve %s",
+				gnutls_ecc_curve_get_name (gnutls_ecc_curve_get (session)));
+		else if (dhe != 0)
+			LOG_D("\t - Ephemeral DH using prime of %d bits",
+				gnutls_dh_get_prime_bits (session));
+
+		/* print the protocol's name (ie TLS 1.0) 
+		*/
+		tmp = gnutls_protocol_get_name (gnutls_protocol_get_version (session));
+		LOG_D("\t - Protocol: %s", tmp);
+
+		/* print the certificate type of the peer.
+		* ie X.509
+		*/
+		tmp = gnutls_certificate_type_get_name (gnutls_certificate_type_get (session));
+		LOG_D("\t - Certificate Type: %s", tmp);
+
+		/* print the compression algorithm (if any)
+		*/
+		tmp = gnutls_compression_get_name (gnutls_compression_get (session));
+		LOG_D("\t - Compression: %s", tmp);
+
+		/* print the name of the cipher used.
+		* ie 3DES.
+		*/
+		tmp = gnutls_cipher_get_name (gnutls_cipher_get (session));
+		LOG_D("\t - Cipher: %s", tmp);
+
+		/* Print the MAC algorithms name.
+		* ie SHA1
+		*/
+		tmp = gnutls_mac_get_name (gnutls_mac_get (session));
+		LOG_D("\t - MAC: %s", tmp);
+	
+#endif /* DEBUG */		
+
+	/* This verification function uses the trusted CAs in the credentials
+	* structure. So you must have installed one or more CA certificates.
+	*/
+	CHECK_GNUTLS_DO( gnutls_certificate_verify_peers2 (session, &status), return GNUTLS_E_CERTIFICATE_ERROR );
+	if (status & GNUTLS_CERT_INVALID) {
+		LOG_E("TLS: Remote certificate invalid on socket %d (Remote: '%s')(Connection: '%s') :", conn->cc_socket, conn->cc_remid, conn->cc_id);
+		if (status & GNUTLS_CERT_SIGNER_NOT_FOUND)
+			LOG_E(" - The certificate hasn't got a known issuer.");
+
+		if (status & GNUTLS_CERT_REVOKED)
+			LOG_E(" - The certificate has been revoked.");
+
+		if (status & GNUTLS_CERT_EXPIRED)
+			LOG_E(" - The certificate has expired.");
+
+		if (status & GNUTLS_CERT_NOT_ACTIVATED)
+			LOG_E(" - The certificate is not yet activated.");
+	}	
+	if (status & GNUTLS_CERT_INVALID)
+	{
+		return GNUTLS_E_CERTIFICATE_ERROR;
+	}
+	
+	/* Up to here the process is the same for X.509 certificates and
+	* OpenPGP keys. From now on X.509 certificates are assumed. This can
+	* be easily extended to work with openpgp keys as well.
+	*/
+	if ((!hostname_verified) && (conn->cc_tls_para.cn)) {
+		if (gnutls_certificate_type_get (session) != GNUTLS_CRT_X509) {
+			LOG_E("TLS: Remote credentials are not x509, rejected on socket %d (Remote: '%s')(Connection: '%s') :", conn->cc_socket, conn->cc_remid, conn->cc_id);
+			return GNUTLS_E_CERTIFICATE_ERROR;
+		}
+
+		CHECK_GNUTLS_DO( gnutls_x509_crt_init (&cert), return GNUTLS_E_CERTIFICATE_ERROR );
+
+		cert_list = gnutls_certificate_get_peers (session, &cert_list_size);
+		CHECK_PARAMS_DO( cert_list, return GNUTLS_E_CERTIFICATE_ERROR );
+
+		CHECK_GNUTLS_DO( gnutls_x509_crt_import (cert, &cert_list[0], GNUTLS_X509_FMT_DER), return GNUTLS_E_CERTIFICATE_ERROR );
+
+		if (!gnutls_x509_crt_check_hostname (cert, conn->cc_tls_para.cn)) {
+			LOG_E("TLS: Remote certificate invalid on socket %d (Remote: '%s')(Connection: '%s') :", conn->cc_socket, conn->cc_remid, conn->cc_id);
+			LOG_E(" - The certificate hostname does not match '%s'", conn->cc_tls_para.cn);
+			gnutls_x509_crt_deinit (cert);
+			return GNUTLS_E_CERTIFICATE_ERROR;
+		}
+
+		gnutls_x509_crt_deinit (cert);
+	}
+
+	/* notify gnutls to continue handshake normally */
+	return 0;
+}
+
+#endif /* GNUTLS_VERSION_300 */
+
+static int fd_cnx_may_dtls(struct cnxctx * conn) {
+#ifndef DISABLE_SCTP
+	if ((conn->cc_proto == IPPROTO_SCTP) && (conn->cc_tls_para.algo == ALGO_HANDSHAKE_DEFAULT))
+		return 1;
+#endif /* DISABLE_SCTP */
+	return 0;
+}
+
+#ifndef DISABLE_SCTP
+static int fd_cnx_uses_dtls(struct cnxctx * conn) {
+	return fd_cnx_may_dtls(conn) && (fd_cnx_teststate(conn, CC_STATUS_TLS));
+}
+#endif /* DISABLE_SCTP */
+
+/* TLS handshake a connection; no need to have called start_clear before. Reception is active if handhsake is successful */
+int fd_cnx_handshake(struct cnxctx * conn, int mode, int algo, char * priority, void * alt_creds)
+{
+	int dtls = 0;
+	
+	TRACE_ENTRY( "%p %d %d %p %p", conn, mode, algo, priority, alt_creds);
+	CHECK_PARAMS( conn && (!fd_cnx_teststate(conn, CC_STATUS_TLS)) && ( (mode == GNUTLS_CLIENT) || (mode == GNUTLS_SERVER) ) && (!conn->cc_loop) );
+
+	/* Save the mode */
+	conn->cc_tls_para.mode = mode;
+	conn->cc_tls_para.algo = algo;
+	
+	/* Cancel receiving thread if any -- it should already be terminated anyway, we just release the resources */
+	CHECK_FCT_DO( fd_thr_term(&conn->cc_rcvthr), /* continue */);
+	
+	/* Once TLS handshake is done, we don't stop after the first message */
+	conn->cc_loop = 1;
+	
+	dtls = fd_cnx_may_dtls(conn);
+	
+	/* Prepare the master session credentials and priority */
+	CHECK_FCT( fd_tls_prepare(&conn->cc_tls_para.session, mode, dtls, priority, alt_creds) );
+
+	/* Special case: multi-stream TLS is not natively managed in GNU TLS, we use a wrapper library */
+	if ((!dtls) && (conn->cc_sctp_para.pairs > 1)) {
+#ifdef DISABLE_SCTP
+		ASSERT(0);
+		CHECK_FCT( ENOTSUP );
+#else /* DISABLE_SCTP */
+		/* Initialize the wrapper, start the demux thread */
+		CHECK_FCT( fd_sctp3436_init(conn) );
+#endif /* DISABLE_SCTP */
+	} else {
+		/* Set the transport pointer passed to push & pull callbacks */
+		GNUTLS_TRACE( gnutls_transport_set_ptr( conn->cc_tls_para.session, (gnutls_transport_ptr_t) conn ) );
+
+		/* Set the push and pull callbacks */
+		if (!dtls) {
+			#ifdef GNUTLS_VERSION_300
+			GNUTLS_TRACE( gnutls_transport_set_pull_timeout_function( conn->cc_tls_para.session, (void *)fd_cnx_s_select ) );
+			#endif /* GNUTLS_VERSION_300 */
+			GNUTLS_TRACE( gnutls_transport_set_pull_function(conn->cc_tls_para.session, (void *)fd_cnx_s_recv) );
+			#ifndef GNUTLS_VERSION_212
+			GNUTLS_TRACE( gnutls_transport_set_push_function(conn->cc_tls_para.session, (void *)fd_cnx_s_send) );
+			#else /* GNUTLS_VERSION_212 */
+			GNUTLS_TRACE( gnutls_transport_set_vec_push_function(conn->cc_tls_para.session, (void *)fd_cnx_s_sendv) );
+			#endif /* GNUTLS_VERSION_212 */
+		} else {
+			TODO("DTLS push/pull functions");
+			return ENOTSUP;
+		}
+	}
+	
+	/* additional initialization for gnutls 3.x */
+	#ifdef GNUTLS_VERSION_300
+		/* the verify function has already been set in the global initialization in config.c */
+	
+	/* fd_tls_verify_credentials_2 uses the connection */
+	gnutls_session_set_ptr (conn->cc_tls_para.session, (void *) conn);
+	
+	if ((conn->cc_tls_para.cn != NULL) && (mode == GNUTLS_CLIENT)) {
+		/* this might allow virtual hosting on the remote peer */
+		CHECK_GNUTLS_DO( gnutls_server_name_set (conn->cc_tls_para.session, GNUTLS_NAME_DNS, conn->cc_tls_para.cn, strlen(conn->cc_tls_para.cn)), /* ignore failure */);
+	}
+	
+	#endif /* GNUTLS_VERSION_300 */
+
+	#ifdef GNUTLS_VERSION_310
+	GNUTLS_TRACE( gnutls_handshake_set_timeout( conn->cc_tls_para.session, GNUTLS_DEFAULT_HANDSHAKE_TIMEOUT));
+	#endif /* GNUTLS_VERSION_310 */
+	
+	/* Mark the connection as protected from here, so that the gnutls credentials will be freed */
+	fd_cnx_addstate(conn, CC_STATUS_TLS);
+	
+	/* Handshake master session */
+	{
+		int ret;
+	
+		CHECK_GNUTLS_DO( ret = gnutls_handshake(conn->cc_tls_para.session),
+			{
+				if (TRACE_BOOL(INFO)) {
+					fd_log_debug("TLS Handshake failed on socket %d (%s) : %s", conn->cc_socket, conn->cc_id, gnutls_strerror(ret));
+				}
+				fd_cnx_markerror(conn);
+				return EINVAL;
+			} );
+
+		#ifndef GNUTLS_VERSION_300
+		/* Now verify the remote credentials are valid -- only simple tests here */
+		CHECK_FCT_DO( fd_tls_verify_credentials(conn->cc_tls_para.session, conn, 1), 
+			{  
+				CHECK_GNUTLS_DO( gnutls_bye(conn->cc_tls_para.session, GNUTLS_SHUT_RDWR),  );
+				fd_cnx_markerror(conn);
+				return EINVAL;
+			});
+		#endif /* GNUTLS_VERSION_300 */
+	}
+	
+	/* Multi-stream TLS: handshake other streams as well */
+	if ((!dtls) && (conn->cc_sctp_para.pairs > 1)) {
+#ifndef DISABLE_SCTP
+		/* Start reading the messages from the master session. That way, if the remote peer closed, we are not stuck inside handshake */
+		CHECK_FCT(fd_sctp3436_startthreads(conn, 0));
+
+		/* Resume all additional sessions from the master one. */
+		CHECK_FCT(fd_sctp3436_handshake_others(conn, priority, alt_creds));
+
+		/* Start decrypting the messages from all threads and queuing them in target queue */
+		CHECK_FCT(fd_sctp3436_startthreads(conn, 1));
+#endif /* DISABLE_SCTP */
+	} else {
+		/* Start decrypting the data */
+		if (!dtls) {
+			CHECK_POSIX( pthread_create( &conn->cc_rcvthr, NULL, rcvthr_tls_single, conn ) );
+		} else {
+			TODO("Signal the dtls_push function that multiple streams can be used from this point.");
+			TODO("Create DTLS rcvthr (must reassembly based on seq numbers & stream id ?)");
+			return ENOTSUP;
+		}
+	}
+	
+	return 0;
+}
+
+/* Retrieve TLS credentials of the remote peer, after handshake */
+int fd_cnx_getcred(struct cnxctx * conn, const gnutls_datum_t **cert_list, unsigned int *cert_list_size)
+{
+	TRACE_ENTRY("%p %p %p", conn, cert_list, cert_list_size);
+	CHECK_PARAMS( conn && fd_cnx_teststate(conn, CC_STATUS_TLS) && cert_list && cert_list_size );
+	
+	/* This function only works for X.509 certificates. */
+	CHECK_PARAMS( gnutls_certificate_type_get (conn->cc_tls_para.session) == GNUTLS_CRT_X509 );
+	
+	GNUTLS_TRACE( *cert_list = gnutls_certificate_get_peers (conn->cc_tls_para.session, cert_list_size) );
+	if (*cert_list == NULL) {
+		TRACE_DEBUG(INFO, "No certificate was provided by remote peer / an error occurred.");
+		return EINVAL;
+	}
+
+	TRACE_DEBUG( FULL, "Saved certificate chain (%d certificates) in peer structure.", *cert_list_size);
+	
+	return 0;
+}
+
+/* Receive next message. if timeout is not NULL, wait only until timeout. This function only pulls from a queue, mgr thread is filling that queue aynchrounously. */
+/* if the altfifo has been set on this conn object, this function must not be called */
+int fd_cnx_receive(struct cnxctx * conn, struct timespec * timeout, unsigned char **buf, size_t * len)
+{
+	int    ev;
+	size_t ev_sz;
+	void * ev_data;
+	
+	TRACE_ENTRY("%p %p %p %p", conn, timeout, buf, len);
+	CHECK_PARAMS(conn && (conn->cc_socket > 0) && buf && len);
+	CHECK_PARAMS(conn->cc_rcvthr != (pthread_t)NULL);
+	CHECK_PARAMS(conn->cc_alt == NULL);
+
+	/* Now, pull the first event */
+get_next:
+	if (timeout) {
+		CHECK_FCT( fd_event_timedget(conn->cc_incoming, timeout, FDEVP_PSM_TIMEOUT, &ev, &ev_sz, &ev_data) );
+	} else {
+		CHECK_FCT( fd_event_get(conn->cc_incoming, &ev, &ev_sz, &ev_data) );
+	}
+	
+	switch (ev) {
+		case FDEVP_CNX_MSG_RECV:
+			/* We got one */
+			*len = ev_sz;
+			*buf = ev_data;
+			return 0;
+			
+		case FDEVP_PSM_TIMEOUT:
+			TRACE_DEBUG(FULL, "Timeout event received");
+			return ETIMEDOUT;
+			
+		case FDEVP_CNX_EP_CHANGE:
+			/* We ignore this event */
+			goto get_next;
+			
+		case FDEVP_CNX_ERROR:
+			TRACE_DEBUG(FULL, "Received ERROR event on the connection");
+			return ENOTCONN;
+	}
+	
+	TRACE_DEBUG(INFO, "Received unexpected event %d (%s)", ev, fd_pev_str(ev));
+	return EINVAL;
+}
+
+/* Where the events are sent */
+struct fifo * fd_cnx_target_queue(struct cnxctx * conn)
+{
+	struct fifo *q;
+	CHECK_POSIX_DO( pthread_mutex_lock(&state_lock), { ASSERT(0); } );
+	q = conn->cc_alt ?: conn->cc_incoming;
+	CHECK_POSIX_DO( pthread_mutex_unlock(&state_lock), { ASSERT(0); } );
+	return q;
+}
+
+/* Set an alternate FIFO list to send FDEVP_CNX_* events to */
+int fd_cnx_recv_setaltfifo(struct cnxctx * conn, struct fifo * alt_fifo)
+{
+	int ret;
+	TRACE_ENTRY( "%p %p", conn, alt_fifo );
+	CHECK_PARAMS( conn && alt_fifo && conn->cc_incoming );
+	
+	/* The magic function does it all */
+	CHECK_POSIX_DO( pthread_mutex_lock(&state_lock), { ASSERT(0); } );
+	CHECK_FCT_DO( ret = fd_fifo_move( conn->cc_incoming, alt_fifo, &conn->cc_alt ), );
+	CHECK_POSIX_DO( pthread_mutex_unlock(&state_lock), { ASSERT(0); } );
+	
+	return ret;
+}
+
+/* Send function when no multi-stream is involved, or sending on stream #0 (send() always use stream 0)*/
+static int send_simple(struct cnxctx * conn, unsigned char * buf, size_t len)
+{
+	ssize_t ret;
+	size_t sent = 0;
+	TRACE_ENTRY("%p %p %zd", conn, buf, len);
+	do {
+		if (fd_cnx_teststate(conn, CC_STATUS_TLS)) {
+			CHECK_GNUTLS_DO( ret = fd_tls_send_handle_error(conn, conn->cc_tls_para.session, buf + sent, len - sent),  );
+		} else {
+			struct iovec iov;
+			iov.iov_base = buf + sent;
+			iov.iov_len  = len - sent;
+			CHECK_SYS_DO( ret = fd_cnx_s_sendv(conn, &iov, 1), );
+		}
+		if (ret <= 0)
+			return ENOTCONN;
+		
+		sent += ret;
+	} while ( sent < len );
+	return 0;
+}
+
+/* Send a message -- this is synchronous -- and we assume it's never called by several threads at the same time (on the same conn), so we don't protect. */
+int fd_cnx_send(struct cnxctx * conn, unsigned char * buf, size_t len)
+{
+	TRACE_ENTRY("%p %p %zd", conn, buf, len);
+	
+	CHECK_PARAMS(conn && (conn->cc_socket > 0) && (! fd_cnx_teststate(conn, CC_STATUS_ERROR)) && buf && len);
+
+	TRACE_DEBUG(FULL, "Sending %zdb %sdata on connection %s", len, fd_cnx_teststate(conn, CC_STATUS_TLS) ? "TLS-protected ":"", conn->cc_id);
+	
+	switch (conn->cc_proto) {
+		case IPPROTO_TCP:
+			CHECK_FCT( send_simple(conn, buf, len) );
+			break;
+		
+#ifndef DISABLE_SCTP
+		case IPPROTO_SCTP: {
+			int dtls = fd_cnx_uses_dtls(conn);
+			if (!dtls) {
+				int stream = 0;
+				if (conn->cc_sctp_para.unordered) {
+					int limit;
+					if (fd_cnx_teststate(conn, CC_STATUS_TLS))
+						limit = conn->cc_sctp_para.pairs;
+					else
+						limit = conn->cc_sctp_para.str_out;
+					
+					if (limit > 1) {
+						conn->cc_sctp_para.next += 1;
+						conn->cc_sctp_para.next %= limit;
+						stream = conn->cc_sctp_para.next;
+					}
+				}
+				
+				if (stream == 0) {
+					/* We can use default function, it sends over stream #0 */
+					CHECK_FCT( send_simple(conn, buf, len) );
+				} else {
+					if (!fd_cnx_teststate(conn, CC_STATUS_TLS)) {
+						struct iovec iov;
+						iov.iov_base = buf;
+						iov.iov_len  = len;
+						
+						CHECK_SYS_DO( fd_sctp_sendstrv(conn, stream, &iov, 1), { fd_cnx_markerror(conn); return ENOTCONN; } );
+					} else {
+						/* push the data to the appropriate session */
+						ssize_t ret;
+						size_t sent = 0;
+						ASSERT(conn->cc_sctp3436_data.array != NULL);
+						do {
+							CHECK_GNUTLS_DO( ret = fd_tls_send_handle_error(conn, conn->cc_sctp3436_data.array[stream].session, buf + sent, len - sent), );
+							if (ret <= 0)
+								return ENOTCONN;
+
+							sent += ret;
+						} while ( sent < len );
+					}
+				}
+			} else {
+				/* DTLS */
+				/* Multistream is handled at lower layer in the push/pull function */
+				CHECK_FCT( send_simple(conn, buf, len) );
+			}
+		}
+		break;
+#endif /* DISABLE_SCTP */
+	
+		default:
+			TRACE_DEBUG(INFO, "Unknown protocol: %d", conn->cc_proto);
+			ASSERT(0);
+			return ENOTSUP;	/* or EINVAL... */
+	}
+	
+	return 0;
+}
+
+
+/**************************************/
+/*     Destruction of connection      */
+/**************************************/
+
+/* Destroy a conn structure, and shutdown the socket */
+void fd_cnx_destroy(struct cnxctx * conn)
+{
+	TRACE_ENTRY("%p", conn);
+	
+	CHECK_PARAMS_DO(conn, return);
+	
+	fd_cnx_addstate(conn, CC_STATUS_CLOSING);
+	
+	/* Initiate shutdown of the TLS session(s): call gnutls_bye(WR), then read until error */
+	if (fd_cnx_teststate(conn, CC_STATUS_TLS)) {
+#ifndef DISABLE_SCTP
+		int dtls = fd_cnx_uses_dtls(conn);
+		if ((!dtls) && (conn->cc_sctp_para.pairs > 1)) {
+			if (! fd_cnx_teststate(conn, CC_STATUS_ERROR )) {
+				/* Bye on master session */
+				CHECK_GNUTLS_DO( gnutls_bye(conn->cc_tls_para.session, GNUTLS_SHUT_WR), fd_cnx_markerror(conn) );
+			}
+
+			if (! fd_cnx_teststate(conn, CC_STATUS_ERROR ) ) {
+				/* and other stream pairs */
+				fd_sctp3436_bye(conn);
+			}
+
+			if (! fd_cnx_teststate(conn, CC_STATUS_ERROR ) ) {
+				/* Now wait for all decipher threads to terminate */
+				fd_sctp3436_waitthreadsterm(conn);
+			} else {
+				/* Abord the threads, the connection is dead already */
+				fd_sctp3436_stopthreads(conn);
+			}
+
+			/* Deinit gnutls resources */
+			fd_sctp3436_gnutls_deinit_others(conn);
+			if (conn->cc_tls_para.session) {
+				GNUTLS_TRACE( gnutls_deinit(conn->cc_tls_para.session) );
+				conn->cc_tls_para.session = NULL;
+			}
+			
+			/* Destroy the wrapper (also stops the demux thread) */
+			fd_sctp3436_destroy(conn);
+
+		} else {
+#endif /* DISABLE_SCTP */
+		/* We are TLS, but not using the sctp3436 wrapper layer */
+			if (! fd_cnx_teststate(conn, CC_STATUS_ERROR ) ) {
+				/* Master session */
+				CHECK_GNUTLS_DO( gnutls_bye(conn->cc_tls_para.session, GNUTLS_SHUT_WR), fd_cnx_markerror(conn) );
+			}
+
+			if (! fd_cnx_teststate(conn, CC_STATUS_ERROR ) ) {
+				/* In this case, just wait for thread rcvthr_tls_single to terminate */
+				if (conn->cc_rcvthr != (pthread_t)NULL) {
+					CHECK_POSIX_DO(  pthread_join(conn->cc_rcvthr, NULL), /* continue */  );
+					conn->cc_rcvthr = (pthread_t)NULL;
+				}
+			} else {
+				/* Cancel the receiver thread in case it did not already terminate */
+				CHECK_FCT_DO( fd_thr_term(&conn->cc_rcvthr), /* continue */ );
+			}
+			
+			/* Free the resources of the TLS session */
+			if (conn->cc_tls_para.session) {
+				GNUTLS_TRACE( gnutls_deinit(conn->cc_tls_para.session) );
+				conn->cc_tls_para.session = NULL;
+			}
+#ifndef DISABLE_SCTP
+		}
+#endif /* DISABLE_SCTP */
+	}
+	
+	/* Terminate the thread in case it is not done yet -- is there any such case left ?*/
+	CHECK_FCT_DO( fd_thr_term(&conn->cc_rcvthr), /* continue */ );
+		
+	/* Shut the connection down */
+	if (conn->cc_socket > 0) {
+		shutdown(conn->cc_socket, SHUT_RDWR);
+		close(conn->cc_socket);
+		conn->cc_socket = -1;
+	}
+	
+	/* Empty and destroy FIFO list */
+	if (conn->cc_incoming) {
+		fd_event_destroy( &conn->cc_incoming, free );
+	}
+	
+	/* Free the object */
+	free(conn);
+	
+	/* Done! */
+	return;
+}
diff --git a/libfdcore/cnxctx.h b/libfdcore/cnxctx.h
new file mode 100644
index 0000000..dcc4bea
--- /dev/null
+++ b/libfdcore/cnxctx.h
@@ -0,0 +1,153 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@freediameter.net>							 *
+*													 *
+* Copyright (c) 2013, 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.								 *
+*********************************************************************************************************/
+
+/* This file contains the definitions for internal use in the connection context files */
+
+#ifndef _CNXCTX_H
+#define _CNXCTX_H
+
+/* Maximum time we allow a connection to be blocked because of head-of-the-line buffers. After this delay, connection is considered in error. */
+#define MAX_HOTL_BLOCKING_TIME	1000	/* ms */
+
+/* The connection context structure */
+struct cnxctx {
+	char		cc_id[60];	/* The name of this connection. the first 5 chars are reserved for flags display (cc_state). */
+	char		cc_remid[60];	/* Id of remote peer */
+	
+	int 		cc_socket;	/* The socket object of the connection -- <=0 if no socket is created */
+
+	int 		cc_family;	/* AF_INET or AF_INET6 (mixed) */
+	int 		cc_proto;	/* IPPROTO_TCP or IPPROTO_SCTP */
+	
+	uint32_t	cc_state;	/* True if the object is being destroyed: we don't send events anymore. access with fd_cnx_getstate() */
+	#define 	CC_STATUS_CLOSING	1
+	#define 	CC_STATUS_ERROR		2
+	#define 	CC_STATUS_SIGNALED	4
+	#define 	CC_STATUS_TLS		8
+
+	pthread_t	cc_rcvthr;	/* thread for receiving messages on the connection */
+	int		cc_loop;	/* tell the thread if it loops or stops after the first message is received */
+	
+	struct fifo *	cc_incoming;	/* FIFO queue of events received on the connection, FDEVP_CNX_* */
+	struct fifo *	cc_alt;		/* alternate fifo to send FDEVP_CNX_* events to. */
+
+	/* If cc_tls == true */
+	struct {
+		DiamId_t 			 cn;		/* If not NULL, remote certif will be checked to match this Common Name */
+		int				 mode; 		/* GNUTLS_CLIENT / GNUTLS_SERVER */
+		int				 algo;		/* ALGO_HANDSHAKE_DEFAULT / ALGO_HANDSHAKE_3436 */
+		gnutls_session_t 		 session;	/* Session object (stream #0 in case of SCTP) */
+	}		cc_tls_para;
+
+	/* If cc_proto == SCTP */
+	struct	{
+		uint16_t str_out;	/* Out streams */
+		uint16_t str_in;	/* In streams */
+		uint16_t pairs;		/* max number of pairs ( = min(in, out)) */
+		uint16_t next;		/* # of stream the next message will be sent to */
+		int	 unordered;	/* boolean telling if use of streams > 0 is permitted */
+	} 		cc_sctp_para;
+
+	/* If both conditions */
+	struct {
+		struct sctp3436_ctx *array; /* an array of cc_sctp_para.pairs elements -- the #0 is special (session is outside)*/
+		struct sr_store	 *sess_store; /* Session data of the master session, to resume the children sessions */
+	} 		cc_sctp3436_data;
+};
+
+void fd_cnx_markerror(struct cnxctx * conn);
+uint32_t fd_cnx_getstate(struct cnxctx * conn);
+int  fd_cnx_teststate(struct cnxctx * conn, uint32_t flag);
+void fd_cnx_addstate(struct cnxctx * conn, uint32_t orstate);
+void fd_cnx_setstate(struct cnxctx * conn, uint32_t abstate);
+struct fifo * fd_cnx_target_queue(struct cnxctx * conn);
+
+
+/* Socket */
+ssize_t fd_cnx_s_recv(struct cnxctx * conn, void *buffer, size_t length);
+void fd_cnx_s_setto(int sock);
+
+/* TLS */
+int fd_tls_rcvthr_core(struct cnxctx * conn, gnutls_session_t session);
+int fd_tls_prepare(gnutls_session_t * session, int mode, int dtls, char * priority, void * alt_creds);
+#ifndef GNUTLS_VERSION_300
+int fd_tls_verify_credentials(gnutls_session_t session, struct cnxctx * conn, int verbose);
+#endif /* GNUTLS_VERSION_300 */
+
+/* TCP */
+int fd_tcp_create_bind_server( int * sock, sSA * sa, socklen_t salen );
+int fd_tcp_listen( int sock );
+int fd_tcp_client( int *sock, sSA * sa, socklen_t salen );
+int fd_tcp_get_local_ep(int sock, sSS * ss, socklen_t *sl);
+int fd_tcp_get_remote_ep(int sock, sSS * ss, socklen_t *sl);
+
+#ifndef DISABLE_SCTP
+/* SCTP */
+int fd_sctp_create_bind_server( int * sock, int family, struct fd_list * list, uint16_t port );
+int fd_sctp_listen( int sock );
+int fd_sctp_client( int *sock, int no_ip6, uint16_t port, struct fd_list * list );
+int fd_sctp_get_local_ep(int sock,  struct fd_list * list);
+int fd_sctp_get_remote_ep(int sock, struct fd_list * list);
+int fd_sctp_get_str_info( int sock, uint16_t *in, uint16_t *out, sSS *primary );
+ssize_t fd_sctp_sendstrv(struct cnxctx * conn, uint16_t strid, const struct iovec *iov, int iovcnt);
+int fd_sctp_recvmeta(struct cnxctx * conn, uint16_t * strid, uint8_t ** buf, size_t * len, int *event);
+
+/* TLS over SCTP (multi-stream) */
+struct sctp3436_ctx {
+	struct cnxctx 	*parent; 	/* for info such as socket, conn name, event list */
+	uint16_t	 strid;		/* Stream # of this session */
+	struct fifo	*raw_recv;	/* Raw data received on this stream, for demux */
+	struct {
+		uint8_t *buf;
+		size_t   bufsz;
+		size_t   offset;
+	} 		 partial;	/* If the pull function did not read the full content of first message in raw, it stores it here for next read call. */
+	pthread_t	 thr;		/* Thread to decrypt raw data in this pair of streams */
+	gnutls_session_t session;	/* TLS context using this pair of streams -- except if strid == 0, in that case session is outside the array */
+};
+
+int fd_sctp3436_init(struct cnxctx * conn);
+int fd_sctp3436_handshake_others(struct cnxctx * conn, char * priority, void * alt_creds);
+int fd_sctp3436_startthreads(struct cnxctx * conn, int others);
+void fd_sctp3436_bye(struct cnxctx * conn);
+void fd_sctp3436_waitthreadsterm(struct cnxctx * conn);
+void fd_sctp3436_gnutls_deinit_others(struct cnxctx * conn);
+void fd_sctp3436_stopthreads(struct cnxctx * conn);
+void fd_sctp3436_destroy(struct cnxctx * conn);
+
+#endif /* DISABLE_SCTP */
+
+#endif /* _CNXCTX_H */
+
diff --git a/libfdcore/config.c b/libfdcore/config.c
new file mode 100644
index 0000000..2a1a063
--- /dev/null
+++ b/libfdcore/config.c
@@ -0,0 +1,644 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@freediameter.net>							 *
+*													 *
+* Copyright (c) 2015, 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 "fdcore-internal.h"
+#include <sys/stat.h>
+
+/* Configuration management */
+
+#ifndef GNUTLS_DEFAULT_PRIORITY
+# define GNUTLS_DEFAULT_PRIORITY "NORMAL"
+#endif /* GNUTLS_DEFAULT_PRIORITY */
+#ifndef GNUTLS_DEFAULT_DHBITS
+# define GNUTLS_DEFAULT_DHBITS 1024
+#endif /* GNUTLS_DEFAULT_DHBITS */
+
+/* Initialize the fd_g_config structure to default values -- it should already have been initialized to all-0 */
+int fd_conf_init()
+{
+	TRACE_ENTRY();
+	
+	fd_g_config->cnf_eyec = EYEC_CONFIG;
+	
+	fd_g_config->cnf_timer_tc = 30;
+	fd_g_config->cnf_timer_tw = 30;
+	
+	fd_g_config->cnf_port     = DIAMETER_PORT;
+	fd_g_config->cnf_port_tls = DIAMETER_SECURE_PORT;
+	fd_g_config->cnf_sctp_str = 30;
+	fd_g_config->cnf_thr_srv  = 5;
+	fd_g_config->cnf_dispthr  = 4;
+	fd_list_init(&fd_g_config->cnf_endpoints, NULL);
+	fd_list_init(&fd_g_config->cnf_apps, NULL);
+	#ifdef DISABLE_SCTP
+	fd_g_config->cnf_flags.no_sctp = 1;
+	#endif /* DISABLE_SCTP */
+	
+	fd_g_config->cnf_orstateid = (uint32_t) time(NULL);
+	
+	CHECK_FCT( fd_dict_init(&fd_g_config->cnf_dict) );
+	CHECK_FCT( fd_fifo_new(&fd_g_config->cnf_main_ev, 0) );
+	
+	/* TLS parameters */
+	CHECK_GNUTLS_DO( gnutls_certificate_allocate_credentials (&fd_g_config->cnf_sec_data.credentials), return ENOMEM );
+	CHECK_GNUTLS_DO( gnutls_dh_params_init (&fd_g_config->cnf_sec_data.dh_cache), return ENOMEM );
+#ifdef GNUTLS_VERSION_300
+	CHECK_GNUTLS_DO( gnutls_x509_trust_list_init(&fd_g_config->cnf_sec_data.trustlist, 0), return ENOMEM );
+#endif /* GNUTLS_VERSION_300 */
+
+	return 0;
+}
+
+DECLARE_FD_DUMP_PROTOTYPE(fd_conf_dump)
+{
+	FD_DUMP_HANDLE_OFFSET();
+	
+	CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "freeDiameter configuration:\n"), 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);
+	CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "  Diameter Realm ......... : %s (l:%zi)\n", fd_g_config->cnf_diamrlm, fd_g_config->cnf_diamrlm_len), return NULL);
+	CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "  Tc Timer ............... : %u\n", fd_g_config->cnf_timer_tc), return NULL);
+	CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "  Tw Timer ............... : %u\n", fd_g_config->cnf_timer_tw), return NULL);
+	CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "  Local port ............. : %hu\n", fd_g_config->cnf_port), return NULL);
+	CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "  Local secure port ...... : %hu\n", fd_g_config->cnf_port_tls), return NULL);
+	if (fd_g_config->cnf_port_3436) {
+		CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "  Local SCTP TLS port .... : %hu\n", fd_g_config->cnf_port_3436), return NULL);
+	}
+	CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "  Number of SCTP streams . : %hu\n", fd_g_config->cnf_sctp_str), return NULL);
+	CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "  Number of clients thr .. : %d\n", fd_g_config->cnf_thr_srv), return NULL);
+	CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "  Number of app threads .. : %hu\n", fd_g_config->cnf_dispthr), return NULL);
+	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 ........ : "), 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)"), 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 ..... : "), 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,%s%s,Vnd:%u\t", 
+					app->appid,
+					app->flags.auth ? "Au" : "--",
+					app->flags.acct ? "Ac" : "--",
+					app->vndid), return NULL);
+			li = li->next;
+		}
+	}
+	
+	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);
+	#ifdef DISABLE_SCTP
+	CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "          - SCTP ......... : DISABLED (at compilation)\n"), return NULL);
+	#else /* DISABLE_SCTP */
+	CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "          - SCTP ......... : %s\n", fd_g_config->cnf_flags.no_sctp ? "DISABLED" : "Enabled"), return NULL);
+	#endif /* DISABLE_SCTP */
+	CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "          - Pref. proto .. : %s\n", fd_g_config->cnf_flags.pr_tcp ? "TCP" : "SCTP"), return NULL);
+	CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "          - TLS method ... : %s\n", fd_g_config->cnf_flags.tls_alg ? "INBAND" : "Separate port"), return NULL);
+	
+	CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "  TLS :   - Certificate .. : %s\n", fd_g_config->cnf_sec_data.cert_file ?: "(NONE)"), return NULL);
+	CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "          - Private key .. : %s\n", fd_g_config->cnf_sec_data.key_file ?: "(NONE)"), return NULL);
+	CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "          - CA (trust) ... : %s (%d certs)\n", fd_g_config->cnf_sec_data.ca_file ?: "(none)", fd_g_config->cnf_sec_data.ca_file_nr), return NULL);
+	CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "          - CRL .......... : %s\n", fd_g_config->cnf_sec_data.crl_file ?: "(none)"), return NULL);
+	CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "          - Priority ..... : %s\n", fd_g_config->cnf_sec_data.prio_string ?: "(default: '" GNUTLS_DEFAULT_PRIORITY "')"), return NULL);
+	if (fd_g_config->cnf_sec_data.dh_file) {
+		CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "          - DH file ...... : %s\n", fd_g_config->cnf_sec_data.dh_file), return NULL);
+	} else {
+		CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "          - DH bits ...... : %d\n", fd_g_config->cnf_sec_data.dh_bits ?: GNUTLS_DEFAULT_DHBITS), return NULL);
+	}
+	
+	CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "  Origin-State-Id ........ : %u", fd_g_config->cnf_orstateid), return NULL);
+	
+	return *buf;
+}
+
+/* read contents of a file opened in "rb" mode and alloc this data into a gnutls_datum_t (must be freed afterwards) */
+int fd_conf_stream_to_gnutls_datum(FILE * pemfile, gnutls_datum_t *out)
+{
+	size_t alloc = 0;
+	
+	CHECK_PARAMS( pemfile && out );
+	memset(out, 0, sizeof(gnutls_datum_t));
+
+	do {
+		uint8_t * realloced = NULL;
+		size_t read = 0;
+
+		if (alloc < out->size + BUFSIZ + 1) {
+			alloc += alloc / 2 + BUFSIZ + 1;
+			CHECK_MALLOC_DO( realloced = realloc(out->data, alloc),
+				{
+					free(out->data);
+					return ENOMEM;
+				} )
+			out->data = realloced;
+		}
+
+		read = fread( out->data + out->size, 1, alloc - out->size - 1, pemfile );
+		out->size += read;
+
+		if (ferror(pemfile)) {
+			int err = errno;
+			TRACE_DEBUG(INFO, "An error occurred while reading file: %s", strerror(err));
+			return err; 
+		}
+	} while (!feof(pemfile));
+	
+	out->data[out->size] = '\0';
+	return 0;
+}
+
+#ifdef GNUTLS_VERSION_300
+/* inspired from GnuTLS manual */
+static int fd_conf_print_details_func (gnutls_x509_crt_t cert,
+                    gnutls_x509_crt_t issuer, gnutls_x509_crl_t crl,
+                    unsigned int verification_output)
+{
+  char name[512];
+  char issuer_name[512];
+  size_t name_size;
+  size_t issuer_name_size;
+  
+  if (!TRACE_BOOL(GNUTLS_DBG_LEVEL))
+	  return 0;
+
+  issuer_name_size = sizeof (issuer_name);
+  gnutls_x509_crt_get_issuer_dn (cert, issuer_name, &issuer_name_size);
+
+  name_size = sizeof (name);
+  gnutls_x509_crt_get_dn (cert, name, &name_size);
+
+  fd_log_debug("\tSubject: %s", name);
+  fd_log_debug("\tIssuer: %s", issuer_name);
+
+  if (issuer != NULL)
+    {
+      issuer_name_size = sizeof (issuer_name);
+      gnutls_x509_crt_get_dn (issuer, issuer_name, &issuer_name_size);
+
+      fd_log_debug("\tVerified against: %s", issuer_name);
+    }
+
+  if (crl != NULL)
+    {
+      issuer_name_size = sizeof (issuer_name);
+      gnutls_x509_crl_get_issuer_dn (crl, issuer_name, &issuer_name_size);
+
+      fd_log_debug("\tVerified against CRL of: %s", issuer_name);
+    }
+
+  fd_log_debug("\tVerification output: %x", verification_output);
+
+  return 0;
+}
+#endif /* GNUTLS_VERSION_300 */
+
+#ifndef GNUTLS_VERSION_300
+GCC_DIAG_OFF("-Wdeprecated-declarations")
+#endif /* !GNUTLS_VERSION_300 */
+/* Parse the configuration file (using the yacc parser) */
+int fd_conf_parse()
+{
+	extern FILE * fddin;
+	const char * orig = NULL;
+	
+	/* Attempt to find the configuration file */
+	if (!fd_g_config->cnf_file)
+		fd_g_config->cnf_file = FD_DEFAULT_CONF_FILENAME;
+	
+	fddin = fopen(fd_g_config->cnf_file, "r");
+	if ((fddin == NULL) && (*fd_g_config->cnf_file != '/')) {
+		char * new_cnf = NULL;
+		/* We got a relative path, attempt to add the default directory prefix */
+		orig = fd_g_config->cnf_file;
+		CHECK_MALLOC( new_cnf = malloc(strlen(orig) + strlen(DEFAULT_CONF_PATH) + 2) ); /* we will not free it, but not important */
+		sprintf( new_cnf, DEFAULT_CONF_PATH "/%s", orig );
+		fd_g_config->cnf_file = new_cnf;
+		fddin = fopen(fd_g_config->cnf_file, "r");
+	}
+	if (fddin == NULL) {
+		int ret = errno;
+		LOG_F("Unable to open configuration file for reading; tried the following locations: %s%s%s; Error: %s",
+				  orig ?: "", orig? " and " : "", fd_g_config->cnf_file, strerror(ret));
+		return ret;
+	}
+	
+	/* call yacc parser */
+	TRACE_DEBUG (FULL, "Parsing configuration file: %s", fd_g_config->cnf_file);
+	CHECK_FCT(  fddparse(fd_g_config)  );
+	
+	/* close the file */
+	fclose(fddin);
+	
+	/* Check that TLS private key was given */
+	if (! fd_g_config->cnf_sec_data.key_file) {
+		/* If TLS is not enabled, we allow empty TLS configuration */
+		if ((fd_g_config->cnf_port_tls == 0) && (fd_g_config->cnf_flags.tls_alg == 0)) {
+			LOG_N("TLS is disabled, this is *NOT* a recommended practice! Diameter protocol conveys highly sensitive information on your users.");
+			fd_g_config->cnf_sec_data.tls_disabled = 1;
+		} else {
+			LOG_F( "Missing private key configuration for TLS. Please provide the TLS_cred configuration directive.");
+			return EINVAL;
+		}
+	}
+	
+	/* Resolve hostname if not provided */
+	if (fd_g_config->cnf_diamid == NULL) {
+		char buf[HOST_NAME_MAX + 1];
+		struct addrinfo hints, *info;
+		int ret;
+		
+		/* local host name */
+		CHECK_SYS(gethostname(buf, sizeof(buf)));
+		
+		/* get FQDN */
+		memset(&hints, 0, sizeof hints);
+		hints.ai_flags = AI_CANONNAME;
+
+		ret = getaddrinfo(buf, NULL, &hints, &info);
+		if (ret != 0) {
+			TRACE_ERROR( "Error resolving local FQDN : '%s' : %s"
+					". Please provide Identity in configuration file.",
+					buf, gai_strerror(ret));
+			return EINVAL;
+		}
+		fd_g_config->cnf_diamid = info->ai_canonname;
+		CHECK_FCT( fd_os_validate_DiameterIdentity(&fd_g_config->cnf_diamid, &fd_g_config->cnf_diamid_len, 1) );
+		freeaddrinfo(info);
+	} else {
+		CHECK_FCT( fd_os_validate_DiameterIdentity(&fd_g_config->cnf_diamid, &fd_g_config->cnf_diamid_len, 0) );
+	}
+	
+	/* Handle the realm part */
+	if (fd_g_config->cnf_diamrlm == NULL) {
+		char * start = NULL;
+		
+		/* Check the diameter identity is a fqdn */
+		start = strchr(fd_g_config->cnf_diamid, '.');
+		if ((start == NULL) || (start[1] == '\0')) {
+			TRACE_ERROR( "Unable to extract realm from the Identity '%s'."
+					" Please fix your Identity setting or provide Realm.",
+					fd_g_config->cnf_diamid);
+			return EINVAL;
+		}
+		
+		fd_g_config->cnf_diamrlm = start + 1;
+		CHECK_FCT( fd_os_validate_DiameterIdentity(&fd_g_config->cnf_diamrlm, &fd_g_config->cnf_diamrlm_len, 1) );
+	} else {
+		CHECK_FCT( fd_os_validate_DiameterIdentity(&fd_g_config->cnf_diamrlm, &fd_g_config->cnf_diamrlm_len, 0) );
+	}
+	
+	/* Validate some flags */
+	if (fd_g_config->cnf_flags.no_ip4 && fd_g_config->cnf_flags.no_ip6) {
+		TRACE_ERROR( "IP and IPv6 cannot be disabled at the same time.");
+		return EINVAL;
+	}
+	if (fd_g_config->cnf_flags.no_tcp && fd_g_config->cnf_flags.no_sctp) {
+		TRACE_ERROR( "TCP and SCTP cannot be disabled at the same time.");
+		return EINVAL;
+	}
+	
+	/* Validate local endpoints */
+	if ((!FD_IS_LIST_EMPTY(&fd_g_config->cnf_endpoints)) && (fd_g_config->cnf_flags.no_ip4 || fd_g_config->cnf_flags.no_ip6)) {
+		struct fd_list * li;
+		for ( li = fd_g_config->cnf_endpoints.next; li != &fd_g_config->cnf_endpoints; li = li->next) {
+			struct fd_endpoint * ep = (struct fd_endpoint *)li;
+			if ( (fd_g_config->cnf_flags.no_ip4 && (ep->sa.sa_family == AF_INET))
+			   ||(fd_g_config->cnf_flags.no_ip6 && (ep->sa.sa_family == AF_INET6)) ) {
+				char sa_buf[sSA_DUMP_STRLEN];;
+				li = li->prev;
+				fd_list_unlink(&ep->chain);
+				fd_sa_sdump_numeric(sa_buf, &ep->sa);
+				LOG_N("Info: Removing local address conflicting with the flags no_IP / no_IP6 : %s", sa_buf);
+				free(ep);
+			}
+		}
+	}
+	
+	/* Configure TLS default parameters */
+	if ((!fd_g_config->cnf_sec_data.tls_disabled) && (!fd_g_config->cnf_sec_data.prio_string)) {
+		const char * err_pos = NULL;
+		CHECK_GNUTLS_DO( gnutls_priority_init( 
+					&fd_g_config->cnf_sec_data.prio_cache,
+					GNUTLS_DEFAULT_PRIORITY,
+					&err_pos),
+				 { TRACE_ERROR("Error in priority string at position : %s", err_pos); return EINVAL; } );
+	}
+	
+	/* Verify that our certificate is valid -- otherwise remote peers will reject it */
+	if (!fd_g_config->cnf_sec_data.tls_disabled) {
+		int ret = 0, i;
+		
+		gnutls_datum_t certfile;
+		
+		gnutls_x509_crt_t * certs = NULL;
+		unsigned int cert_max = 0;
+		
+		
+		/* Read the certificate file */
+		FILE *stream = fopen (fd_g_config->cnf_sec_data.cert_file, "rb");
+		if (!stream) {
+			int err = errno;
+			TRACE_DEBUG(INFO, "An error occurred while opening '%s': %s", fd_g_config->cnf_sec_data.cert_file, strerror(err));
+			return err; 
+		}
+		CHECK_FCT( fd_conf_stream_to_gnutls_datum(stream, &certfile) );
+		fclose(stream);
+		
+		/* Import the certificate(s) */
+		GNUTLS_TRACE( ret = gnutls_x509_crt_list_import(NULL, &cert_max, &certfile, GNUTLS_X509_FMT_PEM, GNUTLS_X509_CRT_LIST_IMPORT_FAIL_IF_EXCEED) );
+		if (ret != GNUTLS_E_SHORT_MEMORY_BUFFER) {
+			CHECK_GNUTLS_DO(ret, return EINVAL);
+		}
+		
+		CHECK_MALLOC( certs = calloc(cert_max, sizeof(gnutls_x509_crt_t)) );
+		CHECK_GNUTLS_DO( gnutls_x509_crt_list_import(certs, &cert_max, &certfile, GNUTLS_X509_FMT_PEM, 
+				#ifdef GNUTLS_VERSION_300
+				GNUTLS_X509_CRT_LIST_FAIL_IF_UNSORTED
+				#else /* GNUTLS_VERSION_300 */
+				0
+				#endif /* GNUTLS_VERSION_300 */
+				),
+			{
+				TRACE_ERROR("Failed to import the data from file '%s'", fd_g_config->cnf_sec_data.cert_file);
+				free(certfile.data);
+				return EINVAL;
+			} );
+		free(certfile.data);
+		
+		ASSERT(cert_max >= 1);
+		
+		/* Now, verify the list against the local CA and CRL */
+		
+		#ifdef GNUTLS_VERSION_300
+		
+			/* We use the trust list for this purpose */
+		{
+			unsigned int output;
+			
+			gnutls_x509_trust_list_verify_named_crt (
+						fd_g_config->cnf_sec_data.trustlist, 
+						certs[0], 
+						fd_g_config->cnf_diamid, 
+						fd_g_config->cnf_diamid_len,
+                                		0,
+                                		&output, 
+						fd_conf_print_details_func);
+
+			/* if this certificate is not explicitly trusted verify against CAs 
+			*/
+			if (output != 0)
+			{
+				gnutls_x509_trust_list_verify_crt (
+							fd_g_config->cnf_sec_data.trustlist, 
+							certs, 
+							cert_max,
+                                			0,
+                                			&output, 
+							fd_conf_print_details_func);
+			}
+			
+			if (output & GNUTLS_CERT_INVALID)
+			{
+				fd_log_debug("TLS: Local certificate chain '%s' is invalid :", fd_g_config->cnf_sec_data.cert_file);
+				if (output & GNUTLS_CERT_SIGNER_NOT_FOUND)
+					TRACE_ERROR(" - The certificate hasn't got a known issuer. Did you forget to specify TLS_CA ?");
+				if (output & GNUTLS_CERT_SIGNER_NOT_CA)
+					TRACE_ERROR(" - The certificate signer is not a CA, or uses version 1, or 3 without basic constraints.");
+				if (output & GNUTLS_CERT_NOT_ACTIVATED)
+					TRACE_ERROR(" - The certificate is not yet activated.");
+				if (output & GNUTLS_CERT_EXPIRED)
+					TRACE_ERROR(" - The certificate is expired.");
+				return EINVAL;
+			}
+			
+			/* Now check the subject matches our hostname */
+			if (!gnutls_x509_crt_check_hostname (certs[0], fd_g_config->cnf_diamid))
+			{
+				TRACE_ERROR("TLS: The certificate owner does not match the hostname '%s'", fd_g_config->cnf_diamid);
+				return EINVAL;
+			}
+			
+		}
+		
+
+		#else /* GNUTLS_VERSION_300 */ 
+		
+			/* GnuTLS 2.x way of checking certificates */
+		{
+			gnutls_x509_crt_t * CA_list;
+			int CA_list_length;
+
+			gnutls_x509_crl_t * CRL_list;
+			int CRL_list_length;
+			
+			unsigned int verify;
+			time_t now;
+			GNUTLS_TRACE( gnutls_certificate_get_x509_cas (fd_g_config->cnf_sec_data.credentials, &CA_list, (unsigned int *) &CA_list_length) );
+			GNUTLS_TRACE( gnutls_certificate_get_x509_crls (fd_g_config->cnf_sec_data.credentials, &CRL_list, (unsigned int *) &CRL_list_length) );
+			CHECK_GNUTLS_DO( gnutls_x509_crt_list_verify(certs, cert_max, CA_list, CA_list_length, CRL_list, CRL_list_length, 0, &verify),
+				{
+					TRACE_ERROR("Failed to verify the local certificate '%s' against local credentials. Please check your certificate is valid.", fd_g_config->cnf_sec_data.cert_file);
+					return EINVAL;
+				} );
+				
+			if (verify) {
+				fd_log_debug("TLS: Local certificate chain '%s' is invalid :", fd_g_config->cnf_sec_data.cert_file);
+				if (verify & GNUTLS_CERT_INVALID)
+					TRACE_ERROR(" - The certificate is not trusted (unknown CA? expired?)");
+				if (verify & GNUTLS_CERT_REVOKED)
+					TRACE_ERROR(" - The certificate has been revoked.");
+				if (verify & GNUTLS_CERT_SIGNER_NOT_FOUND)
+					TRACE_ERROR(" - The certificate hasn't got a known issuer.");
+				if (verify & GNUTLS_CERT_SIGNER_NOT_CA)
+					TRACE_ERROR(" - The certificate signer is not a CA, or uses version 1, or 3 without basic constraints.");
+				if (verify & GNUTLS_CERT_INSECURE_ALGORITHM)
+					TRACE_ERROR(" - The certificate signature uses a weak algorithm.");
+				return EINVAL;
+			}
+
+			/* Check the local Identity is valid with the certificate */
+			if (!gnutls_x509_crt_check_hostname (certs[0], fd_g_config->cnf_diamid)) {
+				TRACE_ERROR("TLS: Local certificate '%s' is invalid :", fd_g_config->cnf_sec_data.cert_file);
+				TRACE_ERROR(" - The certificate hostname does not match '%s'", fd_g_config->cnf_diamid);
+				return EINVAL;
+			}
+
+			/* Check validity of all the certificates in the chain */
+			now = time(NULL);
+			for (i = 0; i < cert_max; i++)
+			{
+				time_t deadline;
+
+				GNUTLS_TRACE( deadline = gnutls_x509_crt_get_expiration_time(certs[i]) );
+				if ((deadline != (time_t)-1) && (deadline < now)) {
+					TRACE_ERROR("TLS: Local certificate chain '%s' is invalid :", fd_g_config->cnf_sec_data.cert_file);
+					TRACE_ERROR(" - The certificate %d in the chain is expired", i);
+					return EINVAL;
+				}
+
+				GNUTLS_TRACE( deadline = gnutls_x509_crt_get_activation_time(certs[i]) );
+				if ((deadline != (time_t)-1) && (deadline > now)) {
+					TRACE_ERROR("TLS: Local certificate chain '%s' is invalid :", fd_g_config->cnf_sec_data.cert_file);
+					TRACE_ERROR(" - The certificate %d in the chain is not yet activated", i);
+					return EINVAL;
+				}
+			}
+		}
+		#endif /* GNUTLS_VERSION_300 */ 
+		
+		/* Everything checked OK, free the certificate list */
+		for (i = 0; i < cert_max; i++)
+		{
+			GNUTLS_TRACE( gnutls_x509_crt_deinit (certs[i]) );
+		}
+		free(certs);
+	
+		#ifdef GNUTLS_VERSION_300
+		/* Use certificate verification during the handshake */
+		gnutls_certificate_set_verify_function (fd_g_config->cnf_sec_data.credentials, fd_tls_verify_credentials_2);
+		#endif /* GNUTLS_VERSION_300 */
+
+	}
+	
+	/* gnutls_certificate_set_verify_limits -- so far the default values are fine... */
+	
+	/* DH */
+	if (!fd_g_config->cnf_sec_data.tls_disabled) {
+		if (fd_g_config->cnf_sec_data.dh_file) {
+			gnutls_datum_t dhparams = { NULL, 0 };
+			size_t alloc = 0;
+			FILE *stream = fopen (fd_g_config->cnf_sec_data.dh_file, "rb");
+			if (!stream) {
+				int err = errno;
+				TRACE_DEBUG(INFO, "An error occurred while opening '%s': %s", fd_g_config->cnf_sec_data.dh_file, strerror(err));
+				return err; 
+			}
+			do {
+				uint8_t * realloced = NULL;
+				size_t read = 0;
+
+				if (alloc < dhparams.size + BUFSIZ + 1) {
+					alloc += alloc / 2 + BUFSIZ + 1;
+					CHECK_MALLOC_DO( realloced = realloc(dhparams.data, alloc),
+						{
+							free(dhparams.data);
+							return ENOMEM;
+						} )
+					dhparams.data = realloced;
+				}
+
+				read = fread( dhparams.data + dhparams.size, 1, alloc - dhparams.size - 1, stream );
+				dhparams.size += read;
+
+				if (ferror(stream)) {
+					int err = errno;
+					TRACE_DEBUG(INFO, "An error occurred while reading '%s': %s", fd_g_config->cnf_sec_data.dh_file, strerror(err));
+					return err; 
+				}
+			} while (!feof(stream));
+			dhparams.data[dhparams.size] = '\0';
+			fclose(stream);
+			CHECK_GNUTLS_DO( gnutls_dh_params_import_pkcs3( 
+						fd_g_config->cnf_sec_data.dh_cache,
+						&dhparams,
+						GNUTLS_X509_FMT_PEM),
+						 { TRACE_ERROR("Error in DH bits value : %d", fd_g_config->cnf_sec_data.dh_bits ?: GNUTLS_DEFAULT_DHBITS); return EINVAL; } );
+			free(dhparams.data);
+
+		} else {
+			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),
+						 { TRACE_ERROR("Error in DH bits value : %d", fd_g_config->cnf_sec_data.dh_bits ?: GNUTLS_DEFAULT_DHBITS); return EINVAL; } );
+		}			
+	
+	}
+	
+	return 0;
+}
+#ifndef GNUTLS_VERSION_300
+GCC_DIAG_ON("-Wdeprecated-declarations")
+#endif /* !GNUTLS_VERSION_300 */
+
+
+/* Destroy contents of fd_g_config structure */
+int fd_conf_deinit()
+{
+	TRACE_ENTRY();
+	
+	if (!fd_g_config)
+		return 0;
+	
+	/* Free the TLS parameters */
+#ifdef GNUTLS_VERSION_300
+	gnutls_x509_trust_list_deinit(fd_g_config->cnf_sec_data.trustlist, 1);
+#endif /* GNUTLS_VERSION_300 */
+	gnutls_priority_deinit(fd_g_config->cnf_sec_data.prio_cache);
+	gnutls_dh_params_deinit(fd_g_config->cnf_sec_data.dh_cache);
+	gnutls_certificate_free_credentials(fd_g_config->cnf_sec_data.credentials);
+	
+	free(fd_g_config->cnf_sec_data.cert_file); fd_g_config->cnf_sec_data.cert_file = NULL;
+	free(fd_g_config->cnf_sec_data.key_file); fd_g_config->cnf_sec_data.key_file = NULL;
+	free(fd_g_config->cnf_sec_data.ca_file); fd_g_config->cnf_sec_data.ca_file = NULL;
+	free(fd_g_config->cnf_sec_data.crl_file); fd_g_config->cnf_sec_data.crl_file = NULL;
+	free(fd_g_config->cnf_sec_data.prio_string); fd_g_config->cnf_sec_data.prio_string = NULL;
+	free(fd_g_config->cnf_sec_data.dh_file); fd_g_config->cnf_sec_data.dh_file = NULL;
+	
+	/* Destroy dictionary */
+	CHECK_FCT_DO( fd_dict_fini(&fd_g_config->cnf_dict), );
+	
+	/* Destroy the main event queue */
+	CHECK_FCT_DO( fd_fifo_del(&fd_g_config->cnf_main_ev), );
+	
+	/* Destroy the local endpoints and applications */
+	CHECK_FCT_DO(fd_ep_filter(&fd_g_config->cnf_endpoints, 0 ), );
+	CHECK_FCT_DO(fd_app_empty(&fd_g_config->cnf_apps ), );
+	
+	/* Destroy the local identity */	
+	free(fd_g_config->cnf_diamid); fd_g_config->cnf_diamid = NULL;
+	free(fd_g_config->cnf_diamrlm); fd_g_config->cnf_diamrlm = NULL;
+	
+	return 0;
+}
+
+
diff --git a/libfdcore/core.c b/libfdcore/core.c
new file mode 100644
index 0000000..23909b7
--- /dev/null
+++ b/libfdcore/core.c
@@ -0,0 +1,373 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@freediameter.net>							 *
+*													 *
+* Copyright (c) 2013, 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 "fdcore-internal.h"
+
+GCC_DIAG_OFF("-Wdeprecated-declarations")
+#include <gcrypt.h>
+GCC_DIAG_ON("-Wdeprecated-declarations")
+
+/* The static configuration structure */
+static struct fd_config g_conf;
+struct fd_config * fd_g_config = NULL;
+
+/* gcrypt functions to support posix threads */
+#ifndef GNUTLS_VERSION_210
+GCRY_THREAD_OPTION_PTHREAD_IMPL;
+#endif /* GNUTLS_VERSION_210 */
+
+/* Thread that process incoming events on the main queue -- and terminates the framework when requested */
+static pthread_t core_runner = (pthread_t)NULL;
+
+/* Signal extensions when the framework is completely initialized (they are waiting in fd_core_waitstartcomplete()) */
+static enum core_state {
+	CORE_NOT_INIT,	/* initial state */
+	CORE_LIBS_INIT,	/* fd_core_initialize was called */
+	CORE_CONF_READY,/* Configuration was parsed, extensions are loaded */
+	CORE_RUNNING,	/* Servers and clients are started, core_runner thread is running */
+	CORE_SHUTDOWN,	/* The framework is terminating all objects */
+	CORE_TERM	/* Shutdown complete. */	
+} core_state = CORE_NOT_INIT;
+static pthread_mutex_t core_mtx = PTHREAD_MUTEX_INITIALIZER;
+static pthread_cond_t  core_cnd = PTHREAD_COND_INITIALIZER;
+
+static enum core_state core_state_get(void)
+{
+	enum core_state cur_state;
+	CHECK_POSIX_DO( pthread_mutex_lock( &core_mtx ), );
+	cur_state = core_state;
+	CHECK_POSIX_DO( pthread_mutex_unlock( &core_mtx ), );
+	return cur_state;
+}
+
+static void core_state_set(enum core_state newstate)
+{
+	CHECK_POSIX_DO( pthread_mutex_lock( &core_mtx ), );
+	LOG_D("Core state: %d -> %d", core_state, newstate);
+	core_state = newstate;
+	CHECK_POSIX_DO( pthread_cond_broadcast( &core_cnd ), );
+	CHECK_POSIX_DO( pthread_mutex_unlock( &core_mtx ), );
+}
+
+static int core_state_wait(enum core_state waitstate)
+{
+	int ret = 0;
+	CHECK_POSIX( pthread_mutex_lock( &core_mtx ));
+	pthread_cleanup_push( fd_cleanup_mutex, &core_mtx );
+	while (waitstate > core_state) {
+		CHECK_POSIX_DO(ret = pthread_cond_wait(&core_cnd, &core_mtx), break);
+	}
+	pthread_cleanup_pop( 0 );
+	CHECK_POSIX( pthread_mutex_unlock( &core_mtx ));
+	return ret;
+}
+
+static void core_shutdown(void)
+{
+	LOG_N( FD_PROJECT_BINARY " framework is stopping...");
+	fd_log_threadname("fD Core Shutdown");
+	
+	/* cleanups */
+	CHECK_FCT_DO( fd_servers_stop(), /* Stop accepting new connections */ );
+	CHECK_FCT_DO( fd_rtdisp_cleanstop(), /* Stop dispatch thread(s) after a clean loop if possible */ );
+	CHECK_FCT_DO( fd_peer_fini(), /* Stop all connections */ );
+	CHECK_FCT_DO( fd_rtdisp_fini(), /* Stop routing threads and destroy routing queues */ );
+	
+	CHECK_FCT_DO( fd_ext_term(), /* Cleanup all extensions */ );
+	CHECK_FCT_DO( fd_rtdisp_cleanup(), /* destroy remaining handlers */ );
+	
+	GNUTLS_TRACE( gnutls_global_deinit() );
+	
+	CHECK_FCT_DO( fd_conf_deinit(), );
+	
+	CHECK_FCT_DO( fd_event_trig_fini(), );
+	
+	fd_log_debug(FD_PROJECT_BINARY " framework is terminated.");
+	
+	fd_libproto_fini();
+	
+}	
+
+
+static void * core_runner_thread(void * arg) 
+{
+	fd_log_threadname("fD Core Runner");
+	
+	core_state_wait(CORE_RUNNING);
+	
+	/* Handle events incoming on the main event queue */
+	while (1) {
+		int code; size_t sz; void * data;
+		CHECK_FCT_DO(  fd_event_get(fd_g_config->cnf_main_ev, &code, &sz, &data),  break  );
+		switch (code) {
+			case FDEV_TRIGGER:
+				{
+					int tv, *p;
+					CHECK_PARAMS_DO( sz == sizeof(int), 
+						{
+							TRACE_DEBUG(NONE, "Internal error: got FDEV_TRIGGER without trigger value!");
+							ASSERT(0);
+							goto end;
+						} );
+					p = data;
+					tv = *p;
+					free(p);
+					CHECK_FCT_DO( fd_event_trig_call_cb(tv), goto end );
+				}
+				break;
+			
+			case FDEV_TERMINATE_INT:
+				goto end;
+			
+			default:
+				TRACE_DEBUG(INFO, "Unexpected event in the main event queue (%d), ignored.", code);
+		}
+	}
+	
+end:
+	core_shutdown();
+	
+	return NULL;
+}
+
+/*********************************/
+/* Public interfaces */
+
+/* Initialize the libfdcore internals. This also initializes libfdproto */
+int fd_core_initialize(void)
+{
+	int ret;
+	
+	if (core_state_get() != CORE_NOT_INIT) {
+		fprintf(stderr, "fd_core_initialize() called more than once!\n");
+		return EINVAL;
+	}
+	
+	/* Initialize the library -- must come first since it initializes the debug facility */
+	ret = fd_libproto_init();
+	if (ret != 0) {
+		fprintf(stderr, "Unable to initialize libfdproto: %s\n", strerror(ret));
+		return ret;
+	}
+	
+	/* Name this thread */
+	fd_log_threadname("Main");
+	
+	LOG_N("libfdproto '%s' initialized.", fd_libproto_version);
+	
+	/* Initialize gcrypt and gnutls */
+	#ifndef GNUTLS_VERSION_210
+	GNUTLS_TRACE( (void) gcry_control (GCRYCTL_SET_THREAD_CBS, &gcry_threads_pthread) );
+	GNUTLS_TRACE( (void) gcry_control (GCRYCTL_ENABLE_QUICK_RANDOM, 0) );
+	#endif /* GNUTLS_VERSION_210 */
+	CHECK_GNUTLS_DO( gnutls_global_init(), return EINVAL );
+	if ( ! gnutls_check_version(GNUTLS_VERSION) ) {
+		TRACE_ERROR( "The GNUTLS library is too old; found '%s', need '" GNUTLS_VERSION "'", gnutls_check_version(NULL));
+		return EINVAL;
+	} else {
+	#ifdef GNUTLS_VERSION_210
+		TRACE_DEBUG(INFO, "libgnutls '%s' initialized.", gnutls_check_version(NULL) );
+	#else /* GNUTLS_VERSION_210 */
+		TRACE_DEBUG(INFO, "libgnutls '%s', libgcrypt '%s', initialized.", gnutls_check_version(NULL), gcry_check_version(NULL) );
+	#endif /* GNUTLS_VERSION_210 */
+	}
+	
+	/* Initialize the config with default values */
+	memset(&g_conf, 0, sizeof(struct fd_config));
+	fd_g_config = &g_conf;
+	CHECK_FCT( fd_conf_init() );
+	
+	/* Add definitions of the base protocol */
+	CHECK_FCT( fd_dict_base_protocol(fd_g_config->cnf_dict) );
+	
+	/* Initialize some modules */
+	CHECK_FCT( fd_hooks_init()  );
+	CHECK_FCT( fd_queues_init() );
+	CHECK_FCT( fd_sess_start()  );
+	CHECK_FCT( fd_p_expi_init() );
+	
+	core_state_set(CORE_LIBS_INIT);
+	
+	LOG_N("libfdcore '%s' initialized.", fd_core_version);
+	
+	
+	/* Next thing is to parse the config, leave this for a different function */
+	return 0;
+}
+
+static pthread_mutex_t core_lock = PTHREAD_MUTEX_INITIALIZER;
+
+/* Parse the freeDiameter.conf configuration file, load the extensions */
+static int fd_core_parseconf_int(const char * conffile)
+{
+	char * buf = NULL, *b;
+	size_t len = 0, offset=0;
+	
+	
+	TRACE_ENTRY("%p", conffile);
+	
+	/* Conf file */
+	if (conffile)
+		fd_g_config->cnf_file = conffile; /* otherwise, we use the default name */
+	
+	CHECK_FCT( fd_conf_parse() );
+	
+	/* The following module use data from the configuration */
+	CHECK_FCT( fd_rtdisp_init() );
+	
+	/* Now, load all dynamic extensions */
+	CHECK_FCT(  fd_ext_load()  );
+	
+	/* Display configuration */
+	b = fd_conf_dump(&buf, &len, NULL);
+	LOG_SPLIT(FD_LOG_NOTICE, NULL, b ?: "<Error during configuration dump...>", NULL);
+	
+	/* Display extensions status */
+	b = fd_ext_dump(&buf, &len, NULL);
+	LOG_SPLIT(FD_LOG_NOTICE, "Loaded extensions: ", b?:"<Error during extensions dump...>", NULL);
+	
+	/* Display registered triggers for FDEV_TRIGGER */
+	b = fd_event_trig_dump(&buf, &len, &offset);
+	if (!b || offset) {
+		LOG_N("%s", b ?: "Error during triggers dump...");
+	}
+	
+	free(buf);	
+		
+	/* Since some extensions might have modified the definitions from the dict_base_protocol, we only load the objects now */
+	CHECK_FCT( fd_msg_init()    );
+	
+	/* Ok, ready for next step */
+	core_state_set(CORE_CONF_READY);
+	
+	return 0;
+}
+
+int fd_core_parseconf(const char * conffile)
+{
+	int ret;
+	CHECK_POSIX( pthread_mutex_lock(&core_lock) );
+	ret = fd_core_parseconf_int(conffile);
+	CHECK_POSIX( pthread_mutex_unlock(&core_lock) );
+	return ret;
+}
+
+
+/* For threads that would need to wait complete start of the framework (ex: in extensions) */
+int fd_core_waitstartcomplete(void)
+{
+	TRACE_ENTRY("");
+	
+	return core_state_wait(CORE_RUNNING);
+}
+
+/* Start the server & client threads */
+static int fd_core_start_int(void)
+{
+	/* Start server threads */ 
+	CHECK_FCT( fd_servers_start() );
+	
+	/* Start the peer state machines */
+	CHECK_FCT( fd_psm_start() );
+	
+	/* Start the core runner thread that handles main events (until shutdown) */
+	CHECK_POSIX( pthread_create(&core_runner, NULL, core_runner_thread, NULL) );
+	
+	/* Unlock threads waiting into fd_core_waitstartcomplete */
+	core_state_set(CORE_RUNNING);
+	
+	/* Ok, everything is running now... */
+	return 0;
+}
+
+int fd_core_start(void)
+{
+	int ret;
+	CHECK_POSIX( pthread_mutex_lock(&core_lock) );
+	ret = fd_core_start_int();
+	CHECK_POSIX( pthread_mutex_unlock(&core_lock) );
+	return ret;
+}
+
+/* Initialize shutdown of the framework. This is not blocking. */
+int fd_core_shutdown(void)
+{
+	enum core_state cur_state = core_state_get();
+	
+	LOG_F("Initiating freeDiameter shutdown sequence (%d)", cur_state);
+		
+	if (cur_state < CORE_RUNNING) {
+		/* Calling application must make sure the initialization is not ongoing in a separate thread... */
+		if (pthread_mutex_lock(&core_lock) != 0) {
+			/* This function must not be called asynchronously from fd_core_parseconf / fd_core_start ! Please review your main app design */
+			ASSERT(0);
+			return EINVAL;
+		}
+		core_shutdown();
+		core_state_set(CORE_TERM);
+		pthread_mutex_unlock(&core_lock);
+	} else if (cur_state == CORE_RUNNING) {
+		core_state_set(CORE_SHUTDOWN);
+		CHECK_FCT( fd_event_send(fd_g_config->cnf_main_ev, FDEV_TERMINATE_INT, 0, NULL) );
+	}
+	
+	/* Other case, the framework is already shutting down */
+	
+	return 0;
+}
+
+
+/* Wait for the shutdown to be complete -- this must be called after fd_core_shutdown to reclaim some resources. */
+int fd_core_wait_shutdown_complete(void)
+{
+	enum core_state cur_state = core_state_get();
+	void * th_ret = NULL;
+	
+	CHECK_FCT(core_state_wait(CORE_SHUTDOWN));
+	
+	if (cur_state == CORE_TERM)
+		return 0;
+	
+	/* Just wait for core_runner_thread to complete and return gracefully */
+	CHECK_POSIX(pthread_join(core_runner, &th_ret));
+
+	core_state_set(CORE_TERM);
+		
+	return 0;
+}
+
+
+
+
diff --git a/libfdcore/dict_base_proto.c b/libfdcore/dict_base_proto.c
new file mode 100644
index 0000000..5c023f6
--- /dev/null
+++ b/libfdcore/dict_base_proto.c
@@ -0,0 +1,3347 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@freediameter.net>							 *
+*													 *
+* Copyright (c) 2013, 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.								 *
+*********************************************************************************************************/
+
+/* Diameter Base protocol definitions.
+ */
+
+#include "fdcore-internal.h"
+
+#include <netinet/in.h>
+#include <sys/socket.h>
+
+/* The pointer for the global dictionary (initialized from main) */
+struct dictionary * fd_g_dict = NULL;
+
+
+
+#define CHECK_dict_new( _type, _data, _parent, _ref )				\
+	CHECK_FCT(  fd_dict_new( dict, (_type), (_data), (_parent), (_ref))  );
+
+#define CHECK_dict_search( _type, _criteria, _what, _result )					\
+	CHECK_FCT(  fd_dict_search( dict, (_type), (_criteria), (_what), (_result), ENOENT)  );
+
+struct local_rules_definition {
+	char 			*avp_name;
+	enum rule_position	position;
+	int 			min;
+	int			max;
+};
+
+#define RULE_ORDER( _position ) ((((_position) == RULE_FIXED_HEAD) || ((_position) == RULE_FIXED_TAIL)) ? 1 : 0 )
+
+#define PARSE_loc_rules( _rulearray, _parent) {						\
+	int __ar;									\
+	for (__ar=0; __ar < sizeof(_rulearray) / sizeof((_rulearray)[0]); __ar++) {	\
+		struct dict_rule_data __data = { NULL, 					\
+			(_rulearray)[__ar].position,					\
+			0, 								\
+			(_rulearray)[__ar].min,						\
+			(_rulearray)[__ar].max};					\
+		__data.rule_order = RULE_ORDER(__data.rule_position);			\
+		CHECK_FCT(  fd_dict_search( 						\
+			dict,								\
+			DICT_AVP, 							\
+			AVP_BY_NAME, 							\
+			(_rulearray)[__ar].avp_name, 					\
+			&__data.rule_avp, 0 ) );					\
+		if ( !__data.rule_avp ) {						\
+			TRACE_DEBUG(INFO, "AVP Not found: '%s'", (_rulearray)[__ar].avp_name );	\
+			return ENOENT;							\
+		}									\
+		CHECK_FCT_DO( fd_dict_new( dict, DICT_RULE, &__data, _parent, NULL),	\
+			{								\
+				TRACE_DEBUG(INFO, "Error on rule with AVP '%s'",	\
+					 (_rulearray)[__ar].avp_name );			\
+				return EINVAL;						\
+			} );								\
+	}										\
+}
+
+int fd_dict_base_protocol(struct dictionary * dict)
+{
+	TRACE_ENTRY("%p", dict);
+	CHECK_PARAMS(dict);
+	
+	/* Vendors section */
+	{
+		/* The base RFC has no vendor information */
+		;
+	}
+	
+	/* Applications section */
+	{
+		/* base accounting application */
+		{
+			struct dict_application_data data = {          3, "Diameter Base Accounting" 	};
+			CHECK_dict_new( DICT_APPLICATION, &data, NULL, NULL);
+		}
+		
+		/* relay application */
+		{
+			struct dict_application_data data  = { 0xffffffff, "Relay" 				};
+			#if AI_RELAY != 0xffffffff
+			#error "AI_RELAY definition mismatch"
+			#endif
+			CHECK_dict_new( DICT_APPLICATION, &data , NULL, NULL);
+		}
+	}
+	
+	/* Derived AVP types section */
+	{
+		/* Address */
+		{
+			/*
+				The Address format is derived from the OctetString AVP Base
+				Format.  It is a discriminated union, representing, for example a
+				32-bit (IPv4) [RFC791] or 128-bit (IPv6) [RFC4291] address, most
+				significant octet first.  The first two octets of the Address AVP
+				represents the AddressType, which contains an Address Family
+				defined in [IANAADFAM].  The AddressType is used to discriminate
+				the content and format of the remaining octets.
+			*/
+			struct dict_type_data data = { AVP_TYPE_OCTETSTRING,	"Address"		, fd_dictfct_Address_interpret	, fd_dictfct_Address_encode,	fd_dictfct_Address_dump	};
+			CHECK_dict_new( DICT_TYPE, &data , NULL, NULL);
+		}
+		
+		/* Time */
+		{
+			/*
+				The Time format is derived from the OctetString AVP Base Format.
+				The string MUST contain four octets, in the same format as the
+				first four bytes are in the NTP timestamp format.  The NTP
+				Timestamp format is defined in chapter 3 of [RFC4330].
+
+				This represents the number of seconds since 0h on 1 January 1900
+				with respect to the Coordinated Universal Time (UTC).
+
+				On 6h 28m 16s UTC, 7 February 2036 the time value will overflow.
+				SNTP [RFC4330] describes a procedure to extend the time to 2104.
+				This procedure MUST be supported by all DIAMETER nodes.
+			*/
+			struct dict_type_data data = { AVP_TYPE_OCTETSTRING,	"Time"			, fd_dictfct_Time_interpret	, fd_dictfct_Time_encode, 	fd_dictfct_Time_dump		};
+			CHECK_dict_new( DICT_TYPE, &data , NULL, NULL);
+		}
+		
+		/* UTF8String */
+		{
+			/*
+				The UTF8String format is derived from the OctetString AVP Base
+				Format.  This is a human readable string represented using the
+				ISO/IEC IS 10646-1 character set, encoded as an OctetString using
+				the UTF-8 [RFC3629] transformation format described in RFC 3629.
+
+				Since additional code points are added by amendments to the 10646
+				standard from time to time, implementations MUST be prepared to
+				encounter any code point from 0x00000001 to 0x7fffffff.  Byte
+				sequences that do not correspond to the valid encoding of a code
+				point into UTF-8 charset or are outside this range are prohibited.
+
+				The use of control codes SHOULD be avoided.  When it is necessary
+				to represent a new line, the control code sequence CR LF SHOULD be
+				used.
+
+				The use of leading or trailing white space SHOULD be avoided.
+
+				For code points not directly supported by user interface hardware
+				or software, an alternative means of entry and display, such as
+				hexadecimal, MAY be provided.
+
+				For information encoded in 7-bit US-ASCII, the UTF-8 charset is
+				identical to the US-ASCII charset.
+
+				UTF-8 may require multiple bytes to represent a single character /
+				code point; thus the length of an UTF8String in octets may be
+				different from the number of characters encoded.
+
+				Note that the AVP Length field of an UTF8String is measured in
+				octets, not characters.
+			*/
+			struct dict_type_data data = { AVP_TYPE_OCTETSTRING,	"UTF8String"		, NULL			, NULL	, fd_dictfct_UTF8String_dump	};
+			CHECK_dict_new( DICT_TYPE, &data , NULL, NULL);
+		}
+		
+		/* DiameterIdentity */
+		{
+			/*
+				The DiameterIdentity format is derived from the OctetString AVP
+				Base Format.
+
+                				DiameterIdentity  = FQDN
+
+
+				DiameterIdentity value is used to uniquely identify a Diameter
+				node for purposes of duplicate connection and routing loop
+				detection.
+
+				The contents of the string MUST be the FQDN of the Diameter node.
+				If multiple Diameter nodes run on the same host, each Diameter
+				node MUST be assigned a unique DiameterIdentity.  If a Diameter
+
+				node can be identified by several FQDNs, a single FQDN should be
+				picked at startup, and used as the only DiameterIdentity for that
+				node, whatever the connection it is sent on.  Note that in this
+				document, DiameterIdentity is in ASCII form in order to be
+				compatible with existing DNS infrastructure.  See Appendix D for
+				interactions between the Diameter protocol and Internationalized
+				Domain Name (IDNs).
+			*/
+			struct dict_type_data data = { AVP_TYPE_OCTETSTRING,	"DiameterIdentity"	, NULL			, NULL		, fd_dictfct_UTF8String_dump	};
+			CHECK_dict_new( DICT_TYPE, &data , NULL, NULL);
+		}
+		
+		/* DiameterURI */
+		{
+			/*
+				The DiameterURI MUST follow the Uniform Resource Identifiers (URI)
+				syntax [RFC3986] rules specified below:
+
+				 "aaa://" FQDN [ port ] [ transport ] [ protocol ]
+
+                				 ; No transport security
+
+				 "aaas://" FQDN [ port ] [ transport ] [ protocol ]
+
+                				 ; Transport security used
+
+				 FQDN               = Fully Qualified Host Name
+
+				 port               = ":" 1*DIGIT
+
+                				 ; One of the ports used to listen for
+                				 ; incoming connections.
+                				 ; If absent,
+                				 ; the default Diameter port (3868) is
+                				 ; assumed.
+
+				 transport          = ";transport=" transport-protocol
+
+                				 ; One of the transports used to listen
+                				 ; for incoming connections.  If absent,
+                				 ; the default SCTP [RFC2960] protocol is
+                				 ; assumed. UDP MUST NOT be used when
+                				 ; the aaa-protocol field is set to
+                				 ; diameter.
+
+				transport-protocol = ( "tcp" / "sctp" / "udp" )
+
+				protocol           = ";protocol=" aaa-protocol
+
+                				 ; If absent, the default AAA protocol
+                				 ; is diameter.
+
+				aaa-protocol       = ( "diameter" / "radius" / "tacacs+" )
+
+				The following are examples of valid Diameter host identities:
+
+				aaa://host.example.com;transport=tcp
+				aaa://host.example.com:6666;transport=tcp
+				aaa://host.example.com;protocol=diameter
+				aaa://host.example.com:6666;protocol=diameter
+				aaa://host.example.com:6666;transport=tcp;protocol=diameter
+				aaa://host.example.com:1813;transport=udp;protocol=radius
+			*/
+			struct dict_type_data data = { AVP_TYPE_OCTETSTRING,	"DiameterURI"		, NULL			, NULL		, fd_dictfct_UTF8String_dump	};
+			CHECK_dict_new( DICT_TYPE, &data , NULL, NULL);
+		}
+		
+		/* Enumerated */
+		{
+			/*
+				Enumerated is derived from the Integer32 AVP Base Format.  The
+				definition contains a list of valid values and their
+				interpretation and is described in the Diameter application
+				introducing the AVP.
+			*/
+			
+			/* We don't use a generic "Enumerated" type in freeDiameter. Instead, we define
+			 * types of the form "Enumerated(<avpname>)" where <avpname> is replaced 
+			 * by the name of the AVP to which the type applies.
+			 *  Example: Enumerated(Disconnect-Cause)
+			 */
+			;
+		}
+		
+		/* IPFilterRule */
+		{
+			/*
+				The IPFilterRule format is derived from the OctetString AVP Base
+				Format and uses the ASCII charset.  The rule syntax is a modified
+				subset of ipfw(8) from FreeBSD.  Packets may be filtered based on
+				the following information that is associated with it:
+
+				    Direction                          (in or out)
+				    Source and destination IP address  (possibly masked)
+				    Protocol
+				    Source and destination port        (lists or ranges)
+				    TCP flags
+				    IP fragment flag
+				    IP options
+				    ICMP types
+
+				Rules for the appropriate direction are evaluated in order, with
+				the first matched rule terminating the evaluation.  Each packet is
+				evaluated once.  If no rule matches, the packet is dropped if the
+				last rule evaluated was a permit, and passed if the last rule was
+				a deny.
+
+				IPFilterRule filters MUST follow the format:
+				
+				    action dir proto from src to dst [options]
+				
+			(...skipped loooong explanation...)
+				
+				There is one kind of packet that the access device MUST always
+				discard, that is an IP fragment with a fragment offset of one.
+				This is a valid packet, but it only has one use, to try to
+				circumvent firewalls.
+
+				An access device that is unable to interpret or apply a deny rule
+				MUST terminate the session.  An access device that is unable to
+				interpret or apply a permit rule MAY apply a more restrictive
+				rule.  An access device MAY apply deny rules of its own before the
+				supplied rules, for example to protect the access device owner's
+				infrastructure.
+			*/
+			struct dict_type_data data = { AVP_TYPE_OCTETSTRING,	"IPFilterRule"		, NULL			, NULL		, fd_dictfct_UTF8String_dump	};
+			CHECK_dict_new( DICT_TYPE, &data , NULL, NULL);
+		}
+	}
+	
+	/* AVP section */
+	{
+		struct dict_object * Address_type;
+		struct dict_object * UTF8String_type;
+		struct dict_object * DiameterIdentity_type;
+		struct dict_object * DiameterURI_type;
+		struct dict_object * Time_type;
+		
+		CHECK_dict_search( DICT_TYPE, TYPE_BY_NAME, "Address", &Address_type);
+		CHECK_dict_search( DICT_TYPE, TYPE_BY_NAME, "UTF8String", &UTF8String_type);
+		CHECK_dict_search( DICT_TYPE, TYPE_BY_NAME, "DiameterIdentity", &DiameterIdentity_type);
+		CHECK_dict_search( DICT_TYPE, TYPE_BY_NAME, "DiameterURI", &DiameterURI_type);
+		CHECK_dict_search( DICT_TYPE, TYPE_BY_NAME, "Time", &Time_type);
+		
+		/* Vendor-Id */
+		{
+			/*
+				The Vendor-Id AVP (AVP Code 266) is of type Unsigned32 and contains
+				the IANA "SMI Network Management Private Enterprise Codes" [RFC3232]
+				value assigned to the vendor of the Diameter device.  It is
+				envisioned that the combination of the Vendor-Id, Product-Name
+				(Section 5.3.7) and the Firmware-Revision (Section 5.3.4) AVPs may
+				provide useful debugging information.
+
+				A Vendor-Id value of zero in the CER or CEA messages is reserved and
+				indicates that this field is ignored.
+			*/
+			struct dict_avp_data data = { 
+					266, 					/* Code */
+					#if AC_VENDOR_ID != 266
+					#error "AC_VENDOR_ID definition mismatch"
+					#endif
+					0, 					/* Vendor */
+					"Vendor-Id", 				/* Name */
+					AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, 	/* Fixed flags */
+					AVP_FLAG_MANDATORY, 			/* Fixed flag values */
+					AVP_TYPE_UNSIGNED32 			/* base type of data */
+					};
+			CHECK_dict_new( DICT_AVP, &data, NULL, NULL);
+		}
+		
+		/* Firmware-Revision */
+		{
+			/*
+				The Firmware-Revision AVP (AVP Code 267) is of type Unsigned32 and is
+				used to inform a Diameter peer of the firmware revision of the
+				issuing device.
+
+				For devices that do not have a firmware revision (general purpose
+				computers running Diameter software modules, for instance), the
+				revision of the Diameter software module may be reported instead.
+			*/
+			struct dict_avp_data data = { 
+					267, 					/* Code */
+					#if AC_FIRMWARE_REVISION != 267
+					#error "AC_FIRMWARE_REVISION definition mismatch"
+					#endif
+					0, 					/* Vendor */
+					"Firmware-Revision", 			/* Name */
+					AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, 	/* Fixed flags */
+					0,		 			/* Fixed flag values */
+					AVP_TYPE_UNSIGNED32 			/* base type of data */
+					};
+			CHECK_dict_new( DICT_AVP, &data , NULL, NULL);
+		}
+		
+		/* Host-IP-Address */
+		{
+			/*
+				The Host-IP-Address AVP (AVP Code 257) is of type Address and is used
+				to inform a Diameter peer of the sender's IP address.  All source
+				addresses that a Diameter node expects to use with SCTP [RFC2960]
+				MUST be advertised in the CER and CEA messages by including a
+				Host-IP- Address AVP for each address.  This AVP MUST ONLY be used in
+				the CER and CEA messages.
+			*/
+			struct dict_avp_data data = { 
+					257, 					/* Code */
+					#if AC_HOST_IP_ADDRESS != 257
+					#error "AC_HOST_IP_ADDRESS definition mismatch"
+					#endif
+					0, 					/* Vendor */
+					"Host-IP-Address", 			/* Name */
+					AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, 	/* Fixed flags */
+					AVP_FLAG_MANDATORY,			/* Fixed flag values */
+					AVP_TYPE_OCTETSTRING 			/* base type of data */
+					};
+			CHECK_dict_new( DICT_AVP, &data , Address_type, NULL);
+		}
+		
+		/* Supported-Vendor-Id */
+		{
+			/*
+				The Supported-Vendor-Id AVP (AVP Code 265) is of type Unsigned32 and
+				contains the IANA "SMI Network Management Private Enterprise Codes"
+				[RFC3232] value assigned to a vendor other than the device vendor but
+				including the application vendor.  This is used in the CER and CEA
+				messages in order to inform the peer that the sender supports (a
+				subset of) the vendor-specific AVPs defined by the vendor identified
+				in this AVP.  The value of this AVP SHOULD NOT be set to zero.
+				Multiple instances of this AVP containing the same value SHOULD NOT
+				be sent.
+			*/
+			struct dict_avp_data data = { 
+					265, 					/* Code */
+					#if AC_SUPPORTED_VENDOR_ID != 265
+					#error "AC_SUPPORTED_VENDOR_ID definition mismatch"
+					#endif
+					0, 					/* Vendor */
+					"Supported-Vendor-Id", 			/* Name */
+					AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, 	/* Fixed flags */
+					AVP_FLAG_MANDATORY,			/* Fixed flag values */
+					AVP_TYPE_UNSIGNED32 			/* base type of data */
+					};
+			CHECK_dict_new( DICT_AVP, &data , NULL, NULL);
+		}
+		
+		/* Product-Name */
+		{
+			/*
+				The Product-Name AVP (AVP Code 269) is of type UTF8String, and
+				contains the vendor assigned name for the product.  The Product-Name
+				AVP SHOULD remain constant across firmware revisions for the same
+				product.
+			*/
+			struct dict_avp_data data = { 
+					269, 					/* Code */
+					#if AC_PRODUCT_NAME != 269
+					#error "AC_PRODUCT_NAME definition mismatch"
+					#endif
+					0, 					/* Vendor */
+					"Product-Name", 			/* Name */
+					AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, 	/* Fixed flags */
+					0,					/* Fixed flag values */
+					AVP_TYPE_OCTETSTRING 			/* base type of data */
+					};
+			CHECK_dict_new( DICT_AVP, &data , UTF8String_type, NULL);
+		}
+		
+		/* Disconnect-Cause */
+		{
+			/*
+				The Disconnect-Cause AVP (AVP Code 273) is of type Enumerated.  A
+				Diameter node MUST include this AVP in the Disconnect-Peer-Request
+				message to inform the peer of the reason for its intention to
+				shutdown the transport connection.  The following values are
+				supported:
+
+				REBOOTING                         0
+				A scheduled reboot is imminent. Receiver of DPR with above result
+				code MAY attempt reconnection.
+
+				BUSY                              1
+				The peer's internal resources are constrained, and it has
+				determined that the transport connection needs to be closed.
+				Receiver of DPR with above result code SHOULD NOT attempt
+				reconnection.
+
+				DO_NOT_WANT_TO_TALK_TO_YOU        2
+				The peer has determined that it does not see a need for the
+				transport connection to exist, since it does not expect any
+				messages to be exchanged in the near future. Receiver of DPR
+				with above result code SHOULD NOT attempt reconnection.
+			*/
+			struct dict_object 	* 	type;
+			struct dict_type_data 		tdata = { AVP_TYPE_INTEGER32,	"Enumerated(Disconnect-Cause)"	, NULL, NULL, NULL };
+			struct dict_enumval_data 	t_0 = { "REBOOTING", 			{ .i32 = 0 }};
+			struct dict_enumval_data 	t_1 = { "BUSY", 			{ .i32 = 1 }};
+			struct dict_enumval_data 	t_2 = { "DO_NOT_WANT_TO_TALK_TO_YOU", 	{ .i32 = 2 }};
+			struct dict_avp_data 		data = { 
+					273, 					/* Code */
+					#if AC_DISCONNECT_CAUSE != 273
+					#error "AC_DISCONNECT_CAUSE definition mismatch"
+					#endif
+					0, 					/* Vendor */
+					"Disconnect-Cause", 			/* Name */
+					AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, 	/* Fixed flags */
+					AVP_FLAG_MANDATORY,			/* Fixed flag values */
+					AVP_TYPE_INTEGER32 			/* base type of data */
+					};
+			/* Create the Enumerated type, and then the AVP */
+			CHECK_dict_new( DICT_TYPE, &tdata , NULL, &type);
+			CHECK_dict_new( DICT_ENUMVAL, &t_0 , type, NULL);
+			CHECK_dict_new( DICT_ENUMVAL, &t_1 , type, NULL);
+			CHECK_dict_new( DICT_ENUMVAL, &t_2 , type, NULL);
+			CHECK_dict_new( DICT_AVP, &data , type, NULL);
+		}
+		
+		/* Origin-Host */
+		{
+			/*
+				The Origin-Host AVP (AVP Code 264) is of type DiameterIdentity, and
+				MUST be present in all Diameter messages.  This AVP identifies the
+				endpoint that originated the Diameter message.  Relay agents MUST NOT
+				modify this AVP.
+
+				The value of the Origin-Host AVP is guaranteed to be unique within a
+				single host.
+
+				Note that the Origin-Host AVP may resolve to more than one address as
+				the Diameter peer may support more than one address.
+
+				This AVP SHOULD be placed as close to the Diameter header as
+				possible.
+			*/
+			struct dict_avp_data data = { 
+					264, 					/* Code */
+					#if AC_ORIGIN_HOST != 264
+					#error "AC_ORIGIN_HOST definition mismatch"
+					#endif
+					0, 					/* Vendor */
+					"Origin-Host", 				/* Name */
+					AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, 	/* Fixed flags */
+					AVP_FLAG_MANDATORY,			/* Fixed flag values */
+					AVP_TYPE_OCTETSTRING 			/* base type of data */
+					};
+			CHECK_dict_new( DICT_AVP, &data , DiameterIdentity_type, NULL);
+		}
+		
+		/* Origin-Realm */
+		{
+			/*
+				The Origin-Realm AVP (AVP Code 296) is of type DiameterIdentity.
+				This AVP contains the Realm of the originator of any Diameter message
+				and MUST be present in all messages.
+
+				This AVP SHOULD be placed as close to the Diameter header as
+				possible.
+			*/
+			struct dict_avp_data data = { 
+					296, 					/* Code */
+					#if AC_ORIGIN_REALM != 296
+					#error "AC_ORIGIN_REALM definition mismatch"
+					#endif
+					0, 					/* Vendor */
+					"Origin-Realm", 			/* Name */
+					AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, 	/* Fixed flags */
+					AVP_FLAG_MANDATORY,			/* Fixed flag values */
+					AVP_TYPE_OCTETSTRING 			/* base type of data */
+					};
+			CHECK_dict_new( DICT_AVP, &data , DiameterIdentity_type, NULL);
+		}
+		
+		/* Destination-Host */
+		{
+			/*
+				The Destination-Host AVP (AVP Code 293) is of type DiameterIdentity.
+				This AVP MUST be present in all unsolicited agent initiated messages,
+				MAY be present in request messages, and MUST NOT be present in Answer
+				messages.
+
+				The absence of the Destination-Host AVP will cause a message to be
+				sent to any Diameter server supporting the application within the
+				realm specified in Destination-Realm AVP.
+
+				This AVP SHOULD be placed as close to the Diameter header as
+				possible.
+			*/
+			struct dict_avp_data data = { 
+					293, 					/* Code */
+					#if AC_DESTINATION_HOST != 293
+					#error "AC_DESTINATION_HOST definition mismatch"
+					#endif
+					0, 					/* Vendor */
+					"Destination-Host", 			/* Name */
+					AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, 	/* Fixed flags */
+					AVP_FLAG_MANDATORY,			/* Fixed flag values */
+					AVP_TYPE_OCTETSTRING 			/* base type of data */
+					};
+			CHECK_dict_new( DICT_AVP, &data , DiameterIdentity_type, NULL);
+		}
+		
+		/* Destination-Realm */
+		{
+			/*
+				The Destination-Realm AVP (AVP Code 283) is of type DiameterIdentity,
+				and contains the realm the message is to be routed to.  The
+				Destination-Realm AVP MUST NOT be present in Answer messages.
+				Diameter Clients insert the realm portion of the User-Name AVP.
+				Diameter servers initiating a request message use the value of the
+				Origin-Realm AVP from a previous message received from the intended
+				target host (unless it is known a priori).  When present, the
+				Destination-Realm AVP is used to perform message routing decisions.
+
+				Request messages whose ABNF does not list the Destination-Realm AVP
+				as a mandatory AVP are inherently non-routable messages.
+
+				This AVP SHOULD be placed as close to the Diameter header as
+				possible.
+			*/
+			struct dict_avp_data data = { 
+					283, 					/* Code */
+					#if AC_DESTINATION_REALM != 283
+					#error "AC_DESTINATION_REALM definition mismatch"
+					#endif
+					0, 					/* Vendor */
+					"Destination-Realm", 			/* Name */
+					AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, 	/* Fixed flags */
+					AVP_FLAG_MANDATORY,			/* Fixed flag values */
+					AVP_TYPE_OCTETSTRING 			/* base type of data */
+					};
+			CHECK_dict_new( DICT_AVP, &data , DiameterIdentity_type, NULL);
+		}
+		
+		/* Route-Record */
+		{
+			/*
+				The Route-Record AVP (AVP Code 282) is of type DiameterIdentity.  The
+				identity added in this AVP MUST be the same as the one received in
+				the Origin-Host of the Capabilities Exchange message.
+			*/
+			struct dict_avp_data data = { 
+					282, 					/* Code */
+					#if AC_ROUTE_RECORD != 282
+					#error "AC_ROUTE_RECORD definition mismatch"
+					#endif
+					0, 					/* Vendor */
+					"Route-Record", 			/* Name */
+					AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, 	/* Fixed flags */
+					AVP_FLAG_MANDATORY,			/* Fixed flag values */
+					AVP_TYPE_OCTETSTRING 			/* base type of data */
+					};
+			CHECK_dict_new( DICT_AVP, &data , DiameterIdentity_type, NULL);
+		}
+		
+		/* Proxy-Host */
+		{
+			/*
+				The Proxy-Host AVP (AVP Code 280) is of type DiameterIdentity.  This
+				AVP contains the identity of the host that added the Proxy-Info AVP.
+			*/
+			struct dict_avp_data adata = { 
+					280, 					/* Code */
+					#if AC_PROXY_HOST != 280
+					#error "AC_PROXY_HOST definition mismatch"
+					#endif
+					0, 					/* Vendor */
+					"Proxy-Host", 				/* Name */
+					AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, 	/* Fixed flags */
+					AVP_FLAG_MANDATORY,			/* Fixed flag values */
+					AVP_TYPE_OCTETSTRING 			/* base type of data */
+					};
+			CHECK_dict_new( DICT_AVP, &adata , DiameterIdentity_type, NULL);
+		}
+		
+		/* Proxy-State */
+		{
+			/*
+				The Proxy-State AVP (AVP Code 33) is of type OctetString, and
+				contains state local information, and MUST be treated as opaque data.
+			*/
+			struct dict_avp_data adata = { 
+					33, 					/* Code */
+					#if AC_PROXY_STATE != 33
+					#error "AC_PROXY_STATE definition mismatch"
+					#endif
+					0, 					/* Vendor */
+					"Proxy-State", 				/* Name */
+					AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, 	/* Fixed flags */
+					AVP_FLAG_MANDATORY,			/* Fixed flag values */
+					AVP_TYPE_OCTETSTRING 			/* base type of data */
+					};
+			CHECK_dict_new( DICT_AVP, &adata , NULL, NULL);
+		}
+			
+		/* Proxy-Info */
+		{
+			/*
+				The Proxy-Info AVP (AVP Code 284) is of type Grouped.  The Grouped
+				Data field has the following ABNF grammar:
+
+				 Proxy-Info ::= < AVP Header: 284 >
+                				{ Proxy-Host }
+                				{ Proxy-State }
+        				      * [ AVP ]
+			*/
+			struct dict_object * avp;
+			struct dict_avp_data data = { 
+					284, 					/* Code */
+					#if AC_PROXY_INFO != 284
+					#error "AC_PROXY_INFO definition mismatch"
+					#endif
+					0, 					/* Vendor */
+					"Proxy-Info", 				/* Name */
+					AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, 	/* Fixed flags */
+					AVP_FLAG_MANDATORY,			/* Fixed flag values */
+					AVP_TYPE_GROUPED 			/* base type of data */
+					};
+			struct local_rules_definition rules[] = 
+						{ 	 {  "Proxy-Host", 			RULE_REQUIRED, -1, 1 }
+							,{  "Proxy-State",			RULE_REQUIRED, -1, 1 }
+						};
+			
+			CHECK_dict_new( DICT_AVP, &data , NULL, &avp);
+			PARSE_loc_rules( rules, avp );
+		}
+		
+		/* Auth-Application-Id */
+		{
+			/*
+				The Auth-Application-Id AVP (AVP Code 258) is of type Unsigned32 and
+				is used in order to advertise support of the Authentication and
+				Authorization portion of an application (see Section 2.4).  If
+				present in a message other than CER and CEA, the value of the Auth-
+				Application-Id AVP MUST match the Application Id present in the
+				Diameter message header.
+			*/
+			struct dict_avp_data data = { 
+					258, 					/* Code */
+					#if AC_AUTH_APPLICATION_ID != 258
+					#error "AC_AUTH_APPLICATION_ID definition mismatch"
+					#endif
+					0, 					/* Vendor */
+					"Auth-Application-Id", 			/* Name */
+					AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, 	/* Fixed flags */
+					AVP_FLAG_MANDATORY,			/* Fixed flag values */
+					AVP_TYPE_UNSIGNED32 			/* base type of data */
+					};
+			CHECK_dict_new( DICT_AVP, &data , NULL, NULL);
+		}
+		
+		/* Acct-Application-Id */
+		{
+			/*
+				The Acct-Application-Id AVP (AVP Code 259) is of type Unsigned32 and
+				is used in order to advertise support of the Accounting portion of an
+				application (see Section 2.4).  If present in a message other than
+				CER and CEA, the value of the Acct-Application-Id AVP MUST match the
+				Application Id present in the Diameter message header.
+			*/
+			struct dict_avp_data data = { 
+					259, 					/* Code */
+					#if AC_ACCT_APPLICATION_ID != 259
+					#error "AC_ACCT_APPLICATION_ID definition mismatch"
+					#endif
+					0, 					/* Vendor */
+					"Acct-Application-Id", 			/* Name */
+					AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, 	/* Fixed flags */
+					AVP_FLAG_MANDATORY,			/* Fixed flag values */
+					AVP_TYPE_UNSIGNED32 			/* base type of data */
+					};
+			CHECK_dict_new( DICT_AVP, &data , NULL, NULL);
+		}
+		
+		/* Inband-Security-Id */
+		{
+			/*
+				The Inband-Security-Id AVP (AVP Code 299) is of type Unsigned32 and
+				is used in order to advertise support of the Security portion of the
+				application.
+
+				Currently, the following values are supported, but there is ample
+				room to add new security Ids.
+
+
+				NO_INBAND_SECURITY 0
+
+				This peer does not support TLS.  This is the default value, if the
+				AVP is omitted.
+
+				TLS 1
+
+				This node supports TLS security, as defined by [RFC4346].
+			*/
+			
+			/* Although the RFC does not specify an "Enumerated" type here, we go forward and create one.
+			 * This is the reason for the "*" in the type name
+			 */
+			
+			struct dict_object 	* 	type;
+			struct dict_type_data	 	tdata = { AVP_TYPE_UNSIGNED32,	"Enumerated(Inband-Security-Id)"	, NULL, NULL, NULL };
+			struct dict_enumval_data 	t_0 = { "NO_INBAND_SECURITY", 		{ .u32 = ACV_ISI_NO_INBAND_SECURITY }};
+			struct dict_enumval_data 	t_1 = { "TLS", 			{ .u32 = ACV_ISI_TLS }};
+			struct dict_avp_data 		data = { 
+					299, 					/* Code */
+					#if AC_INBAND_SECURITY_ID != 299
+					#error "AC_INBAND_SECURITY_ID definition mismatch"
+					#endif
+					0, 					/* Vendor */
+					"Inband-Security-Id", 			/* Name */
+					AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, 	/* Fixed flags */
+					AVP_FLAG_MANDATORY,			/* Fixed flag values */
+					AVP_TYPE_UNSIGNED32 			/* base type of data */
+					};
+			/* Create the Enumerated type, and then the AVP */
+			CHECK_dict_new( DICT_TYPE, &tdata , NULL, &type);
+			CHECK_dict_new( DICT_ENUMVAL, &t_0 , type, NULL);
+			CHECK_dict_new( DICT_ENUMVAL, &t_1 , type, NULL);
+			CHECK_dict_new( DICT_AVP, &data , type, NULL);
+		}
+		
+		/* Vendor-Specific-Application-Id */
+		{
+			/*
+				The Vendor-Specific-Application-Id AVP (AVP Code 260) is of type
+				Grouped and is used to advertise support of a vendor-specific
+				Diameter Application.  Exactly one instance of either Auth-
+				Application-Id or Acct-Application-Id AVP MUST be present.  The
+				Application Id carried by either Auth-Application-Id or Acct-
+				Application-Id AVP MUST comply with vendor specific Application Id
+				assignment described in Sec 11.3.  It MUST also match the Application
+				Id present in the diameter header except when used in a CER or CEA
+				messages.
+
+				The Vendor-Id AVP is an informational AVP pertaining to the vendor
+				who may have authorship of the vendor-specific Diameter application.
+				It MUST NOT be used as a means of defining a completely separate
+				vendor-specific Application Id space.
+
+				This AVP MUST also be present as the first AVP in all experimental
+				commands defined in the vendor-specific application.
+
+				This AVP SHOULD be placed as close to the Diameter header as
+				possible.
+
+				AVP Format
+
+				<Vendor-Specific-Application-Id> ::= < AVP Header: 260 >
+                                				   { Vendor-Id }
+                                				   [ Auth-Application-Id ]
+                                				   [ Acct-Application-Id ]
+
+				A Vendor-Specific-Application-Id AVP MUST contain exactly one of
+				either Auth-Application-Id or Acct-Application-Id.  If a Vendor-
+				Specific-Application-Id is received without any of these two AVPs,
+				then the recipient SHOULD issue an answer with a Result-Code set to
+				DIAMETER_MISSING_AVP.  The answer SHOULD also include a Failed-AVP
+				which MUST contain an example of an Auth-Application-Id AVP and an
+				Acct-Application-Id AVP.
+
+				If a Vendor-Specific-Application-Id is received that contains both
+				Auth-Application-Id and Acct-Application-Id, then the recipient
+				SHOULD issue an answer with Result-Code set to
+				DIAMETER_AVP_OCCURS_TOO_MANY_TIMES.  The answer SHOULD also include a
+				Failed-AVP which MUST contain the received Auth-Application-Id AVP
+				and Acct-Application-Id AVP.
+			*/
+			struct dict_object 	* avp;
+			struct dict_avp_data	  data = { 
+					260, 					/* Code */
+					#if AC_VENDOR_SPECIFIC_APPLICATION_ID != 260
+					#error "AC_VENDOR_SPECIFIC_APPLICATION_ID definition mismatch"
+					#endif
+					0, 					/* Vendor */
+					"Vendor-Specific-Application-Id", 	/* Name */
+					AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, 	/* Fixed flags */
+					AVP_FLAG_MANDATORY,			/* Fixed flag values */
+					AVP_TYPE_GROUPED 			/* base type of data */
+					};
+					
+			struct local_rules_definition rules[] = 
+						{
+#ifndef WORKAROUND_ACCEPT_INVALID_VSAI
+							/* ABNF from RFC6733 */
+						 	 {  "Vendor-Id", 			RULE_REQUIRED, -1, 1 }
+#else /* WORKAROUND_ACCEPT_INVALID_VSAI */
+							/* ABNF from RFC3588 (including erratum, because original text is nonsense) */
+						 	 {  "Vendor-Id", 			RULE_REQUIRED, -1, -1}
+#endif /* WORKAROUND_ACCEPT_INVALID_VSAI */
+							,{  "Auth-Application-Id",		RULE_OPTIONAL, -1, 1 }
+							,{  "Acct-Application-Id",		RULE_OPTIONAL, -1, 1 }
+						};
+			
+			/* Create the grouped AVP */
+			CHECK_dict_new( DICT_AVP, &data , NULL, &avp);
+			PARSE_loc_rules( rules, avp );
+			
+		}
+		
+		/* Redirect-Host */
+		{
+			/*
+				One or more of instances of this AVP MUST be present if the answer
+				message's 'E' bit is set and the Result-Code AVP is set to
+				DIAMETER_REDIRECT_INDICATION.
+
+				Upon receiving the above, the receiving Diameter node SHOULD forward
+				the request directly to one of the hosts identified in these AVPs.
+				The server contained in the selected Redirect-Host AVP SHOULD be used
+				for all messages pertaining to this session.
+			*/
+			struct dict_avp_data data = { 
+					292, 					/* Code */
+					#if AC_REDIRECT_HOST != 292
+					#error "AC_REDIRECT_HOST definition mismatch"
+					#endif
+					0, 					/* Vendor */
+					"Redirect-Host", 			/* Name */
+					AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, 	/* Fixed flags */
+					AVP_FLAG_MANDATORY,			/* Fixed flag values */
+					AVP_TYPE_OCTETSTRING 			/* base type of data */
+					};
+			CHECK_dict_new( DICT_AVP, &data , DiameterURI_type, NULL);
+		}
+		
+		/* Redirect-Host-Usage */
+		{
+			/*
+				The Redirect-Host-Usage AVP (AVP Code 261) is of type Enumerated.
+				This AVP MAY be present in answer messages whose 'E' bit is set and
+				the Result-Code AVP is set to DIAMETER_REDIRECT_INDICATION.
+
+				When present, this AVP dictates how the routing entry resulting from
+				the Redirect-Host is to be used.  The following values are supported:
+
+
+				DONT_CACHE 0
+
+				The host specified in the Redirect-Host AVP should not be cached.
+				This is the default value.
+
+
+				ALL_SESSION 1
+
+				All messages within the same session, as defined by the same value
+				of the Session-ID AVP MAY be sent to the host specified in the
+				Redirect-Host AVP.
+
+
+				ALL_REALM 2
+
+				All messages destined for the realm requested MAY be sent to the
+				host specified in the Redirect-Host AVP.
+
+
+				REALM_AND_APPLICATION 3
+
+				All messages for the application requested to the realm specified
+				MAY be sent to the host specified in the Redirect-Host AVP.
+
+				ALL_APPLICATION 4
+
+				All messages for the application requested MAY be sent to the host
+				specified in the Redirect-Host AVP.
+
+
+				ALL_HOST 5
+
+				All messages that would be sent to the host that generated the
+				Redirect-Host MAY be sent to the host specified in the Redirect-
+				Host AVP.
+
+
+				ALL_USER 6
+
+				All messages for the user requested MAY be sent to the host
+				specified in the Redirect-Host AVP.
+
+
+				When multiple cached routes are created by redirect indications and
+				they differ only in redirect usage and peers to forward requests to
+				(see Section 6.1.8), a precedence rule MUST be applied to the
+				redirect usage values of the cached routes during normal routing to
+				resolve contentions that may occur.  The precedence rule is the order
+				that dictate which redirect usage should be considered before any
+				other as they appear.  The order is as follows:
+
+
+				1.  ALL_SESSION
+
+				2.  ALL_USER
+
+				3.  REALM_AND_APPLICATION
+
+				4.  ALL_REALM
+
+				5.  ALL_APPLICATION
+
+				6.  ALL_HOST
+			*/
+			struct dict_object 	* 	type;
+			struct dict_type_data	 	tdata = { AVP_TYPE_INTEGER32,	"Enumerated(Redirect-Host-Usage)"	, NULL, NULL, NULL };
+			struct dict_enumval_data 	t_0 = { "DONT_CACHE", 			{ .i32 = 0 }};
+			struct dict_enumval_data 	t_1 = { "ALL_SESSION", 			{ .i32 = 1 }};
+			struct dict_enumval_data 	t_2 = { "ALL_REALM", 			{ .i32 = 2 }};
+			struct dict_enumval_data 	t_3 = { "REALM_AND_APPLICATION", 	{ .i32 = 3 }};
+			struct dict_enumval_data 	t_4 = { "ALL_APPLICATION", 		{ .i32 = 4 }};
+			struct dict_enumval_data 	t_5 = { "ALL_HOST", 			{ .i32 = 5 }};
+			struct dict_enumval_data 	t_6 = { "ALL_USER", 			{ .i32 = 6 }};
+			struct dict_avp_data 		data = { 
+					261, 					/* Code */
+					#if AC_REDIRECT_HOST_USAGE != 261
+					#error "AC_REDIRECT_HOST_USAGE definition mismatch"
+					#endif
+					0, 					/* Vendor */
+					"Redirect-Host-Usage", 			/* Name */
+					AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, 	/* Fixed flags */
+					AVP_FLAG_MANDATORY,			/* Fixed flag values */
+					AVP_TYPE_INTEGER32 			/* base type of data */
+					};
+			/* Create the Enumerated type, and then the AVP */
+			CHECK_dict_new( DICT_TYPE, &tdata , NULL, &type);
+			CHECK_dict_new( DICT_ENUMVAL, &t_0 , type, NULL);
+			CHECK_dict_new( DICT_ENUMVAL, &t_1 , type, NULL);
+			CHECK_dict_new( DICT_ENUMVAL, &t_2 , type, NULL);
+			CHECK_dict_new( DICT_ENUMVAL, &t_3 , type, NULL);
+			CHECK_dict_new( DICT_ENUMVAL, &t_4 , type, NULL);
+			CHECK_dict_new( DICT_ENUMVAL, &t_5 , type, NULL);
+			CHECK_dict_new( DICT_ENUMVAL, &t_6 , type, NULL);
+			CHECK_dict_new( DICT_AVP, &data , type, NULL);
+		}
+		
+		/* Redirect-Max-Cache-Time */
+		{
+			/*
+				The Redirect-Max-Cache-Time AVP (AVP Code 262) is of type Unsigned32.
+				This AVP MUST be present in answer messages whose 'E' bit is set, the
+				Result-Code AVP is set to DIAMETER_REDIRECT_INDICATION and the
+				Redirect-Host-Usage AVP set to a non-zero value.
+
+				This AVP contains the maximum number of seconds the peer and route
+				table entries, created as a result of the Redirect-Host, will be
+				cached.  Note that once a host created due to a redirect indication
+				is no longer reachable, any associated peer and routing table entries
+				MUST be deleted.
+			*/
+			struct dict_avp_data data = { 
+					262, 					/* Code */
+					#if AC_REDIRECT_MAX_CACHE_TIME != 262
+					#error "AC_REDIRECT_MAX_CACHE_TIME definition mismatch"
+					#endif
+					0, 					/* Vendor */
+					"Redirect-Max-Cache-Time", 		/* Name */
+					AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, 	/* Fixed flags */
+					AVP_FLAG_MANDATORY,			/* Fixed flag values */
+					AVP_TYPE_UNSIGNED32 			/* base type of data */
+					};
+			CHECK_dict_new( DICT_AVP, &data , NULL, NULL);
+		}
+		
+		/* Result-Code */
+		{
+			/*
+				The Result-Code AVP (AVP Code 268) is of type Unsigned32 and
+				indicates whether a particular request was completed successfully or
+				whether an error occurred.  All Diameter answer messages defined in
+				IETF applications MUST include one Result-Code AVP.  A non-successful
+				Result-Code AVP (one containing a non 2xxx value other than
+				DIAMETER_REDIRECT_INDICATION) MUST include the Error-Reporting-Host
+				AVP if the host setting the Result-Code AVP is different from the
+				identity encoded in the Origin-Host AVP.
+
+				The Result-Code data field contains an IANA-managed 32-bit address
+				space representing errors (see Section 11.4).  Diameter provides the
+				following classes of errors, all identified by the thousands digit in
+				the decimal notation:
+
+				o  1xxx (Informational)
+
+				o  2xxx (Success)
+
+				o  3xxx (Protocol Errors)
+
+				o  4xxx (Transient Failures)
+
+				o  5xxx (Permanent Failure)
+
+				A non-recognized class (one whose first digit is not defined in this
+				section) MUST be handled as a permanent failure.
+			*/
+			
+			/* Although the RFC does not specify an "Enumerated" type here, we go forward and create one.
+			 * This is the reason for the "*" in the type name
+			 */
+			struct dict_object * 	type;
+			struct dict_type_data 	tdata = { AVP_TYPE_UNSIGNED32,	"Enumerated(Result-Code)"	, NULL, NULL, NULL };
+			struct dict_avp_data 	data = { 
+					268, 					/* Code */
+					#if AC_RESULT_CODE != 268
+					#error "AC_RESULT_CODE definition mismatch"
+					#endif
+					0, 					/* Vendor */
+					"Result-Code", 				/* Name */
+					AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, 	/* Fixed flags */
+					AVP_FLAG_MANDATORY,			/* Fixed flag values */
+					AVP_TYPE_UNSIGNED32 			/* base type of data */
+					};
+			/* Create the Enumerated type, and then the AVP */
+			CHECK_dict_new( DICT_TYPE, &tdata , NULL, &type);
+			CHECK_dict_new( DICT_AVP, &data , type, NULL);
+			
+			/* Informational */
+			{
+				/* 1001 */
+				{
+					/*
+						This informational error is returned by a Diameter server to
+						inform the access device that the authentication mechanism being
+						used requires multiple round trips, and a subsequent request needs
+						to be issued in order for access to be granted.
+					*/
+					struct dict_enumval_data 	error_code = { "DIAMETER_MULTI_ROUND_AUTH", 	{ .u32 = ER_DIAMETER_MULTI_ROUND_AUTH }};
+					CHECK_dict_new( DICT_ENUMVAL, &error_code , type, NULL);
+				}
+			}
+			/* Success */
+			{
+				/* 2001 */
+				{
+					/*
+						The Request was successfully completed.
+					*/
+					struct dict_enumval_data 	error_code = { "DIAMETER_SUCCESS", 		{ .u32 = ER_DIAMETER_SUCCESS }};
+					#if ER_DIAMETER_SUCCESS != 2001
+					#error "ER_DIAMETER_SUCCESS definition mismatch"
+					#endif
+					CHECK_dict_new( DICT_ENUMVAL, &error_code , type, NULL);
+				}
+				/* 2002 */
+				{
+					/*
+						When returned, the request was successfully completed, but
+						additional processing is required by the application in order to
+						provide service to the user.
+					*/
+					struct dict_enumval_data 	error_code = { "DIAMETER_LIMITED_SUCCESS", 	{ .u32 = ER_DIAMETER_LIMITED_SUCCESS }};
+					CHECK_dict_new( DICT_ENUMVAL, &error_code , type, NULL);
+				}
+			}
+			/* Protocol Errors */
+			{
+				/* 3001 -- might be changed to 5xxx soon */
+				{
+					/*
+						The Request contained a Command-Code that the receiver did not
+						recognize or support.  This MUST be used when a Diameter node
+						receives an experimental command that it does not understand.
+					*/
+					struct dict_enumval_data 	error_code = { "DIAMETER_COMMAND_UNSUPPORTED", 	{ .u32 = ER_DIAMETER_COMMAND_UNSUPPORTED }};
+					CHECK_dict_new( DICT_ENUMVAL, &error_code , type, NULL);
+				}
+				/* 3002 */
+				{
+					/*
+						This error is given when Diameter can not deliver the message to
+						the destination, either because no host within the realm
+						supporting the required application was available to process the
+						request, or because Destination-Host AVP was given without the
+						associated Destination-Realm AVP.
+					*/
+					struct dict_enumval_data 	error_code = { "DIAMETER_UNABLE_TO_DELIVER", 	{ .u32 = ER_DIAMETER_UNABLE_TO_DELIVER }};
+					CHECK_dict_new( DICT_ENUMVAL, &error_code , type, NULL);
+				}
+				/* 3003 */
+				{
+					/*
+						The intended realm of the request is not recognized.
+					*/
+					struct dict_enumval_data 	error_code = { "DIAMETER_REALM_NOT_SERVED", 	{ .u32 = ER_DIAMETER_REALM_NOT_SERVED }};
+					CHECK_dict_new( DICT_ENUMVAL, &error_code , type, NULL);
+				}
+				/* 3004 */
+				{
+					/*
+						When returned, a Diameter node SHOULD attempt to send the message
+						to an alternate peer.  This error MUST only be used when a
+						specific server is requested, and it cannot provide the requested
+						service.
+					*/
+					struct dict_enumval_data 	error_code = { "DIAMETER_TOO_BUSY", 		{ .u32 = ER_DIAMETER_TOO_BUSY }};
+					CHECK_dict_new( DICT_ENUMVAL, &error_code , type, NULL);
+				}
+				/* 3005 */
+				{
+					/*
+						An agent detected a loop while trying to get the message to the
+						intended recipient.  The message MAY be sent to an alternate peer,
+						if one is available, but the peer reporting the error has
+						identified a configuration problem.
+					*/
+					struct dict_enumval_data 	error_code = { "DIAMETER_LOOP_DETECTED", 	{ .u32 = ER_DIAMETER_LOOP_DETECTED }};
+					CHECK_dict_new( DICT_ENUMVAL, &error_code , type, NULL);
+				}
+				/* 3006 */
+				{
+					/*
+						A redirect agent has determined that the request could not be
+						satisfied locally and the initiator of the request should direct
+						the request directly to the server, whose contact information has
+						been added to the response.  When set, the Redirect-Host AVP MUST
+						be present.
+					*/
+					struct dict_enumval_data 	error_code = { "DIAMETER_REDIRECT_INDICATION", 	{ .u32 = ER_DIAMETER_REDIRECT_INDICATION }};
+					CHECK_dict_new( DICT_ENUMVAL, &error_code , type, NULL);
+				}
+				/* 3007 */
+				{
+					/*
+						A request was sent for an application that is not supported.
+					*/
+					struct dict_enumval_data 	error_code = { "DIAMETER_APPLICATION_UNSUPPORTED",	{ .u32 = ER_DIAMETER_APPLICATION_UNSUPPORTED }};
+					CHECK_dict_new( DICT_ENUMVAL, &error_code , type, NULL);
+				}
+				/* 3008 -- will change to 5xxx soon */
+				{
+					/*
+						A request was received whose bits in the Diameter header were
+						either set to an invalid combination, or to a value that is
+						inconsistent with the command code's definition.
+					*/
+					struct dict_enumval_data 	error_code = { "DIAMETER_INVALID_HDR_BITS", 	{ .u32 = ER_DIAMETER_INVALID_HDR_BITS }};
+					CHECK_dict_new( DICT_ENUMVAL, &error_code , type, NULL);
+				}
+				/* 3009 -- will change to 5xxx soon */
+				{
+					/*
+						A request was received that included an AVP whose flag bits are
+						set to an unrecognized value, or that is inconsistent with the
+						AVP's definition.
+					*/
+					struct dict_enumval_data 	error_code = { "DIAMETER_INVALID_AVP_BITS", 	{ .u32 = ER_DIAMETER_INVALID_AVP_BITS }};
+					CHECK_dict_new( DICT_ENUMVAL, &error_code , type, NULL);
+				}
+				/* 3010  -- will change to 5xxx soon */
+				{
+					/*
+						 A CER was received from an unknown peer.
+					*/
+					struct dict_enumval_data 	error_code = { "DIAMETER_UNKNOWN_PEER", 	{ .u32 = ER_DIAMETER_UNKNOWN_PEER }};
+					CHECK_dict_new( DICT_ENUMVAL, &error_code , type, NULL);
+				}
+			}
+			/* Transient Failures */
+			{
+				/* 4001 */
+				{
+					/*
+						The authentication process for the user failed, most likely due to
+						an invalid password used by the user.  Further attempts MUST only
+						be tried after prompting the user for a new password.
+					*/
+					struct dict_enumval_data 	error_code = { "DIAMETER_AUTHENTICATION_REJECTED", 	{ .u32 = ER_DIAMETER_AUTHENTICATION_REJECTED }};
+					CHECK_dict_new( DICT_ENUMVAL, &error_code , type, NULL);
+				}
+				/* 4002 */
+				{
+					/*
+						A Diameter node received the accounting request but was unable to
+						commit it to stable storage due to a temporary lack of space.
+					*/
+					struct dict_enumval_data 	error_code = { "DIAMETER_OUT_OF_SPACE", 		{ .u32 = ER_DIAMETER_OUT_OF_SPACE }};
+					CHECK_dict_new( DICT_ENUMVAL, &error_code , type, NULL);
+				}
+				/* 4003 */
+				{
+					/*
+						The peer has determined that it has lost the election process and
+						has therefore disconnected the transport connection.
+					*/
+					struct dict_enumval_data 	error_code = { "ELECTION_LOST", 			{ .u32 = ER_ELECTION_LOST }};
+					CHECK_dict_new( DICT_ENUMVAL, &error_code , type, NULL);
+				}
+			}
+			/* Permanent Failures */
+			{
+				/* 5001 */
+				{
+					/*
+						The peer received a message that contained an AVP that is not
+						recognized or supported and was marked with the Mandatory bit.  A
+						Diameter message with this error MUST contain one or more Failed-
+						AVP AVP containing the AVPs that caused the failure.
+					*/
+					struct dict_enumval_data 	error_code = { "DIAMETER_AVP_UNSUPPORTED", 	{ .u32 = ER_DIAMETER_AVP_UNSUPPORTED }};
+					CHECK_dict_new( DICT_ENUMVAL, &error_code , type, NULL);
+				}
+				/* 5002 */
+				{
+					/*
+						The request contained an unknown Session-Id.
+					*/
+					struct dict_enumval_data 	error_code = { "DIAMETER_UNKNOWN_SESSION_ID", 	{ .u32 = ER_DIAMETER_UNKNOWN_SESSION_ID }};
+					CHECK_dict_new( DICT_ENUMVAL, &error_code , type, NULL);
+				}
+				/* 5003 */
+				{
+					/*
+						A request was received for which the user could not be authorized.
+						This error could occur if the service requested is not permitted
+						to the user.
+					*/
+					struct dict_enumval_data 	error_code = { "DIAMETER_AUTHORIZATION_REJECTED",{ .u32 = ER_DIAMETER_AUTHORIZATION_REJECTED }};
+					CHECK_dict_new( DICT_ENUMVAL, &error_code , type, NULL);
+				}
+				/* 5004 */
+				{
+					/*
+						The request contained an AVP with an invalid value in its data
+						portion.  A Diameter message indicating this error MUST include
+						the offending AVPs within a Failed-AVP AVP.
+					*/
+					struct dict_enumval_data 	error_code = { "DIAMETER_INVALID_AVP_VALUE",	{ .u32 = ER_DIAMETER_INVALID_AVP_VALUE }};
+					CHECK_dict_new( DICT_ENUMVAL, &error_code , type, NULL);
+				}
+				/* 5005 */
+				{
+					/*
+						The request did not contain an AVP that is required by the Command
+						Code definition.  If this value is sent in the Result-Code AVP, a
+						Failed-AVP AVP SHOULD be included in the message.  The Failed-AVP
+						AVP MUST contain an example of the missing AVP complete with the
+						Vendor-Id if applicable.  The value field of the missing AVP
+						should be of correct minimum length and contain zeroes.
+					*/
+					struct dict_enumval_data 	error_code = { "DIAMETER_MISSING_AVP",		{ .u32 = ER_DIAMETER_MISSING_AVP }};
+					CHECK_dict_new( DICT_ENUMVAL, &error_code , type, NULL);
+				}
+				/* 5006 */
+				{
+					/*
+						A request was received that cannot be authorized because the user
+						has already expended allowed resources.  An example of this error
+						condition is a user that is restricted to one dial-up PPP port,
+						attempts to establish a second PPP connection.
+					*/
+					struct dict_enumval_data 	error_code = { "DIAMETER_RESOURCES_EXCEEDED",	{ .u32 = ER_DIAMETER_RESOURCES_EXCEEDED }};
+					CHECK_dict_new( DICT_ENUMVAL, &error_code , type, NULL);
+				}
+				/* 5007 */
+				{
+					/*
+						The Home Diameter server has detected AVPs in the request that
+						contradicted each other, and is not willing to provide service to
+						the user.  The Failed-AVP AVPs MUST be present which contains the
+						AVPs that contradicted each other.
+					*/
+					struct dict_enumval_data 	error_code = { "DIAMETER_CONTRADICTING_AVPS",	{ .u32 = ER_DIAMETER_CONTRADICTING_AVPS }};
+					CHECK_dict_new( DICT_ENUMVAL, &error_code , type, NULL);
+				}
+				/* 5008 */
+				{
+					/*
+						A message was received with an AVP that MUST NOT be present.  The
+						Failed-AVP AVP MUST be included and contain a copy of the
+						offending AVP.
+					*/
+					struct dict_enumval_data 	error_code = { "DIAMETER_AVP_NOT_ALLOWED",	{ .u32 = ER_DIAMETER_AVP_NOT_ALLOWED }};
+					CHECK_dict_new( DICT_ENUMVAL, &error_code , type, NULL);
+				}
+				/* 5009 */
+				{
+					/*
+						A message was received that included an AVP that appeared more
+						often than permitted in the message definition.  The Failed-AVP
+						AVP MUST be included and contain a copy of the first instance of
+						the offending AVP that exceeded the maximum number of occurrences
+					*/
+					struct dict_enumval_data 	error_code = { "DIAMETER_AVP_OCCURS_TOO_MANY_TIMES",{ .u32 = ER_DIAMETER_AVP_OCCURS_TOO_MANY_TIMES }};
+					CHECK_dict_new( DICT_ENUMVAL, &error_code , type, NULL);
+				}
+				/* 5010 */
+				{
+					/*
+						This error is returned by a Diameter node that is not acting as a
+						relay when it receives a CER which advertises a set of
+						applications that it does not support.
+					*/
+					struct dict_enumval_data 	error_code = { "DIAMETER_NO_COMMON_APPLICATION",{ .u32 = ER_DIAMETER_NO_COMMON_APPLICATION }};
+					CHECK_dict_new( DICT_ENUMVAL, &error_code , type, NULL);
+				}
+				/* 5011 */
+				{
+					/*
+						This error is returned when a request was received, whose version
+						number is unsupported.
+					*/
+					struct dict_enumval_data 	error_code = { "DIAMETER_UNSUPPORTED_VERSION",	{ .u32 = ER_DIAMETER_UNSUPPORTED_VERSION }};
+					CHECK_dict_new( DICT_ENUMVAL, &error_code , type, NULL);
+				}
+				/* 5012 */
+				{
+					/*
+						This error is returned when a request is rejected for unspecified
+						reasons.
+					*/
+					struct dict_enumval_data 	error_code = { "DIAMETER_UNABLE_TO_COMPLY",	{ .u32 = ER_DIAMETER_UNABLE_TO_COMPLY }};
+					CHECK_dict_new( DICT_ENUMVAL, &error_code , type, NULL);
+				}
+				/* 5013 -- will change to 3xxx */
+				{
+					/*
+						This error is returned when an unrecognized bit in the Diameter
+						header is set to one (1).
+					*/
+					struct dict_enumval_data 	error_code = { "DIAMETER_INVALID_BIT_IN_HEADER", 	{ .u32 = ER_DIAMETER_INVALID_BIT_IN_HEADER }};
+					CHECK_dict_new( DICT_ENUMVAL, &error_code , type, NULL);
+				}
+				/* 5014 */
+				{
+					/*
+						The request contained an AVP with an invalid length.  A Diameter
+						message indicating this error MUST include the offending AVPs
+						within a Failed-AVP AVP.  In cases where the erroneous avp length
+						value exceeds the message length or is less than the minimum AVP
+						header length, it is sufficient to include the offending AVP
+						header and a zero filled payload of the minimum required length
+						for the payloads data type.  If the AVP is a grouped AVP, the
+						grouped AVP header with an empty payload would be sufficient to
+						indicate the offending AVP.  In the case where the offending AVP
+						header cannot be fully decoded when avp length is less than the
+						minimum AVP header length, it is sufficient to include an
+						offending AVP header that is formulated by padding the incomplete
+						AVP header with zero up to the minimum AVP header length.
+					*/
+					struct dict_enumval_data 	error_code = { "DIAMETER_INVALID_AVP_LENGTH",	{ .u32 = ER_DIAMETER_INVALID_AVP_LENGTH }};
+					CHECK_dict_new( DICT_ENUMVAL, &error_code , type, NULL);
+				}
+				/* 5015 -- will change to 3xxx */
+				{
+					/*
+						This error is returned when a request is received with an invalid
+						message length.
+					*/
+					struct dict_enumval_data 	error_code = { "DIAMETER_INVALID_MESSAGE_LENGTH", 	{ .u32 = ER_DIAMETER_INVALID_MESSAGE_LENGTH }};
+					CHECK_dict_new( DICT_ENUMVAL, &error_code , type, NULL);
+				}
+				/* 5016 */
+				{
+					/*
+						The request contained an AVP with which is not allowed to have the
+						given value in the AVP Flags field.  A Diameter message indicating
+						this error MUST include the offending AVPs within a Failed-AVP
+						AVP.
+					*/
+					struct dict_enumval_data 	error_code = { "DIAMETER_INVALID_AVP_BIT_COMBO", 	{ .u32 = ER_DIAMETER_INVALID_AVP_BIT_COMBO }};
+					CHECK_dict_new( DICT_ENUMVAL, &error_code , type, NULL);
+				}
+				/* 5017 */
+				{
+					/*
+						This error is returned when a CER message is received, and there
+						are no common security mechanisms supported between the peers.  A
+						Capabilities-Exchange-Answer (CEA) MUST be returned with the
+						Result-Code AVP set to DIAMETER_NO_COMMON_SECURITY.
+					*/
+					struct dict_enumval_data 	error_code = { "DIAMETER_NO_COMMON_SECURITY",	{ .u32 = ER_DIAMETER_NO_COMMON_SECURITY }};
+					CHECK_dict_new( DICT_ENUMVAL, &error_code , type, NULL);
+				}
+			}
+		}
+		
+		/* Error-Message */
+		{
+			/*
+				The Error-Message AVP (AVP Code 281) is of type UTF8String.  It MAY
+				accompany a Result-Code AVP as a human readable error message.  The
+				Error-Message AVP is not intended to be useful in real-time, and
+				SHOULD NOT be expected to be parsed by network entities.
+			*/
+			struct dict_avp_data data = { 
+					281, 					/* Code */
+					#if AC_ERROR_MESSAGE != 281
+					#error "AC_ERROR_MESSAGE definition mismatch"
+					#endif
+					0, 					/* Vendor */
+					"Error-Message", 			/* Name */
+					AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, 	/* Fixed flags */
+					0,					/* Fixed flag values */
+					AVP_TYPE_OCTETSTRING 			/* base type of data */
+					};
+			CHECK_dict_new( DICT_AVP, &data , UTF8String_type, NULL);
+		}
+		
+		/* Error-Reporting-Host */
+		{
+			/*
+				The Error-Reporting-Host AVP (AVP Code 294) is of type
+				DiameterIdentity.  This AVP contains the identity of the Diameter
+				host that sent the Result-Code AVP to a value other than 2001
+				(Success), only if the host setting the Result-Code is different from
+				the one encoded in the Origin-Host AVP.  This AVP is intended to be
+				used for troubleshooting purposes, and MUST be set when the Result-
+				Code AVP indicates a failure.
+			*/
+			struct dict_avp_data data = { 
+					294, 					/* Code */
+					#if AC_ERROR_REPORTING_HOST != 294
+					#error "AC_ERROR_REPORTING_HOST definition mismatch"
+					#endif
+					0, 					/* Vendor */
+					"Error-Reporting-Host", 		/* Name */
+					AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, 	/* Fixed flags */
+					0,					/* Fixed flag values */
+					AVP_TYPE_OCTETSTRING 			/* base type of data */
+					};
+			CHECK_dict_new( DICT_AVP, &data , DiameterIdentity_type, NULL);
+		}
+		
+		/* Failed-AVP */
+		{
+			/*
+				The Failed-AVP AVP (AVP Code 279) is of type Grouped and provides
+				debugging information in cases where a request is rejected or not
+				fully processed due to erroneous information in a specific AVP.  The
+				value of the Result-Code AVP will provide information on the reason
+				for the Failed-AVP AVP.  A Diameter message SHOULD contain only one
+				Failed-AVP that corresponds to the error indicated by the Result-Code
+				AVP.  For practical purposes, this Failed-AVP would typically refer
+				to the first AVP processing error that a Diameter node encounters.
+
+				The possible reasons for this AVP are the presence of an improperly
+				constructed AVP, an unsupported or unrecognized AVP, an invalid AVP
+				value, the omission of a required AVP, the presence of an explicitly
+				excluded AVP (see tables in Section 10), or the presence of two or
+				more occurrences of an AVP which is restricted to 0, 1, or 0-1
+				occurrences.
+
+				A Diameter message SHOULD contain one Failed-AVP AVP, containing the
+				entire AVP that could not be processed successfully.  If the failure
+				reason is omission of a required AVP, an AVP with the missing AVP
+				code, the missing vendor id, and a zero filled payload of the minimum
+				required length for the omitted AVP will be added.  If the failure
+				reason is an invalid AVP length where the reported length is less
+				than the minimum AVP header length or greater than the reported
+				message length, a copy of the offending AVP header and a zero filled
+				payload of the minimum required length SHOULD be added.
+
+				In the case where the offending AVP is embedded within a grouped AVP,
+				the Failed-AVP MAY contain the grouped AVP which in turn contains the
+				single offending AVP.  The same method MAY be employed if the grouped
+				AVP itself is embedded in yet another grouped AVP and so on.  In this
+				case, the Failed-AVP MAY contain the grouped AVP heirarchy up to the
+				single offending AVP.  This enables the recipient to detect the
+				location of the offending AVP when embedded in a group.
+
+				AVP Format
+
+				 <Failed-AVP> ::= < AVP Header: 279 >
+        				       1* {AVP}
+			*/
+			struct dict_avp_data data = { 
+					279, 					/* Code */
+					#if AC_FAILED_AVP != 279
+					#error "AC_FAILED_AVP definition mismatch"
+					#endif
+					0, 					/* Vendor */
+					"Failed-AVP", 				/* Name */
+					AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, 	/* Fixed flags */
+					AVP_FLAG_MANDATORY,			/* Fixed flag values */
+					AVP_TYPE_GROUPED 			/* base type of data */
+					};
+			CHECK_dict_new( DICT_AVP, &data , NULL, NULL);
+		}
+		
+		/* Experimental-Result */
+		{
+			/*
+				The Experimental-Result AVP (AVP Code 297) is of type Grouped, and
+				indicates whether a particular vendor-specific request was completed
+				successfully or whether an error occurred.  Its Data field has the
+				following ABNF grammar:
+
+				AVP Format
+
+				 Experimental-Result ::= < AVP Header: 297 >
+                        				 { Vendor-Id }
+                        				 { Experimental-Result-Code }
+
+				The Vendor-Id AVP (see Section 5.3.3) in this grouped AVP identifies
+				the vendor responsible for the assignment of the result code which
+				follows.  All Diameter answer messages defined in vendor-specific
+				applications MUST include either one Result-Code AVP or one
+				Experimental-Result AVP.
+			*/
+			struct dict_avp_data data = { 
+					297, 					/* Code */
+					0, 					/* Vendor */
+					"Experimental-Result", 			/* Name */
+					AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, 	/* Fixed flags */
+					AVP_FLAG_MANDATORY,			/* Fixed flag values */
+					AVP_TYPE_GROUPED 			/* base type of data */
+					};
+			CHECK_dict_new( DICT_AVP, &data , NULL, NULL);
+		}
+		
+		/* Experimental-Result-Code */
+		{
+			/*
+				The Experimental-Result-Code AVP (AVP Code 298) is of type Unsigned32
+				and contains a vendor-assigned value representing the result of
+				processing the request.
+
+				It is recommended that vendor-specific result codes follow the same
+				conventions given for the Result-Code AVP regarding the different
+				types of result codes and the handling of errors (for non 2xxx
+				values).
+			*/
+			/* Although the RFC does not specify an "Enumerated" type here, we go forward and create one.
+			 * This is the reason for the "*" in the type name. Vendors will have to define their values.
+			 */
+			struct dict_object * 	type;
+			struct dict_type_data 	tdata = { AVP_TYPE_UNSIGNED32,	"Enumerated(Experimental-Result-Code)"	, NULL, NULL, NULL };
+			struct dict_avp_data 	data = { 
+					298, 					/* Code */
+					0, 					/* Vendor */
+					"Experimental-Result-Code", 		/* Name */
+					AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, 	/* Fixed flags */
+					AVP_FLAG_MANDATORY,			/* Fixed flag values */
+					AVP_TYPE_UNSIGNED32 			/* base type of data */
+					};
+			
+			CHECK_dict_new( DICT_TYPE, &tdata , NULL, &type);
+			CHECK_dict_new( DICT_AVP, &data , type, NULL);
+		}
+		
+		/* Auth-Request-Type */
+		{
+			/*
+				The Auth-Request-Type AVP (AVP Code 274) is of type Enumerated and is
+				included in application-specific auth requests to inform the peers
+				whether a user is to be authenticated only, authorized only or both.
+				Note any value other than both MAY cause RADIUS interoperability
+				issues.  The following values are defined:
+
+
+				AUTHENTICATE_ONLY 1
+
+				The request being sent is for authentication only, and MUST
+				contain the relevant application specific authentication AVPs that
+				are needed by the Diameter server to authenticate the user.
+
+
+				AUTHORIZE_ONLY 2
+
+				The request being sent is for authorization only, and MUST contain
+				the application specific authorization AVPs that are necessary to
+				identify the service being requested/offered.
+
+
+				AUTHORIZE_AUTHENTICATE 3
+
+				The request contains a request for both authentication and
+				authorization.  The request MUST include both the relevant
+				application specific authentication information, and authorization
+				information necessary to identify the service being requested/
+				offered.
+			*/
+			struct dict_object 	* 	type;
+			struct dict_type_data 		tdata = { AVP_TYPE_INTEGER32,	"Enumerated(Auth-Request-Type)"	, NULL, NULL, NULL };
+			struct dict_enumval_data 	t_1 = { "AUTHENTICATE_ONLY", 		{ .i32 = 1 }};
+			struct dict_enumval_data 	t_2 = { "AUTHORIZE_ONLY", 		{ .i32 = 2 }};
+			struct dict_enumval_data 	t_3 = { "AUTHORIZE_AUTHENTICATE", 	{ .i32 = 3 }};
+			struct dict_avp_data 	data = { 
+					274, 					/* Code */
+					0, 					/* Vendor */
+					"Auth-Request-Type", 			/* Name */
+					AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, 	/* Fixed flags */
+					AVP_FLAG_MANDATORY,			/* Fixed flag values */
+					AVP_TYPE_INTEGER32 			/* base type of data */
+					};
+			/* Create the Enumerated type, and then the AVP */
+			CHECK_dict_new( DICT_TYPE, &tdata , NULL, &type);
+			CHECK_dict_new( DICT_ENUMVAL, &t_1 , type, NULL);
+			CHECK_dict_new( DICT_ENUMVAL, &t_2 , type, NULL);
+			CHECK_dict_new( DICT_ENUMVAL, &t_3 , type, NULL);
+			CHECK_dict_new( DICT_AVP, &data , type, NULL);
+		}
+		
+		/* Session-Id */
+		{
+			/*
+				The Session-Id AVP (AVP Code 263) is of type UTF8String and is used
+				to identify a specific session (see Section 8).  All messages
+				pertaining to a specific session MUST include only one Session-Id AVP
+				and the same value MUST be used throughout the life of a session.
+				When present, the Session-Id SHOULD appear immediately following the
+				Diameter Header (see Section 3).
+
+				The Session-Id MUST be globally and eternally unique, as it is meant
+				to uniquely identify a user session without reference to any other
+				information, and may be needed to correlate historical authentication
+				information with accounting information.  The Session-Id includes a
+				mandatory portion and an implementation-defined portion; a
+				recommended format for the implementation-defined portion is outlined
+				below.
+				
+				(skipped, see RFC for detail)
+			*/
+			struct dict_avp_data data = { 
+					263, 					/* Code */
+					#if AC_SESSION_ID != 263
+					#error "AC_SESSION_ID definition mismatch"
+					#endif
+					0, 					/* Vendor */
+					"Session-Id", 				/* Name */
+					AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, 	/* Fixed flags */
+					AVP_FLAG_MANDATORY,			/* Fixed flag values */
+					AVP_TYPE_OCTETSTRING 			/* base type of data */
+					};
+			CHECK_dict_new( DICT_AVP, &data , UTF8String_type, NULL);
+		}
+		
+		/* Authorization-Lifetime */
+		{
+			/*
+				The Authorization-Lifetime AVP (AVP Code 291) is of type Unsigned32
+				and contains the maximum number of seconds of service to be provided
+				to the user before the user is to be re-authenticated and/or re-
+				authorized.  Great care should be taken when the Authorization-
+				Lifetime value is determined, since a low, non-zero, value could
+				create significant Diameter traffic, which could congest both the
+				network and the agents.
+
+				A value of zero (0) means that immediate re-auth is necessary by the
+				access device.  This is typically used in cases where multiple
+				authentication methods are used, and a successful auth response with
+				this AVP set to zero is used to signal that the next authentication
+				method is to be immediately initiated.  The absence of this AVP, or a
+				value of all ones (meaning all bits in the 32 bit field are set to
+				one) means no re-auth is expected.
+
+				If both this AVP and the Session-Timeout AVP are present in a
+				message, the value of the latter MUST NOT be smaller than the
+				Authorization-Lifetime AVP.
+
+				An Authorization-Lifetime AVP MAY be present in re-authorization
+				messages, and contains the number of seconds the user is authorized
+				to receive service from the time the re-auth answer message is
+				received by the access device.
+
+				This AVP MAY be provided by the client as a hint of the maximum
+				lifetime that it is willing to accept.  However, the server MAY
+				return a value that is equal to, or smaller, than the one provided by
+				the client.
+			*/
+			struct dict_avp_data data = { 
+					291, 					/* Code */
+					0, 					/* Vendor */
+					"Authorization-Lifetime", 		/* Name */
+					AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, 	/* Fixed flags */
+					AVP_FLAG_MANDATORY,			/* Fixed flag values */
+					AVP_TYPE_UNSIGNED32 			/* base type of data */
+					};
+			CHECK_dict_new( DICT_AVP, &data , NULL, NULL);
+		}
+		
+		/* Auth-Grace-Period */
+		{
+			/*
+				The Auth-Grace-Period AVP (AVP Code 276) is of type Unsigned32 and
+				contains the number of seconds the Diameter server will wait
+				following the expiration of the Authorization-Lifetime AVP before
+				cleaning up resources for the session.
+			*/
+			struct dict_avp_data data = { 
+					276, 					/* Code */
+					0, 					/* Vendor */
+					"Auth-Grace-Period", 			/* Name */
+					AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, 	/* Fixed flags */
+					AVP_FLAG_MANDATORY,			/* Fixed flag values */
+					AVP_TYPE_UNSIGNED32 			/* base type of data */
+					};
+			CHECK_dict_new( DICT_AVP, &data , NULL, NULL);
+		}
+		
+		/* Auth-Session-State */
+		{
+			/*
+				The Auth-Session-State AVP (AVP Code 277) is of type Enumerated and
+				specifies whether state is maintained for a particular session.  The
+				client MAY include this AVP in requests as a hint to the server, but
+				the value in the server's answer message is binding.  The following
+				values are supported:
+
+
+				STATE_MAINTAINED 0
+
+				This value is used to specify that session state is being
+				maintained, and the access device MUST issue a session termination
+				message when service to the user is terminated.  This is the
+				default value.
+
+
+				NO_STATE_MAINTAINED 1
+
+				This value is used to specify that no session termination messages
+				will be sent by the access device upon expiration of the
+				Authorization-Lifetime.
+			*/
+			struct dict_object 	* 	type;
+			struct dict_type_data	 	tdata = { AVP_TYPE_INTEGER32,	"Enumerated(Auth-Session-State)"	, NULL, NULL, NULL };
+			struct dict_enumval_data 	t_0 = { "STATE_MAINTAINED", 		{ .i32 = 0 }};
+			struct dict_enumval_data 	t_1 = { "NO_STATE_MAINTAINED", 		{ .i32 = 1 }};
+			struct dict_avp_data	 	data = { 
+					277, 					/* Code */
+					0, 					/* Vendor */
+					"Auth-Session-State", 			/* Name */
+					AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, 	/* Fixed flags */
+					AVP_FLAG_MANDATORY,			/* Fixed flag values */
+					AVP_TYPE_INTEGER32 			/* base type of data */
+					};
+			/* Create the Enumerated type, and then the AVP */
+			CHECK_dict_new( DICT_TYPE, &tdata , NULL, &type);
+			CHECK_dict_new( DICT_ENUMVAL, &t_0 , type, NULL);
+			CHECK_dict_new( DICT_ENUMVAL, &t_1 , type, NULL);
+			CHECK_dict_new( DICT_AVP, &data , type, NULL);
+		}
+		
+		/* Re-Auth-Request-Type */
+		{
+			/*
+				The Re-Auth-Request-Type AVP (AVP Code 285) is of type Enumerated and
+				is included in application-specific auth answers to inform the client
+				of the action expected upon expiration of the Authorization-Lifetime.
+				If the answer message contains an Authorization-Lifetime AVP with a
+				positive value, the Re-Auth-Request-Type AVP MUST be present in an
+				answer message.  The following values are defined:
+
+
+				AUTHORIZE_ONLY 0
+
+				An authorization only re-auth is expected upon expiration of the
+				Authorization-Lifetime.  This is the default value if the AVP is
+				not present in answer messages that include the Authorization-
+				Lifetime.
+
+
+				AUTHORIZE_AUTHENTICATE 1
+
+				An authentication and authorization re-auth is expected upon
+				expiration of the Authorization-Lifetime.
+			*/
+			struct dict_object 	* 	type;
+			struct dict_type_data	 	tdata = { AVP_TYPE_INTEGER32,	"Enumerated(Re-Auth-Request-Type)"	, NULL, NULL, NULL };
+			struct dict_enumval_data 	t_0 = { "AUTHORIZE_ONLY", 		{ .i32 = 0 }};
+			struct dict_enumval_data 	t_1 = { "AUTHORIZE_AUTHENTICATE",	{ .i32 = 1 }};
+			struct dict_avp_data	 	data = { 
+					285, 					/* Code */
+					0, 					/* Vendor */
+					"Re-Auth-Request-Type",			/* Name */
+					AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, 	/* Fixed flags */
+					AVP_FLAG_MANDATORY,			/* Fixed flag values */
+					AVP_TYPE_INTEGER32 			/* base type of data */
+					};
+			/* Create the Enumerated type, and then the AVP */
+			CHECK_dict_new( DICT_TYPE, &tdata , NULL, &type);
+			CHECK_dict_new( DICT_ENUMVAL, &t_0 , type, NULL);
+			CHECK_dict_new( DICT_ENUMVAL, &t_1 , type, NULL);
+			CHECK_dict_new( DICT_AVP, &data , type, NULL);
+		}
+		
+		/* Session-Timeout */
+		{
+			/*
+				The Session-Timeout AVP (AVP Code 27) [RFC2865] is of type Unsigned32
+				and contains the maximum number of seconds of service to be provided
+				to the user before termination of the session.  When both the
+				Session-Timeout and the Authorization-Lifetime AVPs are present in an
+				answer message, the former MUST be equal to or greater than the value
+				of the latter.
+
+				A session that terminates on an access device due to the expiration
+				of the Session-Timeout MUST cause an STR to be issued, unless both
+				the access device and the home server had previously agreed that no
+				session termination messages would be sent (see Section 8.11).
+
+				A Session-Timeout AVP MAY be present in a re-authorization answer
+				message, and contains the remaining number of seconds from the
+				beginning of the re-auth.
+
+				A value of zero, or the absence of this AVP, means that this session
+				has an unlimited number of seconds before termination.
+
+				This AVP MAY be provided by the client as a hint of the maximum
+				timeout that it is willing to accept.  However, the server MAY return
+				a value that is equal to, or smaller, than the one provided by the
+				client.
+			*/
+			struct dict_avp_data data = { 
+					27, 					/* Code */
+					0, 					/* Vendor */
+					"Session-Timeout", 			/* Name */
+					AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, 	/* Fixed flags */
+					AVP_FLAG_MANDATORY,			/* Fixed flag values */
+					AVP_TYPE_UNSIGNED32 			/* base type of data */
+					};
+			CHECK_dict_new( DICT_AVP, &data , NULL, NULL);
+		}
+		
+		/* User-Name */
+		{
+			/*
+				The User-Name AVP (AVP Code 1) [RFC2865] is of type UTF8String, which
+				contains the User-Name, in a format consistent with the NAI
+				specification [RFC4282].
+			*/
+			struct dict_avp_data data = { 
+					1, 					/* Code */
+					0, 					/* Vendor */
+					"User-Name", 				/* Name */
+					AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, 	/* Fixed flags */
+					AVP_FLAG_MANDATORY,			/* Fixed flag values */
+					AVP_TYPE_OCTETSTRING 			/* base type of data */
+					};
+			CHECK_dict_new( DICT_AVP, &data , UTF8String_type, NULL);
+		}
+		
+		/* Termination-Cause */
+		{
+			/*
+				The Termination-Cause AVP (AVP Code 295) is of type Enumerated, and
+				is used to indicate the reason why a session was terminated on the
+				access device.  The following values are defined:
+
+
+				DIAMETER_LOGOUT 1
+
+				The user initiated a disconnect
+
+
+				DIAMETER_SERVICE_NOT_PROVIDED 2
+
+				This value is used when the user disconnected prior to the receipt
+				of the authorization answer message.
+
+
+				DIAMETER_BAD_ANSWER 3
+
+				This value indicates that the authorization answer received by the
+				access device was not processed successfully.
+
+
+				DIAMETER_ADMINISTRATIVE 4
+
+				The user was not granted access, or was disconnected, due to
+				administrative reasons, such as the receipt of a Abort-Session-
+				Request message.
+
+
+				DIAMETER_LINK_BROKEN 5
+
+				The communication to the user was abruptly disconnected.
+
+
+				DIAMETER_AUTH_EXPIRED 6
+
+				The user's access was terminated since its authorized session time
+				has expired.
+
+
+				DIAMETER_USER_MOVED 7
+
+				The user is receiving services from another access device.
+
+
+				DIAMETER_SESSION_TIMEOUT 8
+
+				The user's session has timed out, and service has been terminated.
+			*/
+			struct dict_object 	* 	type;
+			struct dict_type_data	 	tdata = { AVP_TYPE_INTEGER32,	"Enumerated(Termination-Cause)"	, NULL, NULL, NULL };
+			struct dict_enumval_data 	t_1 = { "DIAMETER_LOGOUT",			{ .i32 = 1 }};
+			struct dict_enumval_data 	t_2 = { "DIAMETER_SERVICE_NOT_PROVIDED", 	{ .i32 = 2 }};
+			struct dict_enumval_data 	t_3 = { "DIAMETER_BAD_ANSWER",			{ .i32 = 3 }};
+			struct dict_enumval_data 	t_4 = { "DIAMETER_ADMINISTRATIVE", 		{ .i32 = 4 }};
+			struct dict_enumval_data 	t_5 = { "DIAMETER_LINK_BROKEN",			{ .i32 = 5 }};
+			struct dict_enumval_data 	t_6 = { "DIAMETER_AUTH_EXPIRED", 		{ .i32 = 6 }};
+			struct dict_enumval_data 	t_7 = { "DIAMETER_USER_MOVED",			{ .i32 = 7 }};
+			struct dict_enumval_data 	t_8 = { "DIAMETER_SESSION_TIMEOUT", 		{ .i32 = 8 }};
+			struct dict_avp_data 	data = { 
+					295, 					/* Code */
+					0, 					/* Vendor */
+					"Termination-Cause",			/* Name */
+					AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, 	/* Fixed flags */
+					AVP_FLAG_MANDATORY,			/* Fixed flag values */
+					AVP_TYPE_INTEGER32 			/* base type of data */
+					};
+			/* Create the Enumerated type, and then the AVP */
+			CHECK_dict_new( DICT_TYPE, &tdata , NULL, &type);
+			CHECK_dict_new( DICT_ENUMVAL, &t_1 , type, NULL);
+			CHECK_dict_new( DICT_ENUMVAL, &t_2 , type, NULL);
+			CHECK_dict_new( DICT_ENUMVAL, &t_3 , type, NULL);
+			CHECK_dict_new( DICT_ENUMVAL, &t_4 , type, NULL);
+			CHECK_dict_new( DICT_ENUMVAL, &t_5 , type, NULL);
+			CHECK_dict_new( DICT_ENUMVAL, &t_6 , type, NULL);
+			CHECK_dict_new( DICT_ENUMVAL, &t_7 , type, NULL);
+			CHECK_dict_new( DICT_ENUMVAL, &t_8 , type, NULL);
+			CHECK_dict_new( DICT_AVP, &data , type, NULL);
+		}
+		
+		/* Origin-State-Id */
+		{
+			/*
+				The Origin-State-Id AVP (AVP Code 278), of type Unsigned32, is a
+				monotonically increasing value that is advanced whenever a Diameter
+				entity restarts with loss of previous state, for example upon reboot.
+				Origin-State-Id MAY be included in any Diameter message, including
+				CER.
+
+				A Diameter entity issuing this AVP MUST create a higher value for
+				this AVP each time its state is reset.  A Diameter entity MAY set
+				Origin-State-Id to the time of startup, or it MAY use an incrementing
+				counter retained in non-volatile memory across restarts.
+
+				The Origin-State-Id, if present, MUST reflect the state of the entity
+				indicated by Origin-Host.  If a proxy modifies Origin-Host, it MUST
+				either remove Origin-State-Id or modify it appropriately as well.
+				Typically, Origin-State-Id is used by an access device that always
+				starts up with no active sessions; that is, any session active prior
+				to restart will have been lost.  By including Origin-State-Id in a
+				message, it allows other Diameter entities to infer that sessions
+				associated with a lower Origin-State-Id are no longer active.  If an
+				access device does not intend for such inferences to be made, it MUST
+				either not include Origin-State-Id in any message, or set its value
+				to 0.
+			*/
+			struct dict_avp_data data = { 
+					278, 					/* Code */
+					0, 					/* Vendor */
+					"Origin-State-Id", 			/* Name */
+					AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, 	/* Fixed flags */
+					AVP_FLAG_MANDATORY,			/* Fixed flag values */
+					AVP_TYPE_UNSIGNED32 			/* base type of data */
+					};
+			CHECK_dict_new( DICT_AVP, &data , NULL, NULL);
+		}
+		
+		/* Session-Binding */
+		{
+			/*
+				The Session-Binding AVP (AVP Code 270) is of type Unsigned32, and MAY
+				be present in application-specific authorization answer messages.  If
+				present, this AVP MAY inform the Diameter client that all future
+				application-specific re-auth messages for this session MUST be sent
+				to the same authorization server.  This AVP MAY also specify that a
+				Session-Termination-Request message for this session MUST be sent to
+				the same authorizing server.
+
+				This field is a bit mask, and the following bits have been defined:
+
+
+				RE_AUTH 1
+
+				When set, future re-auth messages for this session MUST NOT
+				include the Destination-Host AVP.  When cleared, the default
+				value, the Destination-Host AVP MUST be present in all re-auth
+				messages for this session.
+
+
+				STR 2
+
+				When set, the STR message for this session MUST NOT include the
+				Destination-Host AVP.  When cleared, the default value, the
+				Destination-Host AVP MUST be present in the STR message for this
+				session.
+
+
+				ACCOUNTING 4
+
+				When set, all accounting messages for this session MUST NOT
+				include the Destination-Host AVP.  When cleared, the default
+				value, the Destination-Host AVP, if known, MUST be present in all
+				accounting messages for this session.
+			*/
+			
+			/* Although the RFC does not specify an "Enumerated" type here, we go forward and create one.
+			 * This is the reason for the "*" in the type name
+			 * The actual values of the AVP will not always be defined here, but at least it can be used in some cases.
+			 *  ... maybe the code will be changed later to support bitfields AVP ...
+			 */
+			
+			struct dict_object 	* 	type;
+			struct dict_type_data	 	tdata = { AVP_TYPE_UNSIGNED32,	"Enumerated(Session-Binding)"	, NULL, NULL, NULL };
+			struct dict_enumval_data 	t_1 = { "RE_AUTH", 		{ .u32 = 1 }};
+			struct dict_enumval_data 	t_2 = { "STR", 			{ .u32 = 2 }};
+			struct dict_enumval_data 	t_4 = { "ACCOUNTING", 		{ .u32 = 4 }};
+			struct dict_avp_data 	data = { 
+					270, 					/* Code */
+					0, 					/* Vendor */
+					"Session-Binding", 			/* Name */
+					AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, 	/* Fixed flags */
+					AVP_FLAG_MANDATORY,			/* Fixed flag values */
+					AVP_TYPE_UNSIGNED32 			/* base type of data */
+					};
+			/* Create the Enumerated type, and then the AVP */
+			CHECK_dict_new( DICT_TYPE, &tdata , NULL, &type);
+			CHECK_dict_new( DICT_ENUMVAL, &t_1 , type, NULL);
+			CHECK_dict_new( DICT_ENUMVAL, &t_2 , type, NULL);
+			CHECK_dict_new( DICT_ENUMVAL, &t_4 , type, NULL);
+			CHECK_dict_new( DICT_AVP, &data , type, NULL);
+		}
+		
+		/* Session-Server-Failover */
+		{
+			/*
+				The Session-Server-Failover AVP (AVP Code 271) is of type Enumerated,
+				and MAY be present in application-specific authorization answer
+				messages that either do not include the Session-Binding AVP or
+				include the Session-Binding AVP with any of the bits set to a zero
+				value.  If present, this AVP MAY inform the Diameter client that if a
+				re-auth or STR message fails due to a delivery problem, the Diameter
+				client SHOULD issue a subsequent message without the Destination-Host
+				AVP.  When absent, the default value is REFUSE_SERVICE.
+
+				The following values are supported:
+
+
+				REFUSE_SERVICE 0
+
+				If either the re-auth or the STR message delivery fails, terminate
+				service with the user, and do not attempt any subsequent attempts.
+
+
+				TRY_AGAIN 1
+
+				If either the re-auth or the STR message delivery fails, resend
+				the failed message without the Destination-Host AVP present.
+
+
+				ALLOW_SERVICE 2
+
+				If re-auth message delivery fails, assume that re-authorization
+				succeeded.  If STR message delivery fails, terminate the session.
+
+
+				TRY_AGAIN_ALLOW_SERVICE 3
+
+				If either the re-auth or the STR message delivery fails, resend
+				the failed message without the Destination-Host AVP present.  If
+				the second delivery fails for re-auth, assume re-authorization
+				succeeded.  If the second delivery fails for STR, terminate the
+				session.
+			*/
+			struct dict_object  	* 	type;
+			struct dict_type_data	 	tdata = { AVP_TYPE_INTEGER32,	"Enumerated(Session-Server-Failover)"	, NULL, NULL, NULL };
+			struct dict_enumval_data 	t_0 = { "REFUSE_SERVICE", 		{ .i32 = 0 }};
+			struct dict_enumval_data 	t_1 = { "TRY_AGAIN",			{ .i32 = 1 }};
+			struct dict_enumval_data 	t_2 = { "ALLOW_SERVICE", 		{ .i32 = 2 }};
+			struct dict_enumval_data 	t_3 = { "TRY_AGAIN_ALLOW_SERVICE",	{ .i32 = 3 }};
+			struct dict_avp_data	 	data = { 
+					271, 					/* Code */
+					0, 					/* Vendor */
+					"Session-Server-Failover",		/* Name */
+					AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, 	/* Fixed flags */
+					AVP_FLAG_MANDATORY,			/* Fixed flag values */
+					AVP_TYPE_INTEGER32 			/* base type of data */
+					};
+			/* Create the Enumerated type, and then the AVP */
+			CHECK_dict_new( DICT_TYPE, &tdata , NULL, &type);
+			CHECK_dict_new( DICT_ENUMVAL, &t_0 , type, NULL);
+			CHECK_dict_new( DICT_ENUMVAL, &t_1 , type, NULL);
+			CHECK_dict_new( DICT_ENUMVAL, &t_2 , type, NULL);
+			CHECK_dict_new( DICT_ENUMVAL, &t_3 , type, NULL);
+			CHECK_dict_new( DICT_AVP, &data , type, NULL);
+		}
+		
+		/* Multi-Round-Time-Out */
+		{
+			/*
+				The Multi-Round-Time-Out AVP (AVP Code 272) is of type Unsigned32,
+				and SHOULD be present in application-specific authorization answer
+				messages whose Result-Code AVP is set to DIAMETER_MULTI_ROUND_AUTH.
+				This AVP contains the maximum number of seconds that the access
+				device MUST provide the user in responding to an authentication
+				request.
+			*/
+			struct dict_avp_data data = { 
+					272, 					/* Code */
+					0, 					/* Vendor */
+					"Multi-Round-Time-Out", 			/* Name */
+					AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, 	/* Fixed flags */
+					AVP_FLAG_MANDATORY,			/* Fixed flag values */
+					AVP_TYPE_UNSIGNED32 			/* base type of data */
+					};
+			CHECK_dict_new( DICT_AVP, &data , NULL, NULL);
+		}
+		
+		/* Class */
+		{
+			/*
+				The Class AVP (AVP Code 25) is of type OctetString and is used to by
+				Diameter servers to return state information to the access device.
+				When one or more Class AVPs are present in application-specific
+				authorization answer messages, they MUST be present in subsequent re-
+				authorization, session termination and accounting messages.  Class
+				AVPs found in a re-authorization answer message override the ones
+				found in any previous authorization answer message.  Diameter server
+				implementations SHOULD NOT return Class AVPs that require more than
+				4096 bytes of storage on the Diameter client.  A Diameter client that
+				receives Class AVPs whose size exceeds local available storage MUST
+				terminate the session.
+			*/
+			struct dict_avp_data data = { 
+					25, 					/* Code */
+					0, 					/* Vendor */
+					"Class", 				/* Name */
+					AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, 	/* Fixed flags */
+					AVP_FLAG_MANDATORY,			/* Fixed flag values */
+					AVP_TYPE_OCTETSTRING 			/* base type of data */
+					};
+			CHECK_dict_new( DICT_AVP, &data , NULL, NULL);
+		}
+		
+		/* Event-Timestamp */
+		{
+			/*
+				The Event-Timestamp (AVP Code 55) is of type Time, and MAY be
+				included in an Accounting-Request and Accounting-Answer messages to
+				record the time that the reported event occurred, in seconds since
+				January 1, 1900 00:00 UTC.
+			*/
+			struct dict_avp_data data = { 
+					55, 					/* Code */
+					0, 					/* Vendor */
+					"Event-Timestamp", 			/* Name */
+					AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, 	/* Fixed flags */
+					AVP_FLAG_MANDATORY,			/* Fixed flag values */
+					AVP_TYPE_OCTETSTRING 			/* base type of data */
+					};
+			CHECK_dict_new( DICT_AVP, &data , Time_type, NULL);
+		}
+
+				
+		/* Accounting-Record-Type */
+		{
+			/*
+				The Accounting-Record-Type AVP (AVP Code 480) is of type Enumerated
+				and contains the type of accounting record being sent.  The following
+				values are currently defined for the Accounting-Record-Type AVP:
+
+
+				EVENT_RECORD 1
+
+				An Accounting Event Record is used to indicate that a one-time
+				event has occurred (meaning that the start and end of the event
+				are simultaneous).  This record contains all information relevant
+				to the service, and is the only record of the service.
+
+
+				START_RECORD 2
+
+				An Accounting Start, Interim, and Stop Records are used to
+				indicate that a service of a measurable length has been given.  An
+				Accounting Start Record is used to initiate an accounting session,
+				and contains accounting information that is relevant to the
+				initiation of the session.
+
+
+				INTERIM_RECORD 3
+
+				An Interim Accounting Record contains cumulative accounting
+				information for an existing accounting session.  Interim
+				Accounting Records SHOULD be sent every time a re-authentication
+				or re-authorization occurs.  Further, additional interim record
+				triggers MAY be defined by application-specific Diameter
+				applications.  The selection of whether to use INTERIM_RECORD
+				records is done by the Acct-Interim-Interval AVP.
+
+
+				STOP_RECORD 4
+
+				An Accounting Stop Record is sent to terminate an accounting
+				session and contains cumulative accounting information relevant to
+				the existing session.
+			*/
+			struct dict_object 	* 	type;
+			struct dict_type_data	  	tdata = { AVP_TYPE_INTEGER32,	"Enumerated(Accounting-Record-Type)"	, NULL, NULL, NULL };
+			struct dict_enumval_data 	t_1 = { "EVENT_RECORD",			{ .i32 = 1 }};
+			struct dict_enumval_data 	t_2 = { "START_RECORD", 		{ .i32 = 2 }};
+			struct dict_enumval_data 	t_3 = { "INTERIM_RECORD",		{ .i32 = 3 }};
+			struct dict_enumval_data 	t_4 = { "STOP_RECORD", 			{ .i32 = 4 }};
+			struct dict_avp_data 	data = { 
+					480, 					/* Code */
+					0, 					/* Vendor */
+					"Accounting-Record-Type",		/* Name */
+					AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, 	/* Fixed flags */
+					AVP_FLAG_MANDATORY,			/* Fixed flag values */
+					AVP_TYPE_INTEGER32 			/* base type of data */
+					};
+			/* Create the Enumerated type, and then the AVP */
+			CHECK_dict_new( DICT_TYPE, &tdata , NULL, &type);
+			CHECK_dict_new( DICT_ENUMVAL, &t_1 , type, NULL);
+			CHECK_dict_new( DICT_ENUMVAL, &t_2 , type, NULL);
+			CHECK_dict_new( DICT_ENUMVAL, &t_3 , type, NULL);
+			CHECK_dict_new( DICT_ENUMVAL, &t_4 , type, NULL);
+			CHECK_dict_new( DICT_AVP, &data , type, NULL);
+		}
+		
+		/* Acct-Interim-Interval */
+		{
+			/*
+				The Acct-Interim-Interval AVP (AVP Code 85) is of type Unsigned32 and
+				is sent from the Diameter home authorization server to the Diameter
+				client.  The client uses information in this AVP to decide how and
+				when to produce accounting records.  With different values in this
+				AVP, service sessions can result in one, two, or two+N accounting
+				records, based on the needs of the home-organization.  The following
+				accounting record production behavior is directed by the inclusion of
+				this AVP:
+
+
+				1.  The omission of the Acct-Interim-Interval AVP or its inclusion
+				with Value field set to 0 means that EVENT_RECORD, START_RECORD,
+				and STOP_RECORD are produced, as appropriate for the service.
+
+
+				2.  The inclusion of the AVP with Value field set to a non-zero value
+				means that INTERIM_RECORD records MUST be produced between the
+				START_RECORD and STOP_RECORD records.  The Value field of this
+				AVP is the nominal interval between these records in seconds.
+
+				The Diameter node that originates the accounting information,
+				known as the client, MUST produce the first INTERIM_RECORD record
+				roughly at the time when this nominal interval has elapsed from
+				the START_RECORD, the next one again as the interval has elapsed
+				once more, and so on until the session ends and a STOP_RECORD
+				record is produced.
+
+				The client MUST ensure that the interim record production times
+				are randomized so that large accounting message storms are not
+				created either among records or around a common service start
+				time.
+			*/
+			struct dict_avp_data data = { 
+					85, 					/* Code */
+					0, 					/* Vendor */
+					"Acct-Interim-Interval", 		/* Name */
+					AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, 	/* Fixed flags */
+					AVP_FLAG_MANDATORY,			/* Fixed flag values */
+					AVP_TYPE_UNSIGNED32 			/* base type of data */
+					};
+			CHECK_dict_new( DICT_AVP, &data , NULL, NULL);
+		}
+		
+		/* Accounting-Record-Number */
+		{
+			/*
+				The Accounting-Record-Number AVP (AVP Code 485) is of type Unsigned32
+				and identifies this record within one session.  As Session-Id AVPs
+				are globally unique, the combination of Session-Id and Accounting-
+				Record-Number AVPs is also globally unique, and can be used in
+				matching accounting records with confirmations.  An easy way to
+				produce unique numbers is to set the value to 0 for records of type
+				EVENT_RECORD and START_RECORD, and set the value to 1 for the first
+				INTERIM_RECORD, 2 for the second, and so on until the value for
+				STOP_RECORD is one more than for the last INTERIM_RECORD.
+			*/
+			struct dict_avp_data data = { 
+					485, 					/* Code */
+					0, 					/* Vendor */
+					"Accounting-Record-Number", 		/* Name */
+					AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, 	/* Fixed flags */
+					AVP_FLAG_MANDATORY,			/* Fixed flag values */
+					AVP_TYPE_UNSIGNED32 			/* base type of data */
+					};
+			CHECK_dict_new( DICT_AVP, &data , NULL, NULL);
+		}
+		
+		/* Acct-Session-Id */
+		{
+			/*
+				The Acct-Session-Id AVP (AVP Code 44) is of type OctetString is only
+				used when RADIUS/Diameter translation occurs.  This AVP contains the
+				contents of the RADIUS Acct-Session-Id attribute.
+			*/
+			struct dict_avp_data data = { 
+					44, 					/* Code */
+					0, 					/* Vendor */
+					"Acct-Session-Id", 			/* Name */
+					AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, 	/* Fixed flags */
+					AVP_FLAG_MANDATORY,			/* Fixed flag values */
+					AVP_TYPE_OCTETSTRING 			/* base type of data */
+					};
+			CHECK_dict_new( DICT_AVP, &data , NULL, NULL);
+		}
+		
+		/* Acct-Multi-Session-Id */
+		{
+			/*
+				The Acct-Multi-Session-Id AVP (AVP Code 50) is of type UTF8String,
+				following the format specified in Section 8.8.  The Acct-Multi-
+				Session-Id AVP is used to link together multiple related accounting
+				sessions, where each session would have a unique Session-Id, but the
+				same Acct-Multi-Session-Id AVP.  This AVP MAY be returned by the
+				Diameter server in an authorization answer, and MUST be used in all
+				accounting messages for the given session.
+			*/
+			struct dict_avp_data data = { 
+					50, 					/* Code */
+					0, 					/* Vendor */
+					"Acct-Multi-Session-Id", 		/* Name */
+					AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, 	/* Fixed flags */
+					AVP_FLAG_MANDATORY,			/* Fixed flag values */
+					AVP_TYPE_OCTETSTRING 			/* base type of data */
+					};
+			CHECK_dict_new( DICT_AVP, &data , UTF8String_type, NULL);
+		}
+		
+		/* Accounting-Sub-Session-Id */
+		{
+			/*
+				The Accounting-Sub-Session-Id AVP (AVP Code 287) is of type
+				Unsigned64 and contains the accounting sub-session identifier.  The
+				combination of the Session-Id and this AVP MUST be unique per sub-
+				session, and the value of this AVP MUST be monotonically increased by
+				one for all new sub-sessions.  The absence of this AVP implies no
+				sub-sessions are in use, with the exception of an Accounting-Request
+				whose Accounting-Record-Type is set to STOP_RECORD.  A STOP_RECORD
+				message with no Accounting-Sub-Session-Id AVP present will signal the
+				termination of all sub-sessions for a given Session-Id.
+			*/
+			struct dict_avp_data data = { 
+					287, 					/* Code */
+					0, 					/* Vendor */
+					"Accounting-Sub-Session-Id", 		/* Name */
+					AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, 	/* Fixed flags */
+					AVP_FLAG_MANDATORY,			/* Fixed flag values */
+					AVP_TYPE_UNSIGNED64 			/* base type of data */
+					};
+			CHECK_dict_new( DICT_AVP, &data , NULL, NULL);
+		}
+		
+		/* Accounting-Realtime-Required */
+		{
+			/*
+				The Accounting-Realtime-Required AVP (AVP Code 483) is of type
+				Enumerated and is sent from the Diameter home authorization server to
+				the Diameter client or in the Accounting-Answer from the accounting
+				server.  The client uses information in this AVP to decide what to do
+				if the sending of accounting records to the accounting server has
+				been temporarily prevented due to, for instance, a network problem.
+
+
+				DELIVER_AND_GRANT 1
+
+				The AVP with Value field set to DELIVER_AND_GRANT means that the
+				service MUST only be granted as long as there is a connection to
+				an accounting server.  Note that the set of alternative accounting
+				servers are treated as one server in this sense.  Having to move
+				the accounting record stream to a backup server is not a reason to
+				discontinue the service to the user.
+
+
+				GRANT_AND_STORE 2
+
+				The AVP with Value field set to GRANT_AND_STORE means that service
+				SHOULD be granted if there is a connection, or as long as records
+				can still be stored as described in Section 9.4.
+
+				This is the default behavior if the AVP isn't included in the
+				reply from the authorization server.
+
+
+				GRANT_AND_LOSE 3
+
+				The AVP with Value field set to GRANT_AND_LOSE means that service
+				SHOULD be granted even if the records can not be delivered or
+				stored.
+			*/
+			struct dict_object  	* 	type;
+			struct dict_type_data	 	tdata = { AVP_TYPE_INTEGER32,	"Enumerated(Accounting-Realtime-Required)"	, NULL, NULL, NULL };
+			struct dict_enumval_data 	t_1 = { "DELIVER_AND_GRANT",		{ .i32 = 1 }};
+			struct dict_enumval_data 	t_2 = { "GRANT_AND_STORE", 		{ .i32 = 2 }};
+			struct dict_enumval_data 	t_3 = { "GRANT_AND_LOSE",		{ .i32 = 3 }};
+			struct dict_avp_data 		data = { 
+					483, 					/* Code */
+					0, 					/* Vendor */
+					"Accounting-Realtime-Required",		/* Name */
+					AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, 	/* Fixed flags */
+					AVP_FLAG_MANDATORY,			/* Fixed flag values */
+					AVP_TYPE_INTEGER32 			/* base type of data */
+					};
+			/* Create the Enumerated type, and then the AVP */
+			CHECK_dict_new( DICT_TYPE, &tdata , NULL, &type);
+			CHECK_dict_new( DICT_ENUMVAL, &t_1 , type, NULL);
+			CHECK_dict_new( DICT_ENUMVAL, &t_2 , type, NULL);
+			CHECK_dict_new( DICT_ENUMVAL, &t_3 , type, NULL);
+			CHECK_dict_new( DICT_AVP, &data , type, NULL);
+		}
+		
+	}
+	
+	/* Commands section */
+	{
+		/* To avoid defining global variables for all the AVP that we use here, we do search the dictionary in each sub-block.
+		 * This is far from optimal, but the code is clearer like this, and the time it requires at execution is not noticeable.
+		 */
+		
+		/* Generic message syntax when the 'E' bit is set */
+		{
+			/*
+				The 'E' (Error Bit) in the Diameter header is set when the request
+				caused a protocol-related error (see Section 7.1.3).  A message with
+				the 'E' bit MUST NOT be sent as a response to an answer message.
+				Note that a message with the 'E' bit set is still subjected to the
+				processing rules defined in Section 6.2.  When set, the answer
+				message will not conform to the ABNF specification for the command,
+				and will instead conform to the following ABNF:
+
+				Message Format
+
+				<answer-message> ::= < Diameter Header: code, ERR [PXY] >
+                				0*1< Session-Id >
+                				   { Origin-Host }
+                				   { Origin-Realm }
+                				   { Result-Code }
+                				   [ Origin-State-Id ]
+                				   [ Error-Message ]
+                				   [ Error-Reporting-Host ]
+                				   [ Failed-AVP ]
+                				 * [ Proxy-Info ]
+                				 * [ AVP ]
+
+				Note that the code used in the header is the same than the one found
+				in the request message, but with the 'R' bit cleared and the 'E' bit
+				set.  The 'P' bit in the header is set to the same value as the one
+				found in the request message.
+			*/
+			struct dict_object * cmd_error;
+			struct local_rules_definition rules[] = 
+						{ 	 {  "Session-Id", 			RULE_FIXED_HEAD,0, 1 }
+							,{  "Origin-Host",			RULE_REQUIRED, -1, 1 }
+							,{  "Origin-Realm",			RULE_REQUIRED, -1, 1 }
+							,{  "Result-Code",			RULE_REQUIRED, -1, 1 }
+							,{  "Origin-State-Id",			RULE_OPTIONAL, -1, 1 }
+							,{  "Error-Message",			RULE_OPTIONAL, -1, 1 }
+							,{  "Error-Reporting-Host",		RULE_OPTIONAL, -1, 1 }
+							,{  "Failed-AVP",			RULE_OPTIONAL, -1, 1 }
+							,{  "Proxy-Info",			RULE_OPTIONAL, -1,-1 }
+						};
+			CHECK_FCT( fd_dict_get_error_cmd(dict, &cmd_error) );
+			PARSE_loc_rules( rules, cmd_error );
+		}		
+		
+		/* Capabilities-Exchange-Request */
+		{
+			/*
+				The Capabilities-Exchange-Request (CER), indicated by the Command-
+				Code set to 257 and the Command Flags' 'R' bit set, is sent to
+				exchange local capabilities.  Upon detection of a transport failure,
+				this message MUST NOT be sent to an alternate peer.
+
+				When Diameter is run over SCTP [RFC2960], which allows for
+				connections to span multiple interfaces and multiple IP addresses,
+				the Capabilities-Exchange-Request message MUST contain one Host-IP-
+				Address AVP for each potential IP address that MAY be locally used
+				when transmitting Diameter messages.
+
+				Message Format
+
+				 <CER> ::= < Diameter Header: 257, REQ >
+        				   { Origin-Host }
+        				   { Origin-Realm }
+        				1* { Host-IP-Address }
+        				   { Vendor-Id }
+        				   { Product-Name }
+        				   [ Origin-State-Id ]
+        				 * [ Supported-Vendor-Id ]
+        				 * [ Auth-Application-Id ]
+        				 * [ Inband-Security-Id ]
+        				 * [ Acct-Application-Id ]
+        				 * [ Vendor-Specific-Application-Id ]
+        				   [ Firmware-Revision ]
+        				 * [ AVP ]
+			*/
+			struct dict_object * cmd;
+			struct dict_cmd_data data = { 
+					257, 					/* Code */
+					#if CC_CAPABILITIES_EXCHANGE != 257
+					#error "CC_CAPABILITIES_EXCHANGE definition mismatch"
+					#endif
+					"Capabilities-Exchange-Request", 	/* Name */
+					CMD_FLAG_REQUEST | CMD_FLAG_PROXIABLE | CMD_FLAG_RETRANSMIT | CMD_FLAG_ERROR, 	/* Fixed flags */
+					CMD_FLAG_REQUEST 			/* Fixed flag values */
+					};
+			struct local_rules_definition rules[] = 
+						{ 	 {  "Origin-Host", 			RULE_REQUIRED, -1, 1 }
+							,{  "Origin-Realm",			RULE_REQUIRED, -1, 1 }
+							,{  "Host-IP-Address",			RULE_REQUIRED, -1,-1 }
+							,{  "Vendor-Id",			RULE_REQUIRED, -1, 1 }
+							,{  "Product-Name",			RULE_REQUIRED, -1, 1 }
+							,{  "Origin-State-Id",			RULE_OPTIONAL, -1, 1 }
+							,{  "Supported-Vendor-Id",		RULE_OPTIONAL, -1,-1 }
+							,{  "Auth-Application-Id",		RULE_OPTIONAL, -1,-1 }
+							,{  "Inband-Security-Id",		RULE_OPTIONAL, -1,-1 }
+							,{  "Acct-Application-Id",		RULE_OPTIONAL, -1,-1 }
+							,{  "Vendor-Specific-Application-Id",	RULE_OPTIONAL, -1,-1 }
+							,{  "Firmware-Revision",		RULE_OPTIONAL, -1, 1 }
+						};
+			
+			CHECK_dict_new( DICT_COMMAND, &data , NULL, &cmd);
+			PARSE_loc_rules( rules, cmd );
+		}
+		
+		/* Capabilities-Exchange-Answer */
+		{
+			/*
+				The Capabilities-Exchange-Answer (CEA), indicated by the Command-Code
+				set to 257 and the Command Flags' 'R' bit cleared, is sent in
+				response to a CER message.
+
+				When Diameter is run over SCTP [RFC2960], which allows connections to
+				span multiple interfaces, hence, multiple IP addresses, the
+				Capabilities-Exchange-Answer message MUST contain one Host-IP-Address
+				AVP for each potential IP address that MAY be locally used when
+				transmitting Diameter messages.
+
+				Message Format
+
+				 <CEA> ::= < Diameter Header: 257 >
+        				   { Result-Code }
+        				   { Origin-Host }
+        				   { Origin-Realm }
+        				1* { Host-IP-Address }
+        				   { Vendor-Id }
+        				   { Product-Name }
+        				   [ Origin-State-Id ]
+        				   [ Error-Message ]
+        				   [ Failed-AVP ]
+        				 * [ Supported-Vendor-Id ]
+        				 * [ Auth-Application-Id ]
+        				 * [ Inband-Security-Id ]
+        				 * [ Acct-Application-Id ]
+        				 * [ Vendor-Specific-Application-Id ]
+        				   [ Firmware-Revision ]
+        				 * [ AVP ]
+			*/
+			struct dict_object * cmd;
+			struct dict_cmd_data data = { 
+					257, 					/* Code */
+					#if CC_CAPABILITIES_EXCHANGE != 257
+					#error "CC_CAPABILITIES_EXCHANGE definition mismatch"
+					#endif
+					"Capabilities-Exchange-Answer", 	/* Name */
+					CMD_FLAG_REQUEST | CMD_FLAG_PROXIABLE | CMD_FLAG_RETRANSMIT, 	/* Fixed flags */
+					0 					/* Fixed flag values */
+					};
+			struct local_rules_definition rules[] = 
+						{ 	 {  "Result-Code", 			RULE_REQUIRED, -1, 1 }
+						 	,{  "Origin-Host", 			RULE_REQUIRED, -1, 1 }
+							,{  "Origin-Realm",			RULE_REQUIRED, -1, 1 }
+							,{  "Host-IP-Address",			RULE_REQUIRED, -1,-1 }
+							,{  "Vendor-Id",			RULE_REQUIRED, -1, 1 }
+							,{  "Product-Name",			RULE_REQUIRED, -1, 1 }
+							,{  "Origin-State-Id",			RULE_OPTIONAL, -1, 1 }
+							,{  "Error-Message",			RULE_OPTIONAL, -1, 1 }
+							,{  "Failed-AVP",			RULE_OPTIONAL, -1, 1 }
+							,{  "Supported-Vendor-Id",		RULE_OPTIONAL, -1,-1 }
+							,{  "Auth-Application-Id",		RULE_OPTIONAL, -1,-1 }
+							,{  "Inband-Security-Id",		RULE_OPTIONAL, -1,-1 }
+							,{  "Acct-Application-Id",		RULE_OPTIONAL, -1,-1 }
+							,{  "Vendor-Specific-Application-Id",	RULE_OPTIONAL, -1,-1 }
+							,{  "Firmware-Revision",		RULE_OPTIONAL, -1, 1 }
+						};
+			
+			CHECK_dict_new( DICT_COMMAND, &data , NULL, &cmd);
+			PARSE_loc_rules( rules, cmd );
+		}
+		
+		/* Disconnect-Peer-Request */
+		{
+			/*
+				The Disconnect-Peer-Request (DPR), indicated by the Command-Code set
+				to 282 and the Command Flags' 'R' bit set, is sent to a peer to
+				inform its intentions to shutdown the transport connection.  Upon
+				detection of a transport failure, this message MUST NOT be sent to an
+				alternate peer.
+
+				Message Format
+
+				 <DPR>  ::= < Diameter Header: 282, REQ >
+        				    { Origin-Host }
+        				    { Origin-Realm }
+        				    { Disconnect-Cause }
+			*/
+			struct dict_object * cmd;
+			struct dict_cmd_data data = { 
+					282, 					/* Code */
+					#if CC_DISCONNECT_PEER != 282
+					#error "CC_DISCONNECT_PEER definition mismatch"
+					#endif
+					"Disconnect-Peer-Request", 		/* Name */
+					CMD_FLAG_REQUEST | CMD_FLAG_PROXIABLE | CMD_FLAG_RETRANSMIT | CMD_FLAG_ERROR, 	/* Fixed flags */
+					CMD_FLAG_REQUEST 			/* Fixed flag values */
+					};
+			struct local_rules_definition rules[] = 
+						{ 	 {  "Origin-Host", 			RULE_REQUIRED, -1, 1 }
+							,{  "Origin-Realm",			RULE_REQUIRED, -1, 1 }
+							,{  "Disconnect-Cause",			RULE_REQUIRED, -1, 1 }
+						};
+			
+			CHECK_dict_new( DICT_COMMAND, &data , NULL, &cmd);
+			PARSE_loc_rules( rules, cmd );
+		}
+		
+		/* Disconnect-Peer-Answer */
+		{
+			/*
+				The Disconnect-Peer-Answer (DPA), indicated by the Command-Code set
+				to 282 and the Command Flags' 'R' bit cleared, is sent as a response
+				to the Disconnect-Peer-Request message.  Upon receipt of this
+				message, the transport connection is shutdown.
+
+				Message Format
+
+				 <DPA>  ::= < Diameter Header: 282 >
+        				    { Result-Code }
+        				    { Origin-Host }
+        				    { Origin-Realm }
+        				    [ Error-Message ]
+        				    [ Failed-AVP ]
+			*/
+			struct dict_object * cmd;
+			struct dict_cmd_data data = { 
+					282, 					/* Code */
+					#if CC_DISCONNECT_PEER != 282
+					#error "CC_DISCONNECT_PEER definition mismatch"
+					#endif
+					"Disconnect-Peer-Answer", 		/* Name */
+					CMD_FLAG_REQUEST | CMD_FLAG_PROXIABLE | CMD_FLAG_RETRANSMIT, 	/* Fixed flags */
+					0 					/* Fixed flag values */
+					};
+			struct local_rules_definition rules[] = 
+						{ 	 {  "Result-Code", 			RULE_REQUIRED, -1, 1 }
+						 	,{  "Origin-Host", 			RULE_REQUIRED, -1, 1 }
+							,{  "Origin-Realm",			RULE_REQUIRED, -1, 1 }
+							,{  "Error-Message",			RULE_OPTIONAL, -1, 1 }
+							,{  "Failed-AVP",			RULE_OPTIONAL, -1, 1 }
+						};
+			
+			CHECK_dict_new( DICT_COMMAND, &data , NULL, &cmd);
+			PARSE_loc_rules( rules, cmd );
+		}
+
+		/* Device-Watchdog-Request */
+		{
+			/*
+				The Device-Watchdog-Request (DWR), indicated by the Command-Code set
+				to 280 and the Command Flags' 'R' bit set, is sent to a peer when no
+				traffic has been exchanged between two peers (see Section 5.5.3).
+				Upon detection of a transport failure, this message MUST NOT be sent
+				to an alternate peer.
+
+				Message Format
+
+				 <DWR>  ::= < Diameter Header: 280, REQ >
+        				    { Origin-Host }
+        				    { Origin-Realm }
+        				    [ Origin-State-Id ]
+			*/
+			struct dict_object * cmd;
+			struct dict_cmd_data data = { 
+					280, 					/* Code */
+					#if CC_DEVICE_WATCHDOG != 280
+					#error "CC_DEVICE_WATCHDOG definition mismatch"
+					#endif
+					"Device-Watchdog-Request", 		/* Name */
+					CMD_FLAG_REQUEST | CMD_FLAG_PROXIABLE | CMD_FLAG_RETRANSMIT | CMD_FLAG_ERROR, 	/* Fixed flags */
+					CMD_FLAG_REQUEST 			/* Fixed flag values */
+					};
+			struct local_rules_definition rules[] = 
+						{ 	 {  "Origin-Host", 			RULE_REQUIRED, -1, 1 }
+							,{  "Origin-Realm",			RULE_REQUIRED, -1, 1 }
+							,{  "Origin-State-Id",			RULE_OPTIONAL, -1, 1 }
+						};
+			
+			CHECK_dict_new( DICT_COMMAND, &data , NULL, &cmd);
+			PARSE_loc_rules( rules, cmd );
+		}
+		
+		/* Device-Watchdog-Answer */
+		{
+			/*
+				The Device-Watchdog-Answer (DWA), indicated by the Command-Code set
+				to 280 and the Command Flags' 'R' bit cleared, is sent as a response
+				to the Device-Watchdog-Request message.
+
+				Message Format
+
+				 <DWA>  ::= < Diameter Header: 280 >
+        				    { Result-Code }
+        				    { Origin-Host }
+        				    { Origin-Realm }
+        				    [ Error-Message ]
+        				    [ Failed-AVP ]
+        				    [ Origin-State-Id ]
+			*/
+			struct dict_object * cmd;
+			struct dict_cmd_data data = { 
+					280, 					/* Code */
+					#if CC_DEVICE_WATCHDOG != 280
+					#error "CC_DEVICE_WATCHDOG definition mismatch"
+					#endif
+					"Device-Watchdog-Answer", 		/* Name */
+					CMD_FLAG_REQUEST | CMD_FLAG_PROXIABLE | CMD_FLAG_RETRANSMIT, 	/* Fixed flags */
+					0 					/* Fixed flag values */
+					};
+			struct local_rules_definition rules[] = 
+						{ 	 {  "Result-Code", 			RULE_REQUIRED, -1, 1 }
+						 	,{  "Origin-Host", 			RULE_REQUIRED, -1, 1 }
+							,{  "Origin-Realm",			RULE_REQUIRED, -1, 1 }
+							,{  "Error-Message",			RULE_OPTIONAL, -1, 1 }
+							,{  "Failed-AVP",			RULE_OPTIONAL, -1, 1 }
+							,{  "Origin-State-Id",			RULE_OPTIONAL, -1, 1 }
+						};
+			
+			CHECK_dict_new( DICT_COMMAND, &data , NULL, &cmd);
+			PARSE_loc_rules( rules, cmd );
+		}
+		
+		/* Re-Auth-Request */
+		{
+			/*
+				The Re-Auth-Request (RAR), indicated by the Command-Code set to 258
+				and the message flags' 'R' bit set, may be sent by any server to the
+				access device that is providing session service, to request that the
+				user be re-authenticated and/or re-authorized.
+
+
+				Message Format
+
+				 <RAR>  ::= < Diameter Header: 258, REQ, PXY >
+        				    < Session-Id >
+        				    { Origin-Host }
+        				    { Origin-Realm }
+        				    { Destination-Realm }
+        				    { Destination-Host }
+        				    { Auth-Application-Id }
+        				    { Re-Auth-Request-Type }
+        				    [ User-Name ]
+        				    [ Origin-State-Id ]
+        				  * [ Proxy-Info ]
+        				  * [ Route-Record ]
+        				  * [ AVP ]
+			*/
+			struct dict_object * cmd;
+			struct dict_cmd_data data = { 
+					258, 					/* Code */
+					#if CC_RE_AUTH != 258
+					#error "CC_RE_AUTH definition mismatch"
+					#endif
+					"Re-Auth-Request", 			/* Name */
+					CMD_FLAG_REQUEST | CMD_FLAG_PROXIABLE | CMD_FLAG_ERROR,	/* Fixed flags */
+					CMD_FLAG_REQUEST | CMD_FLAG_PROXIABLE	/* Fixed flag values */
+					};
+			struct local_rules_definition rules[] = 
+						{ 	 {  "Session-Id", 			RULE_FIXED_HEAD, -1, 1 }
+						 	,{  "Origin-Host", 			RULE_REQUIRED,   -1, 1 }
+							,{  "Origin-Realm",			RULE_REQUIRED,   -1, 1 }
+							,{  "Destination-Realm",		RULE_REQUIRED,   -1, 1 }
+						 	,{  "Destination-Host", 		RULE_REQUIRED,   -1, 1 }
+						 	,{  "Auth-Application-Id", 		RULE_REQUIRED,   -1, 1 }
+						 	,{  "Re-Auth-Request-Type", 		RULE_REQUIRED,   -1, 1 }
+							,{  "User-Name",			RULE_OPTIONAL,   -1, 1 }
+							,{  "Origin-State-Id",			RULE_OPTIONAL,   -1, 1 }
+							,{  "Proxy-Info",			RULE_OPTIONAL,   -1,-1 }
+							,{  "Route-Record",			RULE_OPTIONAL,   -1,-1 }
+						};
+			
+			CHECK_dict_new( DICT_COMMAND, &data , NULL, &cmd);
+			PARSE_loc_rules( rules, cmd );
+		}
+		
+		/* Re-Auth-Answer */
+		{
+			/*
+				The Re-Auth-Answer (RAA), indicated by the Command-Code set to 258
+				and the message flags' 'R' bit clear, is sent in response to the RAR.
+				The Result-Code AVP MUST be present, and indicates the disposition of
+				the request.
+
+				A successful RAA message MUST be followed by an application-specific
+				authentication and/or authorization message.
+
+
+				Message Format
+
+				 <RAA>  ::= < Diameter Header: 258, PXY >
+        				    < Session-Id >
+        				    { Result-Code }
+        				    { Origin-Host }
+        				    { Origin-Realm }
+        				    [ User-Name ]
+        				    [ Origin-State-Id ]
+        				    [ Error-Message ]
+        				    [ Error-Reporting-Host ]
+        				    [ Failed-AVP ]
+        				  * [ Redirect-Host ]
+        				    [ Redirect-Host-Usage ]
+        				    [ Redirect-Max-Cache-Time ]
+        				  * [ Proxy-Info ]
+        				  * [ AVP ]
+			*/
+			struct dict_object * cmd;
+			struct dict_cmd_data data = { 
+					258, 					/* Code */
+					#if CC_RE_AUTH != 258
+					#error "CC_RE_AUTH definition mismatch"
+					#endif
+					"Re-Auth-Answer", 			/* Name */
+					CMD_FLAG_REQUEST | CMD_FLAG_PROXIABLE,	/* Fixed flags */
+							   CMD_FLAG_PROXIABLE	/* Fixed flag values */
+					};
+			struct local_rules_definition rules[] = 
+						{ 	 {  "Session-Id", 			RULE_FIXED_HEAD, -1, 1 }
+ 							,{  "Result-Code", 			RULE_REQUIRED,   -1, 1 }
+						 	,{  "Origin-Host", 			RULE_REQUIRED,   -1, 1 }
+							,{  "Origin-Realm",			RULE_REQUIRED,   -1, 1 }
+							,{  "User-Name",			RULE_OPTIONAL,   -1, 1 }
+							,{  "Origin-State-Id",			RULE_OPTIONAL,   -1, 1 }
+							,{  "Error-Message",			RULE_OPTIONAL,   -1, 1 }
+							,{  "Error-Reporting-Host",		RULE_OPTIONAL,   -1, 1 }
+							,{  "Failed-AVP",			RULE_OPTIONAL,   -1, 1 }
+							,{  "Redirect-Host",			RULE_OPTIONAL,   -1,-1 }
+							,{  "Redirect-Host-Usage",		RULE_OPTIONAL,   -1, 1 }
+							,{  "Redirect-Max-Cache-Time",		RULE_OPTIONAL,   -1, 1 }
+							,{  "Proxy-Info",			RULE_OPTIONAL,   -1,-1 }
+						};
+			
+			CHECK_dict_new( DICT_COMMAND, &data , NULL, &cmd);
+			PARSE_loc_rules( rules, cmd );
+		}
+		
+		/* Session-Termination-Request */
+		{
+			/*
+				The Session-Termination-Request (STR), indicated by the Command-Code
+				set to 275 and the Command Flags' 'R' bit set, is sent by the access
+				device to inform the Diameter Server that an authenticated and/or
+				authorized session is being terminated.
+
+
+        				   Message Format
+
+				 <STR> ::= < Diameter Header: 275, REQ, PXY >
+        				   < Session-Id >
+        				   { Origin-Host }
+        				   { Origin-Realm }
+        				   { Destination-Realm }
+        				   { Auth-Application-Id }
+        				   { Termination-Cause }
+        				   [ User-Name ]
+        				   [ Destination-Host ]
+        				 * [ Class ]
+        				   [ Origin-State-Id ]
+        				 * [ Proxy-Info ]
+        				 * [ Route-Record ]
+        				 * [ AVP ]
+			*/
+			struct dict_object * cmd;
+			struct dict_cmd_data data = { 
+					275, 					/* Code */
+					#if CC_SESSION_TERMINATION != 275
+					#error "CC_SESSION_TERMINATION definition mismatch"
+					#endif
+					"Session-Termination-Request", 		/* Name */
+					CMD_FLAG_REQUEST | CMD_FLAG_PROXIABLE | CMD_FLAG_ERROR,	/* Fixed flags */
+					CMD_FLAG_REQUEST | CMD_FLAG_PROXIABLE	/* Fixed flag values */
+					};
+			struct local_rules_definition rules[] = 
+						{ 	 {  "Session-Id", 			RULE_FIXED_HEAD, -1, 1 }
+						 	,{  "Origin-Host", 			RULE_REQUIRED,   -1, 1 }
+							,{  "Origin-Realm",			RULE_REQUIRED,   -1, 1 }
+							,{  "Destination-Realm",		RULE_REQUIRED,   -1, 1 }
+						 	,{  "Auth-Application-Id", 		RULE_REQUIRED,   -1, 1 }
+						 	,{  "Termination-Cause", 		RULE_REQUIRED,   -1, 1 }
+							,{  "User-Name",			RULE_OPTIONAL,   -1, 1 }
+							,{  "Destination-Host",			RULE_OPTIONAL,   -1, 1 }
+							,{  "Class",				RULE_OPTIONAL,   -1,-1 }
+							,{  "Origin-State-Id",			RULE_OPTIONAL,   -1, 1 }
+							,{  "Proxy-Info",			RULE_OPTIONAL,   -1,-1 }
+							,{  "Route-Record",			RULE_OPTIONAL,   -1,-1 }
+						};
+			
+			CHECK_dict_new( DICT_COMMAND, &data , NULL, &cmd);
+			PARSE_loc_rules( rules, cmd );
+		}
+		
+		/* Session-Termination-Answer */
+		{
+			/*
+				The Session-Termination-Answer (STA), indicated by the Command-Code
+				set to 275 and the message flags' 'R' bit clear, is sent by the
+				Diameter Server to acknowledge the notification that the session has
+				been terminated.  The Result-Code AVP MUST be present, and MAY
+				contain an indication that an error occurred while servicing the STR.
+
+				Upon sending or receipt of the STA, the Diameter Server MUST release
+				all resources for the session indicated by the Session-Id AVP.  Any
+				intermediate server in the Proxy-Chain MAY also release any
+				resources, if necessary.
+
+        				    Message Format
+
+				 <STA>  ::= < Diameter Header: 275, PXY >
+        				    < Session-Id >
+        				    { Result-Code }
+        				    { Origin-Host }
+        				    { Origin-Realm }
+        				    [ User-Name ]
+        				  * [ Class ]
+        				    [ Error-Message ]
+        				    [ Error-Reporting-Host ]
+        				    [ Failed-AVP ]
+        				    [ Origin-State-Id ]
+        				  * [ Redirect-Host ]
+        				    [ Redirect-Host-Usage ]
+        				    [ Redirect-Max-Cache-Time ]
+        				  * [ Proxy-Info ]
+        				  * [ AVP ]
+			*/
+			struct dict_object * cmd;
+			struct dict_cmd_data data = { 
+					275, 					/* Code */
+					#if CC_SESSION_TERMINATION != 275
+					#error "CC_SESSION_TERMINATION definition mismatch"
+					#endif
+					"Session-Termination-Answer", 		/* Name */
+					CMD_FLAG_REQUEST | CMD_FLAG_PROXIABLE,	/* Fixed flags */
+							   CMD_FLAG_PROXIABLE	/* Fixed flag values */
+					};
+			struct local_rules_definition rules[] = 
+						{ 	 {  "Session-Id", 			RULE_FIXED_HEAD, -1, 1 }
+ 							,{  "Result-Code", 			RULE_REQUIRED,   -1, 1 }
+						 	,{  "Origin-Host", 			RULE_REQUIRED,   -1, 1 }
+							,{  "Origin-Realm",			RULE_REQUIRED,   -1, 1 }
+							,{  "User-Name",			RULE_OPTIONAL,   -1, 1 }
+							,{  "Class",				RULE_OPTIONAL,   -1,-1 }
+							,{  "Error-Message",			RULE_OPTIONAL,   -1, 1 }
+							,{  "Error-Reporting-Host",		RULE_OPTIONAL,   -1, 1 }
+							,{  "Failed-AVP",			RULE_OPTIONAL,   -1, 1 }
+							,{  "Origin-State-Id",			RULE_OPTIONAL,   -1, 1 }
+							,{  "Redirect-Host",			RULE_OPTIONAL,   -1,-1 }
+							,{  "Redirect-Host-Usage",		RULE_OPTIONAL,   -1, 1 }
+							,{  "Redirect-Max-Cache-Time",		RULE_OPTIONAL,   -1, 1 }
+							,{  "Proxy-Info",			RULE_OPTIONAL,   -1,-1 }
+						};
+			
+			CHECK_dict_new( DICT_COMMAND, &data , NULL, &cmd);
+			PARSE_loc_rules( rules, cmd );
+		}
+		
+		/* Abort-Session-Request */
+		{
+			/*
+				The Abort-Session-Request (ASR), indicated by the Command-Code set to
+				274 and the message flags' 'R' bit set, may be sent by any server to
+				the access device that is providing session service, to request that
+				the session identified by the Session-Id be stopped.
+
+
+        				    Message Format
+
+				 <ASR>  ::= < Diameter Header: 274, REQ, PXY >
+        				    < Session-Id >
+        				    { Origin-Host }
+        				    { Origin-Realm }
+        				    { Destination-Realm }
+        				    { Destination-Host }
+        				    { Auth-Application-Id }
+        				    [ User-Name ]
+        				    [ Origin-State-Id ]
+        				  * [ Proxy-Info ]
+        				  * [ Route-Record ]
+        				  * [ AVP ]
+			*/
+			struct dict_object * cmd;
+			struct dict_cmd_data data = { 
+					274, 					/* Code */
+					#if CC_ABORT_SESSION != 274
+					#error "CC_ABORT_SESSION definition mismatch"
+					#endif
+					"Abort-Session-Request", 		/* Name */
+					CMD_FLAG_REQUEST | CMD_FLAG_PROXIABLE | CMD_FLAG_ERROR,	/* Fixed flags */
+					CMD_FLAG_REQUEST | CMD_FLAG_PROXIABLE	/* Fixed flag values */
+					};
+			struct local_rules_definition rules[] = 
+						{ 	 {  "Session-Id", 			RULE_FIXED_HEAD, -1, 1 }
+						 	,{  "Origin-Host", 			RULE_REQUIRED,   -1, 1 }
+							,{  "Origin-Realm",			RULE_REQUIRED,   -1, 1 }
+							,{  "Destination-Realm",		RULE_REQUIRED,   -1, 1 }
+							,{  "Destination-Host",			RULE_REQUIRED,   -1, 1 }
+						 	,{  "Auth-Application-Id", 		RULE_REQUIRED,   -1, 1 }
+							,{  "User-Name",			RULE_OPTIONAL,   -1, 1 }
+							,{  "Origin-State-Id",			RULE_OPTIONAL,   -1, 1 }
+							,{  "Proxy-Info",			RULE_OPTIONAL,   -1,-1 }
+							,{  "Route-Record",			RULE_OPTIONAL,   -1,-1 }
+						};
+			
+			CHECK_dict_new( DICT_COMMAND, &data , NULL, &cmd);
+			PARSE_loc_rules( rules, cmd );
+		}
+		
+		/* Abort-Session-Answer */
+		{
+			/*
+				The Abort-Session-Answer (ASA), indicated by the Command-Code set to
+				274 and the message flags' 'R' bit clear, is sent in response to the
+				ASR.  The Result-Code AVP MUST be present, and indicates the
+				disposition of the request.
+
+				If the session identified by Session-Id in the ASR was successfully
+				terminated, Result-Code is set to DIAMETER_SUCCESS.  If the session
+				is not currently active, Result-Code is set to
+				DIAMETER_UNKNOWN_SESSION_ID.  If the access device does not stop the
+				session for any other reason, Result-Code is set to
+				DIAMETER_UNABLE_TO_COMPLY.
+
+        				    Message Format
+
+				 <ASA>  ::= < Diameter Header: 274, PXY >
+        				    < Session-Id >
+        				    { Result-Code }
+        				    { Origin-Host }
+        				    { Origin-Realm }
+        				    [ User-Name ]
+        				    [ Origin-State-Id ]
+        				    [ Error-Message ]
+        				    [ Error-Reporting-Host ]
+        				    [ Failed-AVP ]
+        				  * [ Redirect-Host ]
+        				    [ Redirect-Host-Usage ]
+        				    [ Redirect-Max-Cache-Time ]
+        				  * [ Proxy-Info ]
+        				  * [ AVP ]
+			*/
+			struct dict_object * cmd;
+			struct dict_cmd_data data = { 
+					274, 					/* Code */
+					#if CC_ABORT_SESSION != 274
+					#error "CC_ABORT_SESSION definition mismatch"
+					#endif
+					"Abort-Session-Answer", 		/* Name */
+					CMD_FLAG_REQUEST | CMD_FLAG_PROXIABLE,	/* Fixed flags */
+							   CMD_FLAG_PROXIABLE	/* Fixed flag values */
+					};
+			struct local_rules_definition rules[] = 
+						{ 	 {  "Session-Id", 			RULE_FIXED_HEAD, -1, 1 }
+ 							,{  "Result-Code", 			RULE_REQUIRED,   -1, 1 }
+						 	,{  "Origin-Host", 			RULE_REQUIRED,   -1, 1 }
+							,{  "Origin-Realm",			RULE_REQUIRED,   -1, 1 }
+							,{  "User-Name",			RULE_OPTIONAL,   -1, 1 }
+							,{  "Origin-State-Id",			RULE_OPTIONAL,   -1, 1 }
+							,{  "Error-Message",			RULE_OPTIONAL,   -1, 1 }
+							,{  "Error-Reporting-Host",		RULE_OPTIONAL,   -1, 1 }
+							,{  "Failed-AVP",			RULE_OPTIONAL,   -1, 1 }
+							,{  "Redirect-Host",			RULE_OPTIONAL,   -1,-1 }
+							,{  "Redirect-Host-Usage",		RULE_OPTIONAL,   -1, 1 }
+							,{  "Redirect-Max-Cache-Time",		RULE_OPTIONAL,   -1, 1 }
+							,{  "Proxy-Info",			RULE_OPTIONAL,   -1,-1 }
+						};
+			
+			CHECK_dict_new( DICT_COMMAND, &data , NULL, &cmd);
+			PARSE_loc_rules( rules, cmd );
+		}
+		
+		/* Accounting-Request */
+		{
+			/*
+				The Accounting-Request (ACR) command, indicated by the Command-Code
+				field set to 271 and the Command Flags' 'R' bit set, is sent by a
+				Diameter node, acting as a client, in order to exchange accounting
+				information with a peer.
+
+				One of Acct-Application-Id and Vendor-Specific-Application-Id AVPs
+				MUST be present.  If the Vendor-Specific-Application-Id grouped AVP
+				is present, it MUST include an Acct-Application-Id AVP.
+
+				The AVP listed below SHOULD include service specific accounting AVPs,
+				as described in Section 9.3.
+
+
+				Message Format
+
+				 <ACR> ::= < Diameter Header: 271, REQ, PXY >
+        				   < Session-Id >
+        				   { Origin-Host }
+        				   { Origin-Realm }
+        				   { Destination-Realm }
+        				   { Accounting-Record-Type }
+        				   { Accounting-Record-Number }
+        				   [ Acct-Application-Id ]
+        				   [ Vendor-Specific-Application-Id ]
+        				   [ User-Name ]
+        				   [ Destination-Host ]
+        				   [ Accounting-Sub-Session-Id ]
+        				   [ Acct-Session-Id ]
+        				   [ Acct-Multi-Session-Id ]
+        				   [ Acct-Interim-Interval ]
+        				   [ Accounting-Realtime-Required ]
+        				   [ Origin-State-Id ]
+        				   [ Event-Timestamp ]
+        				 * [ Proxy-Info ]
+        				 * [ Route-Record ]
+        				 * [ AVP ]
+			*/
+			struct dict_object * cmd;
+			struct dict_cmd_data data = { 
+					271, 					/* Code */
+					#if CC_ACCOUNTING != 271
+					#error "CC_ACCOUNTING definition mismatch"
+					#endif
+					"Accounting-Request", 			/* Name */
+					CMD_FLAG_REQUEST | CMD_FLAG_PROXIABLE | CMD_FLAG_ERROR,	/* Fixed flags */
+					CMD_FLAG_REQUEST | CMD_FLAG_PROXIABLE	/* Fixed flag values */
+					};
+			struct local_rules_definition rules[] = 
+						{ 	 {  "Session-Id", 			RULE_FIXED_HEAD, -1, 1 }
+						 	,{  "Origin-Host", 			RULE_REQUIRED,   -1, 1 }
+							,{  "Origin-Realm",			RULE_REQUIRED,   -1, 1 }
+							,{  "Destination-Realm",		RULE_REQUIRED,   -1, 1 }
+							,{  "Accounting-Record-Type",		RULE_REQUIRED,   -1, 1 }
+							,{  "Accounting-Record-Number",		RULE_REQUIRED,   -1, 1 }
+							,{  "Acct-Application-Id",		RULE_OPTIONAL,   -1, 1 }
+							,{  "Vendor-Specific-Application-Id",	RULE_OPTIONAL,   -1, 1 }
+							,{  "User-Name",			RULE_OPTIONAL,   -1, 1 }
+							,{  "Destination-Host",			RULE_OPTIONAL,   -1, 1 }
+							,{  "Accounting-Sub-Session-Id",	RULE_OPTIONAL,   -1, 1 }
+							,{  "Acct-Session-Id",			RULE_OPTIONAL,   -1, 1 }
+							,{  "Acct-Multi-Session-Id",		RULE_OPTIONAL,   -1, 1 }
+							,{  "Acct-Interim-Interval",		RULE_OPTIONAL,   -1, 1 }
+							,{  "Accounting-Realtime-Required",	RULE_OPTIONAL,   -1, 1 }
+							,{  "Origin-State-Id",			RULE_OPTIONAL,   -1, 1 }
+							,{  "Event-Timestamp",			RULE_OPTIONAL,   -1, 1 }
+							,{  "Proxy-Info",			RULE_OPTIONAL,   -1,-1 }
+							,{  "Route-Record",			RULE_OPTIONAL,   -1,-1 }
+						};
+			
+			CHECK_dict_new( DICT_COMMAND, &data , NULL, &cmd);
+			PARSE_loc_rules( rules, cmd );
+		}
+		
+		/* Accounting-Answer */
+		{
+			/*
+				The Accounting-Answer (ACA) command, indicated by the Command-Code
+				field set to 271 and the Command Flags' 'R' bit cleared, is used to
+				acknowledge an Accounting-Request command.  The Accounting-Answer
+				command contains the same Session-Id as the corresponding request.
+
+				Only the target Diameter Server, known as the home Diameter Server,
+				SHOULD respond with the Accounting-Answer command.
+
+				One of Acct-Application-Id and Vendor-Specific-Application-Id AVPs
+				MUST be present.  If the Vendor-Specific-Application-Id grouped AVP
+				is present, it MUST contain an Acct-Application-Id AVP.
+
+				The AVP listed below SHOULD include service specific accounting AVPs,
+				as described in Section 9.3.
+
+
+				Message Format
+
+				 <ACA> ::= < Diameter Header: 271, PXY >
+        				   < Session-Id >
+        				   { Result-Code }
+        				   { Origin-Host }
+        				   { Origin-Realm }
+        				   { Accounting-Record-Type }
+        				   { Accounting-Record-Number }
+        				   [ Acct-Application-Id ]
+        				   [ Vendor-Specific-Application-Id ]
+        				   [ User-Name ]
+        				   [ Accounting-Sub-Session-Id ]
+        				   [ Acct-Session-Id ]
+        				   [ Acct-Multi-Session-Id ]
+        				   [ Error-Message ]
+        				   [ Error-Reporting-Host ]
+        				   [ Failed-AVP ]
+        				   [ Acct-Interim-Interval ]
+        				   [ Accounting-Realtime-Required ]
+        				   [ Origin-State-Id ]
+        				   [ Event-Timestamp ]
+        				 * [ Proxy-Info ]
+        				 * [ AVP ]
+			*/
+			struct dict_object * cmd;
+			struct dict_cmd_data data = { 
+					271, 					/* Code */
+					#if CC_ACCOUNTING != 271
+					#error "CC_ACCOUNTING definition mismatch"
+					#endif
+					"Accounting-Answer", 			/* Name */
+					CMD_FLAG_REQUEST | CMD_FLAG_PROXIABLE,	/* Fixed flags */
+							   CMD_FLAG_PROXIABLE	/* Fixed flag values */
+					};
+			struct local_rules_definition rules[] = 
+						{ 	 {  "Session-Id", 			RULE_FIXED_HEAD, -1, 1 }
+ 							,{  "Result-Code", 			RULE_REQUIRED,   -1, 1 }
+						 	,{  "Origin-Host", 			RULE_REQUIRED,   -1, 1 }
+							,{  "Origin-Realm",			RULE_REQUIRED,   -1, 1 }
+							,{  "Accounting-Record-Type",		RULE_REQUIRED,   -1, 1 }
+							,{  "Accounting-Record-Number",		RULE_REQUIRED,   -1, 1 }
+							,{  "Acct-Application-Id",		RULE_OPTIONAL,   -1, 1 }
+							,{  "Vendor-Specific-Application-Id",	RULE_OPTIONAL,   -1, 1 }
+							,{  "User-Name",			RULE_OPTIONAL,   -1, 1 }
+							,{  "Accounting-Sub-Session-Id",	RULE_OPTIONAL,   -1, 1 }
+							,{  "Acct-Session-Id",			RULE_OPTIONAL,   -1, 1 }
+							,{  "Acct-Multi-Session-Id",		RULE_OPTIONAL,   -1, 1 }
+							,{  "Error-Message",			RULE_OPTIONAL,   -1, 1 }
+							,{  "Error-Reporting-Host",		RULE_OPTIONAL,   -1, 1 }
+							,{  "Failed-AVP",			RULE_OPTIONAL,   -1, 1 }
+							,{  "Acct-Interim-Interval",		RULE_OPTIONAL,   -1, 1 }
+							,{  "Accounting-Realtime-Required",	RULE_OPTIONAL,   -1, 1 }
+							,{  "Origin-State-Id",			RULE_OPTIONAL,   -1, 1 }
+							,{  "Event-Timestamp",			RULE_OPTIONAL,   -1, 1 }
+							,{  "Proxy-Info",			RULE_OPTIONAL,   -1,-1 }
+						};
+			
+			CHECK_dict_new( DICT_COMMAND, &data , NULL, &cmd);
+			PARSE_loc_rules( rules, cmd );
+		}
+	}
+
+	return 0;
+}
diff --git a/libfdcore/endpoints.c b/libfdcore/endpoints.c
new file mode 100644
index 0000000..e80c2a3
--- /dev/null
+++ b/libfdcore/endpoints.c
@@ -0,0 +1,271 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@freediameter.net>							 *
+*													 *
+* Copyright (c) 2013, 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 "fdcore-internal.h"
+
+
+/* Add an endpoint information in a list */
+int fd_ep_add_merge( struct fd_list * list, sSA * sa, socklen_t sl, uint32_t flags )
+{
+	struct fd_endpoint * ep;
+	struct fd_list * li;
+	union {
+		sSA * sa;
+		sSA4 *sin;
+		sSA6 *sin6;
+	} ptr;
+	in_port_t * port;
+	int cmp = -1;
+	
+	TRACE_ENTRY("%p %p %u %x", list, sa, sl, flags);
+	CHECK_PARAMS( list && sa && (sl <= sizeof(sSS)) );
+	
+	if (list->next == NULL) {
+		/* the list is not initialized yet, do it */
+		fd_list_init(list, NULL);
+	}
+	
+	ptr.sa = sa;
+	
+	/* Filter out a bunch of invalid addresses */
+	switch (sa->sa_family) {
+		case AF_INET:
+			if (! (flags & EP_ACCEPTALL)) {
+				if (IN_IS_ADDR_UNSPECIFIED(&ptr.sin->sin_addr) 
+				 || IN_IS_ADDR_LOOPBACK(&ptr.sin->sin_addr)
+				    /* the next one filters both EXPERIMENTAL, BADCLASS and MULTICAST. */
+				 || ((ntohl(ptr.sin->sin_addr.s_addr) & 0xe0000000) == 0xe0000000)
+				 || (ptr.sin->sin_addr.s_addr == INADDR_BROADCAST)) {
+					LOG_A("  DEBUG:fd_ep_add_merge  Address was ignored, not added.");
+					return 0;
+				}
+			}
+			port = &ptr.sin->sin_port;
+			break;
+
+		case AF_INET6:
+			if (! (flags & EP_ACCEPTALL)) {
+				if (IN6_IS_ADDR_UNSPECIFIED(&ptr.sin6->sin6_addr) 
+				 || IN6_IS_ADDR_LOOPBACK(&ptr.sin6->sin6_addr)
+				 || IN6_IS_ADDR_MULTICAST(&ptr.sin6->sin6_addr)
+				 || IN6_IS_ADDR_LINKLOCAL(&ptr.sin6->sin6_addr)
+				 || IN6_IS_ADDR_SITELOCAL(&ptr.sin6->sin6_addr)) {
+					LOG_A("  DEBUG:fd_ep_add_merge  Address was ignored, not added.");
+					return 0;
+				}
+			}
+			port = &ptr.sin6->sin6_port;
+			break;
+
+		default:
+			LOG_A("  DEBUG:fd_ep_add_merge  Address family was unknown, not added.");
+			return 0;
+	}
+	
+	/* remove the ACCEPTALL flag */
+	flags &= ~EP_ACCEPTALL;
+	
+	/* Search place in the list */
+	for (li = list->next; li != list; li = li->next) {
+		ep = (struct fd_endpoint *)li;
+		in_port_t * ep_port;
+		
+		/* First, compare the address family */
+		if (ep->sa.sa_family < sa->sa_family)
+			continue;
+		if (ep->sa.sa_family > sa->sa_family)
+			break;
+		
+		/* Then compare the address field */
+		switch (sa->sa_family) {
+			case AF_INET:
+				cmp = memcmp(&ep->sin.sin_addr, &ptr.sin->sin_addr, sizeof(struct in_addr));
+				ep_port = &ep->sin.sin_port;
+				break;
+			case AF_INET6:
+				cmp = memcmp(&ep->sin6.sin6_addr, &ptr.sin6->sin6_addr, sizeof(struct in6_addr));
+				ep_port = &ep->sin6.sin6_port;
+				break;
+			default:
+				ASSERT( 0 ); /* we got a different value previously in this same function */
+		}
+		if (cmp < 0)
+			continue;
+		if (cmp > 0)
+			break;
+		
+		/* Finally compare the port, only if not 0 */
+		if (*port == 0)
+			break;
+		if (*ep_port == 0) {
+			/* save the port information in the list, and break */
+			*ep_port = *port;
+			break;
+		}
+		if (*ep_port < *port) {
+			cmp = -1;
+			continue;
+		}
+		if (*ep_port > *port)
+			cmp = 1;
+		break;
+	}
+	
+	if (cmp) {
+		/* new item to be added */
+		CHECK_MALLOC( ep = malloc(sizeof(struct fd_endpoint)) );
+		memset(ep, 0, sizeof(struct fd_endpoint));
+		fd_list_init(&ep->chain, NULL);
+		memcpy(&ep->ss, sa, sl);
+		
+		/* Insert in the list */
+		fd_list_insert_before(li, &ep->chain);
+	}
+	
+	/* Merge the flags */
+	ep->flags |= flags;
+	
+	return 0;
+}
+
+/* Delete endpoints that do not have a matching flag from a list (0: delete all endpoints) */
+int fd_ep_filter( struct fd_list * list, uint32_t flags )
+{
+	struct fd_list * li;
+	
+	TRACE_ENTRY("%p %x", list, flags);
+	CHECK_PARAMS(list);
+	
+	for (li = list->next; li != list; li = li->next) {
+		struct fd_endpoint * ep = (struct fd_endpoint *)li;
+		
+		if (! (ep->flags & flags)) {
+			li = li->prev;
+			fd_list_unlink(&ep->chain);
+			free(ep);
+		}
+	}
+	
+	return 0;
+}
+
+/* Keep only endpoints of the same family as af */
+int fd_ep_filter_family( struct fd_list * list, int af )
+{
+	struct fd_list * li;
+	
+	TRACE_ENTRY("%p %d", list, af);
+	CHECK_PARAMS(list);
+	
+	for (li = list->next; li != list; li = li->next) {
+		struct fd_endpoint * ep = (struct fd_endpoint *)li;
+		
+		if (ep->sa.sa_family != af) {
+			li = li->prev;
+			fd_list_unlink(&ep->chain);
+			free(ep);
+		}
+	}
+	
+	return 0;
+}
+
+/* Reset the given flag(s) from all items in the list */
+int fd_ep_clearflags( struct fd_list * list, uint32_t flags )
+{
+	struct fd_list * li;
+	
+	TRACE_ENTRY("%p %x", list, flags);
+	CHECK_PARAMS(list);
+	
+	for (li = list->next; li != list; li = li->next) {
+		struct fd_endpoint * ep = (struct fd_endpoint *)li;
+		ep->flags &= ~flags;
+		if (ep->flags == 0) {
+			li = li->prev;
+			fd_list_unlink(&ep->chain);
+			free(ep);
+		}
+	}
+	
+	return 0;
+}
+
+DECLARE_FD_DUMP_PROTOTYPE(fd_ep_dump_one, int preamble, struct fd_endpoint * ep )
+{
+	FD_DUMP_HANDLE_OFFSET();
+	
+	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);
+		return *buf;
+	}
+	
+	CHECK_MALLOC_DO( fd_sa_dump( 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);
+	return *buf;
+}
+
+DECLARE_FD_DUMP_PROTOTYPE(fd_ep_dump, int preamble, int indent, struct fd_list * eps  )
+{
+	struct fd_list * li;
+	
+	FD_DUMP_HANDLE_OFFSET();
+	
+	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;
+			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);
+		}
+	}
+	return *buf;
+}
+	
diff --git a/libfdcore/events.c b/libfdcore/events.c
new file mode 100644
index 0000000..bea5885
--- /dev/null
+++ b/libfdcore/events.c
@@ -0,0 +1,231 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@freediameter.net>							 *
+*													 *
+* Copyright (c) 2013, 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 "fdcore-internal.h"
+
+/* Events are a subset of fifo queues, with a known type */
+
+int fd_event_send(struct fifo *queue, int code, size_t datasz, void * data)
+{
+	struct fd_event * ev;
+	CHECK_MALLOC( ev = malloc(sizeof(struct fd_event)) );
+	ev->code = code;
+	ev->size = datasz;
+	ev->data = data;
+	CHECK_FCT( fd_fifo_post(queue, &ev) );
+	return 0;
+}
+
+int fd_event_get(struct fifo *queue, int *code, size_t *datasz, void ** data)
+{
+	struct fd_event * ev;
+	CHECK_FCT( fd_fifo_get(queue, &ev) );
+	if (code)
+		*code = ev->code;
+	if (datasz)
+		*datasz = ev->size;
+	if (data)
+		*data = ev->data;
+	free(ev);
+	return 0;
+}
+
+int fd_event_timedget(struct fifo *queue, struct timespec * timeout, int timeoutcode, int *code, size_t *datasz, void ** data)
+{
+	struct fd_event * ev;
+	int ret = 0;
+	ret = fd_fifo_timedget(queue, &ev, timeout);
+	if (ret == ETIMEDOUT) {
+		if (code)
+			*code = timeoutcode;
+		if (datasz)
+			*datasz = 0;
+		if (data)
+			*data = NULL;
+	} else {
+		CHECK_FCT( ret );
+		if (code)
+			*code = ev->code;
+		if (datasz)
+			*datasz = ev->size;
+		if (data)
+			*data = ev->data;
+		free(ev);
+	}
+	return 0;
+}
+
+void fd_event_destroy(struct fifo **queue, void (*free_cb)(void * data))
+{
+	struct fd_event * ev;
+	/* Purge all events, and free the associated data if any */
+	while (fd_fifo_tryget( *queue, &ev ) == 0) {
+		(*free_cb)(ev->data);
+		free(ev);
+	}
+	CHECK_FCT_DO( fd_fifo_del(queue), /* continue */ );
+	return ;
+} 
+
+const char * fd_ev_str(int event)
+{
+	switch (event) {
+	#define case_str( _val )\
+		case _val : return #_val
+		case_str(FDEV_TERMINATE_INT);
+		case_str(FDEV_TRIGGER);
+		
+		default:
+			TRACE_DEBUG(FULL, "Unknown event : %d", event);
+			return "Unknown event";
+	}
+}
+
+/**********************************************************************/
+/* Trigged events */
+/* This allows extensions to register for / send triggers to other extensions */
+/* It is used for example for users interactions (through signals or ...) */
+
+/* Because the number of triggers is not expected to grow, we use a simple ordered chained list */
+static pthread_rwlock_t trig_rwl = PTHREAD_RWLOCK_INITIALIZER;
+static struct fd_list   trig_list = FD_LIST_INITIALIZER(trig_list); /* The list of triggers ordered by trigger value */
+struct trig_item {
+	struct fd_list 	chain;
+	int  		trig_value;
+	const char * 	trig_module;
+	void 		(*cb)(void);
+};	
+
+/* Add a new entry in the trigger list */
+int fd_event_trig_regcb(int trigger_val, const char * module, void (*cb)(void))
+{
+	struct trig_item * ti;
+	struct fd_list * li;
+	
+	TRACE_ENTRY("%d %p %p", trigger_val, module, cb);
+	CHECK_PARAMS( trigger_val && cb );
+	
+	/* Create a new trig_item */
+	CHECK_MALLOC( ti = malloc(sizeof(struct trig_item)) );
+	memset(ti, 0, sizeof(struct trig_item));
+	fd_list_init(&ti->chain, ti);
+	ti->trig_value = trigger_val;
+	ti->trig_module = module;
+	ti->cb = cb;
+	
+	/* Now insert in the list */
+	CHECK_POSIX( pthread_rwlock_wrlock(&trig_rwl) );
+	
+	for (li = trig_list.next; li != &trig_list; li = li->next) {
+		struct trig_item *t = li->o;
+		if (t->trig_value >= trigger_val)
+			break;
+	}
+	
+	fd_list_insert_before(li, &ti->chain);
+	
+	CHECK_POSIX( pthread_rwlock_unlock(&trig_rwl) );
+
+	return 0;
+}
+
+DECLARE_FD_DUMP_PROTOTYPE(fd_event_trig_dump)
+{
+	struct fd_list * li;
+	FD_DUMP_HANDLE_OFFSET();
+	
+	CHECK_POSIX_DO( pthread_rwlock_rdlock(&trig_rwl),  );
+	
+	for (li = trig_list.next; li != &trig_list; li = li->next) {
+		struct trig_item *t = li->o;
+		CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "{signal:%d}'%s'->%p ", t->trig_value, t->trig_module, t->cb), break);
+	}
+	
+	CHECK_POSIX_DO( pthread_rwlock_unlock(&trig_rwl),  );
+	return *buf;
+}
+
+static void *call_cb_detached(void * arg)
+{
+	void (*cb)(void) = arg;
+	fd_log_threadname("Trig'd callback thread");
+	TRACE_ENTRY("%p", arg);
+	(*cb)();
+	TRACE_DEBUG(ANNOYING, "Callback %p completed", cb);
+	return NULL;
+}
+
+int fd_event_trig_call_cb(int trigger_val)
+{
+	struct fd_list * li;
+	pthread_attr_t detached;
+	pthread_t th;
+	
+	CHECK_POSIX( pthread_attr_init(&detached) );
+	CHECK_POSIX( pthread_attr_setdetachstate(&detached, PTHREAD_CREATE_DETACHED) );
+	
+	CHECK_POSIX( pthread_rwlock_rdlock(&trig_rwl)  );
+	
+	for (li = trig_list.next; li != &trig_list; li = li->next) {
+		struct trig_item *t = li->o;
+		if (t->trig_value == trigger_val) {
+			TRACE_DEBUG(FULL, "Trigger %d: Calling %p in %s", t->trig_value, t->cb, t->trig_module);
+			CHECK_POSIX_DO( pthread_create( &th, &detached, call_cb_detached, t->cb ), break );
+		}
+		if (t->trig_value > trigger_val)
+			break;
+	}
+	
+	CHECK_POSIX( pthread_rwlock_unlock(&trig_rwl)  );
+	
+	return 0;
+}
+
+int fd_event_trig_fini(void) {
+	
+	TRACE_ENTRY("");
+	
+	CHECK_POSIX( pthread_rwlock_wrlock(&trig_rwl) );
+	
+	while (!FD_IS_LIST_EMPTY(&trig_list)) {
+		struct fd_list * li = trig_list.next;
+		fd_list_unlink(li);
+		free(li);
+	}
+	
+	CHECK_POSIX( pthread_rwlock_unlock(&trig_rwl) );
+	
+	return 0;
+}
diff --git a/libfdcore/extensions.c b/libfdcore/extensions.c
new file mode 100644
index 0000000..0d4fcca
--- /dev/null
+++ b/libfdcore/extensions.c
@@ -0,0 +1,299 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@freediameter.net>							 *
+*													 *
+* Copyright (c) 2013, 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 "fdcore-internal.h"
+
+#include <dlfcn.h>	/* We may use libtool's <ltdl.h> later for better portability.... */
+#include <libgen.h>	/* for "basename" */
+
+/* plugins management */
+
+/* List of extensions to load, from the configuration parsing */
+struct fd_ext_info {
+	struct fd_list	chain;		/* link in the list */
+	char 		*filename;	/* extension filename. must be a dynamic library with fd_ext_init symbol. */
+	char 		*conffile;	/* optional configuration file name for the extension */
+	void 		*handler;	/* object returned by dlopen() */
+	const char 	**depends;	/* names of the other extensions this one depends on (if provided) */
+	char		*ext_name;	/* points to the extension name, either inside depends, or basename(filename) */
+	int		free_ext_name;	/* must be freed if it was malloc'd */
+	void		(*fini)(void);	/* optional address of the fd_ext_fini callback */
+	char		*proto_ver;
+	double		gen_date;
+};
+
+/* list of extensions */
+static struct fd_list ext_list = FD_LIST_INITIALIZER(ext_list);
+
+/* Add new extension */
+int fd_ext_add( char * filename, char * conffile )
+{
+	struct fd_ext_info * new;
+	
+	TRACE_ENTRY("%p %p", filename, conffile);
+	
+	/* Check the filename is valid */
+	CHECK_PARAMS( filename );
+	
+	/* Create a new object in the list */
+	CHECK_MALLOC(  new = malloc( sizeof(struct fd_ext_info) )  );
+	memset(new, 0, sizeof(struct fd_ext_info));
+	fd_list_init(&new->chain, NULL);
+	new->filename = filename;
+	new->conffile = conffile;
+	fd_list_insert_before( &ext_list, &new->chain );
+	TRACE_DEBUG (FULL, "Extension %s added to the list.", filename);
+	return 0;
+}
+
+/* Dump the list */
+DECLARE_FD_DUMP_PROTOTYPE(fd_ext_dump)
+{
+	struct fd_list * li;
+	FD_DUMP_HANDLE_OFFSET();
+	
+	if (FD_IS_LIST_EMPTY(&ext_list)) {
+		CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "-none-"), return NULL);
+	} else {
+		for (li = ext_list.next; li != &ext_list; li = li->next)
+		{
+			struct fd_ext_info * ext = (struct fd_ext_info *)li;
+			CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "'%s'[%s], %sloaded%s",
+						ext->filename, 
+						ext->conffile?:"(no config file)", 
+						ext->handler ? "" : "not ", (li->next == &ext_list) ? "":"\n"), return NULL);
+		}
+	}
+	return *buf;
+}
+
+/* Load the dependencies */
+static int load_dependencies(struct fd_ext_info *ext)
+{
+	TRACE_ENTRY( "%p", ext);
+	/* Attempt to resolve the dependency array */
+	ext->depends = dlsym( ext->handler, "fd_ext_depends");
+	if (!ext->depends) {
+		/* Duplicate the filename */
+		char * tmp = strdup(ext->filename);
+		ext->ext_name = strdup(basename(tmp));
+		free(tmp);
+		ext->free_ext_name = 1;
+		TRACE_DEBUG(FULL, "Old extension's [%s] API: missing dependencies (ignored)", ext->ext_name);
+		return 0;
+	}
+	
+	ext->ext_name = (char *)ext->depends[0];
+	return 0;
+
+}
+
+/* Check the dependencies. The object must have been dlopened already. */
+static int check_dependencies(struct fd_ext_info * ext)
+{
+	int i = 1;
+	TRACE_DEBUG(FULL, "Checking dependencies for '%s'...", ext->ext_name);
+	
+	while (ext->depends[i]) {
+		struct fd_list * li;
+		for (li = ext_list.next; li != &ext_list; li = li->next)
+		{
+			struct fd_ext_info * e = (struct fd_ext_info *)li;
+			if (!strcasecmp(e->ext_name, ext->depends[i])) {
+				/* the dependency was already loaded */
+				break;
+			}
+		}
+
+		if (li == &ext_list) {
+			/* the dependency was not found */
+			LOG_F("Error: extension [%s] depends on [%s] which was not loaded first. Please fix your configuration file.",
+				ext->ext_name, ext->depends[i]);
+			return ESRCH;
+		}
+		
+		i++;
+	}
+
+	/* All dependencies resolved successfully */
+	return 0;
+}
+
+/* Load all extensions in the list */
+int fd_ext_load()
+{
+	int ret;
+	int (*fd_ext_init)(int, int, char *) = NULL;
+	int (*fd_ext_init2)(int, int, char *) = NULL;
+	struct fd_list * li;
+	
+	TRACE_ENTRY();
+	
+	/* Loop on all extensions */
+	for (li = ext_list.next; li != &ext_list; li = li->next)
+	{
+		struct fd_ext_info * ext = (struct fd_ext_info *)li;
+		LOG_D( "Loading : %s", ext->filename);
+		
+		/* Load the extension */
+#ifndef DEBUG
+		ext->handler = dlopen(ext->filename, RTLD_LAZY | RTLD_GLOBAL);
+#else /* DEBUG */
+		/* We resolve symbols immediatly so it's easier to find problems in ABI */
+		ext->handler = dlopen(ext->filename, RTLD_NOW | RTLD_GLOBAL);
+#endif /* DEBUG */
+		if (ext->handler == NULL) {
+			/* An error occured */
+			LOG_F("Loading of extension %s failed: %s", ext->filename, dlerror());
+			ext->handler = dlopen(ext->filename, RTLD_LAZY | RTLD_GLOBAL);
+			if (ext->handler) {
+				if (!check_dependencies(ext)) {
+					LOG_F("In addition, not all declared dependencies are satisfied (Internal Error!)");
+				}
+			}
+			return EINVAL;
+		}
+
+		/* Resolve the entry point of the extension */
+		fd_ext_init = ( int (*) (int, int, char *) )dlsym( ext->handler, "fd_ext_init" );
+		
+		if (fd_ext_init == NULL) {
+			/* An error occured */
+			TRACE_ERROR("Unable to resolve symbol 'fd_ext_init' for extension %s: %s", ext->filename, dlerror());
+			return EINVAL;
+		}
+
+		
+		/* Resolve the exit point of the extension, which is optional for extensions */
+		ext->fini = ( void (*) (void) )dlsym( ext->handler, "fd_ext_fini" );
+		
+		if (ext->fini == NULL) {
+			/* Not provided */
+			TRACE_DEBUG (FULL, "Extension [%s] has no fd_ext_fini function.", ext->filename);
+		} else {
+			/* Provided */
+			TRACE_DEBUG (FULL, "Extension [%s] fd_ext_fini has been resolved successfully.", ext->filename);
+		}
+
+		/* Now call the entry point to initialize the extension */
+		ret = (*fd_ext_init)( FD_PROJECT_VERSION_MAJOR, FD_PROJECT_VERSION_MINOR, ext->conffile );
+		if (ret != 0) {
+			/* The extension was unable to load cleanly */
+			TRACE_ERROR("Extension %s returned an error during initialization: %s", ext->filename, strerror(ret));
+			return ret;
+		}
+		
+		/* Proceed to the next extension */
+	}
+
+	for( li = ext_list.next; li != &ext_list; li = li->next )
+        {
+		struct fd_ext_info * ext = (struct fd_ext_info *)li;
+		CHECK_FCT( load_dependencies(ext));
+	}
+
+
+	for( li = ext_list.next; li != &ext_list; li = li->next )
+	{
+		struct fd_ext_info * ext = (struct fd_ext_info *)li;
+
+		/* Check if declared dependencies are satisfied. */
+		CHECK_FCT( check_dependencies(ext) );
+
+		/* Loading methods init2 & parserules */
+		fd_ext_init2 = ( int (*) (int, int, char *) )dlsym( ext->handler, "fd_ext_init2" );
+
+		if (fd_ext_init2 == NULL) {
+			/* Old extensions do not define fd_ext_init2 */
+			LOG_N("Unable to  resolve symbol 'fd_ext_init2' for extension %s: ", ext->filename);
+		}
+		else {
+			ret = (*fd_ext_init2)( FD_PROJECT_VERSION_MAJOR, FD_PROJECT_VERSION_MINOR, ext->conffile );
+			if (ret != 0) {
+				/* The extension was unable to load cleanly */
+				TRACE_ERROR("Extension %s returned an error during parse local rules: %s", ext->filename, strerror(ret));
+				return ret;
+			}
+		}
+	}
+	
+	LOG_N("All extensions loaded.");
+	
+	/* We have finished. */
+	return 0;
+}
+
+/* Now unload the extensions and free the memory */
+int fd_ext_term( void ) 
+{
+	TRACE_ENTRY();
+	
+	/* Loop on all extensions, in FIFO order */
+	while (!FD_IS_LIST_EMPTY(&ext_list))
+	{
+		struct fd_list * li = ext_list.next;
+		struct fd_ext_info * ext = (struct fd_ext_info *)li;
+	
+		/* Unlink this element from the list */
+		fd_list_unlink(li);
+		
+		/* Call the exit point of the extension, if it was resolved */
+		if (ext->fini != NULL) {
+			TRACE_DEBUG (FULL, "Calling [%s]->fd_ext_fini function.", ext->ext_name ?: ext->filename);
+			(*ext->fini)();
+		}
+		
+#ifndef SKIP_DLCLOSE
+		/* Now unload the extension */
+		if (ext->handler) {
+			TRACE_DEBUG (FULL, "Unloading %s", ext->ext_name ?: ext->filename);
+			if ( dlclose(ext->handler) != 0 ) {
+				TRACE_DEBUG (INFO, "Unloading [%s] failed : %s", ext->ext_name ?: ext->filename, dlerror());
+			}
+		}
+#endif /* SKIP_DLCLOSE */
+		
+		/* Free the object and continue */
+		if (ext->free_ext_name)
+			free(ext->ext_name);
+		free(ext->filename);
+		free(ext->conffile);
+		free(ext);
+	}
+	
+	/* We always return 0 since we would not handle an error anyway... */
+	return 0;
+}
+
diff --git a/libfdcore/fdcore-internal.h b/libfdcore/fdcore-internal.h
new file mode 100644
index 0000000..7fac852
--- /dev/null
+++ b/libfdcore/fdcore-internal.h
@@ -0,0 +1,375 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@freediameter.net>							 *
+*													 *
+* Copyright (c) 2013, 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.								 *
+*********************************************************************************************************/
+
+/* This file contains the definitions for internal use in the freeDiameter core library */
+
+#ifndef _FDCORE_INTERNAL_H
+#define _FDCORE_INTERNAL_H
+
+#include <freeDiameter/freeDiameter-host.h>
+#include <freeDiameter/libfdcore.h>
+
+#ifdef DISABLE_SCTP
+#undef IPPROTO_SCTP
+#define IPPROTO_SCTP	(2 = 4) /* some compilation error to spot the references */
+#endif /* DISABLE_SCTP */
+
+#ifndef HAVE_AI_ADDRCONFIG
+#define AI_ADDRCONFIG 0 /* ignore this flag at the moment */
+#endif /* HAVE_AI_ADDRCONFIG */
+
+/* Timeout for establishing a connection */
+#ifndef CNX_TIMEOUT
+#define  CNX_TIMEOUT	10	/* in seconds */
+#endif /* CNX_TIMEOUT */
+
+/* Timeout for receiving a CER after incoming connection is established */
+#ifndef INCNX_TIMEOUT
+#define  INCNX_TIMEOUT	 20	/* in seconds */
+#endif /* INCNX_TIMEOUT */
+
+/* Timeout for receiving a CEA after CER is sent */
+#ifndef CEA_TIMEOUT
+#define  CEA_TIMEOUT	10	/* in seconds */
+#endif /* CEA_TIMEOUT */
+
+/* The timeout value to wait for answer to a DPR */
+#ifndef DPR_TIMEOUT
+#define DPR_TIMEOUT 	15	/* in seconds */
+#endif /* DPR_TIMEOUT */
+
+/* Delay where the connection is maintained opened to allow exchanging remaining pending answers after DPR/DPA */
+#ifndef GRACE_TIMEOUT
+#define GRACE_TIMEOUT   1	/* in seconds */
+#endif /* GRACE_TIMEOUT */
+
+/* The Vendor-Id to advertise in CER/CEA */
+#ifndef MY_VENDOR_ID
+#define MY_VENDOR_ID	0 	/* Reserved value to tell it must be ignored */
+#endif /* MY_VENDOR_ID */
+
+
+
+/* Configuration */
+int fd_conf_init();
+int fd_conf_deinit();
+int fd_conf_parse();
+int fddparse(struct fd_config * conf); /* yacc generated */
+int fd_conf_stream_to_gnutls_datum(FILE * pemfile, gnutls_datum_t *out);
+
+
+/* Extensions */
+int fd_ext_add( char * filename, char * conffile );
+int fd_ext_load();
+int fd_ext_term(void);
+
+/* Messages */
+int fd_msg_init(void);
+extern struct dict_object * fd_dict_avp_OSI; /* Origin-State-Id */
+extern struct dict_object * fd_dict_cmd_CER; /* Capabilities-Exchange-Request */
+extern struct dict_object * fd_dict_cmd_DWR; /* Device-Watchdog-Request */
+extern struct dict_object * fd_dict_avp_DC;  /* Disconnect-Cause */
+extern struct dict_object * fd_dict_cmd_DPR; /* Disconnect-Peer-Request */
+
+/* Global message queues */
+extern struct fifo * fd_g_incoming; /* all messages received from other peers, except local messages (CER, ...) */
+extern struct fifo * fd_g_outgoing; /* messages to be sent to other peers on the network following routing procedure */
+extern struct fifo * fd_g_local; /* messages to be handled to local extensions */
+/* Message queues */
+int fd_queues_init(void);
+int fd_queues_fini(struct fifo ** queue);
+
+/* Trigged events */
+int fd_event_trig_call_cb(int trigger_val);
+int fd_event_trig_fini(void);
+
+/* Create all the dictionary objects defined in the Diameter base RFC. */
+int fd_dict_base_protocol(struct dictionary * dict);
+
+/* Routing */
+int fd_rtdisp_init(void);
+int fd_rtdisp_cleanstop(void);
+int fd_rtdisp_fini(void);
+int fd_rtdisp_cleanup(void);
+
+/* Sentinel for the sent requests list */
+struct sr_list {
+	struct fd_list 	srs; /* requests ordered by hop-by-hop id */
+	struct fd_list  exp; /* requests that have a timeout set, ordered by timeout */
+	long            cnt; /* number of requests in the srs list */
+	long		cnt_lost; /* number of requests that have not been answered in time. 
+				     It is decremented when an unexpected answer is received, so this may not be accurate. */
+	pthread_mutex_t	mtx; /* mutex to protect these lists */
+	pthread_cond_t  cnd; /* cond var used by the thread that handles timeouts */
+	pthread_t       thr; /* the thread that handles timeouts (expirecb called in separate forked threads) */
+};
+
+/* Peers */
+struct fd_peer { /* The "real" definition of the peer structure */
+	
+	/* The public data */
+	struct peer_hdr	 p_hdr;
+	
+	/* Eye catcher, EYEC_PEER */
+	int		 p_eyec;
+	#define EYEC_PEER	0x373C9336
+	
+	/* Origin of this peer object, for debug */
+	char		*p_dbgorig;
+	
+	/* State of the peer, and its lock */
+	enum peer_state	 p_state;
+	pthread_mutex_t  p_state_mtx;
+	
+	/* Chaining in peers sublists */
+	struct fd_list	 p_actives;	/* list of peers in the STATE_OPEN state -- used by routing */
+	struct fd_list	 p_expiry; 	/* list of expiring peers, ordered by their timeout value */
+	struct timespec	 p_exp_timer;	/* Timestamp where the peer will expire; updated each time activity is seen on the peer (except DW) */
+	
+	/* Some flags influencing the peer state machine */
+	struct {
+		unsigned pf_responder	: 1;	/* The peer has been created to handle incoming connection */
+		unsigned pf_delete	: 1;	/* Destroy the peer when the connection is terminated */
+		unsigned pf_localterm	: 1;	/* If the latest DPR/DPA was initiated from this side */
+		
+		unsigned pf_dw_pending 	: 1;	/* A DWR message was sent and not answered yet */
+		
+		unsigned pf_cnx_pb	: 1;	/* The peer was disconnected because of watchdogs; must exchange 3 watchdogs before putting back to normal */
+		unsigned pf_reopen_cnt	: 2;	/* remaining DW to be exchanged after re-established connection */
+		
+	}		 p_flags;
+	
+	/* The events queue, peer state machine thread, timer for states timeouts */
+	struct fifo	*p_events;	/* The mutex of this FIFO list protects also the state and timer information */
+	pthread_t	 p_psm;
+	struct timespec	 p_psm_timer;
+	
+	/* Outgoing message queue, and thread managing sending the messages */
+	struct fifo	*p_tosend;
+	pthread_t	 p_outthr;
+	
+	/* The next hop-by-hop id value for the link, only read & modified by p_outthr */
+	uint32_t	 p_hbh;
+	
+	/* Sent requests (for fallback), list of struct sentreq ordered by hbh */
+	struct sr_list	 p_sr;
+	struct fifo	*p_tofailover;
+	
+	/* Pending received requests not yet answered (count only) */
+	long		 p_reqin_count; /* We use p_state_mtx to protect this value */
+	
+	/* Data for transitional states before the peer is in OPEN state */
+	struct {
+		struct cnxctx * p_receiver;	/* Only used in case of election */
+		struct msg    * p_cer;		/* Only used in case of election */
+		
+		pthread_t	p_ini_thr;	/* Initiator thread for establishing a connection */
+		struct fd_list  p_connparams;	/* The list of connection attempts, see p_cnx.c */
+	};
+	
+	/* connection context: socket and related information */
+	struct cnxctx	*p_cnxctx;
+	
+	/* Callback for peer validation after the handshake */
+	int		(*p_cb2)(struct peer_info *);
+	
+	/* Callback on initial connection success / failure after the peer was added */
+	void 		(*p_cb)(struct peer_info *, void *);
+	void 		*p_cb_data;
+	
+};
+#define CHECK_PEER( _p ) \
+	(((_p) != NULL) && (((struct fd_peer *)(_p))->p_eyec == EYEC_PEER))
+
+#define fd_peer_getstate(peer)  fd_peer_get_state((struct peer_hdr *)(peer))
+
+
+/* Events codespace for struct fd_peer->p_events */
+enum {
+	/* request to terminate this peer : disconnect, requeue all messages */
+	FDEVP_TERMINATE = 1500
+	
+	/* A connection object has received a message. (data contains the buffer + padding + struct fd_msg_pmdl) */
+	,FDEVP_CNX_MSG_RECV
+			 
+	/* A connection object has encountered an error (disconnected). */
+	,FDEVP_CNX_ERROR
+	
+	/* Endpoints of a connection have been changed (multihomed SCTP). */
+	,FDEVP_CNX_EP_CHANGE
+	
+	/* The connection is being shutdown (SCTP notification). */
+	,FDEVP_CNX_SHUTDOWN
+	
+	/* A new connection (with a CER) has been received */
+	,FDEVP_CNX_INCOMING
+	
+	/* A new connection has been established to the remote peer (event data is the cnxctx object) */
+	,FDEVP_CNX_ESTABLISHED
+	
+	/* A connection attempt (initiator side) has failed */
+	,FDEVP_CNX_FAILED
+	
+	/* The PSM state is expired */
+	,FDEVP_PSM_TIMEOUT
+	
+};
+#define CHECK_PEVENT( _e ) \
+	(((int)(_e) >= FDEVP_TERMINATE) && ((int)(_e) <= FDEVP_PSM_TIMEOUT))
+/* The following macro is actually called in p_psm.c -- another solution would be to declare it static inline */
+#define DECLARE_PEV_STR()				\
+const char * fd_pev_str(int event)			\
+{							\
+	switch (event) {				\
+		case_str(FDEVP_TERMINATE);		\
+		case_str(FDEVP_CNX_MSG_RECV);		\
+		case_str(FDEVP_CNX_ERROR);		\
+		case_str(FDEVP_CNX_EP_CHANGE);		\
+		case_str(FDEVP_CNX_INCOMING);		\
+		case_str(FDEVP_CNX_ESTABLISHED);	\
+		case_str(FDEVP_CNX_FAILED);		\
+		case_str(FDEVP_PSM_TIMEOUT);		\
+	}						\
+	TRACE_DEBUG(FULL, "Unknown event : %d", event);	\
+	return "Unknown event";				\
+}
+const char * fd_pev_str(int event);
+
+/* The data structure for FDEVP_CNX_INCOMING event */
+struct cnx_incoming {
+	struct msg	* cer;		/* the CER message received on this connection */
+	struct cnxctx	* cnx;		/* The connection context */
+	int  		  validate;	/* The peer is new, it must be validated (by an extension) or error CEA to be sent */
+};
+
+/* Functions */
+int  fd_peer_fini();
+int  fd_peer_alloc(struct fd_peer ** ptr);
+int  fd_peer_free(struct fd_peer ** ptr);
+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);
+int fd_p_expi_fini(void);
+int fd_p_expi_update(struct fd_peer * peer );
+
+/* Peer state machine */
+int  fd_psm_start();
+int  fd_psm_begin(struct fd_peer * peer );
+int  fd_psm_terminate(struct fd_peer * peer, char * reason );
+void fd_psm_abord(struct fd_peer * peer );
+void fd_psm_next_timeout(struct fd_peer * peer, int add_random, int delay);
+int fd_psm_change_state(struct fd_peer * peer, int new_state);
+void fd_psm_cleanup(struct fd_peer * peer, int terminate);
+
+/* Peer out */
+int fd_out_send(struct msg ** msg, struct cnxctx * cnx, struct fd_peer * peer, int update_reqin_cnt);
+int fd_out_start(struct fd_peer * peer);
+int fd_out_stop(struct fd_peer * peer);
+
+/* Initiating connections */
+int fd_p_cnx_init(struct fd_peer * peer);
+void fd_p_cnx_abort(struct fd_peer * peer, int cleanup_all);
+
+/* Peer sent requests cache */
+int fd_p_sr_store(struct sr_list * srlist, struct msg **req, uint32_t *hbhloc, uint32_t hbh_restore);
+int fd_p_sr_fetch(struct sr_list * srlist, uint32_t hbh, struct msg **req);
+int fd_p_sr_start(struct sr_list * srlist);
+int fd_p_sr_stop(struct sr_list * srlist);
+void fd_p_sr_failover(struct sr_list * srlist);
+
+/* Local Link messages (CER/CEA, DWR/DWA, DPR/DPA) */
+int fd_p_ce_msgrcv(struct msg ** msg, int req, struct fd_peer * peer);
+int fd_p_ce_handle_newCER(struct msg ** msg, struct fd_peer * peer, struct cnxctx ** cnx, int valid);
+int fd_p_ce_handle_newcnx(struct fd_peer * peer, struct cnxctx * initiator);
+int fd_p_ce_process_receiver(struct fd_peer * peer);
+void fd_p_ce_clear_cnx(struct fd_peer * peer, struct cnxctx ** cnx_kept);
+int fd_p_dw_handle(struct msg ** msg, int req, struct fd_peer * peer);
+int fd_p_dw_timeout(struct fd_peer * peer);
+int fd_p_dw_reopen(struct fd_peer * peer);
+int fd_p_dp_handle(struct msg ** msg, int req, struct fd_peer * peer);
+int fd_p_dp_initiate(struct fd_peer * peer, char * reason);
+int fd_p_dp_newdelay(struct fd_peer * peer);
+
+/* Active peers -- routing process should only ever take the read lock, the write lock is managed by PSMs */
+extern struct fd_list fd_g_activ_peers;
+extern pthread_rwlock_t fd_g_activ_peers_rw; /* protect the list */
+
+
+/* Server sockets */
+int  fd_servers_start();
+int  fd_servers_stop();
+
+/* Connection contexts -- there are also definitions in cnxctx.h for the relevant files */
+struct cnxctx * fd_cnx_serv_tcp(uint16_t port, int family, struct fd_endpoint * ep);
+struct cnxctx * fd_cnx_serv_sctp(uint16_t port, struct fd_list * ep_list);
+int             fd_cnx_serv_listen(struct cnxctx * conn);
+struct cnxctx * fd_cnx_serv_accept(struct cnxctx * serv);
+struct cnxctx * fd_cnx_cli_connect_tcp(sSA * sa, socklen_t addrlen);
+struct cnxctx * fd_cnx_cli_connect_sctp(int no_ip6, uint16_t port, struct fd_list * list);
+int             fd_cnx_start_clear(struct cnxctx * conn, int loop);
+void		fd_cnx_sethostname(struct cnxctx * conn, DiamId_t hn);
+int		fd_cnx_proto_info(struct cnxctx * conn, char * buf, size_t len);
+#define ALGO_HANDSHAKE_DEFAULT	0 /* TLS for TCP, DTLS for SCTP */
+#define ALGO_HANDSHAKE_3436	1 /* For TLS for SCTP also */
+int             fd_cnx_handshake(struct cnxctx * conn, int mode, int algo, char * priority, void * alt_creds);
+char *          fd_cnx_getid(struct cnxctx * conn);
+int		fd_cnx_getproto(struct cnxctx * conn);
+int		fd_cnx_getTLS(struct cnxctx * conn);
+int		fd_cnx_is_unordered_delivery_supported(struct cnxctx * conn);
+int		fd_cnx_unordered_delivery(struct cnxctx * conn, int is_allowed);
+int             fd_cnx_getcred(struct cnxctx * conn, const gnutls_datum_t **cert_list, unsigned int *cert_list_size);
+int 		fd_cnx_get_local_eps(struct fd_list * list);
+int             fd_cnx_getremoteeps(struct cnxctx * conn, struct fd_list * eps);
+char *          fd_cnx_getremoteid(struct cnxctx * conn);
+int             fd_cnx_receive(struct cnxctx * conn, struct timespec * timeout, unsigned char **buf, size_t * len);
+int             fd_cnx_recv_setaltfifo(struct cnxctx * conn, struct fifo * alt_fifo); /* send FDEVP_CNX_MSG_RECV event to the fifo list */
+int             fd_cnx_send(struct cnxctx * conn, unsigned char * buf, size_t len);
+void            fd_cnx_destroy(struct cnxctx * conn);
+#ifdef GNUTLS_VERSION_300
+int             fd_tls_verify_credentials_2(gnutls_session_t session);
+#endif /* GNUTLS_VERSION_300 */
+
+/* Internal calls of the hook mechanism */
+void   fd_hook_call(enum fd_hook_type type, struct msg * msg, struct fd_peer * peer, void * other, struct fd_msg_pmdl * pmdl);
+void   fd_hook_associate(struct msg * msg, struct fd_msg_pmdl * pmdl);
+int    fd_hooks_init(void);
+size_t fd_msg_pmdl_sizewithoverhead(size_t datalen);
+struct fd_msg_pmdl * fd_msg_pmdl_get_inbuf(uint8_t * buf, size_t datalen); 
+
+#endif /* _FDCORE_INTERNAL_H */
diff --git a/libfdcore/fdd.l b/libfdcore/fdd.l
new file mode 100644
index 0000000..8835def
--- /dev/null
+++ b/libfdcore/fdd.l
@@ -0,0 +1,285 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@freediameter.net>							 *
+*													 *
+* Copyright (c) 2013, 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.								 *
+*********************************************************************************************************/
+
+/* Lex configuration parser.
+ *
+ * This file defines the token for parsing the daemon's configuration file
+ * Note that each extension has a separate independant configuration file.
+ *
+ * Note : This module is NOT thread-safe. All processing must be done from one thread only.
+ */
+%{
+/* Include the daemon's header files */
+#include "fdcore-internal.h"
+/* Include yacc tokens definitions */
+#include "fdd.tab.h"
+
+/* Update the column information */
+#ifdef DEBUG_LEX
+#define YY_USER_ACTION { 						\
+	yylloc->first_column = yylloc->last_column + 1; 		\
+	yylloc->last_column = yylloc->first_column + yyleng - 1;	\
+	fd_log_debug(	 						\
+		"(%d:%d-%d:%d) matched rule %d, length=%d, txt='%s'",	\
+		yylloc->first_line, yylloc->first_column, 		\
+		yylloc->last_line, yylloc->last_column, 		\
+		yy_act, yyleng, yytext); 				\
+}
+#else /* DEBUG_LEX */
+#define YY_USER_ACTION { 						\
+	yylloc->first_column = yylloc->last_column + 1; 		\
+	yylloc->last_column = yylloc->first_column + yyleng - 1;	\
+}
+#endif
+
+/* %option noinput ? */
+#define YY_NO_INPUT
+
+/* Additional for files inclusion */
+#include <glob.h>
+#include <string.h>
+
+#define MAX_NESTED_CONF_FILES	5
+
+struct nested_conffiles_t {
+	YY_BUFFER_STATE parent_level_state;
+	glob_t filelist;
+	int current_file;
+} nested_conffiles[MAX_NESTED_CONF_FILES];
+
+int current_nested_level = 0;
+
+int globerrfct(const char *epath, int eerrno)
+{
+	TRACE_ERROR("Failed to scan %s: %s", epath, strerror(eerrno));
+	return 1;
+}
+
+%}
+
+%option bison-bridge bison-locations
+%option noyywrap
+%option nounput
+
+%x in_include
+
+/* Quoted string. Multilines do not match. */
+qstring		\"[^\"\n]*\"
+
+%%
+<*>\n			{ 
+				/* Update the line count */
+				yylloc->first_line++; 
+				yylloc->last_line++; 
+				yylloc->last_column=0; 
+			} 
+
+<*>([[:space:]]{-}[\n])+	; /* Eat all spaces, not new lines */
+<*>#.*$			; /* Eat all comments */
+
+
+include		BEGIN(in_include);
+	/* Following an "include" keyword */
+<in_include>{
+{qstring}	{ /* Name of the file to include. This is directly sent to glob. */
+			int globerror=0;
+			char * buf = strdup(yytext+1);
+			if (buf[yyleng-2] != '"')
+			{
+				TRACE_ERROR("Unterminated string: %s", yytext);
+				return LEX_ERROR;
+			}
+			buf[yyleng-2] = '\0';
+
+			if (current_nested_level >= MAX_NESTED_CONF_FILES)
+			{
+				TRACE_ERROR("Too many recursion levels in configuration files includes");
+				return LEX_ERROR;
+			}
+
+			/* glob the include */
+			globerror = glob(buf, GLOB_ERR, globerrfct, &nested_conffiles[current_nested_level].filelist);
+
+			if (globerror == GLOB_NOSPACE)
+			{
+				TRACE_ERROR("Not enough memory to parse include directive.");
+				return LEX_ERROR;
+			}
+			if (globerror == GLOB_ABORTED)
+			{
+				TRACE_ERROR("An error was encountered in include directive.");
+				return LEX_ERROR;
+			}
+			if (globerror == GLOB_NOMATCH)
+			{
+				globfree(&nested_conffiles[current_nested_level].filelist);
+				goto nomatch;
+			}
+			if (globerror)
+			{
+				TRACE_ERROR("Unexpected error in glob (%d).", globerror);
+				return LEX_ERROR;
+			}
+
+			/* We have a list of files to include. */
+
+			/* save the current buffer for returning when this include has been parsed */
+			nested_conffiles[current_nested_level].parent_level_state = YY_CURRENT_BUFFER;
+
+			/* Start with the first match */
+			nested_conffiles[current_nested_level].current_file = 0;
+
+			yyin = fopen( nested_conffiles[current_nested_level].filelist.gl_pathv[0], "r" );
+
+			if ( ! yyin )
+			{
+				TRACE_ERROR("Error in %s: %s", nested_conffiles[current_nested_level].filelist.gl_pathv[0], strerror(errno));
+				return LEX_ERROR;
+			}
+
+			yy_switch_to_buffer(yy_create_buffer( yyin, YY_BUF_SIZE ));
+
+			/* In case of recursive includes */
+			current_nested_level++;
+
+nomatch:
+			BEGIN(INITIAL);
+		}
+}
+
+<<EOF>>	{
+			if (current_nested_level == 0)
+			{
+			      /* We are at the end of parsing */
+			      yyterminate();
+			}
+
+			/* Otherwise we are doing an include statement */
+			--current_nested_level;
+			yy_delete_buffer(YY_CURRENT_BUFFER);
+
+			/* Go to next file, if any */
+			nested_conffiles[current_nested_level].current_file++;
+			if ( nested_conffiles[current_nested_level].filelist.gl_pathv[nested_conffiles[current_nested_level].current_file] == NULL )
+			{
+				/* We have finished with this list of includes */
+				globfree(&nested_conffiles[current_nested_level].filelist);
+				yy_switch_to_buffer(nested_conffiles[current_nested_level].parent_level_state);
+			}
+			else
+			{
+				/* Proceed to next included file */
+				yyin = fopen( nested_conffiles[current_nested_level].filelist.gl_pathv[nested_conffiles[current_nested_level].current_file], "r" );
+
+				if ( ! yyin )
+				{
+					TRACE_ERROR("Error in %s: %s", nested_conffiles[current_nested_level].filelist.gl_pathv[nested_conffiles[current_nested_level].current_file], strerror(errno));
+					return LEX_ERROR;
+				}
+
+				yy_switch_to_buffer(yy_create_buffer( yyin, YY_BUF_SIZE ));
+
+				/* In case of recursive includes */
+				current_nested_level++;
+			}
+
+}
+
+{qstring}		{
+				/* First copy the string without the quotes for use in the yacc parser */
+				CHECK_MALLOC_DO(	yylval->string = strdup(yytext+1), /* This allocates one useless tail char but... it's easier :D */ 
+							return LEX_ERROR  );/* on error, trig an error in yacc parser */
+
+				yylval->string[yyleng-2] = '\0';
+				
+				/* the yacc parser will check the string is valid */
+				return QSTRING;
+			}
+			
+[[:digit:]]+		{
+				/* Convert this to an integer value */
+				int ret = sscanf(yytext, "%i", &yylval->integer);
+				if (ret != 1) {
+					/* No matching: an error occurred */
+					TRACE_ERROR("Unable to convert the value '%s' to a valid number: %s", yytext, strerror(errno));
+					return LEX_ERROR; /* trig an error in yacc parser */
+					/* Maybe we could REJECT instead of failing here? */
+				}
+				return INTEGER;
+			}
+				
+	/* Full words tokens (keywords) */
+(?i:"Identity")		{ return IDENTITY;	}
+(?i:"Realm")		{ return REALM;   	}
+(?i:"Port")		{ return PORT;    	}
+(?i:"SecPort")		{ return SECPORT;  	}
+	/* (?i:"SctpSec3436")	{ return SEC3436;  	} */
+(?i:"No_IPv6")		{ return NOIP6;		}
+(?i:"No_IP")		{ return NOIP;		}
+(?i:"No_TCP")		{ return NOTCP;		}
+(?i:"No_SCTP")		{ return NOSCTP;	}
+(?i:"Prefer_TCP")	{ return PREFERTCP;	}
+(?i:"TLS_old_method")	{ return OLDTLS;	}
+(?i:"SCTP_streams")	{ return SCTPSTREAMS;	}
+(?i:"AppServThreads")	{ return APPSERVTHREADS;}
+(?i:"ListenOn")		{ return LISTENON;	}
+(?i:"ThreadsPerServer")	{ return THRPERSRV;	}
+(?i:"TcTimer")		{ return TCTIMER;	}
+(?i:"TwTimer")		{ return TWTIMER;	}
+(?i:"NoRelay")		{ return NORELAY;	}
+(?i:"LoadExtension")	{ return LOADEXT;	}
+(?i:"ConnectPeer")	{ return CONNPEER;	}
+(?i:"ConnectTo")	{ return CONNTO;	}
+(?i:"No_TLS")		{ return NOTLS;		}
+(?i:"TLS_Cred")		{ return TLS_CRED;	}
+(?i:"TLS_CA")		{ return TLS_CA;	}
+(?i:"TLS_CRL")		{ return TLS_CRL;	}
+(?i:"TLS_Prio")		{ return TLS_PRIO;	}
+(?i:"TLS_DH_bits")	{ return TLS_DH_BITS;	}
+(?i:"TLS_DH_file")	{ return TLS_DH_FILE;	}
+
+
+	/* Valid single characters for yyparse */
+<*>[=,:;{}]		{ return yytext[0]; }
+
+	/* Unrecognized token */
+<*>[[:alnum:]]+		|	/* This rule is only useful to print a complete token in error messages */
+	/* Unrecognized character */
+<*>.			{
+				TRACE_ERROR("Unrecognized text on line %d col %d: '%s'.", yylloc->first_line, yylloc->first_column, yytext);
+			 	return LEX_ERROR; 
+			}
+
+%%
diff --git a/libfdcore/fdd.y b/libfdcore/fdd.y
new file mode 100644
index 0000000..07707c9
--- /dev/null
+++ b/libfdcore/fdd.y
@@ -0,0 +1,667 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@freediameter.net>							 *
+*													 *
+* Copyright (c) 2015, 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.								 *
+*********************************************************************************************************/
+
+/* Yacc configuration parser.
+ *
+ * This file defines the grammar of the configuration file.
+ * Note that each extension has a separate independant configuration file.
+ *
+ * Note : This module is NOT thread-safe. All processing must be done from one thread only.
+ */
+
+/* For development only : */
+%debug 
+%error-verbose
+
+%parse-param {struct fd_config * conf}
+
+/* Keep track of location */
+%locations 
+%pure-parser
+
+%{
+#include "fdcore-internal.h"
+#include "fdd.tab.h"	/* bug : bison does not define the YYLTYPE before including this bloc, so... */
+
+/* The Lex parser prototype */
+int fddlex(YYSTYPE *lvalp, YYLTYPE *llocp);
+
+/* Function to report error */
+void yyerror (YYLTYPE *ploc, struct fd_config * conf, char const *s)
+{
+	if (ploc->first_line != ploc->last_line) {
+		TRACE_ERROR("%s:%d.%d-%d.%d : %s", conf->cnf_file, ploc->first_line, ploc->first_column, ploc->last_line, ploc->last_column, s);
+	} else if (ploc->first_column != ploc->last_column) {
+		TRACE_ERROR("%s:%d.%d-%d : %s", conf->cnf_file, ploc->first_line, ploc->first_column, ploc->last_column, s);
+	} else {
+		TRACE_ERROR("%s:%d.%d : %s", conf->cnf_file, ploc->first_line, ploc->first_column, s);
+	}
+}
+
+int got_peer_noip = 0;
+int got_peer_noipv6 = 0;
+int got_peer_notcp = 0;
+int got_peer_nosctp = 0;
+
+struct peer_info fddpi;
+
+%}
+
+/* Values returned by lex for token */
+%union {
+	char 		 *string;	/* The string is allocated by strdup in lex.*/
+	int		  integer;	/* Store integer values */
+}
+
+/* In case of error in the lexical analysis */
+%token 		LEX_ERROR
+
+%token <string>	QSTRING
+%token <integer> INTEGER
+
+%type <string> 	extconf
+
+%token		IDENTITY
+%token		REALM
+%token		PORT
+%token		SECPORT
+%token		SEC3436
+%token		NOIP
+%token		NOIP6
+%token		NOTCP
+%token		NOSCTP
+%token		PREFERTCP
+%token		OLDTLS
+%token		NOTLS
+%token		SCTPSTREAMS
+%token		APPSERVTHREADS
+%token		LISTENON
+%token		THRPERSRV
+%token		TCTIMER
+%token		TWTIMER
+%token		NORELAY
+%token		LOADEXT
+%token		CONNPEER
+%token		CONNTO
+%token		TLS_CRED
+%token		TLS_CA
+%token		TLS_CRL
+%token		TLS_PRIO
+%token		TLS_DH_BITS
+%token		TLS_DH_FILE
+
+
+/* -------------------------------------- */
+%%
+
+	/* The grammar definition - Sections blocs. */
+conffile:		/* Empty is OK -- for simplicity here, we reject in daemon later */
+			| conffile identity
+			| conffile realm
+			| conffile tctimer
+			| conffile twtimer
+			| conffile port
+			| conffile secport
+			| conffile sec3436
+			| conffile sctpstreams
+			| conffile listenon
+			| conffile thrpersrv
+			| conffile norelay
+			| conffile appservthreads
+			| conffile noip
+			| conffile noip6
+			| conffile notcp
+			| conffile nosctp
+			| conffile prefertcp
+			| conffile oldtls
+			| conffile loadext
+			| conffile connpeer
+			| conffile tls_cred
+			| conffile tls_ca
+			| conffile tls_crl
+			| conffile tls_prio
+			| conffile tls_dh
+			| conffile errors
+			{
+				yyerror(&yylloc, conf, "An error occurred while parsing the configuration file");
+				return EINVAL;
+			}
+			;
+
+			/* Lexical or syntax error */
+errors:			LEX_ERROR
+			| error
+			;
+
+identity:		IDENTITY '=' QSTRING ';'
+			{
+				conf->cnf_diamid = $3;
+			}
+			;
+
+realm:			REALM '=' QSTRING ';'
+			{
+				conf->cnf_diamrlm = $3;
+			}
+			;
+
+tctimer:		TCTIMER '=' INTEGER ';'
+			{
+				CHECK_PARAMS_DO( ($3 > 0),
+					{ yyerror (&yylloc, conf, "Invalid value"); YYERROR; } );
+				conf->cnf_timer_tc = (unsigned int)$3;
+			}
+			;
+
+twtimer:		TWTIMER '=' INTEGER ';'
+			{
+				CHECK_PARAMS_DO( ($3 > 5),
+					{ yyerror (&yylloc, conf, "Invalid value"); YYERROR; } );
+				conf->cnf_timer_tw = (unsigned int)$3;
+			}
+			;
+
+port:			PORT '=' INTEGER ';'
+			{
+				CHECK_PARAMS_DO( ($3 >= 0) && ($3 < 1<<16),
+					{ yyerror (&yylloc, conf, "Invalid value"); YYERROR; } );
+				conf->cnf_port = (uint16_t)$3;
+			}
+			;
+
+secport:		SECPORT '=' INTEGER ';'
+			{
+				CHECK_PARAMS_DO( ($3 >= 0) && ($3 < 1<<16),
+					{ yyerror (&yylloc, conf, "Invalid value"); YYERROR; } );
+				conf->cnf_port_tls = (uint16_t)$3;
+			}
+			;
+
+sec3436:		SEC3436 '=' INTEGER ';'
+			{
+				CHECK_PARAMS_DO( ($3 >= 0) && ($3 < 1<<16),
+					{ yyerror (&yylloc, conf, "Invalid value"); YYERROR; } );
+				conf->cnf_port_3436 = (uint16_t)$3;
+			}
+			;
+
+sctpstreams:		SCTPSTREAMS '=' INTEGER ';'
+			{
+				CHECK_PARAMS_DO( ($3 > 0) && ($3 < 1<<16),
+					{ yyerror (&yylloc, conf, "Invalid value"); YYERROR; } );
+				conf->cnf_sctp_str = (uint16_t)$3;
+			}
+			;
+
+listenon:		LISTENON '=' QSTRING ';'
+			{
+				struct addrinfo hints, *ai;
+				int ret;
+				
+				memset(&hints, 0, sizeof(hints));
+				hints.ai_flags = AI_PASSIVE | AI_NUMERICHOST;
+				ret = getaddrinfo($3, NULL, &hints, &ai);
+				if (ret) { yyerror (&yylloc, conf, gai_strerror(ret)); YYERROR; }
+				CHECK_FCT_DO( fd_ep_add_merge( &conf->cnf_endpoints, ai->ai_addr, ai->ai_addrlen, EP_FL_CONF ), YYERROR );
+				freeaddrinfo(ai);
+				free($3);
+			}
+			;
+
+thrpersrv:		THRPERSRV '=' INTEGER ';'
+			{
+				CHECK_PARAMS_DO( ($3 > 0),
+					{ yyerror (&yylloc, conf, "Invalid value"); YYERROR; } );
+				conf->cnf_thr_srv = $3;
+			}
+			;
+
+norelay:		NORELAY ';'
+			{
+				conf->cnf_flags.no_fwd = 1;
+			}
+			;
+
+appservthreads:		APPSERVTHREADS '=' INTEGER ';'
+			{
+				CHECK_PARAMS_DO( ($3 > 0) && ($3 < 256),
+					{ yyerror (&yylloc, conf, "Invalid value"); YYERROR; } );
+				conf->cnf_dispthr = (uint16_t)$3;
+			}
+			;
+
+noip:			NOIP ';'
+			{
+				if (got_peer_noipv6) { 
+					yyerror (&yylloc, conf, "No_IP conflicts with a ConnectPeer directive No_IPv6."); 
+					YYERROR; 
+				}
+				conf->cnf_flags.no_ip4 = 1;
+			}
+			;
+
+noip6:			NOIP6 ';'
+			{
+				if (got_peer_noip) { 
+					yyerror (&yylloc, conf, "No_IP conflicts with a ConnectPeer directive No_IP."); 
+					YYERROR; 
+				}
+				conf->cnf_flags.no_ip6 = 1;
+			}
+			;
+
+notcp:			NOTCP ';'
+			{
+				#ifdef DISABLE_SCTP
+				yyerror (&yylloc, conf, "No_TCP cannot be specified for daemon compiled with DISABLE_SCTP option."); 
+				YYERROR; 
+				#endif
+				if (conf->cnf_flags.no_sctp)
+				{
+					yyerror (&yylloc, conf, "No_TCP conflicts with No_SCTP directive." ); 
+					YYERROR; 
+				}
+				if (got_peer_nosctp) { 
+					yyerror (&yylloc, conf, "No_TCP conflicts with a ConnectPeer directive No_SCTP."); 
+					YYERROR; 
+				}
+				conf->cnf_flags.no_tcp = 1;
+			}
+			;
+
+nosctp:			NOSCTP ';'
+			{
+				if (conf->cnf_flags.no_tcp)
+				{
+					yyerror (&yylloc, conf, "No_SCTP conflicts with No_TCP directive." ); 
+					YYERROR; 
+				}
+				if (got_peer_notcp) { 
+					yyerror (&yylloc, conf, "No_SCTP conflicts with a ConnectPeer directive No_TCP.");
+					YYERROR;
+				}
+				conf->cnf_flags.no_sctp = 1;
+			}
+			;
+
+prefertcp:		PREFERTCP ';'
+			{
+				conf->cnf_flags.pr_tcp = 1;
+			}
+			;
+
+oldtls:			OLDTLS ';'
+			{
+				conf->cnf_flags.tls_alg = 1;
+			}
+			;
+
+loadext:		LOADEXT '=' QSTRING extconf ';'
+			{
+				char * fname;
+				char * cfname;
+				FILE * fd;
+				
+				/* Try and open the extension file */
+				fname = $3;
+				fd = fopen(fname, "r");
+				if ((fd == NULL) && (*fname != '/')) {
+					char * bkp = fname;
+					CHECK_MALLOC_DO( fname = malloc( strlen(bkp) + strlen(DEFAULT_EXTENSIONS_PATH) + 2 ),
+						{ yyerror (&yylloc, conf, "Not enough memory"); YYERROR; } );
+					sprintf(fname, DEFAULT_EXTENSIONS_PATH "/%s", bkp);
+					fd = fopen(fname, "r");
+					if (fd == NULL) {
+						free(fname);
+						fname = bkp;
+					} else {
+						free(bkp);
+					}
+				}
+				if (fd != NULL) {
+					fclose(fd);
+				} /* otherwise, LD_LIBRARY_PATH will be tested by dl_open. 
+				This should not give any security issue, otherwise we can add an "else fail" here. */
+				
+				/* Try and open the configuration file (optional) */
+				cfname = $4;
+				if (cfname) {
+					fd = fopen(cfname, "r");
+					if ((fd == NULL) && (*cfname != '/')) {
+						char * test;
+						CHECK_MALLOC_DO( test = malloc( strlen(cfname) + strlen(DEFAULT_CONF_PATH) + 2 ),
+							{ yyerror (&yylloc, conf, "Not enough memory"); YYERROR; } );
+						sprintf(test, DEFAULT_CONF_PATH "/%s", cfname);
+						fd = fopen(test, "r");
+						if (fd) {
+							free(cfname);
+							cfname=test;
+						} else {
+							/* This is not an error, we allow an extension to wait for something else than a real conf file. */
+							free(test);
+						}
+					}
+					if (fd)
+						fclose(fd);
+				}
+				
+				CHECK_FCT_DO( fd_ext_add( fname, cfname ),
+					{ yyerror (&yylloc, conf, "Error adding extension"); YYERROR; } );
+			}
+			;
+			
+extconf:		/* empty */
+			{
+				$$ = NULL;
+			}
+			| ':' QSTRING
+			{
+				$$ = $2;
+			}
+			;
+			
+connpeer:		{
+				memset(&fddpi, 0, sizeof(fddpi));
+				fddpi.config.pic_flags.persist = PI_PRST_ALWAYS;
+				fd_list_init( &fddpi.pi_endpoints, NULL );
+			}
+			CONNPEER '=' QSTRING peerinfo ';'
+			{
+				fddpi.pi_diamid = $4;
+				CHECK_FCT_DO( fd_peer_add ( &fddpi, conf->cnf_file, NULL, NULL ),
+					{ yyerror (&yylloc, conf, "Error adding ConnectPeer information"); YYERROR; } );
+					
+				/* Now destroy any content in the structure */
+				free(fddpi.pi_diamid);
+				free(fddpi.config.pic_realm);
+				free(fddpi.config.pic_priority);
+				while (!FD_IS_LIST_EMPTY(&fddpi.pi_endpoints)) {
+					struct fd_list * li = fddpi.pi_endpoints.next;
+					fd_list_unlink(li);
+					free(li);
+				}
+			}
+			;
+			
+peerinfo:		/* empty */
+			| '{' peerparams '}'
+			;
+			
+peerparams:		/* empty */
+			| peerparams NOIP ';'
+			{
+				if ((conf->cnf_flags.no_ip6) || (fddpi.config.pic_flags.pro3 == PI_P3_IP)) { 
+					yyerror (&yylloc, conf, "No_IP conflicts with a No_IPv6 directive.");
+					YYERROR;
+				}
+				got_peer_noip++;
+				fddpi.config.pic_flags.pro3 = PI_P3_IPv6;
+			}
+			| peerparams NOIP6 ';'
+			{
+				if ((conf->cnf_flags.no_ip4) || (fddpi.config.pic_flags.pro3 == PI_P3_IPv6)) { 
+					yyerror (&yylloc, conf, "No_IPv6 conflicts with a No_IP directive.");
+					YYERROR;
+				}
+				got_peer_noipv6++;
+				fddpi.config.pic_flags.pro3 = PI_P3_IP;
+			}
+			| peerparams NOTCP ';'
+			{
+				#ifdef DISABLE_SCTP
+					yyerror (&yylloc, conf, "No_TCP cannot be specified in daemon compiled with DISABLE_SCTP option.");
+					YYERROR;
+				#endif
+				if ((conf->cnf_flags.no_sctp) || (fddpi.config.pic_flags.pro4 == PI_P4_TCP)) { 
+					yyerror (&yylloc, conf, "No_TCP conflicts with a No_SCTP directive.");
+					YYERROR;
+				}
+				got_peer_notcp++;
+				fddpi.config.pic_flags.pro4 = PI_P4_SCTP;
+			}
+			| peerparams NOSCTP ';'
+			{
+				if ((conf->cnf_flags.no_tcp) || (fddpi.config.pic_flags.pro4 == PI_P4_SCTP)) { 
+					yyerror (&yylloc, conf, "No_SCTP conflicts with a No_TCP directive.");
+					YYERROR;
+				}
+				got_peer_nosctp++;
+				fddpi.config.pic_flags.pro4 = PI_P4_TCP;
+			}
+			| peerparams PREFERTCP ';'
+			{
+				fddpi.config.pic_flags.alg = PI_ALGPREF_TCP;
+			}
+			| peerparams OLDTLS ';'
+			{
+				fddpi.config.pic_flags.sec |= PI_SEC_TLS_OLD;
+			}
+			| peerparams NOTLS ';'
+			{
+				fddpi.config.pic_flags.sec |= PI_SEC_NONE;
+			}
+			| peerparams SEC3436 ';'
+			{
+				fddpi.config.pic_flags.sctpsec |= PI_SCTPSEC_3436;
+			}
+			| peerparams REALM '=' QSTRING ';'
+			{
+				fddpi.config.pic_realm = $4;
+			}
+			| peerparams PORT '=' INTEGER ';'
+			{
+				CHECK_PARAMS_DO( ($4 > 0) && ($4 < 1<<16),
+					{ yyerror (&yylloc, conf, "Invalid port value"); YYERROR; } );
+				fddpi.config.pic_port = (uint16_t)$4;
+			}
+			| peerparams TCTIMER '=' INTEGER ';'
+			{
+				fddpi.config.pic_tctimer = $4;
+			}
+			| peerparams TWTIMER '=' INTEGER ';'
+			{
+				fddpi.config.pic_twtimer = $4;
+			}
+			| peerparams TLS_PRIO '=' QSTRING ';'
+			{
+				fddpi.config.pic_priority = $4;
+			}
+			| peerparams CONNTO '=' QSTRING ';'
+			{
+				struct addrinfo hints, *ai;
+				int ret;
+				int disc = 0;
+				
+				memset(&hints, 0, sizeof(hints));
+				hints.ai_flags = AI_ADDRCONFIG | AI_NUMERICHOST;
+				ret = getaddrinfo($4, NULL, &hints, &ai);
+				if (ret == EAI_NONAME) {
+					/* The name was maybe not numeric, try again */
+					disc = EP_FL_DISC;
+					hints.ai_flags &= ~ AI_NUMERICHOST;
+					ret = getaddrinfo($4, NULL, &hints, &ai);
+				}
+				if (ret) { yyerror (&yylloc, conf, gai_strerror(ret)); YYERROR; }
+				
+				CHECK_FCT_DO( fd_ep_add_merge( &fddpi.pi_endpoints, ai->ai_addr, ai->ai_addrlen, EP_FL_CONF | (disc ?: EP_ACCEPTALL) ), YYERROR );
+				free($4);
+				freeaddrinfo(ai);
+			}
+			;
+
+tls_cred:		TLS_CRED '=' QSTRING ',' QSTRING ';'
+			{
+				FILE * fd;
+				fd = fopen($3, "r");
+				if (fd == NULL) {
+					int ret = errno;
+					TRACE_ERROR("Unable to open certificate file %s for reading: %s", $3, strerror(ret));
+					yyerror (&yylloc, conf, "Error on file name"); 
+					YYERROR;
+				}
+				fclose(fd);
+				fd = fopen($5, "r");
+				if (fd == NULL) {
+					int ret = errno;
+					TRACE_ERROR("Unable to open private key file %s for reading: %s", $5, strerror(ret));
+					yyerror (&yylloc, conf, "Error on file name"); 
+					YYERROR;
+				}
+				fclose(fd);
+				conf->cnf_sec_data.cert_file = $3;
+				conf->cnf_sec_data.key_file = $5;
+				
+				CHECK_GNUTLS_DO( gnutls_certificate_set_x509_key_file( 
+							conf->cnf_sec_data.credentials,
+							conf->cnf_sec_data.cert_file,
+							conf->cnf_sec_data.key_file,
+							GNUTLS_X509_FMT_PEM),
+						{ yyerror (&yylloc, conf, "Error opening certificate or private key file."); YYERROR; } );
+			}
+			;
+
+tls_ca:			TLS_CA '=' QSTRING ';'
+			{
+				FILE * fd;
+				fd = fopen($3, "rb");
+				if (fd == NULL) {
+					int ret = errno;
+					TRACE_ERROR("Unable to open CA file %s for reading: %s", $3, strerror(ret));
+					yyerror (&yylloc, conf, "Error on file name"); 
+					YYERROR;
+				}
+				#ifdef GNUTLS_VERSION_300
+				{
+					/* We import these CA in the trust list */
+					gnutls_x509_crt_t * calist;
+					unsigned int cacount;
+					gnutls_datum_t cafile;
+					
+					CHECK_FCT_DO( fd_conf_stream_to_gnutls_datum(fd, &cafile), 
+							{ yyerror (&yylloc, conf, "Error reading CA file."); YYERROR; } );
+							
+					CHECK_GNUTLS_DO( gnutls_x509_crt_list_import2(&calist, &cacount, &cafile, GNUTLS_X509_FMT_PEM, 
+										GNUTLS_X509_CRT_LIST_FAIL_IF_UNSORTED),
+							{ yyerror (&yylloc, conf, "Error importing CA file."); YYERROR; } );
+					free(cafile.data);
+					
+					CHECK_GNUTLS_DO( gnutls_x509_trust_list_add_cas (fd_g_config->cnf_sec_data.trustlist, calist, cacount, 0),
+							{ yyerror (&yylloc, conf, "Error saving CA in trust list."); YYERROR; } );
+				}
+				#endif /* GNUTLS_VERSION_300 */
+				fclose(fd);
+				conf->cnf_sec_data.ca_file = $3;
+				CHECK_GNUTLS_DO( conf->cnf_sec_data.ca_file_nr += gnutls_certificate_set_x509_trust_file( 
+							conf->cnf_sec_data.credentials,
+							conf->cnf_sec_data.ca_file,
+							GNUTLS_X509_FMT_PEM),
+						{ yyerror (&yylloc, conf, "Error setting CA parameters."); YYERROR; } );
+						
+			}
+			;
+			
+tls_crl:		TLS_CRL '=' QSTRING ';'
+			{
+				FILE * fd;
+				fd = fopen($3, "rb");
+				if (fd == NULL) {
+					int ret = errno;
+					TRACE_ERROR("Unable to open CRL file %s for reading: %s", $3, strerror(ret));
+					yyerror (&yylloc, conf, "Error on file name"); 
+					YYERROR;
+				}
+				#ifdef GNUTLS_VERSION_300
+				{
+					/* We import these CRL in the trust list */
+					gnutls_x509_crl_t * crllist;
+					unsigned int crlcount;
+					gnutls_datum_t crlfile;
+					
+					CHECK_FCT_DO( fd_conf_stream_to_gnutls_datum(fd, &crlfile), 
+							{ yyerror (&yylloc, conf, "Error reading CRL file."); YYERROR; } );
+							
+					CHECK_GNUTLS_DO( gnutls_x509_crl_list_import2(&crllist, &crlcount, &crlfile, GNUTLS_X509_FMT_PEM, 0),
+							{ yyerror (&yylloc, conf, "Error importing CRL file."); YYERROR; } );
+					free(crlfile.data);
+					
+					CHECK_GNUTLS_DO( gnutls_x509_trust_list_add_crls (fd_g_config->cnf_sec_data.trustlist, crllist, crlcount, 
+									GNUTLS_TL_VERIFY_CRL,
+									0),
+							{ yyerror (&yylloc, conf, "Error importing CRL in trust list."); YYERROR; } );
+				}
+				#endif /* GNUTLS_VERSION_300 */
+				fclose(fd);
+				conf->cnf_sec_data.crl_file = $3;
+				CHECK_GNUTLS_DO( gnutls_certificate_set_x509_crl_file( 
+							conf->cnf_sec_data.credentials,
+							conf->cnf_sec_data.crl_file,
+							GNUTLS_X509_FMT_PEM),
+						{ yyerror (&yylloc, conf, "Error setting CRL parameters."); YYERROR; } );
+			}
+			;
+			
+tls_prio:		TLS_PRIO '=' QSTRING ';'
+			{
+				const char * err_pos = NULL;
+				conf->cnf_sec_data.prio_string = $3;
+				CHECK_GNUTLS_DO( gnutls_priority_init( 
+							&conf->cnf_sec_data.prio_cache,
+							conf->cnf_sec_data.prio_string,
+							&err_pos),
+						{ yyerror (&yylloc, conf, "Error setting Priority parameter.");
+						  TRACE_ERROR("Error at position : %s", err_pos);
+						  YYERROR; } );
+			}
+			;
+			
+tls_dh:			TLS_DH_BITS '=' INTEGER ';'
+			{
+				conf->cnf_sec_data.dh_bits = $3;
+			}
+			| TLS_DH_FILE '=' QSTRING ';'
+			{
+				FILE * fd;
+				free(conf->cnf_sec_data.dh_file);
+				conf->cnf_sec_data.dh_file = $3;
+				fd = fopen($3, "r");
+				if (fd == NULL) {
+					int ret = errno;
+					TRACE_ERROR("Unable to open DH file %s for reading: %s", $3, strerror(ret));
+					yyerror (&yylloc, conf, "Error on file name"); 
+					YYERROR;
+				}
+				fclose(fd);
+			}
+			;
diff --git a/libfdcore/fifo_stats.c b/libfdcore/fifo_stats.c
new file mode 100644
index 0000000..0cffa04
--- /dev/null
+++ b/libfdcore/fifo_stats.c
@@ -0,0 +1,79 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@freediameter.net>							 *
+*													 *
+* Copyright (c) 2013, 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 "fdcore-internal.h"
+
+/* See include/freeDiameter/libfdcore.h for more information */
+int fd_stat_getstats(enum fd_stat_type stat, struct peer_hdr * peer, 
+			int * current_count, int * limit_count, int * highest_count, long long * total_count, 
+			struct timespec * total, struct timespec * blocking, struct timespec * last)
+{
+	struct fd_peer * p = (struct fd_peer *)peer;
+	TRACE_ENTRY( "%d %p %p %p %p %p %p %p %p", stat, peer, current_count, limit_count, highest_count, total_count, total, blocking, last);
+	
+	switch (stat) {
+		case STAT_G_LOCAL: {
+			CHECK_FCT( fd_fifo_getstats(fd_g_local, current_count, limit_count, highest_count, total_count, total, blocking, last) );
+		}
+		break;
+
+		case STAT_G_INCOMING: {
+			CHECK_FCT( fd_fifo_getstats(fd_g_incoming, current_count, limit_count, highest_count, total_count, total, blocking, last) );
+		}
+		break;
+
+		case STAT_G_OUTGOING: {
+			CHECK_FCT( fd_fifo_getstats(fd_g_outgoing, current_count, limit_count, highest_count, total_count, total, blocking, last) );
+		}
+		break;
+
+		case STAT_P_PSM: {
+			CHECK_PARAMS( CHECK_PEER( peer ) );
+			CHECK_FCT( fd_fifo_getstats(p->p_events, current_count, limit_count, highest_count, total_count, total, blocking, last) );
+		}
+		break;
+
+		case STAT_P_TOSEND: {
+			CHECK_PARAMS( CHECK_PEER( peer ) );
+			CHECK_FCT( fd_fifo_getstats(p->p_tosend, current_count, limit_count, highest_count, total_count, total, blocking, last) );
+		}
+		break;
+
+		default:
+			return EINVAL;
+	}
+	
+	return 0;
+}
diff --git a/libfdcore/hooks.c b/libfdcore/hooks.c
new file mode 100644
index 0000000..f035614
--- /dev/null
+++ b/libfdcore/hooks.c
@@ -0,0 +1,454 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@freediameter.net>							 *
+*													 *
+* Copyright (c) 2015, 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 "fdcore-internal.h"
+
+/* Structures for the fd_hook_data_hdl management */
+static struct fd_hook_data_hdl {
+	size_t	pmd_size;
+	void  (*pmd_init_cb)(struct fd_hook_permsgdata *);
+	void  (*pmd_fini_cb)(struct fd_hook_permsgdata *);
+} HDH_array[FD_HOOK_HANDLE_LIMIT];
+static int max_index = 0;
+static pthread_mutex_t HDH_lock = PTHREAD_MUTEX_INITIALIZER;
+
+/* The structure linked from the msg structure list */
+struct pmd_list_item {
+	struct fd_list	chain;		/* this list is ordered by hdl */
+	struct fd_hook_data_hdl * hdl; 
+	struct fd_hook_permsgdata { } pmd; /* this data belongs to the extension; we only know the size of it */
+};
+
+#define sizeof_pmd(hdl)	(((size_t)&((struct pmd_list_item *)0)->pmd) + hdl->pmd_size)
+
+/* Now a hook registered by an extension */
+struct fd_hook_hdl {
+	struct fd_list chain[HOOK_LAST+1];
+	void (*fd_hook_cb)(enum fd_hook_type type, struct msg * msg, struct peer_hdr * peer, void * other, struct fd_hook_permsgdata *pmd, void * regdata);
+	void  *regdata;
+	struct fd_hook_data_hdl *data_hdl;
+};
+
+/* Array of those hooks */
+struct {
+	struct fd_list sentinel;
+	pthread_rwlock_t rwlock;
+} HS_array[HOOK_LAST+1];
+
+/* Initialize the array of sentinels for the hooks */
+int fd_hooks_init(void)
+{
+	int i;
+	for (i=0; i <= HOOK_LAST; i++) {
+		fd_list_init(&HS_array[i].sentinel, NULL);
+		CHECK_POSIX( pthread_rwlock_init(&HS_array[i].rwlock, NULL) );
+	}
+	return 0;
+}
+
+/* Get a slot in the array */
+int fd_hook_data_register(
+	size_t permsgdata_size,
+	void (*permsgdata_init_cb) (struct fd_hook_permsgdata *),
+        void (*permsgdata_fini_cb) (struct fd_hook_permsgdata *),
+        struct fd_hook_data_hdl **new_handle)
+{
+	int ret = ENOSPC, idx;
+	TRACE_ENTRY("%zd %p %p %p", permsgdata_size, permsgdata_init_cb, permsgdata_fini_cb, new_handle);
+	
+	CHECK_PARAMS( permsgdata_size && new_handle );
+	
+	CHECK_POSIX( pthread_mutex_lock(&HDH_lock) );
+	if (max_index < FD_HOOK_HANDLE_LIMIT) {
+		idx = max_index++;
+		ret = 0;
+	}
+	CHECK_POSIX( pthread_mutex_unlock(&HDH_lock) );
+	
+	if (ret == 0) {
+		HDH_array[idx].pmd_size = permsgdata_size;
+		HDH_array[idx].pmd_init_cb = permsgdata_init_cb;
+		HDH_array[idx].pmd_fini_cb = permsgdata_fini_cb;
+		*new_handle = &HDH_array[idx];
+	}
+	
+	return ret;
+}
+
+/* Register a new hook callback */
+int fd_hook_register (  uint32_t type_mask, 
+			void (*fd_hook_cb)(enum fd_hook_type type, struct msg * msg, struct peer_hdr * peer, void * other, struct fd_hook_permsgdata *pmd, void * regdata), 
+			void  *regdata, 
+			struct fd_hook_data_hdl *data_hdl,
+			struct fd_hook_hdl ** handler )
+{
+	struct fd_hook_hdl * newhdl = NULL;
+	int i;
+	
+	TRACE_ENTRY("%x %p %p %p %p", type_mask, fd_hook_cb, regdata, data_hdl, handler);
+	
+	CHECK_PARAMS( fd_hook_cb && handler );
+	
+	CHECK_MALLOC( newhdl = malloc(sizeof(struct fd_hook_hdl)) );
+	memset(newhdl, 0, sizeof(struct fd_hook_hdl));
+	
+	newhdl->fd_hook_cb = fd_hook_cb;
+	newhdl->regdata = regdata;
+	newhdl->data_hdl = data_hdl;
+	
+	for (i=0; i <= HOOK_LAST; i++) {
+		fd_list_init(&newhdl->chain[i], newhdl);
+		if (type_mask & (1<<i)) {
+			CHECK_POSIX( pthread_rwlock_wrlock(&HS_array[i].rwlock) );
+			fd_list_insert_before( &HS_array[i].sentinel, &newhdl->chain[i]);
+			CHECK_POSIX( pthread_rwlock_unlock(&HS_array[i].rwlock) );
+		}
+	}
+	
+	*handler = newhdl;
+	return 0;
+}
+
+/* free this hook callback */
+int fd_hook_unregister( struct fd_hook_hdl * handler )
+{
+	int i;
+	TRACE_ENTRY("%p", handler);
+	CHECK_PARAMS( handler );
+	
+	for (i=0; i <= HOOK_LAST; i++) {
+		if ( ! FD_IS_LIST_EMPTY(&handler->chain[i])) {
+			CHECK_POSIX( pthread_rwlock_wrlock(&HS_array[i].rwlock) );
+			fd_list_unlink(&handler->chain[i]);
+			CHECK_POSIX( pthread_rwlock_unlock(&HS_array[i].rwlock) );
+		}
+	}
+	
+	free(handler);
+	
+	return 0;
+}
+
+/* callback for the libfdproto to free the data associated with a message */
+static void pmdl_free(struct fd_msg_pmdl *pmdl)
+{
+	/* destroy all the items in the list */
+	while (!FD_IS_LIST_EMPTY(&pmdl->sentinel)) {
+		struct pmd_list_item * li = (struct pmd_list_item *)(pmdl->sentinel.next);
+		if (li->hdl->pmd_fini_cb) {
+			(*li->hdl->pmd_fini_cb)(&li->pmd);
+		}
+		fd_list_unlink(&li->chain);
+		free(li);
+	}
+	CHECK_POSIX_DO( pthread_mutex_destroy(&pmdl->lock), );
+	pmdl->sentinel.o = NULL;
+}
+
+/* Save the list of pmd into the message structure, as well as the callback to free this list */
+void   fd_hook_associate(struct msg * msg, struct fd_msg_pmdl * pmdl)
+{
+	struct fd_msg_pmdl * in_msg;
+	
+	CHECK_PARAMS_DO( msg && pmdl, return );
+	in_msg = fd_msg_pmdl_get(msg);
+	ASSERT(in_msg && (in_msg->sentinel.o == NULL)); /* error / already initialized ??? */
+	in_msg->sentinel.o = pmdl_free;
+	/* Now move all items from the pmdl pointer into the initialized list */
+	CHECK_POSIX_DO( pthread_mutex_lock(&pmdl->lock), );
+	fd_list_move_end(&in_msg->sentinel, &pmdl->sentinel);
+	CHECK_POSIX_DO( pthread_mutex_unlock(&pmdl->lock), );
+	pmdl_free(pmdl);
+	/* We're done */
+}
+
+/* Return the location of the permsgdata area corresponding to this handle, after eventually having created it. Return NULL in case of failure */
+static struct fd_hook_permsgdata * get_or_create_pmd(struct fd_msg_pmdl *pmdl, struct fd_hook_hdl * h) 
+{
+	struct fd_hook_permsgdata * ret = NULL;
+	struct fd_list * li;
+	
+	CHECK_POSIX_DO( pthread_mutex_lock(&pmdl->lock), );
+	
+	if (pmdl->sentinel.o == NULL) {
+		pmdl->sentinel.o = pmdl_free;
+	}
+	
+	/* Search in the list for an item with the same handle. The list is ordered by this handle */
+	for (li=pmdl->sentinel.next; li != &pmdl->sentinel; li = li->next) {
+		struct pmd_list_item * pli = (struct pmd_list_item *) li;
+		if (pli->hdl == h->data_hdl)
+			ret = &pli->pmd;
+		if (pli->hdl >= h->data_hdl)
+			break;
+	}
+	if (!ret) {
+		/* we need to create a new one and insert before li */
+		struct pmd_list_item * pli;
+		CHECK_MALLOC_DO( pli = malloc(sizeof_pmd(h->data_hdl)), );
+		if (pli) {
+			memset(pli, 0, sizeof_pmd(h->data_hdl));
+			fd_list_init(&pli->chain, pli);
+			pli->hdl = h->data_hdl;
+			ret = &pli->pmd;
+			if (h->data_hdl->pmd_init_cb) {
+				(*h->data_hdl->pmd_init_cb)(ret);
+			}
+			fd_list_insert_before(li, &pli->chain);
+		}
+	}
+	
+	CHECK_POSIX_DO( pthread_mutex_unlock(&pmdl->lock), );
+	return ret;
+}
+
+struct fd_hook_permsgdata * fd_hook_get_request_pmd(struct fd_hook_data_hdl *data_hdl, struct msg * answer)
+{
+	struct msg * qry;
+	struct fd_msg_pmdl *pmdl;
+	struct fd_hook_permsgdata * ret = NULL;
+	struct fd_list * li;
+	
+	CHECK_FCT_DO( fd_msg_answ_getq(answer, &qry), return NULL );
+	if (!qry)
+		return NULL;
+	
+	pmdl = fd_msg_pmdl_get(qry);
+	if (!pmdl)
+		return NULL;
+	
+	CHECK_POSIX_DO( pthread_mutex_lock(&pmdl->lock), );
+	/* Search in the list for an item with the same handle. The list is ordered by this handle */
+	for (li=pmdl->sentinel.next; li != &pmdl->sentinel; li = li->next) {
+		struct pmd_list_item * pli = (struct pmd_list_item *) li;
+		if (pli->hdl == data_hdl)
+			ret = &pli->pmd;
+		if (pli->hdl >= data_hdl)
+			break;
+	}
+	CHECK_POSIX_DO( pthread_mutex_unlock(&pmdl->lock), );
+	return ret;
+}
+
+/* Create a mask */
+uint32_t fd_hook_mask_helper(int dummy, ...)
+{
+	va_list ap;
+	uint32_t ret = 0;
+	int next;
+	
+	va_start(ap, dummy);
+	while ((next = va_arg(ap, int)) >= 0) {
+		if (next > HOOK_LAST)
+			break; /* invalid parameter */
+		ret |= (1<<next);
+	}
+	va_end(ap);
+
+	return ret;
+}
+
+static pthread_mutex_t hook_default_mtx = PTHREAD_MUTEX_INITIALIZER;
+static char * hook_default_buf = NULL;
+static size_t hook_default_len = 0;
+
+/* The function that does the work of calling the extension's callbacks and also managing the permessagedata structures */
+void   fd_hook_call(enum fd_hook_type type, struct msg * msg, struct fd_peer * peer, void * other, struct fd_msg_pmdl * pmdl)
+{
+	struct fd_list * li;
+	ASSERT(type <= HOOK_LAST);
+	int call_default = 0;
+	
+	/* lock the list of hooks for this type */
+	CHECK_POSIX_DO( pthread_rwlock_rdlock(&HS_array[type].rwlock), );
+	
+	pthread_cleanup_push( fd_cleanup_rwlock, &HS_array[type].rwlock );
+	
+	if (FD_IS_LIST_EMPTY(&HS_array[type].sentinel)) {
+		call_default = 1;
+	} else {
+		/* for each registered hook */
+		for (li = HS_array[type].sentinel.next; li != &HS_array[type].sentinel; li = li->next) {
+			struct fd_hook_hdl * h = (struct fd_hook_hdl *)li->o;
+			struct fd_hook_permsgdata * pmd = NULL;
+
+			/* do we need to handle pmd ? */
+			if (h->data_hdl && pmdl) {
+				pmd = get_or_create_pmd(pmdl, h);
+			}
+
+			/* Now, call this callback */
+			(*h->fd_hook_cb)(type, msg, &peer->p_hdr, other, pmd, h->regdata);
+		}
+	}
+	
+	pthread_cleanup_pop(0);
+	
+	/* done */
+	CHECK_POSIX_DO( pthread_rwlock_unlock(&HS_array[type].rwlock), );
+	
+	if (call_default) {
+		CHECK_POSIX_DO( pthread_mutex_lock(&hook_default_mtx), );
+		
+		pthread_cleanup_push( fd_cleanup_mutex, &hook_default_mtx );
+	
+		/* There was no registered handler, default behavior for this hook */
+		switch (type) {
+			case HOOK_DATA_RECEIVED: {
+				struct fd_cnx_rcvdata *rcv_data = other;
+				LOG_A("RCV: %zd bytes", rcv_data->length);
+				break;
+			}
+			
+			case HOOK_MESSAGE_RECEIVED: {
+				CHECK_MALLOC_DO(fd_msg_dump_summary(&hook_default_buf, &hook_default_len, NULL, msg, NULL, 0, 1), break);
+				LOG_D("RCV from '%s': %s", peer ? peer->p_hdr.info.pi_diamid : "<unknown>", hook_default_buf);
+				break;
+			}
+			
+			case HOOK_MESSAGE_LOCAL: {
+				CHECK_MALLOC_DO(fd_msg_dump_full(&hook_default_buf, &hook_default_len, NULL, msg, NULL, 0, 1), break);
+				LOG_A("Handled to framework for sending: %s", hook_default_buf);
+				break;
+			}
+			
+			case HOOK_MESSAGE_SENDING: {
+				LOG_A("SENDING message to '%s'", peer ? peer->p_hdr.info.pi_diamid : "<unknown>");
+				break;
+			}
+			
+			case HOOK_MESSAGE_SENT: {
+				CHECK_MALLOC_DO(fd_msg_dump_summary(&hook_default_buf, &hook_default_len, NULL, msg, NULL, 0, 1), break);
+				LOG_D("SENT to '%s': %s", peer ? peer->p_hdr.info.pi_diamid : "<unknown>", hook_default_buf);
+				break;
+			}
+			
+			case HOOK_MESSAGE_FAILOVER: {
+				CHECK_MALLOC_DO(fd_msg_dump_summary(&hook_default_buf, &hook_default_len, NULL, msg, NULL, 0, 1), break);
+				LOG_D("Failing over message sent to '%s': %s", peer ? peer->p_hdr.info.pi_diamid : "<unknown>", hook_default_buf);
+				break;
+			}
+			
+			case HOOK_MESSAGE_PARSING_ERROR: {
+				if (msg) {
+					DiamId_t id = NULL;
+					if (fd_msg_source_get( msg, &id, NULL ))
+						id = (DiamId_t)"<error getting source>";
+					
+					if (!id)
+						id = (DiamId_t)"<local>";
+					
+					CHECK_MALLOC_DO(fd_msg_dump_treeview(&hook_default_buf, &hook_default_len, NULL, msg, NULL, 0, 1), break);
+					
+					LOG_E("Parsing error: '%s' for the following message received from '%s':", (char *)other, (char *)id);
+					LOG_SPLIT(FD_LOG_ERROR, "   ", hook_default_buf, NULL);
+				} else {
+					struct fd_cnx_rcvdata *rcv_data = other;
+					CHECK_MALLOC_DO(fd_dump_extend_hexdump(&hook_default_buf, &hook_default_len, NULL, rcv_data->buffer, rcv_data->length, 0, 0), break);
+					LOG_E("Parsing error: cannot parse %zdB buffer from '%s': %s",  rcv_data->length, peer ? peer->p_hdr.info.pi_diamid : "<unknown>", hook_default_buf);
+				}
+				break;
+			}
+			
+			case HOOK_MESSAGE_PARSING_ERROR2: {
+				CHECK_MALLOC_DO(fd_msg_dump_treeview(&hook_default_buf, &hook_default_len, NULL, msg, NULL, 0, 1), break);
+
+				LOG_E("Returning following message after parsing error:");
+				LOG_SPLIT(FD_LOG_ERROR, "   ", hook_default_buf, NULL);
+				break;
+			}
+			
+			case HOOK_MESSAGE_ROUTING_ERROR: {
+				CHECK_MALLOC_DO(fd_msg_dump_treeview(&hook_default_buf, &hook_default_len, NULL, msg, NULL, 0, 1), break);
+				LOG_E("Routing error: '%s' for the following message:", (char *)other);
+				LOG_SPLIT(FD_LOG_ERROR, "   ", hook_default_buf, NULL);
+				break;
+			}
+			
+			case HOOK_MESSAGE_ROUTING_FORWARD: {
+				CHECK_MALLOC_DO(fd_msg_dump_summary(&hook_default_buf, &hook_default_len, NULL, msg, NULL, 0, 1), break);
+				LOG_D("FORWARDING: %s", hook_default_buf);
+				break;
+			}
+			
+			case HOOK_MESSAGE_ROUTING_LOCAL: {
+				CHECK_MALLOC_DO(fd_msg_dump_summary(&hook_default_buf, &hook_default_len, NULL, msg, NULL, 0, 1), break);
+				LOG_D("DISPATCHING: %s", hook_default_buf);
+				break;
+			}
+			
+			case HOOK_MESSAGE_DROPPED: {
+				CHECK_MALLOC_DO(fd_msg_dump_treeview(&hook_default_buf, &hook_default_len, NULL, msg, NULL, 0, 1), break);
+				LOG_E("Message discarded ('%s'):", (char *)other);
+				LOG_SPLIT(FD_LOG_ERROR, "   ", hook_default_buf, NULL);
+				break;
+			}
+			
+			case HOOK_PEER_CONNECT_FAILED: {
+				if (msg) {
+					CHECK_MALLOC_DO(fd_msg_dump_full(&hook_default_buf, &hook_default_len, NULL, msg, NULL, 0, 1), break);
+					LOG_N("Connection to '%s' failed: '%s'; CER/CEA dump:", peer ? peer->p_hdr.info.pi_diamid : "<unknown>", (char *)other);
+					LOG_SPLIT(FD_LOG_NOTICE, "   ", hook_default_buf, NULL);
+				} else {
+					LOG_D("Connection to '%s' failed: %s", peer ? peer->p_hdr.info.pi_diamid : "<unknown>", (char *)other);
+				}
+				break;
+			}
+			
+			case HOOK_PEER_CONNECT_SUCCESS: {
+				DiamId_t id = NULL;
+				if ((!fd_msg_source_get( msg, &id, NULL )) && (id == NULL)) { /* The CEA is locally issued */
+					fd_msg_answ_getq(msg, &msg); /* We dump the CER in that case */
+				}
+				CHECK_MALLOC_DO(fd_msg_dump_full(&hook_default_buf, &hook_default_len, NULL, msg, NULL, 0, 1), break);
+				char protobuf[40];
+				if (peer) {
+					CHECK_FCT_DO(fd_peer_cnx_proto_info(&peer->p_hdr, protobuf, sizeof(protobuf)), break );
+				} else {
+					protobuf[0] = '-';
+					protobuf[1] = '\0';
+				}
+				LOG_N("Connected to '%s' (%s), remote capabilities: ", peer ? peer->p_hdr.info.pi_diamid : "<unknown>", protobuf);
+				LOG_SPLIT(FD_LOG_NOTICE, "   ", hook_default_buf, NULL);
+				break;
+			}
+			
+		}
+		
+		pthread_cleanup_pop(0);
+		
+		CHECK_POSIX_DO( pthread_mutex_unlock(&hook_default_mtx), );
+	}
+}
diff --git a/libfdcore/messages.c b/libfdcore/messages.c
new file mode 100644
index 0000000..2242827
--- /dev/null
+++ b/libfdcore/messages.c
@@ -0,0 +1,452 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@freediameter.net>							 *
+*													 *
+* Copyright (c) 2015, 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 "fdcore-internal.h"
+
+static struct dict_object * dict_avp_SI  = NULL; /* Session-Id */
+static struct dict_object * dict_avp_OH  = NULL; /* Origin-Host */
+static struct dict_object * dict_avp_OR  = NULL; /* Origin-Realm */
+static struct dict_object * dict_avp_EM  = NULL; /* Error-Message */
+static struct dict_object * dict_avp_ERH = NULL; /* Error-Reporting-Host */
+static struct dict_object * dict_avp_FAVP= NULL; /* Failed-AVP */
+static struct dict_object * dict_avp_RC  = NULL; /* Result-Code */
+struct dict_object * fd_dict_avp_OSI = NULL; /* Origin-State-Id */
+struct dict_object * fd_dict_cmd_CER = NULL; /* Capabilities-Exchange-Request */
+struct dict_object * fd_dict_cmd_DWR = NULL; /* Device-Watchdog-Request */
+struct dict_object * fd_dict_avp_DC  = NULL; /* Disconnect-Cause */
+struct dict_object * fd_dict_cmd_DPR = NULL; /* Disconnect-Peer-Request */
+
+/* Resolve the dictionary objects */
+int fd_msg_init(void)
+{
+	TRACE_ENTRY("");
+	
+	/* Initialize the dictionary objects that we may use frequently */
+	CHECK_FCT(  fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Session-Id", 	&dict_avp_SI , ENOENT)  );
+	CHECK_FCT(  fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Origin-Host",     	&dict_avp_OH  , ENOENT)  );
+	CHECK_FCT(  fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Origin-Realm",    	&dict_avp_OR  , ENOENT)  );
+	CHECK_FCT(  fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Origin-State-Id", 	&fd_dict_avp_OSI , ENOENT)  );
+	
+	CHECK_FCT(  fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Result-Code",     	&dict_avp_RC  , ENOENT)  );
+	CHECK_FCT(  fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Error-Message",   	&dict_avp_EM  , ENOENT)  );
+	CHECK_FCT(  fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Error-Reporting-Host", &dict_avp_ERH , ENOENT)  );
+	CHECK_FCT(  fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Failed-AVP",      	&dict_avp_FAVP, ENOENT)  );
+	
+	CHECK_FCT(  fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Disconnect-Cause", 	&fd_dict_avp_DC , ENOENT)  );
+	
+	CHECK_FCT( fd_dict_search ( fd_g_config->cnf_dict, DICT_COMMAND, CMD_BY_NAME, "Capabilities-Exchange-Request", &fd_dict_cmd_CER, ENOENT ) );
+	CHECK_FCT( fd_dict_search ( fd_g_config->cnf_dict, DICT_COMMAND, CMD_BY_NAME, "Device-Watchdog-Request", &fd_dict_cmd_DWR, ENOENT ) );
+	CHECK_FCT( fd_dict_search ( fd_g_config->cnf_dict, DICT_COMMAND, CMD_BY_NAME, "Disconnect-Peer-Request", &fd_dict_cmd_DPR, ENOENT ) );
+	
+	
+	return 0;
+}
+
+/* Add Origin-Host, Origin-Realm, Origin-State-Id AVPS at the end of the message */
+int fd_msg_add_origin ( struct msg * msg, int osi )
+{
+	union avp_value val;
+	struct avp * avp_OH  = NULL;
+	struct avp * avp_OR  = NULL;
+	struct avp * avp_OSI = NULL;
+	
+	TRACE_ENTRY("%p", msg);
+	CHECK_PARAMS(  msg  );
+	
+	/* Create the Origin-Host AVP */
+	CHECK_FCT( fd_msg_avp_new( dict_avp_OH, 0, &avp_OH ) );
+	
+	/* Set its value */
+	memset(&val, 0, sizeof(val));
+	val.os.data = (os0_t)fd_g_config->cnf_diamid;
+	val.os.len  = fd_g_config->cnf_diamid_len;
+	CHECK_FCT( fd_msg_avp_setvalue( avp_OH, &val ) );
+	
+	/* Add it to the message */
+	CHECK_FCT( fd_msg_avp_add( msg, MSG_BRW_LAST_CHILD, avp_OH ) );
+	
+	
+	/* Create the Origin-Realm AVP */
+	CHECK_FCT( fd_msg_avp_new( dict_avp_OR, 0, &avp_OR ) );
+	
+	/* Set its value */
+	memset(&val, 0, sizeof(val));
+	val.os.data = (os0_t)fd_g_config->cnf_diamrlm;
+	val.os.len  = fd_g_config->cnf_diamrlm_len;
+	CHECK_FCT( fd_msg_avp_setvalue( avp_OR, &val ) );
+	
+	/* Add it to the message */
+	CHECK_FCT( fd_msg_avp_add( msg, MSG_BRW_LAST_CHILD, avp_OR ) );
+	
+	if (osi) {
+		/* Create the Origin-State-Id AVP */
+		CHECK_FCT( fd_msg_avp_new( fd_dict_avp_OSI, 0, &avp_OSI ) );
+
+		/* Set its value */
+		memset(&val, 0, sizeof(val));
+		val.u32 = fd_g_config->cnf_orstateid;
+		CHECK_FCT( fd_msg_avp_setvalue( avp_OSI, &val ) );
+
+		/* Add it to the message */
+		CHECK_FCT( fd_msg_avp_add( msg, MSG_BRW_LAST_CHILD, avp_OSI ) );
+	}
+	
+	return 0;
+}
+
+/* Create a new Session-Id and add at the beginning of the message. */
+int fd_msg_new_session( struct msg * msg, os0_t opt, size_t optlen )
+{
+	union avp_value val;
+	struct avp * avp  = NULL;
+	struct session * sess = NULL;
+	os0_t sid;
+	size_t sidlen;
+	
+	TRACE_ENTRY("%p %p %zd", msg, opt, optlen);
+	CHECK_PARAMS(  msg  );
+	
+	/* Check there is not already a session in the message */
+	CHECK_FCT( fd_msg_sess_get(fd_g_config->cnf_dict, msg, &sess, NULL) );
+	CHECK_PARAMS( sess == NULL );
+	
+	/* Ok, now create the session */
+	CHECK_FCT( fd_sess_new ( &sess, fd_g_config->cnf_diamid, fd_g_config->cnf_diamid_len, opt, optlen ) );
+	CHECK_FCT( fd_sess_getsid( sess, &sid, &sidlen) );
+	
+	/* Create an AVP to hold it */
+	CHECK_FCT( fd_msg_avp_new( dict_avp_SI, 0, &avp ) );
+	
+	/* Set its value */
+	memset(&val, 0, sizeof(val));
+	val.os.data = sid;
+	val.os.len  = sidlen;
+	CHECK_FCT( fd_msg_avp_setvalue( avp, &val ) );
+	
+	/* Add it to the message */
+	CHECK_FCT( fd_msg_avp_add( msg, MSG_BRW_FIRST_CHILD, avp ) );
+	
+	/* Save the session associated with the message */
+	CHECK_FCT( fd_msg_sess_set( msg, sess) );
+	
+	/* Done! */
+	return 0;
+}
+
+
+/* Add Result-Code and eventually Failed-AVP, Error-Message and Error-Reporting-Host AVPs */
+int fd_msg_rescode_set( struct msg * msg, char * rescode, char * errormsg, struct avp * optavp, int type_id )
+{
+	union avp_value val;
+	struct avp * avp_RC  = NULL;
+	struct avp * avp_EM  = NULL;
+	struct avp * avp_ERH = NULL;
+	struct avp * avp_FAVP= NULL;
+	uint32_t rc_val = 0;
+	int set_e_bit=0;
+	int std_err_msg=0;
+	
+	TRACE_ENTRY("%p %s %p %p %d", msg, rescode, errormsg, optavp, type_id);
+		
+	CHECK_PARAMS(  msg && rescode  );
+	
+	/* Find the enum value corresponding to the rescode string, this will give the class of error */
+	{
+		struct dict_object * enum_obj = NULL;
+		struct dict_enumval_request req;
+		memset(&req, 0, sizeof(struct dict_enumval_request));
+		
+		/* First, get the enumerated type of the Result-Code AVP (this is fast, no need to cache the object) */
+		CHECK_FCT(  fd_dict_search( fd_g_config->cnf_dict, DICT_TYPE, TYPE_OF_AVP, dict_avp_RC, &(req.type_obj), ENOENT  )  );
+		
+		/* Now search for the value given as parameter */
+		req.search.enum_name = rescode;
+		CHECK_FCT(  fd_dict_search( fd_g_config->cnf_dict, DICT_ENUMVAL, ENUMVAL_BY_STRUCT, &req, &enum_obj, ENOTSUP)  );
+		
+		/* finally retrieve its data */
+		CHECK_FCT_DO(  fd_dict_getval( enum_obj, &(req.search) ), return EINVAL );
+		
+		/* copy the found value, we're done */
+		rc_val = req.search.enum_value.u32;
+	}
+	
+	if (type_id == 1) {
+		/* Add the Origin-Host and Origin-Realm AVP */
+		CHECK_FCT( fd_msg_add_origin ( msg, 0 ) );
+	}
+	
+	/* Create the Result-Code AVP */
+	CHECK_FCT( fd_msg_avp_new( dict_avp_RC, 0, &avp_RC ) );
+	
+	/* Set its value */
+	memset(&val, 0, sizeof(val));
+	val.u32  = rc_val;
+	CHECK_FCT( fd_msg_avp_setvalue( avp_RC, &val ) );
+	
+	/* Add it to the message */
+	CHECK_FCT( fd_msg_avp_add( msg, MSG_BRW_LAST_CHILD, avp_RC ) );
+	
+	if (type_id == 2) {
+		/* Add the Error-Reporting-Host AVP */
+		
+		CHECK_FCT( fd_msg_avp_new( dict_avp_ERH, 0, &avp_ERH ) );
+
+		/* Set its value */
+		memset(&val, 0, sizeof(val));
+		val.os.data = (uint8_t *)fd_g_config->cnf_diamid;
+		val.os.len  = fd_g_config->cnf_diamid_len;
+		CHECK_FCT( fd_msg_avp_setvalue( avp_ERH, &val ) );
+
+		/* Add it to the message */
+		CHECK_FCT( fd_msg_avp_add( msg, MSG_BRW_LAST_CHILD, avp_ERH ) );
+	
+	}
+	
+	/* Now add the optavp in a FailedAVP if provided */
+	if (optavp) {
+		struct avp * optavp_cpy = NULL;
+		struct avp_hdr *opt_hdr, *optcpy_hdr;
+		struct dict_object * opt_model = NULL;
+		int is_grouped = 0;
+		
+		/* Create the Failed-AVP AVP */
+		CHECK_FCT( fd_msg_avp_new( dict_avp_FAVP, 0, &avp_FAVP ) );
+		
+		/* Was this AVP a grouped one? Best effort only here */
+		if (!fd_msg_model ( optavp, &opt_model ) && (opt_model != NULL)) {
+			struct dict_avp_data  dictdata;
+			CHECK_FCT(  fd_dict_getval(opt_model, &dictdata)  );
+			if (dictdata.avp_basetype == AVP_TYPE_GROUPED) 
+				is_grouped = 1;
+		}
+		
+		/* Create a new AVP with a copy of the data of the invalid or missing AVP */
+		optavp_cpy = optavp;
+		
+		if (is_grouped) {
+			CHECK_FCT( fd_msg_avp_new( opt_model, 0, &optavp_cpy) );
+		} else {
+			CHECK_FCT( fd_msg_avp_new( NULL, AVPFL_SET_BLANK_VALUE | AVPFL_SET_RAWDATA_FROM_AVP, &optavp_cpy) );
+		
+			CHECK_FCT( fd_msg_avp_hdr(optavp, &opt_hdr) );
+			CHECK_FCT( fd_msg_avp_hdr(optavp_cpy, &optcpy_hdr) );
+			memcpy(optcpy_hdr, opt_hdr, sizeof(struct avp_hdr));
+		}
+		
+		/* Add the passed AVP inside it */
+		CHECK_FCT( fd_msg_avp_add( avp_FAVP, MSG_BRW_LAST_CHILD, optavp_cpy ) );
+		
+		/* And add to the message */
+		CHECK_FCT( fd_msg_avp_add( msg, MSG_BRW_LAST_CHILD, avp_FAVP ) );
+	}
+	
+	
+	/* Deal with the 'E' bit and the error message */
+	switch (rc_val / 1000) {
+		case 1:	/* Informational */
+		case 2: /* Success */
+			/* Nothing special here: no E bit, no error message unless one is specified */
+			break;
+			
+		case 3: /* Protocol Errors */
+			set_e_bit = 1;
+			std_err_msg = 1;
+			break;
+			
+		case 4: /* Transcient Failure */
+		case 5: /* Permanent Failure */
+		default:
+			std_err_msg = 1;
+			break;
+			
+	}
+	
+	{
+		struct msg_hdr * hdr = NULL;
+		
+		CHECK_FCT(  fd_msg_hdr( msg, &hdr )  );
+		
+		if (set_e_bit)
+			hdr->msg_flags |= CMD_FLAG_ERROR;
+		else
+			hdr->msg_flags &= ~ CMD_FLAG_ERROR;
+	}
+	
+	if (std_err_msg || errormsg) {
+		/* Add the Error-Message AVP */
+		
+		CHECK_FCT( fd_msg_avp_new( dict_avp_EM, 0, &avp_EM ) );
+
+		/* Set its value */
+		memset(&val, 0, sizeof(val));
+		
+		if (errormsg) {
+			val.os.data = (uint8_t *)errormsg;
+			val.os.len  = strlen(errormsg);
+		} else {
+			val.os.data = (uint8_t *)rescode;
+			val.os.len  = strlen(rescode);
+		}
+		CHECK_FCT( fd_msg_avp_setvalue( avp_EM, &val ) );
+
+		/* Add it to the message */
+		CHECK_FCT( fd_msg_avp_add( msg, MSG_BRW_LAST_CHILD, avp_EM ) );
+	}
+	
+	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;
+	
+	/* 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, NULL) == 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 optionally 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 );
+	
+	return fd_msg_send_int(pmsg, anscb, data, NULL, NULL);
+}
+
+/* The variation of the same function with a timeout callback */
+int fd_msg_send_timeout ( struct msg ** pmsg, void (*anscb)(void *, struct msg **), void * data, void (*expirecb)(void *, DiamId_t, size_t, struct msg **), const struct timespec *timeout )
+{
+	TRACE_ENTRY("%p %p %p %p %p", pmsg, anscb, data, expirecb, timeout);
+	CHECK_PARAMS( pmsg && expirecb && timeout );
+	
+	return fd_msg_send_int(pmsg, anscb, data, expirecb, timeout);
+}
+
+
+/* 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, struct msg **error)
+{
+	int ret = 0;
+	struct msg * m;
+	struct msg_hdr * hdr = NULL;
+	struct fd_pei	pei;
+	
+	TRACE_ENTRY("%p", msg);
+	
+	CHECK_PARAMS(msg && *msg && error);
+	m = *msg;
+	*error = NULL;
+	
+	/* Parse the message against our dictionary */
+	ret = fd_msg_parse_rules ( m, fd_g_config->cnf_dict, &pei);
+	if 	((ret != EBADMSG) 	/* Parsing grouped AVP failed / Conflicting rule found */
+		&& (ret != ENOTSUP))	/* Command is not supported / Mandatory AVP is not supported */
+		return ret; /* 0 or another error */
+	
+	/* Log */
+	fd_hook_call(HOOK_MESSAGE_PARSING_ERROR, m, NULL, pei.pei_message ?: pei.pei_errcode, fd_msg_pmdl_get(m));
+	
+	CHECK_FCT( fd_msg_hdr(m, &hdr) );
+	
+	/* Now create an answer error if the message is a query */
+	if (hdr->msg_flags & CMD_FLAG_REQUEST) {
+		
+		/* Create the error message */
+		CHECK_FCT( fd_msg_new_answer_from_req ( fd_g_config->cnf_dict, &m, pei.pei_protoerr ? MSGFL_ANSW_ERROR : 0 ) );
+		
+		/* Set the error code */
+		CHECK_FCT( fd_msg_rescode_set(m, pei.pei_errcode, pei.pei_message, pei.pei_avp, 1 ) );
+		
+		/* free the pei AVP to avoid memory leak */
+		if (pei.pei_avp_free) {
+			fd_msg_free(pei.pei_avp);
+		}
+		
+		*msg = NULL;
+		*error = m;
+		
+	} else {
+		do { /* Rescue error messages */
+			struct avp * avp;
+			union avp_value * rc = NULL;
+			
+			/* Search the Result-Code AVP */
+			CHECK_FCT_DO(  fd_msg_browse(*msg, MSG_BRW_FIRST_CHILD, &avp, NULL), break  );
+			while (avp) {
+				struct avp_hdr * ahdr;
+				CHECK_FCT_DO(  fd_msg_avp_hdr( avp, &ahdr ), break  );
+				
+				if ((ahdr->avp_code == AC_RESULT_CODE) && (! (ahdr->avp_flags & AVP_FLAG_VENDOR)) ) {
+					/* Parse this AVP */
+					ASSERT( ahdr->avp_value );
+					rc = ahdr->avp_value;
+					break;
+				}
+				
+				/* Go to next AVP */
+				CHECK_FCT_DO(  fd_msg_browse(avp, MSG_BRW_NEXT, &avp, NULL), break  );
+			}
+			
+			if (rc) {
+				switch (rc->u32 / 1000) {
+					case 1:	/* 1xxx : Informational */
+					case 2:	/* 2xxx : Sucess */
+						/* In these cases, we want the message to validate the ABNF, so we will discard the bad message */
+						break;
+						
+					default: /* Other errors */
+						/* We let the application decide what to do with the message, we rescue it */
+						*error = m;
+				}
+			}
+		} while (0);
+	}
+	
+	return EBADMSG; /* We convert ENOTSUP to EBADMSG as well */
+}
diff --git a/libfdcore/p_ce.c b/libfdcore/p_ce.c
new file mode 100644
index 0000000..1b4cd13
--- /dev/null
+++ b/libfdcore/p_ce.c
@@ -0,0 +1,1103 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@freediameter.net>							 *
+*													 *
+* Copyright (c) 2015, 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 "fdcore-internal.h"
+
+/* This file contains code to handle Capabilities Exchange messages (CER and CEA) and election process */
+
+/* Save a connection as peer's principal */
+static int set_peer_cnx(struct fd_peer * peer, struct cnxctx **cnx)
+{
+	CHECK_PARAMS( peer->p_cnxctx == NULL );
+	
+	/* Save the connection in peer */
+	peer->p_cnxctx = *cnx;
+	*cnx = NULL;
+	
+	/* Set the events to be sent to the PSM */
+	CHECK_FCT( fd_cnx_recv_setaltfifo(peer->p_cnxctx, peer->p_events) );
+	
+	/* Read the credentials if possible */
+	if (fd_cnx_getTLS(peer->p_cnxctx)) {
+		CHECK_FCT( fd_cnx_getcred(peer->p_cnxctx, &peer->p_hdr.info.runtime.pir_cert_list, &peer->p_hdr.info.runtime.pir_cert_list_size) );
+	}
+	
+	/* Read the endpoints, maybe used to reconnect to the peer later */
+	CHECK_FCT( fd_cnx_getremoteeps(peer->p_cnxctx, &peer->p_hdr.info.pi_endpoints) );
+	
+	/* Read the protocol */
+	peer->p_hdr.info.runtime.pir_proto = fd_cnx_getproto(peer->p_cnxctx);
+	
+	return 0;
+}
+
+/* Delete the peer connection, and cleanup associated information */
+void fd_p_ce_clear_cnx(struct fd_peer * peer, struct cnxctx ** cnx_kept)
+{
+	peer->p_hdr.info.runtime.pir_cert_list = NULL;
+	peer->p_hdr.info.runtime.pir_cert_list_size = 0;
+	peer->p_hdr.info.runtime.pir_proto = 0;
+	
+	if (peer->p_cnxctx) {
+		if (cnx_kept != NULL) {
+			*cnx_kept = peer->p_cnxctx;
+		} else {
+			fd_cnx_destroy(peer->p_cnxctx);
+		}
+		peer->p_cnxctx = NULL;
+	}
+}
+
+/* Election: compare the Diameter Ids by lexical order, return true if the election is won */
+static __inline__ int election_result(struct fd_peer * peer)
+{
+	int ret = (strcasecmp(peer->p_hdr.info.pi_diamid, fd_g_config->cnf_diamid) < 0);
+	if (ret) {
+		TRACE_DEBUG(INFO, "Election WON against peer '%s'", peer->p_hdr.info.pi_diamid);
+	} else {
+		TRACE_DEBUG(INFO, "Election LOST against peer '%s'", peer->p_hdr.info.pi_diamid);
+	}
+	return ret;
+}
+
+/* Add AVPs about local information in a CER or CEA */
+static int add_CE_info(struct msg *msg, struct cnxctx * cnx, int isi_tls, int isi_none)
+{
+	struct dict_object * dictobj = NULL;
+	struct avp * avp = NULL;
+	union avp_value val;
+	struct fd_list *li;
+	
+	/* Add the Origin-* AVPs */
+	CHECK_FCT( fd_msg_add_origin ( msg, 1 ) );
+	
+	/* Find the model for Host-IP-Address AVP */
+	CHECK_FCT(  fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Host-IP-Address", &dictobj, ENOENT )  );
+		
+	/* Add the AVP(s) -- not sure what is the purpose... We could probably only add the primary one ? */
+	for (li = fd_g_config->cnf_endpoints.next; li != &fd_g_config->cnf_endpoints; li = li->next) {
+		struct fd_endpoint * ep = (struct fd_endpoint *)li;
+		CHECK_FCT( fd_msg_avp_new ( dictobj, 0, &avp ) );
+		CHECK_FCT( fd_msg_avp_value_encode ( &ep->ss, avp ) );
+		CHECK_FCT( fd_msg_avp_add( msg, MSG_BRW_LAST_CHILD, avp ) );
+	}
+	
+	/* Vendor-Id, Product-Name, and Firmware-Revision AVPs */
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Vendor-Id", &dictobj, ENOENT )  );
+	CHECK_FCT( fd_msg_avp_new ( dictobj, 0, &avp ) );
+	val.u32 = MY_VENDOR_ID;
+	CHECK_FCT( fd_msg_avp_setvalue( avp, &val ) );
+	CHECK_FCT( fd_msg_avp_add( msg, MSG_BRW_LAST_CHILD, avp ) );
+	
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Product-Name", &dictobj, ENOENT )  );
+	CHECK_FCT( fd_msg_avp_new ( dictobj, 0, &avp ) );
+	val.os.data = (unsigned char *)FD_PROJECT_NAME;
+	val.os.len = strlen(FD_PROJECT_NAME);
+	CHECK_FCT( fd_msg_avp_setvalue( avp, &val ) );
+	CHECK_FCT( fd_msg_avp_add( msg, MSG_BRW_LAST_CHILD, avp ) );
+	
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Firmware-Revision", &dictobj, ENOENT )  );
+	CHECK_FCT( fd_msg_avp_new ( dictobj, 0, &avp ) );
+	val.u32 = (uint32_t)(FD_PROJECT_VERSION_MAJOR * 10000 + FD_PROJECT_VERSION_MINOR * 100 + FD_PROJECT_VERSION_REV);
+	CHECK_FCT( fd_msg_avp_setvalue( avp, &val ) );
+	CHECK_FCT( fd_msg_avp_add( msg, MSG_BRW_LAST_CHILD, avp ) );
+	
+	
+	/* Add the Inband-Security-Id AVP if needed */
+	if (isi_tls || isi_none) {
+		CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Inband-Security-Id", &dictobj, ENOENT )  );
+		
+		if (isi_none) {
+			CHECK_FCT( fd_msg_avp_new ( dictobj, 0, &avp ) );
+			val.u32 = ACV_ISI_NO_INBAND_SECURITY;
+			CHECK_FCT( fd_msg_avp_setvalue( avp, &val ) );
+			CHECK_FCT( fd_msg_avp_add( msg, MSG_BRW_LAST_CHILD, avp ) );
+		}
+		
+		if (isi_tls) {
+			CHECK_FCT( fd_msg_avp_new ( dictobj, 0, &avp ) );
+			val.u32 = ACV_ISI_TLS;
+			CHECK_FCT( fd_msg_avp_setvalue( avp, &val ) );
+			CHECK_FCT( fd_msg_avp_add( msg, MSG_BRW_LAST_CHILD, avp ) );
+		}
+	}
+	
+	/* List of local applications */
+	{
+		struct dict_object * dictobj_auth = NULL;
+		struct dict_object * dictobj_acct = NULL;
+		struct dict_object * dictobj_vid = NULL;
+		
+		CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Vendor-Specific-Application-Id", &dictobj, ENOENT )  );
+		CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Vendor-Id", &dictobj_vid, ENOENT )  );
+		CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Auth-Application-Id", &dictobj_auth, ENOENT )  );
+		CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Acct-Application-Id", &dictobj_acct, ENOENT )  );
+		
+		for (li = fd_g_config->cnf_apps.next; li != &fd_g_config->cnf_apps; li = li->next) {
+			struct fd_app * a = (struct fd_app *)(li);
+
+			if (a->flags.auth) {
+				CHECK_FCT( fd_msg_avp_new ( dictobj_auth, 0, &avp ) );
+				val.u32 = a->appid;
+				CHECK_FCT( fd_msg_avp_setvalue( avp, &val ) );
+				if (a->vndid != 0) {
+					struct avp * avp2 = NULL;
+					CHECK_FCT( fd_msg_avp_new ( dictobj, 0, &avp2 ) );
+					CHECK_FCT( fd_msg_avp_add( avp2, MSG_BRW_LAST_CHILD, avp ) );
+					avp = avp2;
+					CHECK_FCT( fd_msg_avp_new ( dictobj_vid, 0, &avp2 ) );
+					val.u32 = a->vndid;
+					CHECK_FCT( fd_msg_avp_setvalue( avp2, &val ) );
+					CHECK_FCT( fd_msg_avp_add( avp, MSG_BRW_LAST_CHILD, avp2 ) );
+				}
+				CHECK_FCT( fd_msg_avp_add( msg, MSG_BRW_LAST_CHILD, avp ) );
+			}
+			if (a->flags.acct) {
+				CHECK_FCT( fd_msg_avp_new ( dictobj_acct, 0, &avp ) );
+				val.u32 = a->appid;
+				CHECK_FCT( fd_msg_avp_setvalue( avp, &val ) );
+				if (a->vndid != 0) {
+					struct avp * avp2 = NULL;
+					CHECK_FCT( fd_msg_avp_new ( dictobj, 0, &avp2 ) );
+					CHECK_FCT( fd_msg_avp_add( avp2, MSG_BRW_LAST_CHILD, avp ) );
+					avp = avp2;
+					CHECK_FCT( fd_msg_avp_new ( dictobj_vid, 0, &avp2 ) );
+					val.u32 = a->vndid;
+					CHECK_FCT( fd_msg_avp_setvalue( avp2, &val ) );
+					CHECK_FCT( fd_msg_avp_add( avp, MSG_BRW_LAST_CHILD, avp2 ) );
+				}
+				CHECK_FCT( fd_msg_avp_add( msg, MSG_BRW_LAST_CHILD, avp ) );
+			}
+		}
+		
+		/* do not forget the relay application */
+		if (! fd_g_config->cnf_flags.no_fwd) {
+			CHECK_FCT( fd_msg_avp_new ( dictobj_auth, 0, &avp ) );
+			val.u32 = AI_RELAY;
+			CHECK_FCT( fd_msg_avp_setvalue( avp, &val ) );
+			CHECK_FCT( fd_msg_avp_add( msg, MSG_BRW_LAST_CHILD, avp ) );
+		}
+	}
+	
+	/* Add the list of supported vendors */
+	{
+		uint32_t * array = fd_dict_get_vendorid_list(fd_g_config->cnf_dict);
+		if (array) {
+			int i = 0;
+			CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Supported-Vendor-Id", &dictobj, ENOENT )  );
+			
+			while (array[i] != 0) {
+				CHECK_FCT( fd_msg_avp_new ( dictobj, 0, &avp ) );
+				val.u32 = array[i];
+				CHECK_FCT( fd_msg_avp_setvalue( avp, &val ) );
+				CHECK_FCT( fd_msg_avp_add( msg, MSG_BRW_LAST_CHILD, avp ) );
+				i++;
+			}
+			
+			free(array);
+		}
+	}
+	
+	return 0;
+}
+
+/* Remove any information saved from a previous CER/CEA exchange */
+static void cleanup_remote_CE_info(struct fd_peer * peer)
+{
+	/* free linked information */
+	free(peer->p_hdr.info.runtime.pir_realm);
+	free(peer->p_hdr.info.runtime.pir_prodname);
+	while (!FD_IS_LIST_EMPTY(&peer->p_hdr.info.runtime.pir_apps)) {
+		struct fd_list * li = peer->p_hdr.info.runtime.pir_apps.next;
+		fd_list_unlink(li);
+		free(li);
+	}
+	/* note: pir_cert_list needs not be freed (belongs to gnutls) */
+	
+	/* cleanup the area */
+	memset(&peer->p_hdr.info.runtime, 0, sizeof(peer->p_hdr.info.runtime));
+	
+	/* reinit the list */
+	fd_list_init(&peer->p_hdr.info.runtime.pir_apps, peer);
+
+	/* Remove previously advertised endpoints */
+	fd_ep_clearflags( &peer->p_hdr.info.pi_endpoints, EP_FL_ADV );
+}
+
+/* Extract information sent by the remote peer and save it in our peer structure */
+static int save_remote_CE_info(struct msg * msg, struct fd_peer * peer, struct fd_pei * error, uint32_t *rc)
+{
+	struct avp * avp = NULL;
+	
+	cleanup_remote_CE_info(peer);
+	
+	CHECK_FCT( fd_msg_browse( msg, MSG_BRW_FIRST_CHILD, &avp, NULL) );
+	
+	/* Loop on all AVPs and save what we are interrested into */
+	while (avp) {
+		struct avp_hdr * hdr;
+
+		CHECK_FCT(  fd_msg_avp_hdr( avp, &hdr )  );
+
+		if (hdr->avp_flags & AVP_FLAG_VENDOR) {
+			/* Ignore all vendor-specific AVPs in CER/CEA because we don't support any currently */
+			LOG_A("Ignored a vendor-specific AVP in CER / CEA");
+			goto next;
+		}
+
+		switch (hdr->avp_code) {
+			case AC_RESULT_CODE: /* Result-Code */
+				if (hdr->avp_value == NULL) {
+					/* This is a sanity check */
+					LOG_F("Ignored an AVP (code %x) with unset value in CER/CEA", hdr->avp_code);
+					ASSERT(0); /* To check if this really happens, and understand why... */
+					goto next;
+				}
+				
+				if (rc)
+					*rc = hdr->avp_value->u32;
+				break;
+		
+			case AC_ORIGIN_HOST: /* Origin-Host */
+				if (hdr->avp_value == NULL) {
+					/* This is a sanity check */
+					LOG_F("Ignored an AVP (code %x) with unset value in CER/CEA", hdr->avp_code);
+					ASSERT(0); /* To check if this really happens, and understand why... */
+					goto next;
+				}
+				
+				/* We check that the value matches what we know, otherwise disconnect the peer */
+				if (fd_os_almostcasesrch(hdr->avp_value->os.data, hdr->avp_value->os.len, 
+							peer->p_hdr.info.pi_diamid, peer->p_hdr.info.pi_diamidlen, NULL)) {
+					TRACE_DEBUG(INFO, "Received a message with Origin-Host set to '%.*s' while expecting '%s'", 
+							(int)hdr->avp_value->os.len, hdr->avp_value->os.data, peer->p_hdr.info.pi_diamid);
+					error->pei_errcode = "DIAMETER_AVP_NOT_ALLOWED";
+					error->pei_message = "Your Origin-Host value does not match my configuration.";
+					error->pei_avp = avp;
+					return EINVAL;
+				}
+
+				break;
+		
+			case AC_ORIGIN_REALM: /* Origin-Realm */
+				if (hdr->avp_value == NULL) {
+					/* This is a sanity check */
+					LOG_F("Ignored an AVP (code %x) with unset value in CER/CEA", hdr->avp_code);
+					ASSERT(0); /* To check if this really happens, and understand why... */
+					goto next;
+				}
+				
+				/* In case of multiple AVPs */
+				if (peer->p_hdr.info.runtime.pir_realm) {
+					TRACE_DEBUG(INFO, "Multiple instances of the Origin-Realm AVP");
+					error->pei_errcode = "DIAMETER_AVP_OCCURS_TOO_MANY_TIMES";
+					error->pei_message = "I found several Origin-Realm AVPs";
+					error->pei_avp = avp;
+					return EINVAL;
+				}
+				
+				/* If the octet string contains a \0 */
+				if (!fd_os_is_valid_DiameterIdentity(hdr->avp_value->os.data, hdr->avp_value->os.len)) {
+					error->pei_errcode = "DIAMETER_INVALID_AVP_VALUE";
+					error->pei_message = "Your Origin-Realm contains invalid characters.";
+					error->pei_avp = avp;
+					return EINVAL;
+				}
+				
+				/* Save the value */
+				CHECK_MALLOC(  peer->p_hdr.info.runtime.pir_realm = os0dup( hdr->avp_value->os.data, hdr->avp_value->os.len )  );
+				peer->p_hdr.info.runtime.pir_realmlen = hdr->avp_value->os.len;
+				break;
+
+			case AC_HOST_IP_ADDRESS: /* Host-IP-Address */
+				if (hdr->avp_value == NULL) {
+					/* This is a sanity check */
+					LOG_F("Ignored an AVP (code %x) with unset value in CER/CEA", hdr->avp_code);
+					ASSERT(0); /* To check if this really happens, and understand why... */
+					goto next;
+				}
+				{
+					sSS	ss;
+
+					/* Get the sockaddr value */
+					memset(&ss, 0, sizeof(ss));
+					CHECK_FCT_DO( fd_msg_avp_value_interpret( avp, &ss),
+						{
+							/* in case of error, assume the AVP value was wrong */
+							error->pei_errcode = "DIAMETER_INVALID_AVP_VALUE";
+							error->pei_avp = avp;
+							return EINVAL;
+						} );
+
+					/* Save this endpoint in the list as advertized */
+					CHECK_FCT( fd_ep_add_merge( &peer->p_hdr.info.pi_endpoints, (sSA *)&ss, sizeof(sSS), EP_FL_ADV ) );
+				}
+				break;
+
+			case AC_VENDOR_ID: /* Vendor-Id */
+				if (hdr->avp_value == NULL) {
+					/* This is a sanity check */
+					LOG_F("Ignored an AVP (code %x) with unset value in CER/CEA", hdr->avp_code);
+					ASSERT(0); /* To check if this really happens, and understand why... */
+					goto next;
+				}
+				
+				/* In case of multiple AVPs */
+				if (peer->p_hdr.info.runtime.pir_vendorid) {
+					TRACE_DEBUG(INFO, "Multiple instances of the Vendor-Id AVP");
+					error->pei_errcode = "DIAMETER_AVP_OCCURS_TOO_MANY_TIMES";
+					error->pei_message = "I found several Vendor-Id AVPs";
+					error->pei_avp = avp;
+					return EINVAL;
+				}
+				
+				peer->p_hdr.info.runtime.pir_vendorid = hdr->avp_value->u32;
+				break;
+
+			case AC_PRODUCT_NAME: /* Product-Name */
+				if (hdr->avp_value == NULL) {
+					/* This is a sanity check */
+					LOG_F("Ignored an AVP (code %x) with unset value in CER/CEA", hdr->avp_code);
+					ASSERT(0); /* To check if this really happens, and understand why... */
+					goto next;
+				}
+				
+				/* In case of multiple AVPs */
+				if (peer->p_hdr.info.runtime.pir_prodname) {
+					TRACE_DEBUG(INFO, "Multiple instances of the Product-Name AVP");
+					error->pei_errcode = "DIAMETER_AVP_OCCURS_TOO_MANY_TIMES";
+					error->pei_message = "I found several Product-Name AVPs";
+					error->pei_avp = avp;
+					return EINVAL;
+				}
+
+				CHECK_MALLOC( peer->p_hdr.info.runtime.pir_prodname = calloc( hdr->avp_value->os.len + 1, 1 )  );
+				memcpy(peer->p_hdr.info.runtime.pir_prodname, hdr->avp_value->os.data, hdr->avp_value->os.len);
+				break;
+
+			case AC_ORIGIN_STATE_ID: /* Origin-State-Id */
+				if (hdr->avp_value == NULL) {
+					/* This is a sanity check */
+					LOG_F("Ignored an AVP (code %x) with unset value in CER/CEA", hdr->avp_code);
+					ASSERT(0); /* To check if this really happens, and understand why... */
+					goto next;
+				}
+				
+				/* In case of multiple AVPs */
+				if (peer->p_hdr.info.runtime.pir_orstate) {
+					TRACE_DEBUG(INFO, "Multiple instances of the Origin-State-Id AVP");
+					error->pei_errcode = "DIAMETER_AVP_OCCURS_TOO_MANY_TIMES";
+					error->pei_message = "I found several Origin-State-Id AVPs";
+					error->pei_avp = avp;
+					return EINVAL;
+				}
+				
+				peer->p_hdr.info.runtime.pir_orstate = hdr->avp_value->u32;
+				break;
+
+			case AC_SUPPORTED_VENDOR_ID: /* Supported-Vendor-Id */
+				if (hdr->avp_value == NULL) {
+					/* This is a sanity check */
+					LOG_F("Ignored an AVP (code %x) with unset value in CER/CEA", hdr->avp_code);
+					ASSERT(0); /* To check if this really happens, and understand why... */
+					goto next;
+				}
+				
+				TRACE_DEBUG(FULL, "'%s' claims support for a subset of vendor %d features.", peer->p_hdr.info.pi_diamid, hdr->avp_value->u32);
+				/* not that it makes a difference for us... 
+				 -- if an application actually needs this info, we could save it somewhere.
+				*/
+				break;
+
+			case AC_VENDOR_SPECIFIC_APPLICATION_ID: /* Vendor-Specific-Application-Id (grouped)*/
+				{
+					struct avp * inavp = NULL;
+					vendor_id_t vid = 0;
+					application_id_t auth_aid = 0;
+					application_id_t acct_aid = 0;
+					int invalid=0;
+
+					/* get the first child AVP */
+					CHECK_FCT(  fd_msg_browse(avp, MSG_BRW_FIRST_CHILD, &inavp, NULL)  );
+
+					while (inavp) {
+						struct avp_hdr * inhdr;
+						CHECK_FCT(  fd_msg_avp_hdr( inavp, &inhdr )  );
+
+						if (inhdr->avp_flags & AVP_FLAG_VENDOR) {
+							LOG_A("Ignored a vendor AVP inside Vendor-Specific-Application-Id AVP");
+							goto innext;
+						}
+
+						if (inhdr->avp_value == NULL) {
+							/* This is a sanity check */
+							LOG_F("Ignored an AVP (code %x) with unset value in CER/CEA", hdr->avp_code);
+							ASSERT(0); /* To check if this really happens, and understand why... */
+							goto innext;
+						}
+						switch (inhdr->avp_code) {
+							case AC_VENDOR_ID: /* Vendor-Id */
+#ifndef WORKAROUND_ACCEPT_INVALID_VSAI
+								if (vid != 0)
+									invalid++; /* We already had one such AVP. This is invalid according to RFC6733 but not RFC3588 (but there is an erratum) */
+#endif /* WORKAROUND_ACCEPT_INVALID_VSAI */
+								vid = inhdr->avp_value->u32;
+								break;
+							case AC_AUTH_APPLICATION_ID: /* Auth-Application-Id */
+								if (auth_aid != 0)
+									invalid++; /* We already had one such AVP */
+#ifndef WORKAROUND_ACCEPT_INVALID_VSAI
+								if (acct_aid != 0)
+									invalid++; /* Only 1 *-Application-Id AVP is allowed */
+#endif /* WORKAROUND_ACCEPT_INVALID_VSAI */
+								auth_aid = inhdr->avp_value->u32;
+								break;
+							case AC_ACCT_APPLICATION_ID: /* Acct-Application-Id */
+								if (acct_aid != 0)
+									invalid++; /* We already had one such AVP */
+#ifndef WORKAROUND_ACCEPT_INVALID_VSAI
+								if (auth_aid != 0)
+									invalid++; /* Only 1 *-Application-Id AVP is allowed */
+#endif /* WORKAROUND_ACCEPT_INVALID_VSAI */
+								acct_aid = inhdr->avp_value->u32;
+								break;
+							/* ignore other AVPs */
+						}
+						
+						if (invalid) {
+							TRACE_DEBUG(FULL, "Invalid Vendor-Specific-Application-Id AVP received");
+							error->pei_errcode = "DIAMETER_INVALID_AVP_VALUE";
+							error->pei_avp = avp;
+							return EINVAL;
+						}
+
+					innext:			
+						/* Go to next in AVP */
+						CHECK_FCT( fd_msg_browse(inavp, MSG_BRW_NEXT, &inavp, NULL) );
+					}
+					
+					/* Add entry in the list */
+					if (auth_aid) {
+						CHECK_FCT( fd_app_merge(&peer->p_hdr.info.runtime.pir_apps, auth_aid, vid, 1, 0) );
+					}
+					if (acct_aid) {
+						CHECK_FCT( fd_app_merge(&peer->p_hdr.info.runtime.pir_apps, acct_aid, vid, 0, 1) );
+					}
+				}
+				break;
+
+			case AC_AUTH_APPLICATION_ID: /* Auth-Application-Id */
+				if (hdr->avp_value == NULL) {
+					/* This is a sanity check */
+					LOG_F("Ignored an AVP (code %x) with unset value in CER/CEA", hdr->avp_code);
+					ASSERT(0); /* To check if this really happens, and understand why... */
+					goto next;
+				}
+				
+				if (hdr->avp_value->u32 == AI_RELAY) {
+					peer->p_hdr.info.runtime.pir_relay = 1;
+				} else {
+					CHECK_FCT( fd_app_merge(&peer->p_hdr.info.runtime.pir_apps, hdr->avp_value->u32, 0, 1, 0) );
+				}
+				break;
+
+			case AC_ACCT_APPLICATION_ID: /* Acct-Application-Id */
+				if (hdr->avp_value == NULL) {
+					/* This is a sanity check */
+					LOG_F("Ignored an AVP (code %x) with unset value in CER/CEA", hdr->avp_code);
+					ASSERT(0); /* To check if this really happens, and understand why... */
+					goto next;
+				}
+				
+				if (hdr->avp_value->u32 == AI_RELAY) {
+					/* Not clear if the relay application can be inside this AVP... */
+					peer->p_hdr.info.runtime.pir_relay = 1;
+				} else {
+					CHECK_FCT( fd_app_merge(&peer->p_hdr.info.runtime.pir_apps, hdr->avp_value->u32, 0, 0, 1) );
+				}
+				break;
+
+			case AC_FIRMWARE_REVISION: /* Firmware-Revision */
+				if (hdr->avp_value == NULL) {
+					/* This is a sanity check */
+					LOG_F("Ignored an AVP (code %x) with unset value in CER/CEA", hdr->avp_code);
+					ASSERT(0); /* To check if this really happens, and understand why... */
+					goto next;
+				}
+				
+				peer->p_hdr.info.runtime.pir_firmrev = hdr->avp_value->u32;
+				break;
+
+			case AC_INBAND_SECURITY_ID: /* Inband-Security-Id */
+				if (hdr->avp_value == NULL) {
+					/* This is a sanity check */
+					LOG_F("Ignored an AVP (code %x) with unset value in CER/CEA", hdr->avp_code);
+					ASSERT(0); /* To check if this really happens, and understand why... */
+					goto next;
+				}
+				if (hdr->avp_value->u32 >= 32 ) {
+					error->pei_errcode = "DIAMETER_INVALID_AVP_VALUE";
+					error->pei_message = "I don't support this Inband-Security-Id value (yet).";
+					error->pei_avp = avp;
+					return EINVAL;
+				}
+				peer->p_hdr.info.runtime.pir_isi |= (1 << hdr->avp_value->u32);
+				break;
+		}
+
+next:			
+		/* Go to next AVP */
+		CHECK_FCT( fd_msg_browse(avp, MSG_BRW_NEXT, &avp, NULL) );
+	}
+	
+	return 0;
+}
+
+/* Create a CER message for sending */ 
+static int create_CER(struct fd_peer * peer, struct cnxctx * cnx, struct msg ** cer)
+{
+	int isi_tls = 0;
+	int isi_none = 0;
+	
+	/* Find CER dictionary object and create an instance */
+	CHECK_FCT( fd_msg_new ( fd_dict_cmd_CER, MSGFL_ALLOC_ETEID, cer ) );
+	
+	/* Do we need Inband-Security-Id AVPs ? If we're already using TLS, we don't... */
+	if (!fd_cnx_getTLS(cnx)) {
+		isi_none = peer->p_hdr.info.config.pic_flags.sec & PI_SEC_NONE; /* we add it even if the peer does not use the old mechanism, it is impossible to distinguish */
+
+		if (peer->p_hdr.info.config.pic_flags.sec & PI_SEC_TLS_OLD) {
+			if (fd_g_config->cnf_sec_data.tls_disabled) {
+				LOG_N("TLS disabled locally, so Inband-Security-Id (TLS) not included for peer %s", peer->p_hdr.info.pi_diamid);
+			} else {
+				isi_tls  = 1;
+			}
+		}
+	}
+	
+	/* Add the information about the local peer */
+	CHECK_FCT( add_CE_info(*cer, cnx, isi_tls, isi_none) );
+	
+	/* Done! */
+	return 0;
+}
+
+
+/* Continue with the initiator side */
+static int to_waitcea(struct fd_peer * peer, struct cnxctx * cnx)
+{
+	/* We sent a CER on the connection, set the event queue so that we receive the CEA */
+	CHECK_FCT( set_peer_cnx(peer, &cnx) );
+	
+	/* Change state and reset the timer */
+	CHECK_FCT( fd_psm_change_state(peer, STATE_WAITCEA) );
+	fd_psm_next_timeout(peer, 0, CEA_TIMEOUT);
+	
+	return 0;
+}
+
+/* Reject an incoming connection attempt */
+static void receiver_reject(struct cnxctx ** recv_cnx, struct msg ** cer, struct fd_pei * error)
+{
+	struct msg_hdr * hdr = NULL;
+	
+	/* Create and send the CEA with appropriate error code */
+	CHECK_FCT_DO( fd_msg_new_answer_from_req ( fd_g_config->cnf_dict, cer, MSGFL_ANSW_ERROR ), goto destroy );
+	CHECK_FCT_DO( fd_msg_rescode_set(*cer, error->pei_errcode, error->pei_message, error->pei_avp, 0 ), goto destroy );
+	CHECK_FCT_DO(  fd_msg_hdr( *cer, &hdr ), goto destroy  );
+	if (hdr->msg_flags & CMD_FLAG_ERROR) {
+		/* Generic error format, just add the origin AVPs */
+		CHECK_FCT_DO( fd_msg_add_origin ( *cer, 1 ), goto destroy );
+	} else {
+		/* Add other AVPs to be compliant with the ABNF */
+		CHECK_FCT_DO( add_CE_info(*cer, *recv_cnx, 0, 0), goto destroy );
+	}
+	CHECK_FCT_DO( fd_out_send(cer, *recv_cnx, NULL, 0), goto destroy );
+	
+	if (error->pei_avp_free) {
+		fd_msg_free(error->pei_avp);
+	}
+	
+	/* And now destroy this connection */
+destroy:
+	fd_cnx_destroy(*recv_cnx);
+	*recv_cnx = NULL;
+	if (*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;
+	}
+}
+
+/* We have established a new connection to the remote peer, send CER and eventually process the election */
+int fd_p_ce_handle_newcnx(struct fd_peer * peer, struct cnxctx * initiator)
+{
+	struct msg * cer = NULL;
+	
+	/* Send CER on the new connection */
+	CHECK_FCT( create_CER(peer, initiator, &cer) );
+	CHECK_FCT( fd_out_send(&cer, initiator, peer, 0) );
+	
+	/* Are we doing an election ? */
+	if (fd_peer_getstate(peer) == STATE_WAITCNXACK_ELEC) {
+		if (election_result(peer)) {
+			/* Close initiator connection */
+			fd_cnx_destroy(initiator);
+
+			LOG_D("%s: Election lost on outgoing connection, closing and answering CEA on incoming connection.", peer->p_hdr.info.pi_diamid);
+			
+			/* Process with the receiver side */
+			CHECK_FCT( fd_p_ce_process_receiver(peer) );
+
+		} else {
+			struct fd_pei pei;
+			memset(&pei, 0, sizeof(pei));
+			pei.pei_errcode = "ELECTION_LOST";
+			pei.pei_message = "Please answer my CER instead, you won the election.";
+			LOG_D("%s: Election lost on incoming connection, closing and waiting for CEA on outgoing connection.", peer->p_hdr.info.pi_diamid);
+
+			/* Answer an ELECTION LOST to the receiver side */
+			receiver_reject(&peer->p_receiver, &peer->p_cer, &pei);
+			CHECK_FCT( to_waitcea(peer, initiator) );
+		}
+	} else {
+		/* No election (yet) */
+		CHECK_FCT( to_waitcea(peer, initiator) );
+	}
+	
+	return 0;
+}
+
+/* We have received a Capabilities Exchange message on the peer connection */
+int fd_p_ce_msgrcv(struct msg ** msg, int req, struct fd_peer * peer)
+{
+	uint32_t rc = 0;
+	int st;
+	struct fd_pei pei;
+	
+	TRACE_ENTRY("%p %p", msg, peer);
+	CHECK_PARAMS( msg && *msg && CHECK_PEER(peer) );
+	
+	/* The only valid situation where we are called is in WAITCEA and we receive a CEA (we may have won an election) */
+	
+	/* Note : to implement Capabilities Update, we would need to change here */
+	
+	/* If it is a CER, just reply an error */
+	if (req) {
+		/* Create the error message */
+		CHECK_FCT( fd_msg_new_answer_from_req ( fd_g_config->cnf_dict, msg, MSGFL_ANSW_ERROR ) );
+		
+		/* Set the error code */
+		CHECK_FCT( fd_msg_rescode_set(*msg, "DIAMETER_UNABLE_TO_COMPLY", "No CER allowed in current state", NULL, 1 ) );
+
+		/* msg now contains an answer message to send back */
+		CHECK_FCT_DO( fd_out_send(msg, NULL, peer, 0), /* In case of error the message has already been dumped */ );
+	}
+	
+	/* If the state is not WAITCEA, just discard the message */
+	if (req || ((st = fd_peer_getstate(peer)) != STATE_WAITCEA)) {
+		if (*msg) {
+			/* 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(st));
+			fd_hook_call(HOOK_MESSAGE_DROPPED, *msg, peer, buf, fd_msg_pmdl_get(*msg));
+
+			CHECK_FCT_DO( fd_msg_free(*msg), /* continue */);
+			*msg = NULL;
+		}
+		
+		return 0;
+	}
+	
+	memset(&pei, 0, sizeof(pei));
+	
+	/* Save info from the CEA into the peer */
+	CHECK_FCT_DO( save_remote_CE_info(*msg, peer, &pei, &rc), 
+		{
+			fd_hook_call(HOOK_PEER_CONNECT_FAILED, *msg, peer, "An error occurred while processing incoming CEA.", NULL);
+			goto cleanup;
+		} );
+	
+	/* Check the Result-Code */
+	switch (rc) {
+		case ER_DIAMETER_SUCCESS:
+			/* Log success */
+			fd_hook_call(HOOK_PEER_CONNECT_SUCCESS, *msg, peer, NULL, NULL);
+			
+			/* Dispose of the message, we don't need it anymore */
+			CHECK_FCT_DO( fd_msg_free(*msg), /* continue */ );
+			*msg = NULL;
+			
+			/* No problem, we can continue */
+			break;
+			
+		case ER_DIAMETER_TOO_BUSY:
+			/* Retry later */
+			fd_hook_call(HOOK_PEER_CONNECT_FAILED, *msg, peer, "Remote peer is too busy", NULL);
+			fd_psm_cleanup(peer, 0);
+			fd_psm_next_timeout(peer, 0, 300);
+			return 0;
+		
+		case ER_ELECTION_LOST:
+			/* Ok, just wait for a little while for the CER to be processed on the other connection. */
+			TRACE_DEBUG(FULL, "Peer %s replied a CEA with Result-Code AVP ELECTION_LOST, waiting for events.", peer->p_hdr.info.pi_diamid);
+			return 0;
+		
+		default:
+			/* In any other case, we abort all attempts to connect to this peer */
+			fd_hook_call(HOOK_PEER_CONNECT_FAILED, *msg, peer, "CEA with unexpected error code", NULL);
+			return EINVAL;
+	}
+	
+	
+	/* Handshake if needed, start clear otherwise */
+	if ( ! fd_cnx_getTLS(peer->p_cnxctx) ) {
+		int todo = peer->p_hdr.info.config.pic_flags.sec & peer->p_hdr.info.runtime.pir_isi ;
+		/* Special case: if the peer did not send a ISI AVP */
+		if (peer->p_hdr.info.runtime.pir_isi == 0)
+			todo = peer->p_hdr.info.config.pic_flags.sec;
+		
+		if (todo == PI_SEC_NONE) {
+			/* Ok for clear connection */
+			TRACE_DEBUG(INFO, "No TLS protection negotiated with peer '%s'.", peer->p_hdr.info.pi_diamid);
+			CHECK_FCT( fd_cnx_start_clear(peer->p_cnxctx, 1) );
+			
+		} else if (fd_g_config->cnf_sec_data.tls_disabled) {
+			LOG_E("Clear connection with remote peer '%s' is not (explicitly) allowed, and TLS is disabled. Giving up...", peer->p_hdr.info.pi_diamid);
+			fd_hook_call(HOOK_PEER_CONNECT_FAILED, NULL, peer, "TLS is disabled and peer is not configured for IPsec", NULL);
+			goto cleanup;
+			
+		} else {
+			fd_psm_change_state(peer, STATE_OPEN_HANDSHAKE);
+			CHECK_FCT_DO( fd_cnx_handshake(peer->p_cnxctx, GNUTLS_CLIENT, ALGO_HANDSHAKE_3436, peer->p_hdr.info.config.pic_priority, NULL),
+				{
+					/* Handshake failed ...  */
+					fd_hook_call(HOOK_PEER_CONNECT_FAILED, NULL, peer, "TLS handshake failed after CER/CEA exchange", NULL);
+					goto cleanup;
+				} );
+
+			/* Retrieve the credentials */
+			CHECK_FCT( fd_cnx_getcred(peer->p_cnxctx, &peer->p_hdr.info.runtime.pir_cert_list, &peer->p_hdr.info.runtime.pir_cert_list_size) );
+		}
+	}
+	
+	/* Move to next state */
+	if (peer->p_flags.pf_cnx_pb) {
+		fd_psm_change_state(peer, STATE_REOPEN );
+		CHECK_FCT( fd_p_dw_reopen(peer) );
+	} else {
+		fd_psm_change_state(peer, STATE_OPEN );
+		fd_psm_next_timeout(peer, 1, peer->p_hdr.info.config.pic_twtimer ?: fd_g_config->cnf_timer_tw);
+	}
+	
+	return 0;
+	
+cleanup:
+	fd_p_ce_clear_cnx(peer, NULL);
+
+	/* Send the error to the peer */
+	CHECK_FCT( fd_event_send(peer->p_events, FDEVP_CNX_ERROR, 0, NULL) );
+
+	return 0;
+}
+
+/* Handle the receiver side to go to OPEN or OPEN_NEW state (any election is resolved) */
+int fd_p_ce_process_receiver(struct fd_peer * peer)
+{
+	struct fd_pei pei;
+	struct msg * msg = NULL;
+	int isi = 0;
+	int fatal = 0;
+	int tls_sync=0;
+	
+	TRACE_ENTRY("%p", peer);
+	
+	CHECK_FCT_DO( set_peer_cnx(peer, &peer->p_receiver),
+		{
+			fd_hook_call(HOOK_PEER_CONNECT_FAILED, NULL, peer, "Error saving the incoming connection in the peer structure", NULL);
+			return __ret__;
+		} );
+	msg = peer->p_cer;
+	peer->p_cer = NULL;
+	
+	memset(&pei, 0, sizeof(pei));
+	
+	/* Parse the content of the received CER */
+	CHECK_FCT_DO( save_remote_CE_info(msg, peer, &pei, NULL), goto error_abort );
+	
+	/* Validate the realm if needed */
+	if (peer->p_hdr.info.config.pic_realm) {
+		size_t len = strlen(peer->p_hdr.info.config.pic_realm);
+		if (fd_os_almostcasesrch(peer->p_hdr.info.config.pic_realm, len, peer->p_hdr.info.runtime.pir_realm, peer->p_hdr.info.runtime.pir_realmlen, NULL)) {
+			TRACE_DEBUG(INFO, "Rejected CER from peer '%s', realm mismatch with configured value (returning DIAMETER_UNKNOWN_PEER).", peer->p_hdr.info.pi_diamid);
+			pei.pei_errcode = "DIAMETER_UNKNOWN_PEER"; /* maybe AVP_NOT_ALLOWED would be better fit? */
+			goto error_abort;
+		}
+	}
+	
+	/* Save the credentials if handshake already occurred */
+	if ( fd_cnx_getTLS(peer->p_cnxctx) ) {
+		CHECK_FCT( fd_cnx_getcred(peer->p_cnxctx, &peer->p_hdr.info.runtime.pir_cert_list, &peer->p_hdr.info.runtime.pir_cert_list_size) );
+	}
+	
+	/* Validate the peer if needed */
+	if (peer->p_flags.pf_responder) {
+		int res = fd_peer_validate( peer );
+		if (res < 0) {
+			TRACE_DEBUG(INFO, "Rejected CER from peer '%s', validation failed (returning DIAMETER_UNKNOWN_PEER).", peer->p_hdr.info.pi_diamid);
+			pei.pei_errcode = "DIAMETER_UNKNOWN_PEER";
+			goto error_abort;
+		}
+		CHECK_FCT( res );
+	}
+	
+	/* Check if we have common applications */
+	if ( fd_g_config->cnf_flags.no_fwd && (! peer->p_hdr.info.runtime.pir_relay) ) {
+		int got_common;
+		CHECK_FCT( fd_app_check_common( &fd_g_config->cnf_apps, &peer->p_hdr.info.runtime.pir_apps, &got_common) );
+		if (!got_common) {
+			TRACE_DEBUG(INFO, "No common application with peer '%s', sending DIAMETER_NO_COMMON_APPLICATION", peer->p_hdr.info.pi_diamid);
+			pei.pei_errcode = "DIAMETER_NO_COMMON_APPLICATION";
+			fatal = 1;
+			goto error_abort;
+		}
+	}
+	
+	/* Do we agree on ISI ? */
+	if ( ! fd_cnx_getTLS(peer->p_cnxctx) ) {
+		
+		/* In case of responder, the validate callback must have set the config.pic_flags.sec value already */
+	
+		/* First case: we are not using old mechanism: ISI are deprecated, we ignore it. */
+		if ( ! (peer->p_hdr.info.config.pic_flags.sec & PI_SEC_TLS_OLD)) {
+			/* Just check then that the peer configuration allows for IPsec protection */
+			if (peer->p_hdr.info.config.pic_flags.sec & PI_SEC_NONE) {
+				isi = PI_SEC_NONE;
+			} else {
+				/* otherwise, we should have already been protected. Reject */
+				TRACE_DEBUG(INFO, "Non TLS-protected CER/CEA exchanges are not allowed with this peer, rejecting.");
+			}
+		} else {
+			/* The old mechanism is allowed with this peer. Now, look into the ISI AVP values */
+			
+			/* In case no ISI was present anyway: */
+			if (!peer->p_hdr.info.runtime.pir_isi) {
+				TRACE_DEBUG(INFO, "Inband-Security-Id AVP is missing in received CER.");
+				if (peer->p_hdr.info.config.pic_flags.sec & PI_SEC_NONE) {
+					isi = PI_SEC_NONE;
+					TRACE_DEBUG(INFO, "IPsec protection allowed by configuration, allowing this mechanism to be used.");
+				} else {
+					/* otherwise, we should have already been protected. Reject */
+					TRACE_DEBUG(INFO, "Rejecting the peer connection (please allow IPsec here or configure TLS in the remote peer).");
+				}
+			} else {
+				/* OK, the remote peer did send the ISI AVP. */
+				if ((peer->p_hdr.info.config.pic_flags.sec & PI_SEC_NONE) && (peer->p_hdr.info.runtime.pir_isi & PI_SEC_NONE)) {
+					/* We have allowed IPsec */
+					isi = PI_SEC_NONE;
+				} else if (fd_g_config->cnf_sec_data.tls_disabled) {
+					/* We can agree on TLS */
+					TRACE_DEBUG(INFO, "Remote peer is not allowed for IPsec and TLS is disabled.");;
+				} else if (peer->p_hdr.info.runtime.pir_isi & PI_SEC_TLS_OLD) {
+					/* We can agree on TLS */
+					isi = PI_SEC_TLS_OLD;
+				} else {
+					TRACE_DEBUG(INFO, "Remote peer requested IPsec protection, but local configuration forbids it.");
+				}
+			}
+		}
+	
+		/* If we did not find an agreement */
+		if (!isi) {
+			TRACE_DEBUG(INFO, "No common security mechanism with '%s', sending DIAMETER_NO_COMMON_SECURITY", peer->p_hdr.info.pi_diamid);
+			pei.pei_errcode = "DIAMETER_NO_COMMON_SECURITY";
+			fatal = 1;
+			goto error_abort;
+		}
+		
+		/* Do not send the ISI IPsec if we are using the new mechanism */
+		if ((isi == PI_SEC_NONE) && (! (peer->p_hdr.info.config.pic_flags.sec & PI_SEC_TLS_OLD)))
+			isi = 0;
+	} else if (peer->p_hdr.info.runtime.pir_isi & PI_SEC_TLS_OLD) {
+		/* Seem some weird peers are sending the Inband-Security-Id AVP on the secure port... No harm */
+		isi = PI_SEC_TLS_OLD;
+	}
+	
+	/* Reply a CEA */
+	CHECK_FCT( fd_msg_new_answer_from_req ( fd_g_config->cnf_dict, &msg, 0 ) );
+	CHECK_FCT( fd_msg_rescode_set(msg, "DIAMETER_SUCCESS", NULL, NULL, 0 ) );
+	CHECK_FCT( add_CE_info(msg, peer->p_cnxctx, isi & PI_SEC_TLS_OLD, isi & PI_SEC_NONE) );
+	
+	/* The connection is complete, but we may still need TLS handshake */
+	fd_hook_call(HOOK_PEER_CONNECT_SUCCESS, msg, peer, NULL, NULL);
+	
+	CHECK_FCT( fd_out_send(&msg, peer->p_cnxctx, peer, 0 ) );
+	
+	/* Handshake if needed */
+	if (isi & PI_SEC_TLS_OLD) {
+		fd_psm_change_state(peer, STATE_OPEN_HANDSHAKE);
+		CHECK_FCT_DO( fd_cnx_handshake(peer->p_cnxctx, GNUTLS_SERVER, ALGO_HANDSHAKE_3436, peer->p_hdr.info.config.pic_priority, NULL),
+			{
+				/* Handshake failed ...  */
+				fd_hook_call(HOOK_PEER_CONNECT_FAILED, NULL, peer, "TLS handshake failed after CER/CEA exchange", NULL);
+				goto cleanup;
+			} );
+		
+		/* Retrieve the credentials */
+		CHECK_FCT_DO( fd_cnx_getcred(peer->p_cnxctx, &peer->p_hdr.info.runtime.pir_cert_list, &peer->p_hdr.info.runtime.pir_cert_list_size),
+			{
+				/* Error ...  */
+				fd_hook_call(HOOK_PEER_CONNECT_FAILED, NULL, peer, "Unable to retrieve remote credentials after TLS handshake", NULL);
+				goto cleanup;
+			} );
+		
+		
+		/* Call second validation callback if needed */
+		if (peer->p_cb2) {
+			TRACE_DEBUG(FULL, "Calling second validation callback for %s", peer->p_hdr.info.pi_diamid);
+			CHECK_FCT_DO( (*peer->p_cb2)( &peer->p_hdr.info ),
+				{
+					fd_hook_call(HOOK_PEER_CONNECT_FAILED, NULL, peer, "Validation callback rejected the peer after handshake", NULL);
+					CHECK_FCT( fd_psm_terminate( peer, "DO_NOT_WANT_TO_TALK_TO_YOU" ) );
+					return 0;
+				}  );
+		}
+		tls_sync = 1;
+	} else {
+		if ( ! fd_cnx_getTLS(peer->p_cnxctx) ) {
+			TRACE_DEBUG(INFO, "No TLS protection negotiated with peer '%s'.", peer->p_hdr.info.pi_diamid);
+			CHECK_FCT( fd_cnx_start_clear(peer->p_cnxctx, 1) );
+		}
+	}
+		
+	/* Move to OPEN or REOPEN state */
+	if (peer->p_flags.pf_cnx_pb) {
+		fd_psm_change_state(peer, STATE_REOPEN );
+		CHECK_FCT( fd_p_dw_reopen(peer) );
+	} else {
+		if ((!tls_sync) && (fd_cnx_is_unordered_delivery_supported(peer->p_cnxctx))) {
+			fd_psm_change_state(peer, STATE_OPEN_NEW );
+			/* send DWR */
+			CHECK_FCT( fd_p_dw_timeout(peer) );
+		} else {
+
+			fd_psm_change_state(peer, STATE_OPEN );
+			fd_psm_next_timeout(peer, 1, peer->p_hdr.info.config.pic_twtimer ?: fd_g_config->cnf_timer_tw);
+		}
+	}
+	
+	return 0;
+
+error_abort:
+	if (pei.pei_errcode) {
+		/* Send the error */
+		fd_hook_call(HOOK_PEER_CONNECT_FAILED, msg, peer, pei.pei_message ?: pei.pei_errcode, NULL);
+		receiver_reject(&peer->p_cnxctx, &msg, &pei);
+	} else {
+		char buf[1024];
+		snprintf(buf, sizeof(buf), "Unexpected error occurred while processing incoming connection from '%s'.", peer->p_hdr.info.pi_diamid);
+		fd_hook_call(HOOK_PEER_CONNECT_FAILED, msg, peer, buf, NULL);
+	}
+	
+cleanup:
+	if (msg) {
+		fd_msg_free(msg);
+	}
+	fd_p_ce_clear_cnx(peer, NULL);
+
+	/* Send the error to the peer */
+	CHECK_FCT( fd_event_send(peer->p_events, fatal ? FDEVP_TERMINATE : FDEVP_CNX_ERROR, 0, NULL) );
+
+	return 0;
+}
+
+/* We have received a CER on a new connection for this peer */
+int fd_p_ce_handle_newCER(struct msg ** msg, struct fd_peer * peer, struct cnxctx ** cnx, int valid)
+{
+	struct fd_pei pei;
+	int cur_state = fd_peer_getstate(peer);
+	memset(&pei, 0, sizeof(pei));
+	
+	switch (cur_state) {
+		case STATE_CLOSED:
+			peer->p_receiver = *cnx;
+			*cnx = NULL;
+			peer->p_cer = *msg;
+			*msg = NULL;
+			CHECK_FCT( fd_p_ce_process_receiver(peer) );
+			break;
+
+		case STATE_WAITCNXACK:
+			/* Save the parameters in the peer, move to STATE_WAITCNXACK_ELEC */
+			peer->p_receiver = *cnx;
+			*cnx = NULL;
+			peer->p_cer = *msg;
+			*msg = NULL;
+			CHECK_FCT( fd_psm_change_state(peer, STATE_WAITCNXACK_ELEC) );
+			break;
+			
+		case STATE_WAITCEA:
+			if (election_result(peer)) {
+				
+				/* Close initiator connection (was already set as principal) */
+				LOG_D("%s: Election lost on outgoing connection, closing and answering CEA on incoming connection.", peer->p_hdr.info.pi_diamid);
+				fd_p_ce_clear_cnx(peer, NULL);
+				
+				/* and go on with the receiver side */
+				peer->p_receiver = *cnx;
+				*cnx = NULL;
+				peer->p_cer = *msg;
+				*msg = NULL;
+				CHECK_FCT( fd_p_ce_process_receiver(peer) );
+
+			} else {
+
+				/* Answer an ELECTION LOST to the receiver side and continue */
+				pei.pei_errcode = "ELECTION_LOST";
+				pei.pei_message = "Please answer my CER instead, you won the election.";
+				LOG_D("%s: Election lost on incoming connection, closing and waiting for CEA on outgoing connection.", peer->p_hdr.info.pi_diamid);
+				receiver_reject(cnx, msg, &pei);
+			}
+			break;
+
+		default:
+			pei.pei_errcode = "DIAMETER_UNABLE_TO_COMPLY"; /* INVALID COMMAND? in case of Capabilities-Updates? */
+			pei.pei_message = "Invalid state to receive a new connection attempt.";
+			LOG_E("%s: Rejecting new connection attempt while our state machine is in state '%s'", peer->p_hdr.info.pi_diamid, STATE_STR(cur_state));
+			receiver_reject(cnx, msg, &pei);
+	}
+				
+	return 0;
+}
diff --git a/libfdcore/p_cnx.c b/libfdcore/p_cnx.c
new file mode 100644
index 0000000..9dc2a03
--- /dev/null
+++ b/libfdcore/p_cnx.c
@@ -0,0 +1,357 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@freediameter.net>							 *
+*													 *
+* Copyright (c) 2013, 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 "fdcore-internal.h"
+
+
+/* TODO: change the behavior to handle properly forced ordering at beginning & end of OPEN state */
+
+/* This file contains code used by a peer state machine to initiate a connection to remote peer */
+
+struct next_conn {
+	struct fd_list	chain;
+	int		proto;	/* Protocol of the next attempt */
+	union {
+		sSS	ss;	/* The address, only for TCP */
+		sSA4	sin;
+		sSA6	sin6;
+	};
+	uint16_t	port;	/* The port, for SCTP (included in ss for TCP) */
+	int		dotls;	/* Handshake TLS after connection ? */
+};
+
+static __inline__ void failed_connection_attempt(struct fd_peer * peer)
+{
+	/* Simply remove the first item in the list if not empty */
+	if (! FD_IS_LIST_EMPTY(&peer->p_connparams) ) {
+		struct fd_list * li = peer->p_connparams.next;
+		fd_list_unlink(li);
+		free(li);
+	}
+}
+
+static void empty_connection_list(struct fd_peer * peer)
+{
+	/* Remove all items */
+	while (!FD_IS_LIST_EMPTY(&peer->p_connparams)) {
+		failed_connection_attempt(peer);
+	}
+}
+
+static int prepare_connection_list(struct fd_peer * peer)
+{
+	struct fd_list * li, *last_prio;
+	struct next_conn   * new; 
+	
+	uint16_t	port_no; /* network order */
+	int		dotls_immediate;
+	int count = 0;
+	
+	TRACE_ENTRY("%p", peer);
+	 
+	/* Resolve peer address(es) if needed */
+	if (FD_IS_LIST_EMPTY(&peer->p_hdr.info.pi_endpoints)) {
+		struct addrinfo hints, *ai, *aip;
+		int ret;
+
+		memset(&hints, 0, sizeof(hints));
+		hints.ai_flags = AI_ADDRCONFIG;
+		ret = getaddrinfo(peer->p_hdr.info.pi_diamid, NULL, &hints, &ai);
+		if (ret) {
+			TRACE_DEBUG(INFO, "Unable to resolve address for peer '%s' (%s), aborting", peer->p_hdr.info.pi_diamid, gai_strerror(ret));
+			if (ret != EAI_AGAIN)
+				fd_psm_terminate( peer, NULL );
+			return 0;
+		}
+		
+		for (aip = ai; aip != NULL; aip = aip->ai_next) {
+			CHECK_FCT( fd_ep_add_merge( &peer->p_hdr.info.pi_endpoints, aip->ai_addr, aip->ai_addrlen, EP_FL_DISC ) );
+		}
+		freeaddrinfo(ai);
+	}
+	
+	/* Remove addresses from unwanted family */
+	if (peer->p_hdr.info.config.pic_flags.pro3) {
+		CHECK_FCT( fd_ep_filter_family(
+					&peer->p_hdr.info.pi_endpoints, 
+					(peer->p_hdr.info.config.pic_flags.pro3 == PI_P3_IP) ? 
+						AF_INET 
+						: AF_INET6));
+	}
+	if (fd_g_config->cnf_flags.no_ip4) {
+		CHECK_FCT( fd_ep_filter_family(
+					&peer->p_hdr.info.pi_endpoints, 
+					AF_INET6));
+	}
+	if (fd_g_config->cnf_flags.no_ip6) {
+		CHECK_FCT( fd_ep_filter_family(
+					&peer->p_hdr.info.pi_endpoints, 
+					AF_INET));
+	}
+	
+	/* We don't use the alternate addresses that were sent by the remote peer */
+	CHECK_FCT( fd_ep_clearflags(&peer->p_hdr.info.pi_endpoints, EP_FL_ADV) );
+	
+	
+	/* Now check we have at least one address to attempt */
+	if (FD_IS_LIST_EMPTY(&peer->p_hdr.info.pi_endpoints)) {
+		TRACE_DEBUG(INFO, "No address %savailable to connect to peer '%s', aborting", 
+					peer->p_hdr.info.config.pic_flags.pro3 ? "in the configured family " : "", peer->p_hdr.info.pi_diamid);
+		fd_psm_terminate( peer, NULL );
+		return 0;
+	}
+	
+	/* Check if we are able to communicate with this peer */
+	if (fd_g_config->cnf_sec_data.tls_disabled && ( peer->p_hdr.info.config.pic_flags.sec != PI_SEC_NONE)) {
+		LOG_E("Peer '%s' not configured for No_TLS and TLS is locally disabled; giving up connection attempts", 
+					peer->p_hdr.info.pi_diamid);
+		fd_psm_terminate( peer, NULL );
+		return 0;
+	}
+	
+	/* Cleanup any previous list */
+	empty_connection_list(peer);
+	
+	/* Prepare the parameters */
+	if ((peer->p_hdr.info.config.pic_flags.sec != PI_SEC_DEFAULT) || (fd_g_config->cnf_flags.tls_alg)) {
+		dotls_immediate = 0;
+		port_no = htons(peer->p_hdr.info.config.pic_port ?: DIAMETER_PORT);
+	} else {
+		dotls_immediate = 1;
+		port_no = htons(peer->p_hdr.info.config.pic_port ?: DIAMETER_SECURE_PORT);
+	}
+	
+	last_prio = &peer->p_connparams;
+	
+	/* Create TCP parameters unless specified otherwise */
+	if ((!fd_g_config->cnf_flags.no_tcp) && (peer->p_hdr.info.config.pic_flags.pro4 != PI_P4_SCTP)) {
+		for (li = peer->p_hdr.info.pi_endpoints.next; li != &peer->p_hdr.info.pi_endpoints; li = li->next) {
+			struct fd_endpoint * ep = (struct fd_endpoint *)li;
+			
+			CHECK_MALLOC( new = malloc(sizeof(struct next_conn)) );
+			memset(new, 0, sizeof(struct next_conn));
+			fd_list_init(&new->chain, new);
+			
+			new->proto = IPPROTO_TCP;
+			
+			memcpy( &new->ss, &ep->ss, sizeof(sSS) );
+			switch (new->ss.ss_family) {
+				case AF_INET:
+					new->sin.sin_port = port_no;
+					break;
+				case AF_INET6:
+					new->sin6.sin6_port = port_no;
+					break;
+				default:
+					free(new);
+					continue; /* Move to the next endpoint */
+			}
+			
+			new->dotls = dotls_immediate;
+			
+			/* Add the new entry to the appropriate position (conf and disc go first) */
+			if (ep->flags & (EP_FL_CONF | EP_FL_DISC)) {
+				fd_list_insert_after(last_prio, &new->chain);
+				last_prio = &new->chain;
+			} else {
+				fd_list_insert_before(&peer->p_connparams, &new->chain);
+			}
+			count++;
+		}
+	}
+	
+	/* Now, add the SCTP entry, if not disabled */
+#ifndef DISABLE_SCTP
+	if ((!fd_g_config->cnf_flags.no_sctp) && (peer->p_hdr.info.config.pic_flags.pro4 != PI_P4_TCP)) {
+		struct next_conn   * new;
+		
+		CHECK_MALLOC( new = malloc(sizeof(struct next_conn)) );
+		memset(new, 0, sizeof(struct next_conn));
+		fd_list_init(&new->chain, new);
+
+		new->proto = IPPROTO_SCTP;
+		new->port  = ntohs(port_no); /* back to host byte order... */
+		new->dotls = dotls_immediate;
+
+		/* Add the new entry to the appropriate position (depending on preferences) */
+		if ((fd_g_config->cnf_flags.pr_tcp) || (peer->p_hdr.info.config.pic_flags.alg == PI_ALGPREF_TCP)) {
+			fd_list_insert_after(last_prio, &new->chain);
+		} else {
+			fd_list_insert_after(&peer->p_connparams, &new->chain); /* very first position */
+		}
+		count++;
+	}
+#endif /* DISABLE_SCTP */
+	
+	LOG_D("Prepared %d sets of connection parameters to peer %s", count, peer->p_hdr.info.pi_diamid);
+	
+	return 0;
+}
+
+
+/* The thread that attempts the connection */
+static void * connect_thr(void * arg)
+{
+	struct fd_peer * peer = arg;
+	struct cnxctx * cnx = NULL;
+	struct next_conn * nc = NULL;
+	int rebuilt = 0;
+	int fatal_error=0;
+	
+	TRACE_ENTRY("%p", arg);
+	CHECK_PARAMS_DO( CHECK_PEER(peer), return NULL );
+
+	/* Set the thread name */
+	{
+		char buf[48];
+		snprintf(buf, sizeof(buf), "ConnTo:%s", peer->p_hdr.info.pi_diamid);
+		fd_log_threadname ( buf );
+	}
+	
+	do {
+		/* Rebuild the list if needed, if it is empty -- but at most once */
+		if (FD_IS_LIST_EMPTY(&peer->p_connparams)) {
+			if (! rebuilt) {
+				CHECK_FCT_DO( fatal_error = prepare_connection_list(peer), goto out );
+				rebuilt ++;
+			}
+			if (FD_IS_LIST_EMPTY(&peer->p_connparams)) {
+				/* We encountered an error or we have looped over all the addresses of the peer. */
+				fd_hook_call(HOOK_PEER_CONNECT_FAILED, NULL, peer, "All connection attempts failed, will retry later", NULL);
+				
+				CHECK_FCT_DO( fatal_error = fd_event_send(peer->p_events, FDEVP_CNX_FAILED, 0, NULL), goto out );
+				return NULL;
+			}
+		}
+		
+		/* Attempt connection to the first entry */
+		nc = (struct next_conn *)(peer->p_connparams.next);
+		
+		switch (nc->proto) {
+			case IPPROTO_TCP:
+				cnx = fd_cnx_cli_connect_tcp((sSA *)&nc->ss, sSAlen(&nc->ss));
+				break;
+#ifndef DISABLE_SCTP			
+			case IPPROTO_SCTP:
+				cnx = fd_cnx_cli_connect_sctp((peer->p_hdr.info.config.pic_flags.pro3 == PI_P3_IP) ? 1 : fd_g_config->cnf_flags.no_ip6, 
+							nc->port, &peer->p_hdr.info.pi_endpoints);
+				break;
+#endif /* DISABLE_SCTP */
+		}
+		
+		if (cnx)
+			break;
+		
+		/* Pop these parameters and continue */
+		failed_connection_attempt(peer);
+		
+		pthread_testcancel();
+		
+	} while (!cnx); /* and until cancellation or all addresses attempted without success */
+	
+	/* Now, we have an established connection in cnx */
+	
+	pthread_cleanup_push((void *)fd_cnx_destroy, cnx);
+	
+	/* Set the hostname in the connection, so that handshake verifies the remote identity */
+	fd_cnx_sethostname(cnx,peer->p_hdr.info.pi_diamid);
+	
+	/* Handshake if needed (secure port) */
+	if (nc->dotls) {
+		CHECK_FCT_DO( fd_cnx_handshake(cnx, GNUTLS_CLIENT, 
+						ALGO_HANDSHAKE_3436,
+						peer->p_hdr.info.config.pic_priority, NULL),
+			{
+				/* Handshake failed ...  */
+				fd_hook_call(HOOK_PEER_CONNECT_FAILED, NULL, peer, "TLS Handshake failed", NULL);
+				fd_cnx_destroy(cnx);
+				empty_connection_list(peer);
+				fd_ep_filter(&peer->p_hdr.info.pi_endpoints, EP_FL_CONF);
+				goto out_pop;
+			} );
+		LOG_A("%s: TLS handshake successful.", peer->p_hdr.info.pi_diamid);
+	} else {
+		/* Prepare to receive the next message */
+		CHECK_FCT_DO( fatal_error = fd_cnx_start_clear(cnx, 0), goto out_pop );
+	}
+	
+	/* Upon success, generate FDEVP_CNX_ESTABLISHED */
+	CHECK_FCT_DO( fatal_error = fd_event_send(peer->p_events, FDEVP_CNX_ESTABLISHED, 0, cnx),  );
+out_pop:
+	;	
+	pthread_cleanup_pop(0);
+	
+out:
+	
+	if (fatal_error) {
+	
+		/* Cleanup the connection */
+		if (cnx)
+			fd_cnx_destroy(cnx);
+
+		/* Generate a termination event */
+		CHECK_FCT_DO(fd_core_shutdown(), );
+	}
+	
+	return NULL;
+}
+
+
+/* Initiate a connection attempt to a remote peer */
+int fd_p_cnx_init(struct fd_peer * peer)
+{
+	TRACE_ENTRY("%p", peer);
+	
+	/* Start the connect thread */
+	CHECK_FCT( pthread_create(&peer->p_ini_thr, NULL, connect_thr, peer) );
+	return 0;
+}
+
+/* Cancel a connection attempt */
+void fd_p_cnx_abort(struct fd_peer * peer, int cleanup_all)
+{
+	TRACE_ENTRY("%p %d", peer, cleanup_all);
+	CHECK_PARAMS_DO( CHECK_PEER(peer), return );
+	
+	if (peer->p_ini_thr != (pthread_t)NULL) {
+		CHECK_FCT_DO( fd_thr_term(&peer->p_ini_thr), /* continue */);
+		failed_connection_attempt(peer);
+	}
+	
+	if (cleanup_all) {
+		empty_connection_list(peer);
+	}
+}
+
diff --git a/libfdcore/p_dp.c b/libfdcore/p_dp.c
new file mode 100644
index 0000000..2cd39d5
--- /dev/null
+++ b/libfdcore/p_dp.c
@@ -0,0 +1,207 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@freediameter.net>							 *
+*													 *
+* Copyright (c) 2013, 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 "fdcore-internal.h"
+
+/* This file contains code to handle Disconnect Peer messages (DPR and DPA) */
+
+/* Delay to use before next reconnect attempt */
+int fd_p_dp_newdelay(struct fd_peer * peer) 
+{
+	int delay = peer->p_hdr.info.config.pic_tctimer ?: fd_g_config->cnf_timer_tc;
+	
+	switch (peer->p_hdr.info.runtime.pir_lastDC) {
+		case ACV_DC_REBOOTING:
+		default:
+			/* We use TcTimer to attempt reconnection */
+			break;
+		case ACV_DC_BUSY:
+			/* No need to hammer the overloaded peer */
+			delay *= 10;
+			break;
+		case ACV_DC_NOT_FRIEND:
+			/* He does not want to speak to us... let's retry a *lot* later maybe */
+			delay *= 200;
+			break;
+	}
+	return delay;
+}
+
+/* Handle a received message */
+int fd_p_dp_handle(struct msg ** msg, int req, struct fd_peer * peer)
+{
+	long to_receive, to_send;
+	TRACE_ENTRY("%p %d %p", msg, req, peer);
+	
+	if (req) {
+		/* We received a DPR, save the Disconnect-Cause and go to CLOSING_GRACE or terminate the connection */
+		struct avp * dc;
+		
+		CHECK_FCT( fd_msg_search_avp ( *msg, fd_dict_avp_DC, &dc ));
+		if (dc) {
+			struct avp_hdr * hdr;
+			CHECK_FCT(  fd_msg_avp_hdr( dc, &hdr )  );
+			if (hdr->avp_value == NULL) {
+				/* This is a sanity check */
+				LOG_F("BUG: Unset value in Disconnect-Cause in DPR");
+				ASSERT(0); /* To check if this really happens, and understand why... */
+			}
+
+			/* save the cause */
+			peer->p_hdr.info.runtime.pir_lastDC = hdr->avp_value->u32;
+		}
+		if (TRACE_BOOL(INFO)) {
+			if (dc) {
+				struct dict_object * dictobj;
+				struct dict_enumval_request er;
+				memset(&er, 0, sizeof(er));
+				
+				/* prepare the request */
+				CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_TYPE, TYPE_OF_AVP, fd_dict_avp_DC, &er.type_obj, ENOENT )  );
+				er.search.enum_value.u32 = peer->p_hdr.info.runtime.pir_lastDC;
+				
+				/* Search the enum value */
+				CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_ENUMVAL, ENUMVAL_BY_STRUCT, &er, &dictobj, 0 )  );
+				if (dictobj) {
+					CHECK_FCT( fd_dict_getval( dictobj, &er.search ) );
+					TRACE_DEBUG(INFO, "Peer '%s' sent a DPR with cause: %s", peer->p_hdr.info.pi_diamid, er.search.enum_name);
+				} else {
+					TRACE_DEBUG(INFO, "Peer '%s' sent a DPR with unknown cause: %u", peer->p_hdr.info.pi_diamid, peer->p_hdr.info.runtime.pir_lastDC);
+				}
+			} else {
+				TRACE_DEBUG(INFO, "Peer '%s' sent a DPR without Disconnect-Cause AVP", peer->p_hdr.info.pi_diamid);
+			}
+		}
+		
+		/* Now reply with a DPA */
+		CHECK_FCT( fd_msg_new_answer_from_req ( fd_g_config->cnf_dict, msg, 0 ) );
+		CHECK_FCT( fd_msg_rescode_set( *msg, "DIAMETER_SUCCESS", NULL, NULL, 1 ) );
+		
+		/* Do we have pending exchanges with this peer? */
+		CHECK_FCT( fd_peer_get_load_pending(&peer->p_hdr, &to_receive, &to_send) );
+		
+		if ((to_receive == 0) && (to_send == 0)) {
+			/* No pending exchange, move to CLOSING directly */
+			CHECK_FCT( fd_psm_change_state(peer, STATE_CLOSING) );
+		
+			/* Now send the DPA */
+			CHECK_FCT( fd_out_send( msg, NULL, peer, 0) );
+			
+			/* and move to CLOSED */
+			fd_psm_cleanup(peer, 0);
+
+			/* Reset the timer for next connection attempt -- we'll retry sooner or later depending on the disconnection cause */
+			fd_psm_next_timeout(peer, 1, fd_p_dp_newdelay(peer));
+		} else {
+			/* We have pending exchanges, we move to CLOSING_GRACE which allows exchanges of answers but
+			not new requests */
+			CHECK_FCT( fd_psm_change_state(peer, STATE_CLOSING_GRACE) );
+			fd_psm_next_timeout(peer, 0, GRACE_TIMEOUT);
+			
+			/* Now send the DPA */
+			CHECK_FCT( fd_out_send( msg, NULL, peer, 0) );
+		}
+	} else {
+		/* We received a DPA */
+		int curstate = fd_peer_getstate(peer);
+		if (curstate != STATE_CLOSING_GRACE) {
+			TRACE_DEBUG(INFO, "Ignoring DPA received in state %s", STATE_STR(curstate));
+		}
+			
+		/* In theory, we should control the Result-Code AVP. But since we will not go back to OPEN state here anyway, let's skip it */
+		
+		/* TODO("Control Result-Code in the DPA") */
+		CHECK_FCT_DO( fd_msg_free( *msg ), /* continue */ );
+		*msg = NULL;
+		
+		/* Do we still have pending exchanges with this peer? */
+		CHECK_FCT( fd_peer_get_load_pending(&peer->p_hdr, &to_receive, &to_send) );
+		if ((to_receive != 0) || (to_send != 0)) {
+			TRACE_DEBUG(INFO, "Received DPA but pending load: [%ld, %ld], giving grace delay before closing", to_receive, to_send);
+			fd_psm_next_timeout(peer, 0, GRACE_TIMEOUT);
+			peer->p_flags.pf_localterm = 1;
+		} else {
+			/* otherwise, go to CLOSING state, the psm will handle terminating the connection */
+			CHECK_FCT( fd_psm_change_state(peer, STATE_CLOSING) );
+		}
+	}
+	
+	return 0;
+}
+
+/* Start disconnection of a peer: send DPR */
+int fd_p_dp_initiate(struct fd_peer * peer, char * reason)
+{
+	struct msg * msg = NULL;
+	struct dict_object * dictobj = NULL;
+	struct avp * avp = NULL;
+	struct dict_enumval_request er;
+	union avp_value val;
+	
+	TRACE_ENTRY("%p %p", peer, reason);
+	
+	/* Create a new DWR instance */
+	CHECK_FCT( fd_msg_new ( fd_dict_cmd_DPR, MSGFL_ALLOC_ETEID, &msg ) );
+	
+	/* Add the Origin information */
+	CHECK_FCT( fd_msg_add_origin ( msg, 0 ) );
+	
+	/* Add the Disconnect-Cause */
+	CHECK_FCT( fd_msg_avp_new ( fd_dict_avp_DC, 0, &avp ) );
+	
+	/* Search the value in the dictionary */
+	memset(&er, 0, sizeof(er));
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_TYPE, TYPE_OF_AVP, fd_dict_avp_DC, &er.type_obj, ENOENT )  );
+	er.search.enum_name = reason ?: "REBOOTING";
+	CHECK_FCT_DO( fd_dict_search( fd_g_config->cnf_dict, DICT_ENUMVAL, ENUMVAL_BY_STRUCT, &er, &dictobj, ENOENT ), { ASSERT(0); /* internal error: unknown reason */ }  );
+	CHECK_FCT( fd_dict_getval( dictobj, &er.search ) );
+	
+	/* Set the value in the AVP */
+	val.u32 = er.search.enum_value.u32;
+	CHECK_FCT( fd_msg_avp_setvalue( avp, &val ) );
+	CHECK_FCT( fd_msg_avp_add( msg, MSG_BRW_LAST_CHILD, avp ) );
+	
+	/* Save the value also in the peer */
+	peer->p_hdr.info.runtime.pir_lastDC = val.u32;
+	
+	/* Update the peer state and timer */
+	CHECK_FCT( fd_psm_change_state(peer, STATE_CLOSING_GRACE) );
+	fd_psm_next_timeout(peer, 0, DPR_TIMEOUT);
+	
+	/* Now send the DPR message */
+	CHECK_FCT_DO( fd_out_send(&msg, NULL, peer, 0), /* ignore since we are on timeout anyway */ );
+	
+	return 0;
+}
diff --git a/libfdcore/p_dw.c b/libfdcore/p_dw.c
new file mode 100644
index 0000000..dc87f8d
--- /dev/null
+++ b/libfdcore/p_dw.c
@@ -0,0 +1,177 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@freediameter.net>							 *
+*													 *
+* Copyright (c) 2013, 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 "fdcore-internal.h"
+
+/* This file contains code to handle Device Watchdog messages (DWR and DWA) */
+
+/* Check the value of Origin-State-Id is consistent in a DWR or DWA -- we return an error otherwise */
+static int check_state_id(struct msg * msg, struct fd_peer * peer)
+{
+	struct avp * osi;
+	
+	/* Check if the request contains the Origin-State-Id */
+	CHECK_FCT( fd_msg_search_avp ( msg, fd_dict_avp_OSI, &osi ) );
+	if (osi) {
+		/* Check the value is consistent with the saved one */
+		struct avp_hdr * hdr;
+		CHECK_FCT(  fd_msg_avp_hdr( osi, &hdr )  );
+		if (hdr->avp_value == NULL) {
+			/* This is a sanity check */
+			LOG_F("Ignored an Origin-State-Id AVP with unset value in DWR/DWA");
+			ASSERT(0); /* To check if this really happens, and understand why... */
+		}
+
+		if (! peer->p_hdr.info.runtime.pir_orstate) {
+			/* It was not already received in CER/CEA, save it now */
+			peer->p_hdr.info.runtime.pir_orstate = hdr->avp_value->u32;
+		}
+
+		if (peer->p_hdr.info.runtime.pir_orstate != hdr->avp_value->u32) {
+			TRACE_DEBUG(INFO, "Received a new Origin-State-Id from peer '%s'! (%x -> %x); resetting the connection.", 
+				peer->p_hdr.info.pi_diamid, 
+				peer->p_hdr.info.runtime.pir_orstate,
+				hdr->avp_value->u32 );
+			return EINVAL;
+		}
+	}
+	return 0;
+}
+
+/* Create and send a DWR */
+static int send_DWR(struct fd_peer * peer)
+{
+	struct msg * msg = NULL;
+	
+	/* Create a new DWR instance */
+	CHECK_FCT( fd_msg_new ( fd_dict_cmd_DWR, MSGFL_ALLOC_ETEID, &msg ) );
+	
+	/* Add the content of the message (only the origin) */
+	CHECK_FCT( fd_msg_add_origin ( msg, 1 ) );
+	
+	/* Now send this message */
+	CHECK_FCT( fd_out_send(&msg, NULL, peer, 0) );
+	
+	/* And mark the pending DW */
+	peer->p_flags.pf_dw_pending = 1;
+	
+	return 0;
+}
+
+/* Handle an incoming message */
+int fd_p_dw_handle(struct msg ** msg, int req, struct fd_peer * peer)
+{
+	int reset_tmr = 0;
+	
+	TRACE_ENTRY("%p %d %p", msg, req, peer);
+	
+	/* Check the value of OSI for information */
+	CHECK_FCT( check_state_id(*msg, peer) );
+	
+	if (req) {
+		/* If we receive a DWR, send back a DWA */
+		CHECK_FCT( fd_msg_new_answer_from_req ( fd_g_config->cnf_dict, msg, 0 ) );
+		CHECK_FCT( fd_msg_rescode_set( *msg, "DIAMETER_SUCCESS", NULL, NULL, 0 ) );
+		CHECK_FCT( fd_msg_add_origin ( *msg, 1 ) );
+		CHECK_FCT( fd_out_send( msg, peer->p_cnxctx, peer, 0) );
+		
+	} else {
+		/* Discard the DWA */
+		CHECK_FCT_DO( fd_msg_free(*msg), /* continue */ );
+		*msg = NULL;
+		
+		/* And clear the pending DW flag */
+		peer->p_flags.pf_dw_pending = 0;
+	}
+	
+	/* Now update timeout */
+	if (req) {
+		/* Update timeout only if we did not already send a DWR ourselves */
+		reset_tmr = !peer->p_flags.pf_dw_pending;
+	} else {
+		/* Reset the timer */
+		reset_tmr = 1;
+	}
+	if (reset_tmr) {
+		fd_psm_next_timeout(peer, 1, peer->p_hdr.info.config.pic_twtimer ?: fd_g_config->cnf_timer_tw);
+	}
+	
+	/* If we are in REOPEN state, increment the counter */
+	if (fd_peer_getstate(peer) == STATE_REOPEN) {
+		peer->p_flags.pf_reopen_cnt += 1;
+		
+		if (peer->p_flags.pf_reopen_cnt) {
+			/* Send a new DWR */
+			CHECK_FCT( send_DWR(peer) );
+		} else {
+			/* Move to OPEN state */
+			CHECK_FCT( fd_psm_change_state(peer, STATE_OPEN) );
+		}
+	}
+		
+	return 0;
+}
+
+/* Handle a timeout in the PSM (OPEN or REOPEN state only) */
+int fd_p_dw_timeout(struct fd_peer * peer)
+{
+	TRACE_ENTRY("%p", peer);
+
+	if (peer->p_flags.pf_dw_pending) {
+		/* We have sent a DWR and received no answer during TwTimer */
+		CHECK_FCT( fd_psm_change_state(peer, STATE_SUSPECT) );
+		fd_psm_next_timeout(peer, 0, 2 * (peer->p_hdr.info.config.pic_twtimer ?: fd_g_config->cnf_timer_tw) );
+	} else {
+		/* The timeout has expired, send a DWR */
+		CHECK_FCT( send_DWR(peer) );
+		fd_psm_next_timeout(peer, 0, peer->p_hdr.info.config.pic_twtimer ?: fd_g_config->cnf_timer_tw );
+	}
+	
+	return 0;
+}
+
+/* Handle DW exchanges after the peer has come alive again */
+int fd_p_dw_reopen(struct fd_peer * peer)
+{
+	TRACE_ENTRY("%p", peer);
+
+	peer->p_flags.pf_reopen_cnt = 1;
+	peer->p_flags.pf_cnx_pb = 0;
+	CHECK_FCT( send_DWR(peer) );
+	
+	return 0;
+}
+
+
diff --git a/libfdcore/p_expiry.c b/libfdcore/p_expiry.c
new file mode 100644
index 0000000..c51f9d8
--- /dev/null
+++ b/libfdcore/p_expiry.c
@@ -0,0 +1,205 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@freediameter.net>							 *
+*													 *
+* Copyright (c) 2013, 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 "fdcore-internal.h"
+
+/* Delay for garbage collection of expired peers, in seconds */
+#define GC_TIME		120
+
+static pthread_t       exp_thr = (pthread_t)NULL;
+static pthread_t       gc_thr  = (pthread_t)NULL;
+static struct fd_list  exp_list = FD_LIST_INITIALIZER( exp_list );
+static pthread_cond_t  exp_cnd  = PTHREAD_COND_INITIALIZER;
+static pthread_mutex_t exp_mtx  = PTHREAD_MUTEX_INITIALIZER;
+
+static void * gc_th_fct(void * arg)
+{
+	fd_log_threadname ( "Peers/garb. col." );
+	TRACE_ENTRY( "%p", arg );
+	
+	do {
+		struct fd_list * li, purge = FD_LIST_INITIALIZER(purge);
+		
+		sleep(GC_TIME);	/* sleep is a cancellation point */
+		
+		/* Now check in the peers list if any peer can be deleted */
+		CHECK_FCT_DO( pthread_rwlock_wrlock(&fd_g_peers_rw), goto error );
+		
+		for (li = fd_g_peers.next; li != &fd_g_peers; li = li->next) {
+			struct fd_peer * peer = (struct fd_peer *)li->o;
+			
+			if (fd_peer_getstate(peer) != STATE_ZOMBIE)
+				continue;
+			
+			if (peer->p_hdr.info.config.pic_flags.persist == PI_PRST_ALWAYS)
+				continue; /* This peer was not supposed to terminate, keep it in the list for debug */
+			
+			/* Ok, the peer was expired, let's remove it */
+			li = li->prev; /* to avoid breaking the loop */
+			fd_list_unlink(&peer->p_hdr.chain);
+			fd_list_insert_before(&purge, &peer->p_hdr.chain);
+		}
+
+		CHECK_FCT_DO( pthread_rwlock_unlock(&fd_g_peers_rw), goto error );
+		
+		/* Now delete peers that are in the purge list */
+		while (!FD_IS_LIST_EMPTY(&purge)) {
+			struct fd_peer * peer = (struct fd_peer *)(purge.next->o);
+			fd_list_unlink(&peer->p_hdr.chain);
+			TRACE_DEBUG(INFO, "Garbage Collect: delete zombie peer '%s'", peer->p_hdr.info.pi_diamid);
+			CHECK_FCT_DO( fd_peer_free(&peer), /* Continue... what else to do ? */ );
+		}
+	} while (1);
+	
+error:
+	TRACE_DEBUG(INFO, "An error occurred in peers module! GC thread is terminating...");
+	ASSERT(0);
+	CHECK_FCT_DO(fd_core_shutdown(), );
+	return NULL;
+}
+
+
+static void * exp_th_fct(void * arg)
+{
+	fd_log_threadname ( "Peers/expire" );
+	TRACE_ENTRY( "%p", arg );
+	
+	CHECK_POSIX_DO( pthread_mutex_lock(&exp_mtx), { ASSERT(0); } );
+	pthread_cleanup_push( fd_cleanup_mutex, &exp_mtx );
+	
+	do {
+		struct timespec	now;
+		struct fd_peer * first;
+		
+		/* Check if there are expiring peers available */
+		if (FD_IS_LIST_EMPTY(&exp_list)) {
+			/* Just wait for a change or cancelation */
+			CHECK_POSIX_DO( pthread_cond_wait( &exp_cnd, &exp_mtx ), { ASSERT(0); } );
+			/* Restart the loop on wakeup */
+			continue;
+		}
+		
+		/* Get the pointer to the peer that expires first */
+		first = (struct fd_peer *)(exp_list.next->o);
+		ASSERT( CHECK_PEER(first) );
+		
+		/* Get the current time */
+		CHECK_SYS_DO(  clock_gettime(CLOCK_REALTIME, &now),  { ASSERT(0); }  );
+
+		/* If first peer is not expired, we just wait until it happens */
+		if ( TS_IS_INFERIOR( &now, &first->p_exp_timer ) ) {
+			
+			CHECK_POSIX_DO2(  pthread_cond_timedwait( &exp_cnd, &exp_mtx, &first->p_exp_timer ),  
+					ETIMEDOUT, /* ETIMEDOUT is a normal return value, continue */,
+					/* on other error, */ { ASSERT(0); } );
+	
+			/* on wakeup, loop */
+			continue;
+		}
+		
+		/* Now, the first peer in the list is expired; signal it */
+		fd_list_unlink( &first->p_expiry );
+		CHECK_FCT_DO( fd_event_send(first->p_events, FDEVP_TERMINATE, 0, "DO_NOT_WANT_TO_TALK_TO_YOU"), break );
+		
+	} while (1);
+	
+	pthread_cleanup_pop( 1 );
+
+	TRACE_DEBUG(INFO, "An error occurred in peers module! Expiry thread is terminating...");
+	CHECK_FCT_DO(fd_core_shutdown(), );
+	return NULL;
+}
+
+/* Initialize peers expiry mechanism */
+int fd_p_expi_init(void)
+{
+	TRACE_ENTRY();
+	CHECK_FCT( pthread_create( &exp_thr, NULL, exp_th_fct, NULL ) );
+	CHECK_FCT( pthread_create( &gc_thr,  NULL, gc_th_fct,  NULL ) );
+	return 0;
+}
+
+/* Finish peers expiry mechanism */
+int fd_p_expi_fini(void)
+{
+	CHECK_FCT_DO( fd_thr_term(&exp_thr), );
+	CHECK_POSIX( pthread_mutex_lock(&exp_mtx) );
+	while (!FD_IS_LIST_EMPTY(&exp_list)) {
+		struct fd_peer * peer = (struct fd_peer *)(exp_list.next->o);
+		fd_list_unlink(&peer->p_expiry );
+	}
+	CHECK_POSIX( pthread_mutex_unlock(&exp_mtx) );
+	
+	CHECK_FCT_DO( fd_thr_term(&gc_thr), );
+	return 0;
+}
+
+/* Add / requeue a peer in the expiry list */
+int fd_p_expi_update(struct fd_peer * peer )
+{
+	TRACE_ENTRY("%p", peer);
+	CHECK_PARAMS( CHECK_PEER(peer) );
+	
+	CHECK_POSIX( pthread_mutex_lock(&exp_mtx) );
+	
+	fd_list_unlink(&peer->p_expiry );
+	
+	/* if peer expires */
+	if (peer->p_hdr.info.config.pic_flags.exp) {
+		struct fd_list * li;
+		
+		/* update the p_exp_timer value */
+		CHECK_SYS_DO(  clock_gettime(CLOCK_REALTIME, &peer->p_exp_timer), { ASSERT(0); }  );
+		peer->p_exp_timer.tv_sec += peer->p_hdr.info.config.pic_lft;
+		
+		/* add to the expiry list in appropriate position (probably around the end) */
+		for (li = exp_list.prev; li != &exp_list; li = li->prev) {
+			struct fd_peer * p = (struct fd_peer *)(li->o);
+			if (TS_IS_INFERIOR( &p->p_exp_timer, &peer->p_exp_timer ) )
+				break;
+		}
+		
+		fd_list_insert_after(li, &peer->p_expiry);
+		
+		/* signal the expiry thread if we added in first position */
+		if (li == &exp_list) {
+			CHECK_POSIX( pthread_cond_signal(&exp_cnd) );
+		}
+	}
+	
+	CHECK_POSIX( pthread_mutex_unlock(&exp_mtx) );
+	return 0;
+}
+
diff --git a/libfdcore/p_out.c b/libfdcore/p_out.c
new file mode 100644
index 0000000..b410f11
--- /dev/null
+++ b/libfdcore/p_out.c
@@ -0,0 +1,237 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@freediameter.net>							 *
+*													 *
+* Copyright (c) 2015, 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 "fdcore-internal.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_peer * peer)
+{
+	struct msg_hdr * hdr;
+	int msg_is_a_req;
+	uint8_t * buf;
+	size_t sz;
+	int ret;
+	uint32_t bkp_hbh = 0;
+	struct msg *cpy_for_logs_only;
+	
+	TRACE_ENTRY("%p %p %p %p", msg, cnx, hbh, peer);
+	
+	/* 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 && peer);
+		/* Alloc the hop-by-hop id and increment the value for next message */
+		bkp_hbh = hdr->msg_hbhid;
+		hdr->msg_hbhid = *hbh;
+		*hbh = hdr->msg_hbhid + 1;
+	}
+	
+	/* Create the message buffer */
+	CHECK_FCT(fd_msg_bufferize( *msg, &buf, &sz ));
+	pthread_cleanup_push( free, buf );
+	
+	cpy_for_logs_only = *msg;
+	
+	/* Save a request before sending so that there is no race condition with the answer */
+	if (msg_is_a_req) {
+		CHECK_FCT_DO( ret = fd_p_sr_store(&peer->p_sr, msg, &hdr->msg_hbhid, bkp_hbh), goto out );
+	}
+	
+	/* Log the message */
+	fd_hook_call(HOOK_MESSAGE_SENT, cpy_for_logs_only, peer, NULL, fd_msg_pmdl_get(cpy_for_logs_only));
+	
+	pthread_cleanup_push((void *)fd_msg_free, *msg /* might be NULL, no problem */);
+	
+	/* Send the message */
+	CHECK_FCT_DO( ret = fd_cnx_send(cnx, buf, sz), );
+	
+	pthread_cleanup_pop(0);
+	
+out:
+	;	
+	pthread_cleanup_pop(1);
+	
+	if (ret)
+		return ret;
+	
+	/* Free remaining messages (i.e. answers) */
+	if (*msg) {
+		CHECK_FCT( fd_msg_free(*msg) );
+		*msg = NULL;
+	}
+	
+	return 0;
+}
+
+/* The code of the "out" thread */
+static void * out_thr(void * arg)
+{
+	struct fd_peer * peer = arg;
+	int stop = 0;
+	struct msg * msg;
+	ASSERT( CHECK_PEER(peer) );
+	
+	/* Set the thread name */
+	{
+		char buf[48];
+		snprintf(buf, sizeof(buf), "OUT/%s", peer->p_hdr.info.pi_diamid);
+		fd_log_threadname ( buf );
+	}
+	
+	/* Loop until cancelation */
+	while (!stop) {
+		int ret;
+		
+		/* Retrieve next message to send */
+		CHECK_FCT_DO( fd_fifo_get(peer->p_tosend, &msg), goto error );
+		
+		/* Send the message, log any error */
+		CHECK_FCT_DO( ret = do_send(&msg, peer->p_cnxctx, &peer->p_hbh, peer),
+			{
+				if (msg) {
+					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);
+				}
+				stop = 1;
+			} );
+			
+	}
+	
+	/* If we're here it means there was an error on the socket. We need to continue to purge the fifo & until we are canceled */
+	CHECK_FCT_DO( fd_event_send(peer->p_events, FDEVP_CNX_ERROR, 0, NULL), /* What do we do if it fails? */ );
+	
+	/* Requeue all routable messages in the global "out" queue, until we are canceled once the PSM deals with the CNX_ERROR sent above */
+	while ( fd_fifo_get(peer->p_tosend, &msg) == 0 ) {
+		if (fd_msg_is_routable(msg)) {
+			CHECK_FCT_DO(fd_fifo_post_noblock(peer->p_tofailover, (void *)&msg), 
+				{
+					/* fallback: destroy the message */
+					fd_hook_call(HOOK_MESSAGE_DROPPED, msg, NULL, "Internal error: unable to requeue this message during failover process", fd_msg_pmdl_get(msg));
+					CHECK_FCT_DO(fd_msg_free(msg), /* What can we do more? */)
+				} );
+		} else {
+			/* Just free it */
+			/* fd_hook_call(HOOK_MESSAGE_DROPPED, m, NULL, "Non-routable message freed during handover", fd_msg_pmdl_get(m)); */
+			CHECK_FCT_DO(fd_msg_free(msg), /* What can we do more? */)
+		}
+	}
+
+error:
+	/* 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;
+}
+
+/* Wrapper to sending a message either by out thread (peer in OPEN state) or directly; cnx or peer must be provided. Flags are valid only for direct sending, not through thread (unused) */
+int fd_out_send(struct msg ** msg, struct cnxctx * cnx, struct fd_peer * peer, int update_reqin_cnt)
+{
+	struct msg_hdr * hdr;
+	
+	TRACE_ENTRY("%p %p %p", msg, cnx, peer);
+	CHECK_PARAMS( msg && *msg && (cnx || (peer && peer->p_cnxctx)));
+
+	fd_hook_call(HOOK_MESSAGE_SENDING, *msg, peer, NULL, fd_msg_pmdl_get(*msg));
+	
+	if (update_reqin_cnt && peer) {
+		CHECK_FCT( fd_msg_hdr(*msg, &hdr) );
+		if (!(hdr->msg_flags & CMD_FLAG_REQUEST)) {
+			/* Update the count of pending answers to send */
+			CHECK_POSIX( pthread_mutex_lock(&peer->p_state_mtx) );
+			peer->p_reqin_count--;
+			CHECK_POSIX( pthread_mutex_unlock(&peer->p_state_mtx) );			
+		}
+	}
+	
+	if (fd_peer_getstate(peer) == STATE_OPEN) {
+		/* Normal case: just queue for the out thread to pick it up */
+		CHECK_FCT( fd_fifo_post(peer->p_tosend, msg) );
+		
+	} else {
+		int ret;
+		uint32_t *hbh = NULL;
+		
+		/* In other cases, the thread is not running, so we handle the sending directly */
+		if (peer)
+			hbh = &peer->p_hbh;
+
+		if (!cnx)
+			cnx = peer->p_cnxctx;
+
+		/* Do send the message */
+		CHECK_FCT_DO( ret = do_send(msg, cnx, hbh, peer),
+			{
+				if (msg) {
+					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;
+				}
+			} );
+	}
+	
+	return 0;
+}
+
+/* Start the "out" thread that picks messages in p_tosend and send them on p_cnxctx */
+int fd_out_start(struct fd_peer * peer)
+{
+	TRACE_ENTRY("%p", peer);
+	CHECK_PARAMS( CHECK_PEER(peer) && (peer->p_outthr == (pthread_t)NULL) );
+	
+	CHECK_POSIX( pthread_create(&peer->p_outthr, NULL, out_thr, peer) );
+	
+	CHECK_FCT( fd_cnx_unordered_delivery(peer->p_cnxctx, 1) );
+	
+	return 0;
+}
+
+/* Stop that thread */
+int fd_out_stop(struct fd_peer * peer)
+{
+	TRACE_ENTRY("%p", peer);
+	CHECK_PARAMS( CHECK_PEER(peer) );
+	
+	CHECK_FCT( fd_cnx_unordered_delivery(peer->p_cnxctx, 0) );
+	
+	CHECK_FCT( fd_thr_term(&peer->p_outthr) );
+	
+	return 0;
+}
+		
diff --git a/libfdcore/p_psm.c b/libfdcore/p_psm.c
new file mode 100644
index 0000000..2a30c5c
--- /dev/null
+++ b/libfdcore/p_psm.c
@@ -0,0 +1,952 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@freediameter.net>							 *
+*													 *
+* Copyright (c) 2013, 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 "fdcore-internal.h"
+
+/*
+This file implement a Peer State Machine which is a mix of:
+ - the state machine described in rfc3588bis
+ - the state machine described in rfc3539#section-3.4
+ - the following observations.
+ 
+The delivery of Diameter messages must not always be unordered: order is important at
+begining and end of a connection lifetime. It means we need agility to
+switch between "ordering enforced" and "ordering not enforced to counter
+Head of the Line Blocking" modes of operation.
+
+The connection state machine represented in RFC3588 (and RFC6733) is
+incomplete, because it lacks the SUSPECT state and the 3 DWR/DWA
+exchanges (section 5.1) when the peer recovers from this state.
+Personnally I don't see the rationale for exchanging 3 messages (why 3?)
+but, if we require at least 1 DWR/DWA exchange to be always performed
+after the CER/CEA exchange (and initiated by the peer that sent the
+CEA), we have a simple way to deal with our ordering problem, as resumed
+below. Peers are: [i]nitiator, [r]esponder.
+ (1) [i] SCTP connection attempt.
+ (2) [r] accept the connection.
+ (3) [i,r] (if secure port) DTLS handshake, close on failure.
+ (4) [i] Send CER
+ (5) [r] Receive CER, send CEA using stream 0, flag "unordered" cleared.
+       [r] Immediately send a DWR after the CEA, also using stream 0,
+flag "unordered" cleared.
+       [r] Move to STATE_OPEN_NEW state -- equivalent to OPEN except
+that all messages are sent ordered at the moment.
+ (6) [i] receive CEA, move to OPEN state. All messages can be sent
+unordered in OPEN state.
+       [i] As per normal operation, reply with DWA to the DWR.
+ (7) [r] Upon reception of the DWA, move to OPEN state, messages can be
+sent unordered from this point.
+
+Note about (5) and (6): if the Diameter Identity received in CER or CEA
+does not match the credentials from the certificate presented during
+TLS handshake, we may need to specify a path of clean disconnection
+(not blocking the remote peer waiting for something).
+
+This proposed mechanism removes the problem of application messages
+received before the CEA by the initiator. Note that if the "old" inband
+TLS handshake is used, this handshake plays the same synchronization
+role than the new DWR/DWA, which becomes useless.
+
+
+The other time where ordering is important is by the end of connection
+lifetime, when one peer is shutting down the link for some reason
+(reboot, overload, no activity, etc...). In case of unordered delivery,
+we may have:
+- peer A sends an application message followed by a DPR. Peer B receives
+the DPR first and tears down the connection. Application message is lost.
+- Peer B sends an application message, then receives a DPR and answers a
+DPA. Peer A receives the DPA before the application message. The
+application message is lost.
+
+This situation is actually happening easily because DPR/DPA messages are
+very short, while application messages can be quite large. Therefore,
+they require much more time to deliver.
+
+I really cannot see a way to counter this effect by using the ordering
+of the messages, except by applying a timer (state STATE_CLOSING_GRACE).
+This timer can be also useful when we detect that some messages has not 
+yet received an answer on this link, to give time to the application to 
+complete the exchange ongoing.
+
+However, this problem must be balanced with the fact that the message
+that is lost will be in many cases sent again as the failover mechanism
+specifies.
+*/
+
+/* The actual declaration of peer_state_str */
+DECLARE_STATE_STR();
+
+/* Helper for next macro */
+#define case_str( _val )		\
+	case _val : return #_val
+
+DECLARE_PEV_STR();
+
+/************************************************************************/
+/*                      Delayed startup                                 */
+/************************************************************************/
+static int started = 0;
+static pthread_mutex_t  started_mtx = PTHREAD_MUTEX_INITIALIZER;
+static pthread_cond_t   started_cnd = PTHREAD_COND_INITIALIZER;
+
+/* Wait for start signal */
+static int fd_psm_waitstart()
+{
+	int ret = 0;
+	TRACE_ENTRY("");
+	CHECK_POSIX( pthread_mutex_lock(&started_mtx) );
+awake:	
+	if (!ret && !started) {
+		pthread_cleanup_push( fd_cleanup_mutex, &started_mtx );
+		CHECK_POSIX_DO( ret = pthread_cond_wait(&started_cnd, &started_mtx), );
+		pthread_cleanup_pop( 0 );
+		goto awake;
+	}
+	CHECK_POSIX( pthread_mutex_unlock(&started_mtx) );
+	return ret;
+}
+
+/* Allow the state machines to start */
+int fd_psm_start()
+{
+	TRACE_ENTRY("");
+	CHECK_POSIX( pthread_mutex_lock(&started_mtx) );
+	started = 1;
+	CHECK_POSIX( pthread_cond_broadcast(&started_cnd) );
+	CHECK_POSIX( pthread_mutex_unlock(&started_mtx) );
+	return 0;
+}
+
+
+/************************************************************************/
+/*                 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, terminating the connection");
+				fd_psm_terminate(peer, "DO_NOT_WANT_TO_TALK_TO_YOU" );
+			} );
+		peer->p_cb2 = NULL;
+		return 0;
+	}
+	
+	/* Insert in the active peers list */
+	CHECK_POSIX( pthread_rwlock_wrlock(&fd_g_activ_peers_rw) );
+	for (li = fd_g_activ_peers.next; li != &fd_g_activ_peers; li = li->next) {
+		struct fd_peer * next_p = (struct fd_peer *)li->o;
+		int cmp = fd_os_cmp(peer->p_hdr.info.pi_diamid, peer->p_hdr.info.pi_diamidlen, 
+					next_p->p_hdr.info.pi_diamid, next_p->p_hdr.info.pi_diamidlen);
+		if (cmp < 0)
+			break;
+	}
+	fd_list_insert_before(li, &peer->p_actives);
+	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); /* TODO: do this in a separate detached thread? */
+		peer->p_cb = NULL;
+		peer->p_cb_data = NULL;
+	}
+	
+	/* Start the thread to handle outgoing messages */
+	CHECK_FCT( fd_out_start(peer) );
+	
+	/* Update the expiry timer now */
+	CHECK_FCT( fd_p_expi_update(peer) );
+	
+	return 0;
+}
+static int leave_open_state(struct fd_peer * peer, int skip_failover)
+{
+	/* 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) );
+	
+	/* Failover the messages */
+	if (!skip_failover) {
+		fd_peer_failover_msg(peer);
+	}
+	
+	return 0;
+}
+
+
+/************************************************************************/
+/*                      Helpers for state changes                       */
+/************************************************************************/
+
+/* Cleanup pending events in the peer */
+void fd_psm_events_free(struct fd_peer * peer)
+{
+	struct fd_event * ev;
+	/* Purge all events, and free the associated data if any */
+	while (fd_fifo_tryget( peer->p_events, &ev ) == 0) {
+		switch (ev->code) {
+			case FDEVP_CNX_ESTABLISHED: {
+				fd_cnx_destroy(ev->data);
+			}
+			break;
+			
+			case FDEVP_TERMINATE:
+				/* Do not free the string since it is a constant */
+			break;
+			
+			case FDEVP_CNX_INCOMING: {
+				struct cnx_incoming * evd = ev->data;
+				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);
+			}
+			default:
+				free(ev->data);
+		}
+		free(ev);
+	}
+}
+
+/* Read state */
+int fd_peer_get_state(struct peer_hdr *peer)
+{
+	int ret;
+	
+	struct fd_peer * p = (struct fd_peer *)peer;
+	
+	if (!CHECK_PEER(p))
+		return -1;
+	
+	CHECK_POSIX_DO( pthread_mutex_lock(&p->p_state_mtx), return -1 );
+	ret = p->p_state;
+	CHECK_POSIX_DO( pthread_mutex_unlock(&p->p_state_mtx), return -1 );
+	
+	return ret;
+}
+
+
+/* Change state */
+int fd_psm_change_state(struct fd_peer * peer, int new_state)
+{
+	int old;
+	
+	TRACE_ENTRY("%p %d(%s)", peer, new_state, STATE_STR(new_state));
+	CHECK_PARAMS( CHECK_PEER(peer) );
+	
+	old = fd_peer_getstate(peer);
+	if (old == new_state)
+		return 0;
+	
+	LOG(((old == STATE_OPEN) || (new_state == STATE_OPEN)) ? FD_LOG_NOTICE : FD_LOG_DEBUG, "'%s'\t-> '%s'\t'%s'",
+			STATE_STR(old),
+			STATE_STR(new_state),
+			peer->p_hdr.info.pi_diamid);
+	
+	
+	CHECK_POSIX( pthread_mutex_lock(&peer->p_state_mtx) );
+	peer->p_state = new_state;
+	CHECK_POSIX( pthread_mutex_unlock(&peer->p_state_mtx) );
+	
+	if (old == STATE_OPEN) {
+		CHECK_FCT( leave_open_state(peer, new_state == STATE_CLOSING_GRACE) );
+	}
+	if (old == STATE_CLOSING_GRACE) {
+		fd_peer_failover_msg(peer);
+	}
+
+	if (new_state == STATE_OPEN) {
+		CHECK_FCT( enter_open_state(peer) );
+	}
+	
+	if (new_state == STATE_CLOSED) {
+		/* Purge event list */
+		fd_psm_events_free(peer);
+		
+		/* Reset the counter of pending anwers to send */
+		peer->p_reqin_count = 0;
+		
+		/* If the peer is not persistant, we destroy it */
+		if (peer->p_hdr.info.config.pic_flags.persist == PI_PRST_NONE) {
+			CHECK_FCT( fd_event_send(peer->p_events, FDEVP_TERMINATE, 0, NULL) );
+		}
+	}
+	
+	return 0;
+}
+
+/* Set timeout timer of next event */
+void fd_psm_next_timeout(struct fd_peer * peer, int add_random, int delay)
+{
+	TRACE_DEBUG(FULL, "Peer timeout reset to %d seconds%s", delay, add_random ? " (+/- 2)" : "" );
+	
+	/* Initialize the timer */
+	CHECK_POSIX_DO(  clock_gettime( CLOCK_REALTIME,  &peer->p_psm_timer ), ASSERT(0) );
+	
+	if (add_random) {
+		if (delay > 2)
+			delay -= 2;
+		else
+			delay = 0;
+
+		/* Add a random value between 0 and 4sec */
+		peer->p_psm_timer.tv_sec += random() % 4;
+		peer->p_psm_timer.tv_nsec+= random() % 1000000000L;
+		if (peer->p_psm_timer.tv_nsec >= 1000000000L) {
+			peer->p_psm_timer.tv_nsec -= 1000000000L;
+			peer->p_psm_timer.tv_sec ++;
+		}
+	}
+	
+	peer->p_psm_timer.tv_sec += delay;
+	
+#ifdef SLOW_PSM
+	/* temporary for debug */
+	peer->p_psm_timer.tv_sec += 10;
+#endif
+}
+
+/* Cleanup the peer */
+void fd_psm_cleanup(struct fd_peer * peer, int terminate)
+{
+	/* Move to CLOSED state: failover messages, stop OUT thread, unlink peer from active list */
+	if (fd_peer_getstate(peer) != STATE_ZOMBIE) {
+		CHECK_FCT_DO( fd_psm_change_state(peer, STATE_CLOSED), /* continue */ );
+	}
+	
+	fd_p_cnx_abort(peer, terminate);
+	
+	fd_p_ce_clear_cnx(peer, NULL);
+	
+	if (peer->p_receiver) {
+		fd_cnx_destroy(peer->p_receiver);
+		peer->p_receiver = NULL;
+	}
+	
+	if (terminate) {
+		fd_psm_events_free(peer);
+		CHECK_FCT_DO( fd_fifo_del(&peer->p_events), /* continue */ );
+	}
+	
+}
+
+
+/************************************************************************/
+/*                      The PSM thread                                  */
+/************************************************************************/
+/* Cancelation cleanup : set ZOMBIE state in the peer */
+void cleanup_setstate(void * arg) 
+{
+	struct fd_peer * peer = (struct fd_peer *)arg;
+	CHECK_PARAMS_DO( CHECK_PEER(peer), return );
+	CHECK_POSIX_DO( pthread_mutex_lock(&peer->p_state_mtx), );
+	peer->p_state = STATE_ZOMBIE;
+	CHECK_POSIX_DO( pthread_mutex_unlock(&peer->p_state_mtx), );
+	return;
+}
+
+/* The state machine thread (controler) */
+static void * p_psm_th( void * arg )
+{
+	struct fd_peer * peer = (struct fd_peer *)arg;
+	int created_started = started ? 1 : 0;
+	int event;
+	size_t ev_sz;
+	void * ev_data;
+	int cur_state;
+	
+	CHECK_PARAMS_DO( CHECK_PEER(peer), ASSERT(0) );
+	
+	pthread_cleanup_push( cleanup_setstate, arg );
+	
+	/* Set the thread name */
+	{
+		char buf[48];
+		snprintf(buf, sizeof(buf), "PSM/%s", peer->p_hdr.info.pi_diamid);
+		fd_log_threadname ( buf );
+	}
+	
+	/* The state machine starts in CLOSED state */
+	CHECK_POSIX_DO( pthread_mutex_lock(&peer->p_state_mtx), goto psm_end );
+	peer->p_state = STATE_CLOSED;
+	CHECK_POSIX_DO( pthread_mutex_unlock(&peer->p_state_mtx), goto psm_end );
+
+	/* Wait that the PSM are authorized to start in the daemon */
+	CHECK_FCT_DO( fd_psm_waitstart(), goto psm_end );
+	
+	/* Initialize the timer */
+	if (peer->p_flags.pf_responder) {
+		fd_psm_next_timeout(peer, 0, INCNX_TIMEOUT);
+	} else {
+		fd_psm_next_timeout(peer, created_started, 0);
+	}
+	
+psm_loop:
+	/* Get next event */
+	TRACE_DEBUG(FULL, "'%s' in state '%s' waiting for next event.",
+			peer->p_hdr.info.pi_diamid, STATE_STR(fd_peer_getstate(peer)));
+	CHECK_FCT_DO( fd_event_timedget(peer->p_events, &peer->p_psm_timer, FDEVP_PSM_TIMEOUT, &event, &ev_sz, &ev_data), goto psm_end );
+	
+	cur_state = fd_peer_getstate(peer);
+	if (cur_state == -1)
+		goto psm_end;
+	
+	TRACE_DEBUG(FULL, "'%s'\t<-- '%s'\t(%p,%zd)\t'%s'",
+			STATE_STR(cur_state),
+			fd_pev_str(event), ev_data, ev_sz,
+			peer->p_hdr.info.pi_diamid);
+
+	/* Now, the action depends on the current state and the incoming event */
+
+	/* The following states are impossible */
+	ASSERT( cur_state != STATE_NEW );
+	ASSERT( cur_state != STATE_ZOMBIE );
+	ASSERT( cur_state != STATE_OPEN_HANDSHAKE ); /* because it should exist only between two loops */
+
+	/* Purge invalid events */
+	if (!CHECK_PEVENT(event)) {
+		TRACE_DEBUG(INFO, "Invalid event received in PSM '%s' : %d", peer->p_hdr.info.pi_diamid, event);
+		ASSERT(0); /* we should investigate this situation */
+		goto psm_loop;
+	}
+
+	/* Requests to terminate the peer object */
+	if (event == FDEVP_TERMINATE) {
+		switch (cur_state) {
+			case STATE_OPEN:
+			case STATE_OPEN_NEW:
+			case STATE_REOPEN:
+				/* We cannot just close the connection, we have to send a DPR first */
+				CHECK_FCT_DO( fd_p_dp_initiate(peer, ev_data), goto psm_end );
+				goto psm_loop;
+			
+			/*	
+			case STATE_CLOSING:
+			case STATE_CLOSING_GRACE:
+			case STATE_WAITCNXACK:
+			case STATE_WAITCNXACK_ELEC:
+			case STATE_WAITCEA:
+			case STATE_SUSPECT:
+			case STATE_CLOSED:
+			*/
+			default:
+				/* In these cases, we just cleanup the peer object (if needed) and terminate */
+				goto psm_end;
+		}
+	}
+	
+	/* A message was received */
+	if (event == FDEVP_CNX_MSG_RECV) {
+		struct msg * msg = NULL;
+		struct msg_hdr * hdr;
+		struct fd_cnx_rcvdata rcv_data;
+		struct fd_msg_pmdl * pmdl = NULL;
+		
+		rcv_data.buffer = ev_data;
+		rcv_data.length = ev_sz;
+		pmdl = fd_msg_pmdl_get_inbuf(rcv_data.buffer, rcv_data.length);
+		
+		/* Parse the received buffer */
+		CHECK_FCT_DO( fd_msg_parse_buffer( (void *)&ev_data, ev_sz, &msg), 
+			{
+				fd_hook_call(HOOK_MESSAGE_PARSING_ERROR, NULL, peer, &rcv_data, pmdl );
+				free(ev_data);
+				CHECK_FCT_DO( fd_event_send(peer->p_events, FDEVP_CNX_ERROR, 0, NULL), goto psm_reset );
+				goto psm_loop;
+			} );
+			
+		fd_hook_associate(msg, pmdl);
+		CHECK_FCT_DO( fd_msg_source_set( msg, peer->p_hdr.info.pi_diamid, peer->p_hdr.info.pi_diamidlen), goto psm_end);
+	
+		/* If the current state does not allow receiving messages, just drop it */
+		if (cur_state == STATE_CLOSED) {
+			/* In such case, just discard the message */
+			fd_hook_call(HOOK_MESSAGE_DROPPED, msg, peer, "Message purged from queue, peer in CLOSED state", fd_msg_pmdl_get(msg));
+			fd_msg_free(msg);
+			goto psm_loop;
+		}
+		
+		/* Extract the header */
+		CHECK_FCT_DO( fd_msg_hdr(msg, &hdr), goto psm_end );
+		
+		/* If it is an answer, associate with the request or drop */
+		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_hook_call(HOOK_MESSAGE_DROPPED, msg, peer, "Answer received with no corresponding sent request.", fd_msg_pmdl_get(msg));
+				fd_msg_free(msg);
+				goto psm_loop;
+			}
+			
+			/* Associate */
+			CHECK_FCT_DO( fd_msg_answ_associate( msg, req ), goto psm_end );
+			
+		}
+		
+		/* Log incoming message */
+		fd_hook_call(HOOK_MESSAGE_RECEIVED, msg, peer, NULL, fd_msg_pmdl_get(msg));
+		
+		if (cur_state == STATE_OPEN_NEW) {
+			/* OK, we have received something, so the connection is supposedly now in OPEN state at the remote site */
+			fd_psm_change_state(peer, STATE_OPEN );
+		}
+		
+		/* Now handle non-link-local messages */
+		if (fd_msg_is_routable(msg)) {
+			switch (cur_state) {
+				/* To maximize compatibility -- should not be a security issue here */
+				case STATE_REOPEN:
+				case STATE_SUSPECT:
+				case STATE_CLOSING:
+				case STATE_CLOSING_GRACE:
+					TRACE_DEBUG(FULL, "Accepted a message while not in OPEN state... ");
+				/* The standard situation : */
+				case STATE_OPEN_NEW:
+				case STATE_OPEN:
+					/* We received a valid routable message, update the expiry timer */
+					CHECK_FCT_DO( fd_p_expi_update(peer), goto psm_end );
+
+					/* Set the message source and add the Route-Record */
+					CHECK_FCT_DO( fd_msg_source_setrr( msg, peer->p_hdr.info.pi_diamid, peer->p_hdr.info.pi_diamidlen, fd_g_config->cnf_dict ), goto psm_end);
+
+					if ((hdr->msg_flags & CMD_FLAG_REQUEST)) {
+						/* Mark the incoming request so that we know we have pending answers for this peer */
+						CHECK_POSIX_DO( pthread_mutex_lock(&peer->p_state_mtx), goto psm_end  );
+						peer->p_reqin_count++;
+						CHECK_POSIX_DO( pthread_mutex_unlock(&peer->p_state_mtx), goto psm_end  );
+					}
+						
+					/* Requeue to the global incoming queue */
+					CHECK_FCT_DO(fd_fifo_post(fd_g_incoming, &msg), goto psm_end );
+
+					/* Update the peer timer (only in OPEN state) */
+					if ((cur_state == STATE_OPEN) && (!peer->p_flags.pf_dw_pending)) {
+						fd_psm_next_timeout(peer, 1, peer->p_hdr.info.config.pic_twtimer ?: fd_g_config->cnf_timer_tw);
+					}
+					break;
+					
+				/* In other states, we discard the message, it is either old or invalid to send it for the remote peer */
+				case STATE_WAITCNXACK:
+				case STATE_WAITCNXACK_ELEC:
+				case STATE_WAITCEA:
+				case STATE_CLOSED:
+				default: {
+					/* 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));
+					fd_hook_call(HOOK_MESSAGE_DROPPED, msg, peer, buf, fd_msg_pmdl_get(msg));
+					fd_msg_free(msg);
+				}
+			}
+			goto psm_loop;
+		}
+		
+		/* Link-local message: They must be understood by our dictionary, otherwise we return an error */
+		{
+			struct msg * error = NULL;
+			int ret = fd_msg_parse_or_error( &msg, &error );
+			if (ret != EBADMSG) {
+				CHECK_FCT_DO( ret, 
+					{
+						char buf[256];
+						snprintf(buf, sizeof(buf), "%s: An unexpected error occurred while parsing a link-local message", peer->p_hdr.info.pi_diamid); 
+						fd_hook_call(HOOK_MESSAGE_DROPPED, msg, peer, buf, fd_msg_pmdl_get(msg));
+						fd_msg_free(msg); 
+						goto psm_end; 
+					} );
+			} else {
+				if (msg == NULL) {
+					/* Send the error back to the peer */
+					CHECK_FCT_DO( ret = fd_out_send(&error, NULL, peer, 0),  );
+					if (error) {
+						char buf[256];
+						/* Only if an error occurred & the message was not saved / dumped */
+						snprintf(buf, sizeof(buf), "%s: error sending a message", peer->p_hdr.info.pi_diamid); 
+						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 */
+					snprintf(buf, sizeof(buf), "%s: Received invalid answer to Base protocol message, disconnecting...", peer->p_hdr.info.pi_diamid);
+					fd_hook_call(HOOK_MESSAGE_DROPPED, msg, peer, buf, fd_msg_pmdl_get(msg));
+					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 );
+				}
+				goto psm_loop;
+			}
+		}
+		
+		/* Handle the LL message and update the expiry timer appropriately */
+		switch (hdr->msg_code) {
+			case CC_CAPABILITIES_EXCHANGE:
+				CHECK_FCT_DO( fd_p_ce_msgrcv(&msg, (hdr->msg_flags & CMD_FLAG_REQUEST), peer), 
+					{
+						if (msg)
+							CHECK_FCT_DO( fd_msg_free(msg), );
+						goto psm_reset;
+					} );
+				break;
+			
+			case CC_DISCONNECT_PEER:
+				CHECK_FCT_DO( fd_p_dp_handle(&msg, (hdr->msg_flags & CMD_FLAG_REQUEST), peer), goto psm_reset );
+				if (fd_peer_getstate(peer) == STATE_CLOSING)
+					goto psm_end;
+
+				break;
+			
+			case CC_DEVICE_WATCHDOG:
+				CHECK_FCT_DO( fd_p_dw_handle(&msg, (hdr->msg_flags & CMD_FLAG_REQUEST), peer), goto psm_reset );
+				break;
+			
+			default:
+				/* Unknown / unexpected / invalid message -- but validated by our dictionary */
+				TRACE_DEBUG(INFO, "Invalid non-routable command received: %u.", hdr->msg_code);
+				if (hdr->msg_flags & CMD_FLAG_REQUEST) {
+					do {
+						/* Reply with an error code */
+						CHECK_FCT_DO( fd_msg_new_answer_from_req ( fd_g_config->cnf_dict, &msg, MSGFL_ANSW_ERROR ), break );
+
+						/* Set the error code */
+						CHECK_FCT_DO( fd_msg_rescode_set(msg, "DIAMETER_COMMAND_UNSUPPORTED", "Or maybe the P-bit or application Id are erroneous.", NULL, 1 ), break );
+
+						/* Send the answer */
+						CHECK_FCT_DO( fd_out_send(&msg, peer->p_cnxctx, peer, 0), break );
+					} while (0);
+				} else {
+					/* We did ASK for it ??? */
+					TRACE_DEBUG(INFO, "Received answer with erroneous 'is_routable' result...");
+				}
+				
+				/* Cleanup the message if not done */
+				if (msg) {
+					char buf[256];
+					snprintf(buf, sizeof(buf), "Received un-handled non-routable command from peer '%s'.", peer->p_hdr.info.pi_diamid);
+					fd_hook_call(HOOK_MESSAGE_DROPPED, msg, NULL, buf, fd_msg_pmdl_get(msg));
+					CHECK_FCT_DO( fd_msg_free(msg), /* continue */);
+					msg = NULL;
+				}
+		};
+		
+		/* At this point the message must have been fully handled already */
+		if (msg) {
+			char buf[256];
+			snprintf(buf, sizeof(buf), "Internal error ('%s'): unhandled message.", peer->p_hdr.info.pi_diamid);
+			fd_hook_call(HOOK_MESSAGE_DROPPED, msg, NULL, buf, fd_msg_pmdl_get(msg));
+			fd_msg_free(msg);
+		}
+		
+		goto psm_loop;
+	}
+	
+	/* The connection object is broken */
+	if (event == FDEVP_CNX_ERROR) {
+		switch (cur_state) {
+			case STATE_WAITCNXACK_ELEC:
+				/* Abort the initiating side */
+				fd_p_cnx_abort(peer, 0);
+				/* Process the receiver side */
+				CHECK_FCT_DO( fd_p_ce_process_receiver(peer), goto psm_end );
+				break;
+			
+			case STATE_WAITCEA:
+			case STATE_OPEN:
+			case STATE_OPEN_NEW:
+			case STATE_REOPEN:
+			case STATE_WAITCNXACK:
+			case STATE_SUSPECT:
+			default:
+				/* Mark the connection problem */
+				peer->p_flags.pf_cnx_pb = 1;
+			
+				fd_hook_call(HOOK_PEER_CONNECT_FAILED, NULL, peer, "The connection was broken", NULL);
+				
+				/* Destroy the connection, restart the timer to a new connection attempt */
+				fd_psm_next_timeout(peer, 1, peer->p_hdr.info.config.pic_tctimer ?: fd_g_config->cnf_timer_tc);
+				
+			case STATE_CLOSED:
+				goto psm_reset;
+				
+			case STATE_CLOSING:
+				/* We sent a DPR so we are terminating, do not wait for DPA */
+				goto psm_end;
+				
+			case STATE_CLOSING_GRACE:
+				if (peer->p_flags.pf_localterm) /* initiated here */
+					goto psm_end;
+				
+				fd_psm_cleanup(peer, 0);
+				
+				/* Reset the timer for next connection attempt */
+				fd_psm_next_timeout(peer, 1, fd_p_dp_newdelay(peer));
+				goto psm_loop;
+		}
+		goto psm_loop;
+	}
+	
+	/* The connection notified a change in endpoints */
+	if (event == FDEVP_CNX_EP_CHANGE) {
+		/* We actually don't care if we are in OPEN state here... */
+		
+		/* Cleanup the remote LL and primary addresses */
+		CHECK_FCT_DO( fd_ep_filter( &peer->p_hdr.info.pi_endpoints, EP_FL_CONF | EP_FL_DISC | EP_FL_ADV ), /* ignore the error */);
+		CHECK_FCT_DO( fd_ep_clearflags( &peer->p_hdr.info.pi_endpoints, EP_FL_PRIMARY ), /* ignore the error */);
+		
+		/* Get the new ones */
+		CHECK_FCT_DO( fd_cnx_getremoteeps(peer->p_cnxctx, &peer->p_hdr.info.pi_endpoints), /* ignore the error */);
+		
+		/* We do not support local endpoints change currently, but it could be added here if needed (refresh fd_g_config->cnf_endpoints) */
+		{
+			char * buf = NULL;
+			size_t len = 0;
+			LOG_D("Got low layer notification (IGNORED): remote endpoint(s) changed: %s",  fd_ep_dump(&buf, &len, NULL, 0, 0, &peer->p_hdr.info.pi_endpoints) ?: "error");
+			free(buf);
+		}
+		
+		/* Done */
+		goto psm_loop;
+	}
+	
+	/* A new connection was established and CER containing this peer id was received */
+	if (event == FDEVP_CNX_INCOMING) {
+		struct cnx_incoming * params = ev_data;
+		ASSERT(params);
+		
+		/* Handle the message */
+		CHECK_FCT_DO( fd_p_ce_handle_newCER(&params->cer, peer, &params->cnx, params->validate), goto psm_end );
+		
+		/* Cleanup if needed */
+		if (params->cnx) {
+			fd_cnx_destroy(params->cnx);
+			params->cnx = NULL;
+		}
+		if (params->cer) {
+			CHECK_FCT_DO( fd_msg_free(params->cer), );
+			params->cer = NULL;
+		}
+		
+		/* Loop */
+		free(ev_data);
+		goto psm_loop;
+	}
+	
+	/* A new connection has been established with the remote peer */
+	if (event == FDEVP_CNX_ESTABLISHED) {
+		struct cnxctx * cnx = ev_data;
+		
+		/* Release the resources of the connecting thread */
+		CHECK_POSIX_DO( pthread_join( peer->p_ini_thr, NULL), /* ignore, it is not a big deal */);
+		peer->p_ini_thr = (pthread_t)NULL;
+		
+		switch (cur_state) {
+			case STATE_WAITCNXACK_ELEC:
+			case STATE_WAITCNXACK:
+				LOG_D("%s: Connection established, %s", peer->p_hdr.info.pi_diamid, fd_cnx_getid(cnx));
+				fd_p_ce_handle_newcnx(peer, cnx);
+				break;
+				
+			default:
+				/* Just abort the attempt and continue */
+				TRACE_DEBUG(FULL, "Connection attempt successful but current state is %s, closing... (too slow?)", STATE_STR(cur_state));
+				fd_cnx_destroy(cnx);
+		}
+		
+		goto psm_loop;
+	}
+	
+	/* A new connection has not been established with the remote peer */
+	if (event == FDEVP_CNX_FAILED) {
+		
+		/* Release the resources of the connecting thread */
+		CHECK_POSIX_DO( pthread_join( peer->p_ini_thr, NULL), /* ignore, it is not a big deal */);
+		peer->p_ini_thr = (pthread_t)NULL;
+		
+		switch (cur_state) {
+			case STATE_WAITCNXACK_ELEC:
+				/* Abort the initiating side */
+				fd_p_cnx_abort(peer, 0);
+				/* Process the receiver side */
+				CHECK_FCT_DO( fd_p_ce_process_receiver(peer), goto psm_end );
+				break;
+				
+			case STATE_WAITCNXACK:
+				/* Go back to CLOSE */
+				fd_psm_next_timeout(peer, 1, peer->p_hdr.info.config.pic_tctimer ?: fd_g_config->cnf_timer_tc);
+				goto psm_reset;
+				
+			default:
+				/* Just ignore */
+				TRACE_DEBUG(FULL, "Connection attempt failed but current state is %s, ignoring...", STATE_STR(cur_state));
+		}
+		
+		goto psm_loop;
+	}
+	
+	/* The timeout for the current state has been reached */
+	if (event == FDEVP_PSM_TIMEOUT) {
+		switch (cur_state) {
+			case STATE_OPEN:
+			case STATE_REOPEN:
+			case STATE_OPEN_NEW:
+				CHECK_FCT_DO( fd_p_dw_timeout(peer), goto psm_end );
+				goto psm_loop;
+				
+			case STATE_CLOSED:
+				LOG_D("%s: Connecting...", peer->p_hdr.info.pi_diamid);
+				CHECK_FCT_DO( fd_psm_change_state(peer, STATE_WAITCNXACK), goto psm_end );
+				fd_psm_next_timeout(peer, 0, CNX_TIMEOUT);
+				CHECK_FCT_DO( fd_p_cnx_init(peer), goto psm_end );
+				goto psm_loop;
+				
+			case STATE_SUSPECT:
+				/* Mark the connection problem */
+				peer->p_flags.pf_cnx_pb = 1;
+			case STATE_WAITCNXACK:
+			case STATE_WAITCEA:
+				fd_hook_call(HOOK_PEER_CONNECT_FAILED, NULL, peer, "Timeout while waiting for remote peer", NULL);
+			case STATE_CLOSING:
+				/* Destroy the connection, restart the timer to a new connection attempt */
+				fd_psm_next_timeout(peer, 1, peer->p_hdr.info.config.pic_tctimer ?: fd_g_config->cnf_timer_tc);
+				goto psm_reset;
+				
+			case STATE_CLOSING_GRACE:
+				/* The grace period is completed, now close */
+				if (peer->p_flags.pf_localterm)
+					goto psm_end;
+				
+				fd_psm_cleanup(peer, 0);
+				/* Reset the timer for next connection attempt */
+				fd_psm_next_timeout(peer, 1, fd_p_dp_newdelay(peer));
+				goto psm_loop;
+				
+			case STATE_WAITCNXACK_ELEC:
+				/* Abort the initiating side */
+				fd_p_cnx_abort(peer, 0);
+				/* Process the receiver side */
+				CHECK_FCT_DO( fd_p_ce_process_receiver(peer), goto psm_end );
+				goto psm_loop;
+			
+			default:
+				ASSERT(0); /* implementation problem, we did not foresee this case? */
+		}
+	}
+	
+	/* Default action : the handling has not yet been implemented. [for debug only] */
+	TRACE_DEBUG(INFO, "Missing handler in PSM for '%s'\t<-- '%s'", STATE_STR(cur_state), fd_pev_str(event));
+psm_reset:
+	if (peer->p_flags.pf_delete)
+		goto psm_end;
+	fd_psm_cleanup(peer, 0);
+	goto psm_loop;
+	
+psm_end:
+	LOG_E("%s: Going to ZOMBIE state (no more activity)", peer->p_hdr.info.pi_diamid);
+	fd_psm_cleanup(peer, 1);
+	TRACE_DEBUG(INFO, "'%s'\t-> 'STATE_ZOMBIE' (terminated)\t'%s'",
+			STATE_STR(fd_peer_getstate(peer)),
+			peer->p_hdr.info.pi_diamid);
+	pthread_cleanup_pop(1); /* set STATE_ZOMBIE */
+	peer->p_psm = (pthread_t)NULL;
+	pthread_detach(pthread_self());
+	return NULL;
+}
+
+
+/************************************************************************/
+/*                      Functions to control the PSM                    */
+/************************************************************************/
+/* Create the PSM thread of one peer structure */
+int fd_psm_begin(struct fd_peer * peer )
+{
+	TRACE_ENTRY("%p", peer);
+	
+	/* Check the peer and state are OK */
+	CHECK_PARAMS( fd_peer_getstate(peer) == STATE_NEW );
+	
+	/* Create the FIFO for events */
+	CHECK_FCT( fd_fifo_new(&peer->p_events, 0) );
+	
+	/* Create the PSM controler thread */
+	CHECK_POSIX( pthread_create( &peer->p_psm, NULL, p_psm_th, peer ) );
+	
+	/* We're done */
+	return 0;
+}
+
+/* End the PSM (clean ending) */
+int fd_psm_terminate(struct fd_peer * peer, char * reason )
+{
+	TRACE_ENTRY("%p", peer);
+	CHECK_PARAMS( CHECK_PEER(peer) );
+	
+	if (fd_peer_getstate(peer) != STATE_ZOMBIE) {
+		CHECK_FCT( fd_event_send(peer->p_events, FDEVP_TERMINATE, 0, reason) );
+	} else {
+		TRACE_DEBUG(FULL, "Peer '%s' was already terminated", peer->p_hdr.info.pi_diamid);
+	}
+	return 0;
+}
+
+/* End the PSM & cleanup the peer structure */
+void fd_psm_abord(struct fd_peer * peer )
+{
+	TRACE_ENTRY("%p", peer);
+	
+	/* Cancel PSM thread */
+	CHECK_FCT_DO( fd_thr_term(&peer->p_psm), /* continue */ );
+	
+	/* Cleanup the data */
+	fd_psm_cleanup(peer, 1);
+	
+	/* Destroy the event list */
+	CHECK_FCT_DO( fd_fifo_del(&peer->p_events), /* continue */ );
+	
+	/* Remaining cleanups are performed in fd_peer_free */
+	return;
+}
+
diff --git a/libfdcore/p_sr.c b/libfdcore/p_sr.c
new file mode 100644
index 0000000..36b5a55
--- /dev/null
+++ b/libfdcore/p_sr.c
@@ -0,0 +1,350 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@freediameter.net>							 *
+*													 *
+* Copyright (c) 2013, 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 "fdcore-internal.h"
+
+/* Structure to store a sent request */
+struct sentreq {
+	struct fd_list	chain; 	/* the "o" field points directly to the (new) hop-by-hop of the request (uint32_t *)  */
+	struct msg	*req;	/* A request that was sent and not yet answered. */
+	uint32_t	prevhbh;/* The value to set back in the hbh header when the message is retrieved */
+	struct fd_list  expire; /* the list of expiring requests */
+	struct timespec timeout; /* Cache the expire date of the request so that the timeout thread does not need to get it each time. */
+	struct timespec added_on; /* the time the request was added */
+};
+
+/* Find an element in the hbh 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;
+}
+
+/* Similar but start from the end, since we add requests in growing hbh order usually */
+static struct fd_list * find_or_prev(struct fd_list * srlist, uint32_t hbh, int * match)
+{
+	struct fd_list * li;
+	*match = 0;
+	for (li = srlist->prev; li != srlist; li = li->prev) {
+		uint32_t * prevhbh = li->o;
+		if (*prevhbh > hbh)
+			continue;
+		if (*prevhbh == hbh)
+			*match = 1;
+		break;
+	}
+	return li;
+}
+
+static void srl_dump(const char * text, struct fd_list * srlist)
+{
+	struct fd_list * li;
+	struct timespec now;
+	
+	LOG_D("%sSentReq list @%p:", text, srlist);
+	
+	CHECK_SYS_DO( clock_gettime(CLOCK_REALTIME, &now), );
+	
+	for (li = srlist->next; li != srlist; li = li->next) {
+		struct sentreq * sr = (struct sentreq *)li;
+		uint32_t * nexthbh = li->o;
+		
+		LOG_D(" - Next req (hbh:0x%x, prev:0x%x): [since %ld.%06ld sec]", *nexthbh, sr->prevhbh,
+			(long)((now.tv_nsec >= sr->added_on.tv_nsec) ? (now.tv_sec - sr->added_on.tv_sec) : (now.tv_sec - sr->added_on.tv_sec - 1)),
+			(long)((now.tv_nsec >= sr->added_on.tv_nsec) ? ((now.tv_nsec - sr->added_on.tv_nsec) / 1000) : ((now.tv_nsec - sr->added_on.tv_nsec + 1000000000) / 1000)));
+	}
+}
+
+/* thread that handles messages expiring. The thread is started only when needed */
+static void * sr_expiry_th(void * arg) {
+	struct sr_list * srlist = arg;
+	
+	TRACE_ENTRY("%p", arg);
+	CHECK_PARAMS_DO( arg, return NULL );
+	
+	/* Set the thread name */
+	{
+		char buf[48];
+		snprintf(buf, sizeof(buf), "ReqExp/%s", ((struct fd_peer *)(srlist->exp.o))->p_hdr.info.pi_diamid);
+		fd_log_threadname ( buf );
+	}
+	
+	do {
+		struct timespec	now;
+		struct sentreq * first;
+		struct msg * request;
+		struct fd_peer * sentto;
+		void (*expirecb)(void *, DiamId_t, size_t, struct msg **);
+		void * data;
+		int no_error;
+
+		CHECK_POSIX_DO( pthread_mutex_lock(&srlist->mtx),  return NULL );
+		pthread_cleanup_push( fd_cleanup_mutex, &srlist->mtx );
+
+loop:	
+		no_error = 0;
+
+		/* Check if there are expiring requests available */
+		if (FD_IS_LIST_EMPTY(&srlist->exp)) {
+			/* Just wait for a change or cancelation */
+			CHECK_POSIX_DO( pthread_cond_wait( &srlist->cnd, &srlist->mtx ), goto unlock );
+			/* Restart the loop on wakeup */
+			goto loop;
+		}
+		
+		/* Get the pointer to the request that expires first */
+		first = (struct sentreq *)(srlist->exp.next->o);
+		
+		/* Get the current time */
+		CHECK_SYS_DO(  clock_gettime(CLOCK_REALTIME, &now),  goto unlock  );
+
+		/* If first request is not expired, we just wait until it happens */
+		if ( TS_IS_INFERIOR( &now, &first->timeout ) ) {
+			
+			CHECK_POSIX_DO2(  pthread_cond_timedwait( &srlist->cnd, &srlist->mtx, &first->timeout ),  
+					ETIMEDOUT, /* ETIMEDOUT is a normal return value, continue */,
+					/* on other error, */ goto unlock );
+	
+			/* on wakeup, loop */
+			goto loop;
+		}
+		
+		/* Now, the first request in the list is expired; remove it and call the expirecb for it */
+		request = first->req;
+		sentto = first->chain.head->o;
+		
+		TRACE_DEBUG(FULL, "Request %x was not answered by %s within the timer delay", *((uint32_t *)first->chain.o), sentto->p_hdr.info.pi_diamid);
+		
+		/* Restore the hbhid */
+		*((uint32_t *)first->chain.o) = first->prevhbh; 
+		
+		/* Free the sentreq information */
+		fd_list_unlink(&first->chain);
+		srlist->cnt--;
+		srlist->cnt_lost++; /* We are not waiting for this answer anymore, but the remote peer may still be processing it. */
+		fd_list_unlink(&first->expire);
+		free(first);
+		
+		no_error = 1;
+unlock:
+		; /* pthread_cleanup_pop sometimes expands as "} ..." and the label before this cause some compilers to complain... */
+		pthread_cleanup_pop( 1 ); /* unlock the mutex */
+		if (!no_error)
+			break;
+
+		
+		/* Retrieve callback in the message */
+		CHECK_FCT_DO( fd_msg_anscb_get( request, NULL, &expirecb, &data ), break);
+		ASSERT(expirecb);
+	
+		/* Clean up this expirecb from the message */
+		CHECK_FCT_DO( fd_msg_anscb_reset( request, 0, 1 ), break);
+
+		/* Call it */
+		(*expirecb)(data, sentto->p_hdr.info.pi_diamid, sentto->p_hdr.info.pi_diamidlen, &request);
+	
+		/* If the callback did not dispose of the message, do it now */
+		if (request) {
+			fd_hook_call(HOOK_MESSAGE_DROPPED, request, NULL, "Expiration period completed without an answer, and the expiry callback did not dispose of the message.", fd_msg_pmdl_get(request));
+			CHECK_FCT_DO( fd_msg_free(request), /* ignore */ );
+		}
+	
+	} while (1);
+	
+	ASSERT(0); /* we have encountered a problem, maybe time to signal the framework to terminate? */
+	return NULL;
+}
+
+
+/* Store a new sent request */
+int fd_p_sr_store(struct sr_list * srlist, struct msg **req, uint32_t *hbhloc, uint32_t hbh_restore)
+{
+	struct sentreq * sr;
+	struct fd_list * prev;
+	int match;
+	struct timespec * ts;
+	
+	TRACE_ENTRY("%p %p %p %x", srlist, req, hbhloc, hbh_restore);
+	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;
+	sr->prevhbh = hbh_restore;
+	fd_list_init(&sr->expire, sr);
+	CHECK_SYS( clock_gettime(CLOCK_REALTIME, &sr->added_on) );
+	
+	/* Search the place in the list */
+	CHECK_POSIX( pthread_mutex_lock(&srlist->mtx) );
+	prev = find_or_prev(&srlist->srs, *hbhloc, &match);
+	if (match) {
+		TRACE_DEBUG(INFO, "A request with the same hop-by-hop Id (0x%x) was already sent: error", *hbhloc);
+		free(sr);
+		srl_dump("Current list of SR: ", &srlist->srs);
+		CHECK_POSIX_DO( pthread_mutex_unlock(&srlist->mtx), /* ignore */ );
+		return EINVAL;
+	}
+	
+	/* Save in the list */
+	*req = NULL;
+	fd_list_insert_after(prev, &sr->chain);
+	srlist->cnt++;
+	
+	/* In case of request with a timeout, also store in the timeout list */
+	ts = fd_msg_anscb_gettimeout( sr->req );
+	if (ts) {
+		struct fd_list * li;
+		
+		memcpy(&sr->timeout, ts, sizeof(struct timespec));
+		
+		/* browse srlist->exp from the end */
+		for (li = srlist->exp.prev; li != &srlist->exp; li = li->prev) {
+			struct sentreq * s = (struct sentreq *)(li->o);
+			if (TS_IS_INFERIOR(&s->timeout, ts))
+				break;
+		}
+		
+		fd_list_insert_after(li, &sr->expire);
+	
+		/* if the thread does not exist yet, create it */
+		if (srlist->thr == (pthread_t)NULL) {
+			CHECK_POSIX_DO( pthread_create(&srlist->thr, NULL, sr_expiry_th, srlist), /* continue anyway */);
+		} else {
+			/* or, if added in first position, signal the condvar to update the sleep time of the thread */
+			if (li == &srlist->exp) {
+				CHECK_POSIX_DO( pthread_cond_signal(&srlist->cnd), /* continue anyway */);
+			}
+		}
+	}
+	
+	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 (%x)", hbh);
+		srl_dump("Current list of SR: ", &srlist->srs);
+		*req = NULL;
+		if (srlist->cnt_lost > 0) {
+			srlist->cnt_lost--; /* This is probably an answer for a request we already timedout. */
+		} /* else, probably a bug in the remote peer */
+	} else {
+		/* Restore hop-by-hop id */
+		*((uint32_t *)sr->chain.o) = sr->prevhbh;
+		/* Unlink */
+		fd_list_unlink(&sr->chain);
+		srlist->cnt--;
+		fd_list_unlink(&sr->expire);
+		*req = sr->req;
+		free(sr);
+	}
+	CHECK_POSIX( pthread_mutex_unlock(&srlist->mtx) );
+	
+	/* do not stop the expire thread here, it might cause creating/destroying it very often otherwise */
+
+	/* 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);
+		srlist->cnt--;
+		fd_list_unlink(&sr->expire);
+		if (fd_msg_is_routable(sr->req)) {
+			struct msg_hdr * hdr = NULL;
+			int ret;
+			
+			/* Set the 'T' flag */
+			CHECK_FCT_DO(fd_msg_hdr(sr->req, &hdr), /* continue */);
+			if (hdr)
+				hdr->msg_flags |= CMD_FLAG_RETRANSMIT;
+			
+			/* Restore the original hop-by-hop id of the request */
+			*((uint32_t *)sr->chain.o) = sr->prevhbh;
+			
+			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_noblock(fd_g_outgoing, (void *)&sr->req),
+				{
+					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_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);
+	}
+	/* The list of expiring requests must be empty now */
+	ASSERT( FD_IS_LIST_EMPTY(&srlist->exp) );
+	ASSERT( srlist->cnt == 0 ); /* debug the counter management if needed */
+	
+	CHECK_POSIX_DO( pthread_mutex_unlock(&srlist->mtx), /* continue anyway */ );
+	
+	/* Terminate the expiry thread (must be done when the lock can be taken) */
+	CHECK_FCT_DO( fd_thr_term(&srlist->thr), /* ignore error */ );
+}
+
diff --git a/libfdcore/peers.c b/libfdcore/peers.c
new file mode 100644
index 0000000..98f1921
--- /dev/null
+++ b/libfdcore/peers.c
@@ -0,0 +1,693 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@freediameter.net>							 *
+*													 *
+* Copyright (c) 2013, 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 "fdcore-internal.h"
+
+/* Global list of peers */
+struct fd_list   fd_g_peers = FD_LIST_INITIALIZER(fd_g_peers);
+pthread_rwlock_t fd_g_peers_rw = PTHREAD_RWLOCK_INITIALIZER;
+
+/* List of active peers */
+struct fd_list   fd_g_activ_peers = FD_LIST_INITIALIZER(fd_g_activ_peers);	/* peers linked by their p_actives oredered by p_diamid */
+pthread_rwlock_t fd_g_activ_peers_rw = PTHREAD_RWLOCK_INITIALIZER;
+
+/* List of validation callbacks (registered with fd_peer_validate_register) */
+static struct fd_list validators = FD_LIST_INITIALIZER(validators);	/* list items are simple fd_list with "o" pointing to the callback */
+static pthread_rwlock_t validators_rw = PTHREAD_RWLOCK_INITIALIZER;
+
+
+/* Alloc / reinit a peer structure. if *ptr is not NULL, it must already point to a valid struct fd_peer. */
+int fd_peer_alloc(struct fd_peer ** ptr)
+{
+	struct fd_peer *p;
+	
+	TRACE_ENTRY("%p", ptr);
+	CHECK_PARAMS(ptr);
+	
+	if (*ptr) {
+		p = *ptr;
+	} else {
+		CHECK_MALLOC( p = malloc(sizeof(struct fd_peer)) );
+		*ptr = p;
+	}
+	
+	/* Now initialize the content */
+	memset(p, 0, sizeof(struct fd_peer));
+	
+	fd_list_init(&p->p_hdr.chain, p);
+	
+	fd_list_init(&p->p_hdr.info.pi_endpoints, p);
+	fd_list_init(&p->p_hdr.info.runtime.pir_apps, p);
+	
+	p->p_eyec = EYEC_PEER;
+	CHECK_POSIX( pthread_mutex_init(&p->p_state_mtx, NULL) );
+	
+	fd_list_init(&p->p_actives, p);
+	fd_list_init(&p->p_expiry, p);
+	CHECK_FCT( fd_fifo_new(&p->p_tosend, 5) );
+	CHECK_FCT( fd_fifo_new(&p->p_tofailover, 0) );
+	p->p_hbh = lrand48();
+	
+	fd_list_init(&p->p_sr.srs, p);
+	fd_list_init(&p->p_sr.exp, p);
+	CHECK_POSIX( pthread_mutex_init(&p->p_sr.mtx, NULL) );
+	CHECK_POSIX( pthread_cond_init(&p->p_sr.cnd, NULL) );
+	
+	fd_list_init(&p->p_connparams, p);
+	
+	return 0;
+}
+
+/* Add a new peer entry */
+int fd_peer_add ( struct peer_info * info, const char * orig_dbg, void (*cb)(struct peer_info *, void *), void * cb_data )
+{
+	struct fd_peer *p = NULL;
+	struct fd_list * li, *li_inf;
+	int ret = 0;
+	
+	TRACE_ENTRY("%p %p %p %p", info, orig_dbg, cb, cb_data);
+	CHECK_PARAMS(info && info->pi_diamid);
+	
+	if (info->config.pic_realm) {
+		if (!fd_os_is_valid_DiameterIdentity((os0_t)info->config.pic_realm, strlen(info->config.pic_realm))) {
+			TRACE_DEBUG(INFO, "'%s' is not a valid DiameterIdentity.", info->config.pic_realm);
+			return EINVAL;
+		}
+	}
+	
+	/* Create a structure to contain the new peer information */
+	CHECK_FCT( fd_peer_alloc(&p) );
+	
+	/* Copy the informations from the parameters received */
+	p->p_hdr.info.pi_diamid = info->pi_diamid;
+	CHECK_FCT( fd_os_validate_DiameterIdentity(&p->p_hdr.info.pi_diamid, &p->p_hdr.info.pi_diamidlen, 1) );
+	
+	memcpy( &p->p_hdr.info.config, &info->config, sizeof(p->p_hdr.info.config) );
+	
+	/* Duplicate the strings if provided */
+	if (info->config.pic_realm) {
+		CHECK_MALLOC( p->p_hdr.info.config.pic_realm = strdup(info->config.pic_realm) );
+	}
+	if (info->config.pic_priority) {
+		CHECK_MALLOC( p->p_hdr.info.config.pic_priority = strdup(info->config.pic_priority) );
+	}
+	
+	/* Move the list of endpoints into the peer */
+	if (info->pi_endpoints.next)
+		while (!FD_IS_LIST_EMPTY( &info->pi_endpoints ) ) {
+			li = info->pi_endpoints.next;
+			fd_list_unlink(li);
+			fd_list_insert_before(&p->p_hdr.info.pi_endpoints, li);
+		}
+	
+	/* The internal data */
+	if (orig_dbg) {
+		CHECK_MALLOC( p->p_dbgorig = strdup(orig_dbg) );
+	} else {
+		CHECK_MALLOC( p->p_dbgorig = strdup("unspecified") );
+	}
+	p->p_cb = cb;
+	p->p_cb_data = cb_data;
+	
+	/* Ok, now check if we don't already have an entry with the same Diameter Id, and insert this one */
+	CHECK_POSIX( pthread_rwlock_wrlock(&fd_g_peers_rw) );
+	li_inf = &fd_g_peers;
+	for (li = fd_g_peers.next; li != &fd_g_peers; li = li->next) {
+		struct fd_peer * next = (struct fd_peer *)li;
+		int cont;
+		int cmp = fd_os_almostcasesrch( p->p_hdr.info.pi_diamid, p->p_hdr.info.pi_diamidlen, 
+						next->p_hdr.info.pi_diamid, next->p_hdr.info.pi_diamidlen,
+						&cont );
+		if (cmp > 0)
+			li_inf = li; /* it will come after this element, for sure */
+		
+		if (cmp == 0) {
+			ret = EEXIST; /* we have a duplicate */
+			break;
+		}
+		if (!cont)
+			break;
+	}
+	
+	/* We can insert the new peer object */
+	if (! ret)
+		do {
+			/* Update expiry list */
+			CHECK_FCT_DO( ret = fd_p_expi_update( p ), break );
+
+			/* Insert the new element in the list */
+			fd_list_insert_after( li_inf, &p->p_hdr.chain );
+		} while (0);
+
+	CHECK_POSIX( pthread_rwlock_unlock(&fd_g_peers_rw) );
+	if (ret) {
+		CHECK_FCT( fd_peer_free(&p) );
+	} else {
+		CHECK_FCT( fd_psm_begin(p) );
+	}
+	return ret;
+}
+
+/* Search for a peer */
+int fd_peer_getbyid( DiamId_t diamid, size_t diamidlen, int igncase, struct peer_hdr ** peer )
+{
+	struct fd_list * li;
+	TRACE_ENTRY("%p %zd %d %p", diamid, diamidlen, igncase, peer);
+	CHECK_PARAMS( diamid && diamidlen && peer );
+	
+	*peer = NULL;
+	
+	/* Search in the list */
+	CHECK_POSIX( pthread_rwlock_rdlock(&fd_g_peers_rw) );
+	if (igncase) {
+		for (li = fd_g_peers.next; li != &fd_g_peers; li = li->next) {
+			struct fd_peer * next = (struct fd_peer *)li;
+			int cmp, cont;
+			cmp = fd_os_almostcasesrch( diamid, diamidlen, next->p_hdr.info.pi_diamid, next->p_hdr.info.pi_diamidlen, &cont );
+			if (cmp == 0) {
+				*peer = &next->p_hdr;
+				break;
+			}
+			if (!cont)
+				break;
+		}
+	} else {
+		for (li = fd_g_peers.next; li != &fd_g_peers; li = li->next) {
+			struct fd_peer * next = (struct fd_peer *)li;
+			int cmp = fd_os_cmp( diamid, diamidlen, next->p_hdr.info.pi_diamid, next->p_hdr.info.pi_diamidlen );
+			if (cmp > 0)
+				continue;
+			if (cmp == 0)
+				*peer = &next->p_hdr;
+			break;
+		}
+	}
+	CHECK_POSIX( pthread_rwlock_unlock(&fd_g_peers_rw) );
+	
+	return 0;
+}
+
+
+#define free_null( _v ) 	\
+	if (_v) {		\
+		free(_v);	\
+		(_v) = NULL;	\
+	}
+	
+#define free_list( _l ) 						\
+	while (!FD_IS_LIST_EMPTY(_l)) {					\
+		struct fd_list * __li = ((struct fd_list *)(_l))->next;	\
+		fd_list_unlink(__li);					\
+		free(__li);						\
+	}
+
+/* Empty the lists of p_tosend, p_failover, 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 ) {
+		/* but only if they are routable */
+		if (fd_msg_is_routable(m)) {
+			fd_hook_call(HOOK_MESSAGE_FAILOVER, m, peer, NULL, fd_msg_pmdl_get(m));
+			CHECK_FCT_DO(fd_fifo_post_noblock(fd_g_outgoing, (void *)&m), 
+				{
+					/* fallback: destroy the message */
+					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? */)
+				} );
+		} else {
+			/* Just free it */
+			/* fd_hook_call(HOOK_MESSAGE_DROPPED, m, NULL, "Non-routable message freed during handover", fd_msg_pmdl_get(m)); */
+			CHECK_FCT_DO(fd_msg_free(m), /* What can we do more? */)
+		}
+	}
+	
+	/* Requeue all messages in the "failover" queue */
+	while ( fd_fifo_tryget(peer->p_tofailover, &m) == 0 ) {
+		fd_hook_call(HOOK_MESSAGE_FAILOVER, m, peer, NULL, fd_msg_pmdl_get(m));
+		CHECK_FCT_DO(fd_fifo_post_noblock(fd_g_outgoing, (void *)&m), 
+			{
+				/* fallback: destroy the message */
+				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? */)
+			} );
+	}
+	
+	/* Requeue all routable sent requests */
+	fd_p_sr_failover(&peer->p_sr);
+	
+	/* Done */
+	return;
+}
+
+/* Describe the current connection */
+int fd_peer_cnx_proto_info(struct peer_hdr *peer, char * buf, size_t len)
+{
+	struct fd_peer * p = (struct fd_peer *)peer;
+	TRACE_ENTRY("%p %p %zd", peer, buf, len);
+	CHECK_PARAMS(CHECK_PEER(peer) && buf && len);
+	
+	if (p->p_cnxctx) {
+		CHECK_FCT(fd_cnx_proto_info(p->p_cnxctx, buf, len));
+	} else if (p->p_receiver) {
+		CHECK_FCT(fd_cnx_proto_info(p->p_receiver, buf, len));
+	} else {
+		snprintf(buf, len, "Not Connected");
+	}
+	
+	return 0;
+}
+
+/* Return the value of srlist->cnt */
+int fd_peer_get_load_pending(struct peer_hdr *peer, long * to_receive, long * to_send)
+{
+	struct fd_peer * p = (struct fd_peer *)peer;
+	TRACE_ENTRY("%p %p %p", peer, to_receive, to_send);
+	CHECK_PARAMS(CHECK_PEER(peer));
+	
+	if (to_receive) {
+		CHECK_POSIX( pthread_mutex_lock(&p->p_sr.mtx) );
+		*to_receive = p->p_sr.cnt;
+		CHECK_POSIX( pthread_mutex_unlock(&p->p_sr.mtx) );
+	}
+	if (to_send) {
+		CHECK_POSIX( pthread_mutex_lock(&p->p_state_mtx) );
+		*to_send = p->p_reqin_count;
+		CHECK_POSIX( pthread_mutex_unlock(&p->p_state_mtx) );
+	}
+	
+	return 0;
+}
+
+
+/* Destroy a structure once cleanups have been performed (fd_psm_abord, ...) */
+int fd_peer_free(struct fd_peer ** ptr)
+{
+	struct fd_peer *p;
+	
+	TRACE_ENTRY("%p", ptr);
+	CHECK_PARAMS(ptr);
+	p = *ptr;
+	*ptr = NULL;
+	CHECK_PARAMS(p);
+	
+	CHECK_PARAMS( FD_IS_LIST_EMPTY(&p->p_hdr.chain) );
+	
+	free_null(p->p_hdr.info.pi_diamid);
+	
+	free_null(p->p_hdr.info.config.pic_realm); 
+	free_null(p->p_hdr.info.config.pic_priority); 
+	
+	free_null(p->p_hdr.info.runtime.pir_realm);
+	free_null(p->p_hdr.info.runtime.pir_prodname);
+	free_list( &p->p_hdr.info.runtime.pir_apps );
+	
+	free_list( &p->p_hdr.info.pi_endpoints );
+	
+	free_null(p->p_dbgorig);
+	
+	fd_list_unlink(&p->p_expiry);
+	fd_list_unlink(&p->p_actives);
+	
+	CHECK_FCT_DO( fd_fifo_del(&p->p_tosend), /* continue */ );
+	CHECK_FCT_DO( fd_fifo_del(&p->p_tofailover), /* continue */ );
+	CHECK_POSIX_DO( pthread_mutex_destroy(&p->p_state_mtx), /* continue */);
+	CHECK_POSIX_DO( pthread_mutex_destroy(&p->p_sr.mtx), /* continue */);
+	CHECK_POSIX_DO( pthread_cond_destroy(&p->p_sr.cnd), /* continue */);
+	
+	/* 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;
+}
+
+/* Terminate peer module (destroy all peers, first gently, then violently) */
+int fd_peer_fini()
+{
+	struct fd_list * li;
+	struct fd_list purge = FD_LIST_INITIALIZER(purge); /* Store zombie peers here */
+	int list_empty;
+	struct timespec	wait_until, now;
+	
+	TRACE_ENTRY();
+	
+	CHECK_FCT_DO(fd_p_expi_fini(), /* continue */);
+	
+	TRACE_DEBUG(INFO, "Sending terminate signal to all peer connections");
+	
+	CHECK_FCT_DO( pthread_rwlock_wrlock(&fd_g_peers_rw), /* continue */ );
+	for (li = fd_g_peers.next; li != &fd_g_peers; li = li->next) {
+		struct fd_peer * peer = (struct fd_peer *)li->o;
+		
+		if (fd_peer_getstate(peer) != STATE_ZOMBIE) {
+			CHECK_FCT_DO( fd_psm_terminate(peer, "REBOOTING"), /* continue */ );
+		} else {
+			li = li->prev; /* to avoid breaking the loop */
+			fd_list_unlink(&peer->p_hdr.chain);
+			fd_list_insert_before(&purge, &peer->p_hdr.chain);
+		}
+	}
+	list_empty = FD_IS_LIST_EMPTY(&fd_g_peers);
+	CHECK_FCT_DO( pthread_rwlock_unlock(&fd_g_peers_rw), /* continue */ );
+	
+	if (!list_empty) {
+		CHECK_SYS(  clock_gettime(CLOCK_REALTIME, &now)  );
+		fd_psm_start(); /* just in case */
+		TRACE_DEBUG(INFO, "Waiting for connections shutdown... (%d sec max)", DPR_TIMEOUT + 1);
+		wait_until.tv_sec  = now.tv_sec + DPR_TIMEOUT + 1;
+		wait_until.tv_nsec = now.tv_nsec;
+	}
+	
+	while ((!list_empty) && (TS_IS_INFERIOR(&now, &wait_until))) {
+		
+		/* Allow the PSM(s) to execute */
+		usleep(100000);
+		
+		/* Remove zombie peers */
+		CHECK_FCT_DO( pthread_rwlock_wrlock(&fd_g_peers_rw), /* continue */ );
+		for (li = fd_g_peers.next; li != &fd_g_peers; li = li->next) {
+			struct fd_peer * peer = (struct fd_peer *)li->o;
+			if (fd_peer_getstate(peer) == STATE_ZOMBIE) {
+				li = li->prev; /* to avoid breaking the loop */
+				fd_list_unlink(&peer->p_hdr.chain);
+				fd_list_insert_before(&purge, &peer->p_hdr.chain);
+			}
+		}
+		list_empty = FD_IS_LIST_EMPTY(&fd_g_peers);
+		CHECK_FCT_DO( pthread_rwlock_unlock(&fd_g_peers_rw), /* continue */ );
+		CHECK_SYS(  clock_gettime(CLOCK_REALTIME, &now)  );
+	}
+	
+	if (!list_empty) {
+		TRACE_DEBUG(INFO, "Forcing connections shutdown");
+		CHECK_FCT_DO( pthread_rwlock_wrlock(&fd_g_peers_rw), /* continue */ );
+		while (!FD_IS_LIST_EMPTY(&fd_g_peers)) {
+			struct fd_peer * peer = (struct fd_peer *)(fd_g_peers.next->o);
+			fd_psm_abord(peer);
+			fd_list_unlink(&peer->p_hdr.chain);
+			fd_list_insert_before(&purge, &peer->p_hdr.chain);
+		}
+		CHECK_FCT_DO( pthread_rwlock_unlock(&fd_g_peers_rw), /* continue */ );
+	}
+	
+	/* Free memory objects of all peers */
+	while (!FD_IS_LIST_EMPTY(&purge)) {
+		struct fd_peer * peer = (struct fd_peer *)(purge.next->o);
+		fd_list_unlink(&peer->p_hdr.chain);
+		fd_peer_free(&peer);
+	}
+	
+	/* Now empty the validators list */
+	CHECK_FCT_DO( pthread_rwlock_wrlock(&validators_rw), /* continue */ );
+	while (!FD_IS_LIST_EMPTY( &validators )) {
+		struct fd_list * v = validators.next;
+		fd_list_unlink(v);
+		free(v);
+	}
+	CHECK_FCT_DO( pthread_rwlock_unlock(&validators_rw), /* continue */ );
+	
+	return 0;
+}
+
+/* Dump info of one peer */
+DECLARE_FD_DUMP_PROTOTYPE(fd_peer_dump, struct peer_hdr * p, int details)
+{
+	FD_DUMP_HANDLE_OFFSET();
+	
+	CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "{peer}(@%p): ", p), return NULL);
+	
+	if (!CHECK_PEER(p)) {
+		CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "INVALID/NULL"), return NULL);
+	} else {
+		struct fd_peer * peer = (struct fd_peer *)p;
+		
+		CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "%s [%s, cnt:%ldsr,%ldpa]", peer->p_hdr.info.pi_diamid, STATE_STR(fd_peer_getstate(peer)), peer->p_sr.cnt, peer->p_reqin_count), return NULL);
+		if (details > 0) {
+			CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, " rlm:%s", peer->p_hdr.info.runtime.pir_realm ?: "<unknown>"), return NULL);
+			if (peer->p_hdr.info.runtime.pir_prodname) {
+				CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, " ['%s' %u]", peer->p_hdr.info.runtime.pir_prodname, peer->p_hdr.info.runtime.pir_firmrev), return NULL);
+			}
+		}
+		if (details > 1) {
+			CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, " [from:%s] flags:%s%s%s%s%s%s%s%s lft:%ds", 
+				peer->p_dbgorig ?: "unset",
+				peer->p_hdr.info.config.pic_flags.pro3 == PI_P3_DEFAULT ? "-" :
+					(peer->p_hdr.info.config.pic_flags.pro3 == PI_P3_IP ? "4" : "6"),
+				peer->p_hdr.info.config.pic_flags.pro4 == PI_P4_DEFAULT ? "-" :
+					(peer->p_hdr.info.config.pic_flags.pro4 == PI_P4_TCP ? "T" : "S"),
+				peer->p_hdr.info.config.pic_flags.alg ? "P" : "-",
+				peer->p_hdr.info.config.pic_flags.sec & PI_SEC_NONE ? "N" :"-",
+				peer->p_hdr.info.config.pic_flags.sec & PI_SEC_TLS_OLD ? "O" :"-",
+				peer->p_hdr.info.config.pic_flags.sctpsec & PI_SCTPSEC_3436 ? "3" :"-",
+				peer->p_hdr.info.config.pic_flags.exp ? "E" : "-",
+				peer->p_hdr.info.config.pic_flags.persist ? "P" : "-",
+				peer->p_hdr.info.config.pic_lft), return NULL);
+		}
+	
+	}
+	
+	return *buf;
+}
+
+/* Dump the list of peers */
+DECLARE_FD_DUMP_PROTOTYPE(fd_peer_dump_list, int details)
+{
+	struct fd_list * li;
+	FD_DUMP_HANDLE_OFFSET();
+	
+	CHECK_FCT_DO( pthread_rwlock_rdlock(&fd_g_peers_rw), /* continue */ );
+	
+	for (li = fd_g_peers.next; li != &fd_g_peers; li = li->next) {
+		CHECK_MALLOC_DO( fd_peer_dump(FD_DUMP_STD_PARAMS, (struct peer_hdr *)li->o, details), break);
+		if (li->next != &fd_g_peers) {
+			CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "\n"), break);
+		}
+	}
+	
+	CHECK_FCT_DO( pthread_rwlock_unlock(&fd_g_peers_rw), /* continue */ );
+	return *buf;
+}
+
+static struct dict_object *avp_oh_model = NULL;
+static pthread_mutex_t cache_avp_lock = PTHREAD_MUTEX_INITIALIZER;
+
+/* Handle an incoming CER request on a new connection */
+int fd_peer_handle_newCER( struct msg ** cer, struct cnxctx ** cnx )
+{
+	struct msg * msg;
+	struct avp *avp_oh;
+	struct avp_hdr * avp_hdr;
+	struct fd_list * li, *li_inf;
+	int found = 0;
+	int ret = 0;
+	struct fd_peer * peer;
+	struct cnx_incoming * ev_data;
+	
+	TRACE_ENTRY("%p %p", cer, cnx);
+	CHECK_PARAMS(cer && *cer && cnx && *cnx);
+	
+	msg = *cer; 
+	
+	/* If needed, resolve the dictionary model for Origin-Host */
+	CHECK_POSIX( pthread_mutex_lock(&cache_avp_lock) );
+	if (!avp_oh_model) {
+		avp_code_t code = AC_ORIGIN_HOST;
+		CHECK_FCT_DO( fd_dict_search ( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_CODE, &code, &avp_oh_model, ENOENT),
+			{ LOG_E("Cannot find Origin-Host AVP definition in the dictionary!"); (void) pthread_mutex_unlock(&cache_avp_lock); return __ret__; } );
+	}
+	CHECK_POSIX( pthread_mutex_unlock(&cache_avp_lock) );
+	
+	/* Find the Diameter Identity of the remote peer in the message */
+	CHECK_FCT( fd_msg_search_avp ( msg, avp_oh_model, &avp_oh ) );
+	ASSERT(avp_oh); /* otherwise it should not have passed rules validation, right? */
+	CHECK_FCT( fd_msg_avp_hdr ( avp_oh, &avp_hdr ) );
+	
+	/* First, check if the Origin-Host value is valid */
+	if (!fd_os_is_valid_DiameterIdentity(avp_hdr->avp_value->os.data, avp_hdr->avp_value->os.len)) {
+		CHECK_FCT( fd_msg_new_answer_from_req ( fd_g_config->cnf_dict, cer, MSGFL_ANSW_ERROR ) );
+		CHECK_FCT( fd_msg_rescode_set(*cer, "DIAMETER_INVALID_AVP_VALUE", 
+							"Your Origin-Host contains invalid characters.", avp_oh, 1 ) );
+		
+		fd_hook_call(HOOK_PEER_CONNECT_FAILED, *cer, NULL, "Received CER with invalid Origin-Host AVP", NULL);
+		
+		CHECK_FCT( fd_out_send(cer, *cnx, NULL, 0) );
+		return EINVAL;
+	}
+	
+	/* Search if we already have this peer id in our list. We take directly the write lock so that we don't need to upgrade if it is a new peer.
+	 * There is space for a small optimization here if needed.
+	 */
+	CHECK_POSIX( pthread_rwlock_wrlock(&fd_g_peers_rw) );
+	
+	li_inf = &fd_g_peers;
+	for (li = fd_g_peers.next; li != &fd_g_peers; li = li->next) {
+		int cmp, cont;
+		peer = (struct fd_peer *)li;
+		cmp = fd_os_almostcasesrch( avp_hdr->avp_value->os.data, avp_hdr->avp_value->os.len, peer->p_hdr.info.pi_diamid, peer->p_hdr.info.pi_diamidlen, &cont );
+		if (cmp > 0) {
+			li_inf = li;
+		}
+		if (cmp == 0) {
+			found = 1;
+			break;
+		}
+		if (!cont)
+			break;
+	}
+	
+	if (!found) {
+		/* Create a new peer entry for this new remote peer */
+		peer = NULL;
+		CHECK_FCT_DO( ret = fd_peer_alloc(&peer), goto out );
+		
+		/* Set the peer Diameter Id and the responder flag parameters */
+		CHECK_MALLOC_DO( peer->p_hdr.info.pi_diamid = os0dup(avp_hdr->avp_value->os.data, avp_hdr->avp_value->os.len), 
+			{ ret = ENOMEM; goto out; } );
+		peer->p_hdr.info.pi_diamidlen = avp_hdr->avp_value->os.len;
+		CHECK_MALLOC_DO( peer->p_dbgorig = strdup(fd_cnx_getid(*cnx)), { ret = ENOMEM; goto out; } );
+		peer->p_flags.pf_responder = 1;
+		peer->p_flags.pf_delete = 1;
+		
+		LOG_D("Created new peer object for incoming CER: %s", peer->p_hdr.info.pi_diamid);
+		
+#ifndef DISABLE_PEER_EXPIRY
+		/* Set this peer to expire on inactivity */
+		peer->p_hdr.info.config.pic_flags.exp 	= PI_EXP_INACTIVE;
+		peer->p_hdr.info.config.pic_lft		= 3600;	/* 1 hour without any message 
+		-- RFC3539 states that this must not be inferior to BRINGDOWN_INTERVAL = 5 minutes */
+		
+		CHECK_FCT_DO( ret = fd_p_expi_update( peer ), goto out );
+#endif /* DISABLE_PEER_EXPIRY */
+		
+		/* Insert the new peer in the list (the PSM will take care of setting the expiry after validation) */
+		fd_list_insert_after( li_inf, &peer->p_hdr.chain );
+		
+		/* Start the PSM, which will receive the event below */
+		CHECK_FCT_DO( ret = fd_psm_begin(peer), goto out );
+	} else {
+		/* Check if the peer is in zombie state */
+		if (fd_peer_getstate(peer) == STATE_ZOMBIE) {
+			/* Re-activate the peer */
+			if (peer->p_hdr.info.config.pic_flags.exp)
+				peer->p_flags.pf_responder = 1;
+			CHECK_POSIX_DO( pthread_mutex_lock(&peer->p_state_mtx), );
+			peer->p_state = STATE_NEW;
+			CHECK_POSIX_DO( pthread_mutex_unlock(&peer->p_state_mtx), );
+			peer->p_flags.pf_localterm = 0;
+			CHECK_FCT_DO( ret = fd_psm_begin(peer), goto out );
+		}
+	}
+	
+	/* Send the new connection event to the PSM */
+	CHECK_MALLOC_DO( ev_data = malloc(sizeof(struct cnx_incoming)), { ret = ENOMEM; goto out; } );
+	memset(ev_data, 0, sizeof(*ev_data));
+	
+	ev_data->cer = msg;
+	ev_data->cnx = *cnx;
+	ev_data->validate = !found;
+	
+	CHECK_FCT_DO( ret = fd_event_send(peer->p_events, FDEVP_CNX_INCOMING, sizeof(*ev_data), ev_data), goto out );
+	
+out:	
+	CHECK_POSIX( pthread_rwlock_unlock(&fd_g_peers_rw) );
+
+	if (ret == 0) {
+		/* Reset the "out" parameters, so that they are not cleanup on function return. */
+		*cer = NULL;
+		*cnx = NULL;
+	} else {
+		char buf[1024];
+		snprintf(buf, sizeof(buf), "An error occurred while processing new incoming CER: %s", strerror(ret));
+		fd_hook_call(HOOK_PEER_CONNECT_FAILED, *cer, NULL, buf, NULL);
+	}
+	
+	return ret;
+}
+
+/* Save a callback to accept / reject incoming unknown peers */
+int fd_peer_validate_register ( int (*peer_validate)(struct peer_info * /* info */, int * /* auth */, int (**cb2)(struct peer_info *)) )
+{
+	struct fd_list * v;
+	
+	TRACE_ENTRY("%p", peer_validate);
+	CHECK_PARAMS(peer_validate);
+	
+	/* Alloc a new entry */
+	CHECK_MALLOC( v = malloc(sizeof(struct fd_list)) );
+	fd_list_init( v, peer_validate );
+	
+	/* Add at the beginning of the list */
+	CHECK_FCT( pthread_rwlock_wrlock(&validators_rw) );
+	fd_list_insert_after(&validators, v);
+	CHECK_FCT( pthread_rwlock_unlock(&validators_rw));
+	
+	/* Done! */
+	return 0;
+}
+
+/* Validate a peer by calling the callbacks in turn -- return 0 if the peer is validated, ! 0 in case of error (>0) or if the peer is rejected (-1) */
+int fd_peer_validate( struct fd_peer * peer )
+{
+	int ret = 0;
+	struct fd_list * v;
+	
+	CHECK_FCT( pthread_rwlock_rdlock(&validators_rw) );
+	for (v = validators.next; v != &validators; v = v->next) {
+		int auth = 0;
+		pthread_cleanup_push(fd_cleanup_rwlock, &validators_rw);
+		CHECK_FCT_DO( ret = ((int(*)(struct peer_info *, int *, int (**)(struct peer_info *)))(v->o)) (&peer->p_hdr.info, &auth, &peer->p_cb2),  );
+		pthread_cleanup_pop(0);
+		if (ret)
+			goto out;
+		if (auth) {
+			ret = (auth > 0) ? 0 : -1;
+			goto out;
+		}
+		peer->p_cb2 = NULL;
+	}
+	
+	/* No callback has given a firm result, the default is to reject */
+	ret = -1;
+out:
+	CHECK_FCT( pthread_rwlock_unlock(&validators_rw));
+	return ret;
+}
diff --git a/libfdcore/queues.c b/libfdcore/queues.c
new file mode 100644
index 0000000..b317d34
--- /dev/null
+++ b/libfdcore/queues.c
@@ -0,0 +1,84 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@freediameter.net>							 *
+*													 *
+* Copyright (c) 2013, 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 "fdcore-internal.h"
+
+/* The global message queues */
+struct fifo * fd_g_incoming = NULL;
+struct fifo * fd_g_outgoing = NULL;
+struct fifo * fd_g_local = NULL;
+
+/* Initialize the message queues. */
+int fd_queues_init(void)
+{
+	TRACE_ENTRY();
+	CHECK_FCT( fd_fifo_new ( &fd_g_incoming, 20 ) );
+	CHECK_FCT( fd_fifo_new ( &fd_g_outgoing, 30 ) );
+	CHECK_FCT( fd_fifo_new ( &fd_g_local, 25 ) );
+	return 0;
+}
+
+/* Destroy a queue after emptying it (and dumping the content) */
+int fd_queues_fini(struct fifo ** queue)
+{
+	struct msg * msg;
+	int ret = 0;
+	
+	TRACE_ENTRY("%p", queue);
+	
+	/* Note : the threads that post into this queue should already been stopped before this !!! */
+	
+	CHECK_PARAMS(queue);
+	if (*queue == NULL)
+		return 0; /* the queue was not already initialized */
+
+	/* Empty all contents */
+	while (1) {
+		/* Check if there is a message in the queue */
+		ret = fd_fifo_tryget(*queue, &msg);
+		if (ret == EWOULDBLOCK)
+			break;
+		CHECK_FCT(ret);
+		
+		/* We got one! */
+		fd_hook_call(HOOK_MESSAGE_DROPPED, msg, NULL, "Message lost because framework is terminating.", fd_msg_pmdl_get(msg));
+		fd_msg_free(msg);
+	}
+	
+	/* Now, delete the empty queue */
+	CHECK_FCT( fd_fifo_del ( queue ) );
+	
+	return 0;
+}
diff --git a/libfdcore/routing_dispatch.c b/libfdcore/routing_dispatch.c
new file mode 100644
index 0000000..93df065
--- /dev/null
+++ b/libfdcore/routing_dispatch.c
@@ -0,0 +1,1328 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@freediameter.net>							 *
+*													 *
+* Copyright (c) 2015, 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 "fdcore-internal.h"
+
+/********************************************************************************/
+/*              First part : handling the extensions callbacks                  */
+/********************************************************************************/
+
+/* Lists of the callbacks, and locks to protect them */
+static pthread_rwlock_t rt_fwd_lock = PTHREAD_RWLOCK_INITIALIZER;
+static struct fd_list 	rt_fwd_list = FD_LIST_INITIALIZER_O(rt_fwd_list, &rt_fwd_lock);
+
+static pthread_rwlock_t rt_out_lock = PTHREAD_RWLOCK_INITIALIZER;
+static struct fd_list 	rt_out_list = FD_LIST_INITIALIZER_O(rt_out_list, &rt_out_lock);
+
+/* Items in the lists are the same */
+struct rt_hdl {
+	struct fd_list	chain;	/* link in the rt_fwd_list or rt_out_list */
+	void *		cbdata;	/* the registered data */
+	union {
+		int	order;	/* This value is used to sort the list */
+		int 	dir;	/* It is the direction for FWD handlers */
+		int	prio;	/* and the priority for OUT handlers */
+	};
+	union {
+		int (*rt_fwd_cb)(void * cbdata, struct msg ** msg);
+		int (*rt_out_cb)(void * cbdata, struct msg ** msg, struct fd_list * candidates);
+	};
+};	
+
+/* Add a new entry in the list */
+static int add_ordered(struct rt_hdl * new, struct fd_list * list)
+{
+	/* The list is ordered by prio parameter */
+	struct fd_list * li;
+	
+	CHECK_POSIX( pthread_rwlock_wrlock(list->o) );
+	
+	for (li = list->next; li != list; li = li->next) {
+		struct rt_hdl * h = (struct rt_hdl *) li;
+		if (new->order <= h->order)
+			break;
+	}
+	
+	fd_list_insert_before(li, &new->chain);
+	
+	CHECK_POSIX( pthread_rwlock_unlock(list->o) );
+	
+	return 0;
+}
+
+/* Register a new FWD callback */
+int fd_rt_fwd_register ( int (*rt_fwd_cb)(void * cbdata, struct msg ** msg), void * cbdata, enum fd_rt_fwd_dir dir, struct fd_rt_fwd_hdl ** handler )
+{
+	struct rt_hdl * new;
+	
+	TRACE_ENTRY("%p %p %d %p", rt_fwd_cb, cbdata, dir, handler);
+	CHECK_PARAMS( rt_fwd_cb );
+	CHECK_PARAMS( (dir >= RT_FWD_REQ) && ( dir <= RT_FWD_ANS) );
+	
+	/* Create a new container */
+	CHECK_MALLOC(new = malloc(sizeof(struct rt_hdl)));
+	memset(new, 0, sizeof(struct rt_hdl));
+	
+	/* Write the content */
+	fd_list_init(&new->chain, NULL);
+	new->cbdata 	= cbdata;
+	new->dir    	= dir;
+	new->rt_fwd_cb 	= rt_fwd_cb;
+	
+	/* Save this in the list */
+	CHECK_FCT( add_ordered(new, &rt_fwd_list) );
+	
+	/* Give it back to the extension if needed */
+	if (handler)
+		*handler = (void *)new;
+	
+	return 0;
+}
+
+/* Remove it */
+int fd_rt_fwd_unregister ( struct fd_rt_fwd_hdl * handler, void ** cbdata )
+{
+	struct rt_hdl * del;
+	TRACE_ENTRY( "%p %p", handler, cbdata);
+	CHECK_PARAMS( handler );
+	
+	del = (struct rt_hdl *)handler;
+	CHECK_PARAMS( del->chain.head == &rt_fwd_list );
+	
+	/* Unlink */
+	CHECK_POSIX( pthread_rwlock_wrlock(&rt_fwd_lock) );
+	fd_list_unlink(&del->chain);
+	CHECK_POSIX( pthread_rwlock_unlock(&rt_fwd_lock) );
+	
+	if (cbdata)
+		*cbdata = del->cbdata;
+	
+	free(del);
+	return 0;
+}
+
+/* Register a new OUT callback */
+int fd_rt_out_register ( int (*rt_out_cb)(void * cbdata, struct msg ** pmsg, struct fd_list * candidates), void * cbdata, int priority, struct fd_rt_out_hdl ** handler )
+{
+	struct rt_hdl * new;
+	
+	TRACE_ENTRY("%p %p %d %p", rt_out_cb, cbdata, priority, handler);
+	CHECK_PARAMS( rt_out_cb );
+	
+	/* Create a new container */
+	CHECK_MALLOC(new = malloc(sizeof(struct rt_hdl)));
+	memset(new, 0, sizeof(struct rt_hdl));
+	
+	/* Write the content */
+	fd_list_init(&new->chain, NULL);
+	new->cbdata 	= cbdata;
+	new->prio    	= priority;
+	new->rt_out_cb 	= rt_out_cb;
+	
+	/* Save this in the list */
+	CHECK_FCT( add_ordered(new, &rt_out_list) );
+	
+	/* Give it back to the extension if needed */
+	if (handler)
+		*handler = (void *)new;
+	
+	return 0;
+}
+
+/* Remove it */
+int fd_rt_out_unregister ( struct fd_rt_out_hdl * handler, void ** cbdata )
+{
+	struct rt_hdl * del;
+	TRACE_ENTRY( "%p %p", handler, cbdata);
+	CHECK_PARAMS( handler );
+	
+	del = (struct rt_hdl *)handler;
+	CHECK_PARAMS( del->chain.head == &rt_out_list );
+	
+	/* Unlink */
+	CHECK_POSIX( pthread_rwlock_wrlock(&rt_out_lock) );
+	fd_list_unlink(&del->chain);
+	CHECK_POSIX( pthread_rwlock_unlock(&rt_out_lock) );
+	
+	if (cbdata)
+		*cbdata = del->cbdata;
+	
+	free(del);
+	return 0;
+}
+
+/********************************************************************************/
+/*                      Some default OUT routing callbacks                      */
+/********************************************************************************/
+
+/* Prevent sending to peers that do not support the message application */
+static int dont_send_if_no_common_app(void * cbdata, struct msg ** pmsg, struct fd_list * candidates)
+{
+	struct msg * msg = *pmsg;
+	struct fd_list * li;
+	struct msg_hdr * hdr;
+	
+	TRACE_ENTRY("%p %p %p", cbdata, msg, candidates);
+	CHECK_PARAMS(msg && candidates);
+	
+	CHECK_FCT( fd_msg_hdr(msg, &hdr) );
+	
+	/* For Base Diameter Protocol, every peer is supposed to support it, so skip */
+	if (hdr->msg_appl == 0)
+		return 0;
+	
+	/* Otherwise, check that the peers support the application */
+	for (li = candidates->next; li != candidates; li = li->next) {
+		struct rtd_candidate *c = (struct rtd_candidate *) li;
+		struct fd_peer * peer;
+		struct fd_app *found;
+		CHECK_FCT( fd_peer_getbyid( c->diamid, c->diamidlen, 0, (void *)&peer ) );
+		if (peer && !peer->p_hdr.info.runtime.pir_relay) {
+			/* Check if the remote peer advertised the message's appli */
+			CHECK_FCT( fd_app_check(&peer->p_hdr.info.runtime.pir_apps, hdr->msg_appl, &found) );
+			if (!found)
+				c->score += FD_SCORE_NO_DELIVERY;
+		}
+	}
+
+	return 0;
+}
+
+/* Detect if the Destination-Host and Destination-Realm match the peer */
+static int score_destination_avp(void * cbdata, struct msg ** pmsg, struct fd_list * candidates)
+{
+	struct msg * msg = *pmsg;
+	struct fd_list * li;
+	struct avp * avp;
+	union avp_value *dh = NULL, *dr = NULL;
+	
+	TRACE_ENTRY("%p %p %p", cbdata, msg, candidates);
+	CHECK_PARAMS(msg && candidates);
+	
+	/* Search the Destination-Host and Destination-Realm AVPs -- we could also use fd_msg_search_avp here, but this one is slightly more efficient */
+	CHECK_FCT(  fd_msg_browse(msg, MSG_BRW_FIRST_CHILD, &avp, NULL) );
+	while (avp) {
+		struct avp_hdr * ahdr;
+		CHECK_FCT(  fd_msg_avp_hdr( avp, &ahdr ) );
+
+		if (! (ahdr->avp_flags & AVP_FLAG_VENDOR)) {
+			switch (ahdr->avp_code) {
+				case AC_DESTINATION_HOST:
+					/* Parse this AVP */
+					CHECK_FCT( fd_msg_parse_dict ( avp, fd_g_config->cnf_dict, NULL ) );
+					ASSERT( ahdr->avp_value );
+					dh = ahdr->avp_value;
+					break;
+
+				case AC_DESTINATION_REALM:
+					/* Parse this AVP */
+					CHECK_FCT( fd_msg_parse_dict ( avp, fd_g_config->cnf_dict, NULL ) );
+					ASSERT( ahdr->avp_value );
+					dr = ahdr->avp_value;
+					break;
+			}
+		}
+
+		if (dh && dr)
+			break;
+
+		/* Go to next AVP */
+		CHECK_FCT(  fd_msg_browse(avp, MSG_BRW_NEXT, &avp, NULL) );
+	}
+	
+	/* Now, check each candidate against these AVP values */
+	for (li = candidates->next; li != candidates; li = li->next) {
+		struct rtd_candidate *c = (struct rtd_candidate *) li;
+		
+	    #if 0 /* this is actually useless since the sending process will also ensure that the peer is still available */
+		struct fd_peer * peer;
+		/* 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_almostcasesrch(dh->os.data, dh->os.len, c->diamid, c->diamidlen, NULL) ) {
+			/* The candidate is the Destination-Host */
+			c->score += FD_SCORE_FINALDEST;
+		} else {
+			if (dr && !fd_os_almostcasesrch(dr->os.data, dr->os.len, c->realm, c->realmlen, NULL) ) {
+				/* The candidate's realm matchs the Destination-Realm */
+				c->score += FD_SCORE_REALM;
+			}
+		}
+	}
+
+	return 0;
+}
+
+/********************************************************************************/
+/*                        Helper functions                                      */
+/********************************************************************************/
+
+/* Find (first) '!' and '@' positions in a UTF-8 encoded string (User-Name AVP value) */
+static void nai_get_indexes(union avp_value * un, int * excl_idx, int * at_idx)
+{
+	int i;
+	
+	TRACE_ENTRY("%p %p %p", un, excl_idx, at_idx);
+	CHECK_PARAMS_DO( un && excl_idx && at_idx, return );
+	
+	*excl_idx = 0;
+	*at_idx = 0;
+	
+	/* Search if there is a '!' before any '@' -- do we need to check it contains a '.' ? */
+	for (i = 0; i < un->os.len; i++) {
+		/* The '!' marks the decorated NAI */
+		if ( un->os.data[i] == (unsigned char) '!' ) {
+			if (!*excl_idx)
+				*excl_idx = i;
+			continue;
+		}
+		/* If we reach the realm part, we can stop */
+		if ( un->os.data[i] == (unsigned char) '@' ) {
+			*at_idx = i;
+			break;
+		}
+		/* Stop if we find a \0 in the middle */
+		if ( un->os.data[i] == 0 ) {
+			return;
+		}
+		/* Skip escaped characters */
+		if ( un->os.data[i] == (unsigned char) '\\' ) {
+			i++;
+			continue;
+		}
+	}
+	
+	return;
+}	
+
+/* Test if a User-Name AVP contains a Decorated NAI -- RFC4282, RFC5729 */
+/* Create new User-Name and Destination-Realm values */
+static int process_decorated_NAI(int * was_nai, union avp_value * un, union avp_value * dr)
+{
+	int at_idx, sep_idx;
+	unsigned char * old_un;
+	TRACE_ENTRY("%p %p %p", was_nai, un, dr);
+	CHECK_PARAMS(was_nai && un && dr);
+	
+	/* Save the decorated User-Name, for example 'homerealm.example.net!user@otherrealm.example.net' */
+	old_un = un->os.data;
+	
+	/* Search the positions of the first '!' and the '@' in the string */
+	nai_get_indexes(un, &sep_idx, &at_idx);
+	if ((!sep_idx) || (sep_idx > at_idx) || !fd_os_is_valid_DiameterIdentity(old_un, sep_idx /* this is the new realm part */)) {
+		*was_nai = 0;
+		return 0;
+	}
+	
+	*was_nai = 1;
+	
+	/* Create the new User-Name value */
+	CHECK_MALLOC( un->os.data = malloc( at_idx ) );
+	memcpy( un->os.data, old_un + sep_idx + 1, at_idx - sep_idx ); /* user@ */
+	memcpy( un->os.data + at_idx - sep_idx, old_un, sep_idx ); /* homerealm.example.net */
+	
+	/* Create the new Destination-Realm value */
+	CHECK_MALLOC( dr->os.data = realloc(dr->os.data, sep_idx) );
+	memcpy( dr->os.data, old_un, sep_idx );
+	dr->os.len = sep_idx;
+	
+	TRACE_DEBUG(FULL, "Processed Decorated NAI : '%.*s' became '%.*s' (%.*s)",
+				(int)un->os.len, old_un,
+				(int)at_idx, un->os.data,
+				(int)dr->os.len, dr->os.data);
+	
+	un->os.len = at_idx;
+	free(old_un);
+	
+	return 0;
+}
+
+
+/* Function to return an error to an incoming request */
+static int return_error(struct msg ** pmsg, char * error_code, char * error_message, struct avp * failedavp)
+{
+	struct fd_peer * peer;
+	int is_loc = 0;
+
+	/* Get the source of the message */
+	{
+		DiamId_t id;
+		size_t   idlen;
+		CHECK_FCT( fd_msg_source_get( *pmsg, &id, &idlen ) );
+		
+		if (id == NULL) {
+			is_loc = 1; /* The message was issued locally */
+		} else {
+		
+			/* Search the peer with this id */
+			CHECK_FCT( fd_peer_getbyid( id, idlen, 0, (void *)&peer ) );
+
+			if (!peer) {
+				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;
+			}
+		}
+	}
+	
+	/* Create the error message */
+	CHECK_FCT( fd_msg_new_answer_from_req ( fd_g_config->cnf_dict, pmsg, MSGFL_ANSW_ERROR ) );
+
+	/* Set the error code */
+	CHECK_FCT( fd_msg_rescode_set(*pmsg, error_code, error_message, failedavp, 1 ) );
+
+	/* Send the answer */
+	if (is_loc) {
+		CHECK_FCT( fd_fifo_post(fd_g_incoming, pmsg) );
+	} else {
+		CHECK_FCT( fd_out_send(pmsg, NULL, peer, 1) );
+	}
+	
+	/* Done */
+	return 0;
+}
+
+
+/****************************************************************************/
+/*         Second part : threads moving messages in the daemon              */
+/****************************************************************************/
+
+/* The DISPATCH message processing */
+static int msg_dispatch(struct msg * msg)
+{
+	struct msg_hdr * hdr;
+	int is_req = 0;
+	struct session * sess;
+	enum disp_action action;
+	char * ec = NULL;
+	char * em = NULL;
+	struct msg *msgptr = msg, *error = NULL;
+
+	/* Read the message header */
+	CHECK_FCT( fd_msg_hdr(msg, &hdr) );
+	is_req = hdr->msg_flags & CMD_FLAG_REQUEST;
+	
+	/* Note: if the message is for local delivery, we should test for duplicate
+	  (draft-asveren-dime-dupcons-00). This may conflict with path validation decisions, no clear answer yet */
+
+	/* At this point, we need to understand the message content, so parse it */
+	CHECK_FCT_DO( fd_msg_parse_or_error( &msgptr, &error ),
+		{
+			int rescue = 0;
+			if (__ret__ != EBADMSG) {
+				fd_hook_call(HOOK_MESSAGE_DROPPED, msgptr, NULL, "Error while parsing received answer", fd_msg_pmdl_get(msgptr));
+				fd_msg_free(msgptr);
+			} else {
+				if (!msgptr) {
+					fd_hook_call(HOOK_MESSAGE_PARSING_ERROR2, error, NULL, NULL, fd_msg_pmdl_get(error));
+					/* error now contains the answer message to send back */
+					CHECK_FCT( fd_fifo_post(fd_g_outgoing, &error) );
+				} else if (!error) {
+					/* We have received an invalid answer to our query */
+					fd_hook_call(HOOK_MESSAGE_DROPPED, msgptr, NULL, "Received answer failed the dictionary / rules parsing", fd_msg_pmdl_get(msgptr));
+					fd_msg_free(msgptr);
+				} else {
+					/* We will pass the invalid received error to the application */
+					rescue = 1;
+				}
+			}
+			if (!rescue)
+				return 0; /* We are done with this message, go to the next */
+		} );
+
+	/* First, if the original request was registered with a callback and we receive the answer, call it. */
+	if ( ! is_req ) {
+		struct msg * qry;
+		void (*anscb)(void *, struct msg **) = NULL;
+		void * data = NULL;
+
+		/* Retrieve the corresponding query */
+		CHECK_FCT( fd_msg_answ_getq( msgptr, &qry ) );
+
+		/* Retrieve any registered handler */
+		CHECK_FCT( fd_msg_anscb_get( qry, &anscb, NULL, &data ) );
+
+		/* If a callback was registered, pass the message to it */
+		if (anscb != NULL) {
+
+			TRACE_DEBUG(FULL, "Calling callback registered when query was sent (%p, %p)", anscb, data);
+			(*anscb)(data, &msgptr);
+			
+			/* If the message is processed, we're done */
+			if (msgptr == NULL) {
+				return 0;
+			}
+			
+			/* otherwise continue the dispatching --hoping that the anscb callback did not mess with our message :) */
+		}
+	}
+	
+	/* Retrieve the session of the message */
+	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, &em, &error) );
+
+	/* Now, act depending on msg and action and ec */
+	if (msgptr) {
+		switch ( action ) {
+			case DISP_ACT_CONT:
+				/* 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;
+				}
+				/* We don't relay => reply error */
+				em = "The message was not handled by any extension callback";
+				ec = "DIAMETER_COMMAND_UNSUPPORTED";
+				/* and continue as if an error occurred... */
+			case DISP_ACT_ERROR:
+				/* We have a problem with delivering the message */
+				if (ec == NULL) {
+					ec = "DIAMETER_UNABLE_TO_COMPLY";
+				}
+				
+				if (!is_req) {
+					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;
+				}
+				
+				/* Create an answer with the error code and message */
+				CHECK_FCT( fd_msg_new_answer_from_req ( fd_g_config->cnf_dict, &msgptr, 0 ) );
+				CHECK_FCT( fd_msg_rescode_set(msgptr, ec, em, NULL, 1 ) );
+				
+			case DISP_ACT_SEND:
+				/* 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 */
+	return 0;
+}
+
+/* The ROUTING-IN message processing */
+static int msg_rt_in(struct msg * msg)
+{
+	struct msg_hdr * hdr;
+	int is_req = 0;
+	int is_err = 0;
+	DiamId_t qry_src = NULL;
+	struct msg *msgptr = msg;
+
+	/* Read the message header */
+	CHECK_FCT( fd_msg_hdr(msg, &hdr) );
+	is_req = hdr->msg_flags & CMD_FLAG_REQUEST;
+	is_err = hdr->msg_flags & CMD_FLAG_ERROR;
+
+	/* 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;
+	}
+	
+	/* If it is a request, we must analyze its content to decide what we do with it */
+	if (is_req) {
+		struct avp * avp, *un = NULL;
+		union avp_value * un_val = NULL, *dr_val = NULL;
+		enum status { UNKNOWN, YES, NO };
+		/* Are we Destination-Host? */
+		enum status is_dest_host = UNKNOWN;
+		/* Are we Destination-Realm? */
+		enum status is_dest_realm = UNKNOWN;
+		/* Do we support the application of the message? */
+		enum status is_local_app = UNKNOWN;
+
+		/* Check if we have local support for the message application */
+		if ( (hdr->msg_appl == 0) || (hdr->msg_appl == AI_RELAY) ) {
+			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 {
+			struct fd_app * app;
+			CHECK_FCT( fd_app_check(&fd_g_config->cnf_apps, hdr->msg_appl, &app) );
+			is_local_app = (app ? YES : NO);
+		}
+
+		/* Parse the message for Dest-Host, Dest-Realm, and Route-Record */
+		CHECK_FCT(  fd_msg_browse(msgptr, MSG_BRW_FIRST_CHILD, &avp, NULL)  );
+		while (avp) {
+			struct avp_hdr * ahdr;
+			struct fd_pei error_info;
+			int ret;
+			
+			memset(&error_info, 0, sizeof(struct fd_pei)); 
+			
+			CHECK_FCT(  fd_msg_avp_hdr( avp, &ahdr )  );
+
+			if (! (ahdr->avp_flags & AVP_FLAG_VENDOR)) {
+				switch (ahdr->avp_code) {
+					case AC_DESTINATION_HOST:
+						/* Parse this AVP */
+						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) );
+									if (error_info.pei_avp_free) { fd_msg_free(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;
+								}
+							} );
+						ASSERT( ahdr->avp_value );
+						/* Compare the Destination-Host AVP of the message with our identity */
+						if (!fd_os_almostcasesrch(ahdr->avp_value->os.data, ahdr->avp_value->os.len, fd_g_config->cnf_diamid, fd_g_config->cnf_diamid_len, NULL)) {
+							is_dest_host = YES;
+						} else {
+							is_dest_host = NO;
+						}
+						break;
+
+					case AC_DESTINATION_REALM:
+						/* Parse this AVP */
+						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) );
+									if (error_info.pei_avp_free) { fd_msg_free(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;
+								}
+							} );
+						ASSERT( ahdr->avp_value );
+						dr_val = ahdr->avp_value;
+						/* Compare the Destination-Realm AVP of the message with our identity */
+						if (!fd_os_almostcasesrch(dr_val->os.data, dr_val->os.len, fd_g_config->cnf_diamrlm, fd_g_config->cnf_diamrlm_len, NULL)) {
+							is_dest_realm = YES;
+						} else {
+							is_dest_realm = NO;
+						}
+						break;
+
+					/* we also use User-Name for decorated NAI */
+					case AC_USER_NAME:
+						/* Parse this AVP */
+						CHECK_FCT_DO( ret = fd_msg_parse_dict ( avp, fd_g_config->cnf_dict, &error_info ),
+							{
+								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) );
+									if (error_info.pei_avp_free) { fd_msg_free(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;
+								}
+							} );
+						ASSERT( ahdr->avp_value );
+						un = avp;
+						un_val = ahdr->avp_value;
+						break;
+						
+					case AC_ROUTE_RECORD:
+						/* Parse this AVP */
+						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) );
+									if (error_info.pei_avp_free) { fd_msg_free(error_info.pei_avp); }
+									return 0;
+								} else {
+									fd_hook_call(HOOK_MESSAGE_PARSING_ERROR, msgptr, NULL, "Unspecified error while parsing Route-Record AVP", fd_msg_pmdl_get(msgptr));
+									return ret;
+								}
+							} );
+						ASSERT( ahdr->avp_value );
+						/* Is this our own name ? */
+						if (!fd_os_almostcasesrch(ahdr->avp_value->os.data, ahdr->avp_value->os.len, fd_g_config->cnf_diamid, fd_g_config->cnf_diamid_len, NULL)) {
+							/* Yes: then we must return DIAMETER_LOOP_DETECTED according to Diameter RFC */
+							char * error = "DIAMETER_LOOP_DETECTED";
+							fd_hook_call(HOOK_MESSAGE_PARSING_ERROR, msgptr, NULL, error, fd_msg_pmdl_get(msgptr));
+							CHECK_FCT( return_error( &msgptr, error, NULL, NULL) );
+							return 0;
+						}
+						break;
+						
+					
+				}
+			}
+
+			/* 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;
+
+			/* Go to next AVP */
+			CHECK_FCT(  fd_msg_browse(avp, MSG_BRW_NEXT, &avp, NULL)  );
+		}
+
+		/* OK, now decide what we do with the request */
+
+		/* 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;
+		}
+
+		/* If we are listed as Destination-Host */
+		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;
+		}
+
+		/* 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;
+			}
+		} 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) */
+			/* Handle the decorated NAI */
+			if (un_val) {
+				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;
+					} );
+			}
+				
+			if (is_nai) {
+				/* We have transformed the AVP, now submit it again in the queue */
+				CHECK_FCT(fd_fifo_post(fd_g_incoming, &msgptr) );
+				return 0;
+			}
+
+			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;
+			}
+		}
+
+		/* From that point, for requests, we will call the registered callbacks, then forward to another peer */
+
+	} else {
+		/* The message is an answer */
+		struct msg * qry;
+
+		/* Retrieve the corresponding query and its origin */
+		CHECK_FCT( fd_msg_answ_getq( msgptr, &qry ) );
+		CHECK_FCT( fd_msg_source_get( qry, &qry_src, NULL ) );
+
+		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;
+		}
+		
+		/* From that point, for answers, we will call the registered callbacks, then pass it to the dispatch module or forward it */
+	}
+
+	/* Call all registered callbacks for this message */
+	{
+		struct fd_list * li;
+
+		CHECK_FCT( pthread_rwlock_rdlock( &rt_fwd_lock ) );
+		pthread_cleanup_push( fd_cleanup_rwlock, &rt_fwd_lock );
+
+		/* requests: dir = 1 & 2 => in order; answers = 3 & 2 => in reverse order */
+		for (	li = (is_req ? rt_fwd_list.next : rt_fwd_list.prev) ; msgptr && (li != &rt_fwd_list) ; li = (is_req ? li->next : li->prev) ) {
+			struct rt_hdl * rh = (struct rt_hdl *)li;
+			int ret;
+
+			if (is_req && (rh->dir > RT_FWD_ALL))
+				break;
+			if ((!is_req) && (rh->dir < RT_FWD_ALL))
+				break;
+
+			/* Ok, call this cb */
+			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),
+				{
+					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;
+				} );
+		}
+
+		pthread_cleanup_pop(0);
+		CHECK_FCT( pthread_rwlock_unlock( &rt_fwd_lock ) );
+
+		/* If a callback has handled the message, we stop now */
+		if (!msgptr)
+			return 0;
+	}
+
+	/* 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) );
+	}
+
+	/* We're done with this message */
+	return 0;
+}
+		
+
+/* The ROUTING-OUT message processing */
+static int msg_rt_out(struct msg * msg)
+{
+	struct rt_data * rtd = NULL;
+	struct msg_hdr * hdr;
+	int is_req = 0;
+	int ret;
+	struct fd_list * li, *candidates;
+	struct avp * avp;
+	struct rtd_candidate * c;
+	struct msg *msgptr = msg;
+	DiamId_t qry_src = NULL;
+	size_t qry_src_len = 0;
+	
+	/* Read the message header */
+	CHECK_FCT( fd_msg_hdr(msgptr, &hdr) );
+	is_req = hdr->msg_flags & CMD_FLAG_REQUEST;
+	
+	/* For answers, the routing is very easy */
+	if ( ! is_req ) {
+		struct msg * qry;
+		struct msg_hdr * qry_hdr;
+		struct fd_peer * peer = NULL;
+
+		/* Retrieve the corresponding query and its origin */
+		CHECK_FCT( fd_msg_answ_getq( msgptr, &qry ) );
+		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, qry_src_len, 0, (void *) &peer ) );
+		if (fd_peer_getstate(peer) != STATE_OPEN && fd_peer_getstate(peer) != STATE_CLOSING_GRACE) {
+			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;
+		}
+
+		/* We must restore the hop-by-hop id */
+		CHECK_FCT( fd_msg_hdr(qry, &qry_hdr) );
+		hdr->msg_hbhid = qry_hdr->msg_hbhid;
+
+		/* Push the message into this peer */
+		CHECK_FCT( fd_out_send(&msgptr, NULL, peer, 1) );
+
+		/* We're done with this answer */
+		return 0;
+	}
+	
+	/* From that point, the message is a request */
+	CHECK_FCT( fd_msg_source_get( msgptr, &qry_src, &qry_src_len ) );
+	/* if qry_src != NULL, this message is relayed, otherwise it is locally issued */
+
+	/* Get the routing data out of the message if any (in case of re-transmit) */
+	CHECK_FCT( fd_msg_rt_get ( msgptr, &rtd ) );
+
+	/* If there is no routing data already, let's create it */
+	if (rtd == NULL) {
+		CHECK_FCT( fd_rtd_init(&rtd) );
+
+		/* Add all peers currently in OPEN state */
+		CHECK_FCT( pthread_rwlock_rdlock(&fd_g_activ_peers_rw) );
+		for (li = fd_g_activ_peers.next; li != &fd_g_activ_peers; li = li->next) {
+			struct fd_peer * p = (struct fd_peer *)li->o;
+			CHECK_FCT_DO( ret = fd_rtd_candidate_add(rtd, 
+							p->p_hdr.info.pi_diamid, 
+							p->p_hdr.info.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) );
+
+		/* Now let's remove all peers from the Route-Records */
+		CHECK_FCT(  fd_msg_browse(msgptr, MSG_BRW_FIRST_CHILD, &avp, NULL)  );
+		while (avp) {
+			struct avp_hdr * ahdr;
+			struct fd_pei error_info;
+			CHECK_FCT(  fd_msg_avp_hdr( avp, &ahdr )  );
+
+			if ((ahdr->avp_code == AC_ROUTE_RECORD) && (! (ahdr->avp_flags & AVP_FLAG_VENDOR)) ) {
+				/* Parse this AVP */
+				CHECK_FCT_DO( ret = fd_msg_parse_dict ( avp, fd_g_config->cnf_dict, &error_info ),
+					{
+						if (error_info.pei_errcode) {
+							CHECK_FCT( return_error( &msgptr, error_info.pei_errcode, error_info.pei_message, error_info.pei_avp) );
+							if (error_info.pei_avp_free) { fd_msg_free(error_info.pei_avp); }
+							return 0;
+						} else {
+							return ret;
+						}
+					} );
+				ASSERT( ahdr->avp_value );
+				/* Remove this value from the list. We don't need to pay special attention to the contents here. */
+				fd_rtd_candidate_del(rtd, ahdr->avp_value->os.data, ahdr->avp_value->os.len);
+			}
+
+			/* Go to next AVP */
+			CHECK_FCT(  fd_msg_browse(avp, MSG_BRW_NEXT, &avp, NULL)  );
+		}
+		
+		/* Save the routing information in the message */
+		CHECK_FCT( fd_msg_rt_associate ( msgptr, rtd ) );
+	}
+
+	/* Note: we reset the scores and pass the message to the callbacks, maybe we could re-use the saved scores when we have received an error ? -- TODO */
+
+	/* Ok, we have our list in rtd now, let's (re)initialize the scores */
+	fd_rtd_candidate_extract(rtd, &candidates, FD_SCORE_INI);
+
+	/* Pass the list to registered callbacks (even if it is empty list) */
+	{
+		CHECK_FCT( pthread_rwlock_rdlock( &rt_out_lock ) );
+		pthread_cleanup_push( fd_cleanup_rwlock, &rt_out_lock );
+
+		/* We call the cb by reverse priority order */
+		for (	li = rt_out_list.prev ; (msgptr != NULL) && (li != &rt_out_list) ; li = li->prev ) {
+			struct rt_hdl * rh = (struct rt_hdl *)li;
+
+			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),
+				{
+					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;
+				} );
+		}
+
+		pthread_cleanup_pop(0);
+		CHECK_FCT( pthread_rwlock_unlock( &rt_out_lock ) );
+
+		/* If an error occurred or the callback disposed of the message, go to next message */
+		if (! msgptr) {
+			return 0;
+		}
+	}
+	
+	/* Order the candidate peers by score attributed by the callbacks */
+	CHECK_FCT( fd_rtd_candidate_reorder(candidates) );
+
+	/* Now try sending the message */
+	for (li = candidates->prev; li != candidates; li = li->prev) {
+		struct fd_peer * peer;
+
+		c = (struct rtd_candidate *) li;
+
+		/* Stop when we have reached the end of valid candidates */
+		if (c->score < 0)
+			break;
+
+		/* Search for the peer */
+		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(&msgptr, NULL, peer, 1), continue );
+			
+			/* If the sending was successful */
+			break;
+		}
+	}
+
+	/* If the message has not been sent, return an error */
+	if (msgptr) {
+		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);
+	}
+
+	/* We're done with this message */
+	
+	return 0;
+}
+
+
+/********************************************************************************/
+/*                     Management of the threads                                */
+/********************************************************************************/
+
+/* Note: in the first version, we only create one thread of each kind.
+ We could improve the scalability by using the threshold feature of the queues
+ to create additional threads if a queue is filling up, or at least giving a configurable
+ number of threads of each kind.
+ */
+
+/* Control of the threads */
+static enum { RUN = 0, STOP = 1 } order_val = RUN;
+static pthread_mutex_t order_state_lock = PTHREAD_MUTEX_INITIALIZER;
+
+/* Threads report their status */
+enum thread_state { NOTRUNNING = 0, RUNNING = 1 };
+static void cleanup_state(void * state_loc)
+{
+	CHECK_POSIX_DO( pthread_mutex_lock(&order_state_lock), );
+	*(enum thread_state *)state_loc = NOTRUNNING;
+	CHECK_POSIX_DO( pthread_mutex_unlock(&order_state_lock), );
+}
+
+/* This is the common thread code (same for routing and dispatching) */
+static void * process_thr(void * arg, int (*action_cb)(struct msg * msg), struct fifo * queue, char * action_name)
+{
+	TRACE_ENTRY("%p %p %p %p", arg, action_cb, queue, action_name);
+	
+	/* Set the thread name */
+	{
+		char buf[48];
+		snprintf(buf, sizeof(buf), "%s (%p)", action_name, arg);
+		fd_log_threadname ( buf );
+	}
+	
+	/* The thread reports its status when canceled */
+	CHECK_PARAMS_DO(arg, return NULL);
+	pthread_cleanup_push( cleanup_state, arg );
+	
+	/* Mark the thread running */
+	CHECK_POSIX_DO( pthread_mutex_lock(&order_state_lock), );
+	*(enum thread_state *)arg = RUNNING;
+	CHECK_POSIX_DO( pthread_mutex_unlock(&order_state_lock), );
+	
+	do {
+		struct msg * msg;
+	
+		/* Test the current order */
+		{
+			int must_stop;
+			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_state_lock), { ASSERT(0); } );
+			if (must_stop)
+				goto end;
+			
+			pthread_testcancel();
+		}
+		
+		/* Ok, we are allowed to run */
+		
+		/* Get the next message from the queue */
+		{
+			int ret;
+			struct timespec ts;
+			
+			CHECK_SYS_DO( clock_gettime(CLOCK_REALTIME, &ts), goto fatal_error );
+			ts.tv_sec += 1;
+			
+			ret = fd_fifo_timedget ( queue, &msg, &ts );
+			if (ret == ETIMEDOUT)
+				/* loop, check if the thread must stop now */
+				continue;
+			if (ret == EPIPE)
+				/* The queue was destroyed, we are probably exiting */
+				goto end;
+			
+			/* check if another error occurred */
+			CHECK_FCT_DO( ret, goto fatal_error );
+		}
+		
+		LOG_A("%s: Picked next message", action_name);
+
+		/* Now process the message */
+		CHECK_FCT_DO( (*action_cb)(msg), goto fatal_error);
+
+		/* We're done with this message */
+	
+	} while (1);
+	
+fatal_error:
+	TRACE_DEBUG(INFO, "An unrecoverable error occurred, %s thread is terminating...", action_name);
+	CHECK_FCT_DO(fd_core_shutdown(), );
+	
+end:	
+	; /* noop so that we get rid of "label at end of compund statement" warning */
+	/* Mark the thread as terminated */
+	pthread_cleanup_pop(1);
+	return NULL;
+}
+
+/* The dispatch thread */
+static void * dispatch_thr(void * arg)
+{
+	return process_thr(arg, msg_dispatch, fd_g_local, "Dispatch");
+}
+
+/* The (routing-in) thread -- see description in freeDiameter.h */
+static void * routing_in_thr(void * arg)
+{
+	return process_thr(arg, msg_rt_in, fd_g_incoming, "Routing-IN");
+}
+
+/* The (routing-out) thread -- see description in freeDiameter.h */
+static void * routing_out_thr(void * arg)
+{
+	return process_thr(arg, msg_rt_out, fd_g_outgoing, "Routing-OUT");
+}
+
+
+/********************************************************************************/
+/*                     The functions for the other files                        */
+/********************************************************************************/
+
+static pthread_t * dispatch = NULL;
+static enum thread_state * disp_state = NULL;
+
+/* Later: make this more dynamic */
+static pthread_t rt_out = (pthread_t)NULL;
+static enum thread_state out_state = NOTRUNNING;
+
+static pthread_t rt_in  = (pthread_t)NULL;
+static enum thread_state in_state = NOTRUNNING;
+
+/* Initialize the routing and dispatch threads */
+int fd_rtdisp_init(void)
+{
+	int i;
+	
+	/* Prepare the array for dispatch */
+	CHECK_MALLOC( disp_state = calloc(fd_g_config->cnf_dispthr, sizeof(enum thread_state)) );
+	CHECK_MALLOC( dispatch = calloc(fd_g_config->cnf_dispthr, sizeof(pthread_t)) );
+	
+	/* Create the threads */
+	for (i=0; i < fd_g_config->cnf_dispthr; i++) {
+		CHECK_POSIX( pthread_create( &dispatch[i], NULL, dispatch_thr, &disp_state[i] ) );
+	}
+	CHECK_POSIX( pthread_create( &rt_out, NULL, routing_out_thr, &out_state) );
+	CHECK_POSIX( pthread_create( &rt_in,  NULL, routing_in_thr,  &in_state) );
+	
+	/* Later: TODO("Set the thresholds for the queues to create more threads as needed"); */
+	
+	/* Register the built-in callbacks */
+	CHECK_FCT( fd_rt_out_register( dont_send_if_no_common_app, NULL, 10, NULL ) );
+	CHECK_FCT( fd_rt_out_register( score_destination_avp, NULL, 10, NULL ) );
+	
+	return 0;
+}
+
+/* Ask the thread to terminate after next iteration */
+int fd_rtdisp_cleanstop(void)
+{
+	CHECK_POSIX_DO( pthread_mutex_lock(&order_state_lock), );
+	order_val = STOP;
+	CHECK_POSIX_DO( pthread_mutex_unlock(&order_state_lock), );
+
+	return 0;
+}
+
+static void stop_thread_delayed(enum thread_state *st, pthread_t * thr, char * th_name)
+{
+	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 */
+	if (!terminated) {
+		TRACE_DEBUG(INFO, "Waiting for the %s thread to have a chance to terminate", th_name);
+		do {
+			struct timespec	 ts, ts_final;
+
+			CHECK_SYS_DO( clock_gettime(CLOCK_REALTIME, &ts), break );
+			
+			ts_final.tv_sec = ts.tv_sec + 1;
+			ts_final.tv_nsec = ts.tv_nsec;
+			
+			while (TS_IS_INFERIOR( &ts, &ts_final )) {
+			
+				CHECK_POSIX_DO( pthread_mutex_lock(&order_state_lock), );
+				terminated = (*st == NOTRUNNING);
+				CHECK_POSIX_DO( pthread_mutex_unlock(&order_state_lock), );
+				if (terminated)
+					break;
+				
+				usleep(100000);
+				CHECK_SYS_DO( clock_gettime(CLOCK_REALTIME, &ts), break );
+			}
+		} while (0);
+	}
+
+	/* Now stop the thread and reclaim its resources */
+	CHECK_FCT_DO( fd_thr_term(thr ), /* continue */);
+	
+}
+
+/* Stop the thread after up to one second of wait */
+int fd_rtdisp_fini(void)
+{
+	int i;
+	
+	/* Destroy the incoming queue */
+	CHECK_FCT_DO( fd_queues_fini(&fd_g_incoming), /* ignore */);
+	
+	/* Stop the routing IN thread */
+	stop_thread_delayed(&in_state, &rt_in, "IN routing");
+	
+	/* Destroy the outgoing queue */
+	CHECK_FCT_DO( fd_queues_fini(&fd_g_outgoing), /* ignore */);
+	
+	/* Stop the routing OUT thread */
+	stop_thread_delayed(&out_state, &rt_out, "OUT routing");
+	
+	/* Destroy the local queue */
+	CHECK_FCT_DO( fd_queues_fini(&fd_g_local), /* ignore */);
+	
+	/* Stop the Dispatch threads */
+	if (dispatch != NULL) {
+		for (i=0; i < fd_g_config->cnf_dispthr; i++) {
+			stop_thread_delayed(&disp_state[i], &dispatch[i], "Dispatching");
+		}
+		free(dispatch);
+		dispatch = NULL;
+	}
+	if (disp_state != NULL) {
+		free(disp_state);
+		disp_state = NULL;
+	}
+	
+	return 0;
+}
+
+/* Cleanup handlers */
+int fd_rtdisp_cleanup(void)
+{
+	/* Cleanup all remaining handlers */
+	while (!FD_IS_LIST_EMPTY(&rt_fwd_list)) {
+		CHECK_FCT_DO( fd_rt_fwd_unregister ( (void *)rt_fwd_list.next, NULL ), /* continue */ );
+	}
+	while (!FD_IS_LIST_EMPTY(&rt_out_list)) {
+		CHECK_FCT_DO( fd_rt_out_unregister ( (void *)rt_out_list.next, NULL ), /* continue */ );
+	}
+	
+	fd_disp_unregister_all(); /* destroy remaining handlers */
+
+	return 0;
+}
+
+
+/********************************************************************************/
+/*                     For extensions to register a new appl                    */
+/********************************************************************************/
+
+/* Add an application into the peer's supported apps */
+int fd_disp_app_support ( struct dict_object * app, struct dict_object * vendor, int auth, int acct )
+{
+	application_id_t aid = 0;
+	vendor_id_t	 vid = 0;
+	
+	TRACE_ENTRY("%p %p %d %d", app, vendor, auth, acct);
+	CHECK_PARAMS( app && (auth || acct) );
+	
+	{
+		enum dict_object_type type = 0;
+		struct dict_application_data data;
+		CHECK_FCT( fd_dict_gettype(app, &type) );
+		CHECK_PARAMS( type == DICT_APPLICATION );
+		CHECK_FCT( fd_dict_getval(app, &data) );
+		aid = data.application_id;
+	}
+
+	if (vendor) {
+		enum dict_object_type type = 0;
+		struct dict_vendor_data data;
+		CHECK_FCT( fd_dict_gettype(vendor, &type) );
+		CHECK_PARAMS( type == DICT_VENDOR );
+		CHECK_FCT( fd_dict_getval(vendor, &data) );
+		vid = data.vendor_id;
+	}
+	
+	return fd_app_merge(&fd_g_config->cnf_apps, aid, vid, auth, acct);
+}
+
+
+
diff --git a/libfdcore/sctp.c b/libfdcore/sctp.c
new file mode 100644
index 0000000..c80a497
--- /dev/null
+++ b/libfdcore/sctp.c
@@ -0,0 +1,1353 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@freediameter.net>							 *
+*													 *
+* Copyright (c) 2015, 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 "fdcore-internal.h"
+#include "cnxctx.h"
+
+#include <netinet/sctp.h>
+#include <sys/uio.h>
+
+/* Size of buffer to receive ancilliary data. May need to be enlarged if more sockopt are set... */
+#ifndef CMSG_BUF_LEN
+#define CMSG_BUF_LEN	1024
+#endif /* CMSG_BUF_LEN */
+
+/* Use old draft-ietf-tsvwg-sctpsocket-17 API ? If not defined, RFC6458 API will be used */
+/* #define OLD_SCTP_SOCKET_API */
+
+/* Automatically fallback to old API if some of the new symbols are not defined */
+#if (!defined(SCTP_CONNECTX_4_ARGS) || (!defined(SCTP_RECVRCVINFO)) || (!defined(SCTP_SNDINFO))) 
+# define OLD_SCTP_SOCKET_API
+#endif
+
+
+/* Temper with the retransmission timers to try and improve disconnection detection response? Undef this to keep the defaults of SCTP stack */
+#ifndef USE_DEFAULT_SCTP_RTX_PARAMS	/* make this a configuration option if useful */
+#define ADJUST_RTX_PARAMS
+#endif /* USE_DEFAULT_SCTP_RTX_PARAMS */
+
+/* Pre-binding socket options -- # streams read in config */
+static int fd_setsockopt_prebind(int sk)
+{
+	socklen_t sz;
+	
+	TRACE_ENTRY( "%d", sk);
+	
+	CHECK_PARAMS( sk > 0 );
+	
+	{
+		int reuse = 1;
+		CHECK_SYS(  setsockopt(sk, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse))  );
+	}
+	
+#ifdef ADJUST_RTX_PARAMS
+	/* Set the retransmit parameters */
+	#ifdef SCTP_RTOINFO
+	{
+		struct sctp_rtoinfo rtoinfo;
+		memset(&rtoinfo, 0, sizeof(rtoinfo));
+
+		if (TRACE_BOOL(ANNOYING)) {
+			sz = sizeof(rtoinfo);
+			/* Read socket defaults */
+			CHECK_SYS(  getsockopt(sk, IPPROTO_SCTP, SCTP_RTOINFO, &rtoinfo, &sz)  );
+			if (sz != sizeof(rtoinfo))
+			{
+				TRACE_DEBUG(INFO, "Invalid size of socket option: %d / %d", sz, (socklen_t)sizeof(rtoinfo));
+				return ENOTSUP;
+			}
+			fd_log_debug( "Def SCTP_RTOINFO : srto_initial : %u", rtoinfo.srto_initial);
+			fd_log_debug( "                   srto_min     : %u", rtoinfo.srto_min);
+			fd_log_debug( "                   srto_max     : %u", rtoinfo.srto_max);
+		}
+
+		/* rtoinfo.srto_initial: Estimate of the RTT before it can be measured; keep the default value */
+		rtoinfo.srto_max = 5000; /* Maximum retransmit timer (in ms), we want fast retransmission time. */
+		rtoinfo.srto_min = 1000; /* Value under which the RTO does not descend, we set this value to not conflict with srto_max */
+
+		/* Set the option to the socket */
+		CHECK_SYS(  setsockopt(sk, IPPROTO_SCTP, SCTP_RTOINFO, &rtoinfo, sizeof(rtoinfo))  );
+		
+		if (TRACE_BOOL(ANNOYING)) {
+			/* Check new values */
+			CHECK_SYS(  getsockopt(sk, IPPROTO_SCTP, SCTP_RTOINFO, &rtoinfo, &sz)  );
+			fd_log_debug( "New SCTP_RTOINFO : srto_initial : %u", rtoinfo.srto_initial);
+			fd_log_debug( "                   srto_max     : %u", rtoinfo.srto_max);
+			fd_log_debug( "                   srto_min     : %u", rtoinfo.srto_min);
+		}
+	}
+	#else /* SCTP_RTOINFO */
+	TRACE_DEBUG(ANNOYING, "Skipping SCTP_RTOINFO");
+	#endif /* SCTP_RTOINFO */
+	
+	/* Set the association parameters: max number of retransmits, ... */
+	#ifdef SCTP_ASSOCINFO
+	{
+		struct sctp_assocparams assoc;
+		memset(&assoc, 0, sizeof(assoc));
+
+		if (TRACE_BOOL(ANNOYING)) {
+			sz = sizeof(assoc);
+			/* Read socket defaults */
+			CHECK_SYS(  getsockopt(sk, IPPROTO_SCTP, SCTP_ASSOCINFO, &assoc, &sz)  );
+			if (sz != sizeof(assoc))
+			{
+				TRACE_DEBUG(INFO, "Invalid size of socket option: %d / %d", sz, (socklen_t)sizeof(assoc));
+				return ENOTSUP;
+			}
+			fd_log_debug( "Def SCTP_ASSOCINFO : sasoc_asocmaxrxt               : %hu", assoc.sasoc_asocmaxrxt);
+			fd_log_debug( "                     sasoc_number_peer_destinations : %hu", assoc.sasoc_number_peer_destinations);
+			fd_log_debug( "                     sasoc_peer_rwnd                : %u" , assoc.sasoc_peer_rwnd);
+			fd_log_debug( "                     sasoc_local_rwnd               : %u" , assoc.sasoc_local_rwnd);
+			fd_log_debug( "                     sasoc_cookie_life              : %u" , assoc.sasoc_cookie_life);
+		}
+
+		assoc.sasoc_asocmaxrxt = 4;	/* Maximum number of retransmission attempts: we want fast detection of errors */
+						/* Note that this must remain less than the sum of retransmission parameters of the different paths. */
+		
+		/* Set the option to the socket */
+		CHECK_SYS(  setsockopt(sk, IPPROTO_SCTP, SCTP_ASSOCINFO, &assoc, sizeof(assoc))  );
+		
+		if (TRACE_BOOL(ANNOYING)) {
+			/* Check new values */
+			CHECK_SYS(  getsockopt(sk, IPPROTO_SCTP, SCTP_ASSOCINFO, &assoc, &sz)  );
+			fd_log_debug( "New SCTP_ASSOCINFO : sasoc_asocmaxrxt               : %hu", assoc.sasoc_asocmaxrxt);
+			fd_log_debug( "                     sasoc_number_peer_destinations : %hu", assoc.sasoc_number_peer_destinations);
+			fd_log_debug( "                     sasoc_peer_rwnd                : %u" , assoc.sasoc_peer_rwnd);
+			fd_log_debug( "                     sasoc_local_rwnd               : %u" , assoc.sasoc_local_rwnd);
+			fd_log_debug( "                     sasoc_cookie_life              : %u" , assoc.sasoc_cookie_life);
+		}
+	}
+	#else /* SCTP_ASSOCINFO */
+	TRACE_DEBUG(ANNOYING, "Skipping SCTP_ASSOCINFO");
+	#endif /* SCTP_ASSOCINFO */
+#endif /* ADJUST_RTX_PARAMS */
+	
+	/* Set the INIT parameters, such as number of streams */
+	#ifdef SCTP_INITMSG
+	{
+		struct sctp_initmsg init;
+		memset(&init, 0, sizeof(init));
+		
+		if (TRACE_BOOL(ANNOYING)) {
+			sz = sizeof(init);
+
+			/* Read socket defaults */
+			CHECK_SYS(  getsockopt(sk, IPPROTO_SCTP, SCTP_INITMSG, &init, &sz)  );
+			if (sz != sizeof(init))
+			{
+				TRACE_DEBUG(INFO, "Invalid size of socket option: %d / %d", sz, (socklen_t)sizeof(init));
+				return ENOTSUP;
+			}
+			fd_log_debug( "Def SCTP_INITMSG : sinit_num_ostreams   : %hu", init.sinit_num_ostreams);
+			fd_log_debug( "                   sinit_max_instreams  : %hu", init.sinit_max_instreams);
+			fd_log_debug( "                   sinit_max_attempts   : %hu", init.sinit_max_attempts);
+			fd_log_debug( "                   sinit_max_init_timeo : %hu", init.sinit_max_init_timeo);
+		}
+
+		/* Set the init options -- need to receive SCTP_COMM_UP to confirm the requested parameters, but we don't care (best effort) */
+		init.sinit_num_ostreams	  = fd_g_config->cnf_sctp_str;	/* desired number of outgoing streams */
+		init.sinit_max_init_timeo = CNX_TIMEOUT * 1000;
+
+		/* Set the option to the socket */
+		CHECK_SYS(  setsockopt(sk, IPPROTO_SCTP, SCTP_INITMSG, &init, sizeof(init))  );
+		
+		if (TRACE_BOOL(ANNOYING)) {
+			/* Check new values */
+			CHECK_SYS(  getsockopt(sk, IPPROTO_SCTP, SCTP_INITMSG, &init, &sz)  );
+			fd_log_debug( "New SCTP_INITMSG : sinit_num_ostreams   : %hu", init.sinit_num_ostreams);
+			fd_log_debug( "                   sinit_max_instreams  : %hu", init.sinit_max_instreams);
+			fd_log_debug( "                   sinit_max_attempts   : %hu", init.sinit_max_attempts);
+			fd_log_debug( "                   sinit_max_init_timeo : %hu", init.sinit_max_init_timeo);
+		}
+	}
+	#else /* SCTP_INITMSG */
+	TRACE_DEBUG(ANNOYING, "Skipping SCTP_INITMSG");
+	#endif /* SCTP_INITMSG */
+	
+	/* The SO_LINGER option will be reset if we want to perform SCTP ABORT */
+	#ifdef SO_LINGER
+	{
+		struct linger linger;
+		memset(&linger, 0, sizeof(linger));
+		
+		if (TRACE_BOOL(ANNOYING)) {
+			sz = sizeof(linger);
+			/* Read socket defaults */
+			CHECK_SYS(  getsockopt(sk, SOL_SOCKET, SO_LINGER, &linger, &sz)  );
+			if (sz != sizeof(linger))
+			{
+				TRACE_DEBUG(INFO, "Invalid size of socket option: %d / %d", sz, (socklen_t)sizeof(linger));
+				return ENOTSUP;
+			}
+			fd_log_debug( "Def SO_LINGER : l_onoff  : %d", linger.l_onoff);
+			fd_log_debug( " 	       l_linger : %d", linger.l_linger);
+		}
+		
+		linger.l_onoff	= 0;	/* Do not activate the linger */
+		linger.l_linger = 0;	/* Ignored, but it would mean : Return immediately when closing (=> abort) (graceful shutdown in background) */
+		
+		/* Set the option */
+		CHECK_SYS(  setsockopt(sk, SOL_SOCKET, SO_LINGER, &linger, sizeof(linger))  );
+		
+		if (TRACE_BOOL(ANNOYING)) {
+			/* Check new values */
+			CHECK_SYS(  getsockopt(sk, SOL_SOCKET, SO_LINGER, &linger, &sz)  );
+			fd_log_debug( "New SO_LINGER : l_onoff  : %d", linger.l_onoff);
+			fd_log_debug( "		  l_linger : %d", linger.l_linger);
+		}
+	}
+	#else /* SO_LINGER */
+	TRACE_DEBUG(ANNOYING, "Skipping SO_LINGER");
+	#endif /* SO_LINGER */
+	
+	/* Set the NODELAY option (Nagle-like algorithm) */
+	#ifdef SCTP_NODELAY
+	{
+		int nodelay;
+		
+		if (TRACE_BOOL(ANNOYING)) {
+			sz = sizeof(nodelay);
+			/* Read socket defaults */
+			CHECK_SYS(  getsockopt(sk, IPPROTO_SCTP, SCTP_NODELAY, &nodelay, &sz)  );
+			if (sz != sizeof(nodelay))
+			{
+				TRACE_DEBUG(INFO, "Invalid size of socket option: %d / %d", sz, (socklen_t)sizeof(nodelay));
+				return ENOTSUP;
+			}
+			fd_log_debug( "Def SCTP_NODELAY value : %s", nodelay ? "true" : "false");
+		}
+
+		nodelay = 1;	/* We turn ON to disable the Nagle algorithm, so that packets are sent ASAP. */
+		
+		/* Set the option to the socket */
+		CHECK_SYS(  setsockopt(sk, IPPROTO_SCTP, SCTP_NODELAY, &nodelay, sizeof(nodelay))  );
+		
+		if (TRACE_BOOL(ANNOYING)) {
+			/* Check new values */
+			CHECK_SYS(  getsockopt(sk, IPPROTO_SCTP, SCTP_NODELAY, &nodelay, &sz)  );
+			fd_log_debug( "New SCTP_NODELAY value : %s", nodelay ? "true" : "false");
+		}
+	}
+	#else /* SCTP_NODELAY */
+	TRACE_DEBUG(ANNOYING, "Skipping SCTP_NODELAY");
+	#endif /* SCTP_NODELAY */
+	
+	/*
+	   SO_RCVBUF			size of receiver window
+	   SO_SNDBUF			size of pending data to send
+	   SCTP_AUTOCLOSE		for one-to-many only
+	   SCTP_PRIMARY_ADDR		use this address as primary locally
+	   SCTP_ADAPTATION_LAYER	set adaptation layer indication, we don't use this 
+	*/
+	
+	/* Set the SCTP_DISABLE_FRAGMENTS option, required for TLS */
+	#ifdef SCTP_DISABLE_FRAGMENTS
+	{
+		int nofrag;
+		
+		if (TRACE_BOOL(ANNOYING)) {
+			sz = sizeof(nofrag);
+			/* Read socket defaults */
+			CHECK_SYS(  getsockopt(sk, IPPROTO_SCTP, SCTP_DISABLE_FRAGMENTS, &nofrag, &sz)  );
+			if (sz != sizeof(nofrag))
+			{
+				TRACE_DEBUG(INFO, "Invalid size of socket option: %d / %d", sz, (socklen_t)sizeof(nofrag));
+				return ENOTSUP;
+			}
+			fd_log_debug( "Def SCTP_DISABLE_FRAGMENTS value : %s", nofrag ? "true" : "false");
+		}
+
+		nofrag = 0;	/* We turn ON the fragmentation, since Diameter messages & TLS messages can be quite large. */
+		
+		/* Set the option to the socket */
+		CHECK_SYS(  setsockopt(sk, IPPROTO_SCTP, SCTP_DISABLE_FRAGMENTS, &nofrag, sizeof(nofrag))  );
+		
+		if (TRACE_BOOL(ANNOYING)) {
+			/* Check new values */
+			CHECK_SYS(  getsockopt(sk, IPPROTO_SCTP, SCTP_DISABLE_FRAGMENTS, &nofrag, &sz)  );
+			fd_log_debug( "New SCTP_DISABLE_FRAGMENTS value : %s", nofrag ? "true" : "false");
+		}
+	}
+	#else /* SCTP_DISABLE_FRAGMENTS */
+	# error "TLS requires support of SCTP_DISABLE_FRAGMENTS"
+	#endif /* SCTP_DISABLE_FRAGMENTS */
+	
+	/* SCTP_PEER_ADDR_PARAMS	control heartbeat per peer address. We set it as a default for all addresses in the association; not sure if it works ... */
+	#ifdef SCTP_PEER_ADDR_PARAMS
+	{
+		struct sctp_paddrparams parms;
+		memset(&parms, 0, sizeof(parms));
+		
+		/* Some kernel versions need this to be set */
+		parms.spp_address.ss_family = AF_INET;
+		
+		if (TRACE_BOOL(ANNOYING)) {
+			sz = sizeof(parms);
+
+			/* Read socket defaults */
+			CHECK_SYS(  getsockopt(sk, IPPROTO_SCTP, SCTP_PEER_ADDR_PARAMS, &parms, &sz)  );
+			if (sz != sizeof(parms))
+			{
+				TRACE_DEBUG(INFO, "Invalid size of socket option: %d / %d", sz, (socklen_t)sizeof(parms));
+				return ENOTSUP;
+			}
+			fd_log_debug( "Def SCTP_PEER_ADDR_PARAMS : spp_hbinterval    : %u",  parms.spp_hbinterval);
+			fd_log_debug( "                            spp_pathmaxrxt    : %hu", parms.spp_pathmaxrxt);
+			fd_log_debug( "                            spp_pathmtu       : %u",  parms.spp_pathmtu);
+			fd_log_debug( "                            spp_flags         : %x",  parms.spp_flags);
+			// fd_log_debug( "                            spp_ipv6_flowlabel: %u",  parms.spp_ipv6_flowlabel);
+			// fd_log_debug( "                            spp_ipv4_tos      : %hhu",parms.spp_ipv4_tos);
+		}
+
+		parms.spp_flags = SPP_HB_ENABLE;	/* Enable heartbeat for the association */
+		#ifdef SPP_PMTUD_ENABLE
+		parms.spp_flags |= SPP_PMTUD_ENABLE;	/* also enable path MTU discovery mechanism */
+		#endif /* SPP_PMTUD_ENABLE */
+		
+#ifdef ADJUST_RTX_PARAMS
+		parms.spp_hbinterval = 6000;		/* Send an heartbeat every 6 seconds to quickly start retransmissions */
+		/* parms.spp_pathmaxrxt : max nbr of restransmissions on this address. There is a relationship with sasoc_asocmaxrxt, so we leave the default here */
+#endif /* ADJUST_RTX_PARAMS */
+
+		/* Set the option to the socket */
+		CHECK_SYS(  setsockopt(sk, IPPROTO_SCTP, SCTP_PEER_ADDR_PARAMS, &parms, sizeof(parms)) );
+		
+		if (TRACE_BOOL(ANNOYING)) {
+			/* Check new values */
+			CHECK_SYS(  getsockopt(sk, IPPROTO_SCTP, SCTP_PEER_ADDR_PARAMS, &parms, &sz)  );
+			fd_log_debug( "New SCTP_PEER_ADDR_PARAMS : spp_hbinterval    : %u",  parms.spp_hbinterval);
+			fd_log_debug( "                            spp_pathmaxrxt    : %hu", parms.spp_pathmaxrxt);
+			fd_log_debug( "                            spp_pathmtu       : %u",  parms.spp_pathmtu);
+			fd_log_debug( "                            spp_flags         : %x",  parms.spp_flags);
+			// fd_log_debug( "                            spp_ipv6_flowlabel: %u",  parms.spp_ipv6_flowlabel);
+			// fd_log_debug( "                            spp_ipv4_tos      : %hhu",parms.spp_ipv4_tos);
+		}
+	}
+	#else /* SCTP_PEER_ADDR_PARAMS */
+	TRACE_DEBUG(ANNOYING, "Skipping SCTP_PEER_ADDR_PARAMS");
+	#endif /* SCTP_PEER_ADDR_PARAMS */
+	
+	/*
+	   SCTP_DEFAULT_SEND_PARAM - DEPRECATED // parameters for the sendto() call, we don't use it.
+	*/
+
+	/* Subscribe to some notifications */
+#ifdef OLD_SCTP_SOCKET_API
+	#ifdef SCTP_EVENTS /* DEPRECATED */
+	{
+		struct sctp_event_subscribe event;
+
+		memset(&event, 0, sizeof(event));
+		event.sctp_data_io_event	= 1;	/* to receive the stream ID in SCTP_SNDRCV ancilliary data on message reception */
+		event.sctp_association_event	= 0;	/* new or closed associations (mostly for one-to-many style sockets) */
+		event.sctp_address_event	= 1;	/* address changes */
+		event.sctp_send_failure_event	= 1;	/* delivery failures */
+		event.sctp_peer_error_event	= 1;	/* remote peer sends an error */
+		event.sctp_shutdown_event	= 1;	/* peer has sent a SHUTDOWN */
+		event.sctp_partial_delivery_event = 1;	/* a partial delivery is aborted, probably indicating the connection is being shutdown */
+		// event.sctp_adaptation_layer_event = 0;	/* adaptation layer notifications */
+		// event.sctp_authentication_event = 0;	/* when new key is made active */
+
+		/* Set the option to the socket */
+		CHECK_SYS(  setsockopt(sk, IPPROTO_SCTP, SCTP_EVENTS, &event, sizeof(event)) );
+		
+		if (TRACE_BOOL(ANNOYING)) {
+			sz = sizeof(event);
+			CHECK_SYS(  getsockopt(sk, IPPROTO_SCTP, SCTP_EVENTS, &event, &sz) );
+			if (sz != sizeof(event))
+			{
+				TRACE_DEBUG(INFO, "Invalid size of socket option: %d / %d", sz, (socklen_t)sizeof(event));
+				return ENOTSUP;
+			}
+
+			fd_log_debug( "SCTP_EVENTS : sctp_data_io_event          : %hhu", event.sctp_data_io_event);
+			fd_log_debug( "       	     sctp_association_event      : %hhu", event.sctp_association_event);
+			fd_log_debug( "       	     sctp_address_event	         : %hhu", event.sctp_address_event);
+			fd_log_debug( "       	     sctp_send_failure_event     : %hhu", event.sctp_send_failure_event);
+			fd_log_debug( "       	     sctp_peer_error_event       : %hhu", event.sctp_peer_error_event);
+			fd_log_debug( "       	     sctp_shutdown_event	 : %hhu", event.sctp_shutdown_event);
+			fd_log_debug( "       	     sctp_partial_delivery_event : %hhu", event.sctp_partial_delivery_event);
+			fd_log_debug( "       	     sctp_adaptation_layer_event : %hhu", event.sctp_adaptation_layer_event);
+			// fd_log_debug( "             sctp_authentication_event    : %hhu", event.sctp_authentication_event);
+		}
+	}
+	#else /* SCTP_EVENTS */
+	TRACE_DEBUG(ANNOYING, "Skipping SCTP_EVENTS");
+	#endif /* SCTP_EVENTS */
+#endif /*  OLD_SCTP_SOCKET_API */
+	
+	/* Set the v4 mapped addresses option */
+	#ifdef SCTP_I_WANT_MAPPED_V4_ADDR
+	if (!fd_g_config->cnf_flags.no_ip6) {
+		int v4mapped;
+		
+		if (TRACE_BOOL(ANNOYING)) {
+			sz = sizeof(v4mapped);
+			/* Read socket defaults */
+			CHECK_SYS(  getsockopt(sk, IPPROTO_SCTP, SCTP_I_WANT_MAPPED_V4_ADDR, &v4mapped, &sz)  );
+			if (sz != sizeof(v4mapped))
+			{
+				TRACE_DEBUG(INFO, "Invalid size of socket option: %d / %d", sz, (socklen_t)sizeof(v4mapped));
+				return ENOTSUP;
+			}
+			fd_log_debug( "Def SCTP_I_WANT_MAPPED_V4_ADDR value : %s", v4mapped ? "true" : "false");
+		}
+
+		#ifndef SCTP_USE_MAPPED_ADDRESSES
+		v4mapped = 0;	/* We don't want v4 mapped addresses */
+		#else /* SCTP_USE_MAPPED_ADDRESSES */
+		v4mapped = 1;	/* but we may have to, otherwise the bind fails in some environments */
+		#endif /* SCTP_USE_MAPPED_ADDRESSES */
+		
+		/* Set the option to the socket */
+		CHECK_SYS(  setsockopt(sk, IPPROTO_SCTP, SCTP_I_WANT_MAPPED_V4_ADDR, &v4mapped, sizeof(v4mapped))  );
+		
+		if (TRACE_BOOL(ANNOYING)) {
+			/* Check new values */
+			CHECK_SYS(  getsockopt(sk, IPPROTO_SCTP, SCTP_I_WANT_MAPPED_V4_ADDR, &v4mapped, &sz)  );
+			fd_log_debug( "New SCTP_I_WANT_MAPPED_V4_ADDR value : %s", v4mapped ? "true" : "false");
+		}
+	} else {
+		TRACE_DEBUG(ANNOYING, "Skipping SCTP_I_WANT_MAPPED_V4_ADDR, since IPv6 disabled.");
+	}
+	#else /* SCTP_I_WANT_MAPPED_V4_ADDR */
+	TRACE_DEBUG(ANNOYING, "Skipping SCTP_I_WANT_MAPPED_V4_ADDR");
+	#endif /* SCTP_I_WANT_MAPPED_V4_ADDR */
+	
+	/*
+	   SCTP_MAXSEG			max size of fragmented segments -- bound to PMTU
+	   SCTP_HMAC_IDENT		authentication algorithms
+	   SCTP_AUTH_ACTIVE_KEY		set the active key
+	   SCTP_DELAYED_SACK		control delayed acks
+	*/
+	
+	
+	/* Set the interleaving option */
+	#ifdef SCTP_FRAGMENT_INTERLEAVE
+	{
+		int interleave;
+		
+		if (TRACE_BOOL(ANNOYING)) {
+			sz = sizeof(interleave);
+			/* Read socket defaults */
+			CHECK_SYS(  getsockopt(sk, IPPROTO_SCTP, SCTP_FRAGMENT_INTERLEAVE, &interleave, &sz)  );
+			if (sz != sizeof(interleave))
+			{
+				TRACE_DEBUG(INFO, "Invalid size of socket option: %d / %d", sz, (socklen_t)sizeof(interleave));
+				return ENOTSUP;
+			}
+			fd_log_debug( "Def SCTP_FRAGMENT_INTERLEAVE value : %d", interleave);
+		}
+
+		#if 0
+		interleave = 2;	/* Allow partial delivery on several streams at the same time, since we are stream-aware in our security modules */
+		#else /* 0 */
+		interleave = 1;	/* hmmm actually, we are not yet capable of handling this, and we don t need it. */
+		#endif /* 0 */
+		
+		/* Set the option to the socket */
+		CHECK_SYS(  setsockopt(sk, IPPROTO_SCTP, SCTP_FRAGMENT_INTERLEAVE, &interleave, sizeof(interleave))  );
+		
+		if (TRACE_BOOL(ANNOYING)) {
+			/* Check new values */
+			CHECK_SYS(  getsockopt(sk, IPPROTO_SCTP, SCTP_FRAGMENT_INTERLEAVE, &interleave, &sz)  );
+			fd_log_debug( "New SCTP_FRAGMENT_INTERLEAVE value : %d", interleave);
+		}
+	}
+	#else /* SCTP_FRAGMENT_INTERLEAVE */
+	TRACE_DEBUG(ANNOYING, "Skipping SCTP_FRAGMENT_INTERLEAVE");
+	#endif /* SCTP_FRAGMENT_INTERLEAVE */
+	
+	/*
+	   SCTP_PARTIAL_DELIVERY_POINT	control partial delivery size
+	   SCTP_USE_EXT_RCVINFO	- DEPRECATED	use extended receive info structure (information about the next message if available)
+	 */
+	/* SCTP_AUTO_ASCONF is set by the postbind function */
+	/*
+	   SCTP_MAX_BURST		number of packets that can be burst emitted
+	   SCTP_CONTEXT			save a context information along with the association.
+	 */
+	 
+	/* SCTP_EXPLICIT_EOR: we assume implicit EOR in freeDiameter, so let's ensure this is known by the stack */
+	#ifdef SCTP_EXPLICIT_EOR
+	{
+		int bool;
+		
+		if (TRACE_BOOL(ANNOYING)) {
+			sz = sizeof(bool);
+			/* Read socket defaults */
+			CHECK_SYS(  getsockopt(sk, IPPROTO_SCTP, SCTP_EXPLICIT_EOR, &bool, &sz)  );
+			if (sz != sizeof(bool))
+			{
+				TRACE_DEBUG(INFO, "Invalid size of socket option: %d / %d", sz, (socklen_t)sizeof(bool));
+				return ENOTSUP;
+			}
+			fd_log_debug( "Def SCTP_EXPLICIT_EOR value : %s", bool ? "true" : "false");
+		}
+
+		bool = 0;
+		
+		/* Set the option to the socket */
+		CHECK_SYS(  setsockopt(sk, IPPROTO_SCTP, SCTP_EXPLICIT_EOR, &bool, sizeof(bool))  );
+		
+		if (TRACE_BOOL(ANNOYING)) {
+			/* Check new values */
+			CHECK_SYS(  getsockopt(sk, IPPROTO_SCTP, SCTP_EXPLICIT_EOR, &bool, &sz)  );
+			fd_log_debug( "New SCTP_EXPLICIT_EOR value : %s", bool ? "true" : "false");
+		}
+	}
+	#else /* SCTP_EXPLICIT_EOR */
+	TRACE_DEBUG(ANNOYING, "Skipping SCTP_EXPLICIT_EOR");
+	#endif /* SCTP_EXPLICIT_EOR */
+	
+	/*
+	   SCTP_REUSE_PORT		share one listening port with several sockets
+	*/
+	
+#ifndef OLD_SCTP_SOCKET_API
+	#ifdef SCTP_EVENT
+	{
+		/* Subscribe to the following events */
+		int events_I_want[] = {
+		#ifdef SCTP_ASSOC_CHANGE
+			/* SCTP_ASSOC_CHANGE, */
+		#endif 
+		#ifdef SCTP_PEER_ADDR_CHANGE
+			SCTP_PEER_ADDR_CHANGE,
+		#endif
+		#ifdef SCTP_REMOTE_ERROR
+			SCTP_REMOTE_ERROR,
+		#endif
+		#ifdef SCTP_SEND_FAILED_EVENT
+			SCTP_SEND_FAILED_EVENT,
+		#endif
+		#ifdef SCTP_SHUTDOWN_EVENT
+			SCTP_SHUTDOWN_EVENT,
+		#endif
+		#ifdef SCTP_ADAPTATION_INDICATION
+			/* SCTP_ADAPTATION_INDICATION, */
+		#endif
+		#ifdef SCTP_PARTIAL_DELIVERY_EVENT
+			/* SCTP_PARTIAL_DELIVERY_EVENT, */
+		#endif
+		#ifdef SCTP_AUTHENTICATION_EVENT
+			/* SCTP_AUTHENTICATION_EVENT, */
+		#endif
+		#ifdef SCTP_SENDER_DRY_EVENT
+			/* SCTP_SENDER_DRY_EVENT, */
+		#endif
+			0
+		};
+		int i;
+		
+		struct sctp_event event;
+		
+		for (i = 0; i < (sizeof(events_I_want) / sizeof(events_I_want[0]) - 1); i++) {
+			memset(&event, 0, sizeof(event));
+			event.se_type = events_I_want[i];
+			event.se_on = 1;
+			
+			/* Set the option to the socket */
+			CHECK_SYS(  setsockopt(sk, IPPROTO_SCTP, SCTP_EVENT, &event, sizeof(event)) );
+		}
+	}
+	#else /* SCTP_EVENT */
+	TRACE_DEBUG(ANNOYING, "Skipping SCTP_EVENT");
+	#endif /* SCTP_EVENT */
+	
+	
+	#ifdef SCTP_RECVRCVINFO /* Replaces SCTP_SNDRCV */
+	{
+		int bool = 1;
+		
+		/* Set the option to the socket */
+		CHECK_SYS(  setsockopt(sk, IPPROTO_SCTP, SCTP_RECVRCVINFO, &bool, sizeof(bool))  );
+		
+	}
+	#else /* SCTP_RECVRCVINFO */
+	TRACE_DEBUG(ANNOYING, "Skipping SCTP_RECVRCVINFO");
+	#endif /* SCTP_RECVRCVINFO */
+	
+	
+#endif /* OLD_SCTP_SOCKET_API */
+	
+	/* 
+		SCTP_RECVNXTINFO
+		
+		SCTP_DEFAULT_SNDINFO : send defaults
+		SCTP_DEFAULT_PRINFO  : default PR-SCTP
+	*/
+	
+
+	/* In case of no_ip4, force the v6only option */
+	#ifdef IPV6_V6ONLY
+	if (fd_g_config->cnf_flags.no_ip4) {
+		int opt = 1;
+		CHECK_SYS(setsockopt(sk, IPPROTO_IPV6, IPV6_V6ONLY, &opt, sizeof(opt)));
+	}
+	#endif /* IPV6_V6ONLY */
+	
+	return 0;
+}
+
+
+/* Post-binding socket options */
+static int fd_setsockopt_postbind(int sk, int bound_to_default)
+{
+	TRACE_ENTRY( "%d %d", sk, bound_to_default);
+	
+	CHECK_PARAMS( (sk > 0) );
+	
+	/* Set the ASCONF option */
+	#ifdef SCTP_AUTO_ASCONF
+	if (bound_to_default) {
+		int asconf;
+		
+		if (TRACE_BOOL(ANNOYING)) {
+			socklen_t sz;
+
+			sz = sizeof(asconf);
+			/* Read socket defaults */
+			CHECK_SYS(  getsockopt(sk, IPPROTO_SCTP, SCTP_AUTO_ASCONF, &asconf, &sz)  );
+			if (sz != sizeof(asconf))
+			{
+				TRACE_DEBUG(INFO, "Invalid size of socket option: %d / %d", sz, (socklen_t)sizeof(asconf));
+				return ENOTSUP;
+			}
+			fd_log_debug( "Def SCTP_AUTO_ASCONF value : %s", asconf ? "true" : "false");
+		}
+
+		asconf = 1;	/* allow automatic use of added or removed addresses in the association (for bound-all sockets) */
+		
+		/* Set the option to the socket */
+		CHECK_SYS(  setsockopt(sk, IPPROTO_SCTP, SCTP_AUTO_ASCONF, &asconf, sizeof(asconf))  );
+		
+		if (TRACE_BOOL(ANNOYING)) {
+			socklen_t sz = sizeof(asconf);
+			/* Check new values */
+			CHECK_SYS(  getsockopt(sk, IPPROTO_SCTP, SCTP_AUTO_ASCONF, &asconf, &sz)  );
+			fd_log_debug( "New SCTP_AUTO_ASCONF value : %s", asconf ? "true" : "false");
+		}
+	}
+	#else /* SCTP_AUTO_ASCONF */
+	TRACE_DEBUG(ANNOYING, "Skipping SCTP_AUTO_ASCONF");
+	#endif /* SCTP_AUTO_ASCONF */
+	
+	return 0;
+}
+
+/* Add addresses from a list to an array, with filter on the flags */
+static int add_addresses_from_list_mask(uint8_t ** array, size_t * size, int * addr_count, int target_family, uint16_t port, struct fd_list * list, uint32_t mask, uint32_t val)
+{
+	struct fd_list * li;
+	int to_add4 = 0;
+	int to_add6 = 0;
+	union {
+		uint8_t *buf;
+		sSA4	*sin;
+		sSA6	*sin6;
+	} ptr;
+	size_t sz;
+	
+	/* First, count the number of addresses to add */
+	for (li = list->next; li != list; li = li->next) {
+		struct fd_endpoint * ep = (struct fd_endpoint *) li;
+		
+		/* Do the flag match ? */
+		if ((val & mask) != (ep->flags & mask))
+			continue;
+		
+		if (ep->sa.sa_family == AF_INET) {
+			to_add4 ++;
+		} else {
+			to_add6 ++;
+		}
+	}
+	
+	if ((to_add4 + to_add6) == 0)
+		return 0; /* nothing to do */
+	
+	/* The size to add */
+	if (target_family == AF_INET) {
+		sz = to_add4 * sizeof(sSA4);
+	} else {
+		#ifndef SCTP_USE_MAPPED_ADDRESSES
+			sz = (to_add4 * sizeof(sSA4)) + (to_add6 * sizeof(sSA6));
+		#else /* SCTP_USE_MAPPED_ADDRESSES */
+			sz = (to_add4 + to_add6) * sizeof(sSA6);
+		#endif /* SCTP_USE_MAPPED_ADDRESSES */
+	}
+	
+	/* Now, (re)alloc the array to store the new addresses */
+	CHECK_MALLOC( *array = realloc(*array, *size + sz) );
+	
+	/* Finally, add the addresses */
+	for (li = list->next; li != list; li = li->next) {
+		struct fd_endpoint * ep = (struct fd_endpoint *) li;
+		
+		/* Skip v6 addresses for v4 socket */
+		if ((target_family == AF_INET) && (ep->sa.sa_family == AF_INET6))
+			continue;
+		
+		/* Are the flags matching ? */
+		if ((val & mask) != (ep->flags & mask))
+			continue;
+		
+		/* Size of the new SA we are adding (array may contain a mix of sockaddr_in and sockaddr_in6) */
+		#ifndef SCTP_USE_MAPPED_ADDRESSES
+		if (ep->sa.sa_family == AF_INET6)
+		#else /* SCTP_USE_MAPPED_ADDRESSES */
+		if (target_family == AF_INET6)
+		#endif /* SCTP_USE_MAPPED_ADDRESSES */
+			sz = sizeof(sSA6);
+		else
+			sz = sizeof(sSA4);
+		
+		/* Place where we add the new address */
+		ptr.buf = *array + *size; /* place of the new SA */
+		
+		/* Update other information */
+		*size += sz;
+		*addr_count += 1;
+		
+		/* And write the addr in the buffer */
+		if (sz == sizeof(sSA4)) {
+			memcpy(ptr.buf, &ep->sin, sz);
+			ptr.sin->sin_port = port;
+		} else {
+			if (ep->sa.sa_family == AF_INET) { /* We must map the address */ 
+				memset(ptr.buf, 0, sz);
+				ptr.sin6->sin6_family = AF_INET6;
+				IN6_ADDR_V4MAP( &ptr.sin6->sin6_addr.s6_addr, ep->sin.sin_addr.s_addr );
+			} else {
+				memcpy(ptr.sin6, &ep->sin6, sz);
+			}
+			ptr.sin6->sin6_port = port;
+		}
+	}
+	
+	return 0;
+}
+
+/* Create a socket server and bind it according to daemon s configuration */
+int fd_sctp_create_bind_server( int * sock, int family, struct fd_list * list, uint16_t port )
+{
+	int bind_default;
+	
+	TRACE_ENTRY("%p %i %p %hu", sock, family, list, port);
+	CHECK_PARAMS(sock);
+	
+	/* Create the socket */
+	CHECK_SYS( *sock = socket(family, SOCK_STREAM, IPPROTO_SCTP) );
+	
+	/* Set pre-binding socket options, including number of streams etc... */
+	CHECK_FCT( fd_setsockopt_prebind(*sock) );
+	
+	bind_default = (! list) || (FD_IS_LIST_EMPTY(list)) ;
+redo:
+	if ( bind_default ) {
+		/* Implicit endpoints : bind to default addresses */
+		union {
+			sSS  ss;
+			sSA  sa;
+			sSA4 sin;
+			sSA6 sin6;
+		} s;
+		
+		/* 0.0.0.0 and [::] are all zeros */
+		memset(&s, 0, sizeof(s));
+		
+		s.sa.sa_family = family;
+		
+		if (family == AF_INET)
+			s.sin.sin_port = htons(port);
+		else
+			s.sin6.sin6_port = htons(port);
+		
+		CHECK_SYS( bind(*sock, &s.sa, sSAlen(&s)) );
+		
+	} else {
+		/* Explicit endpoints to bind to from config */
+		
+		sSA * sar = NULL; /* array of addresses */
+		size_t sz = 0; /* size of the array */
+		int count = 0; /* number of sock addr in the array */
+		
+		/* Create the array of configured addresses */
+		CHECK_FCT( add_addresses_from_list_mask((void *)&sar, &sz, &count, family, htons(port), list, EP_FL_CONF, EP_FL_CONF) );
+		
+		if (!count) {
+			/* None of the addresses in the list came from configuration, we bind to default */
+			bind_default = 1;
+			goto redo;
+		}
+		
+		#if 0
+			union {
+				sSA	*sa;
+				uint8_t *buf;
+			} ptr;
+			int i;
+			ptr.sa = sar;
+			fd_log_debug("Calling sctp_bindx with the following address array:");
+			for (i = 0; i < count; i++) {
+				TRACE_sSA(FD_LOG_DEBUG, FULL, "    - ", ptr.sa, NI_NUMERICHOST | NI_NUMERICSERV, "" );
+				ptr.buf += (ptr.sa->sa_family == AF_INET) ? sizeof(sSA4) : sizeof(sSA6) ;
+			}
+		#endif
+		
+		/* Bind to this array */
+		CHECK_SYS(  sctp_bindx(*sock, sar, count, SCTP_BINDX_ADD_ADDR)  );
+		
+		/* We don't need sar anymore */
+		free(sar);
+	}
+	
+	/* Now, the server is bound, set remaining sockopt */
+	CHECK_FCT( fd_setsockopt_postbind(*sock, bind_default) );
+	
+	/* Debug: show all local listening addresses */
+	#if 0
+		sSA *sar;
+		union {
+			sSA	*sa;
+			uint8_t *buf;
+		} ptr;
+		int sz;
+		
+		CHECK_SYS(  sz = sctp_getladdrs(*sock, 0, &sar)  );
+		
+		fd_log_debug("SCTP server bound on :");
+		for (ptr.sa = sar; sz-- > 0; ptr.buf += (ptr.sa->sa_family == AF_INET) ? sizeof(sSA4) : sizeof(sSA6)) {
+			TRACE_sSA(FD_LOG_DEBUG, FULL, "    - ", ptr.sa, NI_NUMERICHOST | NI_NUMERICSERV, "" );
+		}
+		sctp_freeladdrs(sar);
+	#endif
+
+	return 0;
+}
+
+/* Allow clients connections on server sockets */
+int fd_sctp_listen( int sock )
+{
+	TRACE_ENTRY("%d", sock);
+	CHECK_SYS( listen(sock, 5) );
+	return 0;
+}
+
+/* Create a client socket and connect to remote server */
+int fd_sctp_client( int *sock, int no_ip6, uint16_t port, struct fd_list * list )
+{
+	int family;
+	union {
+		uint8_t *buf;
+		sSA	*sa;
+	} sar;
+	size_t size = 0;
+	int count = 0;
+	int ret;
+	
+	sar.buf = NULL;
+	
+	TRACE_ENTRY("%p %i %hu %p", sock, no_ip6, port, list);
+	CHECK_PARAMS( sock && list && (!FD_IS_LIST_EMPTY(list)) );
+	
+	if (no_ip6) {
+		family = AF_INET;
+	} else {
+		family = AF_INET6;
+	}
+	
+	/* Create the socket */
+	CHECK_SYS( *sock = socket(family, SOCK_STREAM, IPPROTO_SCTP) );
+	
+	/* Cleanup if we are cancelled */
+	pthread_cleanup_push(fd_cleanup_socket, sock);
+	
+	/* Set the socket options */
+	CHECK_FCT_DO( ret = fd_setsockopt_prebind(*sock), goto out );
+	
+	/* Create the array of addresses, add first the configured addresses, then the discovered, then the other ones */
+	CHECK_FCT_DO( ret = add_addresses_from_list_mask(&sar.buf, &size, &count, family, htons(port), list, EP_FL_CONF,              EP_FL_CONF	), goto out );
+	CHECK_FCT_DO( ret = add_addresses_from_list_mask(&sar.buf, &size, &count, family, htons(port), list, EP_FL_CONF | EP_FL_DISC, EP_FL_DISC	), goto out );
+	CHECK_FCT_DO( ret = add_addresses_from_list_mask(&sar.buf, &size, &count, family, htons(port), list, EP_FL_CONF | EP_FL_DISC, 0		), goto out );
+	
+	/* Try connecting */
+	LOG_A("Attempting SCTP connection (%d addresses attempted) ", count);
+		
+#if 0
+		/* Dump the SAs */
+		union {
+			uint8_t *buf;
+			sSA	*sa;
+			sSA4	*sin;
+			sSA6	*sin6;
+		} ptr;
+		int i;
+		ptr.buf = sar.buf;
+		for (i=0; i< count; i++) {
+			TRACE_sSA(FD_LOG_DEBUG, FULL, "  - ", ptr.sa, NI_NUMERICHOST | NI_NUMERICSERV, "" );
+			ptr.buf += (ptr.sa->sa_family == AF_INET) ? sizeof(sSA4) : sizeof(sSA6);
+		}
+#endif
+	
+	/* Bug in some Linux kernel, the sctp_connectx is not a cancellation point. To avoid blocking freeDiameter, we allow async cancel here */
+	pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
+#ifdef SCTP_CONNECTX_4_ARGS
+	ret = sctp_connectx(*sock, sar.sa, count, NULL);
+#else /* SCTP_CONNECTX_4_ARGS */
+	ret = sctp_connectx(*sock, sar.sa, count);
+#endif /* SCTP_CONNECTX_4_ARGS */
+	/* back to normal cancelation behabior */
+	pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL);
+	
+	if (ret < 0) {
+		ret = errno;
+		/* Some errors are expected, we log at different level */
+		LOG_A("sctp_connectx returned an error: %s", strerror(ret));
+		goto out;
+	}
+	
+	free(sar.buf); sar.buf = NULL;
+	
+	/* Set the remaining sockopts */
+	CHECK_FCT_DO( ret = fd_setsockopt_postbind(*sock, 1), 
+		{ 
+			CHECK_SYS_DO( shutdown(*sock, SHUT_RDWR), /* continue */ );
+		} );
+	
+out:
+	;
+	pthread_cleanup_pop(0);
+	
+	if (ret) {
+		if (*sock > 0) {
+			CHECK_SYS_DO( close(*sock), /* continue */ );
+			*sock = -1;
+		}
+		free(sar.buf);
+	}
+	return ret;
+}
+
+/* Retrieve streams information from a connected association -- optionally provide the primary address */
+int fd_sctp_get_str_info( int sock, uint16_t *in, uint16_t *out, sSS *primary )
+{
+	struct sctp_status status;
+	socklen_t sz = sizeof(status);
+	
+	TRACE_ENTRY("%d %p %p %p", sock, in, out, primary);
+	CHECK_PARAMS( (sock > 0) && in && out );
+	
+	/* Read the association parameters */
+	memset(&status, 0, sizeof(status));
+	CHECK_SYS(  getsockopt(sock, IPPROTO_SCTP, SCTP_STATUS, &status, &sz) );
+	if (sz != sizeof(status))
+	{
+		TRACE_DEBUG(INFO, "Invalid size of socket option: %d / %zd", sz, sizeof(status));
+		return ENOTSUP;
+	}
+#if 0
+		char sa_buf[sSA_DUMP_STRLEN];
+		fd_sa_sdump_numeric(sa_buf, &status.sstat_primary.spinfo_address);
+		fd_log_debug( "SCTP_STATUS : sstat_state                  : %i" , status.sstat_state);
+		fd_log_debug( "              sstat_rwnd  	          : %u" , status.sstat_rwnd);
+		fd_log_debug( "		     sstat_unackdata	          : %hu", status.sstat_unackdata);
+		fd_log_debug( "		     sstat_penddata 	          : %hu", status.sstat_penddata);
+		fd_log_debug( "		     sstat_instrms  	          : %hu", status.sstat_instrms);
+		fd_log_debug( "		     sstat_outstrms 	          : %hu", status.sstat_outstrms);
+		fd_log_debug( "		     sstat_fragmentation_point    : %u" , status.sstat_fragmentation_point);
+		fd_log_debug( "		     sstat_primary.spinfo_address : %s" , sa_buf);
+		fd_log_debug( "		     sstat_primary.spinfo_state   : %d" , status.sstat_primary.spinfo_state);
+		fd_log_debug( "		     sstat_primary.spinfo_cwnd    : %u" , status.sstat_primary.spinfo_cwnd);
+		fd_log_debug( "		     sstat_primary.spinfo_srtt    : %u" , status.sstat_primary.spinfo_srtt);
+		fd_log_debug( "		     sstat_primary.spinfo_rto     : %u" , status.sstat_primary.spinfo_rto);
+		fd_log_debug( "		     sstat_primary.spinfo_mtu     : %u" , status.sstat_primary.spinfo_mtu);
+#endif /* 0 */
+	
+	*in = status.sstat_instrms;
+	*out = status.sstat_outstrms;
+	
+	if (primary)
+		memcpy(primary, &status.sstat_primary.spinfo_address, sizeof(sSS));
+	
+	return 0;
+}
+
+/* Get the list of remote endpoints of the socket */
+int fd_sctp_get_remote_ep(int sock, struct fd_list * list)
+{
+	union {
+		sSA	*sa;
+		uint8_t	*buf;
+	} ptr;
+	
+	sSA * data = NULL;
+	int count;
+	
+	TRACE_ENTRY("%d %p", sock, list);
+	CHECK_PARAMS(list);
+	
+	/* Read the list on the socket */
+	CHECK_SYS( count = sctp_getpaddrs(sock, 0, &data)  );
+	ptr.sa = data;
+	
+	while (count) {
+		socklen_t sl;
+		switch (ptr.sa->sa_family) {
+			case AF_INET:	sl = sizeof(sSA4); break;
+			case AF_INET6:	sl = sizeof(sSA6); break;
+			default:
+				TRACE_DEBUG(INFO, "Unknown address family returned in sctp_getpaddrs: %d, skip", ptr.sa->sa_family);
+				/* There is a bug in current Linux kernel: http://www.spinics.net/lists/linux-sctp/msg00760.html */
+				goto stop;
+		}
+				
+		CHECK_FCT( fd_ep_add_merge( list, ptr.sa, sl, EP_FL_LL ) );
+		ptr.buf += sl;
+		count --;
+	}
+stop:	
+	/* Free the list */
+	sctp_freepaddrs(data);
+	
+	/* Now get the primary address, the add function will take care of merging with existing entry */
+	{
+		 
+		struct sctp_status status;
+		socklen_t sz = sizeof(status);
+		int ret;
+		
+		memset(&status, 0, sizeof(status));
+		/* Attempt to use SCTP_STATUS message to retrieve the primary address */
+		CHECK_SYS_DO( ret = getsockopt(sock, IPPROTO_SCTP, SCTP_STATUS, &status, &sz), /* continue */);
+		if (sz != sizeof(status))
+			ret = -1;
+		sz = sizeof(sSS);
+		if (ret < 0)
+		{
+			/* Fallback to getsockname -- not recommended by draft-ietf-tsvwg-sctpsocket-19#section-7.4 */
+			CHECK_SYS(getpeername(sock, (sSA *)&status.sstat_primary.spinfo_address, &sz));
+		}
+			
+		CHECK_FCT( fd_ep_add_merge( list, (sSA *)&status.sstat_primary.spinfo_address, sz, EP_FL_PRIMARY ) );
+	}
+	
+	/* Done! */
+	return 0;
+}
+
+/* Send a vector over a specified stream */
+ssize_t fd_sctp_sendstrv(struct cnxctx * conn, uint16_t strid, const struct iovec *iov, int iovcnt)
+{
+	struct msghdr mhdr;
+	struct cmsghdr 		*hdr;
+#ifdef OLD_SCTP_SOCKET_API
+	struct sctp_sndrcvinfo	*sndrcv;
+	uint8_t anci[CMSG_SPACE(sizeof(struct sctp_sndrcvinfo))];
+#else /* OLD_SCTP_SOCKET_API */
+	struct sctp_sndinfo 	*sndinf;
+	uint8_t anci[CMSG_SPACE(sizeof(struct sctp_sndinfo))];	
+#endif /* OLD_SCTP_SOCKET_API */
+	ssize_t ret;
+	struct timespec ts, now;
+	
+	TRACE_ENTRY("%p %hu %p %d", conn, strid, iov, iovcnt);
+	CHECK_PARAMS_DO(conn && iov && iovcnt, { errno = EINVAL; return -1; } );
+	CHECK_SYS_DO(  clock_gettime(CLOCK_REALTIME, &ts), return -1 );
+	
+	memset(&mhdr, 0, sizeof(mhdr));
+	memset(&anci, 0, sizeof(anci));
+	
+	/* Anciliary data: specify SCTP stream */
+	hdr = (struct cmsghdr *)anci;
+	hdr->cmsg_len   = sizeof(anci);
+	hdr->cmsg_level = IPPROTO_SCTP;
+#ifdef OLD_SCTP_SOCKET_API
+	hdr->cmsg_type  = SCTP_SNDRCV;
+	sndrcv = (struct sctp_sndrcvinfo *)CMSG_DATA(hdr);
+	sndrcv->sinfo_stream = strid;
+#else /* OLD_SCTP_SOCKET_API */
+	hdr->cmsg_type  = SCTP_SNDINFO;
+	sndinf = (struct sctp_sndinfo *)CMSG_DATA(hdr);
+	sndinf->snd_sid = strid;
+#endif /* OLD_SCTP_SOCKET_API */
+	/* note : we could store other data also, for example in .sinfo_ppid for remote peer or in .sinfo_context for errors. */
+	
+	/* We don't use mhdr.msg_name here; it could be used to specify an address different from the primary */
+	
+	mhdr.msg_iov    = (struct iovec *)iov;
+	mhdr.msg_iovlen = iovcnt;
+	
+	mhdr.msg_control    = anci;
+	mhdr.msg_controllen = sizeof(anci);
+	
+	TRACE_DEBUG(FULL, "Sending %d chunks of data (first:%zdb) on stream %hu of socket %d", iovcnt, iov[0].iov_len, strid, conn->cc_socket);
+again:	
+	ret = sendmsg(conn->cc_socket, &mhdr, 0);
+	/* Handle special case of timeout */
+	if ((ret < 0) && ((errno == EAGAIN) || (errno == EINTR))) {
+		pthread_testcancel();
+		/* Check how much time we were blocked for this sending. */
+		CHECK_SYS_DO(  clock_gettime(CLOCK_REALTIME, &now), return -1 );
+		if ( ((now.tv_sec - ts.tv_sec) * 1000 + ((now.tv_nsec - ts.tv_nsec) / 1000000L)) > MAX_HOTL_BLOCKING_TIME) {
+			LOG_D("Unable to send any data for %dms, closing the connection", MAX_HOTL_BLOCKING_TIME);
+		} else if (! fd_cnx_teststate(conn, CC_STATUS_CLOSING )) {
+			goto again; /* don't care, just ignore */
+		}
+		
+		/* propagate the error */
+		errno = -ret;
+		ret = -1;
+	}
+	
+	CHECK_SYS_DO( ret, ); /* for tracing error only */
+	
+	return ret;
+}
+
+/* Receive the next data from the socket, or next notification */
+int fd_sctp_recvmeta(struct cnxctx * conn, uint16_t * strid, uint8_t ** buf, size_t * len, int *event)
+{
+	ssize_t 		 ret = 0;
+	struct msghdr 		 mhdr;
+	char   			 ancidata[ CMSG_BUF_LEN ];
+	struct iovec 		 iov;
+	uint8_t			*data = NULL;
+	size_t 			 bufsz = 0, datasize = 0;
+	size_t			 mempagesz = sysconf(_SC_PAGESIZE); /* We alloc buffer by memory pages for efficiency */
+	int 			 timedout = 0;
+	
+	TRACE_ENTRY("%p %p %p %p %p", conn, strid, buf, len, event);
+	CHECK_PARAMS( conn && buf && len && event );
+	
+	/* Cleanup out parameters */
+	*buf = NULL;
+	*len = 0;
+	*event = 0;
+	
+	/* Prepare header for receiving message */
+	memset(&mhdr, 0, sizeof(mhdr));
+	mhdr.msg_iov    = &iov;
+	mhdr.msg_iovlen = 1;
+	mhdr.msg_control    = &ancidata;
+	mhdr.msg_controllen = sizeof(ancidata);
+	
+next_message:
+	datasize = 0;
+	
+	/* We will loop while all data is not received. */
+incomplete:
+	while (datasize >= bufsz ) {
+		/* The buffer is full, enlarge it */
+		bufsz += mempagesz;
+		CHECK_MALLOC( data = realloc(data, bufsz ) );
+	}
+	/* the new data will be received following the preceding */
+	memset(&iov,  0, sizeof(iov));
+	iov.iov_base = data + datasize ;
+	iov.iov_len  = bufsz - datasize;
+
+	/* Receive data from the socket */
+again:
+	pthread_cleanup_push(free, data);
+	ret = recvmsg(conn->cc_socket, &mhdr, 0);
+	pthread_testcancel();
+	pthread_cleanup_pop(0);
+	
+	/* First, handle timeouts (same as fd_cnx_s_recv) */
+	if ((ret < 0) && ((errno == EAGAIN) || (errno == EINTR))) {
+		if (! fd_cnx_teststate(conn, CC_STATUS_CLOSING ))
+			goto again; /* don't care, just ignore */
+		if (!timedout) {
+			timedout ++; /* allow for one timeout while closing */
+			goto again;
+		}
+		/* fallback to normal handling */
+	}
+	
+	/* Handle errors */
+	if (ret <= 0) { /* Socket timedout, closed, or an error occurred */
+		CHECK_SYS_DO(ret, /* to log in case of error */);
+		free(data);
+		*event = FDEVP_CNX_ERROR;
+		return 0;
+	}
+	
+	/* Update the size of data we received */
+	datasize += ret;
+
+	/* SCTP provides an indication when we received a full record; loop if it is not the case */
+	if ( ! (mhdr.msg_flags & MSG_EOR) ) {
+		goto incomplete;
+	}
+	
+	/* Handle the case where the data received is a notification */
+	if (mhdr.msg_flags & MSG_NOTIFICATION) {
+		union sctp_notification * notif = (union sctp_notification *) data;
+		
+		TRACE_DEBUG(FULL, "Received %zdb data of notification on socket %d", datasize, conn->cc_socket);
+	
+		switch (notif->sn_header.sn_type) {
+			
+			case SCTP_ASSOC_CHANGE: /* We should not receive these as we did not subscribe for it */
+				TRACE_DEBUG(FULL, "Received SCTP_ASSOC_CHANGE notification");
+				TRACE_DEBUG(ANNOYING, "    state : %hu", notif->sn_assoc_change.sac_state);
+				TRACE_DEBUG(ANNOYING, "    error : %hu", notif->sn_assoc_change.sac_error);
+				TRACE_DEBUG(ANNOYING, "    instr : %hu", notif->sn_assoc_change.sac_inbound_streams);
+				TRACE_DEBUG(ANNOYING, "   outstr : %hu", notif->sn_assoc_change.sac_outbound_streams);
+				
+				*event = FDEVP_CNX_EP_CHANGE;
+				break;
+	
+			case SCTP_PEER_ADDR_CHANGE:
+				TRACE_DEBUG(FULL, "Received SCTP_PEER_ADDR_CHANGE notification");
+				/* TRACE_sSA(FD_LOG_DEBUG, ANNOYING, "    intf_change : ", &(notif->sn_paddr_change.spc_aaddr), NI_NUMERICHOST | NI_NUMERICSERV, "" ); */
+				TRACE_DEBUG(ANNOYING, "          state : %d", notif->sn_paddr_change.spc_state);
+				TRACE_DEBUG(ANNOYING, "          error : %d", notif->sn_paddr_change.spc_error);
+				
+				*event = FDEVP_CNX_EP_CHANGE;
+				break;
+	
+			case SCTP_REMOTE_ERROR:
+				TRACE_DEBUG(FULL, "Received SCTP_REMOTE_ERROR notification");
+				TRACE_DEBUG(ANNOYING, "    err : %hu", ntohs(notif->sn_remote_error.sre_error));
+				TRACE_DEBUG(ANNOYING, "    len : %hu", ntohs(notif->sn_remote_error.sre_length));
+				
+				*event = FDEVP_CNX_ERROR;
+				break;
+	
+#ifdef OLD_SCTP_SOCKET_API
+			case SCTP_SEND_FAILED:
+				TRACE_DEBUG(FULL, "Received SCTP_SEND_FAILED notification");
+				TRACE_DEBUG(ANNOYING, "    len : %hu", notif->sn_send_failed.ssf_length);
+				TRACE_DEBUG(ANNOYING, "    err : %d",  notif->sn_send_failed.ssf_error);
+				
+				*event = FDEVP_CNX_ERROR;
+				break;
+#else /*  OLD_SCTP_SOCKET_API */
+			case SCTP_SEND_FAILED_EVENT:
+				TRACE_DEBUG(FULL, "Received SCTP_SEND_FAILED_EVENT notification");
+				*event = FDEVP_CNX_ERROR;
+				break;
+#endif	/*  OLD_SCTP_SOCKET_API */		
+			case SCTP_SHUTDOWN_EVENT:
+				TRACE_DEBUG(FULL, "Received SCTP_SHUTDOWN_EVENT notification");
+				
+				*event = FDEVP_CNX_SHUTDOWN;
+				break;
+				
+#ifndef OLD_SCTP_SOCKET_API
+			case SCTP_NOTIFICATIONS_STOPPED_EVENT:
+				TRACE_DEBUG(INFO, "Received SCTP_NOTIFICATIONS_STOPPED_EVENT notification, marking the association in error state");
+				*event = FDEVP_CNX_ERROR;
+				break;
+#endif	/*  OLD_SCTP_SOCKET_API */		
+			
+			default:	
+				TRACE_DEBUG(FULL, "Received unknown notification %d, ignored", notif->sn_header.sn_type);
+				goto next_message;
+		}
+		
+		free(data);
+		return 0;
+	}
+	
+	/* From this point, we have received a message */
+	*event = FDEVP_CNX_MSG_RECV;
+	*buf = data;
+	*len = datasize;
+	
+	if (strid) {
+		struct cmsghdr 		*hdr;
+#ifdef OLD_SCTP_SOCKET_API
+		struct sctp_sndrcvinfo	*sndrcv;
+#else /*  OLD_SCTP_SOCKET_API */
+		struct sctp_rcvinfo	*rcvinf;
+#endif /*  OLD_SCTP_SOCKET_API */
+		
+		/* Handle the anciliary data */
+		for (hdr = CMSG_FIRSTHDR(&mhdr); hdr; hdr = CMSG_NXTHDR(&mhdr, hdr)) {
+
+			/* We deal only with anciliary data at SCTP level */
+			if (hdr->cmsg_level != IPPROTO_SCTP) {
+				TRACE_DEBUG(FULL, "Received some anciliary data at level %d, skipped", hdr->cmsg_level);
+				continue;
+			}
+			
+#ifdef OLD_SCTP_SOCKET_API
+			/* Also only interested in SCTP_SNDRCV message for the moment */
+			if (hdr->cmsg_type != SCTP_SNDRCV) {
+				TRACE_DEBUG(FULL, "Anciliary block IPPROTO_SCTP / %d, skipped", hdr->cmsg_type);
+				continue;
+			}
+			
+			sndrcv = (struct sctp_sndrcvinfo *) CMSG_DATA(hdr);
+			if (TRACE_BOOL(ANNOYING)) {
+				fd_log_debug( "Anciliary block IPPROTO_SCTP / SCTP_SNDRCV");
+				fd_log_debug( "    sinfo_stream    : %hu", sndrcv->sinfo_stream);
+				fd_log_debug( "    sinfo_ssn       : %hu", sndrcv->sinfo_ssn);
+				fd_log_debug( "    sinfo_flags     : %hu", sndrcv->sinfo_flags);
+				/* fd_log_debug( "    sinfo_pr_policy : %hu", sndrcv->sinfo_pr_policy); */
+				fd_log_debug( "    sinfo_ppid      : %u" , sndrcv->sinfo_ppid);
+				fd_log_debug( "    sinfo_context   : %u" , sndrcv->sinfo_context);
+				/* fd_log_debug( "    sinfo_pr_value  : %u" , sndrcv->sinfo_pr_value); */
+				fd_log_debug( "    sinfo_tsn       : %u" , sndrcv->sinfo_tsn);
+				fd_log_debug( "    sinfo_cumtsn    : %u" , sndrcv->sinfo_cumtsn);
+			}
+
+			*strid = sndrcv->sinfo_stream;
+#else /*  OLD_SCTP_SOCKET_API */
+			/* Also only interested in SCTP_RCVINFO message for the moment */
+			if (hdr->cmsg_type != SCTP_RCVINFO) {
+				TRACE_DEBUG(FULL, "Anciliary block IPPROTO_SCTP / %d, skipped", hdr->cmsg_type);
+				continue;
+			}
+			
+			rcvinf = (struct sctp_rcvinfo *) CMSG_DATA(hdr);
+
+			*strid = rcvinf->rcv_sid;
+#endif /*  OLD_SCTP_SOCKET_API */
+			
+			
+		}
+		TRACE_DEBUG(FULL, "Received %zdb data on socket %d, stream %hu", datasize, conn->cc_socket, *strid);
+	} else {
+		TRACE_DEBUG(FULL, "Received %zdb data on socket %d (stream ignored)", datasize, conn->cc_socket);
+	}
+	
+	return 0;
+}
diff --git a/libfdcore/sctp3436.c b/libfdcore/sctp3436.c
new file mode 100644
index 0000000..f45a1ab
--- /dev/null
+++ b/libfdcore/sctp3436.c
@@ -0,0 +1,793 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@freediameter.net>							 *
+*													 *
+* Copyright (c) 2013, 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.								 *
+*********************************************************************************************************/
+
+/* This file contains code for TLS over multi-stream SCTP wrapper implementation (GnuTLS does not support this) */
+/* See http://aaa.koganei.wide.ad.jp/blogs/index.php/waaad/2008/08/18/tls-over-sctp for history */
+
+#include "fdcore-internal.h"
+#include "cnxctx.h"
+
+#include <netinet/sctp.h>
+#include <sys/uio.h>
+
+/*
+
+Architecture of this wrapper:
+ - we have several fifo queues (1 per stream pairs).
+ GnuTLS is configured to use custom push / pull functions:
+ - the pull function retrieves the data from the fifo queue corresponding to a stream #.
+ - the push function sends the data on a certain stream.
+ We also have a demux thread that reads the socket and store received data in the appropriate fifo
+ 
+ We have one gnutls_session per stream pair, and as many threads that read the gnutls records and save incoming data to the target queue.
+ 
+This complexity is required because we cannot read a socket for a given stream only; we can only get the next message and find its stream.
+*/
+
+/* Note that this mechanism is replaced by DTLS in RFC6733 */
+
+/*************************************************************/
+/*                      threads                              */
+/*************************************************************/
+
+/* Demux received data and store in the appropriate fifo */
+static void * demuxer(void * arg)
+{
+	struct cnxctx * conn = arg;
+	uint8_t * buf;
+	size_t    bufsz;
+	int	  event;
+	uint16_t  strid;
+	
+	TRACE_ENTRY("%p", arg);
+	CHECK_PARAMS_DO(conn && (conn->cc_socket > 0), goto out);
+	
+	/* Set the thread name */
+	{
+		char buf[48];
+		snprintf(buf, sizeof(buf), "Demuxer (%d:%s)", conn->cc_socket, conn->cc_remid);
+		fd_log_threadname ( buf );
+	}
+	
+	ASSERT( conn->cc_proto == IPPROTO_SCTP );
+	ASSERT( fd_cnx_target_queue(conn) );
+	ASSERT( conn->cc_sctp3436_data.array );
+	
+	do {
+		CHECK_FCT_DO( fd_sctp_recvmeta(conn, &strid, &buf, &bufsz, &event), goto fatal );
+		switch (event) {
+			case FDEVP_CNX_MSG_RECV:
+				/* Demux this message to the appropriate fifo, another thread will pull, gnutls process, and send to target queue */
+				if (strid < conn->cc_sctp_para.pairs) {
+					CHECK_FCT_DO(fd_event_send(conn->cc_sctp3436_data.array[strid].raw_recv, event, bufsz, buf), goto fatal );
+				} else {
+					TRACE_DEBUG(INFO, "Received packet (%zd bytes) on out-of-range stream #%d from %s, discarded.", bufsz, strid, conn->cc_remid);
+					free(buf);
+				}
+				break;
+				
+			case FDEVP_CNX_EP_CHANGE:
+				/* Send this event to the target queue */
+				CHECK_FCT_DO( fd_event_send( fd_cnx_target_queue(conn), event, bufsz, buf), goto fatal );
+				break;
+			
+			case FDEVP_CNX_ERROR:
+				goto out;
+				
+			case FDEVP_CNX_SHUTDOWN:
+				/* Just ignore the notification for now, we will get another error later anyway */
+				continue;
+				
+			default:
+				goto fatal;
+		}
+		
+	} while (conn->cc_loop);
+	
+out:
+	/* Signal termination of the connection to all decipher threads */
+	for (strid = 0; strid < conn->cc_sctp_para.pairs; strid++) {
+		if (conn->cc_sctp3436_data.array[strid].raw_recv) {
+			CHECK_FCT_DO(fd_event_send(conn->cc_sctp3436_data.array[strid].raw_recv, FDEVP_CNX_ERROR, 0, NULL), goto fatal );
+		}
+	}
+	fd_cnx_markerror(conn);
+	TRACE_DEBUG(FULL, "Thread terminated");	
+	return NULL;
+	
+fatal:
+	/* An unrecoverable error occurred, stop the daemon */
+	CHECK_FCT_DO(fd_core_shutdown(), );
+	goto out;
+}
+
+/* Decrypt the data received in this stream pair and store it in the target queue */
+static void * decipher(void * arg)
+{
+	struct sctp3436_ctx * ctx = arg;
+	struct cnxctx 	 *cnx;
+	
+	TRACE_ENTRY("%p", arg);
+	CHECK_PARAMS_DO(ctx && ctx->raw_recv && ctx->parent, goto error);
+	cnx = ctx->parent;
+	ASSERT( fd_cnx_target_queue(cnx) );
+	
+	/* Set the thread name */
+	{
+		char buf[48];
+		snprintf(buf, sizeof(buf), "Decipher (%hu@%d:%s)", ctx->strid, cnx->cc_socket, cnx->cc_remid);
+		fd_log_threadname ( buf );
+	}
+	
+	/* The next function loops while there is no error */
+	CHECK_FCT_DO(fd_tls_rcvthr_core(cnx, ctx->strid ? ctx->session : cnx->cc_tls_para.session), /* continue */);
+error:
+	fd_cnx_markerror(cnx);
+	TRACE_DEBUG(FULL, "Thread terminated");	
+	return NULL;
+}
+
+/*************************************************************/
+/*                     push / pull                           */
+/*************************************************************/
+
+#ifdef GNUTLS_VERSION_300
+/* Check if data is available for gnutls on a given context */
+static int sctp3436_pull_timeout(gnutls_transport_ptr_t tr, unsigned int ms)
+{
+	struct sctp3436_ctx * ctx = (struct sctp3436_ctx *) tr;
+	struct timespec tsstore, *ts = NULL;
+	int ret;
+	
+	TRACE_ENTRY("%p %d", tr, ms);
+	
+	if (ctx->partial.buf)
+		return 1; /* data is already available for pull */
+	
+	if (ms) {
+		CHECK_SYS_DO(  clock_gettime(CLOCK_REALTIME, &tsstore),  return -1  );
+		tsstore.tv_nsec += (long)ms * 1000000;
+		tsstore.tv_sec += tsstore.tv_nsec / 1000000000L;
+		tsstore.tv_nsec %= 1000000000L;
+		ts = &tsstore;
+	}
+	
+	ret = fd_fifo_select ( ctx->raw_recv, ts );
+	if (ret < 0) {
+		errno = -ret;
+		ret = -1;
+	}
+		
+	return ret;
+}
+#endif /* GNUTLS_VERSION_300 */
+
+/* Send data over the connection, called by gnutls */
+#ifndef GNUTLS_VERSION_212
+static ssize_t sctp3436_push(gnutls_transport_ptr_t tr, const void * data, size_t len)
+{
+	struct sctp3436_ctx * ctx = (struct sctp3436_ctx *) tr;
+	struct iovec iov;
+	
+	TRACE_ENTRY("%p %p %zd", tr, data, len);
+	CHECK_PARAMS_DO( tr && data, { errno = EINVAL; return -1; } );
+	
+	iov.iov_base = (void *)data;
+	iov.iov_len  = len;
+	
+	return fd_sctp_sendstrv(ctx->parent, ctx->strid, &iov, 1);
+}
+#else /*  GNUTLS_VERSION_212 */
+static ssize_t sctp3436_pushv(gnutls_transport_ptr_t tr, const giovec_t * iov, int iovcnt)
+{
+	struct sctp3436_ctx * ctx = (struct sctp3436_ctx *) tr;
+	
+	TRACE_ENTRY("%p %p %d", tr, iov, iovcnt);
+	CHECK_PARAMS_DO( tr && iov, { errno = EINVAL; return -1; } );
+	
+	return fd_sctp_sendstrv(ctx->parent, ctx->strid, (const struct iovec *)iov, iovcnt);
+}
+#endif /*  GNUTLS_VERSION_212 */
+
+/* Retrieve data received on a stream and already demultiplexed */
+static ssize_t sctp3436_pull(gnutls_transport_ptr_t tr, void * buf, size_t len)
+{
+	struct sctp3436_ctx * ctx = (struct sctp3436_ctx *) tr;
+	size_t pulled = 0;
+	int emptied;
+	
+	TRACE_ENTRY("%p %p %zd", tr, buf, len);
+	CHECK_PARAMS_DO( tr && buf, { errno = EINVAL; goto error; } );
+	
+	/* If we don't have data available now, pull new message from the fifo -- this is blocking (until the queue is destroyed) */
+	if (!ctx->partial.buf) {
+		int ev;
+		CHECK_FCT_DO( errno = fd_event_get(ctx->raw_recv, &ev, &ctx->partial.bufsz, (void *)&ctx->partial.buf), goto error );
+		if (ev == FDEVP_CNX_ERROR) {
+			/* Documentations says to return 0 on connection closed, but it does hang within gnutls_handshake */
+			return -1;
+		}
+	}
+		
+	pulled = ctx->partial.bufsz - ctx->partial.offset;
+	if (pulled <= len) {
+		emptied = 1;
+	} else {
+		/* limit to the capacity of destination buffer */
+		emptied = 0;
+		pulled = len;
+	}
+
+	/* Store the data in the destination buffer */
+	memcpy(buf, ctx->partial.buf + ctx->partial.offset, pulled);
+
+	/* Free the buffer if we read all its content, and reset the partial structure */
+	if (emptied) {
+		free(ctx->partial.buf);
+		memset(&ctx->partial, 0, sizeof(ctx->partial));
+	} else {
+		ctx->partial.offset += pulled;
+	}
+
+	/* We are done */
+	return pulled;
+	
+error:
+	gnutls_transport_set_errno (ctx->session, errno);
+	return -1;
+}
+
+/* Set the parameters of a session to use the appropriate fifo and stream information */
+#ifndef GNUTLS_VERSION_300
+GCC_DIAG_OFF("-Wdeprecated-declarations")
+#endif /* !GNUTLS_VERSION_300 */
+static void set_sess_transport(gnutls_session_t session, struct sctp3436_ctx *ctx)
+{
+	/* Set the transport pointer passed to push & pull callbacks */
+	GNUTLS_TRACE( gnutls_transport_set_ptr( session, (gnutls_transport_ptr_t) ctx ) );
+	
+	/* Reset the low water value, since we don't use sockets */
+#ifndef GNUTLS_VERSION_300
+	/* starting version 2.12, this call is not needed */
+	GNUTLS_TRACE( gnutls_transport_set_lowat( session, 0 ) );
+#else  /* GNUTLS_VERSION_300 */
+	/* but in 3.0 we have to provide the pull_timeout callback */
+	GNUTLS_TRACE( gnutls_transport_set_pull_timeout_function( session, sctp3436_pull_timeout ) );
+#endif /* GNUTLS_VERSION_300 */
+	
+	/* Set the push and pull callbacks */
+	GNUTLS_TRACE( gnutls_transport_set_pull_function(session, sctp3436_pull) );
+#ifndef GNUTLS_VERSION_212
+	GNUTLS_TRACE( gnutls_transport_set_push_function(session, sctp3436_push) );
+#else /* GNUTLS_VERSION_212 */
+	GNUTLS_TRACE( gnutls_transport_set_vec_push_function(session, sctp3436_pushv) );
+#endif /* GNUTLS_VERSION_212 */
+
+	return;
+}
+#ifndef GNUTLS_VERSION_300
+GCC_DIAG_ON("-Wdeprecated-declarations")
+#endif /* !GNUTLS_VERSION_300 */
+
+/*************************************************************/
+/*               Session resuming support                    */
+/*************************************************************/
+
+struct sr_store {
+	struct fd_list	 list;	/* list of sr_data, ordered by key.size then key.data */
+	pthread_rwlock_t lock;
+	struct cnxctx   *parent;
+	/* Add another list to chain in a global list to implement a garbage collector on sessions -- TODO if needed */
+};
+
+/* Saved master session data for resuming sessions */
+struct sr_data {
+	struct fd_list	chain;
+	gnutls_datum_t	key;
+	gnutls_datum_t 	data;
+};
+
+/* Initialize the store area for a connection */
+static int store_init(struct cnxctx * conn)
+{
+	TRACE_ENTRY("%p", conn);
+	CHECK_PARAMS( conn && !conn->cc_sctp3436_data.sess_store );
+	
+	CHECK_MALLOC( conn->cc_sctp3436_data.sess_store = malloc(sizeof(struct sr_store)) );
+	memset(conn->cc_sctp3436_data.sess_store, 0, sizeof(struct sr_store));
+	
+	fd_list_init(&conn->cc_sctp3436_data.sess_store->list, NULL);
+	CHECK_POSIX( pthread_rwlock_init(&conn->cc_sctp3436_data.sess_store->lock, NULL) );
+	conn->cc_sctp3436_data.sess_store->parent = conn;
+	
+	return 0;
+}
+
+/* Destroy the store area for a connection, and all its content */
+static void store_destroy(struct cnxctx * conn)
+{
+	/* Del all list entries */
+	TRACE_ENTRY("%p", conn);
+	CHECK_PARAMS_DO( conn, return );
+	
+	if (!conn->cc_sctp3436_data.sess_store)
+		return;
+	
+	CHECK_POSIX_DO( pthread_rwlock_destroy(&conn->cc_sctp3436_data.sess_store->lock), /* continue */ );
+	
+	while (!FD_IS_LIST_EMPTY(&conn->cc_sctp3436_data.sess_store->list)) {
+		struct sr_data * sr = (struct sr_data *) conn->cc_sctp3436_data.sess_store->list.next;
+		fd_list_unlink( &sr->chain );
+		free(sr->key.data);
+		free(sr->data.data);
+		free(sr);
+	}
+	
+	free(conn->cc_sctp3436_data.sess_store);
+	conn->cc_sctp3436_data.sess_store = NULL;
+	return;
+}
+
+/* Search the position (or next if not found) of a key in a store */
+static struct fd_list * find_or_next(struct sr_store * sto, gnutls_datum_t key, int * match)
+{
+	struct fd_list * ret;
+	*match = 0;
+	
+	for (ret = sto->list.next; ret != &sto->list; ret = ret->next) {
+		int cmp = 0;
+		struct sr_data * sr = (struct sr_data *)ret;
+		
+		cmp = fd_os_cmp(key.data, key.size, sr->key.data, sr->key.size);
+		if (cmp > 0)
+			continue;
+		
+		if (cmp == 0)
+			*match = 1;
+		
+		break;
+	}
+	
+	return ret;
+}
+
+/* Callbacks for the TLS server side of the connection, called during gnutls_handshake */
+static int sr_store (void *dbf, gnutls_datum_t key, gnutls_datum_t data)
+{
+	struct sr_store * sto = (struct sr_store *)dbf;
+	struct fd_list * li;
+	struct sr_data * sr;
+	int match = 0;
+	int ret = 0;
+	
+	TRACE_DEBUG( GNUTLS_DBG_LEVEL, "GNUTLS Callback: %s", __PRETTY_FUNCTION__ );
+	CHECK_PARAMS_DO( sto && key.data && data.data, return -1 );
+	
+	CHECK_POSIX_DO( pthread_rwlock_wrlock(&sto->lock), return -1 );
+	TRACE_BUFFER(FD_LOG_DEBUG, GNUTLS_DBG_LEVEL, "Session store [key ", key.data, key.size, "]");
+	
+	li = find_or_next(sto, key, &match);
+	if (match) {
+		sr = (struct sr_data *)li;
+		
+		/* Check the data is the same */
+		if ((data.size != sr->data.size) || memcmp(data.data, sr->data.data, data.size)) {
+			TRACE_DEBUG(INFO, "GnuTLS tried to store a session with same key and different data!");
+			TRACE_BUFFER(FD_LOG_DEBUG, INFO, "Session store [key ", key.data, key.size, "]");
+			TRACE_BUFFER(FD_LOG_DEBUG, INFO, "  -- old data [", sr->data.data, sr->data.size, "]");
+			TRACE_BUFFER(FD_LOG_DEBUG, INFO, "  -- new data [", data.data, data.size, "]");
+			
+			ret = -1;
+		} else {
+			TRACE_DEBUG(GNUTLS_DBG_LEVEL, "GnuTLS tried to store a session with same key and same data, skipped.");
+		}
+		goto out;
+	}
+	
+	/* Create a new entry */
+	CHECK_MALLOC_DO( sr = malloc(sizeof(struct sr_data)), { ret = -1; goto out; } );
+	memset(sr, 0, sizeof(struct sr_data));
+
+	fd_list_init(&sr->chain, sr);
+
+	CHECK_MALLOC_DO( sr->key.data = malloc(key.size), { ret = -1; goto out; } );
+	sr->key.size = key.size;
+	memcpy(sr->key.data, key.data, key.size);
+
+	CHECK_MALLOC_DO( sr->data.data = malloc(data.size), { ret = -1; goto out; } );
+	sr->data.size = data.size;
+	memcpy(sr->data.data, data.data, data.size);
+	
+	/* Save this new entry in the list, we are done */
+	fd_list_insert_before(li, &sr->chain);
+
+out:	
+	CHECK_POSIX_DO( pthread_rwlock_unlock(&sto->lock), return -1 );
+	return ret;
+}
+
+static int sr_remove (void *dbf, gnutls_datum_t key)
+{
+	struct sr_store * sto = (struct sr_store *)dbf;
+	struct fd_list * li;
+	struct sr_data * sr;
+	int match = 0;
+	int ret = 0;
+	
+	TRACE_DEBUG( GNUTLS_DBG_LEVEL, "GNUTLS Callback: %s", __PRETTY_FUNCTION__ );
+	CHECK_PARAMS_DO( sto && key.data, return -1 );
+	
+	CHECK_POSIX_DO( pthread_rwlock_wrlock(&sto->lock), return -1 );
+	TRACE_BUFFER(FD_LOG_DEBUG, GNUTLS_DBG_LEVEL, "Session delete [key ", key.data, key.size, "]");
+	
+	li = find_or_next(sto, key, &match);
+	if (match) {
+		sr = (struct sr_data *)li;
+		
+		/* Destroy this data */
+		fd_list_unlink(li);
+		free(sr->key.data);
+		free(sr->data.data);
+		free(sr);
+	} else {
+		/* It was not found */
+		ret = -1;
+	}
+	
+	CHECK_POSIX_DO( pthread_rwlock_unlock(&sto->lock), return -1 );
+	return ret;
+}
+
+static gnutls_datum_t sr_fetch (void *dbf, gnutls_datum_t key)
+{
+	struct sr_store * sto = (struct sr_store *)dbf;
+	struct fd_list * li;
+	struct sr_data * sr;
+	int match = 0;
+	gnutls_datum_t res = { NULL, 0 };
+	gnutls_datum_t error = { NULL, 0 };
+
+	TRACE_DEBUG( GNUTLS_DBG_LEVEL, "GNUTLS Callback: %s", __PRETTY_FUNCTION__ );
+	CHECK_PARAMS_DO( sto && key.data, return error );
+
+	CHECK_POSIX_DO( pthread_rwlock_rdlock(&sto->lock), return error );
+	TRACE_BUFFER(FD_LOG_DEBUG, GNUTLS_DBG_LEVEL, "Session fetch [key ", key.data, key.size, "]");
+	
+	li = find_or_next(sto, key, &match);
+	if (match) {
+		sr = (struct sr_data *)li;
+		GNUTLS_TRACE( CHECK_MALLOC_DO(res.data = gnutls_malloc(sr->data.size), goto out ) );
+		res.size = sr->data.size;
+		memcpy(res.data, sr->data.data, res.size);
+	}
+out:	
+	TRACE_DEBUG(GNUTLS_DBG_LEVEL, "Fetched (%p, %d) from store %p", res.data, res.size, sto);
+	CHECK_POSIX_DO( pthread_rwlock_unlock(&sto->lock), return error);
+	return res;
+}
+
+/* Set the session pointer in a session object */
+static void set_resume_callbacks(gnutls_session_t session, struct cnxctx * conn)
+{
+	TRACE_ENTRY("%p", conn);
+	
+	GNUTLS_TRACE( gnutls_db_set_retrieve_function(session, sr_fetch));
+	GNUTLS_TRACE( gnutls_db_set_remove_function  (session, sr_remove));
+	GNUTLS_TRACE( gnutls_db_set_store_function   (session, sr_store));
+	GNUTLS_TRACE( gnutls_db_set_ptr              (session, conn->cc_sctp3436_data.sess_store));
+	
+	return;
+}
+
+/* The handshake is made in parallel in several threads to speed up */
+static void * handshake_resume_th(void * arg)
+{
+	struct sctp3436_ctx * ctx = (struct sctp3436_ctx *) arg;
+	int resumed;
+	
+	TRACE_ENTRY("%p", arg);
+	
+	/* Set the thread name */
+	{
+		char buf[48];
+		snprintf(buf, sizeof(buf), "Handshake resume (%hu@%d)", ctx->strid, ctx->parent->cc_socket);
+		fd_log_threadname ( buf );
+	}
+	
+	TRACE_DEBUG(FULL, "Starting TLS resumed handshake on stream %hu", ctx->strid);
+
+	CHECK_GNUTLS_DO( gnutls_handshake( ctx->session ), return NULL);
+			
+	GNUTLS_TRACE( resumed = gnutls_session_is_resumed(ctx->session) );
+	#ifndef GNUTLS_VERSION_300
+	if (!resumed) {
+		/* Check the credentials here also */
+		CHECK_FCT_DO( fd_tls_verify_credentials(ctx->session, ctx->parent, 0), return NULL );
+	}
+	#endif /* GNUTLS_VERSION_300 */
+	if (TRACE_BOOL(FULL)) {
+		if (resumed) {
+			fd_log_debug("Session was resumed successfully on stream %hu (conn: '%s')", ctx->strid, fd_cnx_getid(ctx->parent));
+		} else {
+			fd_log_debug("Session was NOT resumed on stream %hu  (full handshake) (conn: '%s')", ctx->strid, fd_cnx_getid(ctx->parent));
+		}
+	}
+			
+	/* Finished, OK */
+	return arg;
+}
+
+
+/*************************************************************/
+/*                     Exported functions                    */
+/*************************************************************/
+
+/* Initialize the wrapper for the connection */
+int fd_sctp3436_init(struct cnxctx * conn)
+{
+	uint16_t i;
+	
+	TRACE_ENTRY("%p", conn);
+	CHECK_PARAMS( conn && (conn->cc_sctp_para.pairs > 1) && (!conn->cc_sctp3436_data.array) );
+	
+	/* First, alloc the array and initialize the non-TLS data */
+	CHECK_MALLOC( conn->cc_sctp3436_data.array = calloc(conn->cc_sctp_para.pairs, sizeof(struct sctp3436_ctx))  );
+	for (i = 0; i < conn->cc_sctp_para.pairs; i++) {
+		conn->cc_sctp3436_data.array[i].parent = conn;
+		conn->cc_sctp3436_data.array[i].strid  = i;
+		CHECK_FCT( fd_fifo_new(&conn->cc_sctp3436_data.array[i].raw_recv, 10) );
+	}
+	
+	/* Set push/pull functions in the master session, using fifo in array[0] */
+	set_sess_transport(conn->cc_tls_para.session, &conn->cc_sctp3436_data.array[0]);
+	
+	/* For server side, we also initialize the resuming capabilities */
+	if (conn->cc_tls_para.mode == GNUTLS_SERVER) {
+		
+		/* Prepare the store for sessions data */
+		CHECK_FCT( store_init(conn) );
+		
+		/* Set the callbacks for resuming in the master session */
+		set_resume_callbacks(conn->cc_tls_para.session, conn);
+	}
+
+	/* Start the demux thread */
+	CHECK_POSIX( pthread_create( &conn->cc_rcvthr, NULL, demuxer, conn ) );
+	
+	return 0;
+}
+
+/* Handshake other streams, after full handshake on the master session */
+int fd_sctp3436_handshake_others(struct cnxctx * conn, char * priority, void * alt_creds)
+{
+	uint16_t i;
+	int errors = 0;
+	gnutls_datum_t 	master_data;
+	
+	TRACE_ENTRY("%p %p", conn, priority);
+	CHECK_PARAMS( conn && (conn->cc_sctp_para.pairs > 1) && conn->cc_sctp3436_data.array );
+
+	/* Server side: we set all the parameters, the resume callback will take care of resuming the session */
+	/* Client side: we duplicate the parameters of the master session, then set the transport pointer */
+	
+	/* For client side, retrieve the master session parameters */
+	if (conn->cc_tls_para.mode == GNUTLS_CLIENT) {
+		CHECK_GNUTLS_DO( gnutls_session_get_data2(conn->cc_tls_para.session, &master_data), return ENOMEM );
+		/* For debug: */
+		if (TRACE_BOOL(GNUTLS_DBG_LEVEL)) {
+			uint8_t  id[256];
+			size_t	 ids = sizeof(id);
+			CHECK_GNUTLS_DO( gnutls_session_get_id(conn->cc_tls_para.session, id, &ids), /* continue */ );
+			TRACE_BUFFER(FD_LOG_DEBUG, GNUTLS_DBG_LEVEL, "Master session id: [", id, ids, "]");
+		}
+	}
+	
+	/* Initialize the session objects and start the handshake in a separate thread */
+	for (i = 1; i < conn->cc_sctp_para.pairs; i++) {
+		/* Set credentials and priority */
+		CHECK_FCT( fd_tls_prepare(&conn->cc_sctp3436_data.array[i].session, conn->cc_tls_para.mode, 0, priority, alt_creds) );
+		
+		/* additional initialization for gnutls 3.x */
+		#ifdef GNUTLS_VERSION_300
+			/* the verify function has already been set in the global initialization in config.c */
+
+		/* fd_tls_verify_credentials_2 uses the connection */
+		gnutls_session_set_ptr (conn->cc_sctp3436_data.array[i].session, (void *) conn);
+
+		if ((conn->cc_tls_para.cn != NULL) && (conn->cc_tls_para.mode == GNUTLS_CLIENT)) {
+			/* this might allow virtual hosting on the remote peer */
+			CHECK_GNUTLS_DO( gnutls_server_name_set (conn->cc_sctp3436_data.array[i].session, GNUTLS_NAME_DNS, conn->cc_tls_para.cn, strlen(conn->cc_tls_para.cn)), /* ignore failure */);
+		}
+
+		#endif /* GNUTLS_VERSION_300 */
+
+		#ifdef GNUTLS_VERSION_310
+		GNUTLS_TRACE( gnutls_handshake_set_timeout( conn->cc_sctp3436_data.array[i].session, GNUTLS_DEFAULT_HANDSHAKE_TIMEOUT));
+		#endif /* GNUTLS_VERSION_310 */
+
+		/* For the client, copy data from master session; for the server, set session resuming pointers */
+		if (conn->cc_tls_para.mode == GNUTLS_CLIENT) {
+			CHECK_GNUTLS_DO( gnutls_session_set_data(conn->cc_sctp3436_data.array[i].session, master_data.data, master_data.size), return ENOMEM );
+		} else {
+			set_resume_callbacks(conn->cc_sctp3436_data.array[i].session, conn);
+		}
+		
+		/* Set transport parameters */
+		set_sess_transport(conn->cc_sctp3436_data.array[i].session, &conn->cc_sctp3436_data.array[i]);
+		
+		/* Start the handshake thread */
+		CHECK_POSIX( pthread_create( &conn->cc_sctp3436_data.array[i].thr, NULL, handshake_resume_th, &conn->cc_sctp3436_data.array[i] ) );
+	}
+	
+	/* We can now release the memory of master session data if any */
+	if (conn->cc_tls_para.mode == GNUTLS_CLIENT) {
+		GNUTLS_TRACE( gnutls_free(master_data.data) );
+	}
+	
+	/* Now wait for all handshakes to finish */
+	for (i = 1; i < conn->cc_sctp_para.pairs; i++) {
+		void * ret;
+		CHECK_POSIX( pthread_join(conn->cc_sctp3436_data.array[i].thr, &ret) );
+		conn->cc_sctp3436_data.array[i].thr = (pthread_t) NULL;
+		if (ret == NULL) {
+			errors++; /* Handshake failed on this stream */
+		}
+	}
+	
+	if (errors) {
+		TRACE_DEBUG(INFO, "Handshake failed on %d/%hd stream pairs", errors, conn->cc_sctp_para.pairs);
+		fd_cnx_markerror(conn);
+		return ENOTCONN;
+	}
+	
+	return 0;
+}
+
+/* Receive messages from others ? all other stream pairs : the master pair */
+int fd_sctp3436_startthreads(struct cnxctx * conn, int others)
+{
+	uint16_t i;
+	
+	TRACE_ENTRY("%p", conn);
+	CHECK_PARAMS( conn && conn->cc_sctp3436_data.array );
+	
+	if (others) {
+		for (i = 1; i < conn->cc_sctp_para.pairs; i++) {
+
+			/* Start the decipher thread */
+			CHECK_POSIX( pthread_create( &conn->cc_sctp3436_data.array[i].thr, NULL, decipher, &conn->cc_sctp3436_data.array[i] ) );
+		}
+	} else {
+		CHECK_POSIX( pthread_create( &conn->cc_sctp3436_data.array[0].thr, NULL, decipher, &conn->cc_sctp3436_data.array[0] ) );
+	}
+	return 0;
+}
+
+/* Initiate a "bye" on all stream pairs */
+void fd_sctp3436_bye(struct cnxctx * conn)
+{
+	uint16_t i;
+	
+	CHECK_PARAMS_DO( conn && conn->cc_sctp3436_data.array, return );
+	
+	/* End all TLS sessions, in series (not as efficient as paralel, but simpler) */
+	for (i = 1; i < conn->cc_sctp_para.pairs; i++) {
+		if ( ! fd_cnx_teststate(conn, CC_STATUS_ERROR)) {
+			CHECK_GNUTLS_DO( gnutls_bye(conn->cc_sctp3436_data.array[i].session, GNUTLS_SHUT_WR), fd_cnx_markerror(conn) );
+		}
+	}
+}
+
+/* After "bye" was sent on all streams, read from sessions until an error is received */
+void fd_sctp3436_waitthreadsterm(struct cnxctx * conn)
+{
+	uint16_t i;
+	
+	TRACE_ENTRY("%p", conn);
+	CHECK_PARAMS_DO( conn && conn->cc_sctp3436_data.array, return );
+	
+	for (i = 0; i < conn->cc_sctp_para.pairs; i++) {
+		if (conn->cc_sctp3436_data.array[i].thr != (pthread_t)NULL) {
+			CHECK_POSIX_DO( pthread_join(conn->cc_sctp3436_data.array[i].thr, NULL), /* continue */ );
+			conn->cc_sctp3436_data.array[i].thr = (pthread_t)NULL;
+		}
+	}
+	return;
+}
+
+/* Free gnutls resources of all sessions */
+void fd_sctp3436_gnutls_deinit_others(struct cnxctx * conn)
+{
+	uint16_t i;
+	
+	TRACE_ENTRY("%p", conn);
+	CHECK_PARAMS_DO( conn && conn->cc_sctp3436_data.array, return );
+	
+	for (i = 1; i < conn->cc_sctp_para.pairs; i++) {
+		if (conn->cc_sctp3436_data.array[i].session) {
+			GNUTLS_TRACE( gnutls_deinit(conn->cc_sctp3436_data.array[i].session) );
+			conn->cc_sctp3436_data.array[i].session = NULL;
+		}
+	}
+}
+
+
+/* Stop all receiver threads */
+void fd_sctp3436_stopthreads(struct cnxctx * conn)
+{
+	uint16_t i;
+	
+	TRACE_ENTRY("%p", conn);
+	CHECK_PARAMS_DO( conn && conn->cc_sctp3436_data.array, return );
+	
+	for (i = 0; i < conn->cc_sctp_para.pairs; i++) {
+		CHECK_FCT_DO( fd_thr_term(&conn->cc_sctp3436_data.array[i].thr), /* continue */ );
+	}
+	return;
+}
+
+/* Destroy a wrapper context */
+void fd_sctp3436_destroy(struct cnxctx * conn)
+{
+	uint16_t i;
+	
+	CHECK_PARAMS_DO( conn && conn->cc_sctp3436_data.array, return );
+	
+	/* Terminate all receiving threads in case we did not do it yet */
+	fd_sctp3436_stopthreads(conn);
+	
+	/* Now, stop the demux thread */
+	CHECK_FCT_DO( fd_thr_term(&conn->cc_rcvthr), /* continue */ );
+	
+	/* Free remaining data in the array */
+	for (i = 0; i < conn->cc_sctp_para.pairs; i++) {
+		if (conn->cc_sctp3436_data.array[i].raw_recv)
+			fd_event_destroy( &conn->cc_sctp3436_data.array[i].raw_recv, free );
+		free(conn->cc_sctp3436_data.array[i].partial.buf);
+		if (conn->cc_sctp3436_data.array[i].session) {
+			GNUTLS_TRACE( gnutls_deinit(conn->cc_sctp3436_data.array[i].session) );
+			conn->cc_sctp3436_data.array[i].session = NULL;
+		}
+	}
+	
+	/* Free the array itself now */
+	free(conn->cc_sctp3436_data.array);
+	conn->cc_sctp3436_data.array = NULL;
+	
+	/* Delete the store of sessions */
+	store_destroy(conn);
+	
+	return ;
+}
diff --git a/libfdcore/server.c b/libfdcore/server.c
new file mode 100644
index 0000000..85a8568
--- /dev/null
+++ b/libfdcore/server.c
@@ -0,0 +1,509 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@freediameter.net>							 *
+*													 *
+* Copyright (c) 2013, 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 "fdcore-internal.h"
+
+/* Server (listening) part of the framework */
+
+static struct fd_list	FD_SERVERS = FD_LIST_INITIALIZER(FD_SERVERS);	/* The list of all server objects */
+/* We don't need to protect this list, it is only accessed from the main framework thread. */
+
+enum s_state {
+	NOT_CREATED=0,
+	RUNNING,
+	TERMINATED,
+	ERROR	/* an error occurred, this is not a valid status */
+};
+
+/* Servers information */
+struct server {
+	struct fd_list	chain;		/* link in the FD_SERVERS list */
+
+	struct cnxctx *	conn;		/* server connection context (listening socket) */
+	int 		proto;		/* IPPROTO_TCP or IPPROTO_SCTP */
+	int 		secur;		/* TLS is started immediatly after connection ? 0: no; 1: RFU; 2: yes (TLS/TCP or TLS/SCTP) */
+	
+	pthread_t	thr;		/* The thread waiting for new connections (will store the data in the clients fifo) */
+	enum s_state	state;		/* state of the thread */
+	
+	struct fifo	*pending;	/* FIFO of struct cnxctx */
+	struct pool_workers {
+		struct server * s;	/* pointer to the parent server structure */
+		int		id;	/* The worker id for logs */
+		pthread_t	worker; /* The thread */
+	}		*workers;	/* array of cnf_thr_srv items */
+};
+
+
+/* Micro functions to read/change the status thread-safely */
+static pthread_mutex_t s_lock = PTHREAD_MUTEX_INITIALIZER;
+static enum s_state get_status(struct server * s)
+{
+	enum s_state r;
+	CHECK_POSIX_DO( pthread_mutex_lock(&s_lock), return ERROR );
+	r = s->state;
+	CHECK_POSIX_DO( pthread_mutex_unlock(&s_lock), return ERROR );
+	return r;
+}
+static void set_status(struct server * s, enum s_state st)
+{
+	CHECK_POSIX_DO( pthread_mutex_lock(&s_lock), return );
+	s->state = st;
+	CHECK_POSIX_DO( pthread_mutex_unlock(&s_lock), return );
+}
+	
+
+/* dump one item of the server->pending fifo */
+static DECLARE_FD_DUMP_PROTOTYPE(dump_cnx, void * item) {
+	struct cnxctx * c = item;
+	CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, " '%s'", fd_cnx_getid(c)), return NULL);
+	return *buf;
+}
+
+/* Dump all servers information */
+DECLARE_FD_DUMP_PROTOTYPE(fd_servers_dump, int details)
+{
+	struct fd_list * li;
+	
+	FD_DUMP_HANDLE_OFFSET();
+	
+	for (li = FD_SERVERS.next; li != &FD_SERVERS; li = li->next) {
+		struct server * s = (struct server *)li;
+		enum s_state st = get_status(s);
+		
+		if (details) {
+			CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "{server}(@%p)'%s': %s, %s(%d), %s", s, fd_cnx_getid(s->conn), 
+					IPPROTO_NAME( s->proto ),
+					s->secur ? "Secur" : "NotSecur", s->secur,
+					(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_MALLOC_DO( fd_fifo_dump(FD_DUMP_STD_PARAMS, "pending connections", s->pending, dump_cnx), return NULL );
+
+			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);
+		}
+	}
+	
+	return *buf;
+}
+
+
+/* The thread in the pool for handling new clients connecting to a server */
+static void * client_worker(void * arg)
+{
+	struct pool_workers * pw = arg;
+	struct server * s = pw->s;
+	struct cnxctx * c = NULL;
+	int fatal = 0;
+	struct timespec ts;
+	struct fd_cnx_rcvdata rcv_data;
+	struct fd_msg_pmdl * pmdl = NULL;
+	struct msg    * msg = NULL;
+	struct msg_hdr *hdr = NULL;
+	struct fd_pei pei;
+	
+	TRACE_ENTRY("%p", arg);
+	
+	/* Set the thread name */
+	{
+		char buf[48];
+		snprintf(buf, sizeof(buf), "Worker#%d[%s%s]", pw->id, IPPROTO_NAME(s->proto), s->secur?", Sec" : "");
+		fd_log_threadname ( buf );
+	}
+	
+	/* Loop until canceled / error */
+next_client:
+	LOG_A("Ready to process next incoming connection");
+
+	memset(&rcv_data, 0, sizeof(rcv_data));
+	
+	/* Get the next connection */
+	CHECK_FCT_DO( fd_fifo_get( s->pending, &c ), { fatal = 1; goto cleanup; } );
+
+	/* Handshake if we are a secure server port, or start clear otherwise */
+	if (s->secur) {
+		LOG_D("Starting handshake with %s", fd_cnx_getid(c));
+
+		int ret = fd_cnx_handshake(c, GNUTLS_SERVER, (s->secur == 1) ? ALGO_HANDSHAKE_DEFAULT : ALGO_HANDSHAKE_3436, NULL, NULL);
+		if (ret != 0) {
+			char buf[1024];
+			snprintf(buf, sizeof(buf), "TLS handshake failed for connection '%s', connection closed.", fd_cnx_getid(c));
+
+			fd_hook_call(HOOK_PEER_CONNECT_FAILED, NULL, NULL, buf, NULL);
+
+			goto cleanup;
+		}
+	} else {
+		CHECK_FCT_DO( fd_cnx_start_clear(c, 0), goto cleanup );
+	}
+	
+	/* Set the timeout to receive the first message */
+	CHECK_SYS_DO( clock_gettime(CLOCK_REALTIME, &ts), { fatal = 1; goto cleanup; } );
+	ts.tv_sec += INCNX_TIMEOUT;
+	
+	/* Receive the first Diameter message on the connection -- cleanup in case of timeout */
+	CHECK_FCT_DO( fd_cnx_receive(c, &ts, &rcv_data.buffer, &rcv_data.length), 
+		{
+			char buf[1024];
+			
+			switch (__ret__) {
+			case ETIMEDOUT:
+				snprintf(buf, sizeof(buf), "Client '%s' did not send CER within %ds, connection aborted.", fd_cnx_getid(c), INCNX_TIMEOUT);
+				fd_hook_call(HOOK_PEER_CONNECT_FAILED, NULL, NULL, buf, NULL);
+				break;
+			
+			case ENOTCONN:
+				snprintf(buf, sizeof(buf), "Connection from '%s' in error before CER was received.", fd_cnx_getid(c));
+				fd_hook_call(HOOK_PEER_CONNECT_FAILED, NULL, NULL, buf, NULL);
+				break;
+			
+			default:
+				snprintf(buf, sizeof(buf), "Connection from '%s': unspecified error, connection aborted.", fd_cnx_getid(c));
+				fd_hook_call(HOOK_PEER_CONNECT_FAILED, NULL, NULL, buf, NULL);
+			}
+			goto cleanup;
+		} );
+	
+	TRACE_DEBUG(FULL, "Received %zdb from new client '%s'", rcv_data.length, fd_cnx_getid(c));
+	
+	pmdl = fd_msg_pmdl_get_inbuf(rcv_data.buffer, rcv_data.length);
+	
+	/* Try parsing this message */
+	CHECK_FCT_DO( fd_msg_parse_buffer( &rcv_data.buffer, rcv_data.length, &msg ), 
+		{ 	/* Parsing failed */ 
+			fd_hook_call(HOOK_MESSAGE_PARSING_ERROR, NULL, NULL, &rcv_data, pmdl );
+			goto cleanup;
+		} );
+	
+	/* Log incoming message */
+	fd_hook_associate(msg, pmdl);
+	fd_hook_call(HOOK_MESSAGE_RECEIVED, msg, NULL, fd_cnx_getid(c), fd_msg_pmdl_get(msg));
+	
+	/* We expect a CER, it must parse with our dictionary and rules */
+	CHECK_FCT_DO( fd_msg_parse_rules( msg, fd_g_config->cnf_dict, &pei ), 
+		{ /* Parsing failed -- trace details */ 
+			char buf[1024];
+			
+			fd_hook_call(HOOK_MESSAGE_PARSING_ERROR, msg, NULL, pei.pei_message ?: pei.pei_errcode, fd_msg_pmdl_get(msg));
+			
+			snprintf(buf, sizeof(buf), "Error parsing CER from '%s', connection aborted.", fd_cnx_getid(c));
+			fd_hook_call(HOOK_PEER_CONNECT_FAILED, NULL, NULL, buf, NULL);
+			
+			goto cleanup;
+		} );
+	
+	/* Now check we received a CER */
+	CHECK_FCT_DO( fd_msg_hdr ( msg, &hdr ), { fatal = 1; goto cleanup; }  );
+	CHECK_PARAMS_DO( (hdr->msg_appl == 0) && (hdr->msg_flags & CMD_FLAG_REQUEST) && (hdr->msg_code == CC_CAPABILITIES_EXCHANGE),
+		{ /* Parsing failed -- trace details */ 
+			char buf[1024];
+			snprintf(buf, sizeof(buf), "Expected CER from '%s', received a different message, connection aborted.", fd_cnx_getid(c));
+			fd_hook_call(HOOK_PEER_CONNECT_FAILED, msg, NULL, buf, NULL);
+			goto cleanup;
+		} );
+	
+	/* Finally, pass the information to the peers module which will handle it in a separate thread */
+	pthread_cleanup_push((void *)fd_cnx_destroy, c);
+	pthread_cleanup_push((void *)fd_msg_free, msg);
+	CHECK_FCT_DO( fd_peer_handle_newCER( &msg, &c ),  );
+	pthread_cleanup_pop(0);
+	pthread_cleanup_pop(0);
+
+cleanup:
+	/* Cleanup the parsed message if any */
+	if (msg) {
+		CHECK_FCT_DO( fd_msg_free(msg), /* continue */);
+		msg = NULL;
+	}
+	
+	/* Close the connection if needed */
+	if (c != NULL) {
+		fd_cnx_destroy(c);
+		c = NULL;
+	}
+	
+	/* Cleanup the received buffer if any */
+	free(rcv_data.buffer);
+	
+	
+	if (!fatal)
+		goto next_client;
+
+	LOG_E("Worker thread exiting.");
+	return NULL;
+}	
+
+/* The thread managing a server */
+static void * serv_th(void * arg)
+{
+	struct server *s = (struct server *)arg;
+	
+	CHECK_PARAMS_DO(s, goto error);
+	fd_log_threadname ( fd_cnx_getid(s->conn) );
+	
+	set_status(s, RUNNING);
+	
+	/* Accept incoming connections */
+	CHECK_FCT_DO( fd_cnx_serv_listen(s->conn), goto error );
+	
+	do {
+		struct cnxctx * conn = NULL;
+		
+		/* Wait for a new client or cancel */
+		CHECK_MALLOC_DO( conn = fd_cnx_serv_accept(s->conn), break );
+		
+		/* Store this connection in the fifo for processing by the worker pool. Will block when the fifo is full */
+		pthread_cleanup_push((void *)fd_cnx_destroy, conn);
+		CHECK_FCT_DO( fd_fifo_post( s->pending, &conn ), break );
+		pthread_cleanup_pop(0);
+		
+	} while (1);
+error:	
+	if (s)
+		set_status(s, TERMINATED);
+
+	/* Send error signal to the core */
+	LOG_F( "An error occurred in server module! Thread is terminating...");
+	CHECK_FCT_DO(fd_core_shutdown(), );
+
+	return NULL;
+}
+
+
+/* Create a new server structure */
+static struct server * new_serv( int proto, int secur )
+{
+	struct server * new;
+	int i;
+	
+	/* New server structure */
+	CHECK_MALLOC_DO( new = malloc(sizeof(struct server)), return NULL );
+	
+	memset(new, 0, sizeof(struct server));
+	fd_list_init(&new->chain, new);
+	new->proto = proto;
+	new->secur = secur;
+	
+	CHECK_FCT_DO( fd_fifo_new(&new->pending, 5), return NULL);
+	CHECK_MALLOC_DO( new->workers = calloc( fd_g_config->cnf_thr_srv, sizeof(struct pool_workers) ), return NULL );
+	
+	for (i = 0; i < fd_g_config->cnf_thr_srv; i++) {
+		/* Create the pool */
+		new->workers[i].s = new;
+		new->workers[i].id = i;
+		CHECK_POSIX_DO( pthread_create( &new->workers[i].worker, NULL, client_worker, &new->workers[i]), return NULL );
+	}
+	
+	return new;
+}
+
+/* Start all the servers */
+int fd_servers_start()
+{
+	struct server * s;
+	
+	int empty_conf_ep = FD_IS_LIST_EMPTY(&fd_g_config->cnf_endpoints);
+	
+	/* SCTP */
+	if (!fd_g_config->cnf_flags.no_sctp) {
+#ifdef DISABLE_SCTP
+		ASSERT(0);
+#else /* DISABLE_SCTP */
+		
+		/* Create the server on unsecure port */
+		if (fd_g_config->cnf_port) {
+			CHECK_MALLOC( s = new_serv(IPPROTO_SCTP, 0) );
+			CHECK_MALLOC( s->conn = fd_cnx_serv_sctp(fd_g_config->cnf_port, empty_conf_ep ? NULL : &fd_g_config->cnf_endpoints) );
+			fd_list_insert_before( &FD_SERVERS, &s->chain );
+			CHECK_POSIX( pthread_create( &s->thr, NULL, serv_th, s ) );
+		}
+		
+		/* Create the server on secure port */
+		if (fd_g_config->cnf_port_tls) {
+			CHECK_MALLOC( s = new_serv(IPPROTO_SCTP, 2 /* Change when DTLS is introduced */) );
+			CHECK_MALLOC( s->conn = fd_cnx_serv_sctp(fd_g_config->cnf_port_tls, empty_conf_ep ? NULL : &fd_g_config->cnf_endpoints) );
+			fd_list_insert_before( &FD_SERVERS, &s->chain );
+			CHECK_POSIX( pthread_create( &s->thr, NULL, serv_th, s ) );
+		}
+		
+		/* Create the other server on 3436 secure port */
+		/*if (fd_g_config->cnf_port_3436) {
+			CHECK_MALLOC( s = new_serv(IPPROTO_SCTP, 2) );
+			CHECK_MALLOC( s->conn = fd_cnx_serv_sctp(fd_g_config->cnf_port_3436, empty_conf_ep ? NULL : &fd_g_config->cnf_endpoints) );
+			fd_list_insert_before( &FD_SERVERS, &s->chain );
+			CHECK_POSIX( pthread_create( &s->thr, NULL, serv_th, s ) );
+		}*/
+		
+#endif /* DISABLE_SCTP */
+	}
+	
+	/* TCP */
+	if (!fd_g_config->cnf_flags.no_tcp) {
+		
+		if (empty_conf_ep) {
+			/* Bind TCP servers on [0.0.0.0] */
+			if (!fd_g_config->cnf_flags.no_ip4) {
+				
+				if (fd_g_config->cnf_port) {
+					CHECK_MALLOC( s = new_serv(IPPROTO_TCP, 0) );
+					CHECK_MALLOC( s->conn = fd_cnx_serv_tcp(fd_g_config->cnf_port, AF_INET, NULL) );
+					fd_list_insert_before( &FD_SERVERS, &s->chain );
+					CHECK_POSIX( pthread_create( &s->thr, NULL, serv_th, s ) );
+				}
+
+				if (fd_g_config->cnf_port_tls) {
+					CHECK_MALLOC( s = new_serv(IPPROTO_TCP, 1) );
+					CHECK_MALLOC( s->conn = fd_cnx_serv_tcp(fd_g_config->cnf_port_tls, AF_INET, NULL) );
+					fd_list_insert_before( &FD_SERVERS, &s->chain );
+					CHECK_POSIX( pthread_create( &s->thr, NULL, serv_th, s ) );
+				}
+			}
+			
+			/* Bind TCP servers on [::] */
+			if (!fd_g_config->cnf_flags.no_ip6) {
+
+				if (fd_g_config->cnf_port) {
+					CHECK_MALLOC( s = new_serv(IPPROTO_TCP, 0) );
+					CHECK_MALLOC( s->conn = fd_cnx_serv_tcp(fd_g_config->cnf_port, AF_INET6, NULL) );
+					fd_list_insert_before( &FD_SERVERS, &s->chain );
+					CHECK_POSIX( pthread_create( &s->thr, NULL, serv_th, s ) );
+				}
+
+				if (fd_g_config->cnf_port_tls) {
+					CHECK_MALLOC( s = new_serv(IPPROTO_TCP, 1) );
+					CHECK_MALLOC( s->conn = fd_cnx_serv_tcp(fd_g_config->cnf_port_tls, AF_INET6, NULL) );
+					fd_list_insert_before( &FD_SERVERS, &s->chain );
+					CHECK_POSIX( pthread_create( &s->thr, NULL, serv_th, s ) );
+				}
+			}
+		} else {
+			/* Create all endpoints -- check flags */
+			struct fd_list * li;
+			for (li = fd_g_config->cnf_endpoints.next; li != &fd_g_config->cnf_endpoints; li = li->next) {
+				struct fd_endpoint * ep = (struct fd_endpoint *)li;
+				sSA * sa = (sSA *) &ep->ss;
+				if (! (ep->flags & EP_FL_CONF))
+					continue;
+				if (fd_g_config->cnf_flags.no_ip4 && (sa->sa_family == AF_INET))
+					continue;
+				if (fd_g_config->cnf_flags.no_ip6 && (sa->sa_family == AF_INET6))
+					continue;
+				
+				if (fd_g_config->cnf_port) {
+					CHECK_MALLOC( s = new_serv(IPPROTO_TCP, 0) );
+					CHECK_MALLOC( s->conn = fd_cnx_serv_tcp(fd_g_config->cnf_port, sa->sa_family, ep) );
+					fd_list_insert_before( &FD_SERVERS, &s->chain );
+					CHECK_POSIX( pthread_create( &s->thr, NULL, serv_th, s ) );
+				}
+
+				if (fd_g_config->cnf_port_tls) {
+					CHECK_MALLOC( s = new_serv(IPPROTO_TCP, 1) );
+					CHECK_MALLOC( s->conn = fd_cnx_serv_tcp(fd_g_config->cnf_port_tls, sa->sa_family, ep) );
+					fd_list_insert_before( &FD_SERVERS, &s->chain );
+					CHECK_POSIX( pthread_create( &s->thr, NULL, serv_th, s ) );
+				}
+			}
+		}
+	}
+	
+	/* Now, if we had an empty list of local adresses (no address configured), try to read the real addresses from the kernel */
+	if (empty_conf_ep) {
+		CHECK_FCT(fd_cnx_get_local_eps(&fd_g_config->cnf_endpoints));
+		if (FD_IS_LIST_EMPTY(&fd_g_config->cnf_endpoints)) {
+			TRACE_DEBUG(INFO, "Unable to find the address(es) of the local system. " 
+					"Please use \"ListenOn\" parameter in the configuration. "
+					"This information is required to generate the CER/CEA messages.");
+			return EINVAL;
+		}
+	}
+	
+	{
+		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, 0, 0, &fd_g_config->cnf_endpoints ), );
+		LOG_N("%s", buf ?: "Error dumping addresses");
+		free(buf);
+	}
+	return 0;
+}
+
+/* Terminate all the servers */
+int fd_servers_stop()
+{
+	TRACE_ENTRY("");
+	
+	TRACE_DEBUG(INFO, "Shutting down server sockets...");
+	
+	/* Loop on all servers */
+	while (!FD_IS_LIST_EMPTY(&FD_SERVERS)) {
+		struct server * s = (struct server *)(FD_SERVERS.next);
+		int i;
+		struct cnxctx * c;
+		
+		/* cancel thread */
+		CHECK_FCT_DO( fd_thr_term(&s->thr), /* continue */);
+		
+		/* destroy server connection context */
+		fd_cnx_destroy(s->conn);
+		
+		/* cancel and destroy all worker threads */
+		for (i = 0; i < fd_g_config->cnf_thr_srv; i++) {
+			/* Destroy worker thread */
+			CHECK_FCT_DO( fd_thr_term(&s->workers[i].worker), /* continue */);
+		}
+		free(s->workers);
+		
+		/* Close any pending connection */
+		while ( fd_fifo_tryget( s->pending, &c ) == 0 ) {
+			fd_cnx_destroy(c);
+		}
+		CHECK_FCT_DO( fd_fifo_del(&s->pending), );
+		
+		/* Now destroy the server object */
+		fd_list_unlink(&s->chain);
+		free(s);
+	}
+	
+	/* We're done! */
+	return 0;
+}
diff --git a/libfdcore/tcp.c b/libfdcore/tcp.c
new file mode 100644
index 0000000..fb6cf1e
--- /dev/null
+++ b/libfdcore/tcp.c
@@ -0,0 +1,168 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@freediameter.net>							 *
+*													 *
+* Copyright (c) 2013, 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 "fdcore-internal.h"
+#include "cnxctx.h"
+
+#include <netinet/tcp.h>
+#include <netinet/ip6.h>
+#include <sys/socket.h>
+
+/* Set the socket options for TCP sockets, before bind is called */
+static int fd_tcp_setsockopt(int family, int sk)
+{
+	int ret = 0;
+	int opt;
+	
+	/* Clear the NODELAY option in case it was set, as requested by rfc3539#section-3.2 */
+	/* Note that this is supposed to be the default, so we could probably remove this call ... */
+	opt = 0;
+	ret = setsockopt(sk, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof(opt));
+	if (ret != 0) {
+		ret = errno;
+		TRACE_DEBUG(INFO, "Unable to set the socket TCP_NODELAY option: %s", strerror(ret));
+		return ret;
+	}
+	
+	/* Under Linux, we may also set the TCP_CONGESTION option to one of the following strings:
+	    - reno (default)
+	    - bic
+	    - cubic
+	    - highspeed
+	    - htcp
+	    - hybla
+	    - illinois
+	    - lp
+	    - scalable
+	    - vegas
+	    - veno
+	    - westwood
+	    - yeah
+	*/
+	
+	/* In case of v6 address, force the v6only option, we use a different socket for v4 */
+	#ifdef IPV6_V6ONLY
+	if (family == AF_INET6) {
+		opt = 1;
+		CHECK_SYS(setsockopt(sk, IPPROTO_IPV6, IPV6_V6ONLY, &opt, sizeof(opt)));
+	}
+	#endif /* IPV6_V6ONLY */
+	
+	{
+		opt = 1;
+		CHECK_SYS(  setsockopt(sk, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt))  );
+	}
+	
+	/* There are also others sockopt that can be set, but nothing useful for us AFAICT */
+	
+	return 0;
+}
+
+/* Create a socket server and bind it */
+int fd_tcp_create_bind_server( int * sock, sSA * sa, socklen_t salen )
+{
+	TRACE_ENTRY("%p %p %d", sock, sa, salen);
+	
+	CHECK_PARAMS(  sock && sa  );
+	
+	/* Create the socket */
+	CHECK_SYS(  *sock = socket(sa->sa_family, SOCK_STREAM, IPPROTO_TCP)  );
+
+	/* Set the socket options */
+	CHECK_FCT(  fd_tcp_setsockopt(sa->sa_family, *sock)  );
+	
+	/* Bind the socket */
+	CHECK_SYS(  bind( *sock, sa, salen )  );
+			
+	/* We're done */
+	return 0;
+}
+
+/* Allow clients connections on server sockets */
+int fd_tcp_listen( int sock )
+{
+	TRACE_ENTRY("%d", sock);
+	CHECK_SYS( listen(sock, 5) );
+	return 0;
+}
+
+/* Create a client socket and connect to remote server */
+int fd_tcp_client( int *sock, sSA * sa, socklen_t salen )
+{
+	int ret = 0;
+	int s;
+	
+	TRACE_ENTRY("%p %p %d", sock, sa, salen);
+	CHECK_PARAMS( sock && (*sock <= 0) && sa && salen );
+	
+	/* Create the socket */
+	CHECK_SYS(  s = socket(sa->sa_family, SOCK_STREAM, IPPROTO_TCP)  );
+	
+	/* Set the socket options */
+	CHECK_FCT(  fd_tcp_setsockopt(sa->sa_family, s)  );
+	
+	/* Cleanup if we are cancelled */
+	pthread_cleanup_push(fd_cleanup_socket, &s);
+	
+	/* Try connecting to the remote address */
+	ret = connect(s, sa, salen);
+	
+	pthread_cleanup_pop(0);
+	
+	if (ret < 0) {
+		ret = errno;
+		LOG_A( "connect returned an error: %s", strerror(ret));
+		CHECK_SYS_DO( close(s), /* continue */ );
+		*sock = -1;
+		return ret;
+	}
+	
+	/* Done! */
+	*sock = s;
+	return ret;
+}
+
+/* Get the remote name of a TCP socket */
+int fd_tcp_get_remote_ep(int sock, sSS * ss, socklen_t *sl)
+{
+	TRACE_ENTRY("%d %p %p", sock, ss, sl);
+	CHECK_PARAMS( ss && sl );
+	
+	*sl = sizeof(sSS);
+	CHECK_SYS(getpeername(sock, (sSA *)ss, sl));
+	
+	return 0;
+}
+
diff --git a/libfdcore/version.c b/libfdcore/version.c
new file mode 100644
index 0000000..fa3a732
--- /dev/null
+++ b/libfdcore/version.c
@@ -0,0 +1,47 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@freediameter.net>							 *
+*													 *
+* Copyright (c) 2013, 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 <fdcore-internal.h>
+#include <freeDiameter/version.h>
+
+#ifdef FD_PROJECT_VERSION_HG
+# define FD_LIBFDCORE_VERSION \
+	_stringize(FD_PROJECT_VERSION_MAJOR) "." _stringize(FD_PROJECT_VERSION_MINOR) "." _stringize(FD_PROJECT_VERSION_REV) "-" FD_PROJECT_VERSION_HG_VAL
+#else
+# define FD_LIBFDCORE_VERSION \
+	_stringize(FD_PROJECT_VERSION_MAJOR) "." _stringize(FD_PROJECT_VERSION_MINOR) "." _stringize(FD_PROJECT_VERSION_REV)
+#endif
+
+const char fd_core_version[] = FD_LIBFDCORE_VERSION;