changeset 22:0b3b46da2c12

Progress on server code
author Sebastien Decugis <sdecugis@nict.go.jp>
date Mon, 19 Oct 2009 18:43:09 +0900
parents bef197f6826f
children db6c40b8b307
files doc/freediameter.conf.sample freeDiameter/CMakeLists.txt freeDiameter/cnxctx.c freeDiameter/config.c freeDiameter/fD.h freeDiameter/fdd.y freeDiameter/main.c freeDiameter/p_expiry.c freeDiameter/p_psm.c freeDiameter/peers.c freeDiameter/sctp.c freeDiameter/server.c freeDiameter/tcp.c include/freeDiameter/freeDiameter.h include/freeDiameter/libfreeDiameter.h libfreeDiameter/log.c
diffstat 16 files changed, 689 insertions(+), 262 deletions(-) [+]
line wrap: on
line diff
--- a/doc/freediameter.conf.sample	Thu Oct 08 20:05:16 2009 +0900
+++ b/doc/freediameter.conf.sample	Mon Oct 19 18:43:09 2009 +0900
@@ -183,11 +183,11 @@
 #  No_TCP; No_SCTP; No_IP; No_IPv6; Prefer_TCP; TLS_old_method;
 #  No_TLS;       # assume transparent security instead of TLS
 #  Port = 3868;  # The port to connect to
-#  SCTP_streams = 30;
 #  TcTimer = 30;
 #  TwTimer = 30;
 #  ConnectTo = "202.249.37.5";
 #  ConnectTo = "2001:200:903:2::202:1";
+#  TLS_Prio = "NORMAL";
 # Examples:
 #ConnectPeer = "aaa.wide.ad.jp";
 #ConnectPeer = "old.diameter.serv" { TcTimer = 60; TLS_old_method; No_SCTP; } ;
@@ -200,11 +200,11 @@
 Port = 3866;
 SecPort = 3867;
 TLS_old_method;
-No_IP;
+No_SCTP;
 Prefer_TCP;
 SCTP_streams = 50;
-ListenOn = "202.249.37.5";
-ListenOn = "2001:200:903:2::202:1";
+#ListenOn = "202.249.37.5";
+#ListenOn = "2001:200:903:2::202:1";
 TcTimer = 60;
 TwTimer = 6;
 NoRelay;
@@ -212,7 +212,7 @@
 LoadExtension = "extensions/dict_nasreq.fdx";
 LoadExtension = "extensions/dict_eap.fdx";
 ConnectPeer = "jules.nautilus6.org" ;
-ConnectPeer = "aaa.nautilus6.org" { No_TLS; No_IP; No_TCP; SCTP_streams = 60; } ;
+ConnectPeer = "aaa.nautilus6.org" { No_TLS; No_IP; } ;
 TLS_Cred = "/etc/openssl-ca/clients/certs/test.cert" , "/etc/openssl-ca/clients/privkeys/test.key.pem";
 TLS_CA = "/etc/openssl-ca/public-www/cacert.pem";
 # TLS_CRL = "/etc/openssl-ca/public-www/crl.pem";
--- a/freeDiameter/CMakeLists.txt	Thu Oct 08 20:05:16 2009 +0900
+++ b/freeDiameter/CMakeLists.txt	Mon Oct 19 18:43:09 2009 +0900
@@ -20,6 +20,7 @@
 	p_expiry.c
 	p_psm.c
 	server.c
+	tcp.c
 	)
 
 IF(NOT DISABLE_SCTP)
--- a/freeDiameter/cnxctx.c	Thu Oct 08 20:05:16 2009 +0900
+++ b/freeDiameter/cnxctx.c	Mon Oct 19 18:43:09 2009 +0900
@@ -35,21 +35,53 @@
 
 #include "fD.h"
 
+/* 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.
+ */
+
+/* Note: this file could be moved to libfreeDiameter instead, but since it uses gnuTLS we prefer to keep it in the daemon */
+
+/* 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).
+ *    - 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
+ *    - fd_cnx_getendpoints : get the endpoints (IP) of the connection
+ *
+ * 4) End
+ *    - fd_cnx_destroy
+ */
+
 /* The connection context structure */
 struct cnxctx {
+	char		cc_id[60];	/* The name of this connection */
+	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_proto;	/* IPPROTO_TCP or IPPROTO_SCTP */
 	int		cc_tls;		/* Is TLS already started ? */
-	
+
 	struct fifo *	cc_events;	/* Events occuring on the connection */
 	pthread_t	cc_mgr;		/* manager thread for the connection */
 	struct fifo *	cc_incoming;	/* FIFO queue of messages received on the connection */
-	
-	uint16_t	cc_port;	/* Remote port of the connection, when we are client */
-	struct fd_list	cc_ep_remote;	/* The remote address(es) of the connection */
-	struct fd_list	cc_ep_local;	/* The local address(es) of the connection */
-	
+	struct fifo *	cc_alt;		/* alternate fifo to send FDEVP_CNX_MSG_RECV events to. */
+
 	/* If cc_proto == SCTP */
 	struct	{
 		int		str_out;/* Out streams */
@@ -57,13 +89,13 @@
 		int		pairs;	/* max number of pairs ( = min(in, out)) */
 		int		next;	/* # of stream the next message will be sent to */
 	} 		cc_sctp_para;
-	
+
 	/* If cc_tls == true */
 	struct {
 		int				 mode; 		/* GNUTLS_CLIENT / GNUTLS_SERVER */
 		gnutls_session_t 		 session;	/* Session object (stream #0 in case of SCTP) */
 	}		cc_tls_para;
-	
+
 	/* If both conditions */
 	struct {
 		gnutls_session_t 		*res_sessions;	/* Sessions of other pairs of streams, resumed from the first */
@@ -72,62 +104,232 @@
 };
 
 
-/* Initialize a context structure from a socket */
-struct cnxctx * fd_cnx_init(int sock, int proto)
+/* Initialize a context structure */
+static struct cnxctx * fd_cnx_init(int full)
 {
 	struct cnxctx * conn = NULL;
-	
-	TRACE_ENTRY("%d %d", sock, proto);
-	CHECK_PARAMS_DO( (proto == IPPROTO_TCP) || (proto == IPPROTO_SCTP), return NULL);
-	
+
+	TRACE_ENTRY("%d", full);
+
 	CHECK_MALLOC_DO( conn = malloc(sizeof(struct cnxctx)), return NULL );
 	memset(conn, 0, sizeof(struct cnxctx));
-	
-	conn->cc_socket = sock;
-	conn->cc_proto  = proto;
-	
-	fd_list_init(&conn->cc_ep_remote, conn);
-	fd_list_init(&conn->cc_ep_local, conn);
+
+	if (full) {
+		CHECK_FCT_DO( fd_fifo_new ( &conn->cc_events ), return NULL );
+		CHECK_FCT_DO( fd_fifo_new ( &conn->cc_incoming ), return NULL );
+	}
+
+	return conn;
+}
+
+/* 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) || (!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);
+	} else {
+		((sSA6 *)sa)->sin6_port = htons(port);
+	}
+
+	/* Create the socket */
+	CHECK_FCT_DO( fd_tcp_create_bind_server( &cnx->cc_socket, sa, sizeof(sSS) ), goto error );
+
+	/* Generate the name for the connection object */
+	{
+		char addrbuf[INET6_ADDRSTRLEN];
+		int  rc;
+		rc = getnameinfo(sa, sizeof(sSS), 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), "Srv TCP [%s]:%hu (%d)", addrbuf, port, cnx->cc_socket);
+	}
+
+	cnx->cc_proto = IPPROTO_TCP;
+
+	return cnx;
+
+error:
+	fd_cnx_destroy(cnx);
+	return NULL;
+}
+#ifndef DISABLE_SCTP
+struct cnxctx * fd_cnx_serv_sctp(uint16_t port, struct fd_list * ep_list)
+{
+	struct cnxctx * cnx = NULL;
+	sSS dummy;
+	sSA * sa = (sSA *) &dummy;
+
+	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 );
+
+	/* Create the socket */
+	CHECK_FCT_DO( fd_sctp_create_bind_server( &cnx->cc_socket, ep_list, port ), goto error );
+
+	/* Generate the name for the connection object */
+	snprintf(cnx->cc_id, sizeof(cnx->cc_id), "Srv SCTP :%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;
+
+		case IPPROTO_SCTP:
+			CHECK_FCT(fd_sctp_listen(conn->cc_socket));
+			break;
+
+		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;
+	struct fd_endpoint * ep;
+
+	TRACE_ENTRY("%p", serv);
+	CHECK_PARAMS_DO(serv, return NULL);
 	
-	if (proto == IPPROTO_SCTP) {
-#ifndef DISABLE_SCTP
-		CHECK_FCT_DO( fd_sctp_get_str_info( sock, &conn->cc_sctp_para.str_in, &conn->cc_sctp_para.str_out ),
-				{ free(conn); return NULL; } );
-		conn->cc_sctp_para.pairs = (conn->cc_sctp_para.str_out < conn->cc_sctp_para.str_in) ? conn->cc_sctp_para.str_out : conn->cc_sctp_para.str_in;
-#else /* DISABLE_SCTP */
-		ASSERT(0);
-#endif /* DISABLE_SCTP */
+	CHECK_SYS_DO( cli_sock = accept(serv->cc_socket, (sSA *)&ss, &ss_len), return NULL );
+	
+	if (TRACE_BOOL(INFO)) {
+		fd_log_debug("%s - new client [", fd_cnx_getid(serv));
+		sSA_DUMP_NODE( &ss, AI_NUMERICHOST );
+		fd_log_debug("] connected.\n");
 	}
 	
-	return conn;
+	CHECK_MALLOC_DO( cli = fd_cnx_init(1), { shutdown(cli_sock, SHUT_RDWR); return NULL; } );
+	cli->cc_socket = cli_sock;
+	cli->cc_proto = serv->cc_proto;
+	
+	/* Generate the name for the connection object */
+	{
+		char addrbuf[INET6_ADDRSTRLEN];
+		char portbuf[10];
+		int  rc;
+		
+		/* Numeric values for debug */
+		rc = getnameinfo((sSA *)&ss, sizeof(sSS), addrbuf, sizeof(addrbuf), portbuf, sizeof(portbuf), NI_NUMERICHOST | NI_NUMERICSERV);
+		if (rc)
+			snprintf(addrbuf, sizeof(addrbuf), "[err:%s]", gai_strerror(rc));
+		
+		snprintf(cli->cc_id, sizeof(cli->cc_id), "Client %s [%s]:%s (%d) / serv (%d)", 
+				IPPROTO_NAME(cli->cc_proto), 
+				addrbuf, portbuf, 
+				cli->cc_socket, serv->cc_socket);
+		
+		/* Textual value for log messages */
+		rc = getnameinfo((sSA *)&ss, sizeof(sSS), cli->cc_remid, sizeof(cli->cc_remid), NULL, 0, NI_NUMERICHOST);
+		if (rc)
+			snprintf(cli->cc_remid, sizeof(cli->cc_remid), "[err:%s]", gai_strerror(rc));
+	}
+
+	/* SCTP-specific handlings */
+#ifndef DISABLE_SCTP
+	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 ), goto error );
+		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;
+	}
+#endif /* DISABLE_SCTP */
+
+	return cli;
+error:
+	fd_cnx_destroy(cli);
+	return NULL;
+}
+
+/* Client side: connect to a remote server */
+struct cnxctx * fd_cnx_cli_connect(int proto, const sSA * sa,  socklen_t addrlen)
+{
+
+	TODO("...");
+	return NULL;
+}
+
+/* Return a string describing the connection, for debug */
+char * fd_cnx_getid(struct cnxctx * conn)
+{
+	CHECK_PARAMS_DO( conn, return "" );
+	return conn->cc_id;
 }
 
 /* Start receving messages in clear (no TLS) on the connection */
 int fd_cnx_start_clear(struct cnxctx * conn)
 {
-	
+
 	TODO("...");
 	return ENOTSUP;
 }
 
 /* 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 fd_cnx_handshake(struct cnxctx * conn, int mode, char * priority)
 {
 	TRACE_ENTRY( "%p %d", conn, mode);
 	CHECK_PARAMS( conn && ( (mode == GNUTLS_CLIENT) || (mode == GNUTLS_SERVER) ) );
-	
+
 	/* Save the mode */
 	conn->cc_tls_para.mode = mode;
-	
+
 	/* Create the master session context */
 	CHECK_GNUTLS_DO( gnutls_init (&conn->cc_tls_para.session, mode), return ENOMEM );
-	
+
 	/* Set the algorithm suite */
+	TODO("Use overwrite priority if non NULL");
 	CHECK_GNUTLS_DO( gnutls_priority_set( conn->cc_tls_para.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 (conn->cc_tls_para.session, GNUTLS_CRD_CERTIFICATE, fd_g_config->cnf_sec_data.credentials), return EINVAL );
-	
+
 	/* Request the remote credentials as well */
 	if (mode == GNUTLS_SERVER) {
 		gnutls_certificate_server_set_request (conn->cc_tls_para.session, GNUTLS_CERT_REQUIRE);
@@ -145,23 +347,23 @@
 		ASSERT(0);
 #endif /* DISABLE_SCTP */
 	}
-	
+
 	/* 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\n", conn->cc_socket, gnutls_strerror(ret));
+					fd_log_debug("TLS Handshake failed on socket %d (%s) : %s\n", conn->cc_socket, conn->cc_id, gnutls_strerror(ret));
 				}
 				return EINVAL;
 			} );
-		
+
 		/* Now verify the remote credentials are valid -- only simple test here */
 		CHECK_GNUTLS_DO( gnutls_certificate_verify_peers2 (conn->cc_tls_para.session, &ret), return EINVAL );
 		if (ret) {
 			if (TRACE_BOOL(INFO)) {
-				fd_log_debug("TLS: Remote certificate invalid on socket %d :\n", conn->cc_socket);
+				fd_log_debug("TLS: Remote certificate invalid on socket %d (%s) :\n", conn->cc_socket, conn->cc_id);
 				if (ret & GNUTLS_CERT_INVALID)
 					fd_log_debug(" - The certificate is not trusted (unknown CA?)\n");
 				if (ret & GNUTLS_CERT_REVOKED)
@@ -176,30 +378,46 @@
 			return EINVAL;
 		}
 	}
-	
+
 	/* Other sessions in case of multi-stream SCTP are resumed from the master */
 	if ((conn->cc_proto == IPPROTO_SCTP) && (conn->cc_sctp_para.pairs > 0)) {
 #ifndef DISABLE_SCTP
 		TODO("Init and resume all additional sessions from the master one.");
 #endif /* DISABLE_SCTP */
 	}
-	
+
+	TODO("Start the connection state machine thread");
+
 	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)
 {
-	
+
 	TODO("...");
 	return ENOTSUP;
 }
 
-/* Get the list of endpoints (IP addresses) of the remote peer on this object */
-int fd_cnx_getendpoints(struct cnxctx * conn, struct fd_list * senti)
+/* Get the list of endpoints (IP addresses) of the local and remote peers on this conenction */
+int fd_cnx_getendpoints(struct cnxctx * conn, struct fd_list * local, struct fd_list * remote)
 {
+	TRACE_ENTRY("%p %p %p", conn, local, remote);
+	CHECK_PARAMS(conn);
 	
-	TODO("...");
+	if (local) {
+		/* Retrieve the local endpoint(s) of the connection */
+		TODO("TCP : getsockname");
+		TODO("SCTP: sctp_getladdrs / _sctp_getboundaddrs (waaad)");
+	}
+	
+	if (remote) {
+		/* Retrieve the peer endpoint(s) of the connection */
+		TODO("TCP : getpeername");
+		TODO("SCTP: sctp_getpaddrs");
+		
+	}
+
 	return ENOTSUP;
 }
 
@@ -207,25 +425,35 @@
 /* Get a string describing the remote peer address (ip address or fqdn) */
 char * fd_cnx_getremoteid(struct cnxctx * conn)
 {
-	
-	TODO("...");
-	return NULL;
+	CHECK_PARAMS_DO( conn, return "" );
+	return conn->cc_remid;
 }
 
 
 /* Receive next message. if timeout is not NULL, wait only until timeout */
 int fd_cnx_receive(struct cnxctx * conn, struct timespec * timeout, unsigned char **buf, size_t * len)
 {
-	
+
 	TODO("...");
 	return ENOTSUP;
 }
 
+/* Set / reset alternate FIFO list to send FDEVP_CNX_MSG_RECV to when message is received */
+int fd_cnx_recv_setaltfifo(struct cnxctx * conn, struct fifo * alt_fifo)
+{
+	TRACE_ENTRY( "%p %p", conn, alt_fifo );
+	CHECK_PARAMS( conn );
+	
+	/* Let's cross fingers that there is no race condition here... */
+	conn->cc_alt = alt_fifo;
+	
+	return 0;
+}
 
 /* Send a message */
 int fd_cnx_send(struct cnxctx * conn, unsigned char * buf, size_t len)
 {
-	
+
 	TODO("...");
 	return ENOTSUP;
 }
@@ -234,8 +462,31 @@
 /* Destroy a conn structure, and shutdown the socket */
 void fd_cnx_destroy(struct cnxctx * conn)
 {
+	TRACE_ENTRY("%p", conn);
 	
-	TODO("...");
+	CHECK_PARAMS_DO(conn, return);
+
+	TODO("End TLS session(s) if started");
+	
+	TODO("Stop manager thread if running");
+	
+	/* Shut the connection down */
+	if (conn->cc_socket > 0) {
+		shutdown(conn->cc_socket, SHUT_RDWR);
+	}
+	
+	TODO("Empty FIFO queues");
+	
+	/* Destroy FIFO lists */
+	if (conn->cc_events)
+		CHECK_FCT_DO( fd_fifo_del ( &conn->cc_events ), /* continue */ );
+	if (conn->cc_incoming)
+		CHECK_FCT_DO( fd_fifo_del ( &conn->cc_incoming ), /* continue */ );
+	
+	/* Free the object */
+	free(conn);
+	
+	/* Done! */
 	return;
 }
 
--- a/freeDiameter/config.c	Thu Oct 08 20:05:16 2009 +0900
+++ b/freeDiameter/config.c	Mon Oct 19 18:43:09 2009 +0900
@@ -228,6 +228,25 @@
 		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->ss.ss_family == AF_INET))
+			   ||(fd_g_config->cnf_flags.no_ip6 && (ep->ss.ss_family == AF_INET6)) ) {
+				li = li->prev;
+				fd_list_unlink(&ep->chain);
+				if (TRACE_BOOL(INFO)) {
+					fd_log_debug("Info: Removing local address conflicting with the flags no_IP / no_IP6 : ");
+					sSA_DUMP_NODE( &ep->ss, AI_NUMERICHOST );
+					fd_log_debug("\n");
+				}
+				free(ep);
+			}
+		}
+	}
+	
 	/* Configure TLS default parameters */
 	if (! fd_g_config->cnf_sec_data.prio_string) {
 		const char * err_pos = NULL;
@@ -238,14 +257,17 @@
 				 { TRACE_DEBUG(INFO, "Error in priority string at position : %s", err_pos); return EINVAL; } );
 	}
 	if (! fd_g_config->cnf_sec_data.dh_bits) {
-		TRACE_DEBUG(FULL, "Generating DH parameters...");
+		if (TRACE_BOOL(INFO)) {
+			fd_log_debug("Generating Diffie-Hellman parameters of size %d (this takes a few seconds)... ", GNUTLS_DEFAULT_DHBITS);
+		}
 		CHECK_GNUTLS_DO( gnutls_dh_params_generate2( 
 					fd_g_config->cnf_sec_data.dh_cache,
 					GNUTLS_DEFAULT_DHBITS),
 				 { TRACE_DEBUG(INFO, "Error in DH bits value : %d", GNUTLS_DEFAULT_DHBITS); return EINVAL; } );
-		TRACE_DEBUG(FULL, "DH parameters generated.");
+		if (TRACE_BOOL(INFO)) {
+			fd_log_debug("Done!\n");
+		}
 	}
 	
-	
 	return 0;
 }
--- a/freeDiameter/fD.h	Thu Oct 08 20:05:16 2009 +0900
+++ b/freeDiameter/fD.h	Mon Oct 19 18:43:09 2009 +0900
@@ -156,11 +156,15 @@
 	/* request to terminate this peer : disconnect, requeue all messages */
 	,FDEVP_TERMINATE
 	
+	/* A connection object has received a message -- stored in event->data */
+	,FDEVP_CNX_MSG_RECV
+	
 	/* A message was received in the peer */
 	,FDEVP_MSG_INCOMING
 	
 	/* The PSM state is expired */
 	,FDEVP_PSM_TIMEOUT
+	
 };
 const char * fd_pev_str(int event);
 #define CHECK_EVENT( _e ) \
@@ -195,22 +199,34 @@
 /* Server sockets */
 void fd_servers_dump();
 int fd_servers_start();
-void fd_servers_stop();
+int fd_servers_stop();
 
 /* Connection contexts */
-struct cnxctx * fd_cnx_init(int sock, int proto);
+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(int proto, const sSA * sa,  socklen_t addrlen);
+char * fd_cnx_getid(struct cnxctx * conn);
 int fd_cnx_start_clear(struct cnxctx * conn);
-int fd_cnx_handshake(struct cnxctx * conn, int mode);
+int fd_cnx_handshake(struct cnxctx * conn, int mode, char * priority);
 int fd_cnx_getcred(struct cnxctx * conn, const gnutls_datum_t **cert_list, unsigned int *cert_list_size);
-int fd_cnx_getendpoints(struct cnxctx * conn, struct fd_list * senti);
+int fd_cnx_getendpoints(struct cnxctx * conn, struct fd_list * local, struct fd_list * remote);
 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);
 
+/* TCP */
+int fd_tcp_create_bind_server( int * sock, sSA * sa, socklen_t salen );
+int fd_tcp_listen( int sock );
+
 /* SCTP */
 #ifndef DISABLE_SCTP
-int fd_sctp_create_bind_server( int * socket, uint16_t port );
+int fd_sctp_create_bind_server( int * sock, struct fd_list * list, uint16_t port );
+int fd_sctp_listen( int sock );
+
 int fd_sctp_get_str_info( int socket, int *in, int *out );
 
 #endif /* DISABLE_SCTP */
--- a/freeDiameter/fdd.y	Thu Oct 08 20:05:16 2009 +0900
+++ b/freeDiameter/fdd.y	Mon Oct 19 18:43:09 2009 +0900
@@ -335,6 +335,7 @@
 					
 				/* Now destroy any content in the structure */
 				free(fddpi.pi_diamid);
+				free(fddpi.pi_sec_data.priority);
 				while (!FD_IS_LIST_EMPTY(&fddpi.pi_endpoints)) {
 					struct fd_list * li = fddpi.pi_endpoints.next;
 					fd_list_unlink(li);
@@ -414,16 +415,14 @@
 					{ yyerror (&yylloc, conf, "Invalid value"); YYERROR; } );
 				fddpi.pi_port = (uint16_t)$4;
 			}
-			| peerparams SCTPSTREAMS '=' INTEGER ';'
-			{
-				CHECK_PARAMS_DO( ($4 > 0) && ($4 < 1<<16),
-					{ yyerror (&yylloc, conf, "Invalid value"); YYERROR; } );
-				fddpi.pi_streams = (uint16_t)$4;
-			}
 			| peerparams TCTIMER '=' INTEGER ';'
 			{
 				fddpi.pi_tctimer = $4;
 			}
+			| peerparams TLS_PRIO '=' QSTRING ';'
+			{
+				fddpi.pi_sec_data.priority = $4;
+			}
 			| peerparams TWTIMER '=' INTEGER ';'
 			{
 				fddpi.pi_twtimer = $4;
--- a/freeDiameter/main.c	Thu Oct 08 20:05:16 2009 +0900
+++ b/freeDiameter/main.c	Mon Oct 19 18:43:09 2009 +0900
@@ -50,6 +50,7 @@
 static struct fd_config conf;
 struct fd_config * fd_g_config = &conf;
 
+/* gcrypt functions to support posix threads */
 GCRY_THREAD_OPTION_PTHREAD_IMPL;
 
 /* freeDiameter starting point */
@@ -104,6 +105,9 @@
 	/* Load the dynamic extensions */
 	CHECK_FCT(  fd_ext_load()  );
 	
+	/* Start the servers */
+	CHECK_FCT( fd_servers_start() );
+	
 	/* Start the peer state machines */
 	CHECK_FCT( fd_psm_start() );
 	
@@ -153,6 +157,7 @@
 	TRACE_DEBUG(INFO, FD_PROJECT_BINARY " daemon is stopping...");
 	
 	/* cleanups */
+	CHECK_FCT_DO( fd_servers_stop(), /* Stop accepting new connections */ );
 	TODO("Stop dispatch thread(s) properly (no cancel yet)");
 	CHECK_FCT_DO( fd_peer_fini(), /* Stop all connections */ );
 	TODO("Stop dispatch & routing threads");
--- a/freeDiameter/p_expiry.c	Thu Oct 08 20:05:16 2009 +0900
+++ b/freeDiameter/p_expiry.c	Mon Oct 19 18:43:09 2009 +0900
@@ -46,14 +46,13 @@
 
 static void * gc_th_fct(void * arg)
 {
-	fd_log_threadname ( "Peers/garbage" );
-	TRACE_ENTRY( "" );
+	fd_log_threadname ( "Peers/garb. col." );
+	TRACE_ENTRY( "%p", arg );
 	
 	do {
 		struct fd_list * li, purge = FD_LIST_INITIALIZER(purge);
 		
-		pthread_testcancel();
-		sleep(GC_TIME);
+		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 );
@@ -95,7 +94,7 @@
 static void * exp_th_fct(void * arg)
 {
 	fd_log_threadname ( "Peers/expire" );
-	TRACE_ENTRY( "" );
+	TRACE_ENTRY( "%p", arg );
 	
 	CHECK_POSIX_DO( pthread_mutex_lock(&exp_mtx),  goto error );
 	pthread_cleanup_push( fd_cleanup_mutex, &exp_mtx );
@@ -123,7 +122,7 @@
 		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 error, continue */,
+					ETIMEDOUT, /* ETIMEDOUT is a normal return value, continue */,
 					/* on other error, */ goto error );
 	
 			/* on wakeup, loop */
--- a/freeDiameter/p_psm.c	Thu Oct 08 20:05:16 2009 +0900
+++ b/freeDiameter/p_psm.c	Mon Oct 19 18:43:09 2009 +0900
@@ -54,8 +54,9 @@
 	switch (event) {
 	#define case_str( _val )\
 		case _val : return #_val
+		case_str(FDEVP_DUMP_ALL);
 		case_str(FDEVP_TERMINATE);
-		case_str(FDEVP_DUMP_ALL);
+		case_str(FDEVP_CNX_MSG_RECV);
 		case_str(FDEVP_MSG_INCOMING);
 		case_str(FDEVP_PSM_TIMEOUT);
 		
@@ -240,6 +241,8 @@
 	
 psm_end:
 	pthread_cleanup_pop(1); /* set STATE_ZOMBIE */
+	pthread_detach(peer->p_psm);
+	peer->p_psm = (pthread_t)NULL;
 	return NULL;
 }	
 	
@@ -266,6 +269,7 @@
 {
 	TRACE_ENTRY("%p", peer);
 	CHECK_PARAMS( CHECK_PEER(peer) );
+	
 	if (peer->p_hdr.info.pi_state != STATE_ZOMBIE) {
 		CHECK_FCT( fd_event_send(peer->p_events, FDEVP_TERMINATE, NULL) );
 	} else {
--- a/freeDiameter/peers.c	Thu Oct 08 20:05:16 2009 +0900
+++ b/freeDiameter/peers.c	Mon Oct 19 18:43:09 2009 +0900
@@ -100,11 +100,14 @@
 	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;
 	
+	if (info->pi_sec_data.priority) {
+		CHECK_MALLOC( p->p_hdr.info.pi_sec_data.priority = strdup(info->pi_sec_data.priority) );
+	}
+	
 	/* Move the items from one list to the other */
 	if (info->pi_endpoints.next)
 		while (!FD_IS_LIST_EMPTY( &info->pi_endpoints ) ) {
@@ -113,6 +116,7 @@
 			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) );
@@ -228,7 +232,7 @@
 	}
 	
 	if (p->p_cnxctx) {
-		TODO("destroy p->p_cnxctx");
+		fd_cnx_destroy(p->p_cnxctx);
 	}
 	
 	if (p->p_cb)
@@ -292,6 +296,7 @@
 		}
 		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) {
--- a/freeDiameter/sctp.c	Thu Oct 08 20:05:16 2009 +0900
+++ b/freeDiameter/sctp.c	Mon Oct 19 18:43:09 2009 +0900
@@ -35,16 +35,27 @@
 
 #include "fD.h"
 
-int fd_sctp_create_bind_server( int * socket, uint16_t port )
+/* Create a socket server and bind it according to daemon s configuration */
+int fd_sctp_create_bind_server( int * sock, struct fd_list * list, uint16_t port )
 {
 	TODO("Create sctp server, using fd_g_config: cnf_endpoints, no_ip4, no_ip6, cnf_sctp_str");
 	
 	return ENOTSUP;
 }
 
+/* Allow clients connections on server sockets */
+int fd_sctp_listen( int sock )
+{
+	TRACE_ENTRY("%d", sock);
+	CHECK_SYS( listen(sock, 5) );
+	return 0;
+}
+
+/* Retrieve streams information from a connected association */
 int fd_sctp_get_str_info( int socket, int *in, int *out )
 {
 	TODO("Retrieve streams info from the socket");
 	
 	return ENOTSUP;
 }
+
--- a/freeDiameter/server.c	Thu Oct 08 20:05:16 2009 +0900
+++ b/freeDiameter/server.c	Mon Oct 19 18:43:09 2009 +0900
@@ -35,47 +35,67 @@
 
 #include "fD.h"
 
-/* This file contains the server (listening) part of the daemon */
+/* Server (listening) part of the daemon */
 
-struct fd_list		FD_SERVERS = FD_LIST_INITIALIZER(FD_SERVERS);	/* The list of all server sockets */
-/* We don't need to protect this list, it is only accessed from the main thread. */
+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 daemon thread. */
 
-/* Server (listening socket) information */
+/* Servers information */
 struct server {
 	struct fd_list	chain;		/* link in the FD_SERVERS list */
 
-	int 		socket;		/* server socket, or <= 0 */
-	
+	struct cnxctx *	conn;		/* server connection context (listening socket) */
 	int 		proto;		/* IPPROTO_TCP or IPPROTO_SCTP */
 	int 		secur;		/* TLS is started immediatly after connection ? */
 	
-	pthread_t	serv_thr;	/* The thread listening for new connections */
-	int		serv_status;	/* 0 : not created; 1 : running; 2 : terminated */
+	pthread_t	thr;		/* The thread listening for new connections */
+	int		status;		/* 0 : not created; 1 : running; 2 : terminated */
 	
-	pthread_mutex_t	clients_mtx;	/* Mutex to protect the list of clients connected to the thread */
-	struct fd_list	clients;	/* The list of clients connecting to this server, which information is not yet known */
-	
-	char *		serv_name;	/* A string to identify this server */
+	struct fd_list	clients;	/* List of clients connected to this server, not yet identified */
+	pthread_mutex_t	clients_mtx;	/* Mutex to protect the list of clients */
 };
 
-/* Client (connected remote endpoint, not received CER yet) information */
+/* Client information (connecting peer for which we don't have the CER yet) */
 struct client {
 	struct fd_list	 chain;	/* link in the server's list of clients */
-	struct cnxctx	*conn;	/* Parameters of the connection; sends its events to the ev fifo bellow */
-	struct timespec	 ts;	/* Delay for receiving CER: INCNX_TIMEOUT */
-	pthread_t	 cli_thr; /* connection state machine (simplified PSM) */
-};
-
-/* Parameter for the thread handling the new connected client, to avoid bloking the server thread */
-struct cli_fast {
-	struct server * serv;
-	int		sock;
-	sSS		ss;
-	socklen_t	sslen;
+	struct cnxctx	*conn;	/* Parameters of the connection */
+	struct timespec	 ts;	/* Deadline for receiving CER (after INCNX_TIMEOUT) */
+	pthread_t	 thr; 	/* connection state machine */
 };
 
 
-static void * client_simple_psm(void * arg)
+/* Dump all servers information */
+void fd_servers_dump()
+{
+	struct fd_list * li, *cli;
+	
+	fd_log_debug("Dumping servers list :\n");
+	for (li = FD_SERVERS.next; li != &FD_SERVERS; li = li->next) {
+		struct server * s = (struct server *)li;
+		fd_log_debug("  Serv %p '%s': %s, %s, %s\n", 
+				s, fd_cnx_getid(s->conn), 
+				IPPROTO_NAME( s->proto ),
+				s->secur ? "Secur" : "NotSecur",
+				(s->status == 0) ? "Thread not created" :
+				((s->status == 1) ? "Thread running" :
+				((s->status == 2) ? "Thread terminated" :
+							  "Thread status unknown")));
+		/* Dump the client list of this server */
+		(void) pthread_mutex_lock(&s->clients_mtx);
+		for (cli = s->clients.next; cli != &s->clients; cli = cli->next) {
+			struct client * c = (struct client *)cli;
+			char bufts[128];
+			fd_log_debug("     Connected: '%s' (timeout: %s)\n",
+					fd_cnx_getid(c->conn),
+					fd_log_time(&c->ts, bufts, sizeof(bufts)));
+		}
+		(void) pthread_mutex_unlock(&s->clients_mtx);
+	}
+}
+
+
+/* The state machine to handle incoming connection before the remote peer is identified */
+static void * client_sm(void * arg)
 {
 	struct client * c = arg;
 	struct server * s = NULL;
@@ -87,10 +107,19 @@
 	s = c->chain.head->o;
 	
 	/* Name the current thread */
-	{
-		char addr[128];
-		snprintf(addr, sizeof(addr), "Srv %d/Cli %s", s->socket, fd_cnx_getremoteid(c->conn));
-		fd_log_threadname ( addr );
+	fd_log_threadname ( fd_cnx_getid(c->conn) );
+	
+	/* Handshake if we are a secure server port, or start clear otherwise */
+	if (s->secur) {
+		int ret = fd_cnx_handshake(c->conn, GNUTLS_SERVER, NULL);
+		if (ret != 0) {
+			if (TRACE_BOOL(INFO)) {
+				fd_log_debug("TLS handshake failed for client '%s', connection aborted.\n", fd_cnx_getid(c->conn));
+			}
+			goto cleanup;
+		}
+	} else {
+		CHECK_FCT_DO( fd_cnx_start_clear(c->conn), goto cleanup );
 	}
 	
 	/* Set the timeout to receive the first message */
@@ -103,12 +132,22 @@
 	TODO("Message != CER => close");
 	TODO("Message == CER : ");
 	TODO("Search matching peer");
-	TODO("...");
+	TODO("Send event to the peer");
 	
-	/* The end: we have freed the client structure already */
-	TODO("Unlink the client structure");
-	TODO(" pthread_detach(c->cli_thr); ");
-	TODO(" free(c); ");
+	/* The end */
+cleanup:
+	/* Unlink the client structure */
+	CHECK_POSIX_DO( pthread_mutex_lock(&s->clients_mtx), goto fatal_error );
+	fd_list_unlink( &c->chain );
+	CHECK_POSIX_DO( pthread_mutex_unlock(&s->clients_mtx), goto fatal_error );
+	
+	/* Destroy the connection object if present */
+	if (c->conn)
+		fd_cnx_destroy(c->conn);
+	
+	/* Detach the thread, cleanup the client structure */
+	pthread_detach(pthread_self());
+	free(c);
 	return NULL;
 	
 fatal_error:	/* This has effect to terminate the daemon */
@@ -116,112 +155,46 @@
 	return NULL;
 }
 
-/* This thread is called when a new client had just connected */
-static void * handle_client_fast(void * arg)
-{
-	struct cli_fast * cf = arg;
-	struct client * c = NULL;
-	
-	/* Name the current thread */
-	ASSERT(arg);
-	{
-		char addr[128];
-		int offset = snprintf(addr, sizeof(addr), "Srv %d/CliFast %d : ", cf->serv->socket, cf->sock);
-		int rc = getnameinfo((sSA *)&cf->ss, sizeof(sSS), addr + offset, sizeof(addr) - offset, NULL, 0, 0);
-		if (rc)
-			memcpy(addr + offset, gai_strerror(rc), sizeof(addr) - offset);
-		
-		fd_log_threadname ( addr );
-	
-		if (TRACE_BOOL(INFO)) {
-			fd_log_debug( "New connection %s, sock %d, from '%s'\n", cf->serv->serv_name, cf->sock, addr + offset);
-		}
-	}
-	
-	/* Create a client structure */
-	CHECK_MALLOC_DO( c = malloc(sizeof(struct client)), goto fatal_error );
-	memset(c, 0, sizeof(struct client));
-	fd_list_init(&c->chain, c);
-	
-	/* Create the connection context */
-	CHECK_MALLOC_DO( c->conn = fd_cnx_init(cf->sock, cf->serv->proto), goto fatal_error );
-	
-	/* In case we are a secure server, handshake now */
-	if (cf->serv->secur) {
-		CHECK_FCT_DO( fd_cnx_handshake(c->conn, GNUTLS_CLIENT), goto cleanup );
-	}
-	
-	
-	/* Save the client in the list */
-	CHECK_POSIX_DO( pthread_mutex_lock( &cf->serv->clients_mtx ), goto fatal_error );
-	fd_list_insert_before(&cf->serv->clients, &c->chain);
-	CHECK_POSIX_DO( pthread_mutex_unlock( &cf->serv->clients_mtx ), goto fatal_error );
-	
-	/* Start the client thread */
-	CHECK_POSIX_DO( pthread_create( &c->cli_thr, NULL, client_simple_psm, c ), goto fatal_error );
-	
-	/* We're done here */
-	free(cf);
-	return NULL;
-	
-cleanup:	/* Clean all objects and return (minor error on the connection)*/
-	if (c && c->conn) {
-		TODO( "Free the c->conn object & gnutls data" );
-	}
-	
-	return NULL;	
-	
-fatal_error:	/* This has effect to terminate the daemon */
-	CHECK_FCT_DO(fd_event_send(fd_g_config->cnf_main_ev, FDEV_TERMINATE, NULL), );
-	free(cf);
-	free(c);
-	return NULL;
-}
-
-/* The thread for the server */
+/* The thread managing a server */
 static void * serv_th(void * arg)
 {
-	struct server *sv = (struct server *)arg;
-	struct cli_fast cf;
-	pthread_attr_t	attr;
+	struct server *s = (struct server *)arg;
 	
-	CHECK_PARAMS_DO(sv, goto error);
-	fd_log_threadname ( sv->serv_name );
-	sv->serv_status = 1;
-	
-	memset(&cf, 0, sizeof(struct cli_fast));
-	cf.serv = sv;
-	
-	CHECK_POSIX_DO( pthread_attr_init(&attr), goto error );
-	CHECK_POSIX_DO( pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED), goto error );
+	CHECK_PARAMS_DO(s, goto error);
+	fd_log_threadname ( fd_cnx_getid(s->conn) );
+	s->status = 1;
 	
 	/* Accept incoming connections */
-	CHECK_SYS_DO(  listen(sv->socket, 5), goto error );
+	CHECK_FCT_DO( fd_cnx_serv_listen(s->conn), goto error );
 	
 	do {
-		struct cli_fast * ncf;
-		pthread_t	  thr;
-		
-		/* Re-init socket size */
-		cf.sslen = sizeof(sSS);
+		struct client * c = NULL;
+		struct cnxctx * conn = NULL;
 		
 		/* Wait for a new client */
-		CHECK_SYS_DO( cf.sock = accept(sv->socket, (sSA *)&cf.ss, &cf.sslen), goto error );
+		CHECK_MALLOC_DO( conn = fd_cnx_serv_accept(s->conn), goto error );
 		
 		TRACE_DEBUG(FULL, "New connection accepted");
 		
-		/* Create the copy for the client thread */
-		CHECK_MALLOC_DO( ncf = malloc(sizeof(struct cli_fast)), goto error );
-		memcpy(ncf, &cf, sizeof(struct cli_fast));
+		/* Create a client structure */
+		CHECK_MALLOC_DO( c = malloc(sizeof(struct client)), goto error );
+		memset(c, 0, sizeof(struct client));
+		fd_list_init(&c->chain, c);
+		c->conn = conn;
 		
-		/* Create the thread to handle the new incoming connection */
-		CHECK_POSIX_DO( pthread_create( &thr, &attr, handle_client_fast, ncf), goto error );
+		/* Save the client in the list */
+		CHECK_POSIX_DO( pthread_mutex_lock( &s->clients_mtx ), goto error );
+		fd_list_insert_before(&s->clients, &c->chain);
+		CHECK_POSIX_DO( pthread_mutex_unlock( &s->clients_mtx ), goto error );
+
+		/* Start the client thread */
+		CHECK_POSIX_DO( pthread_create( &c->thr, NULL, client_sm, c ), goto error );
 		
 	} while (1);
 	
 error:	
-	if (sv)
-		sv->serv_status = 2;
+	if (s)
+		s->status = 2;
 	/* Send error signal to the daemon */
 	TRACE_DEBUG(INFO, "An error occurred in server module! Thread is terminating...");
 	CHECK_FCT_DO(fd_event_send(fd_g_config->cnf_main_ev, FDEV_TERMINATE, NULL), );
@@ -231,60 +204,29 @@
 
 
 /* Create a new server structure */
-static struct server * new_serv( int proto, int secur, int socket )
+static struct server * new_serv( int proto, int secur )
 {
-	char buf[32];
-	char * sn = NULL;
 	struct server * new;
 	
-	/* Create the server debug name */
-	buf[sizeof(buf) - 1] = '\0';
-	snprintf(buf, sizeof(buf) - 1, "Serv %d (%s%s)", socket, IPPROTO_NAME( proto ),	secur ? "s" : "");
-	CHECK_MALLOC_DO( sn = strdup(buf), return NULL );
-	
 	/* 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->socket = socket;
 	new->proto = proto;
 	new->secur = secur;
 	CHECK_POSIX_DO( pthread_mutex_init(&new->clients_mtx, NULL), return NULL );
 	fd_list_init(&new->clients, new);
 	
-	new->serv_name = sn;
-	
 	return new;
 }
 
-/* Dump all servers information */
-void fd_servers_dump()
-{
-	struct fd_list * li;
-	
-	fd_log_debug("Dumping servers list :\n");
-	for (li = FD_SERVERS.next; li != &FD_SERVERS; li = li->next) {
-		struct server * sv = (struct server *)li;
-		fd_log_debug("  Serv '%s': %s(%d), %s, %s, %s\n", 
-				sv->serv_name, 
-				(sv->socket > 0) ? "Open" : "Closed", sv->socket,
-				IPPROTO_NAME( sv->proto ),
-				sv->secur ? "Secur" : "NotSecur",
-				(sv->serv_status == 0) ? "Thread not created" :
-				((sv->serv_status == 1) ? "Thread running" :
-				((sv->serv_status == 2) ? "Thread terminated" :
-							  "Thread status unknown")));
-		/* Dump the client list */
-		TODO("Dump client list");
-	}
-}
-
 /* Start all the servers */
 int fd_servers_start()
 {
-	int  socket;
-	struct server * sv;
+	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) {
@@ -293,12 +235,21 @@
 #else /* DISABLE_SCTP */
 		
 		/* Create the server on default port */
-		CHECK_FCT( fd_sctp_create_bind_server( &socket, fd_g_config->cnf_port ) );
-		CHECK_MALLOC( sv = new_serv(IPPROTO_SCTP, 0, socket) );
-		TODO("Link");
-		TODO("Start thread");
+		CHECK_MALLOC( s = new_serv(IPPROTO_SCTP, 0) );
+		CHECK_MALLOC( s->conn = fd_cnx_serv_sctp(fd_g_config->cnf_port, FD_IS_LIST_EMPTY(&fd_g_config->cnf_endpoints) ? NULL : &fd_g_config->cnf_endpoints) );
+		fd_list_insert_before( &FD_SERVERS, &s->chain );
+		CHECK_POSIX( pthread_create( &s->thr, NULL, serv_th, s ) );
+		
+		/* Retrieve the list of endpoints if it was empty */
+		if (empty_conf_ep) {
+			(void) fd_cnx_getendpoints(s->conn, &fd_g_config->cnf_endpoints, NULL);
+		}
 		
 		/* Create the server on secure port */
+		CHECK_MALLOC( s = new_serv(IPPROTO_SCTP, 1) );
+		CHECK_MALLOC( s->conn = fd_cnx_serv_sctp(fd_g_config->cnf_port_tls, FD_IS_LIST_EMPTY(&fd_g_config->cnf_endpoints) ? 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 */
 	}
@@ -306,11 +257,56 @@
 	/* TCP */
 	if (!fd_g_config->cnf_flags.no_tcp) {
 		
-		if (FD_IS_LIST_EMPTY(&fd_g_config->cnf_endpoints)) {
-			/* if not no_IP : create server for 0.0.0.0 */
-			/* if not no_IP6 : create server for :: */
+		if (empty_conf_ep) {
+			/* Bind TCP servers on [0.0.0.0] */
+			if (!fd_g_config->cnf_flags.no_ip4) {
+				
+				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 ) );
+
+				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) {
+				
+				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 ) );
+
+				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->meta.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;
+				
+				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 ) );
+
+				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 ) );
+			}
 		}
 	}
 	
@@ -318,10 +314,12 @@
 }
 
 /* Terminate all the servers */
-void fd_servers_stop()
+int fd_servers_stop()
 {
+	TODO("Not implemented");
+	
 	/* Loop on all servers */
 		/* cancel thread */
-		/* shutdown the socket */
-		/* empty list of clients (stop them) */
+		/* destroy server connection context */
+		/* cancel and destroy all clients */
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/freeDiameter/tcp.c	Mon Oct 19 18:43:09 2009 +0900
@@ -0,0 +1,112 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@nict.go.jp>							 *
+*													 *
+* Copyright (c) 2009, WIDE Project and NICT								 *
+* All rights reserved.											 *
+* 													 *
+* Redistribution and use of this software in source and binary forms, with or without modification, are  *
+* permitted provided that the following conditions are met:						 *
+* 													 *
+* * Redistributions of source code must retain the above 						 *
+*   copyright notice, this list of conditions and the 							 *
+*   following disclaimer.										 *
+*    													 *
+* * Redistributions in binary form must reproduce the above 						 *
+*   copyright notice, this list of conditions and the 							 *
+*   following disclaimer in the documentation and/or other						 *
+*   materials provided with the distribution.								 *
+* 													 *
+* * Neither the name of the WIDE Project or NICT nor the 						 *
+*   names of its contributors may be used to endorse or 						 *
+*   promote products derived from this software without 						 *
+*   specific prior written permission of WIDE Project and 						 *
+*   NICT.												 *
+* 													 *
+* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED *
+* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
+* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR *
+* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 	 *
+* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 	 *
+* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR *
+* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF   *
+* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.								 *
+*********************************************************************************************************/
+
+#include "fD.h"
+#include <netinet/tcp.h>
+#include <netinet/ip6.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 */
+	
+	
+	/* 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;
+}
--- a/include/freeDiameter/freeDiameter.h	Thu Oct 08 20:05:16 2009 +0900
+++ b/include/freeDiameter/freeDiameter.h	Mon Oct 19 18:43:09 2009 +0900
@@ -255,7 +255,6 @@
 	
 	/* Additional parameters */
 	uint32_t 	pi_lft;		/* lifetime of this peer when inactive (see pi_flags.exp definition) */
-	uint16_t	pi_streams; 	/* number of streams for SCTP. 0 = default */
 	uint16_t	pi_port; 	/* port to connect to. 0: default. */
 	int		pi_tctimer; 	/* use this value for TcTimer instead of global, if != 0 */
 	int		pi_twtimer; 	/* use this value for TwTimer instead of global, if != 0 */
@@ -270,6 +269,7 @@
 	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 {
+		char			*priority;	/* In case the default priority is not appropriate */
 		/* 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 */
--- a/include/freeDiameter/libfreeDiameter.h	Thu Oct 08 20:05:16 2009 +0900
+++ b/include/freeDiameter/libfreeDiameter.h	Mon Oct 19 18:43:09 2009 +0900
@@ -119,16 +119,17 @@
  * FUNCTION:	fd_log_time
  *
  * PARAMETERS:
+ *  ts	 	: The timestamp to log, or NULL for "now"
  *  buf 	: An array where the time must be stored
  *  len		: size of the buffer
  *
  * DESCRIPTION: 
- *  Writes the current timestamp (in human readable format) in a buffer. 
+ *  Writes the timestamp (in human readable format) in a buffer. 
  *
  * RETURN VALUE:
  *  pointer to buf.
  */
-char * fd_log_time ( char * buf, size_t len );
+char * fd_log_time ( struct timespec * ts, char * buf, size_t len );
 
 
 /* levels definitions */
@@ -172,7 +173,7 @@
 		char * __thn = ((char *)pthread_getspecific(fd_log_thname) ?: "unnamed");					\
 		fd_log_debug("\t | tid:%-20s\t%s\tin %s@%s:%d\n"								\
 			  "\t%s|%*s" format "\n",  										\
-					__thn, fd_log_time(__buf, sizeof(__buf)), __PRETTY_FUNCTION__, __FILE__, __LINE__,	\
+					__thn, fd_log_time(NULL, __buf, sizeof(__buf)), __PRETTY_FUNCTION__, __FILE__, __LINE__,	\
 					(level < FULL)?"@":" ",level, "", ## args); 						\
 	}															\
 }
--- a/libfreeDiameter/log.c	Thu Oct 08 20:05:16 2009 +0900
+++ b/libfreeDiameter/log.c	Mon Oct 19 18:43:09 2009 +0900
@@ -86,8 +86,8 @@
 	return;
 }
 
-/* Write current time into a buffer */
-char * fd_log_time ( char * buf, size_t len )
+/* Write time into a buffer */
+char * fd_log_time ( struct timespec * ts, char * buf, size_t len )
 {
 	int ret;
 	size_t offset = 0;
@@ -95,14 +95,17 @@
 	struct tm tm;
 	
 	/* Get current time */
-	ret = clock_gettime(CLOCK_REALTIME, &tp);
-	if (ret != 0) {
-		snprintf(buf, len, "%s", strerror(ret));
-		return buf;
+	if (!ts) {
+		ret = clock_gettime(CLOCK_REALTIME, &tp);
+		if (ret != 0) {
+			snprintf(buf, len, "%s", strerror(ret));
+			return buf;
+		}
+		ts = &tp;
 	}
 	
-	offset += strftime(buf + offset, len - offset, "%D,%T", localtime_r( &tp.tv_sec , &tm ));
-	offset += snprintf(buf + offset, len - offset, ".%6.6ld", tp.tv_nsec / 1000);
+	offset += strftime(buf + offset, len - offset, "%D,%T", localtime_r( &ts->tv_sec , &tm ));
+	offset += snprintf(buf + offset, len - offset, ".%6.6ld", ts->tv_nsec / 1000);
 
 	return buf;
 }
"Welcome to our mercurial repository"