changeset 1229:cf9bad611f90

Merge with freeDiameter-proposed
author Sebastien Decugis <sdecugis@freediameter.net>
date Thu, 20 Jun 2013 10:20:29 +0800
parents 33b94b5b8289 (diff) 65c6460f60f2 (current diff)
children 992437a90bda
files
diffstat 21 files changed, 812 insertions(+), 485 deletions(-) [+]
line wrap: on
line diff
--- a/.hgtags	Wed Jun 19 10:20:47 2013 +0800
+++ b/.hgtags	Thu Jun 20 10:20:29 2013 +0800
@@ -63,6 +63,4 @@
 d00b5914351e83fa90b2ea09f8c51cd82b312157 proposed_merged
 d00b5914351e83fa90b2ea09f8c51cd82b312157 proposed_merged
 fb9282c75ec51eb43690413c5b66a3192dfc79d3 proposed_merged
-fb9282c75ec51eb43690413c5b66a3192dfc79d3 proposed_merged
-043b894b0511b6beb155576e9e2c509a21be8360 proposed_merged
-08e72b858f03a77869792cb8ad8e0acbc83c2590 1.2.0-rc2
+56c36d1007b4eb1108b7a3702589c9cbb2aeeb1f dtls_dev
--- a/CMakeLists.txt	Wed Jun 19 10:20:47 2013 +0800
+++ b/CMakeLists.txt	Thu Jun 20 10:20:29 2013 +0800
@@ -84,6 +84,7 @@
 ENDIF(APPLE) 
 
 # Location for the include files
+SET(CMAKE_include_directories_BEFORE ON)
 INCLUDE_DIRECTORIES(include)
 INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR}/include)
 SUBDIRS(include/freeDiameter)
--- a/cmake/Modules/FindGnuTLS.cmake	Wed Jun 19 10:20:47 2013 +0800
+++ b/cmake/Modules/FindGnuTLS.cmake	Thu Jun 20 10:20:29 2013 +0800
@@ -1,13 +1,11 @@
 # - Find gnutls
-# Find the native GNUTLS includes and library
+# Find the native GNUTLS includes and library. Version 3.0.0 at least is required
 #
 #  GNUTLS_FOUND - True if gnutls found.
 #  GNUTLS_INCLUDE_DIR - where to find gnutls.h, etc.
 #  GNUTLS_LIBRARIES - List of libraries when using gnutls.
-#  GNUTLS_VERSION_210 - true if GnuTLS version is >= 2.10.0 (does not require additional separate gcrypt initialization)
-#  GNUTLS_VERSION_212 - true if GnuTLS version is >= 2.12.0 (supports gnutls_transport_set_vec_push_function)
-#  GNUTLS_VERSION_300 - true if GnuTLS version is >= 3.00.0 (x509 verification functions changed)
 #  GNUTLS_VERSION_310 - true if GnuTLS version is >= 3.01.0 (stabilization branch with new APIs)
+#  GNUTLS_VERSION_322 - true if GnuTLS version is >= 3.2.2 (DTLS over SCTP improvements)
 
 if (GNUTLS_INCLUDE_DIR AND GNUTLS_LIBRARIES)
   set(GNUTLS_FIND_QUIETLY TRUE)
@@ -46,19 +44,19 @@
   IF( NOT( "${GNUTLS_VERSION_TEST_FOR}" STREQUAL "${GNUTLS_LIBRARY}" ))
     INCLUDE (CheckLibraryExists) 
     MESSAGE(STATUS "Checking GNUTLS version")
-    UNSET(GNUTLS_VERSION_210)
-    UNSET(GNUTLS_VERSION_210 CACHE)
-    UNSET(GNUTLS_VERSION_212)
-    UNSET(GNUTLS_VERSION_212 CACHE)
     UNSET(GNUTLS_VERSION_300)
     UNSET(GNUTLS_VERSION_300 CACHE)
     UNSET(GNUTLS_VERSION_310)
     UNSET(GNUTLS_VERSION_310 CACHE)
+    UNSET(GNUTLS_VERSION_322)
+    UNSET(GNUTLS_VERSION_322 CACHE)
     GET_FILENAME_COMPONENT(GNUTLS_PATH ${GNUTLS_LIBRARY} PATH)
-    CHECK_LIBRARY_EXISTS(gnutls gnutls_hash ${GNUTLS_PATH} GNUTLS_VERSION_210) 
-    CHECK_LIBRARY_EXISTS(gnutls gnutls_transport_set_vec_push_function ${GNUTLS_PATH} GNUTLS_VERSION_212) 
     CHECK_LIBRARY_EXISTS(gnutls gnutls_x509_trust_list_verify_crt ${GNUTLS_PATH} GNUTLS_VERSION_300) 
+    IF(NOT GNUTLS_VERSION_300)
+	MESSAGE(FATAL_ERROR "GnuTLS found but version is too old, need 3.x at least for DTLS support")
+    ENDIF(NOT GNUTLS_VERSION_300)
     CHECK_LIBRARY_EXISTS(gnutls gnutls_handshake_set_timeout ${GNUTLS_PATH} GNUTLS_VERSION_310) 
+    CHECK_LIBRARY_EXISTS(gnutls gnutls_handshake_set_hook_function ${GNUTLS_PATH} GNUTLS_VERSION_322) 
     SET( GNUTLS_VERSION_TEST_FOR ${GNUTLS_LIBRARY} CACHE INTERNAL "Version the test was made against" )
   ENDIF (NOT( "${GNUTLS_VERSION_TEST_FOR}" STREQUAL "${GNUTLS_LIBRARY}" ))
 ENDIF(GNUTLS_FOUND)
--- a/doc/freediameter.conf.sample	Wed Jun 19 10:20:47 2013 +0800
+++ b/doc/freediameter.conf.sample	Thu Jun 20 10:20:29 2013 +0800
@@ -34,6 +34,13 @@
 # Default: 5658. Use 0 to disable.
 #SecPort = 5658;
 
+# freeDiameter now supports 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 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;
+
 # Use RFC3588 method for TLS protection, where TLS is negociated after CER/CEA exchange is completed 
 # on the unsecure connection. The alternative is RFC6733 mechanism, where TLS protects also the 
 # CER/CEA exchange on a dedicated secure port.
@@ -230,7 +237,8 @@
 #ConnectPeer = "diameterid" [ { parameter1; parameter2; ...} ] ;
 # Parameters that can be specified in the peer's parameter list:
 #  No_TCP; No_SCTP; No_IP; No_IPv6; Prefer_TCP; TLS_old_method;
-#  No_TLS;       # assume transparent security instead of TLS. DTLS is not supported yet (will change in future versions).
+#  No_TLS;       # assume transparent security instead of TLS.
+#  SctpSec3436;	 # Use TLS/SCTP instead of DTLS/SCTP to protect SCTP associations with this peer (not recommended).
 #  Port = 5658;  # The port to connect to
 #  TcTimer = 30;
 #  TwTimer = 30;
--- a/freeDiameterd/main.c	Wed Jun 19 10:20:47 2013 +0800
+++ b/freeDiameterd/main.c	Thu Jun 20 10:20:29 2013 +0800
@@ -51,7 +51,10 @@
 
 /* gnutls debug */
 static void fd_gnutls_debug(int level, const char * str) {
-	fd_log_debug(" [gnutls:%d] %s", level, str);
+	int len = strlen(str);
+	if (str[len-1] == '\n')
+		len--;
+	fd_log_debug(" [gnutls:%d] %.*s", level, len, str);
 }
 
 
--- a/include/freeDiameter/freeDiameter-host.h.in	Wed Jun 19 10:20:47 2013 +0800
+++ b/include/freeDiameter/freeDiameter-host.h.in	Thu Jun 20 10:20:29 2013 +0800
@@ -60,10 +60,8 @@
 #cmakedefine DIAMID_IDNA_REJECT
 #cmakedefine DISABLE_PEER_EXPIRY
 #cmakedefine WORKAROUND_ACCEPT_INVALID_VSAI
-#cmakedefine GNUTLS_VERSION_210
-#cmakedefine GNUTLS_VERSION_212
-#cmakedefine GNUTLS_VERSION_300
 #cmakedefine GNUTLS_VERSION_310
+#cmakedefine GNUTLS_VERSION_322
 
 #cmakedefine ERRORS_ON_TODO
 #cmakedefine DEBUG
--- a/include/freeDiameter/libfdcore.h	Wed Jun 19 10:20:47 2013 +0800
+++ b/include/freeDiameter/libfdcore.h	Thu Jun 20 10:20:29 2013 +0800
@@ -44,6 +44,8 @@
 #include <freeDiameter/libfdproto.h>
 #include <gnutls/gnutls.h>
 #include <gnutls/x509.h>
+#include <gnutls/dtls.h>
+
 
 /* GNUTLS version */
 #ifndef GNUTLS_VERSION
@@ -165,9 +167,7 @@
 		
 		/* GNUTLS server credential(s) */
 		gnutls_certificate_credentials_t credentials; /* contains local cert + trust anchors */
-		#ifdef GNUTLS_VERSION_300
 		gnutls_x509_trust_list_t         trustlist; /* the logic to check local certificate has changed */
-		#endif /* GNUTLS_VERSION_300 */
 		
 	} 		 cnf_sec_data;
 	
--- a/include/freeDiameter/libfdproto.h	Wed Jun 19 10:20:47 2013 +0800
+++ b/include/freeDiameter/libfdproto.h	Thu Jun 20 10:20:29 2013 +0800
@@ -129,6 +129,7 @@
 /*                          DEBUG                             */
 /*============================================================*/
 
+#define LOGS_VALIGN
 
 /*
  * FUNCTION:	fd_log
@@ -296,7 +297,11 @@
 
 /* In DEBUG mode, we add meta-information along each trace. This makes multi-threading problems easier to debug. */
 #ifdef DEBUG
-# define STD_TRACE_FMT_STRING "pid:%s in %s@%s:%d: "
+# ifdef LOGS_VALIGN
+#  define STD_TRACE_FMT_STRING "pid:%-25.25s in %25.25s@%-15.15s:%-4d: "
+# else /* LOGS_VALIGN */
+#  define STD_TRACE_FMT_STRING "pid:%s in %s@%s:%d: "
+# endif /* LOGS_VALIGN */
 # define STD_TRACE_FMT_ARGS   , ((char *)pthread_getspecific(fd_log_thname) ?: "unnamed"), __PRETTY_FUNCTION__, __STRIPPED_FILE__, __LINE__
 #else /* DEBUG */
 # define STD_TRACE_FMT_STRING ""
--- a/libfdcore/CMakeLists.txt	Wed Jun 19 10:20:47 2013 +0800
+++ b/libfdcore/CMakeLists.txt	Thu Jun 20 10:20:29 2013 +0800
@@ -38,7 +38,7 @@
 	)
 
 IF(NOT DISABLE_SCTP)
-	SET(FDCORE_SRC ${FDCORE_SRC} sctp.c sctp3436.c)
+	SET(FDCORE_SRC ${FDCORE_SRC} sctp.c sctp3436.c sctp_dtls.c)
 ENDIF(NOT DISABLE_SCTP)
 
 SET(FDCORE_GEN_SRC
--- a/libfdcore/cnxctx.c	Wed Jun 19 10:20:47 2013 +0800
+++ b/libfdcore/cnxctx.c	Thu Jun 20 10:20:29 2013 +0800
@@ -88,6 +88,7 @@
 
 	CHECK_MALLOC_DO( conn = malloc(sizeof(struct cnxctx)), return NULL );
 	memset(conn, 0, sizeof(struct cnxctx));
+	fd_list_init(&conn->cc_sctp_dtls_data.chunks, NULL);
 
 	if (full) {
 		CHECK_FCT_DO( fd_fifo_new ( &conn->cc_incoming, 5 ), return NULL );
@@ -630,7 +631,6 @@
 }
 
 
-#ifdef GNUTLS_VERSION_300
 /* The pull_timeout function for gnutls */
 static int fd_cnx_s_select (struct cnxctx * conn, unsigned int ms)
 {
@@ -645,7 +645,6 @@
 	
 	return select (conn->cc_socket + 1, &rfds, NULL, NULL, &tv);
 }		
-#endif /* GNUTLS_VERSION_300 */
 
 /* A recv-like function, taking a cnxctx object instead of socket as entry. We use it to quickly react to timeouts without traversing GNUTLS wrapper each time */
 ssize_t fd_cnx_s_recv(struct cnxctx * conn, void *buffer, size_t length)
@@ -708,17 +707,6 @@
 	return ret;
 }
 
-/* Send, for older GNUTLS */
-#ifndef GNUTLS_VERSION_212
-static ssize_t fd_cnx_s_send(struct cnxctx * conn, const void *buffer, size_t length)
-{
-	struct iovec iov;
-	iov.iov_base = (void *)buffer;
-	iov.iov_len  = length;
-	return fd_cnx_s_sendv(conn, &iov, 1);
-}
-#endif /* GNUTLS_VERSION_212 */
-
 #define ALIGNOF(t) ((char *)(&((struct { char c; t _h; } *)0)->_h) - (char *)0)  /* Could use __alignof__(t) on some systems but this is more portable probably */
 #define PMDL_PADDED(len) ( ((len) + ALIGNOF(struct fd_msg_pmdl) - 1) & ~(ALIGNOF(struct fd_msg_pmdl) - 1) )
 
@@ -946,7 +934,7 @@
 
 
 /* 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 fd_tls_recv_handle_error(struct cnxctx * conn, gnutls_session_t session, void * data, size_t sz)
 {
 	ssize_t ret;
 again:	
@@ -976,6 +964,14 @@
 					TRACE_DEBUG(FULL, "Got 0 size while reading the socket, probably connection closed...");
 					break;
 				
+				case GNUTLS_E_WARNING_ALERT_RECEIVED:
+					LOG_N("Received TLS WARNING ALERT: %s", gnutls_alert_get_name(gnutls_alert_get(session)) ?: "<unknown alert>");
+					goto again;
+					
+				case GNUTLS_E_FATAL_ALERT_RECEIVED:
+					LOG_E("Received TLS FATAL ALERT: %s", gnutls_alert_get_name(gnutls_alert_get(session)) ?: "<unknown alert>");
+					break;
+					
 				default:
 					if (gnutls_error_is_fatal (ret) == 0) {
 						LOG_N("Ignoring non-fatal GNU TLS error: %s", gnutls_strerror (ret));
@@ -995,7 +991,7 @@
 }
 
 /* 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 fd_tls_send_handle_error(struct cnxctx * conn, gnutls_session_t session, void * data, size_t sz)
 {
 	ssize_t ret;
 	struct timespec ts, now;
@@ -1043,13 +1039,7 @@
 
 
 /* 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)
+int fd_tls_rcvthr_core(struct cnxctx * conn, gnutls_session_t session, int dtls)
 {
 	/* No guarantee that GnuTLS preserves the message boundaries, so we re-build it as in TCP. */
 	do {
@@ -1060,7 +1050,10 @@
 		size_t	received = 0;
 
 		do {
-			ret = fd_tls_recv_handle_error(conn, session, &header[received], sizeof(header) - received);
+			if (!dtls)
+				ret = fd_tls_recv_handle_error(conn, session, &header[received], sizeof(header) - received);
+			else
+				ret = fd_dtls_recv_handle_error(conn, session, &header[received], sizeof(header) - received);
 			if (ret <= 0) {
 				/* The connection is closed */
 				goto out;
@@ -1085,7 +1078,10 @@
 
 		while (received < rcv_data.length) {
 			pthread_cleanup_push(free_rcvdata, &rcv_data); /* In case we are canceled, clean the partialy built buffer */
-			ret = fd_tls_recv_handle_error(conn, session, rcv_data.buffer + received, rcv_data.length - received);
+			if (!dtls)
+				ret = fd_tls_recv_handle_error(conn, session, rcv_data.buffer + received, rcv_data.length - received);
+			else
+				ret = fd_dtls_recv_handle_error(conn, session, rcv_data.buffer + received, rcv_data.length - received);
 			pthread_cleanup_pop(0);
 
 			if (ret <= 0) {
@@ -1130,8 +1126,8 @@
 	ASSERT( fd_cnx_target_queue(conn) );
 
 	/* The next function only returns when there is an error on the socket */	
-	CHECK_FCT_DO(fd_tls_rcvthr_core(conn, conn->cc_tls_para.session), /* continue */);
-
+	CHECK_FCT_DO(fd_tls_rcvthr_core(conn, conn->cc_tls_para.session, 0), /* continue */);
+	
 	TRACE_DEBUG(FULL, "Thread terminated");	
 	return NULL;
 }
@@ -1139,13 +1135,8 @@
 /* Prepare a gnutls session object for handshake */
 int fd_tls_prepare(gnutls_session_t * session, int mode, int dtls, char * priority, void * alt_creds)
 {
-	if (dtls) {
-		LOG_E("DTLS sessions not yet supported");
-		return ENOTSUP;
-	}
-
 	/* Create the session context */
-	CHECK_GNUTLS_DO( gnutls_init (session, mode), return ENOMEM );
+	CHECK_GNUTLS_DO( gnutls_init (session, mode | (dtls ? GNUTLS_DATAGRAM : 0 )), return ENOMEM );
 
 	/* Set the algorithm suite */
 	if (priority) {
@@ -1155,6 +1146,11 @@
 	} else {
 		CHECK_GNUTLS_DO( gnutls_priority_set( *session, fd_g_config->cnf_sec_data.prio_cache ), return EINVAL );
 	}
+	
+	/* Set DTLS-specific parameters */
+	if (dtls) {
+		CHECK_FCT_DO( fd_sctp_dtls_prepare(*session), return EINVAL);
+	}
 
 	/* Set the credentials of this side of the connection */
 	CHECK_GNUTLS_DO( gnutls_credentials_set (*session, GNUTLS_CRD_CERTIFICATE, alt_creds ?: fd_g_config->cnf_sec_data.credentials), return EINVAL );
@@ -1167,232 +1163,6 @@
 	return 0;
 }
 
-#ifndef GNUTLS_VERSION_300
-
-/* Verify remote credentials after successful handshake (return 0 if OK, EINVAL otherwise) */
-int fd_tls_verify_credentials(gnutls_session_t session, struct cnxctx * conn, int verbose)
-{
-	int i, ret = 0;
-	unsigned int gtret;
-	const gnutls_datum_t *cert_list;
-	unsigned int cert_list_size;
-	gnutls_x509_crt_t cert;
-	time_t now;
-	
-	TRACE_ENTRY("%p %d", conn, verbose);
-	CHECK_PARAMS(conn);
-	
-	/* Trace the session information -- http://www.gnu.org/software/gnutls/manual/gnutls.html#Obtaining-session-information */
-	#ifdef DEBUG
-	if (verbose) {
-		const char *tmp;
-		gnutls_kx_algorithm_t kx;
-  		gnutls_credentials_type_t cred;
-		
-		LOG_A("TLS Session information for connection '%s':", conn->cc_id);
-
-		/* print the key exchange's algorithm name */
-		GNUTLS_TRACE( kx = gnutls_kx_get (session) );
-		GNUTLS_TRACE( tmp = gnutls_kx_get_name (kx) );
-		LOG_A("\t - Key Exchange: %s", tmp);
-
-		/* Check the authentication type used and switch
-		* to the appropriate. */
-		GNUTLS_TRACE( cred = gnutls_auth_get_type (session) );
-		switch (cred)
-		{
-			case GNUTLS_CRD_IA:
-				LOG_A("\t - TLS/IA session");
-				break;
-
-			case GNUTLS_CRD_PSK:
-				/* This returns NULL in server side. */
-				if (gnutls_psk_client_get_hint (session) != NULL)
-					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)
-					LOG_A("\t - PSK authentication. Connected as '%s'",
-						gnutls_psk_server_get_username (session));
-				break;
-
-			case GNUTLS_CRD_ANON:	/* anonymous authentication */
-				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) {
-					LOG_A("\t - Ephemeral DH using prime of %d bits",
-						gnutls_dh_get_prime_bits (session));
-				}
-				break;
-#ifdef ENABLE_SRP				
-			case GNUTLS_CRD_SRP:
-				LOG_A("\t - SRP session with username %s",
-					gnutls_srp_server_get_username (session));
-				break;
-#endif /* ENABLE_SRP */
-
-			default:
-				fd_log_debug("\t - Different type of credentials for the session (%d).", cred);
-				break;
-
-		}
-
-		/* print the protocol's name (ie TLS 1.0) */
-		tmp = gnutls_protocol_get_name (gnutls_protocol_get_version (session));
-		LOG_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));
-		LOG_A("\t - Certificate Type: %s", tmp);
-
-		/* print the compression algorithm (if any) */
-		tmp = gnutls_compression_get_name (gnutls_compression_get (session));
-		LOG_A("\t - Compression: %s", tmp);
-
-		/* print the name of the cipher used. ie 3DES. */
-		tmp = gnutls_cipher_get_name (gnutls_cipher_get (session));
-		LOG_A("\t - Cipher: %s", tmp);
-
-		/* Print the MAC algorithms name. ie SHA1 */
-		tmp = gnutls_mac_get_name (gnutls_mac_get (session));
-		LOG_A("\t - MAC: %s", tmp);
-	}
-	#endif /* DEBUG */
-	
-	/* First, use built-in verification */
-	CHECK_GNUTLS_DO( gnutls_certificate_verify_peers2 (session, &gtret), return EINVAL );
-	if (gtret) {
-		if (TRACE_BOOL(INFO)) {
-			fd_log_debug("TLS: Remote certificate invalid on socket %d (Remote: '%s')(Connection: '%s') :", conn->cc_socket, conn->cc_remid, conn->cc_id);
-			if (gtret & GNUTLS_CERT_INVALID)
-				fd_log_debug(" - The certificate is not trusted (unknown CA? expired?)");
-			if (gtret & GNUTLS_CERT_REVOKED)
-				fd_log_debug(" - The certificate has been revoked.");
-			if (gtret & GNUTLS_CERT_SIGNER_NOT_FOUND)
-				fd_log_debug(" - The certificate hasn't got a known issuer.");
-			if (gtret & GNUTLS_CERT_SIGNER_NOT_CA)
-				fd_log_debug(" - The certificate signer is not a CA, or uses version 1, or 3 without basic constraints.");
-			if (gtret & GNUTLS_CERT_INSECURE_ALGORITHM)
-				fd_log_debug(" - The certificate signature uses a weak algorithm.");
-		}
-		return EINVAL;
-	}
-	
-	/* Code from http://www.gnu.org/software/gnutls/manual/gnutls.html#Verifying-peer_0027s-certificate */
-	if (gnutls_certificate_type_get (session) != GNUTLS_CRT_X509)
-		return EINVAL;
-	
-	GNUTLS_TRACE( cert_list = gnutls_certificate_get_peers (session, &cert_list_size) );
-	if (cert_list == NULL)
-		return EINVAL;
-	
-	now = time(NULL);
-	
-	#ifdef DEBUG
-		char serial[40];
-		char dn[128];
-		size_t size;
-		unsigned int algo, bits;
-		time_t expiration_time, activation_time;
-		
-		LOG_D("TLS Certificate information for connection '%s' (%d certs provided):", conn->cc_id, cert_list_size);
-		for (i = 0; i < cert_list_size; i++)
-		{
-
-			CHECK_GNUTLS_DO( gnutls_x509_crt_init (&cert), return EINVAL);
-			CHECK_GNUTLS_DO( gnutls_x509_crt_import (cert, &cert_list[i], GNUTLS_X509_FMT_DER), return EINVAL);
-		
-			LOG_A(" Certificate %d info:", i);
-
-			GNUTLS_TRACE( expiration_time = gnutls_x509_crt_get_expiration_time (cert) );
-			GNUTLS_TRACE( activation_time = gnutls_x509_crt_get_activation_time (cert) );
-
-			LOG( i ? FD_LOG_ANNOYING : FD_LOG_DEBUG, "\t - Certificate is valid since: %.24s", ctime (&activation_time));
-			LOG( i ? FD_LOG_ANNOYING : FD_LOG_DEBUG, "\t - Certificate expires: %.24s", ctime (&expiration_time));
-
-			/* Print the serial number of the certificate. */
-			size = sizeof (serial);
-			gnutls_x509_crt_get_serial (cert, serial, &size);
-			
-			{
-				int j;
-				char buf[1024];
-				snprintf(buf, sizeof(buf), "\t - Certificate serial number: ");
-				for (j = 0; j < size; j++) {
-					snprintf(buf+strlen(buf), sizeof(buf)-strlen(buf), "%02hhx", serial[j]);
-				}
-				LOG( i ? FD_LOG_ANNOYING : FD_LOG_DEBUG, "%s", buf);
-			}
-
-			/* Extract some of the public key algorithm's parameters */
-			GNUTLS_TRACE( algo = gnutls_x509_crt_get_pk_algorithm (cert, &bits) );
-			LOG( i ? FD_LOG_ANNOYING : FD_LOG_DEBUG, "\t - Certificate public key: %s",
-			      gnutls_pk_algorithm_get_name (algo));
-
-			/* Print the version of the X.509 certificate. */
-			LOG( i ? FD_LOG_ANNOYING : FD_LOG_DEBUG, "\t - Certificate version: #%d",
-			      gnutls_x509_crt_get_version (cert));
-
-			size = sizeof (dn);
-			GNUTLS_TRACE( gnutls_x509_crt_get_dn (cert, dn, &size) );
-			LOG( i ? FD_LOG_ANNOYING : FD_LOG_DEBUG, "\t - DN: %s", dn);
-
-			size = sizeof (dn);
-			GNUTLS_TRACE( gnutls_x509_crt_get_issuer_dn (cert, dn, &size) );
-			LOG( i ? FD_LOG_ANNOYING : FD_LOG_DEBUG, "\t - Issuer's DN: %s", dn);
-
-			GNUTLS_TRACE( gnutls_x509_crt_deinit (cert) );
-		}
-	#endif /* DEBUG */
-
-	/* Check validity of all the certificates */
-	for (i = 0; i < cert_list_size; i++)
-	{
-		time_t deadline;
-		
-		CHECK_GNUTLS_DO( gnutls_x509_crt_init (&cert), return EINVAL);
-		CHECK_GNUTLS_DO( gnutls_x509_crt_import (cert, &cert_list[i], GNUTLS_X509_FMT_DER), return EINVAL);
-		
-		GNUTLS_TRACE( deadline = gnutls_x509_crt_get_expiration_time(cert) );
-		if ((deadline != (time_t)-1) && (deadline < now)) {
-			if (TRACE_BOOL(INFO)) {
-				fd_log_debug("TLS: Remote certificate invalid on socket %d (Remote: '%s')(Connection: '%s') :", conn->cc_socket, conn->cc_remid, conn->cc_id);
-				fd_log_debug(" - The certificate %d in the chain is expired", i);
-			}
-			ret = EINVAL;
-		}
-		
-		GNUTLS_TRACE( deadline = gnutls_x509_crt_get_activation_time(cert) );
-		if ((deadline != (time_t)-1) && (deadline > now)) {
-			if (TRACE_BOOL(INFO)) {
-				fd_log_debug("TLS: Remote certificate invalid on socket %d (Remote: '%s')(Connection: '%s') :", conn->cc_socket, conn->cc_remid, conn->cc_id);
-				fd_log_debug(" - The certificate %d in the chain is not yet activated", i);
-			}
-			ret = EINVAL;
-		}
-		
-		if ((i == 0) && (conn->cc_tls_para.cn)) {
-			if (!gnutls_x509_crt_check_hostname (cert, conn->cc_tls_para.cn)) {
-				if (TRACE_BOOL(INFO)) {
-					fd_log_debug("TLS: Remote certificate invalid on socket %d (Remote: '%s')(Connection: '%s') :", conn->cc_socket, conn->cc_remid, conn->cc_id);
-					fd_log_debug(" - The certificate hostname does not match '%s'", conn->cc_tls_para.cn);
-				}
-				ret = EINVAL;
-			}
-		}
-		
-		GNUTLS_TRACE( gnutls_x509_crt_deinit (cert) );
-	}
-
-	return ret;
-}
-
-#else /* GNUTLS_VERSION_300 */
-
 /* Verify remote credentials DURING handshake (return gnutls status) */
 int fd_tls_verify_credentials_2(gnutls_session_t session)
 {
@@ -1626,8 +1396,6 @@
 	return 0;
 }
 
-#endif /* GNUTLS_VERSION_300 */
-
 static int fd_cnx_may_dtls(struct cnxctx * conn) {
 #ifndef DISABLE_SCTP
 	if ((conn->cc_proto == IPPROTO_SCTP) && (conn->cc_tls_para.algo == ALGO_HANDSHAKE_DEFAULT))
@@ -1675,30 +1443,19 @@
 		CHECK_FCT( fd_sctp3436_init(conn) );
 #endif /* DISABLE_SCTP */
 	} else {
-		/* Set the transport pointer passed to push & pull callbacks */
-		GNUTLS_TRACE( gnutls_transport_set_ptr( conn->cc_tls_para.session, (gnutls_transport_ptr_t) conn ) );
-
 		/* Set the push and pull callbacks */
 		if (!dtls) {
-			#ifdef GNUTLS_VERSION_300
+			/* Set the transport pointer passed to push & pull callbacks */
+			GNUTLS_TRACE( gnutls_transport_set_ptr( conn->cc_tls_para.session, (gnutls_transport_ptr_t) conn ) );
+
 			GNUTLS_TRACE( gnutls_transport_set_pull_timeout_function( conn->cc_tls_para.session, (void *)fd_cnx_s_select ) );
-			#endif /* GNUTLS_VERSION_300 */
 			GNUTLS_TRACE( gnutls_transport_set_pull_function(conn->cc_tls_para.session, (void *)fd_cnx_s_recv) );
-			#ifndef GNUTLS_VERSION_212
-			GNUTLS_TRACE( gnutls_transport_set_push_function(conn->cc_tls_para.session, (void *)fd_cnx_s_send) );
-			#else /* GNUTLS_VERSION_212 */
 			GNUTLS_TRACE( gnutls_transport_set_vec_push_function(conn->cc_tls_para.session, (void *)fd_cnx_s_sendv) );
-			#endif /* GNUTLS_VERSION_212 */
 		} else {
-			TODO("DTLS push/pull functions");
-			return ENOTSUP;
+			CHECK_FCT( fd_sctp_dtls_settransport(conn->cc_tls_para.session, conn) );
 		}
 	}
 	
-	/* additional initialization for gnutls 3.x */
-	#ifdef GNUTLS_VERSION_300
-		/* the verify function has already been set in the global initialization in config.c */
-	
 	/* fd_tls_verify_credentials_2 uses the connection */
 	gnutls_session_set_ptr (conn->cc_tls_para.session, (void *) conn);
 	
@@ -1707,8 +1464,6 @@
 		CHECK_GNUTLS_DO( gnutls_server_name_set (conn->cc_tls_para.session, GNUTLS_NAME_DNS, conn->cc_tls_para.cn, strlen(conn->cc_tls_para.cn)), /* ignore failure */);
 	}
 	
-	#endif /* GNUTLS_VERSION_300 */
-
 	#ifdef GNUTLS_VERSION_310
 	GNUTLS_TRACE( gnutls_handshake_set_timeout( conn->cc_tls_para.session, GNUTLS_DEFAULT_HANDSHAKE_TIMEOUT));
 	#endif /* GNUTLS_VERSION_310 */
@@ -1729,15 +1484,6 @@
 				return EINVAL;
 			} );
 
-		#ifndef GNUTLS_VERSION_300
-		/* Now verify the remote credentials are valid -- only simple tests here */
-		CHECK_FCT_DO( fd_tls_verify_credentials(conn->cc_tls_para.session, conn, 1), 
-			{  
-				CHECK_GNUTLS_DO( gnutls_bye(conn->cc_tls_para.session, GNUTLS_SHUT_RDWR),  );
-				fd_cnx_markerror(conn);
-				return EINVAL;
-			});
-		#endif /* GNUTLS_VERSION_300 */
 	}
 	
 	/* Multi-stream TLS: handshake other streams as well */
@@ -1757,9 +1503,7 @@
 		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;
+			CHECK_POSIX( pthread_create( &conn->cc_rcvthr, NULL, fd_sctp_dtls_rcvthr, conn ) );
 		}
 	}
 	
@@ -1938,8 +1682,7 @@
 				}
 			} else {
 				/* DTLS */
-				/* Multistream is handled at lower layer in the push/pull function */
-				CHECK_FCT( send_simple(conn, buf, len) );
+				CHECK_FCT( fd_sctp_dtls_send(conn, buf, len) );
 			}
 		}
 		break;
--- a/libfdcore/cnxctx.h	Wed Jun 19 10:20:47 2013 +0800
+++ b/libfdcore/cnxctx.h	Thu Jun 20 10:20:29 2013 +0800
@@ -76,11 +76,20 @@
 		uint16_t str_out;	/* Out streams */
 		uint16_t str_in;	/* In streams */
 		uint16_t pairs;		/* max number of pairs ( = min(in, out)) */
-		uint16_t next;		/* # of stream the next message will be sent to */
+		uint16_t next;		/* # of the stream the next message will be sent to */
 		int	 unordered;	/* boolean telling if use of streams > 0 is permitted */
 	} 		cc_sctp_para;
+	
+	/* For DTLS over SCTP */
+	struct {
+		/* This structure will be changed if we implement a different algorithm to reassemble the messages */
+		uint8_t		nextseq[8]; /* the next sequence number we expect to be delivered to upper layer. Can be overwriten if a new epoch is received. */
+		uint8_t		validseq[8]; /* last seq number that was actually decrypted, so we can trust this value. */
+		struct fd_list	chunks;	/* store the chunks with greater seq numbers received on SCTP socket for delayed delivery. */
+		size_t		offset;	/* how much data of the current chunk has already been passed to gnutls */
+	}		cc_sctp_dtls_data;
 
-	/* If both conditions */
+	/* For TLS over SCTP */
 	struct {
 		struct sctp3436_ctx *array; /* an array of cc_sctp_para.pairs elements -- the #0 is special (session is outside)*/
 		struct sr_store	 *sess_store; /* Session data of the master session, to resume the children sessions */
@@ -100,11 +109,9 @@
 void fd_cnx_s_setto(int sock);
 
 /* TLS */
-int fd_tls_rcvthr_core(struct cnxctx * conn, gnutls_session_t session);
+int fd_tls_rcvthr_core(struct cnxctx * conn, gnutls_session_t session, int dtls);
 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 */
+ssize_t fd_tls_send_handle_error(struct cnxctx * conn, gnutls_session_t session, void * data, size_t sz);
 
 /* TCP */
 int fd_tcp_create_bind_server( int * sock, sSA * sa, socklen_t salen );
@@ -124,6 +131,13 @@
 ssize_t fd_sctp_sendstrv(struct cnxctx * conn, uint16_t strid, const struct iovec *iov, int iovcnt);
 int fd_sctp_recvmeta(struct cnxctx * conn, uint16_t * strid, uint8_t ** buf, size_t * len, int *event);
 
+/* DTLS over SCTP */
+int fd_sctp_dtls_prepare(gnutls_session_t session);
+int fd_sctp_dtls_settransport(gnutls_session_t session, struct cnxctx * conn);
+int fd_sctp_dtls_send(struct cnxctx * conn, unsigned char * buf, size_t len);
+ssize_t fd_dtls_recv_handle_error(struct cnxctx * conn, gnutls_session_t session, void * data, size_t sz);
+void * fd_sctp_dtls_rcvthr(void * arg);
+
 /* TLS over SCTP (multi-stream) */
 struct sctp3436_ctx {
 	struct cnxctx 	*parent; 	/* for info such as socket, conn name, event list */
--- a/libfdcore/config.c	Wed Jun 19 10:20:47 2013 +0800
+++ b/libfdcore/config.c	Thu Jun 20 10:20:29 2013 +0800
@@ -74,9 +74,7 @@
 	/* TLS parameters */
 	CHECK_GNUTLS_DO( gnutls_certificate_allocate_credentials (&fd_g_config->cnf_sec_data.credentials), return ENOMEM );
 	CHECK_GNUTLS_DO( gnutls_dh_params_init (&fd_g_config->cnf_sec_data.dh_cache), return ENOMEM );
-#ifdef GNUTLS_VERSION_300
 	CHECK_GNUTLS_DO( gnutls_x509_trust_list_init(&fd_g_config->cnf_sec_data.trustlist, 0), return ENOMEM );
-#endif /* GNUTLS_VERSION_300 */
 
 	return 0;
 }
@@ -186,7 +184,6 @@
 	return 0;
 }
 
-#ifdef GNUTLS_VERSION_300
 /* inspired from GnuTLS manual */
 static int fd_conf_print_details_func (gnutls_x509_crt_t cert,
                     gnutls_x509_crt_t issuer, gnutls_x509_crl_t crl,
@@ -229,11 +226,7 @@
 
   return 0;
 }
-#endif /* GNUTLS_VERSION_300 */
 
-#ifndef GNUTLS_VERSION_300
-GCC_DIAG_OFF("-Wdeprecated-declarations")
-#endif /* !GNUTLS_VERSION_300 */
 /* Parse the configuration file (using the yacc parser) */
 int fd_conf_parse()
 {
@@ -391,11 +384,7 @@
 		
 		CHECK_MALLOC( certs = calloc(cert_max, sizeof(gnutls_x509_crt_t)) );
 		CHECK_GNUTLS_DO( gnutls_x509_crt_list_import(certs, &cert_max, &certfile, GNUTLS_X509_FMT_PEM, 
-				#ifdef GNUTLS_VERSION_300
 				GNUTLS_X509_CRT_LIST_FAIL_IF_UNSORTED
-				#else /* GNUTLS_VERSION_300 */
-				0
-				#endif /* GNUTLS_VERSION_300 */
 				),
 			{
 				TRACE_ERROR("Failed to import the data from file '%s'", fd_g_config->cnf_sec_data.cert_file);
@@ -408,9 +397,7 @@
 		
 		/* Now, verify the list against the local CA and CRL */
 		
-		#ifdef GNUTLS_VERSION_300
-		
-			/* We use the trust list for this purpose */
+		/* We use the trust list for this purpose */
 		{
 			unsigned int output;
 			
@@ -459,72 +446,6 @@
 			
 		}
 		
-
-		#else /* GNUTLS_VERSION_300 */ 
-		
-			/* GnuTLS 2.x way of checking certificates */
-		{
-			gnutls_x509_crt_t * CA_list;
-			int CA_list_length;
-
-			gnutls_x509_crl_t * CRL_list;
-			int CRL_list_length;
-			
-			unsigned int verify;
-			time_t now;
-			GNUTLS_TRACE( gnutls_certificate_get_x509_cas (fd_g_config->cnf_sec_data.credentials, &CA_list, (unsigned int *) &CA_list_length) );
-			GNUTLS_TRACE( gnutls_certificate_get_x509_crls (fd_g_config->cnf_sec_data.credentials, &CRL_list, (unsigned int *) &CRL_list_length) );
-			CHECK_GNUTLS_DO( gnutls_x509_crt_list_verify(certs, cert_max, CA_list, CA_list_length, CRL_list, CRL_list_length, 0, &verify),
-				{
-					TRACE_ERROR("Failed to verify the local certificate '%s' against local credentials. Please check your certificate is valid.", fd_g_config->cnf_sec_data.cert_file);
-					return EINVAL;
-				} );
-				
-			if (verify) {
-				fd_log_debug("TLS: Local certificate chain '%s' is invalid :", fd_g_config->cnf_sec_data.cert_file);
-				if (verify & GNUTLS_CERT_INVALID)
-					TRACE_ERROR(" - The certificate is not trusted (unknown CA? expired?)");
-				if (verify & GNUTLS_CERT_REVOKED)
-					TRACE_ERROR(" - The certificate has been revoked.");
-				if (verify & GNUTLS_CERT_SIGNER_NOT_FOUND)
-					TRACE_ERROR(" - The certificate hasn't got a known issuer.");
-				if (verify & GNUTLS_CERT_SIGNER_NOT_CA)
-					TRACE_ERROR(" - The certificate signer is not a CA, or uses version 1, or 3 without basic constraints.");
-				if (verify & GNUTLS_CERT_INSECURE_ALGORITHM)
-					TRACE_ERROR(" - The certificate signature uses a weak algorithm.");
-				return EINVAL;
-			}
-
-			/* Check the local Identity is valid with the certificate */
-			if (!gnutls_x509_crt_check_hostname (certs[0], fd_g_config->cnf_diamid)) {
-				TRACE_ERROR("TLS: Local certificate '%s' is invalid :", fd_g_config->cnf_sec_data.cert_file);
-				TRACE_ERROR(" - The certificate hostname does not match '%s'", fd_g_config->cnf_diamid);
-				return EINVAL;
-			}
-
-			/* Check validity of all the certificates in the chain */
-			now = time(NULL);
-			for (i = 0; i < cert_max; i++)
-			{
-				time_t deadline;
-
-				GNUTLS_TRACE( deadline = gnutls_x509_crt_get_expiration_time(certs[i]) );
-				if ((deadline != (time_t)-1) && (deadline < now)) {
-					TRACE_ERROR("TLS: Local certificate chain '%s' is invalid :", fd_g_config->cnf_sec_data.cert_file);
-					TRACE_ERROR(" - The certificate %d in the chain is expired", i);
-					return EINVAL;
-				}
-
-				GNUTLS_TRACE( deadline = gnutls_x509_crt_get_activation_time(certs[i]) );
-				if ((deadline != (time_t)-1) && (deadline > now)) {
-					TRACE_ERROR("TLS: Local certificate chain '%s' is invalid :", fd_g_config->cnf_sec_data.cert_file);
-					TRACE_ERROR(" - The certificate %d in the chain is not yet activated", i);
-					return EINVAL;
-				}
-			}
-		}
-		#endif /* GNUTLS_VERSION_300 */ 
-		
 		/* Everything checked OK, free the certificate list */
 		for (i = 0; i < cert_max; i++)
 		{
@@ -532,10 +453,8 @@
 		}
 		free(certs);
 	
-		#ifdef GNUTLS_VERSION_300
 		/* Use certificate verification during the handshake */
 		gnutls_certificate_set_verify_function (fd_g_config->cnf_sec_data.credentials, fd_tls_verify_credentials_2);
-		#endif /* GNUTLS_VERSION_300 */
 
 	}
 	
@@ -596,9 +515,6 @@
 	
 	return 0;
 }
-#ifndef GNUTLS_VERSION_300
-GCC_DIAG_ON("-Wdeprecated-declarations")
-#endif /* !GNUTLS_VERSION_300 */
 
 
 /* Destroy contents of fd_g_config structure */
@@ -610,9 +526,7 @@
 		return 0;
 	
 	/* Free the TLS parameters */
-#ifdef GNUTLS_VERSION_300
 	gnutls_x509_trust_list_deinit(fd_g_config->cnf_sec_data.trustlist, 1);
-#endif /* GNUTLS_VERSION_300 */
 	gnutls_priority_deinit(fd_g_config->cnf_sec_data.prio_cache);
 	gnutls_dh_params_deinit(fd_g_config->cnf_sec_data.dh_cache);
 	gnutls_certificate_free_credentials(fd_g_config->cnf_sec_data.credentials);
--- a/libfdcore/core.c	Wed Jun 19 10:20:47 2013 +0800
+++ b/libfdcore/core.c	Thu Jun 20 10:20:29 2013 +0800
@@ -43,11 +43,6 @@
 static struct fd_config g_conf;
 struct fd_config * fd_g_config = NULL;
 
-/* gcrypt functions to support posix threads */
-#ifndef GNUTLS_VERSION_210
-GCRY_THREAD_OPTION_PTHREAD_IMPL;
-#endif /* GNUTLS_VERSION_210 */
-
 /* Thread that process incoming events on the main queue -- and terminates the framework when requested */
 static pthread_t core_runner = (pthread_t)NULL;
 
@@ -188,20 +183,12 @@
 	LOG_N("libfdproto '%s' initialized.", fd_libproto_version);
 	
 	/* Initialize gcrypt and gnutls */
-	#ifndef GNUTLS_VERSION_210
-	GNUTLS_TRACE( (void) gcry_control (GCRYCTL_SET_THREAD_CBS, &gcry_threads_pthread) );
-	GNUTLS_TRACE( (void) gcry_control (GCRYCTL_ENABLE_QUICK_RANDOM, 0) );
-	#endif /* GNUTLS_VERSION_210 */
 	CHECK_GNUTLS_DO( gnutls_global_init(), return EINVAL );
 	if ( ! gnutls_check_version(GNUTLS_VERSION) ) {
 		TRACE_ERROR( "The GNUTLS library is too old; found '%s', need '" GNUTLS_VERSION "'", gnutls_check_version(NULL));
 		return EINVAL;
 	} else {
-	#ifdef GNUTLS_VERSION_210
 		TRACE_DEBUG(INFO, "libgnutls '%s' initialized.", gnutls_check_version(NULL) );
-	#else /* GNUTLS_VERSION_210 */
-		TRACE_DEBUG(INFO, "libgnutls '%s', libgcrypt '%s', initialized.", gnutls_check_version(NULL), gcry_check_version(NULL) );
-	#endif /* GNUTLS_VERSION_210 */
 	}
 	
 	/* Initialize the config with default values */
--- a/libfdcore/fdcore-internal.h	Wed Jun 19 10:20:47 2013 +0800
+++ b/libfdcore/fdcore-internal.h	Thu Jun 20 10:20:29 2013 +0800
@@ -359,9 +359,7 @@
 int             fd_cnx_recv_setaltfifo(struct cnxctx * conn, struct fifo * alt_fifo); /* send FDEVP_CNX_MSG_RECV event to the fifo list */
 int             fd_cnx_send(struct cnxctx * conn, unsigned char * buf, size_t len);
 void            fd_cnx_destroy(struct cnxctx * conn);
-#ifdef GNUTLS_VERSION_300
 int             fd_tls_verify_credentials_2(gnutls_session_t session);
-#endif /* GNUTLS_VERSION_300 */
 
 /* Internal calls of the hook mechanism */
 void   fd_hook_call(enum fd_hook_type type, struct msg * msg, struct fd_peer * peer, void * other, struct fd_msg_pmdl * pmdl);
--- a/libfdcore/fdd.l	Wed Jun 19 10:20:47 2013 +0800
+++ b/libfdcore/fdd.l	Thu Jun 20 10:20:29 2013 +0800
@@ -245,7 +245,7 @@
 (?i:"Realm")		{ return REALM;   	}
 (?i:"Port")		{ return PORT;    	}
 (?i:"SecPort")		{ return SECPORT;  	}
-	/* (?i:"SctpSec3436")	{ return SEC3436;  	} */
+(?i:"SctpSec3436")	{ return SEC3436;  	}
 (?i:"No_IPv6")		{ return NOIP6;		}
 (?i:"No_IP")		{ return NOIP;		}
 (?i:"No_TCP")		{ return NOTCP;		}
--- a/libfdcore/fdd.y	Wed Jun 19 10:20:47 2013 +0800
+++ b/libfdcore/fdd.y	Thu Jun 20 10:20:29 2013 +0800
@@ -562,7 +562,7 @@
 					yyerror (&yylloc, conf, "Error on file name"); 
 					YYERROR;
 				}
-				#ifdef GNUTLS_VERSION_300
+
 				{
 					/* We import these CA in the trust list */
 					gnutls_x509_crt_t * calist;
@@ -580,7 +580,7 @@
 					CHECK_GNUTLS_DO( gnutls_x509_trust_list_add_cas (fd_g_config->cnf_sec_data.trustlist, calist, cacount, 0),
 							{ yyerror (&yylloc, conf, "Error saving CA in trust list."); YYERROR; } );
 				}
-				#endif /* GNUTLS_VERSION_300 */
+
 				fclose(fd);
 				conf->cnf_sec_data.ca_file = $3;
 				CHECK_GNUTLS_DO( conf->cnf_sec_data.ca_file_nr += gnutls_certificate_set_x509_trust_file( 
@@ -602,7 +602,7 @@
 					yyerror (&yylloc, conf, "Error on file name"); 
 					YYERROR;
 				}
-				#ifdef GNUTLS_VERSION_300
+				
 				{
 					/* We import these CRL in the trust list */
 					gnutls_x509_crl_t * crllist;
@@ -621,7 +621,7 @@
 									0),
 							{ yyerror (&yylloc, conf, "Error importing CRL in trust list."); YYERROR; } );
 				}
-				#endif /* GNUTLS_VERSION_300 */
+				
 				fclose(fd);
 				conf->cnf_sec_data.crl_file = $3;
 				CHECK_GNUTLS_DO( gnutls_certificate_set_x509_crl_file( 
--- a/libfdcore/p_cnx.c	Wed Jun 19 10:20:47 2013 +0800
+++ b/libfdcore/p_cnx.c	Thu Jun 20 10:20:29 2013 +0800
@@ -291,7 +291,7 @@
 	/* Handshake if needed (secure port) */
 	if (nc->dotls) {
 		CHECK_FCT_DO( fd_cnx_handshake(cnx, GNUTLS_CLIENT, 
-						ALGO_HANDSHAKE_3436,
+						(peer->p_hdr.info.config.pic_flags.sctpsec == PI_SCTPSEC_3436) ? ALGO_HANDSHAKE_3436 : ALGO_HANDSHAKE_DEFAULT,
 						peer->p_hdr.info.config.pic_priority, NULL),
 			{
 				/* Handshake failed ...  */
--- a/libfdcore/sctp3436.c	Wed Jun 19 10:20:47 2013 +0800
+++ b/libfdcore/sctp3436.c	Thu Jun 20 10:20:29 2013 +0800
@@ -152,7 +152,7 @@
 	}
 	
 	/* The next function loops while there is no error */
-	CHECK_FCT_DO(fd_tls_rcvthr_core(cnx, ctx->strid ? ctx->session : cnx->cc_tls_para.session), /* continue */);
+	CHECK_FCT_DO(fd_tls_rcvthr_core(cnx, ctx->strid ? ctx->session : cnx->cc_tls_para.session, 0), /* continue */);
 error:
 	fd_cnx_markerror(cnx);
 	TRACE_DEBUG(FULL, "Thread terminated");	
@@ -163,7 +163,6 @@
 /*                     push / pull                           */
 /*************************************************************/
 
-#ifdef GNUTLS_VERSION_300
 /* Check if data is available for gnutls on a given context */
 static int sctp3436_pull_timeout(gnutls_transport_ptr_t tr, unsigned int ms)
 {
@@ -192,24 +191,8 @@
 		
 	return ret;
 }
-#endif /* GNUTLS_VERSION_300 */
 
 /* Send data over the connection, called by gnutls */
-#ifndef GNUTLS_VERSION_212
-static ssize_t sctp3436_push(gnutls_transport_ptr_t tr, const void * data, size_t len)
-{
-	struct sctp3436_ctx * ctx = (struct sctp3436_ctx *) tr;
-	struct iovec iov;
-	
-	TRACE_ENTRY("%p %p %zd", tr, data, len);
-	CHECK_PARAMS_DO( tr && data, { errno = EINVAL; return -1; } );
-	
-	iov.iov_base = (void *)data;
-	iov.iov_len  = len;
-	
-	return fd_sctp_sendstrv(ctx->parent, ctx->strid, &iov, 1);
-}
-#else /*  GNUTLS_VERSION_212 */
 static ssize_t sctp3436_pushv(gnutls_transport_ptr_t tr, const giovec_t * iov, int iovcnt)
 {
 	struct sctp3436_ctx * ctx = (struct sctp3436_ctx *) tr;
@@ -219,7 +202,6 @@
 	
 	return fd_sctp_sendstrv(ctx->parent, ctx->strid, (const struct iovec *)iov, iovcnt);
 }
-#endif /*  GNUTLS_VERSION_212 */
 
 /* Retrieve data received on a stream and already demultiplexed */
 static ssize_t sctp3436_pull(gnutls_transport_ptr_t tr, void * buf, size_t len)
@@ -270,36 +252,18 @@
 }
 
 /* Set the parameters of a session to use the appropriate fifo and stream information */
-#ifndef GNUTLS_VERSION_300
-GCC_DIAG_OFF("-Wdeprecated-declarations")
-#endif /* !GNUTLS_VERSION_300 */
 static void set_sess_transport(gnutls_session_t session, struct sctp3436_ctx *ctx)
 {
 	/* Set the transport pointer passed to push & pull callbacks */
 	GNUTLS_TRACE( gnutls_transport_set_ptr( session, (gnutls_transport_ptr_t) ctx ) );
 	
-	/* Reset the low water value, since we don't use sockets */
-#ifndef GNUTLS_VERSION_300
-	/* starting version 2.12, this call is not needed */
-	GNUTLS_TRACE( gnutls_transport_set_lowat( session, 0 ) );
-#else  /* GNUTLS_VERSION_300 */
-	/* but in 3.0 we have to provide the pull_timeout callback */
+	/* Set the push and pull callbacks */
 	GNUTLS_TRACE( gnutls_transport_set_pull_timeout_function( session, sctp3436_pull_timeout ) );
-#endif /* GNUTLS_VERSION_300 */
-	
-	/* Set the push and pull callbacks */
 	GNUTLS_TRACE( gnutls_transport_set_pull_function(session, sctp3436_pull) );
-#ifndef GNUTLS_VERSION_212
-	GNUTLS_TRACE( gnutls_transport_set_push_function(session, sctp3436_push) );
-#else /* GNUTLS_VERSION_212 */
 	GNUTLS_TRACE( gnutls_transport_set_vec_push_function(session, sctp3436_pushv) );
-#endif /* GNUTLS_VERSION_212 */
 
 	return;
 }
-#ifndef GNUTLS_VERSION_300
-GCC_DIAG_ON("-Wdeprecated-declarations")
-#endif /* !GNUTLS_VERSION_300 */
 
 /*************************************************************/
 /*               Session resuming support                    */
@@ -531,12 +495,6 @@
 	CHECK_GNUTLS_DO( gnutls_handshake( ctx->session ), return NULL);
 			
 	GNUTLS_TRACE( resumed = gnutls_session_is_resumed(ctx->session) );
-	#ifndef GNUTLS_VERSION_300
-	if (!resumed) {
-		/* Check the credentials here also */
-		CHECK_FCT_DO( fd_tls_verify_credentials(ctx->session, ctx->parent, 0), return NULL );
-	}
-	#endif /* GNUTLS_VERSION_300 */
 	if (TRACE_BOOL(FULL)) {
 		if (resumed) {
 			fd_log_debug("Session was resumed successfully on stream %hu (conn: '%s')", ctx->strid, fd_cnx_getid(ctx->parent));
@@ -619,10 +577,6 @@
 		/* Set credentials and priority */
 		CHECK_FCT( fd_tls_prepare(&conn->cc_sctp3436_data.array[i].session, conn->cc_tls_para.mode, 0, priority, alt_creds) );
 		
-		/* additional initialization for gnutls 3.x */
-		#ifdef GNUTLS_VERSION_300
-			/* the verify function has already been set in the global initialization in config.c */
-
 		/* fd_tls_verify_credentials_2 uses the connection */
 		gnutls_session_set_ptr (conn->cc_sctp3436_data.array[i].session, (void *) conn);
 
@@ -631,8 +585,6 @@
 			CHECK_GNUTLS_DO( gnutls_server_name_set (conn->cc_sctp3436_data.array[i].session, GNUTLS_NAME_DNS, conn->cc_tls_para.cn, strlen(conn->cc_tls_para.cn)), /* ignore failure */);
 		}
 
-		#endif /* GNUTLS_VERSION_300 */
-
 		#ifdef GNUTLS_VERSION_310
 		GNUTLS_TRACE( gnutls_handshake_set_timeout( conn->cc_sctp3436_data.array[i].session, GNUTLS_DEFAULT_HANDSHAKE_TIMEOUT));
 		#endif /* GNUTLS_VERSION_310 */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libfdcore/sctp_dtls.c	Thu Jun 20 10:20:29 2013 +0800
@@ -0,0 +1,712 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@freediameter.net>							 *
+*													 *
+* Copyright (c) 2013, WIDE Project and NICT								 *
+* All rights reserved.											 *
+* 													 *
+* Redistribution and use of this software in source and binary forms, with or without modification, are  *
+* permitted provided that the following conditions are met:						 *
+* 													 *
+* * Redistributions of source code must retain the above 						 *
+*   copyright notice, this list of conditions and the 							 *
+*   following disclaimer.										 *
+*    													 *
+* * Redistributions in binary form must reproduce the above 						 *
+*   copyright notice, this list of conditions and the 							 *
+*   following disclaimer in the documentation and/or other						 *
+*   materials provided with the distribution.								 *
+* 													 *
+* * Neither the name of the WIDE Project or NICT nor the 						 *
+*   names of its contributors may be used to endorse or 						 *
+*   promote products derived from this software without 						 *
+*   specific prior written permission of WIDE Project and 						 *
+*   NICT.												 *
+* 													 *
+* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED *
+* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
+* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR *
+* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 	 *
+* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 	 *
+* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR *
+* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF   *
+* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.								 *
+*********************************************************************************************************/
+
+/* This file contains the code for DTLS over multi-stream SCTP implementation */
+
+#include "fdcore-internal.h"
+#include "cnxctx.h"
+
+
+/* In DTLS over SCTP, all the DTLS internal messages (handshake, etc) must be sent over stream 0 so that we are sure they are received in order.
+ Since we need to distinguish different DTLS payloads, we need some knowledge of DTLS protocol here. 
+ We will then chose the stream within our "push" function called by GNUTLS.
+ */
+#define DTLS_TYPE_OFFSET		0	/* The TYPE byte is the first in a DTLS packet */
+#define DTLS_TYPE_application_data	23	/* This is the value when the DTLS packet contains DATA (i.e. Diameter payload in our case) */
+#define DTLS_SEQ_OFFSET			3	/* The SEQUENCE bytes come after type and proto version */
+
+
+#define DTLS_SCTP_MTU	1<<14 /* as per RFC 6083 */
+
+
+/* The DTLS MTU is limited to 2^14, but Diameter messages can be larger. It means we MUST handle Diameter messages reassembly here; and this is not simple.
+
+There are two ways to deal with this problem: 
+
+- first solution is to force ordering when parsing all the datagrams received (as SCTP guarantees we will receive them), 
+     so we are guaranteed to reconstruct the stream of data in the same order as it was sent, and we can process the received data the same way as TCP.
+  * pros: very robust, does not depend on how the remote side is sending the data (assuming they do not interleave chunks of diameter messages, we'd have no solution otherwise)
+  * cons: less efficient than the next solution, as on the receiving side we cannot parse new payloads until all the previous ones are received. 
+          It defeats some of the benefits of the partial ordering of SCTP.
+	  
+- second solution is to make sure the fragmented payloads are sent over the same stream (which are always ordered) and rebuild the messages per stream. 
+  * pros: enables to process complete messages received on other streams while waiting for some chunks (similar to non-DTLS situation, except that in that case SCTP handles the fragmentation)
+  * cons: we must be sure the sending side is actually sending pieces of a message on the same stream. And the processing on receiving side is more complex.
+  
+We'd have actually more solutions, for example storing the message hop-by-hop id in the snd_ppid field of SCTP header, but this would work only in front of freeDiameter.
+
+Here is an illustration of the two solutions: 
+we assume 3 streams S1,S2,S3 and 4 messages, message M1 of 2^14 + 2^13 (=24576) bytes and 3 messages M2,M3,M4 of 2^12 (=4096) bytes to send from peer A to peer B.
+
+Peer A calls fd_cnx_send() 4 times with the 4 messages M1,M2,M3,M4, which in turn calls gnutls_record_send(), which generates the chunks C1...C5 below:
+	C1: gnutls_record_send(M1) -> returns 2^14 since the complete record exceed the MTU.
+	C2: gnutls_record_send(M1+2^14) -> returns the remaining 2^13
+	C3: gnutls_record_send(M2)
+	C4: gnutls_record_send(M3)
+	C5: gnutls_record_send(M4)
+
+*** Solution 1)
+
+Implementing the first solution above, the chunks are sent as follows (assuming round-robin sending over the streams): 
+C1 over S1, 
+C2 over S2, 
+C3 over S3, 
+C4 over S1, 
+C5 over S2.
+
+Given the size of the chunks, they might be delivered in the following order on the receiving side:
+C3
+C2
+C5
+C1
+C4
+
+This means we have to store C3, C2 and C5 until C1 is received, then we can process C1,C2,C3, 
+and again wait for C4 before processing C4 and C5, while C3, C4 and C5 are totally independent 
+and could be processed directly after being received.
+
+
+*** Solution 2)
+
+Here the partial ordering is enforced, so the sending side MUST send C1 and C2 over the same stream, e.g.:
+C1 over S1, 
+C2 over S1, 
+C3 over S2, 
+C4 over S3, 
+C5 over S1.
+
+On the receiving side, given the sizes of the message, we might receive the chunks in the following order:
+C3
+C4
+C1
+C2
+C5
+
+We can process C3 and C4 as soon as they are received, then C1 is stored (when decrypted we can see it is a partial chunk)
+until the remaining payload is received; however we can continue to process the data received over other streams without delay.
+
+
+*** What we do here.
+
+freeDiameter implements the Solution 2 on the sending side (no additional cost), via fd_sctp_dtls_send() below.
+
+On the receiving side, we implement Solution 1 at the moment (safe).  We do it at the lowest layer, before passing the data to GNUTLS. 
+This way, we can catch all sequence numbers easily. 
+Note however we have no way to handle cleanly the change of ephoch in case of cipher change (this is unclear in RFC6083 as well)
+
+We'll see later if it makes sense to implement solution 2. 
+How to decide if we can use it? one way could be to start doing solution 1, 
+and when a large record is received check if the chunks were received on the same stream or not.
+
+Implementation of solution 2 is difficult because we need to pass the stream information through GNU TLS and there is no easy way to do it.
+
+*/
+
+/* Retrieve the next data from the socket. Returns 0 if no payload data is available, >0 otherwise, and <0 in case of error */
+static int get_next_data_from_socket(struct cnxctx * conn, uint16_t *strid, uint8_t ** buf, size_t *len)
+{
+	int got_data = 0;
+	int event;
+	CHECK_FCT_DO( fd_sctp_recvmeta(conn, strid, buf, len, &event), return -1 );
+	switch (event) {
+		case FDEVP_CNX_MSG_RECV:
+			got_data = 1;
+			LOG_A("Received DTLS data, len %zd, type %hhd, Seq %02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx, Stream %hu",
+				*len, (*buf)[0],
+				(*buf)[3],(*buf)[4],(*buf)[5],(*buf)[6], (*buf)[7],(*buf)[8],(*buf)[9],(*buf)[10],
+				*strid);
+			break;
+
+		case FDEVP_CNX_EP_CHANGE:
+			/* Send this event to the target queue */
+			CHECK_FCT_DO( fd_event_send( fd_cnx_target_queue(conn), event, *len, *buf), return -1 );
+			break;
+
+		case FDEVP_CNX_SHUTDOWN:
+			/* Just ignore the notification for now, we will get another error later anyway */
+			break;
+
+		case FDEVP_CNX_ERROR:
+		default:
+			return -1;
+	}
+	
+	return got_data;
+}
+
+/* Count the number of records received in a chunk (including partial) and increment the nextseq field accordingly */
+static void update_nextseq_from_records(struct cnxctx * conn, uint8_t * buf, size_t len)
+{
+	size_t offset = 0;
+	uint16_t next_record_len;
+	int i;
+	
+	
+	while (offset + 13 <= len) {
+		next_record_len = (buf[offset+11] << 8) + buf[offset+12];
+		LOG_A("update_nextseq_from_records off:%zd Type %hhd, Ver:%02hhx.%02hhx, Len:%d, Seq:%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx",
+			offset,
+			buf[offset], buf[offset+1], buf[offset+2], (((int)buf[offset+11])<<8)+((int)buf[offset+12]),
+			buf[offset+3],buf[offset+4],buf[offset+5],buf[offset+6],buf[offset+7],buf[offset+8],buf[offset+9],buf[offset+10]
+			);
+		
+		if (memcmp(buf + offset + DTLS_SEQ_OFFSET, conn->cc_sctp_dtls_data.nextseq, 8) != 0) {
+			/* The next record is not the one we expect in sequence. Is it a new epoch ? */
+			uint8_t newepoch[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
+			if (conn->cc_sctp_dtls_data.nextseq[1] != 0xFF) {
+				newepoch[0] = conn->cc_sctp_dtls_data.nextseq[0]; newepoch[1] = conn->cc_sctp_dtls_data.nextseq[1] + 1;
+			} else if (conn->cc_sctp_dtls_data.nextseq[0] != 0xFF) {
+				newepoch[0] =conn->cc_sctp_dtls_data.nextseq[0] + 1; newepoch[1] = 0;
+			} else {
+				LOG_F("Epoch field wrapped, can this happen ???");
+				ASSERT(0); TODO("FFS");
+			}
+			
+			if (memcmp(buf + offset + DTLS_SEQ_OFFSET, newepoch, 8) == 0) {
+				/* Yes, this is a new epoch record, store this as next seq and continue */
+				memcpy(conn->cc_sctp_dtls_data.nextseq, newepoch, 8);
+			} else {
+				LOG_E("buf seq: %02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx", buf[offset +3], buf[offset +4], buf[offset +5], buf[offset +6], buf[offset +7], buf[offset +8], buf[offset +9], buf[offset +10]);
+				LOG_E("nextseq: %02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx", conn->cc_sctp_dtls_data.nextseq[0], conn->cc_sctp_dtls_data.nextseq[1], conn->cc_sctp_dtls_data.nextseq[2], conn->cc_sctp_dtls_data.nextseq[3], conn->cc_sctp_dtls_data.nextseq[4], conn->cc_sctp_dtls_data.nextseq[5], conn->cc_sctp_dtls_data.nextseq[6], conn->cc_sctp_dtls_data.nextseq[7]);
+				TODO("This should not be happening...");
+				ASSERT(0);
+			}
+		}
+		
+		/* increment seq number */
+		for (i = 7; i>=3; i--) {
+			if (conn->cc_sctp_dtls_data.nextseq[i] == 0xFF) {
+				conn->cc_sctp_dtls_data.nextseq[i] = 0;
+			} else {
+				conn->cc_sctp_dtls_data.nextseq[i] ++;
+				break;
+			}
+		}
+		if (i==2) {
+			LOG_F("Sequence_number field wrapped, can this happen ???");
+			ASSERT(0); TODO("FFS");
+		}
+		
+		offset += (size_t)next_record_len + 13;
+	}
+}
+
+/***************************************************************************************************/
+/* Helper functions to reorder the received chunks by sequence number                              */
+/***************************************************************************************************/
+
+struct chunk {
+	struct fd_list	chain;	/* link in the ordered list of chunks */
+	uint8_t		seq[8];	/* epoch + sequence number */
+	uint8_t *	buffer; /* the data */
+	size_t		len;	/* length of the buffer */
+	uint16_t	stream; /* which stream the chunk was received on */
+	/* We could also add a timestamp here */
+};
+
+/* Inserts new buffer received from the connection in the list of chunks */
+static int chunk_insert(struct cnxctx * conn, uint16_t streamid, uint8_t *buffer, size_t len)
+{
+	struct chunk * new;
+	struct fd_list * li;
+	uint8_t * newseq;
+	
+	/* Check the new sequence is >= what we processed in upper layer */
+	newseq = buffer + DTLS_SEQ_OFFSET;
+	if (memcmp(newseq, conn->cc_sctp_dtls_data.validseq, 8) < 0) {
+		LOG_E("Received DTLS packet with smaller sequence number than already processed, discarded. FFS.");
+		free(buffer);
+		return 0;
+	}
+	
+	/* Create a new chunk structure to store this chunk */
+	CHECK_MALLOC( new = malloc(sizeof(struct chunk)) );
+	memset(new, 0, sizeof(struct chunk));
+	fd_list_init(&new->chain, new);
+	memcpy(&new->seq, newseq, 8);
+	new->buffer = buffer;
+	new->len = len;
+	new->stream = streamid;
+	
+	/* Insert this new structure in the list attached to the connection */
+	for (li = conn->cc_sctp_dtls_data.chunks.prev; li != &conn->cc_sctp_dtls_data.chunks; li = li->prev) {
+		int cmp = memcmp(new->seq, ((struct chunk *)li->o)->seq, 8);
+		if (cmp < 0) continue;
+		if (cmp == 0) {
+			/* discard repeated seq */
+			LOG_E("Received DTLS packet with duplicate sequence number, discarded. FFS.");
+			free(buffer);
+			free(new);
+			return 0;
+		}
+		break;
+	}
+	/* special case: if we are already delivering partially the first chunk, we do insert only after this one */
+	if (conn->cc_sctp_dtls_data.offset && (li == &conn->cc_sctp_dtls_data.chunks))
+		li = li->next;
+	
+	fd_list_insert_after(li, &new->chain);
+	
+	return 0;
+	
+}
+
+/* Retrieve data from the list of chunks. Returns 0 if no data is ready for upper layer, the available length otherwise (up to upperlen) */
+static size_t chunk_retrieve(struct cnxctx * conn, void * upperbuf, size_t upperlen, int probeonly) 
+{
+	struct chunk * next;
+	int cmp;
+	size_t ret = 0;
+redo:	
+	if (FD_IS_LIST_EMPTY(&conn->cc_sctp_dtls_data.chunks)) {
+		return 0;
+	}
+	
+	next = conn->cc_sctp_dtls_data.chunks.next->o;
+	
+	/* If we are already delivering this chunk, just continue until complete */
+	if (conn->cc_sctp_dtls_data.offset != 0) {
+		if (probeonly)
+			return 1;
+		
+		ret = next->len - conn->cc_sctp_dtls_data.offset;
+		if (upperlen < ret)
+			ret = upperlen;
+		
+		memcpy(upperbuf, next->buffer + conn->cc_sctp_dtls_data.offset, ret);
+		conn->cc_sctp_dtls_data.offset += ret;
+		if (conn->cc_sctp_dtls_data.offset == next->len) {
+			/* we delivered the complete chunk, now we can remove it */
+			conn->cc_sctp_dtls_data.offset = 0;
+			free(next->buffer);
+			fd_list_unlink(&next->chain);
+			free(next);
+		}
+		return ret;
+	}
+	
+	cmp = memcmp(next->seq, conn->cc_sctp_dtls_data.nextseq, 8);
+	if (cmp < 0) {
+		cmp = memcmp(next->seq, conn->cc_sctp_dtls_data.validseq, 8);
+		if (cmp < 0) {
+			/* This is old stuff or invalid stuff, discard */
+			LOG_E("Unqueued DTLS packet with old sequence number, discarding.");
+			free(next->buffer);
+			fd_list_unlink(&next->chain);
+			free(next);
+			goto redo;
+		}
+		/* If the first chunk in our list has a smaller seq number than what we already delivered, we pass it above (to prevent possible DoS by sending forged sequence numbers) */
+		if (probeonly)
+			return 1;
+		
+		ret = next->len;
+		if (upperlen < ret) {
+			ret = upperlen;
+			memcpy(upperbuf, next->buffer, ret);
+			conn->cc_sctp_dtls_data.offset = ret;
+		} else {
+			memcpy(upperbuf, next->buffer, ret);
+			free(next->buffer);
+			fd_list_unlink(&next->chain);
+			free(next);
+		}
+		LOG_A("Unqueueing (old) chunk with seq number %02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx", 
+			next->seq[0],next->seq[1],next->seq[2],next->seq[3],next->seq[4],next->seq[5],next->seq[6],next->seq[7]);
+		return ret;
+	}
+	if (cmp > 0) {
+		/* is this the first message of a new epoch ? */
+		uint8_t newepoch[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
+		if (next->seq[1] != 0xFF) {
+			newepoch[0] = next->seq[0]; newepoch[1] = next->seq[1] + 1;
+		} else if (next->seq[0] != 0xFF) {
+			newepoch[0] = next->seq[0] + 1; newepoch[1] = 0;
+		} else {
+			LOG_F("Epoch field wrapped, can this happen ???");
+			ASSERT(0); TODO("FFS");
+		}
+		
+		if (memcmp(newepoch, next->seq, 8) == 0) {
+			/* Bingo, this is the first message of the new epoch. We update our nextseq accordingly */
+			if (probeonly)
+				return 1;
+			memcpy(conn->cc_sctp_dtls_data.nextseq, newepoch, 8);
+			update_nextseq_from_records(conn, next->buffer, next->len);
+			
+			LOG_A("Unqueueing chunk with seq number %02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx (epoch change)", 
+				next->seq[0],next->seq[1],next->seq[2],next->seq[3],next->seq[4],next->seq[5],next->seq[6],next->seq[7]);
+			ret = next->len;
+			if (upperlen < ret) {
+				ret = upperlen;
+				memcpy(upperbuf, next->buffer, ret);
+				conn->cc_sctp_dtls_data.offset = ret;
+			} else {
+				memcpy(upperbuf, next->buffer, ret);
+				free(next->buffer);
+				fd_list_unlink(&next->chain);
+				free(next);
+			}
+			return ret;
+		}
+			
+		/* otherwise, we don't return this data */
+		return 0;
+	}
+	
+	/* next is the next chunk expected on this connection */
+	if (probeonly)
+		return 1;
+	
+	/* We increment the next seq by the number or records found in this chunk */
+	update_nextseq_from_records(conn, next->buffer, next->len);
+	
+	/* And we deliver this to upper layer */
+	LOG_A("Unqueueing chunk: Type %hhd, Ver:%02hhx.%02hhx, Seq:%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx", 
+		next->buffer[0], next->buffer[1], next->buffer[2], 
+		next->seq[0],next->seq[1],next->seq[2],next->seq[3],next->seq[4],next->seq[5],next->seq[6],next->seq[7]);
+	ret = next->len;
+	if (upperlen < ret) {
+		ret = upperlen;
+		memcpy(upperbuf, next->buffer, ret);
+		conn->cc_sctp_dtls_data.offset = ret;
+	} else {
+		memcpy(upperbuf, next->buffer, ret);
+		free(next->buffer);
+		fd_list_unlink(&next->chain);
+		free(next);
+	}
+	return ret;
+}
+
+/* returns positive value if data is available for upper layer, 0 if the time is elapsed */
+static int chunk_select(struct cnxctx * conn, unsigned int ms)
+{
+	fd_set rfds;
+	struct timespec absend, inter;
+	int ret;
+	uint8_t * buf;
+	size_t 	  len;
+	uint16_t  strid;
+
+	/* absolute time we will timeout */
+	CHECK_SYS_DO(  clock_gettime(CLOCK_REALTIME, &absend), return -1 );
+	absend.tv_sec += ((ms + (absend.tv_nsec / 1000000L)) / 1000);
+	absend.tv_nsec = ( ms * 1000000L + absend.tv_nsec ) % 1000000000L;
+	
+	do {
+		/* Check if we have available data in the list of chunks */
+		if (chunk_retrieve(conn, NULL, 0, 1) > 0)
+			return 1;
+	
+		/* otherwise we need to retrieve more data from the socket, so we select */
+
+		FD_ZERO (&rfds);
+		FD_SET (conn->cc_socket, &rfds);
+		
+		/* We wait until absend only */
+		CHECK_SYS_DO(  clock_gettime(CLOCK_REALTIME, &inter), return -1 );
+		if (inter.tv_nsec <= absend.tv_nsec) {
+			if (inter.tv_sec > absend.tv_sec) {
+				inter.tv_sec = 0; inter.tv_nsec = 0;
+			} else {
+				inter.tv_sec = absend.tv_sec - inter.tv_sec;
+				inter.tv_nsec = absend.tv_nsec - inter.tv_nsec;
+			}
+		} else {
+			if (inter.tv_sec >= absend.tv_sec) {
+				inter.tv_sec = 0; inter.tv_nsec = 0;
+			} else {
+				inter.tv_sec = absend.tv_sec - inter.tv_sec - 1;
+				inter.tv_nsec = 1000000000L - inter.tv_nsec + absend.tv_nsec;
+			}
+		}
+
+		/* Now, wait for new data on the socket */
+		ret = pselect (conn->cc_socket + 1, &rfds, NULL, NULL, &inter, NULL);
+		if (ret <= 0)
+			break; /* no data was received, we can return */
+		
+		/* We got data, get it and insert in the list of chunks  */
+		ret = get_next_data_from_socket(conn, &strid, &buf, &len);
+		if (ret < 0)
+			break;
+		if (ret == 0)
+			continue;
+		
+		CHECK_FCT_DO( chunk_insert(conn, strid, buf, len), return -1 );
+		/* and loop */
+	} while (1);
+	
+	return ret;
+}
+
+/***************************************************************************************************/
+/* Functions "under" GNU TLS                                                                       */
+/***************************************************************************************************/
+
+/* Send data over the connection, called by gnutls. This function checks the type of DTLS packet and sends
+all non-application data over stream 0 (to enforce ordering) and application data over the stream set by
+upper layer in conn->cc_sctp_para.next */
+static ssize_t sctp_dtls_pushv(gnutls_transport_ptr_t tr, const giovec_t * iov, int iovcnt)
+{
+	struct cnxctx * conn = (struct cnxctx *)tr;
+	uint16_t stream = 0;
+	
+	
+	TRACE_ENTRY("%p %p %d", tr, iov, iovcnt);
+	CHECK_PARAMS_DO( tr && iov, { errno = EINVAL; return -1; } );
+	
+	if ((conn->cc_sctp_para.unordered != 0) 
+	&& (iovcnt > 0) 
+	&& (iov->iov_len > 0) 
+	&& (((uint8_t *)iov->iov_base)[DTLS_TYPE_OFFSET] == DTLS_TYPE_application_data)) {
+		/* Data is sent over different streams, if allowed */
+		stream = conn->cc_sctp_para.next;
+	}
+	
+	if ((iovcnt > 0) && (iov->iov_len > 10)) {
+		LOG_A("Sending DTLS data, type %hhd, Seq %02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx, Stream %hu",
+			((uint8_t *)iov->iov_base)[0],
+			((uint8_t *)iov->iov_base)[3],((uint8_t *)iov->iov_base)[4],((uint8_t *)iov->iov_base)[5],((uint8_t *)iov->iov_base)[6],
+			((uint8_t *)iov->iov_base)[7],((uint8_t *)iov->iov_base)[8],((uint8_t *)iov->iov_base)[9],((uint8_t *)iov->iov_base)[10],
+			stream);	
+	} else {
+		LOG_A("Sending DTLS data, {iovcnt=%d, iov->iov_len=%zd}, Stream %hu",
+			iovcnt, ((iovcnt>0) ? iov->iov_len : 0), stream);	
+	}
+	
+	return fd_sctp_sendstrv(conn, stream, (const struct iovec *)iov, iovcnt);
+}
+
+/* Check if data is available for gnutls on a given connection.  */
+static int sctp_dtls_pull_timeout(gnutls_transport_ptr_t tr, unsigned int ms)
+{
+	struct cnxctx * conn = (struct cnxctx *)tr;
+	return chunk_select(conn, ms);
+}
+
+
+/* This function returns only ordered data to the upper layer */
+static ssize_t sctp_dtls_pull(gnutls_transport_ptr_t tr, void * gnutlsbuf, size_t gnutlslen)
+{
+	struct cnxctx * conn = (struct cnxctx *)tr;
+	ssize_t ret = 0;
+	
+	while ( (ret = chunk_retrieve(conn,gnutlsbuf,gnutlslen,0)) == 0) {
+		
+		/* No partial data, read the next SCTP record */
+		int       stop = 0;
+		uint8_t * buf;
+		size_t 	  len;
+		uint16_t  strid;
+		do {
+			stop = get_next_data_from_socket(conn, &strid, &buf, &len);
+			if (stop < 0)
+				goto out;
+		} while (!stop);
+		
+		CHECK_FCT_DO( chunk_insert(conn, strid, buf, len), goto out );
+	}
+	
+out:
+	return ret;
+
+}
+
+
+/***************************************************************************************************/
+/* Functions "above" GNU TLS                                                                       */
+/***************************************************************************************************/
+
+/* Set the parameters of a session to use the cnxctx object */
+int fd_sctp_dtls_settransport(gnutls_session_t session, struct cnxctx * conn)
+{
+	/* Set the transport pointer passed to push & pull callbacks */
+	GNUTLS_TRACE( gnutls_transport_set_ptr( session, (gnutls_transport_ptr_t) conn ) );
+	
+	/* in 3.0 we have to provide the pull_timeout callback */
+	GNUTLS_TRACE( gnutls_transport_set_pull_timeout_function( session, sctp_dtls_pull_timeout ) );
+	
+	/* Set the push and pull callbacks */
+	GNUTLS_TRACE( gnutls_transport_set_pull_function(session, sctp_dtls_pull) );
+	GNUTLS_TRACE( gnutls_transport_set_vec_push_function(session, sctp_dtls_pushv) );
+
+	return 0;
+}
+
+
+/* Set additional session parameters before handshake. The GNUTLS_DATAGRAM is already set in fd_tls_prepare */
+int fd_sctp_dtls_prepare(gnutls_session_t session)
+{
+	/* We do not use cookies at the moment. Not sure it is useful or not */
+	/* TODO("Cookie exchange?"); */
+	/* gnutls_dtls_prestate_set (session, &prestate); */
+
+	GNUTLS_TRACE( gnutls_dtls_set_mtu(session, DTLS_SCTP_MTU));
+
+	GNUTLS_TRACE( gnutls_dtls_set_timeouts(session, 70000, 60000)); /* Set retrans > total so that there is no retransmission, since SCTP is reliable */
+
+#ifdef GNUTLS_VERSION_322
+	TODO("Disable replay protection");
+	TODO("Register hook on the Finish message to change SCTP_AUTH active key on the socket");
+#endif /* GNUTLS_VERSION_322 */
+	
+	return 0;
+	
+}
+
+/* the following function is actually almost same as fd_tls_recv_handle_error at the moment, since all handling is done under gnutls */
+ssize_t fd_dtls_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_seq(session, data, sz, conn->cc_sctp_dtls_data.validseq), 
+		{
+			switch (ret) {
+				case GNUTLS_E_REHANDSHAKE: 
+					if (!fd_cnx_teststate(conn, CC_STATUS_CLOSING)) {
+						CHECK_GNUTLS_DO( ret = gnutls_handshake(session),
+							{
+								if (TRACE_BOOL(INFO)) {
+									fd_log_debug("TLS re-handshake failed on socket %d (%s) : %s", conn->cc_socket, conn->cc_id, gnutls_strerror(ret));
+								}
+								goto end;
+							} );
+					}
+
+				case GNUTLS_E_AGAIN:
+				case GNUTLS_E_INTERRUPTED:
+					if (!fd_cnx_teststate(conn, CC_STATUS_CLOSING))
+						goto again;
+					TRACE_DEBUG(FULL, "Connection is closing, so abord gnutls_record_recv now.");
+					break;
+
+				case GNUTLS_E_UNEXPECTED_PACKET_LENGTH:
+					/* The connection is closed */
+					TRACE_DEBUG(FULL, "Got 0 size while reading the socket, probably connection closed...");
+					break;
+				
+				case GNUTLS_E_WARNING_ALERT_RECEIVED:
+					LOG_N("Received TLS WARNING ALERT: %s", gnutls_alert_get_name(gnutls_alert_get(session)) ?: "<unknown alert>");
+					if (!fd_cnx_teststate(conn, CC_STATUS_CLOSING))
+						goto again;
+					TRACE_DEBUG(FULL, "Connection is closing, so abord gnutls_record_recv now.");
+					break;
+					
+				case GNUTLS_E_FATAL_ALERT_RECEIVED:
+					LOG_E("Received TLS FATAL ALERT: %s", gnutls_alert_get_name(gnutls_alert_get(session)) ?: "<unknown alert>");
+					break;
+					
+				default:
+					if (gnutls_error_is_fatal (ret) == 0) {
+						LOG_N("Ignoring non-fatal GNU TLS error: %s", gnutls_strerror (ret));
+						goto again;
+					}
+					LOG_E("Fatal GNUTLS error: %s", gnutls_strerror (ret));
+			}
+		} );
+		
+	if (ret == 0)
+		CHECK_GNUTLS_DO( gnutls_bye(session, GNUTLS_SHUT_RDWR),  );
+	
+end:	
+	if (ret <= 0)
+		fd_cnx_markerror(conn);
+	return ret;
+}
+
+/* Receiver thread that reassemble the decrypted messages (when size is > 2<<14) for upper layer. Very similar to fd_tls_rcvthr_core in this version */
+void * fd_sctp_dtls_rcvthr(void * arg) {
+
+	struct cnxctx * conn = arg;
+	
+	TRACE_ENTRY("%p", arg);
+	CHECK_PARAMS_DO(conn && (conn->cc_socket > 0), return NULL );
+	
+	/* Set the thread name */
+	{
+		char buf[48];
+		snprintf(buf, sizeof(buf), "Receiver (%d) DTLS", conn->cc_socket);
+		fd_log_threadname ( buf );
+	}
+	
+	ASSERT( fd_cnx_teststate(conn, CC_STATUS_TLS) );
+	ASSERT( fd_cnx_target_queue(conn) );
+	
+	/* The next function only returns when there is an error on the socket */	
+	CHECK_FCT_DO(fd_tls_rcvthr_core(conn, conn->cc_tls_para.session, 1), /* continue */);
+
+	TRACE_DEBUG(FULL, "Thread terminated");	
+	return NULL;
+}	
+
+		
+
+/* Send a new Diameter message over the association */
+int fd_sctp_dtls_send(struct cnxctx * conn, unsigned char * buf, size_t len)
+{
+	ssize_t ret;
+	size_t sent = 0;
+	size_t maxlen = gnutls_dtls_get_data_mtu(conn->cc_tls_para.session);
+	TRACE_ENTRY("%p %p %zd", conn, buf, len);
+	
+	CHECK_PARAMS(conn);
+	
+	/* First, decide which stream this data will be sent to */
+	if (conn->cc_sctp_para.str_out > 32) {
+		TODO("Limiting to 32 streams. Remove this limit when anti-replay is disabled");
+		conn->cc_sctp_para.str_out = 32;
+	}
+	if (conn->cc_sctp_para.str_out > 1) {
+		conn->cc_sctp_para.next += 1;
+		conn->cc_sctp_para.next %= conn->cc_sctp_para.str_out;
+	} else {
+		conn->cc_sctp_para.next = 0;
+	}
+	
+	/* Now send the data over this stream. Do it in a loop in case the length is larger than the MTU */
+	do {
+		size_t tosend = len - sent;
+		if (tosend > maxlen)
+			tosend = maxlen;
+		CHECK_GNUTLS_DO( ret = fd_tls_send_handle_error(conn, conn->cc_tls_para.session, buf + sent, tosend),  );
+		if (ret <= 0)
+			return ENOTCONN;
+		
+		sent += ret;
+	} while ( sent < len );
+	return 0;
+}
--- a/libfdcore/server.c	Wed Jun 19 10:20:47 2013 +0800
+++ b/libfdcore/server.c	Thu Jun 20 10:20:29 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 ? 0: no; 1: RFU; 2: yes (TLS/TCP or TLS/SCTP) */
+	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 waiting for new connections (will store the data in the clients fifo) */
 	enum s_state	state;		/* state of the thread */
@@ -359,19 +359,19 @@
 		
 		/* Create the server on secure port */
 		if (fd_g_config->cnf_port_tls) {
-			CHECK_MALLOC( s = new_serv(IPPROTO_SCTP, 2 /* Change when DTLS is introduced */) );
+			CHECK_MALLOC( s = new_serv(IPPROTO_SCTP, 1) );
 			CHECK_MALLOC( s->conn = fd_cnx_serv_sctp(fd_g_config->cnf_port_tls, empty_conf_ep ? NULL : &fd_g_config->cnf_endpoints) );
 			fd_list_insert_before( &FD_SERVERS, &s->chain );
 			CHECK_POSIX( pthread_create( &s->thr, NULL, serv_th, s ) );
 		}
 		
 		/* Create the other server on 3436 secure port */
-		/*if (fd_g_config->cnf_port_3436) {
+		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 19 10:20:47 2013 +0800
+++ b/tests/testcnx.c	Thu Jun 20 10:20:29 2013 +0800
@@ -614,7 +614,6 @@
 									 GNUTLS_X509_FMT_PEM), );
 	CHECK( 1, ret );
 	
-	#ifdef GNUTLS_VERSION_300
 	{
 		/* We import these CA in the trust list */
 		gnutls_x509_crt_t * calist;
@@ -631,9 +630,6 @@
 	/* Use certificate verification during the handshake */
 	gnutls_certificate_set_verify_function (fd_g_config->cnf_sec_data.credentials, fd_tls_verify_credentials_2);
 	
-	#endif /* GNUTLS_VERSION_300 */
-							
-	
 	/* Set the server credentials (in config) */
 	CHECK_GNUTLS_DO( ret = gnutls_certificate_set_x509_key_mem( fd_g_config->cnf_sec_data.credentials,
 									&server_cert,
"Welcome to our mercurial repository"