changeset 1181:22de21feec64

Preparing for DTLS support
author Sebastien Decugis <sdecugis@freediameter.net>
date Wed, 05 Jun 2013 19:22:26 +0800
parents 773498f59520
children cc96a4dfb3d1
files doc/freediameter.conf.sample libfdcore/cnxctx.c libfdcore/cnxctx.h libfdcore/config.c libfdcore/fdcore-internal.h libfdcore/p_ce.c libfdcore/p_cnx.c libfdcore/p_psm.c libfdcore/peers.c libfdcore/sctp3436.c libfdcore/server.c tests/testcnx.c
diffstat 12 files changed, 559 insertions(+), 124 deletions(-) [+]
line wrap: on
line diff
--- a/doc/freediameter.conf.sample	Wed Jun 05 15:02:29 2013 +0800
+++ b/doc/freediameter.conf.sample	Wed Jun 05 19:22:26 2013 +0800
@@ -35,7 +35,8 @@
 
 # freeDiameter 1.2.0 introduces the support of DTLS over SCTP (RFC6083) instead of TLS over SCTP (RFC3436), 
 # as specified in RFC6733. If you need compatibility with older implementation that use TLS over SCTP, you 
-# can open an additional SCTP server port using this method:
+# can open an additional SCTP server port using TLS/SCTP by specifying the following parameter.
+# Note that no TCP server is started on the following port.
 # Default: 0 (disabled). Use 3869 for compatibility with freeDiameter < 1.2.0.
 #SctpSec3436 = 0;
 
--- a/libfdcore/cnxctx.c	Wed Jun 05 15:02:29 2013 +0800
+++ b/libfdcore/cnxctx.c	Wed Jun 05 19:22:26 2013 +0800
@@ -95,6 +95,8 @@
 	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)
 {
@@ -138,7 +140,7 @@
 		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), "TCP srv [%s]:%hu (%d)", addrbuf, port, cnx->cc_socket);
+		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;
@@ -178,7 +180,7 @@
 	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), "SCTP srv :%hu (%d)", port, cnx->cc_socket);
+	snprintf(cnx->cc_id, sizeof(cnx->cc_id), CC_ID_HDR "SCTP srv :%hu (%d)", port, cnx->cc_socket);
 
 	cnx->cc_proto = IPPROTO_SCTP;
 
@@ -248,7 +250,7 @@
 		}
 		
 		/* Numeric values for debug... */
-		snprintf(cli->cc_id, sizeof(cli->cc_id), "%s from [%s]:%s (%d<-%d)", 
+		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);
 		
 		
@@ -312,7 +314,7 @@
 	{
 		int  rc;
 		
-		snprintf(cnx->cc_id, sizeof(cnx->cc_id), "TCP,#%d->%s", cnx->cc_socket, sa_buf);
+		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);
@@ -375,7 +377,7 @@
 	{
 		int  rc;
 		
-		snprintf(cnx->cc_id, sizeof(cnx->cc_id), "SCTP,#%d->%s", cnx->cc_socket, sa_buf);
+		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);
@@ -432,16 +434,39 @@
 	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); } );
 }
 
@@ -658,7 +683,7 @@
 	return ret;
 }
 
-#ifndef DISABLE_SCTP
+#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;
@@ -852,12 +877,12 @@
 
 
 
-/* Returns 0 on error, received data size otherwise (always >= 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),
+	CHECK_GNUTLS_DO( ret = gnutls_record_recv(session, data, sz), 
 		{
 			switch (ret) {
 				case GNUTLS_E_REHANDSHAKE: 
@@ -884,7 +909,11 @@
 					break;
 				
 				default:
-					TRACE_DEBUG(INFO, "This GNU TLS error is not handled, assume unrecoverable error");
+					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));
 			}
 		} );
 		
@@ -897,7 +926,7 @@
 	return ret;
 }
 
-/* Wrapper around gnutls_record_send to handle some error codes */
+/* 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;
@@ -924,7 +953,11 @@
 					break;
 
 				default:
-					TRACE_DEBUG(INFO, "This TLS error is not handled, assume unrecoverable error");
+					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:	
@@ -936,9 +969,15 @@
 
 
 /* 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 */
+	/* 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;
@@ -1024,8 +1063,13 @@
 }
 
 /* Prepare a gnutls session object for handshake */
-int fd_tls_prepare(gnutls_session_t * session, int mode, char * priority, void * alt_creds)
+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 );
 
@@ -1065,17 +1109,18 @@
 	CHECK_PARAMS(conn);
 	
 	/* Trace the session information -- http://www.gnu.org/software/gnutls/manual/gnutls.html#Obtaining-session-information */
-	if (verbose && TRACE_BOOL(FULL)) {
+	#ifdef DEBUG
+	if (verbose) {
 		const char *tmp;
 		gnutls_kx_algorithm_t kx;
   		gnutls_credentials_type_t cred;
 		
-		fd_log_debug("TLS Session information for connection '%s':", conn->cc_id);
+		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) );
-		fd_log_debug("\t - Key Exchange: %s", tmp);
+		LOG_A("\t - Key Exchange: %s", tmp);
 
 		/* Check the authentication type used and switch
 		* to the appropriate. */
@@ -1083,35 +1128,35 @@
 		switch (cred)
 		{
 			case GNUTLS_CRD_IA:
-				fd_log_debug("\t - TLS/IA session");
+				LOG_A("\t - TLS/IA session");
 				break;
 
 			case GNUTLS_CRD_PSK:
 				/* This returns NULL in server side. */
 				if (gnutls_psk_client_get_hint (session) != NULL)
-					fd_log_debug("\t - PSK authentication. PSK hint '%s'",
+					LOG_A("\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)
-					fd_log_debug("\t - PSK authentication. Connected as '%s'",
+					LOG_A("\t - PSK authentication. Connected as '%s'",
 						gnutls_psk_server_get_username (session));
 				break;
 
 			case GNUTLS_CRD_ANON:	/* anonymous authentication */
-				fd_log_debug("\t - Anonymous DH using prime of %d bits",
+				LOG_A("\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) {
-					fd_log_debug("\t - Ephemeral DH using prime of %d bits",
+					LOG_A("\t - Ephemeral DH using prime of %d bits",
 						gnutls_dh_get_prime_bits (session));
 				}
 				break;
 #ifdef ENABLE_SRP				
 			case GNUTLS_CRD_SRP:
-				fd_log_debug("\t - SRP session with username %s",
+				LOG_A("\t - SRP session with username %s",
 					gnutls_srp_server_get_username (session));
 				break;
 #endif /* ENABLE_SRP */
@@ -1124,24 +1169,25 @@
 
 		/* print the protocol's name (ie TLS 1.0) */
 		tmp = gnutls_protocol_get_name (gnutls_protocol_get_version (session));
-		fd_log_debug("\t - Protocol: %s", tmp);
+		LOG_A("\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));
-		fd_log_debug("\t - Certificate Type: %s", tmp);
+		LOG_A("\t - Certificate Type: %s", tmp);
 
 		/* print the compression algorithm (if any) */
 		tmp = gnutls_compression_get_name (gnutls_compression_get (session));
-		fd_log_debug("\t - Compression: %s", tmp);
+		LOG_A("\t - Compression: %s", tmp);
 
 		/* print the name of the cipher used. ie 3DES. */
 		tmp = gnutls_cipher_get_name (gnutls_cipher_get (session));
-		fd_log_debug("\t - Cipher: %s", tmp);
+		LOG_A("\t - Cipher: %s", tmp);
 
 		/* Print the MAC algorithms name. ie SHA1 */
 		tmp = gnutls_mac_get_name (gnutls_mac_get (session));
-		fd_log_debug("\t - MAC: %s", tmp);
+		LOG_A("\t - MAC: %s", tmp);
 	}
+	#endif /* DEBUG */
 	
 	/* First, use built-in verification */
 	CHECK_GNUTLS_DO( gnutls_certificate_verify_peers2 (session, &gtret), return EINVAL );
@@ -1172,27 +1218,27 @@
 	
 	now = time(NULL);
 	
-	if (verbose && TRACE_BOOL(FULL)) {
+	#ifdef DEBUG
 		char serial[40];
 		char dn[128];
 		size_t size;
 		unsigned int algo, bits;
 		time_t expiration_time, activation_time;
 		
-		fd_log_debug("TLS Certificate information for connection '%s' (%d certs provided):", conn->cc_id, cert_list_size);
+		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);
 		
-			fd_log_debug(" Certificate %d info:", i);
+			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) );
 
-			fd_log_debug("\t - Certificate is valid since: %s", ctime (&activation_time));
-			fd_log_debug("\t - Certificate expires: %s", ctime (&expiration_time));
+			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);
@@ -1205,29 +1251,29 @@
 				for (j = 0; j < size; j++) {
 					snprintf(buf+strlen(buf), sizeof(buf)-strlen(buf), "%02hhx", serial[j]);
 				}
-				fd_log_debug("%s", buf);
+				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) );
-			fd_log_debug("\t - Certificate public key: %s",
+			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. */
-			fd_log_debug("\t - Certificate version: #%d",
+			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) );
-			fd_log_debug("\t - DN: %s", dn);
+			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) );
-			fd_log_debug("\t - Issuer's DN: %s", dn);
+			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++)
@@ -1290,7 +1336,7 @@
 	conn = gnutls_session_get_ptr (session);
 	
 	/* Trace the session information -- http://www.gnu.org/software/gnutls/manual/gnutls.html#Obtaining-session-information */
-	if (TRACE_BOOL(FULL)) {
+#ifdef DEBUG
 		const char *tmp;
 		gnutls_credentials_type_t cred;
 		gnutls_kx_algorithm_t kx;
@@ -1298,13 +1344,13 @@
 
 		dhe = ecdh = 0;
 
-		fd_log_debug("TLS Session information for connection '%s':", conn->cc_id);
+		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) );
-		fd_log_debug("\t- Key Exchange: %s", tmp);
+		LOG_A("\t- Key Exchange: %s", tmp);
 
 		/* Check the authentication type used and switch
 		* to the appropriate.
@@ -1313,13 +1359,13 @@
 		switch (cred)
 		{
 			case GNUTLS_CRD_IA:
-				fd_log_debug("\t - TLS/IA session");
+				LOG_A("\t - TLS/IA session");
 				break;
 
 
 			#ifdef ENABLE_SRP
 			case GNUTLS_CRD_SRP:
-				fd_log_debug("\t - SRP session with username %s",
+				LOG_A("\t - SRP session with username %s",
 					gnutls_srp_server_get_username (session));
 				break;
 			#endif
@@ -1328,12 +1374,12 @@
 				/* This returns NULL in server side.
 				*/
 				if (gnutls_psk_client_get_hint (session) != NULL)
-					fd_log_debug("\t - PSK authentication. PSK hint '%s'",
+					LOG_A("\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)
-					fd_log_debug("\t - PSK authentication. Connected as '%s'",
+					LOG_A("\t - PSK authentication. Connected as '%s'",
 						gnutls_psk_server_get_username (session));
 
 				if (kx == GNUTLS_KX_ECDHE_PSK)
@@ -1343,7 +1389,7 @@
 				break;
 
 			case GNUTLS_CRD_ANON:      /* anonymous authentication */
-				fd_log_debug("\t - Anonymous DH using prime of %d bits",
+				LOG_A("\t - Anonymous DH using prime of %d bits",
 					gnutls_dh_get_prime_bits (session));
 				if (kx == GNUTLS_KX_ANON_ECDH)
 					ecdh = 1;
@@ -1366,7 +1412,7 @@
 
 					cert_list = gnutls_certificate_get_peers (session, &cert_list_size);
 
-					fd_log_debug("\t Peer provided %d certificates.", cert_list_size);
+					LOG_A("\t Peer provided %d certificates.", cert_list_size);
 
 					if (cert_list_size > 0)
 					{
@@ -1378,7 +1424,7 @@
 
 						gnutls_x509_crt_import (cert, &cert_list[0], GNUTLS_X509_FMT_DER);
 
-						fd_log_debug("\t Certificate info:");
+												LOG_A("\t Certificate info:");
 
 						/* This is the preferred way of printing short information about
 						 a certificate. */
@@ -1386,7 +1432,7 @@
 						ret = gnutls_x509_crt_print (cert, GNUTLS_CRT_PRINT_ONELINE, &cinfo);
 						if (ret == 0)
 						{
-						  fd_log_debug("\t\t%s", cinfo.data);
+						  LOG_A("\t\t%s", cinfo.data);
 						  gnutls_free (cinfo.data);
 						}
 						
@@ -1409,46 +1455,46 @@
 				break;
 
 			default:
-				fd_log_debug("\t - unknown session type (%d)", cred);
+				LOG_A("\t - unknown session type (%d)", cred);
 
 		}                           /* switch */
 
 		if (ecdh != 0)
-			fd_log_debug("\t - Ephemeral ECDH using curve %s",
+			LOG_A("\t - Ephemeral ECDH using curve %s",
 				gnutls_ecc_curve_get_name (gnutls_ecc_curve_get (session)));
 		else if (dhe != 0)
-			fd_log_debug("\t - Ephemeral DH using prime of %d bits",
+			LOG_A("\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));
-		fd_log_debug("\t - Protocol: %s", tmp);
+		LOG_A("\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));
-		fd_log_debug("\t - Certificate Type: %s", tmp);
+		LOG_A("\t - Certificate Type: %s", tmp);
 
 		/* print the compression algorithm (if any)
 		*/
 		tmp = gnutls_compression_get_name (gnutls_compression_get (session));
-		fd_log_debug("\t - Compression: %s", tmp);
+		LOG_A("\t - Compression: %s", tmp);
 
 		/* print the name of the cipher used.
 		* ie 3DES.
 		*/
 		tmp = gnutls_cipher_get_name (gnutls_cipher_get (session));
-		fd_log_debug("\t - Cipher: %s", tmp);
+		LOG_A("\t - Cipher: %s", tmp);
 
 		/* Print the MAC algorithms name.
 		* ie SHA1
 		*/
 		tmp = gnutls_mac_get_name (gnutls_mac_get (session));
-		fd_log_debug("\t - MAC: %s", tmp);
+		LOG_A("\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.
@@ -1508,14 +1554,29 @@
 
 #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;
+}
+
+static int fd_cnx_uses_dtls(struct cnxctx * conn) {
+	return fd_cnx_may_dtls(conn) && (fd_cnx_teststate(conn, CC_STATUS_TLS));
+}
+
 /* 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, char * priority, void * alt_creds)
+int fd_cnx_handshake(struct cnxctx * conn, int mode, int algo, char * priority, void * alt_creds)
 {
-	TRACE_ENTRY( "%p %d %p %p", conn, mode, priority, 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 */);
@@ -1523,11 +1584,13 @@
 	/* 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, priority, alt_creds) );
+	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 (conn->cc_sctp_para.pairs > 1) {
+	if ((!dtls) && (conn->cc_sctp_para.pairs > 1)) {
 #ifdef DISABLE_SCTP
 		ASSERT(0);
 		CHECK_FCT( ENOTSUP );
@@ -1540,8 +1603,13 @@
 		GNUTLS_TRACE( gnutls_transport_set_ptr( conn->cc_tls_para.session, (gnutls_transport_ptr_t) conn ) );
 
 		/* Set the push and pull callbacks */
-		GNUTLS_TRACE( gnutls_transport_set_pull_function(conn->cc_tls_para.session, (void *)fd_cnx_s_recv) );
-		GNUTLS_TRACE( gnutls_transport_set_push_function(conn->cc_tls_para.session, (void *)fd_cnx_s_send) );
+		if (!dtls) {
+			GNUTLS_TRACE( gnutls_transport_set_pull_function(conn->cc_tls_para.session, (void *)fd_cnx_s_recv) );
+			GNUTLS_TRACE( gnutls_transport_set_push_function(conn->cc_tls_para.session, (void *)fd_cnx_s_send) );
+		} else {
+			TODO("DTLS push/pull functions");
+			return ENOTSUP;
+		}
 	}
 	
 	/* additional initialization for gnutls 3.x */
@@ -1588,9 +1656,9 @@
 			});
 		#endif /* GNUTLS_VERSION_300 */
 	}
-
+	
 	/* Multi-stream TLS: handshake other streams as well */
-	if (conn->cc_sctp_para.pairs > 1) {
+	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));
@@ -1603,7 +1671,13 @@
 #endif /* DISABLE_SCTP */
 	} else {
 		/* Start decrypting the data */
-		CHECK_POSIX( pthread_create( &conn->cc_rcvthr, NULL, rcvthr_tls_single, conn ) );
+		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;
@@ -1736,40 +1810,49 @@
 		
 #ifndef DISABLE_SCTP
 		case IPPROTO_SCTP: {
-			if (flags & FD_CNX_ORDERED) {
-				/* We send over stream #0 */
-				CHECK_FCT( send_simple(conn, buf, len) );
-			} else {
-				/* Default case : no flag specified */
-			
-				int another_str = 0; /* do we send over stream #0 ? */
-				
-				if ((conn->cc_sctp_para.str_out > 1) && ((!fd_cnx_teststate(conn, CC_STATUS_TLS)) || (conn->cc_sctp_para.pairs > 1)))  {
-					/* Update the id of the stream we will send this message over */
-					conn->cc_sctp_para.next += 1;
-					conn->cc_sctp_para.next %= (fd_cnx_teststate(conn, CC_STATUS_TLS) ? conn->cc_sctp_para.pairs : conn->cc_sctp_para.str_out);
-					another_str = (conn->cc_sctp_para.next ? 1 : 0);
-				}
+			int dtls = fd_cnx_uses_dtls(conn);
 
-				if ( ! another_str ) {
+			if (!dtls) {
+				if (flags & FD_CNX_ORDERED) {
+					/* We send over stream #0 */
 					CHECK_FCT( send_simple(conn, buf, len) );
 				} else {
-					if (!fd_cnx_teststate(conn, CC_STATUS_TLS)) {
-						CHECK_FCT_DO( fd_sctp_sendstr(conn, conn->cc_sctp_para.next, buf, len), { fd_cnx_markerror(conn); return ENOTCONN; } );
+					/* Default case : no flag specified */
+			
+					int another_str = 0; /* do we send over stream #0 ? */
+
+					if ((conn->cc_sctp_para.str_out > 1) && ((!fd_cnx_teststate(conn, CC_STATUS_TLS)) || (conn->cc_sctp_para.pairs > 1)))  {
+						/* Update the id of the stream we will send this message over */
+						conn->cc_sctp_para.next += 1;
+						conn->cc_sctp_para.next %= (fd_cnx_teststate(conn, CC_STATUS_TLS) ? conn->cc_sctp_para.pairs : conn->cc_sctp_para.str_out);
+						another_str = (conn->cc_sctp_para.next ? 1 : 0);
+					}
+
+					if ( ! another_str ) {
+						CHECK_FCT( send_simple(conn, buf, len) );
 					} else {
-						/* push the record 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[conn->cc_sctp_para.next].session, buf + sent, len - sent), );
-							if (ret <= 0)
-								return ENOTCONN;
+						if (!fd_cnx_teststate(conn, CC_STATUS_TLS)) {
+							CHECK_FCT_DO( fd_sctp_sendstr(conn, conn->cc_sctp_para.next, buf, len), { fd_cnx_markerror(conn); return ENOTCONN; } );
+						} else {
+							/* push the record 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[conn->cc_sctp_para.next].session, buf + sent, len - sent), );
+								if (ret <= 0)
+									return ENOTCONN;
 
-							sent += ret;
-						} while ( sent < len );
+								sent += ret;
+							} while ( sent < len );
+						}
 					}
 				}
+			} else {
+				/* DTLS */
+				/* We signal the push function directly to tell if using stream 0 or round-robin */
+				TODO("DTLS send");
+				return ENOTSUP;
 			}
 		}
 		break;
@@ -1801,7 +1884,8 @@
 	/* 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
-		if (conn->cc_sctp_para.pairs > 1) {
+		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) );
--- a/libfdcore/cnxctx.h	Wed Jun 05 15:02:29 2013 +0800
+++ b/libfdcore/cnxctx.h	Wed Jun 05 19:22:26 2013 +0800
@@ -40,7 +40,7 @@
 
 /* The connection context structure */
 struct cnxctx {
-	char		cc_id[60];	/* The name of this connection */
+	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 */
@@ -64,6 +64,7 @@
 	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;
 
@@ -96,7 +97,7 @@
 
 /* TLS */
 int fd_tls_rcvthr_core(struct cnxctx * conn, gnutls_session_t session);
-int fd_tls_prepare(gnutls_session_t * session, int mode, char * priority, void * alt_creds);
+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 */
--- a/libfdcore/config.c	Wed Jun 05 15:02:29 2013 +0800
+++ b/libfdcore/config.c	Wed Jun 05 19:22:26 2013 +0800
@@ -93,6 +93,9 @@
 	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 server threads : %hu\n", fd_g_config->cnf_dispthr), return NULL);
 	if (FD_IS_LIST_EMPTY(&fd_g_config->cnf_endpoints)) {
--- a/libfdcore/fdcore-internal.h	Wed Jun 05 15:02:29 2013 +0800
+++ b/libfdcore/fdcore-internal.h	Wed Jun 05 19:22:26 2013 +0800
@@ -341,7 +341,9 @@
 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_handshake(struct cnxctx * conn, int mode, char * priority, void * alt_creds);
+#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);
--- a/libfdcore/p_ce.c	Wed Jun 05 15:02:29 2013 +0800
+++ b/libfdcore/p_ce.c	Wed Jun 05 19:22:26 2013 +0800
@@ -785,7 +785,7 @@
 			
 		} else {
 			fd_psm_change_state(peer, STATE_OPEN_HANDSHAKE);
-			CHECK_FCT_DO( fd_cnx_handshake(peer->p_cnxctx, GNUTLS_CLIENT, peer->p_hdr.info.config.pic_priority, NULL),
+			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);
@@ -954,7 +954,7 @@
 	/* 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, peer->p_hdr.info.config.pic_priority, NULL),
+		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);
--- a/libfdcore/p_cnx.c	Wed Jun 05 15:02:29 2013 +0800
+++ b/libfdcore/p_cnx.c	Wed Jun 05 19:22:26 2013 +0800
@@ -281,7 +281,9 @@
 	
 	/* Handshake if needed (secure port) */
 	if (nc->dotls) {
-		CHECK_FCT_DO( fd_cnx_handshake(cnx, GNUTLS_CLIENT, peer->p_hdr.info.config.pic_priority, NULL),
+		CHECK_FCT_DO( fd_cnx_handshake(cnx, GNUTLS_CLIENT, 
+						(peer->p_hdr.info.config.pic_flags.sctpsec == PI_SCTPSEC_3436) ? ALGO_HANDSHAKE_3436 : ALGO_HANDSHAKE_DEFAULT,
+						peer->p_hdr.info.config.pic_priority, NULL),
 			{
 				/* Handshake failed ...  */
 				fd_hook_call(HOOK_PEER_CONNECT_FAILED, NULL, peer, "TLS Handshake failed", NULL);
--- a/libfdcore/p_psm.c	Wed Jun 05 15:02:29 2013 +0800
+++ b/libfdcore/p_psm.c	Wed Jun 05 19:22:26 2013 +0800
@@ -784,7 +784,7 @@
 		switch (cur_state) {
 			case STATE_WAITCNXACK_ELEC:
 			case STATE_WAITCNXACK:
-				LOG_D("%s: Connection established", peer->p_hdr.info.pi_diamid);
+				LOG_D("%s: Connection established, %s", peer->p_hdr.info.pi_diamid, fd_cnx_getid(cnx));
 				fd_p_ce_handle_newcnx(peer, cnx);
 				break;
 				
--- a/libfdcore/peers.c	Wed Jun 05 15:02:29 2013 +0800
+++ b/libfdcore/peers.c	Wed Jun 05 19:22:26 2013 +0800
@@ -429,7 +429,7 @@
 			}
 		}
 		if (details > 1) {
-			CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, " [from:%s] flags:%s%s%s%s%s%s%s lft:%ds", 
+			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"),
@@ -438,6 +438,7 @@
 				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);
--- a/libfdcore/sctp3436.c	Wed Jun 05 15:02:29 2013 +0800
+++ b/libfdcore/sctp3436.c	Wed Jun 05 19:22:26 2013 +0800
@@ -565,7 +565,7 @@
 	/* 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, priority, alt_creds) );
+		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
--- a/libfdcore/server.c	Wed Jun 05 15:02:29 2013 +0800
+++ b/libfdcore/server.c	Wed Jun 05 19:22:26 2013 +0800
@@ -53,7 +53,7 @@
 
 	struct cnxctx *	conn;		/* server connection context (listening socket) */
 	int 		proto;		/* IPPROTO_TCP or IPPROTO_SCTP */
-	int 		secur;		/* TLS is started immediatly after connection ? */
+	int 		secur;		/* TLS is started immediatly after connection ? 0: no; 1: yes (TLS/TCP or DTLS/SCTP); 2: yes (TLS/TCP or TLS/SCTP) */
 	
 	pthread_t	thr;		/* The thread listening for new connections */
 	enum s_state	state;		/* state of the thread */
@@ -104,9 +104,9 @@
 		enum s_state st = get_status(s);
 		
 		if (details) {
-			CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "{server}(@%p)'%s': %s, %s, %s", s, fd_cnx_getid(s->conn), 
+			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 ? "Secur" : "NotSecur", s->secur,
 					(st == NOT_CREATED) ? "Thread not created" :
 					((st == RUNNING) ? "Thread running" :
 					((st == TERMINATED) ? "Thread terminated" :
@@ -146,8 +146,11 @@
 	
 	TRACE_ENTRY("%p", c);
 	
+	memset(&rcv_data, 0, sizeof(rcv_data));
+	
 	CHECK_PARAMS_DO(c && c->conn && c->chain.head, goto fatal_error );
 	
+	
 	s = c->chain.head->o;
 	
 	/* Name the current thread */
@@ -155,7 +158,7 @@
 	
 	/* 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, NULL);
+		int ret = fd_cnx_handshake(c->conn, 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 client '%s', connection aborted.", fd_cnx_getid(c->conn));
@@ -363,6 +366,14 @@
 			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 */
 	}
 	
--- a/tests/testcnx.c	Wed Jun 05 15:02:29 2013 +0800
+++ b/tests/testcnx.c	Wed Jun 05 19:22:26 2013 +0800
@@ -548,6 +548,7 @@
 struct handshake_flags {
 	struct cnxctx * cnx;
 	gnutls_certificate_credentials_t	creds;
+	int algo;
 	int ret;
 };
 
@@ -556,7 +557,7 @@
 {
 	struct handshake_flags * hf = arg;
 	fd_log_threadname ( "testcnx:handshake" );
-	hf->ret = fd_cnx_handshake(hf->cnx, GNUTLS_CLIENT, NULL, hf->creds);
+	hf->ret = fd_cnx_handshake(hf->cnx, GNUTLS_CLIENT, hf->algo, NULL, hf->creds);
 	return NULL;
 }
 
@@ -863,7 +864,7 @@
 		
 		/* At this point in legacy Diameter we start the handshake */
 		CHECK( 0, pthread_create(&thr, NULL, handshake_thr, &hf) );
-		CHECK( 0, fd_cnx_handshake(server_side, GNUTLS_SERVER, NULL, NULL) );
+		CHECK( 0, fd_cnx_handshake(server_side, GNUTLS_SERVER, ALGO_HANDSHAKE_DEFAULT /* No impact on TCP */, NULL, NULL) );
 		CHECK( 0, pthread_join(thr, NULL) );
 		CHECK( 0, hf.ret );
 		
@@ -904,6 +905,7 @@
 		cf.proto = IPPROTO_SCTP;
 		
 		memset(&hf, 0, sizeof(hf));
+		hf.algo = ALGO_HANDSHAKE_3436; /* this is mandatory for old TLS mechanism */
 		
 		/* Initialize remote certificate */
 		CHECK_GNUTLS_DO( ret = gnutls_certificate_allocate_credentials (&hf.creds), );
@@ -948,7 +950,7 @@
 		
 		/* At this point in legacy Diameter we start the handshake */
 		CHECK( 0, pthread_create(&thr, NULL, handshake_thr, &hf) );
-		CHECK( 0, fd_cnx_handshake(server_side, GNUTLS_SERVER, NULL, NULL) );
+		CHECK( 0, fd_cnx_handshake(server_side, GNUTLS_SERVER, ALGO_HANDSHAKE_3436, NULL, NULL) );
 		CHECK( 0, pthread_join(thr, NULL) );
 		CHECK( 0, hf.ret );
 		
@@ -1014,7 +1016,7 @@
 		
 		/* Start the handshake directly */
 		CHECK( 0, pthread_create(&thr, NULL, handshake_thr, &hf) );
-		CHECK( 0, fd_cnx_handshake(server_side, GNUTLS_SERVER, NULL, NULL) );
+		CHECK( 0, fd_cnx_handshake(server_side, GNUTLS_SERVER, ALGO_HANDSHAKE_DEFAULT, NULL, NULL) );
 		CHECK( 0, pthread_join(thr, NULL) );
 		CHECK( 0, hf.ret );
 		
@@ -1045,7 +1047,11 @@
 	}
 	
 #ifndef DISABLE_SCTP
-	/* SCTP Client / server emulating new Diameter behavior (handshake at connection directly) */
+	
+	
+	/* SCTP Client / server emulating new Diameter behavior (DTLS handshake at connection directly) */
+	TODO("Enabled after DTLS implementation");
+	if (0)
 	{
 		struct connect_flags cf;
 		struct handshake_flags hf;
@@ -1079,7 +1085,73 @@
 		
 		/* Start the handshake directly */
 		CHECK( 0, pthread_create(&thr, NULL, handshake_thr, &hf) );
-		CHECK( 0, fd_cnx_handshake(server_side, GNUTLS_SERVER, NULL, NULL) );
+		CHECK( 0, fd_cnx_handshake(server_side, GNUTLS_SERVER, ALGO_HANDSHAKE_DEFAULT, NULL, NULL) );
+		CHECK( 0, pthread_join(thr, NULL) );
+		CHECK( 0, hf.ret );
+		
+		/* Send a few TLS protected messages, and replies */
+		for (i = 0; i < 2 * NB_STREAMS; i++) {
+			CHECK( 0, fd_cnx_send(server_side, cer_buf, cer_sz, 0));
+			CHECK( 0, fd_cnx_receive(client_side, NULL, &rcv_buf, &rcv_sz));
+			CHECK( cer_sz, rcv_sz );
+			CHECK( 0, memcmp( rcv_buf, cer_buf, cer_sz ) );
+			free(rcv_buf);
+
+			CHECK( 0, fd_cnx_send(client_side, cer_buf, cer_sz, 0));
+			CHECK( 0, fd_cnx_receive(server_side, NULL, &rcv_buf, &rcv_sz));
+			CHECK( cer_sz, rcv_sz );
+			CHECK( 0, memcmp( rcv_buf, cer_buf, cer_sz ) );
+			free(rcv_buf);
+		}
+		
+		
+		/* Now close the connection */
+		CHECK( 0, pthread_create(&thr, NULL, destroy_thr, client_side) );
+		fd_cnx_destroy(server_side);
+		CHECK( 0, pthread_join(thr, NULL) );
+		
+		/* Free the credentials */
+		gnutls_certificate_free_keys(hf.creds);
+		gnutls_certificate_free_cas(hf.creds);
+		gnutls_certificate_free_credentials(hf.creds);
+	}
+	
+	/* SCTP Client / server emulating old intermediary Diameter behavior (TLS handshake at connection directly) */
+	{
+		struct connect_flags cf;
+		struct handshake_flags hf;
+		
+		memset(&cf, 0, sizeof(cf));
+		cf.proto = IPPROTO_SCTP;
+		
+		memset(&hf, 0, sizeof(hf));
+		hf.algo = ALGO_HANDSHAKE_3436; /* this is mandatory for old TLS mechanism */
+		
+		/* Initialize remote certificate */
+		CHECK_GNUTLS_DO( ret = gnutls_certificate_allocate_credentials (&hf.creds), );
+		CHECK( GNUTLS_E_SUCCESS, ret );
+		/* Set the CA */
+		CHECK_GNUTLS_DO( ret = gnutls_certificate_set_x509_trust_mem( hf.creds, &ca, GNUTLS_X509_FMT_PEM), );
+		CHECK( 1, ret );
+		/* Set the key */
+		CHECK_GNUTLS_DO( ret = gnutls_certificate_set_x509_key_mem( hf.creds, &client_cert, &client_priv, GNUTLS_X509_FMT_PEM), );
+		CHECK( GNUTLS_E_SUCCESS, ret );
+		
+		/* Start the client thread */
+		CHECK( 0, pthread_create(&thr, NULL, connect_thr, &cf) );
+
+		/* Accept the connection of the client */
+		server_side = fd_cnx_serv_accept(listener_sctp);
+		CHECK( 1, server_side ? 1 : 0 );
+		
+		/* Retrieve the client connection object */
+		CHECK( 0, pthread_join( thr, (void *)&client_side ) );
+		CHECK( 1, client_side ? 1 : 0 );
+		hf.cnx = client_side;
+		
+		/* Start the handshake directly */
+		CHECK( 0, pthread_create(&thr, NULL, handshake_thr, &hf) );
+		CHECK( 0, fd_cnx_handshake(server_side, GNUTLS_SERVER, ALGO_HANDSHAKE_3436, NULL, NULL) );
 		CHECK( 0, pthread_join(thr, NULL) );
 		CHECK( 0, hf.ret );
 		
@@ -1113,6 +1185,9 @@
 	
 	/* Test with different number of streams between server and client */
 #ifndef DISABLE_SCTP
+	/* DTLS / SCTP style */
+	TODO("Enabled after DTLS implementation");
+	if (0)
 	{
 		struct connect_flags cf;
 		struct handshake_flags hf;
@@ -1147,7 +1222,7 @@
 		
 		/* Start the handshake directly */
 		CHECK( 0, pthread_create(&thr, NULL, handshake_thr, &hf) );
-		CHECK( 0, fd_cnx_handshake(server_side, GNUTLS_SERVER, NULL, NULL) );
+		CHECK( 0, fd_cnx_handshake(server_side, GNUTLS_SERVER, ALGO_HANDSHAKE_DEFAULT, NULL, NULL) );
 		CHECK( 0, pthread_join(thr, NULL) );
 		CHECK( 0, hf.ret );
 		
@@ -1186,7 +1261,7 @@
 		
 		/* Start the handshake directly */
 		CHECK( 0, pthread_create(&thr, NULL, handshake_thr, &hf) );
-		CHECK( 0, fd_cnx_handshake(server_side, GNUTLS_SERVER, NULL, NULL) );
+		CHECK( 0, fd_cnx_handshake(server_side, GNUTLS_SERVER, ALGO_HANDSHAKE_DEFAULT, NULL, NULL) );
 		CHECK( 0, pthread_join(thr, NULL) );
 		CHECK( 0, hf.ret );
 		
@@ -1216,6 +1291,113 @@
 		gnutls_certificate_free_cas(hf.creds);
 		gnutls_certificate_free_credentials(hf.creds);
 	}
+	
+	/* TLS / SCTP style */
+	{
+		struct connect_flags cf;
+		struct handshake_flags hf;
+		
+		memset(&cf, 0, sizeof(cf));
+		cf.proto = IPPROTO_SCTP;
+		
+		memset(&hf, 0, sizeof(hf));
+		hf.algo = ALGO_HANDSHAKE_3436;
+		
+		/* Initialize remote certificate */
+		CHECK_GNUTLS_DO( ret = gnutls_certificate_allocate_credentials (&hf.creds), );
+		CHECK( GNUTLS_E_SUCCESS, ret );
+		/* Set the CA */
+		CHECK_GNUTLS_DO( ret = gnutls_certificate_set_x509_trust_mem( hf.creds, &ca, GNUTLS_X509_FMT_PEM), );
+		CHECK( 1, ret );
+		/* Set the key */
+		CHECK_GNUTLS_DO( ret = gnutls_certificate_set_x509_key_mem( hf.creds, &client_cert, &client_priv, GNUTLS_X509_FMT_PEM), );
+		CHECK( GNUTLS_E_SUCCESS, ret );
+		
+		/* Start the client thread with more streams than the server */
+		fd_g_config->cnf_sctp_str = 2 * NB_STREAMS;
+		CHECK( 0, pthread_create(&thr, NULL, connect_thr, &cf) );
+
+		/* Accept the connection of the client */
+		server_side = fd_cnx_serv_accept(listener_sctp);
+		CHECK( 1, server_side ? 1 : 0 );
+		
+		/* Retrieve the client connection object */
+		CHECK( 0, pthread_join( thr, (void *)&client_side ) );
+		CHECK( 1, client_side ? 1 : 0 );
+		hf.cnx = client_side;
+		
+		/* Start the handshake directly */
+		CHECK( 0, pthread_create(&thr, NULL, handshake_thr, &hf) );
+		CHECK( 0, fd_cnx_handshake(server_side, GNUTLS_SERVER, ALGO_HANDSHAKE_3436, NULL, NULL) );
+		CHECK( 0, pthread_join(thr, NULL) );
+		CHECK( 0, hf.ret );
+		
+		/* Send a few TLS protected message, and replies */
+		for (i = 0; i < 4 * NB_STREAMS; i++) {
+			CHECK( 0, fd_cnx_send(server_side, cer_buf, cer_sz, 0));
+			CHECK( 0, fd_cnx_receive(client_side, NULL, &rcv_buf, &rcv_sz));
+			CHECK( cer_sz, rcv_sz );
+			CHECK( 0, memcmp( rcv_buf, cer_buf, cer_sz ) );
+			free(rcv_buf);
+
+			CHECK( 0, fd_cnx_send(client_side, cer_buf, cer_sz, 0));
+			CHECK( 0, fd_cnx_receive(server_side, NULL, &rcv_buf, &rcv_sz));
+			CHECK( cer_sz, rcv_sz );
+			CHECK( 0, memcmp( rcv_buf, cer_buf, cer_sz ) );
+			free(rcv_buf);
+		}
+		
+		/* Now close the connection */
+		CHECK( 0, pthread_create(&thr, NULL, destroy_thr, client_side) );
+		fd_cnx_destroy(server_side);
+		CHECK( 0, pthread_join(thr, NULL) );
+		
+		/* Do the same test but with more streams on the server this time */
+		fd_g_config->cnf_sctp_str = NB_STREAMS / 2;
+		CHECK( 0, pthread_create(&thr, NULL, connect_thr, &cf) );
+
+		/* Accept the connection of the client */
+		server_side = fd_cnx_serv_accept(listener_sctp);
+		CHECK( 1, server_side ? 1 : 0 );
+		
+		/* Retrieve the client connection object */
+		CHECK( 0, pthread_join( thr, (void *)&client_side ) );
+		CHECK( 1, client_side ? 1 : 0 );
+		hf.cnx = client_side;
+		
+		/* Start the handshake directly */
+		CHECK( 0, pthread_create(&thr, NULL, handshake_thr, &hf) );
+		CHECK( 0, fd_cnx_handshake(server_side, GNUTLS_SERVER, ALGO_HANDSHAKE_3436, NULL, NULL) );
+		CHECK( 0, pthread_join(thr, NULL) );
+		CHECK( 0, hf.ret );
+		
+		/* Send a few TLS protected message, and replies */
+		for (i = 0; i < 2 * NB_STREAMS; i++) {
+			CHECK( 0, fd_cnx_send(server_side, cer_buf, cer_sz, 0));
+			CHECK( 0, fd_cnx_receive(client_side, NULL, &rcv_buf, &rcv_sz));
+			CHECK( cer_sz, rcv_sz );
+			CHECK( 0, memcmp( rcv_buf, cer_buf, cer_sz ) );
+			free(rcv_buf);
+
+			CHECK( 0, fd_cnx_send(client_side, cer_buf, cer_sz, 0));
+			CHECK( 0, fd_cnx_receive(server_side, NULL, &rcv_buf, &rcv_sz));
+			CHECK( cer_sz, rcv_sz );
+			CHECK( 0, memcmp( rcv_buf, cer_buf, cer_sz ) );
+			free(rcv_buf);
+		}
+		
+		/* Now close the connection */
+		CHECK( 0, pthread_create(&thr, NULL, destroy_thr, client_side) );
+		fd_cnx_destroy(server_side);
+		CHECK( 0, pthread_join(thr, NULL) );
+		
+		
+		/* Free the credentials */
+		gnutls_certificate_free_keys(hf.creds);
+		gnutls_certificate_free_cas(hf.creds);
+		gnutls_certificate_free_credentials(hf.creds);
+	}
+	
 #endif /* DISABLE_SCTP */
 	
 	
@@ -1256,7 +1438,7 @@
 		
 		/* Start the handshake directly */
 		CHECK( 0, pthread_create(&thr, NULL, handshake_thr, &hf) );
-		CHECK( EINVAL, fd_cnx_handshake(server_side, GNUTLS_SERVER, NULL, NULL) );
+		CHECK( EINVAL, fd_cnx_handshake(server_side, GNUTLS_SERVER, ALGO_HANDSHAKE_DEFAULT, NULL, NULL) );
 		fd_cnx_destroy(server_side);
 		
 		CHECK( 0, pthread_join(thr, NULL) );
@@ -1273,6 +1455,9 @@
 	
 	/* Same in SCTP */
 #ifndef DISABLE_SCTP
+	/* DTLS */
+	TODO("Enabled after DTLS implementation");
+	if (0)
 	{
 		struct connect_flags cf;
 		struct handshake_flags hf;
@@ -1307,7 +1492,57 @@
 		
 		/* Start the handshake directly */
 		CHECK( 0, pthread_create(&thr, NULL, handshake_thr, &hf) );
-		CHECK( EINVAL, fd_cnx_handshake(server_side, GNUTLS_SERVER, NULL, NULL) );
+		CHECK( EINVAL, fd_cnx_handshake(server_side, GNUTLS_SERVER, ALGO_HANDSHAKE_DEFAULT, NULL, NULL) );
+		fd_cnx_destroy(server_side);
+		CHECK( 0, pthread_join(thr, NULL) );
+		
+		/* Now close the connection */
+		CHECK( 0, pthread_create(&thr, NULL, destroy_thr, client_side) );
+		CHECK( 0, pthread_join(thr, NULL) );
+		
+		/* Free the credentials */
+		gnutls_certificate_free_keys(hf.creds);
+		gnutls_certificate_free_cas(hf.creds);
+		gnutls_certificate_free_credentials(hf.creds);
+	}
+	
+	/* TLS */
+	{
+		struct connect_flags cf;
+		struct handshake_flags hf;
+		
+		memset(&cf, 0, sizeof(cf));
+		cf.proto = IPPROTO_SCTP;
+		
+		memset(&hf, 0, sizeof(hf));
+		hf.algo = ALGO_HANDSHAKE_3436;
+		
+		/* Initialize remote certificate */
+		CHECK_GNUTLS_DO( ret = gnutls_certificate_allocate_credentials (&hf.creds), );
+		CHECK( GNUTLS_E_SUCCESS, ret );
+		/* Set the CA */
+		CHECK_GNUTLS_DO( ret = gnutls_certificate_set_x509_trust_mem( hf.creds, &notrust_ca, GNUTLS_X509_FMT_PEM), );
+		CHECK_GNUTLS_DO( ret = gnutls_certificate_set_x509_trust_mem( hf.creds, &ca, GNUTLS_X509_FMT_PEM), );
+		CHECK( 1, ret );
+		/* Set the key */
+		CHECK_GNUTLS_DO( ret = gnutls_certificate_set_x509_key_mem( hf.creds, &notrust_cert, &notrust_priv, GNUTLS_X509_FMT_PEM), );
+		CHECK( GNUTLS_E_SUCCESS, ret );
+		
+		/* Start the client thread */
+		CHECK( 0, pthread_create(&thr, NULL, connect_thr, &cf) );
+
+		/* Accept the connection of the client */
+		server_side = fd_cnx_serv_accept(listener_sctp);
+		CHECK( 1, server_side ? 1 : 0 );
+		
+		/* Retrieve the client connection object */
+		CHECK( 0, pthread_join( thr, (void *)&client_side ) );
+		CHECK( 1, client_side ? 1 : 0 );
+		hf.cnx = client_side;
+		
+		/* Start the handshake directly */
+		CHECK( 0, pthread_create(&thr, NULL, handshake_thr, &hf) );
+		CHECK( EINVAL, fd_cnx_handshake(server_side, GNUTLS_SERVER, ALGO_HANDSHAKE_3436, NULL, NULL) );
 		fd_cnx_destroy(server_side);
 		CHECK( 0, pthread_join(thr, NULL) );
 		
@@ -1356,7 +1591,7 @@
 		
 		/* Start the handshake directly */
 		CHECK( 0, pthread_create(&thr, NULL, handshake_thr, &hf) );
-		CHECK( EINVAL, fd_cnx_handshake(server_side, GNUTLS_SERVER, NULL, NULL) );
+		CHECK( EINVAL, fd_cnx_handshake(server_side, GNUTLS_SERVER, ALGO_HANDSHAKE_DEFAULT, NULL, NULL) );
 		fd_cnx_destroy(server_side);
 		CHECK( 0, pthread_join(thr, NULL) );
 		
@@ -1408,7 +1643,7 @@
 		
 		/* Start the handshake, check it is successful */
 		CHECK( 0, pthread_create(&thr, NULL, handshake_thr, &hf) );
-		CHECK( 0, fd_cnx_handshake(server_side, GNUTLS_SERVER, NULL, NULL) );
+		CHECK( 0, fd_cnx_handshake(server_side, GNUTLS_SERVER, ALGO_HANDSHAKE_DEFAULT, NULL, NULL) );
 		CHECK( 0, pthread_join(thr, NULL) );
 		CHECK( 0, hf.ret );
 		
@@ -1434,7 +1669,7 @@
 		
 		/* Start the handshake, check it is successful */
 		CHECK( 0, pthread_create(&thr, NULL, handshake_thr, &hf) );
-		CHECK( EINVAL, fd_cnx_handshake(server_side, GNUTLS_SERVER, NULL, NULL) );
+		CHECK( EINVAL, fd_cnx_handshake(server_side, GNUTLS_SERVER, ALGO_HANDSHAKE_DEFAULT, NULL, NULL) );
 		fd_cnx_destroy(server_side);
 		CHECK( 0, pthread_join(thr, NULL) );
 		
@@ -1488,7 +1723,7 @@
 		
 		/* Start the handshake */
 		CHECK( 0, pthread_create(&thr, NULL, handshake_thr, &hf) );
-		CHECK( 0, fd_cnx_handshake(server_side, GNUTLS_SERVER, NULL, NULL) );
+		CHECK( 0, fd_cnx_handshake(server_side, GNUTLS_SERVER, ALGO_HANDSHAKE_DEFAULT, NULL, NULL) );
 		CHECK( 0, pthread_join(thr, NULL) );
 		CHECK( 0, hf.ret );
 		
@@ -1542,6 +1777,8 @@
 	
 #ifndef DISABLE_SCTP
 	/* And re-test with a SCTP connection */
+	TODO("Enabled after DTLS implementation");
+	if (0)
 	{
 		struct connect_flags cf;
 		struct handshake_flags hf;
@@ -1581,7 +1818,100 @@
 		
 		/* Start the handshake */
 		CHECK( 0, pthread_create(&thr, NULL, handshake_thr, &hf) );
-		CHECK( 0, fd_cnx_handshake(server_side, GNUTLS_SERVER, NULL, NULL) );
+		CHECK( 0, fd_cnx_handshake(server_side, GNUTLS_SERVER, ALGO_HANDSHAKE_DEFAULT, NULL, NULL) );
+		CHECK( 0, pthread_join(thr, NULL) );
+		CHECK( 0, hf.ret );
+		
+		/* Test some simple functions */
+		
+		/* fd_cnx_getid */
+		str = fd_cnx_getid(server_side);
+		CHECK( 1, str ? 1 : 0 );
+		CHECK( 1, (str[0] != '\0') ? 1 : 0 );
+		
+		/* fd_cnx_getproto */
+		i = fd_cnx_getproto(server_side);
+		CHECK( IPPROTO_SCTP, i);
+		
+		/* fd_cnx_getTLS */
+		i = fd_cnx_getTLS(server_side);
+		CHECK( 1, i ? 1 : 0 );
+		
+		/* fd_cnx_getcred */
+		CHECK( 0, fd_cnx_getcred(server_side, &cert_list, &cert_list_size) );
+		CHECK( 1, (cert_list_size > 0) ? 1 : 0 );
+		/* We could also verify that the cert_list really contains the client_cert and ca certificates */
+		
+		/* fd_cnx_getremoteid */
+		str = fd_cnx_getremoteid(server_side);
+		CHECK( 1, str ? 1 : 0 );
+		CHECK( 1, (str[0] != '\0') ? 1 : 0 );
+		
+		/* fd_cnx_recv_setaltfifo */
+		CHECK( 0, fd_cnx_send(client_side, cer_buf, cer_sz, 0));
+		CHECK( 0, fd_fifo_new(&myfifo, 0) );
+		CHECK( 0, fd_cnx_recv_setaltfifo(server_side, myfifo) );
+		CHECK( 0, clock_gettime(CLOCK_REALTIME, &now) );
+		do {
+			CHECK( 0, fd_event_timedget(myfifo, &now, ETIMEDOUT, &ev_code, NULL, (void *)&rcv_buf) );
+			free(rcv_buf);
+		} while (ev_code != FDEVP_CNX_MSG_RECV);
+		
+		/* Now close the connection */
+		CHECK( 0, pthread_create(&thr, NULL, destroy_thr, client_side) );
+		fd_cnx_destroy(server_side);
+		CHECK( 0, pthread_join(thr, NULL) );
+		
+		fd_event_destroy(&myfifo, free);
+		
+		/* Free the credentials */
+		gnutls_certificate_free_keys(hf.creds);
+		gnutls_certificate_free_cas(hf.creds);
+		gnutls_certificate_free_credentials(hf.creds);
+	}
+	
+	/* TLS */
+	{
+		struct connect_flags cf;
+		struct handshake_flags hf;
+		char * str;
+		const gnutls_datum_t *cert_list;
+		unsigned int cert_list_size;
+		struct fifo * myfifo = NULL;
+		struct timespec now;
+		int ev_code;
+		
+		memset(&cf, 0, sizeof(cf));
+		cf.proto = IPPROTO_SCTP;
+		
+		memset(&hf, 0, sizeof(hf));
+		hf.algo = ALGO_HANDSHAKE_3436;
+		
+		/* Initialize remote certificate */
+		CHECK_GNUTLS_DO( ret = gnutls_certificate_allocate_credentials (&hf.creds), );
+		CHECK( GNUTLS_E_SUCCESS, ret );
+		/* Set the CA */
+		CHECK_GNUTLS_DO( ret = gnutls_certificate_set_x509_trust_mem( hf.creds, &ca, GNUTLS_X509_FMT_PEM), );
+		CHECK( 1, ret );
+		/* Set the key */
+		CHECK_GNUTLS_DO( ret = gnutls_certificate_set_x509_key_mem( hf.creds, &client_cert, &client_priv, GNUTLS_X509_FMT_PEM), );
+		CHECK( GNUTLS_E_SUCCESS, ret );
+		
+		/* Start the client thread */
+		CHECK( 0, pthread_create(&thr, NULL, connect_thr, &cf) );
+
+		/* Accept the connection of the client */
+		server_side = fd_cnx_serv_accept(listener_sctp);
+		CHECK( 1, server_side ? 1 : 0 );
+		
+		/* Retrieve the client connection object */
+		CHECK( 0, pthread_join( thr, (void *)&client_side ) );
+		CHECK( 1, client_side ? 1 : 0 );
+		hf.cnx = client_side;
+		
+		/* Start the handshake */
+		CHECK( 0, pthread_create(&thr, NULL, handshake_thr, &hf) );
+		CHECK( 0, fd_cnx_handshake(server_side, GNUTLS_SERVER, ALGO_HANDSHAKE_3436, NULL, NULL) );
 		CHECK( 0, pthread_join(thr, NULL) );
 		CHECK( 0, hf.ret );
 		
"Welcome to our mercurial repository"