changeset 16:013ce9851131

Started including TLS code
author Sebastien Decugis <sdecugis@nict.go.jp>
date Fri, 02 Oct 2009 18:57:06 +0900
parents 050f4f6f9f2a
children ab3c58d88be3
files freeDiameter/CMakeLists.txt freeDiameter/fD.h freeDiameter/main.c freeDiameter/p_expiry.c freeDiameter/p_psm.c freeDiameter/peers.c include/freeDiameter/freeDiameter.h
diffstat 7 files changed, 426 insertions(+), 182 deletions(-) [+]
line wrap: on
line diff
--- a/freeDiameter/CMakeLists.txt	Fri Oct 02 17:46:14 2009 +0900
+++ b/freeDiameter/CMakeLists.txt	Fri Oct 02 18:57:06 2009 +0900
@@ -26,6 +26,10 @@
 		fdd.tab.h
 	)
 
+# Require GNU TLS for building the daemon
+FIND_PACKAGE(GNUTLS REQUIRED)
+INCLUDE_DIRECTORIES(${GNUTLS_INCLUDE_DIRS})
+SET(FD_LIBS ${FD_LIBS} ${GNUTLS_LIBRARIES})
 
 # Building the executable
 ADD_EXECUTABLE(freeDiameterd ${FD_COMMON_SRC} ${FD_COMMON_GEN_SRC} main.c)
--- a/freeDiameter/fD.h	Fri Oct 02 17:46:14 2009 +0900
+++ b/freeDiameter/fD.h	Fri Oct 02 18:57:06 2009 +0900
@@ -150,11 +150,11 @@
 
 /* Events codespace for struct fd_peer->p_events */
 enum {
-	/* request to terminate this peer : disconnect, requeue all messages */
-	 FDEVP_TERMINATE = 2000
+	/* Dump all info about this peer in the debug log */
+	 FDEVP_DUMP_ALL = 2000
 	
-	/* Dump all info about this peer in the debug log */
-	,FDEVP_DUMP_ALL
+	/* request to terminate this peer : disconnect, requeue all messages */
+	,FDEVP_TERMINATE
 	
 	/* A message was received in the peer */
 	,FDEVP_MSG_INCOMING
@@ -163,6 +163,8 @@
 	,FDEVP_PSM_TIMEOUT
 };
 const char * fd_pev_str(int event);
+#define CHECK_EVENT( _e ) \
+	(((int)(_e) >= FDEVP_DUMP_ALL) && ((int)(_e) <= FDEVP_PSM_TIMEOUT))
 
 /* Structure to store a sent request */
 struct sentreq {
@@ -173,6 +175,9 @@
 /* Functions */
 int fd_peer_fini();
 void fd_peer_dump_list(int details);
+void fd_peer_dump(struct fd_peer * peer, int details);
+int fd_peer_alloc(struct fd_peer ** ptr);
+int fd_peer_free(struct fd_peer ** ptr);
 /* fd_peer_add declared in freeDiameter.h */
 
 /* Peer expiry */
--- a/freeDiameter/main.c	Fri Oct 02 17:46:14 2009 +0900
+++ b/freeDiameter/main.c	Fri Oct 02 18:57:06 2009 +0900
@@ -37,6 +37,12 @@
 
 #include <signal.h>
 #include <getopt.h>
+#include <locale.h>
+
+#ifdef GCRY_THREAD_OPTION_PTHREAD_IMPL
+GCRY_THREAD_OPTION_PTHREAD_IMPL;
+#endif /* GCRY_THREAD_OPTION_PTHREAD_IMPL */
+
 
 /* forward declarations */
 static void * sig_hdl(void * arg);
@@ -61,6 +67,7 @@
 	
 	/* Initialize the library */
 	CHECK_FCT( fd_lib_init() );
+	TRACE_DEBUG(INFO, "libfreeDiameter initialized.");
 	
 	/* Name this thread */
 	fd_log_threadname("Main");
@@ -71,6 +78,15 @@
 	/* Parse the command-line */
 	CHECK_FCT(  main_cmdline(argc, argv)  );
 	
+	/* Initialize gnutls */
+	CHECK_GNUTLS_DO( gnutls_global_init(), return EINVAL );
+	if ( ! gnutls_check_version(GNUTLS_VERSION) ) {
+		fprintf(stderr, "The GNUTLS library is too old; found '%s', need '" GNUTLS_VERSION "'\n", gnutls_check_version(NULL));
+		return EINVAL;
+	} else {
+		TRACE_DEBUG(INFO, "GNUTLS library '%s' initialized.", gnutls_check_version(NULL));
+	}
+	
 	/* Allow SIGINT and SIGTERM from this point */
 	CHECK_POSIX(  pthread_create(&sig_th, NULL, sig_hdl, NULL)  );
 	
@@ -141,6 +157,8 @@
 	
 	CHECK_FCT_DO( fd_thr_term(&sig_th), /* continue */ );
 	
+	gnutls_global_deinit();
+	
 	return ret;
 }
 
@@ -167,21 +185,23 @@
 {
 	int c;
 	int option_index = 0;
+	char * locale;
 	
       	struct option long_options[] = {
-		{ "help",	0, NULL, 'h' },
-		{ "version",	0, NULL, 'V' },
-		{ "config",	1, NULL, 'c' },
-		{ "debug",	0, NULL, 'd' },
-		{ "quiet",	0, NULL, 'q' },
-		{ NULL,	0, NULL, 0 }
+		{ "help",	no_argument, 		NULL, 'h' },
+		{ "version",	no_argument, 		NULL, 'V' },
+		{ "config",	required_argument, 	NULL, 'c' },
+		{ "debug",	no_argument, 		NULL, 'd' },
+		{ "quiet",	no_argument, 		NULL, 'q' },
+		{ "dbglocale",	optional_argument, 	NULL, 'l' },
+		{ NULL,		0, 			NULL, 0 }
 	};
 	
 	TRACE_ENTRY("%d %p", argc, argv);
 	
 	/* Loop on arguments */
 	while (1) {
-		c = getopt_long (argc, argv, "hVc:dq", long_options, &option_index);
+		c = getopt_long (argc, argv, "hVc:dql:", long_options, &option_index);
 		if (c == -1) 
 			break;	/* Exit from the loop.  */
 		
@@ -199,6 +219,16 @@
 				fd_g_config->cnf_file = optarg;
 				break;
 
+			case 'l':	/* Change the locale.  */
+				locale = setlocale(LC_ALL, optarg?:"");
+				if (locale) {
+					TRACE_DEBUG(INFO, "Locale set to: %s", optarg ?: locale);
+				} else {
+					TRACE_DEBUG(INFO, "Unable to set locale (%s)", optarg);
+					return EINVAL;
+				}
+				break;
+
 			case 'd':	/* Increase verbosity of debug messages.  */
 				fd_g_debug_lvl++;
 				break;
@@ -209,11 +239,11 @@
 
 			case '?':	/* Invalid option.  */
 				/* `getopt_long' already printed an error message.  */
-				TRACE_DEBUG(INFO, "getopt_long found an invalid character\n");
+				TRACE_DEBUG(INFO, "getopt_long found an invalid character");
 				return EINVAL;
 
 			default:	/* bug: option not considered.  */
-				TRACE_DEBUG(INFO, "A command-line option is missing in parser: %c\n", c);
+				TRACE_DEBUG(INFO, "A command-line option is missing in parser: %c", c);
 				ASSERT(0);
 				return EINVAL;
 		}
@@ -266,6 +296,7 @@
 		"                           default location (%s).\n", DEFAULT_CONF_FILE);
  	printf( "\nDebug:\n"
   		"  These options are mostly useful for developers\n"
+  		"  -l, --dbglocale        Set the locale for error messages\n"
   		"  -d, --debug            Increase verbosity of debug messages\n"
   		"  -q, --quiet            Decrease verbosity then remove debug messages\n");
 }
--- a/freeDiameter/p_expiry.c	Fri Oct 02 17:46:14 2009 +0900
+++ b/freeDiameter/p_expiry.c	Fri Oct 02 18:57:06 2009 +0900
@@ -35,11 +35,63 @@
 
 #include "fD.h"
 
+/* Delay for garbage collection of expired threads, in seconds */
+#define GC_TIME		60
+
 static pthread_t       exp_thr;
+static pthread_t       gc_thr;
 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/garbage" );
+	TRACE_ENTRY( "" );
+	
+	do {
+		struct fd_list * li, purge = FD_LIST_INITIALIZER(purge);
+		
+		pthread_testcancel();
+		sleep(GC_TIME);
+		
+		/* 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;
+			
+			if (peer->p_hdr.info.pi_state != STATE_ZOMBIE)
+				continue;
+			
+			if (peer->p_hdr.info.pi_flags.exp == PI_EXP_NONE)
+				continue; /* This peer was not supposed to expire, keep it in the list */
+			
+			/* 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);
+			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_event_send(fd_g_config->cnf_main_ev, FDEV_TERMINATE, NULL), );
+	return NULL;
+}
+
+
 static void * exp_th_fct(void * arg)
 {
 	fd_log_threadname ( "Peers/expire" );
@@ -97,6 +149,7 @@
 {
 	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;
 }
 
@@ -112,6 +165,7 @@
 	}
 	
 	CHECK_POSIX( pthread_mutex_unlock(&exp_mtx) );
+	CHECK_FCT_DO( fd_thr_term(&gc_thr), );
 	return 0;
 }
 
--- a/freeDiameter/p_psm.c	Fri Oct 02 17:46:14 2009 +0900
+++ b/freeDiameter/p_psm.c	Fri Oct 02 18:57:06 2009 +0900
@@ -36,7 +36,7 @@
 #include "fD.h"
 
 const char *peer_state_str[] = { 
-	  "STATE_ZOMBIE"
+	  "STATE_NEW"
 	, "STATE_OPEN"
 	, "STATE_CLOSED"
 	, "STATE_CLOSING"
@@ -45,6 +45,7 @@
 	, "STATE_WAITCEA"
 	, "STATE_SUSPECT"
 	, "STATE_REOPEN"
+	, "STATE_ZOMBIE"
 	};
 
 const char * fd_pev_str(int event)
@@ -122,6 +123,7 @@
 #endif
 }
 
+/* Wait for the next event in the PSM, or timeout */
 static int psm_ev_timedget(struct fd_peer * peer, int *code, void ** data)
 {
 	struct fd_event * ev;
@@ -141,13 +143,15 @@
 	}
 	
 	return 0;
-}	
+}
 
-/* The state machine thread */
+/* The state machine thread (controler) */
 static void * p_psm_th( void * arg )
 {
 	struct fd_peer * peer = (struct fd_peer *)arg;
 	int created_started = started;
+	int event;
+	void * ev_data;
 	
 	CHECK_PARAMS_DO( CHECK_PEER(peer), ASSERT(0) );
 	
@@ -160,12 +164,12 @@
 		fd_log_threadname ( buf );
 	}
 	
-	/* Wait that the PSM are authorized to start in the daemon */
-	CHECK_FCT_DO( fd_psm_waitstart(), goto end );
-	
 	/* The state machine starts in CLOSED state */
 	peer->p_hdr.info.pi_state = STATE_CLOSED;
 	
+	/* 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) {
 		psm_next_timeout(peer, 0, INCNX_TIMEOUT);
@@ -173,27 +177,68 @@
 		psm_next_timeout(peer, created_started ? 0 : 1, 0);
 	}
 	
-psm:
-	do {
-		int event;
-		void * ev_data;
-		
-		/* Get next event */
-		CHECK_FCT_DO( psm_ev_timedget(peer, &event, &ev_data), goto end );
-		TRACE_DEBUG(FULL, "'%s'\t<-- '%s'\t(%p)\t'%s'",
-				STATE_STR(peer->p_hdr.info.pi_state),
-				fd_pev_str(event), ev_data,
-				peer->p_hdr.info.pi_diamid);
-		
-		/* Now, the action depends on the current state and the incoming event */
-		
+psm_loop:
+	/* Get next event */
+	CHECK_FCT_DO( psm_ev_timedget(peer, &event, &ev_data), goto psm_end );
+	TRACE_DEBUG(FULL, "'%s'\t<-- '%s'\t(%p)\t'%s'",
+			STATE_STR(peer->p_hdr.info.pi_state),
+			fd_pev_str(event), ev_data,
+			peer->p_hdr.info.pi_diamid);
+
+	/* Now, the action depends on the current state and the incoming event */
+
+	/* The following two states are impossible */
+	ASSERT( peer->p_hdr.info.pi_state != STATE_NEW );
+	ASSERT( peer->p_hdr.info.pi_state != STATE_ZOMBIE );
+
+	/* Purge invalid events */
+	if (!CHECK_EVENT(event)) {
+		TRACE_DEBUG(INFO, "Invalid event received in PSM '%s' : %d", peer->p_hdr.info.pi_diamid, event);
+		goto psm_loop;
+	}
+
+	/* Handle the (easy) debug event now */
+	if (event == FDEVP_DUMP_ALL) {
+		fd_peer_dump(peer, ANNOYING);
+		goto psm_loop;
+	}
+
+	/* Requests to terminate the peer object */
+	if (event == FDEVP_TERMINATE) {
+		switch (peer->p_hdr.info.pi_state) {
+			case STATE_CLOSING:
+			case STATE_WAITCNXACK:
+			case STATE_WAITCNXACK_ELEC:
+			case STATE_WAITCEA:
+			case STATE_SUSPECT:
+				/* In these cases, we just cleanup the peer object and terminate now */
+				TODO("Cleanup the PSM: terminate connection object, ...");
+			case STATE_CLOSED:
+				/* Then we just terminate the PSM */
+				goto psm_end;
+				
+			case STATE_OPEN:
+			case STATE_REOPEN:
+				/* We cannot just close the conenction, we have to send a DPR first */
+				TODO("Send DPR, mark the peer as CLOSING");
+				goto psm_loop;
+		}
+	}
 	
-	} while (1);	
-	
+	/* MSG_RECEIVED: fd_p_expi_update(struct fd_peer * peer ) */
+	/* If timeout or OPEN : call cb if defined */
+
+	/* Default action : the handling has not yet been implemented. */
+	TODO("Missing handler in PSM : '%s'\t<-- '%s'", STATE_STR(peer->p_hdr.info.pi_state), fd_pev_str(event));
+	if (event == FDEVP_PSM_TIMEOUT) {
+		/* We have not handled timeout in this state, let's postpone next alert */
+		psm_next_timeout(peer, 0, 60);
+	}
 	
-end:	
-	/* set STATE_ZOMBIE */
-	pthread_cleanup_pop(1);
+	goto psm_loop;
+	
+psm_end:
+	pthread_cleanup_pop(1); /* set STATE_ZOMBIE */
 	return NULL;
 }	
 	
@@ -204,8 +249,15 @@
 int fd_psm_begin(struct fd_peer * peer )
 {
 	TRACE_ENTRY("%p", peer);
-	TODO("");
-	return ENOTSUP;
+	
+	/* Check the peer and state are OK */
+	CHECK_PARAMS( CHECK_PEER(peer) && (peer->p_hdr.info.pi_state == STATE_NEW) );
+	
+	/* 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) */
@@ -213,7 +265,11 @@
 {
 	TRACE_ENTRY("%p", peer);
 	CHECK_PARAMS( CHECK_PEER(peer) );
-	CHECK_FCT( fd_event_send(peer->p_events, FDEVP_TERMINATE, NULL) );
+	if (peer->p_hdr.info.pi_state != STATE_ZOMBIE) {
+		CHECK_FCT( fd_event_send(peer->p_events, FDEVP_TERMINATE, NULL) );
+	} else {
+		TRACE_DEBUG(FULL, "Peer '%s' was already terminated", peer->p_hdr.info.pi_diamid);
+	}
 	return 0;
 }
 
@@ -224,7 +280,8 @@
 	TODO("Cancel PSM thread");
 	TODO("Cancel IN thread");
 	TODO("Cancel OUT thread");
-	TODO("Cleanup the connection");
+	TODO("Cleanup the peer connection object");
+	TODO("Cleanup the message queues (requeue)");
 	return;
 }
 
--- a/freeDiameter/peers.c	Fri Oct 02 17:46:14 2009 +0900
+++ b/freeDiameter/peers.c	Fri Oct 02 18:57:06 2009 +0900
@@ -35,63 +35,13 @@
 
 #include "fD.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;
 
-/* Terminate peer module (destroy all peers) */
-int fd_peer_fini()
-{
-	struct fd_list * li;
-	TRACE_ENTRY();
-	
-	CHECK_FCT_DO(fd_p_expi_fini(), /* continue */);
-	
-	TRACE_DEBUG(INFO, "Sending signal to terminate to all peer connections");
-	
-	CHECK_FCT_DO( pthread_rwlock_rdlock(&fd_g_peers_rw), /* continue */ );
-	/* For each peer in the list, ... */
-	for (li = fd_g_peers.next; li != &fd_g_peers; li = li->next) {
-		struct fd_peer * np = (struct fd_peer *)li;
-		CHECK_FCT_DO( fd_psm_terminate(np), /* continue */ );
-	}
-	CHECK_FCT_DO( pthread_rwlock_unlock(&fd_g_peers_rw), /* continue */ );
-	
-	TODO("Give some time to all PSM, then destroy remaining threads");
-	/* fd_psm_abord(struct fd_peer * peer ) */
-	
-	return 0;
-}
-
-/* Dump the list of peers */
-void fd_peer_dump_list(int details)
-{
-	struct fd_list * li;
-	
-	fd_log_debug("Dumping list of peers :\n");
-	CHECK_FCT_DO( pthread_rwlock_rdlock(&fd_g_peers_rw), /* continue */ );
-	
-	for (li = fd_g_peers.next; li != &fd_g_peers; li = li->next) {
-		struct fd_peer * np = (struct fd_peer *)li;
-		if (np->p_eyec != EYEC_PEER) {
-			fd_log_debug("  Invalid entry @%p !\n", li);
-			continue;
-		}
-		
-		fd_log_debug("   %s\t%s", STATE_STR(np->p_hdr.info.pi_state), np->p_hdr.info.pi_diamid);
-		if (details > INFO) {
-			fd_log_debug("\t(rlm:%s)", np->p_hdr.info.pi_realm);
-			if (np->p_hdr.info.pi_prodname)
-				fd_log_debug("\t['%s' %u]", np->p_hdr.info.pi_prodname, np->p_hdr.info.pi_firmrev);
-			fd_log_debug("\t(from %s)", np->p_dbgorig);
-		}
-		fd_log_debug("\n");
-	}
-	
-	CHECK_FCT_DO( pthread_rwlock_unlock(&fd_g_peers_rw), /* continue */ );
-}
 
 /* Alloc / reinit a peer structure. if *ptr is not NULL, it must already point to a valid struct fd_peer. */
-static int fd_sp_reinit(struct fd_peer ** ptr)
+int fd_peer_alloc(struct fd_peer ** ptr)
 {
 	struct fd_peer *p;
 	
@@ -125,6 +75,86 @@
 	return 0;
 }
 
+/* Add a new peer entry */
+int fd_peer_add ( struct peer_info * info, char * orig_dbg, void (*cb)(struct peer_info *, void *), void * cb_data )
+{
+	struct fd_peer *p = NULL;
+	struct fd_list * li;
+	int ret = 0;
+	TRACE_ENTRY("%p %p %p %p", info, orig_dbg, cb, cb_data);
+	CHECK_PARAMS(info && info->pi_diamid);
+	
+	/* Create a structure to contain the new peer information */
+	CHECK_FCT( fd_peer_alloc(&p) );
+	
+	/* Copy the informations from the parameters received */
+	CHECK_MALLOC( p->p_hdr.info.pi_diamid = strdup(info->pi_diamid) );
+	if (info->pi_realm) {
+		CHECK_MALLOC( p->p_hdr.info.pi_realm = strdup(info->pi_realm) );
+	}
+	
+	p->p_hdr.info.pi_flags.pro3 = info->pi_flags.pro3;
+	p->p_hdr.info.pi_flags.pro4 = info->pi_flags.pro4;
+	p->p_hdr.info.pi_flags.alg  = info->pi_flags.alg;
+	p->p_hdr.info.pi_flags.sec  = info->pi_flags.sec;
+	p->p_hdr.info.pi_flags.exp  = info->pi_flags.exp;
+	
+	p->p_hdr.info.pi_lft     = info->pi_lft;
+	p->p_hdr.info.pi_streams = info->pi_streams;
+	p->p_hdr.info.pi_port    = info->pi_port;
+	p->p_hdr.info.pi_tctimer = info->pi_tctimer;
+	p->p_hdr.info.pi_twtimer = info->pi_twtimer;
+	
+	/* Move the items from one list to the other */
+	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("unknown") );
+	}
+	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) );
+	
+	for (li = fd_g_peers.next; li != &fd_g_peers; li = li->next) {
+		struct fd_peer * prev = (struct fd_peer *)li;
+		int cmp = strcasecmp( p->p_hdr.info.pi_diamid, prev->p_hdr.info.pi_diamid );
+		if (cmp < 0)
+			continue;
+		if (cmp == 0)
+			ret = EEXIST;
+		break;
+	}
+	
+	/* We can insert the new peer object */
+	if (! ret) {
+		/* Update expiry list */
+		CHECK_FCT_DO( ret = fd_p_expi_update( p ), goto out );
+		
+		/* Insert the new element in the list */
+		fd_list_insert_before( li, &p->p_hdr.chain );
+	}
+
+out:	
+	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;
+}
+
+
 #define free_null( _v ) 	\
 	if (_v) {		\
 		free(_v);	\
@@ -139,7 +169,7 @@
 	}
 
 /* Destroy a structure once all cleanups have been performed */
-static int fd_sp_destroy(struct fd_peer ** ptr)
+int fd_peer_free(struct fd_peer ** ptr)
 {
 	struct fd_peer *p;
 	void * t;
@@ -209,85 +239,133 @@
 	return 0;
 }
 
-
-/* Add a new peer entry */
-int fd_peer_add ( struct peer_info * info, char * orig_dbg, void (*cb)(struct peer_info *, void *), void * cb_data )
+/* Terminate peer module (destroy all peers) */
+int fd_peer_fini()
 {
-	struct fd_peer *p = NULL;
 	struct fd_list * li;
-	int ret = 0;
-	TRACE_ENTRY("%p %p %p %p", info, orig_dbg, cb, cb_data);
-	CHECK_PARAMS(info && info->pi_diamid);
+	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;
+		
+		if (peer->p_hdr.info.pi_state != STATE_ZOMBIE) {
+			CHECK_FCT_DO( fd_psm_terminate(peer), /* 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 */ );
 	
-	/* Create a structure to contain the new peer information */
-	CHECK_FCT( fd_sp_reinit(&p) );
+	if (!list_empty) {
+		CHECK_SYS(  clock_gettime(CLOCK_REALTIME, &now)  );
+		TRACE_DEBUG(INFO, "Waiting for connections shutdown... (%d sec max)", DPR_TIMEOUT);
+		wait_until.tv_sec  = now.tv_sec + DPR_TIMEOUT;
+		wait_until.tv_nsec = now.tv_nsec;
+	}
 	
-	/* Copy the informations from the parameters received */
-	CHECK_MALLOC( p->p_hdr.info.pi_diamid = strdup(info->pi_diamid) );
-	if (info->pi_realm) {
-		CHECK_MALLOC( p->p_hdr.info.pi_realm = strdup(info->pi_realm) );
+	while ((!list_empty) && (TS_IS_INFERIOR(&now, &wait_until))) {
+		
+		/* Allow the PSM(s) to execute */
+		pthread_yield();
+		
+		/* 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;
+			if (peer->p_hdr.info.pi_state == 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 */ );
 	}
 	
-	p->p_hdr.info.pi_flags.pro3 = info->pi_flags.pro3;
-	p->p_hdr.info.pi_flags.pro4 = info->pi_flags.pro4;
-	p->p_hdr.info.pi_flags.alg  = info->pi_flags.alg;
-	p->p_hdr.info.pi_flags.sec  = info->pi_flags.sec;
-	p->p_hdr.info.pi_flags.exp  = info->pi_flags.exp;
+	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);
+			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 */ );
+	}
 	
-	p->p_hdr.info.pi_lft     = info->pi_lft;
-	p->p_hdr.info.pi_streams = info->pi_streams;
-	p->p_hdr.info.pi_port    = info->pi_port;
-	p->p_hdr.info.pi_tctimer = info->pi_tctimer;
-	p->p_hdr.info.pi_twtimer = info->pi_twtimer;
+	/* Free memory objects of all peers */
+	while (!FD_IS_LIST_EMPTY(&purge)) {
+		struct fd_peer * peer = (struct fd_peer *)(purge.next);
+		fd_list_unlink(&peer->p_hdr.chain);
+		fd_peer_free(&peer);
+	}
 	
-	/* Move the items from one list to the other */
-	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);
-		}
-	
-	p->p_hdr.info.pi_sec_module = info->pi_sec_module;
-	memcpy(&p->p_hdr.info.pi_sec_data, &info->pi_sec_data, sizeof(info->pi_sec_data));
+	return 0;
+}
+
+/* Dump info of one peer */
+void fd_peer_dump(struct fd_peer * peer, int details)
+{
+	if (peer->p_eyec != EYEC_PEER) {
+		fd_log_debug("  Invalid peer @ %p !\n", peer);
+		return;
+	}
+
+	fd_log_debug(">  %s\t%s", STATE_STR(peer->p_hdr.info.pi_state), peer->p_hdr.info.pi_diamid);
+	if (details > INFO) {
+		fd_log_debug("\t(rlm:%s)", peer->p_hdr.info.pi_realm);
+		if (peer->p_hdr.info.pi_prodname)
+			fd_log_debug("\t['%s' %u]", peer->p_hdr.info.pi_prodname, peer->p_hdr.info.pi_firmrev);
+	}
+	fd_log_debug("\n");
+	if (details > FULL) {
+		/* Dump all info */
+		fd_log_debug("\tEntry origin : %s\n", peer->p_dbgorig);
+		fd_log_debug("\tFlags : %s%s%s%s%s - %s%s%s\n", 
+				peer->p_hdr.info.pi_flags.pro3 == PI_P3_DEFAULT ? "" :
+					(peer->p_hdr.info.pi_flags.pro3 == PI_P3_IP ? "IP." : "IPv6."),
+				peer->p_hdr.info.pi_flags.pro4 == PI_P4_DEFAULT ? "" :
+					(peer->p_hdr.info.pi_flags.pro4 == PI_P4_TCP ? "TCP." : "SCTP."),
+				peer->p_hdr.info.pi_flags.alg ? "PrefTCP." : "",
+				peer->p_hdr.info.pi_flags.sec == PI_SEC_DEFAULT ? "" :
+					(peer->p_hdr.info.pi_flags.sec == PI_SEC_NONE ? "IPSec." : "InbandTLS."),
+				peer->p_hdr.info.pi_flags.exp ? "Expire." : "",
+				peer->p_hdr.info.pi_flags.inband & PI_INB_NONE ? "InbandIPsecOK." : "",
+				peer->p_hdr.info.pi_flags.inband & PI_INB_TLS ?  "InbandTLSOK." : "",
+				peer->p_hdr.info.pi_flags.relay ? "Relay (0xffffff)" : "No relay"
+				);
+		fd_log_debug("\tLifetime : %d sec\n", peer->p_hdr.info.pi_lft);
+		
+		TODO("Dump remaining useful information");
+	}
+}
+
+/* Dump the list of peers */
+void fd_peer_dump_list(int details)
+{
+	struct fd_list * li;
 	
-	/* The internal data */
-	if (orig_dbg) {
-		CHECK_MALLOC( p->p_dbgorig = strdup(orig_dbg) );
-	} else {
-		CHECK_MALLOC( p->p_dbgorig = strdup("unknown") );
-	}
-	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) );
+	fd_log_debug("Dumping list of peers :\n");
+	CHECK_FCT_DO( pthread_rwlock_rdlock(&fd_g_peers_rw), /* continue */ );
 	
 	for (li = fd_g_peers.next; li != &fd_g_peers; li = li->next) {
-		struct fd_peer * prev = (struct fd_peer *)li;
-		int cmp = strcasecmp( p->p_hdr.info.pi_diamid, prev->p_hdr.info.pi_diamid );
-		if (cmp < 0)
-			continue;
-		if (cmp == 0)
-			ret = EEXIST;
-		break;
+		struct fd_peer * np = (struct fd_peer *)li;
+		fd_peer_dump(np, details);
 	}
 	
-	/* We can insert the new peer object */
-	if (! ret) {
-		/* Update expiry list */
-		CHECK_FCT_DO( ret = fd_p_expi_update( p ), goto out );
-		
-		/* Insert the new element in the list */
-		fd_list_insert_before( li, &p->p_hdr.chain );
-	}
+	CHECK_FCT_DO( pthread_rwlock_unlock(&fd_g_peers_rw), /* continue */ );
+}
 
-out:	
-	CHECK_POSIX( pthread_rwlock_unlock(&fd_g_peers_rw) );
-	if (ret) {
-		CHECK_FCT( fd_sp_destroy(&p) );
-	} else {
-		CHECK_FCT( fd_psm_begin(p) );
-	}
-	return ret;
-}
--- a/include/freeDiameter/freeDiameter.h	Fri Oct 02 17:46:14 2009 +0900
+++ b/include/freeDiameter/freeDiameter.h	Fri Oct 02 18:57:06 2009 +0900
@@ -38,6 +38,24 @@
 
 
 #include <freeDiameter/libfreeDiameter.h>
+#include <gnutls/gnutls.h>
+#include <gnutls/x509.h>
+
+/* GNUTLS version */
+#ifndef GNUTLS_VERSION
+#define GNUTLS_VERSION LIBGNUTLS_VERSION
+#endif /* GNUTLS_VERSION */
+
+/* Check the return value of a GNUTLS function, log and propagate */
+#define CHECK_GNUTLS_DO( __call__, __fallback__ ) {						\
+	int __ret__;										\
+	TRACE_DEBUG_ALL( "Check FCT: " #__call__ );						\
+	__ret__ = (__call__);									\
+	if (__ret__ < 0) {									\
+		TRACE_DEBUG(INFO, "Error in '" #__call__ "':\t%s", gnutls_strerror(__ret__));	\
+		__fallback__;									\
+	}											\
+}
 
 
 /* Structure to hold the configuration of the freeDiameter daemon */
@@ -70,6 +88,11 @@
 		unsigned tls_alg: 1;	/* TLS algorithm for initiated cnx. 0: separate port. 1: inband-security (old) */
 	} 		 cnf_flags;
 	
+	struct {
+			/* GNUTLS global state */
+			/* Server credential(s) */
+	} 		 cnf_sec_data;
+	
 	uint32_t	 cnf_orstateid;	/* The value to use in Origin-State-Id, default to random value */
 	struct dictionary *cnf_dict;	/* pointer to the global dictionary */
 	struct fifo	  *cnf_main_ev;	/* events for the daemon's main (struct fd_event items) */
@@ -150,7 +173,7 @@
 /* States of a peer */
 enum peer_state {
 	/* Stable states */
-	STATE_ZOMBIE = 0,	/* The threads handling the peer are not running for some reason */
+	STATE_NEW = 0,		/* The peer has been just been created, PSM thread not started yet */
 	STATE_OPEN,		/* Connexion established */
 	
 	/* Peer state machine */
@@ -167,10 +190,14 @@
 	/* Failover state machine */
 	STATE_SUSPECT,		/* A DWR was sent and not answered within TwTime. Failover in progress. */
 	STATE_REOPEN,		/* Connection has been re-established, waiting for 3 DWR/DWA exchanges before putting back to service */
+	
+	/* Error state */
+	STATE_ZOMBIE		/* The PSM thread is not running anymore; it must be re-started or peer should be deleted. */
+#define STATE_MAX STATE_ZOMBIE
 };
 extern const char *peer_state_str[];
 #define STATE_STR(state) \
-	(((unsigned)(state)) <= STATE_REOPEN ? peer_state_str[((unsigned)(state)) ] : "<Invalid>")
+	(((unsigned)(state)) <= STATE_MAX ? peer_state_str[((unsigned)(state)) ] : "<Invalid>")
 
 /* Information about a remote peer. Same structure is used for creating a new entry, but not all fields are meaningful in that case */
 struct peer_info {
@@ -220,23 +247,6 @@
 	
 	struct fd_list	pi_endpoints;	/* Endpoint(s) of the remote peer (configured, discovered, or advertized). list of struct fd_endpoint. DNS resolved if empty. */
 	
-	/* TLS specific data -- the exact data pointed here depends on the security module in use (ex: gnutls, ...) */
-	enum {
-		PI_SEC_GNUTLS = 0,	/* The security module is GNUTLS, this is the default */
-		PI_SEC_OTHER		/* Another security module (TBD) */
-	}		pi_sec_module;
-	union {
-		/* Security data when pi_sec_module == PI_SEC_GNUTLS */
-		struct {
-			void *	CA;	/* Authority to use to validate this peer credentials (a CA or root certificate) -- use default if NULL */
-			void *	cred;	/* The (valid) credentials that the peer has presented */
-		} 	gnutls;
-		/* Security data when pi_sec_module == PI_SEC_OTHER */
-		struct {
-			void * dummy;	/* Something meaningful for the other security module */
-		} 	other;
-	} 		pi_sec_data;
-	
 	/* The remaining information must not be modified, and is not used for peer creation */
 	enum peer_state	pi_state;
 	uint32_t	pi_vendorid;	/* Content of the Vendor-Id AVP, or 0 by default */
@@ -244,6 +254,11 @@
 	char *		pi_prodname;	/* copy of UTF-8 Product-Name AVP (\0 terminated) */
 	uint32_t	pi_firmrev;	/* Content of the Firmware-Revision AVP */
 	struct fd_list	pi_apps;	/* applications advertised by the remote peer, except relay (pi_flags.relay) */
+	struct {
+		/* This is inspired from http://www.gnu.org/software/gnutls/manual/gnutls.html#ex_003ax509_002dinfo */
+		const gnutls_datum_t 	*cert_list; 	/* The (valid) credentials that the peer has presented */
+		unsigned int 		 cert_list_size;/* Number of certificates in the list */
+	} 		pi_sec_data;
 };
 
 struct peer_hdr {
"Welcome to our mercurial repository"