Index: libfdcore/cnxctx.c
===================================================================
--- libfdcore/cnxctx.c	(revision 1181)
+++ libfdcore/cnxctx.c	(revision 1186)
@@ -39,4 +39,5 @@
 #include <net/if.h>
 #include <ifaddrs.h> /* for getifaddrs */
+#include <sys/uio.h> /* writev */
 
 /* The maximum size of Diameter message we accept to receive (<= 2^24) to avoid too big mallocs in case of trashed headers */
@@ -479,11 +480,19 @@
 }
 
+/* Mark the connection to tell if OOO delivery is permitted (only for SCTP) */
+int fd_cnx_unordered_delivery(struct cnxctx * conn, int is_allowed)
+{
+	CHECK_PARAMS( conn );
+	conn->cc_sctp_para.unordered = is_allowed;
+	return 0;
+}
+
 /* Return true if the connection supports unordered delivery of messages */
-int fd_cnx_isMultichan(struct cnxctx * conn)
+int fd_cnx_is_unordered_delivery_supported(struct cnxctx * conn)
 {
 	CHECK_PARAMS_DO( conn, return 0 );
 	#ifndef DISABLE_SCTP
 	if (conn->cc_proto == IPPROTO_SCTP)
-		return (conn->cc_sctp_para.str_in > 1) || (conn->cc_sctp_para.str_out > 1);
+		return (conn->cc_sctp_para.str_out > 1);
 	#endif /* DISABLE_SCTP */
 	return 0;
@@ -531,4 +540,20 @@
 }
 
+static int fd_cnx_may_dtls(struct cnxctx * conn);
+
+/* Get a short string representing the connection */
+int fd_cnx_proto_info(struct cnxctx * conn, char * buf, size_t len) 
+{
+	CHECK_PARAMS( conn );
+	
+	if (fd_cnx_teststate(conn, CC_STATUS_TLS)) {
+		snprintf(buf, len, "%s,%s,soc#%d", IPPROTO_NAME(conn->cc_proto), fd_cnx_may_dtls(conn) ? "DTLS" : "TLS", conn->cc_socket);
+	} else {
+		snprintf(buf, len, "%s,soc#%d", IPPROTO_NAME(conn->cc_proto), conn->cc_socket);
+	}
+	
+	return 0;
+}
+
 /* Retrieve a list of all IP addresses of the local system from the kernel, using getifaddrs */
 int fd_cnx_get_local_eps(struct fd_list * list)
@@ -600,4 +625,22 @@
 	CHECK_SYS_DO( setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)), /* Also timeout for sending, to avoid waiting forever */ );
 }
+
+
+#ifdef GNUTLS_VERSION_300
+/* The pull_timeout function for gnutls */
+static int fd_cnx_s_select (struct cnxctx * conn, unsigned int ms)
+{
+	fd_set rfds;
+	struct timeval tv;
+	
+	FD_ZERO (&rfds);
+	FD_SET (conn->cc_socket, &rfds);
+	
+	tv.tv_sec = ms / 1000;
+	tv.tv_usec = (ms * 1000) % 1000000;
+	
+	return select (conn->cc_socket + 1, &rfds, NULL, NULL, &tv);
+}		
+#endif /* GNUTLS_VERSION_300 */
 
 /* A recv-like function, taking a cnxctx object instead of socket as entry. We use it to quickly react to timeouts without traversing GNUTLS wrapper each time */
@@ -628,6 +671,7 @@
 }
 
-/* Send */
-static ssize_t fd_cnx_s_send(struct cnxctx * conn, void *buffer, size_t length)
+/* Send, for older GNUTLS */
+#ifndef GNUTLS_VERSION_212
+static ssize_t fd_cnx_s_send(struct cnxctx * conn, const void *buffer, size_t length)
 {
 	ssize_t ret = 0;
@@ -635,4 +679,31 @@
 again:
 	ret = send(conn->cc_socket, buffer, length, 0);
+	/* Handle special case of timeout */
+	if ((ret < 0) && ((errno == EAGAIN) || (errno == EINTR))) {
+		pthread_testcancel();
+		if (! fd_cnx_teststate(conn, CC_STATUS_CLOSING ))
+			goto again; /* don't care, just ignore */
+		if (!timedout) {
+			timedout ++; /* allow for one timeout while closing */
+			goto again;
+		}
+		CHECK_SYS_DO(ret, /* continue */);
+	}
+	
+	/* Mark the error */
+	if (ret <= 0)
+		fd_cnx_markerror(conn);
+	
+	return ret;
+}
+#endif /* GNUTLS_VERSION_212 */
+
+/* Send */
+static ssize_t fd_cnx_s_sendv(struct cnxctx * conn, const struct iovec * iov, int iovcnt)
+{
+	ssize_t ret = 0;
+	int timedout = 0;
+again:
+	ret = writev(conn->cc_socket, iov, iovcnt);
 	/* Handle special case of timeout */
 	if ((ret < 0) && ((errno == EAGAIN) || (errno == EINTR))) {
@@ -1605,6 +1676,13 @@
 		/* Set the push and pull callbacks */
 		if (!dtls) {
+			#ifdef GNUTLS_VERSION_300
+			GNUTLS_TRACE( gnutls_transport_set_pull_timeout_function( conn->cc_tls_para.session, (void *)fd_cnx_s_select ) );
+			#endif /* GNUTLS_VERSION_300 */
 			GNUTLS_TRACE( gnutls_transport_set_pull_function(conn->cc_tls_para.session, (void *)fd_cnx_s_recv) );
+			#ifndef GNUTLS_VERSION_212
 			GNUTLS_TRACE( gnutls_transport_set_push_function(conn->cc_tls_para.session, (void *)fd_cnx_s_send) );
+			#else /* GNUTLS_VERSION_212 */
+			GNUTLS_TRACE( gnutls_transport_set_vec_push_function(conn->cc_tls_para.session, (void *)fd_cnx_s_sendv) );
+			#endif /* GNUTLS_VERSION_212 */
 		} else {
 			TODO("DTLS push/pull functions");
@@ -1784,6 +1862,8 @@
 			CHECK_GNUTLS_DO( ret = fd_tls_send_handle_error(conn, conn->cc_tls_para.session, buf + sent, len - sent),  );
 		} else {
-			/* Maybe better to replace this call with sendmsg for atomic sending? */
-			CHECK_SYS_DO( ret = fd_cnx_s_send(conn, buf + sent, len - sent), );
+			struct iovec iov;
+			iov.iov_base = buf + sent;
+			iov.iov_len  = len - sent;
+			CHECK_SYS_DO( ret = fd_cnx_s_sendv(conn, &iov, 1), );
 		}
 		if (ret <= 0)
@@ -1796,7 +1876,7 @@
 
 /* Send a message -- this is synchronous -- and we assume it's never called by several threads at the same time (on the same conn), so we don't protect. */
-int fd_cnx_send(struct cnxctx * conn, unsigned char * buf, size_t len, uint32_t flags)
-{
-	TRACE_ENTRY("%p %p %zd %x", conn, buf, len, flags);
+int fd_cnx_send(struct cnxctx * conn, unsigned char * buf, size_t len)
+{
+	TRACE_ENTRY("%p %p %zd", conn, buf, len);
 	
 	CHECK_PARAMS(conn && (conn->cc_socket > 0) && (! fd_cnx_teststate(conn, CC_STATUS_ERROR)) && buf && len);
@@ -1812,46 +1892,48 @@
 		case IPPROTO_SCTP: {
 			int dtls = fd_cnx_uses_dtls(conn);
-
 			if (!dtls) {
-				if (flags & FD_CNX_ORDERED) {
-					/* We send over stream #0 */
+				int stream = 0;
+				if (conn->cc_sctp_para.unordered) {
+					int limit;
+					if (fd_cnx_teststate(conn, CC_STATUS_TLS))
+						limit = conn->cc_sctp_para.pairs;
+					else
+						limit = conn->cc_sctp_para.str_out;
+					
+					if (limit > 1) {
+						conn->cc_sctp_para.next += 1;
+						conn->cc_sctp_para.next %= limit;
+						stream = conn->cc_sctp_para.next;
+					}
+				}
+				
+				if (stream == 0) {
+					/* We can use default function, it sends over stream #0 */
 					CHECK_FCT( send_simple(conn, buf, len) );
 				} else {
-					/* 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) );
+					if (!fd_cnx_teststate(conn, CC_STATUS_TLS)) {
+						struct iovec iov;
+						iov.iov_base = buf;
+						iov.iov_len  = len;
+						
+						CHECK_SYS_DO( fd_sctp_sendstrv(conn, stream, &iov, 1), { fd_cnx_markerror(conn); return ENOTCONN; } );
 					} else {
-						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 );
-						}
+						/* push the data to the appropriate session */
+						ssize_t ret;
+						size_t sent = 0;
+						ASSERT(conn->cc_sctp3436_data.array != NULL);
+						do {
+							CHECK_GNUTLS_DO( ret = fd_tls_send_handle_error(conn, conn->cc_sctp3436_data.array[stream].session, buf + sent, len - sent), );
+							if (ret <= 0)
+								return ENOTCONN;
+
+							sent += ret;
+						} while ( sent < len );
 					}
 				}
 			} else {
 				/* DTLS */
-				/* We signal the push function directly to tell if using stream 0 or round-robin */
-				TODO("DTLS send");
-				return ENOTSUP;
+				/* Multistream is handled at lower layer in the push/pull function */
+				CHECK_FCT( send_simple(conn, buf, len) );
 			}
 		}
Index: libfdcore/cnxctx.h
===================================================================
--- libfdcore/cnxctx.h	(revision 1181)
+++ libfdcore/cnxctx.h	(revision 1186)
@@ -75,4 +75,5 @@
 		uint16_t pairs;		/* max number of pairs ( = min(in, out)) */
 		uint16_t next;		/* # of stream the next message will be sent to */
+		int	 unordered;	/* boolean telling if use of streams > 0 is permitted */
 	} 		cc_sctp_para;
 
@@ -118,5 +119,5 @@
 int fd_sctp_get_remote_ep(int sock, struct fd_list * list);
 int fd_sctp_get_str_info( int sock, uint16_t *in, uint16_t *out, sSS *primary );
-int fd_sctp_sendstr(struct cnxctx * conn, uint16_t strid, uint8_t * buf, size_t len);
+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);
 
Index: libfdcore/fdcore-internal.h
===================================================================
--- libfdcore/fdcore-internal.h	(revision 1181)
+++ libfdcore/fdcore-internal.h	(revision 1186)
@@ -296,5 +296,5 @@
 
 /* Peer out */
-int fd_out_send(struct msg ** msg, struct cnxctx * cnx, struct fd_peer * peer, uint32_t flags);
+int fd_out_send(struct msg ** msg, struct cnxctx * cnx, struct fd_peer * peer);
 int fd_out_start(struct fd_peer * peer);
 int fd_out_stop(struct fd_peer * peer);
@@ -342,4 +342,5 @@
 int             fd_cnx_start_clear(struct cnxctx * conn, int loop);
 void		fd_cnx_sethostname(struct cnxctx * conn, DiamId_t hn);
+int		fd_cnx_proto_info(struct cnxctx * conn, char * buf, size_t len);
 #define ALGO_HANDSHAKE_DEFAULT	0 /* TLS for TCP, DTLS for SCTP */
 #define ALGO_HANDSHAKE_3436	1 /* For TLS for SCTP also */
@@ -348,5 +349,6 @@
 int		fd_cnx_getproto(struct cnxctx * conn);
 int		fd_cnx_getTLS(struct cnxctx * conn);
-int		fd_cnx_isMultichan(struct cnxctx * conn);
+int		fd_cnx_is_unordered_delivery_supported(struct cnxctx * conn);
+int		fd_cnx_unordered_delivery(struct cnxctx * conn, int is_allowed);
 int             fd_cnx_getcred(struct cnxctx * conn, const gnutls_datum_t **cert_list, unsigned int *cert_list_size);
 int 		fd_cnx_get_local_eps(struct fd_list * list);
@@ -355,12 +357,9 @@
 int             fd_cnx_receive(struct cnxctx * conn, struct timespec * timeout, unsigned char **buf, size_t * len);
 int             fd_cnx_recv_setaltfifo(struct cnxctx * conn, struct fifo * alt_fifo); /* send FDEVP_CNX_MSG_RECV event to the fifo list */
-int             fd_cnx_send(struct cnxctx * conn, unsigned char * buf, size_t len, uint32_t flags);
+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 */
-
-/* Flags for the fd_cnx_send function : */
-#define FD_CNX_ORDERED		(1 << 0)	/* All messages sent with this flag set will be delivered in the same order. No guarantee on other messages */
 
 /* Internal calls of the hook mechanism */
Index: libfdcore/hooks.c
===================================================================
--- libfdcore/hooks.c	(revision 1168)
+++ libfdcore/hooks.c	(revision 1186)
@@ -420,5 +420,12 @@
 				}
 				CHECK_MALLOC_DO(fd_msg_dump_treeview(&buf, &len, NULL, msg, NULL, 0, 1), break);
-				LOG_N("Connected to '%s', remote capabilities: ", peer ? peer->p_hdr.info.pi_diamid : "<unknown>");
+				char protobuf[40];
+				if (peer) {
+					CHECK_FCT_DO(fd_peer_cnx_proto_info(&peer->p_hdr, protobuf, sizeof(protobuf)), break );
+				} else {
+					protobuf[0] = '-';
+					protobuf[1] = '\0';
+				}
+				LOG_N("Connected to '%s' (%s), remote capabilities: ", peer ? peer->p_hdr.info.pi_diamid : "<unknown>", protobuf);
 				LOG_SPLIT(FD_LOG_NOTICE, "   ", buf?:"<error dumping message>", NULL);
 				break;
Index: libfdcore/p_ce.c
===================================================================
--- libfdcore/p_ce.c	(revision 1181)
+++ libfdcore/p_ce.c	(revision 1186)
@@ -633,5 +633,5 @@
 	CHECK_FCT_DO( fd_msg_new_answer_from_req ( fd_g_config->cnf_dict, cer, MSGFL_ANSW_ERROR ), goto destroy );
 	CHECK_FCT_DO( fd_msg_rescode_set(*cer, error->pei_errcode, error->pei_message, error->pei_avp, 1 ), goto destroy );
-	CHECK_FCT_DO( fd_out_send(cer, *recv_cnx, NULL, FD_CNX_ORDERED), goto destroy );
+	CHECK_FCT_DO( fd_out_send(cer, *recv_cnx, NULL), goto destroy );
 	
 	/* And now destroy this connection */
@@ -653,5 +653,5 @@
 	/* Send CER on the new connection */
 	CHECK_FCT( create_CER(peer, initiator, &cer) );
-	CHECK_FCT( fd_out_send(&cer, initiator, peer, FD_CNX_ORDERED) );
+	CHECK_FCT( fd_out_send(&cer, initiator, peer) );
 	
 	/* Are we doing an election ? */
@@ -708,5 +708,5 @@
 
 		/* msg now contains an answer message to send back */
-		CHECK_FCT_DO( fd_out_send(msg, NULL, peer, FD_CNX_ORDERED), /* In case of error the message has already been dumped */ );
+		CHECK_FCT_DO( fd_out_send(msg, NULL, peer), /* In case of error the message has already been dumped */ );
 	}
 	
@@ -950,5 +950,5 @@
 	fd_hook_call(HOOK_PEER_CONNECT_SUCCESS, msg, peer, NULL, NULL);
 	
-	CHECK_FCT( fd_out_send(&msg, peer->p_cnxctx, peer, FD_CNX_ORDERED ) );
+	CHECK_FCT( fd_out_send(&msg, peer->p_cnxctx, peer ) );
 	
 	/* Handshake if needed */
@@ -994,5 +994,5 @@
 		CHECK_FCT( fd_p_dw_reopen(peer) );
 	} else {
-		if ((!tls_sync) && (fd_cnx_isMultichan(peer->p_cnxctx))) {
+		if ((!tls_sync) && (fd_cnx_is_unordered_delivery_supported(peer->p_cnxctx))) {
 			fd_psm_change_state(peer, STATE_OPEN_NEW );
 			/* send DWR */
Index: libfdcore/p_cnx.c
===================================================================
--- libfdcore/p_cnx.c	(revision 1181)
+++ libfdcore/p_cnx.c	(revision 1186)
@@ -346,2 +346,3 @@
 	}
 }
+
Index: libfdcore/p_dp.c
===================================================================
--- libfdcore/p_dp.c	(revision 1127)
+++ libfdcore/p_dp.c	(revision 1186)
@@ -118,5 +118,5 @@
 		
 			/* Now send the DPA */
-			CHECK_FCT( fd_out_send( msg, NULL, peer, FD_CNX_ORDERED) );
+			CHECK_FCT( fd_out_send( msg, NULL, peer) );
 			
 			/* and move to CLOSED */
@@ -132,5 +132,5 @@
 			
 			/* Now send the DPA */
-			CHECK_FCT( fd_out_send( msg, NULL, peer, FD_CNX_ORDERED) );
+			CHECK_FCT( fd_out_send( msg, NULL, peer) );
 		}
 	} else {
@@ -202,5 +202,5 @@
 	
 	/* Now send the DPR message */
-	CHECK_FCT_DO( fd_out_send(&msg, NULL, peer, FD_CNX_ORDERED), /* ignore since we are on timeout anyway */ );
+	CHECK_FCT_DO( fd_out_send(&msg, NULL, peer), /* ignore since we are on timeout anyway */ );
 	
 	return 0;
Index: libfdcore/p_dw.c
===================================================================
--- libfdcore/p_dw.c	(revision 1127)
+++ libfdcore/p_dw.c	(revision 1186)
@@ -83,5 +83,5 @@
 	
 	/* Now send this message */
-	CHECK_FCT( fd_out_send(&msg, NULL, peer, FD_CNX_ORDERED) );
+	CHECK_FCT( fd_out_send(&msg, NULL, peer) );
 	
 	/* And mark the pending DW */
@@ -106,5 +106,5 @@
 		CHECK_FCT( fd_msg_rescode_set( *msg, "DIAMETER_SUCCESS", NULL, NULL, 0 ) );
 		CHECK_FCT( fd_msg_add_origin ( *msg, 1 ) );
-		CHECK_FCT( fd_out_send( msg, peer->p_cnxctx, peer, FD_CNX_ORDERED) );
+		CHECK_FCT( fd_out_send( msg, peer->p_cnxctx, peer) );
 		
 	} else {
Index: libfdcore/p_out.c
===================================================================
--- libfdcore/p_out.c	(revision 1119)
+++ libfdcore/p_out.c	(revision 1186)
@@ -37,5 +37,5 @@
 
 /* Alloc a new hbh for requests, bufferize the message and send on the connection, save in sentreq if provided */
-static int do_send(struct msg ** msg, uint32_t flags, struct cnxctx * cnx, uint32_t * hbh, struct fd_peer * peer)
+static int do_send(struct msg ** msg, struct cnxctx * cnx, uint32_t * hbh, struct fd_peer * peer)
 {
 	struct msg_hdr * hdr;
@@ -47,5 +47,5 @@
 	struct msg *cpy_for_logs_only;
 	
-	TRACE_ENTRY("%p %x %p %p %p", msg, flags, cnx, hbh, peer);
+	TRACE_ENTRY("%p %p %p %p", msg, cnx, hbh, peer);
 	
 	/* Retrieve the message header */
@@ -76,5 +76,5 @@
 	
 	/* Send the message */
-	CHECK_FCT_DO( ret = fd_cnx_send(cnx, buf, sz, flags), );
+	CHECK_FCT_DO( ret = fd_cnx_send(cnx, buf, sz), );
 out:
 	;	
@@ -128,5 +128,5 @@
 		
 		/* Send the message, log any error */
-		CHECK_FCT_DO( ret = do_send(&msg, 0, peer->p_cnxctx, &peer->p_hbh, peer),
+		CHECK_FCT_DO( ret = do_send(&msg, peer->p_cnxctx, &peer->p_hbh, peer),
 			{
 				if (msg) {
@@ -149,9 +149,9 @@
 
 /* Wrapper to sending a message either by out thread (peer in OPEN state) or directly; cnx or peer must be provided. Flags are valid only for direct sending, not through thread (unused) */
-int fd_out_send(struct msg ** msg, struct cnxctx * cnx, struct fd_peer * peer, uint32_t flags)
+int fd_out_send(struct msg ** msg, struct cnxctx * cnx, struct fd_peer * peer)
 {
 	struct msg_hdr * hdr;
 	
-	TRACE_ENTRY("%p %p %p %x", msg, cnx, peer, flags);
+	TRACE_ENTRY("%p %p %p", msg, cnx, peer);
 	CHECK_PARAMS( msg && *msg && (cnx || (peer && peer->p_cnxctx)));
 	
@@ -182,5 +182,5 @@
 
 		/* Do send the message */
-		CHECK_FCT_DO( ret = do_send(msg, flags, cnx, hbh, peer),
+		CHECK_FCT_DO( ret = do_send(msg, cnx, hbh, peer),
 			{
 				if (msg) {
@@ -205,4 +205,6 @@
 	CHECK_POSIX( pthread_create(&peer->p_outthr, NULL, out_thr, peer) );
 	
+	CHECK_FCT( fd_cnx_unordered_delivery(peer->p_cnxctx, 1) );
+	
 	return 0;
 }
@@ -214,4 +216,6 @@
 	CHECK_PARAMS( CHECK_PEER(peer) );
 	
+	CHECK_FCT( fd_cnx_unordered_delivery(peer->p_cnxctx, 0) );
+	
 	CHECK_FCT( fd_thr_term(&peer->p_outthr) );
 	
Index: libfdcore/p_psm.c
===================================================================
--- libfdcore/p_psm.c	(revision 1181)
+++ libfdcore/p_psm.c	(revision 1186)
@@ -72,5 +72,5 @@
 Note about (5) and (6): if the Diameter Identity received in CER or CEA
 does not match the credentials from the certificate presented during
-DTLS handshake, we may need to specify a path of clean disconnection
+TLS handshake, we may need to specify a path of clean disconnection
 (not blocking the remote peer waiting for something).
 
@@ -601,5 +601,5 @@
 				if (msg == NULL) {
 					/* Send the error back to the peer */
-					CHECK_FCT_DO( ret = fd_out_send(&error, NULL, peer, FD_CNX_ORDERED),  );
+					CHECK_FCT_DO( ret = fd_out_send(&error, NULL, peer),  );
 					if (error) {
 						char buf[256];
@@ -655,5 +655,5 @@
 
 						/* Send the answer */
-						CHECK_FCT_DO( fd_out_send(&msg, peer->p_cnxctx, peer, FD_CNX_ORDERED), break );
+						CHECK_FCT_DO( fd_out_send(&msg, peer->p_cnxctx, peer), break );
 					} while (0);
 				} else {
Index: libfdcore/peers.c
===================================================================
--- libfdcore/peers.c	(revision 1185)
+++ libfdcore/peers.c	(revision 1186)
@@ -258,4 +258,22 @@
 }
 
+/* Describe the current connection */
+int fd_peer_cnx_proto_info(struct peer_hdr *peer, char * buf, size_t len)
+{
+	struct fd_peer * p = (struct fd_peer *)peer;
+	TRACE_ENTRY("%p %p %zd", peer, buf, len);
+	CHECK_PARAMS(CHECK_PEER(peer) && buf && len);
+	
+	if (p->p_cnxctx) {
+		CHECK_FCT(fd_cnx_proto_info(p->p_cnxctx, buf, len));
+	} else if (p->p_receiver) {
+		CHECK_FCT(fd_cnx_proto_info(p->p_receiver, buf, len));
+	} else {
+		snprintf(buf, len, "Not Connected");
+	}
+	
+	return 0;
+}
+
 /* Return the value of srlist->cnt */
 int fd_peer_get_load_pending(struct peer_hdr *peer, long * to_receive, long * to_send)
@@ -512,5 +530,5 @@
 		fd_hook_call(HOOK_PEER_CONNECT_FAILED, *cer, NULL, "Received CER with invalid Origin-Host AVP", NULL);
 		
-		CHECK_FCT( fd_out_send(cer, *cnx, NULL, FD_CNX_ORDERED) );
+		CHECK_FCT( fd_out_send(cer, *cnx, NULL) );
 		return EINVAL;
 	}
Index: libfdcore/routing_dispatch.c
===================================================================
--- libfdcore/routing_dispatch.c	(revision 1154)
+++ libfdcore/routing_dispatch.c	(revision 1186)
@@ -413,5 +413,5 @@
 		CHECK_FCT( fd_fifo_post(fd_g_incoming, pmsg) );
 	} else {
-		CHECK_FCT( fd_out_send(pmsg, NULL, peer, 0) );
+		CHECK_FCT( fd_out_send(pmsg, NULL, peer) );
 	}
 	
@@ -887,5 +887,5 @@
 
 		/* Push the message into this peer */
-		CHECK_FCT( fd_out_send(&msgptr, NULL, peer, 0) );
+		CHECK_FCT( fd_out_send(&msgptr, NULL, peer) );
 
 		/* We're done with this answer */
@@ -1004,5 +1004,5 @@
 		if (fd_peer_getstate(peer) == STATE_OPEN) {
 			/* Send to this one */
-			CHECK_FCT_DO( fd_out_send(&msgptr, NULL, peer, 0), continue );
+			CHECK_FCT_DO( fd_out_send(&msgptr, NULL, peer), continue );
 			
 			/* If the sending was successful */
Index: libfdcore/sctp.c
===================================================================
--- libfdcore/sctp.c	(revision 1175)
+++ libfdcore/sctp.c	(revision 1186)
@@ -1080,9 +1080,8 @@
 }
 
-/* Send a buffer over a specified stream */
-int fd_sctp_sendstr(struct cnxctx * conn, uint16_t strid, uint8_t * buf, size_t len)
+/* Send a vector over a specified stream */
+ssize_t fd_sctp_sendstrv(struct cnxctx * conn, uint16_t strid, const struct iovec *iov, int iovcnt)
 {
 	struct msghdr mhdr;
-	struct iovec  iov;
 	struct cmsghdr 		*hdr;
 #ifdef OLD_SCTP_SOCKET_API
@@ -1096,14 +1095,9 @@
 	int timedout = 0;
 	
-	TRACE_ENTRY("%p %hu %p %zd", conn, strid, buf, len);
-	CHECK_PARAMS(conn && buf && len);
+	TRACE_ENTRY("%p %hu %p %d", conn, strid, iov, iovcnt);
+	CHECK_PARAMS_DO(conn && iov && iovcnt, { errno = EINVAL; return -1; } );
 	
 	memset(&mhdr, 0, sizeof(mhdr));
-	memset(&iov,  0, sizeof(iov));
 	memset(&anci, 0, sizeof(anci));
-	
-	/* IO Vector: message data */
-	iov.iov_base = buf;
-	iov.iov_len  = len;
 	
 	/* Anciliary data: specify SCTP stream */
@@ -1124,11 +1118,11 @@
 	/* We don't use mhdr.msg_name here; it could be used to specify an address different from the primary */
 	
-	mhdr.msg_iov    = &iov;
-	mhdr.msg_iovlen = 1;
+	mhdr.msg_iov    = (struct iovec *)iov;
+	mhdr.msg_iovlen = iovcnt;
 	
 	mhdr.msg_control    = anci;
 	mhdr.msg_controllen = sizeof(anci);
 	
-	TRACE_DEBUG(FULL, "Sending %zdb data on stream %hu of socket %d", len, strid, conn->cc_socket);
+	TRACE_DEBUG(FULL, "Sending %d chunks of data (first:%zdb) on stream %hu of socket %d", iovcnt, iov[0].iov_len, strid, conn->cc_socket);
 again:	
 	ret = sendmsg(conn->cc_socket, &mhdr, 0);
@@ -1144,8 +1138,7 @@
 	}
 	
-	CHECK_SYS( ret );
-	ASSERT( ret == len ); /* There should not be partial delivery with sendmsg... */
-	
-	return 0;
+	CHECK_SYS_DO( ret, ); /* for tracing error only */
+	
+	return ret;
 }
 
Index: libfdcore/sctp3436.c
===================================================================
--- libfdcore/sctp3436.c	(revision 1181)
+++ libfdcore/sctp3436.c	(revision 1186)
@@ -52,10 +52,10 @@
  We also have a demux thread that reads the socket and store received data in the appropriate fifo
  
- We have one gnutls_session per stream pair, and as many streams that read the gnutls records and save incoming data to the target queue.
+ We have one gnutls_session per stream pair, and as many threads that read the gnutls records and save incoming data to the target queue.
  
 This complexity is required because we cannot read a socket for a given stream only; we can only get the next message and find its stream.
 */
 
-/* TODO: change this whole wrapper to DTLS which should not require many different threads */
+/* Note that this mechanism is replaced by DTLS in RFC6733 */
 
 /*************************************************************/
@@ -164,16 +164,61 @@
 /*************************************************************/
 
+#ifdef GNUTLS_VERSION_300
+/* Check if data is available for gnutls on a given context */
+static int sctp3436_pull_timeout(gnutls_transport_ptr_t tr, unsigned int ms)
+{
+	struct sctp3436_ctx * ctx = (struct sctp3436_ctx *) tr;
+	struct timespec tsstore, *ts = NULL;
+	int ret;
+	
+	TRACE_ENTRY("%p %d", tr, ms);
+	
+	if (ctx->partial.buf)
+		return 1; /* data is already available for pull */
+	
+	if (ms) {
+		CHECK_SYS_DO(  clock_gettime(CLOCK_REALTIME, &tsstore),  return -1  );
+		tsstore.tv_nsec += (long)ms * 1000000;
+		tsstore.tv_sec += tsstore.tv_nsec / 1000000000L;
+		tsstore.tv_nsec %= 1000000000L;
+		ts = &tsstore;
+	}
+	
+	ret = fd_fifo_select ( ctx->raw_recv, ts );
+	if (ret < 0) {
+		errno = -ret;
+		ret = -1;
+	}
+		
+	return ret;
+}
+#endif /* GNUTLS_VERSION_300 */
+
 /* Send data over the connection, called by gnutls */
+#ifndef GNUTLS_VERSION_212
 static ssize_t sctp3436_push(gnutls_transport_ptr_t tr, const void * data, size_t len)
 {
 	struct sctp3436_ctx * ctx = (struct sctp3436_ctx *) tr;
+	struct iovec iov;
 	
 	TRACE_ENTRY("%p %p %zd", tr, data, len);
 	CHECK_PARAMS_DO( tr && data, { errno = EINVAL; return -1; } );
 	
-	CHECK_FCT_DO( fd_sctp_sendstr(ctx->parent, ctx->strid, (uint8_t *)data, len), return -1 );
-	
-	return len;
-}
+	iov.iov_base = (void *)data;
+	iov.iov_len  = len;
+	
+	return fd_sctp_sendstrv(ctx->parent, ctx->strid, &iov, 1);
+}
+#else /*  GNUTLS_VERSION_212 */
+static ssize_t sctp3436_pushv(gnutls_transport_ptr_t tr, const giovec_t * iov, int iovcnt)
+{
+	struct sctp3436_ctx * ctx = (struct sctp3436_ctx *) tr;
+	
+	TRACE_ENTRY("%p %p %d", tr, iov, iovcnt);
+	CHECK_PARAMS_DO( tr && iov, { errno = EINVAL; return -1; } );
+	
+	return fd_sctp_sendstrv(ctx->parent, ctx->strid, (const struct iovec *)iov, iovcnt);
+}
+#endif /*  GNUTLS_VERSION_212 */
 
 /* Retrieve data received on a stream and already demultiplexed */
@@ -238,9 +283,16 @@
 	/* starting version 2.12, this call is not needed */
 	GNUTLS_TRACE( gnutls_transport_set_lowat( session, 0 ) );
+#else  /* GNUTLS_VERSION_300 */
+	/* but in 3.0 we have to provide the pull_timeout callback */
+	GNUTLS_TRACE( gnutls_transport_set_pull_timeout_function( session, sctp3436_pull_timeout ) );
 #endif /* GNUTLS_VERSION_300 */
 	
 	/* Set the push and pull callbacks */
 	GNUTLS_TRACE( gnutls_transport_set_pull_function(session, sctp3436_pull) );
+#ifndef GNUTLS_VERSION_212
 	GNUTLS_TRACE( gnutls_transport_set_push_function(session, sctp3436_push) );
+#else /* GNUTLS_VERSION_212 */
+	GNUTLS_TRACE( gnutls_transport_set_vec_push_function(session, sctp3436_pushv) );
+#endif /* GNUTLS_VERSION_212 */
 
 	return;
