changeset 1186:56c36d1007b4

Further preparation of the DTLS integration. Some cleanups in the GNUTLS handling.
author Sebastien Decugis <sdecugis@freediameter.net>
date Fri, 07 Jun 2013 18:48:34 +0800
parents 23695957bfc0
children 436e4342ecd0
files cmake/Modules/FindGnuTLS.cmake include/freeDiameter/freeDiameter-host.h.in include/freeDiameter/libfdcore.h include/freeDiameter/libfdproto.h libfdcore/cnxctx.c libfdcore/cnxctx.h libfdcore/fdcore-internal.h libfdcore/hooks.c libfdcore/p_ce.c libfdcore/p_cnx.c libfdcore/p_dp.c libfdcore/p_dw.c libfdcore/p_out.c libfdcore/p_psm.c libfdcore/peers.c libfdcore/routing_dispatch.c libfdcore/sctp.c libfdcore/sctp3436.c libfdproto/fifo.c tests/testcnx.c tests/testsctp.c
diffstat 21 files changed, 363 insertions(+), 125 deletions(-) [+]
line wrap: on
line diff
--- a/cmake/Modules/FindGnuTLS.cmake	Thu Jun 06 19:06:00 2013 +0800
+++ b/cmake/Modules/FindGnuTLS.cmake	Fri Jun 07 18:48:34 2013 +0800
@@ -5,6 +5,7 @@
 #  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)
 
@@ -47,12 +48,15 @@
     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)
     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) 
     CHECK_LIBRARY_EXISTS(gnutls gnutls_handshake_set_timeout ${GNUTLS_PATH} GNUTLS_VERSION_310) 
     SET( GNUTLS_VERSION_TEST_FOR ${GNUTLS_LIBRARY} CACHE INTERNAL "Version the test was made against" )
--- a/include/freeDiameter/freeDiameter-host.h.in	Thu Jun 06 19:06:00 2013 +0800
+++ b/include/freeDiameter/freeDiameter-host.h.in	Fri Jun 07 18:48:34 2013 +0800
@@ -61,6 +61,7 @@
 #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
 
--- a/include/freeDiameter/libfdcore.h	Thu Jun 06 19:06:00 2013 +0800
+++ b/include/freeDiameter/libfdcore.h	Fri Jun 07 18:48:34 2013 +0800
@@ -398,6 +398,23 @@
 int fd_peer_get_state(struct peer_hdr *peer);
 
 /* 
+ * FUNCTION:	fd_peer_cnx_proto_info
+ *
+ * PARAMETERS:
+ *  peer	: The peer which information to be read
+ *  buf		: Where to store the protocol information
+ *  len		: available space in bug
+ *
+ * DESCRIPTION: 
+ *   Creates a string describing the current connection to this peer, e.g.: "TCP,TLS,soc#3".
+ *
+ * RETURN VALUE:
+ *  0   : buffer was written
+ * >=0	: error code.
+ */
+int fd_peer_cnx_proto_info(struct peer_hdr *peer, char * buf, size_t len);
+
+/* 
  * FUNCTION:	fd_peer_get_load_pending
  *
  * PARAMETERS:
--- a/include/freeDiameter/libfdproto.h	Thu Jun 06 19:06:00 2013 +0800
+++ b/include/freeDiameter/libfdproto.h	Fri Jun 07 18:48:34 2013 +0800
@@ -3183,6 +3183,28 @@
 #define fd_fifo_timedget(queue, item, abstime) \
 	fd_fifo_timedget_int((queue), (void *)(item), (abstime))
 
+
+/*
+ * FUNCTION:	fd_fifo_select
+ *
+ * PARAMETERS:
+ *  queue	: The queue to test.
+ *  abstime	: the absolute time until which we can block waiting for an item. If NULL, the function returns immediatly.
+ *
+ * DESCRIPTION: 
+ *  This function is similar to select(), it waits for data to be available in the queue
+ * until the abstime is expired.
+ * Upon function entry, even if abstime is already expired the data availability is tested.
+ *
+ * RETURN VALUE:
+ *  0		: timeout expired without available data.
+ *  <0		: An error occurred (e.g., -EINVAL...)
+ *  >0		: data is available. The next call to fd_fifo_get will not block.
+ */
+int fd_fifo_select ( struct fifo * queue, const struct timespec *abstime );
+
+
+
 /* Dump a fifo list and optionally its inner elements -- beware of deadlocks! */
 typedef DECLARE_FD_DUMP_PROTOTYPE((*fd_fifo_dump_item_cb), void * item); /* This function should be 1 line if possible, or use indent level. Ends with '\n' */
 DECLARE_FD_DUMP_PROTOTYPE(fd_fifo_dump, char * name, struct fifo * queue, fd_fifo_dump_item_cb dump_item);
--- a/libfdcore/cnxctx.c	Thu Jun 06 19:06:00 2013 +0800
+++ b/libfdcore/cnxctx.c	Fri Jun 07 18:48:34 2013 +0800
@@ -38,6 +38,7 @@
 
 #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 */
 #ifndef DIAMETER_MSG_SIZE_MAX
@@ -478,13 +479,21 @@
 	return fd_cnx_teststate(conn, CC_STATUS_TLS);
 }
 
+/* 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;
 }
@@ -530,6 +539,22 @@
 	return conn->cc_remid;
 }
 
+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,6 +625,24 @@
 	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 */
 ssize_t fd_cnx_s_recv(struct cnxctx * conn, void *buffer, size_t length)
 {
@@ -627,8 +670,9 @@
 	return ret;
 }
 
-/* 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;
 	int timedout = 0;
@@ -652,6 +696,33 @@
 	
 	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))) {
+		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;
+}
 
 #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) )
@@ -1604,8 +1675,15 @@
 
 		/* 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");
 			return ENOTSUP;
@@ -1783,8 +1861,10 @@
 		if (fd_cnx_teststate(conn, CC_STATUS_TLS)) {
 			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)
 			return ENOTCONN;
@@ -1795,9 +1875,9 @@
 }
 
 /* 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)
+int fd_cnx_send(struct cnxctx * conn, unsigned char * buf, size_t len)
 {
-	TRACE_ENTRY("%p %p %zd %x", conn, buf, len, flags);
+	TRACE_ENTRY("%p %p %zd", conn, buf, len);
 	
 	CHECK_PARAMS(conn && (conn->cc_socket > 0) && (! fd_cnx_teststate(conn, CC_STATUS_ERROR)) && buf && len);
 
@@ -1811,48 +1891,50 @@
 #ifndef DISABLE_SCTP
 		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;
+						/* 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 );
-						}
+							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) );
 			}
 		}
 		break;
--- a/libfdcore/cnxctx.h	Thu Jun 06 19:06:00 2013 +0800
+++ b/libfdcore/cnxctx.h	Fri Jun 07 18:48:34 2013 +0800
@@ -74,6 +74,7 @@
 		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 */
+		int	 unordered;	/* boolean telling if use of streams > 0 is permitted */
 	} 		cc_sctp_para;
 
 	/* If both conditions */
@@ -117,7 +118,7 @@
 int fd_sctp_get_local_ep(int sock,  struct fd_list * list);
 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);
 
 /* TLS over SCTP (multi-stream) */
--- a/libfdcore/fdcore-internal.h	Thu Jun 06 19:06:00 2013 +0800
+++ b/libfdcore/fdcore-internal.h	Fri Jun 07 18:48:34 2013 +0800
@@ -295,7 +295,7 @@
 void fd_psm_cleanup(struct fd_peer * peer, int terminate);
 
 /* 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);
 
@@ -341,28 +341,27 @@
 struct cnxctx * fd_cnx_cli_connect_sctp(int no_ip6, uint16_t port, struct fd_list * list);
 int             fd_cnx_start_clear(struct cnxctx * conn, int loop);
 void		fd_cnx_sethostname(struct cnxctx * conn, DiamId_t hn);
+int		fd_cnx_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 */
 int             fd_cnx_handshake(struct cnxctx * conn, int mode, int algo, char * priority, void * alt_creds);
 char *          fd_cnx_getid(struct cnxctx * conn);
 int		fd_cnx_getproto(struct cnxctx * conn);
 int		fd_cnx_getTLS(struct cnxctx * conn);
-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);
 int             fd_cnx_getremoteeps(struct cnxctx * conn, struct fd_list * eps);
 char *          fd_cnx_getremoteid(struct cnxctx * conn);
 int             fd_cnx_receive(struct cnxctx * conn, struct timespec * timeout, unsigned char **buf, size_t * len);
 int             fd_cnx_recv_setaltfifo(struct cnxctx * conn, struct fifo * alt_fifo); /* send FDEVP_CNX_MSG_RECV event to the fifo list */
-int             fd_cnx_send(struct cnxctx * conn, unsigned char * buf, size_t len, 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 */
 void   fd_hook_call(enum fd_hook_type type, struct msg * msg, struct fd_peer * peer, void * other, struct fd_msg_pmdl * pmdl);
 void   fd_hook_associate(struct msg * msg, struct fd_msg_pmdl * pmdl);
--- a/libfdcore/hooks.c	Thu Jun 06 19:06:00 2013 +0800
+++ b/libfdcore/hooks.c	Fri Jun 07 18:48:34 2013 +0800
@@ -419,7 +419,14 @@
 					fd_msg_answ_getq(msg, &msg); /* We dump the CER in that case */
 				}
 				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;
 			}
--- a/libfdcore/p_ce.c	Thu Jun 06 19:06:00 2013 +0800
+++ b/libfdcore/p_ce.c	Fri Jun 07 18:48:34 2013 +0800
@@ -632,7 +632,7 @@
 	/* Create and send the CEA with appropriate error code */
 	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 */
 destroy:
@@ -652,7 +652,7 @@
 	
 	/* 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 ? */
 	if (fd_peer_getstate(peer) == STATE_WAITCNXACK_ELEC) {
@@ -707,7 +707,7 @@
 		CHECK_FCT( fd_msg_rescode_set(*msg, "DIAMETER_UNABLE_TO_COMPLY", "No CER allowed in current state", NULL, 1 ) );
 
 		/* 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 */ );
 	}
 	
 	/* If the state is not WAITCEA, just discard the message */
@@ -949,7 +949,7 @@
 	/* The connection is complete, but we may still need TLS handshake */
 	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 */
 	if (isi & PI_SEC_TLS_OLD) {
@@ -993,7 +993,7 @@
 		fd_psm_change_state(peer, STATE_REOPEN );
 		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 */
 			CHECK_FCT( fd_p_dw_timeout(peer) );
--- a/libfdcore/p_cnx.c	Thu Jun 06 19:06:00 2013 +0800
+++ b/libfdcore/p_cnx.c	Fri Jun 07 18:48:34 2013 +0800
@@ -345,3 +345,4 @@
 		empty_connection_list(peer);
 	}
 }
+
--- a/libfdcore/p_dp.c	Thu Jun 06 19:06:00 2013 +0800
+++ b/libfdcore/p_dp.c	Fri Jun 07 18:48:34 2013 +0800
@@ -117,7 +117,7 @@
 			CHECK_FCT( fd_psm_change_state(peer, STATE_CLOSING) );
 		
 			/* 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 */
 			fd_psm_cleanup(peer, 0);
@@ -131,7 +131,7 @@
 			fd_psm_next_timeout(peer, 0, GRACE_TIMEOUT);
 			
 			/* Now send the DPA */
-			CHECK_FCT( fd_out_send( msg, NULL, peer, FD_CNX_ORDERED) );
+			CHECK_FCT( fd_out_send( msg, NULL, peer) );
 		}
 	} else {
 		/* We received a DPA */
@@ -201,7 +201,7 @@
 	fd_psm_next_timeout(peer, 0, DPR_TIMEOUT);
 	
 	/* 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;
 }
--- a/libfdcore/p_dw.c	Thu Jun 06 19:06:00 2013 +0800
+++ b/libfdcore/p_dw.c	Fri Jun 07 18:48:34 2013 +0800
@@ -82,7 +82,7 @@
 	CHECK_FCT( fd_msg_add_origin ( msg, 1 ) );
 	
 	/* 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 */
 	peer->p_flags.pf_dw_pending = 1;
@@ -105,7 +105,7 @@
 		CHECK_FCT( fd_msg_new_answer_from_req ( fd_g_config->cnf_dict, msg, 0 ) );
 		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 {
 		/* Discard the DWA */
--- a/libfdcore/p_out.c	Thu Jun 06 19:06:00 2013 +0800
+++ b/libfdcore/p_out.c	Fri Jun 07 18:48:34 2013 +0800
@@ -36,7 +36,7 @@
 #include "fdcore-internal.h"
 
 /* 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;
 	int msg_is_a_req;
@@ -46,7 +46,7 @@
 	uint32_t bkp_hbh = 0;
 	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 */
 	CHECK_FCT( fd_msg_hdr(*msg, &hdr) );
@@ -75,7 +75,7 @@
 	fd_hook_call(HOOK_MESSAGE_SENT, cpy_for_logs_only, peer, NULL, fd_msg_pmdl_get(cpy_for_logs_only));
 	
 	/* 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:
 	;	
 	pthread_cleanup_pop(1);
@@ -127,7 +127,7 @@
 		pthread_cleanup_push(cleanup_requeue, msg);
 		
 		/* 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) {
 					char buf[256];
@@ -148,11 +148,11 @@
 }
 
 /* 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)));
 	
 	if (peer) {
@@ -181,7 +181,7 @@
 			cnx = peer->p_cnxctx;
 
 		/* 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) {
 					char buf[256];
@@ -204,6 +204,8 @@
 	
 	CHECK_POSIX( pthread_create(&peer->p_outthr, NULL, out_thr, peer) );
 	
+	CHECK_FCT( fd_cnx_unordered_delivery(peer->p_cnxctx, 1) );
+	
 	return 0;
 }
 
@@ -213,6 +215,8 @@
 	TRACE_ENTRY("%p", peer);
 	CHECK_PARAMS( CHECK_PEER(peer) );
 	
+	CHECK_FCT( fd_cnx_unordered_delivery(peer->p_cnxctx, 0) );
+	
 	CHECK_FCT( fd_thr_term(&peer->p_outthr) );
 	
 	return 0;
--- a/libfdcore/p_psm.c	Thu Jun 06 19:06:00 2013 +0800
+++ b/libfdcore/p_psm.c	Fri Jun 07 18:48:34 2013 +0800
@@ -71,7 +71,7 @@
 
 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).
 
 This proposed mechanism removes the problem of application messages
@@ -600,7 +600,7 @@
 			} else {
 				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];
 						/* Only if an error occurred & the message was not saved / dumped */
@@ -654,7 +654,7 @@
 						CHECK_FCT_DO( fd_msg_rescode_set(msg, "DIAMETER_COMMAND_UNSUPPORTED", "Or maybe the P-bit or application Id are erroneous.", NULL, 1 ), break );
 
 						/* 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 {
 					/* We did ASK for it ??? */
--- a/libfdcore/peers.c	Thu Jun 06 19:06:00 2013 +0800
+++ b/libfdcore/peers.c	Fri Jun 07 18:48:34 2013 +0800
@@ -257,6 +257,24 @@
 	return;
 }
 
+/* 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)
 {
@@ -511,7 +529,7 @@
 		
 		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;
 	}
 	
--- a/libfdcore/routing_dispatch.c	Thu Jun 06 19:06:00 2013 +0800
+++ b/libfdcore/routing_dispatch.c	Fri Jun 07 18:48:34 2013 +0800
@@ -412,7 +412,7 @@
 	if (is_loc) {
 		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) );
 	}
 	
 	/* Done */
@@ -886,7 +886,7 @@
 		hdr->msg_hbhid = qry_hdr->msg_hbhid;
 
 		/* 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 */
 		return 0;
@@ -1003,7 +1003,7 @@
 
 		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 */
 			break;
--- a/libfdcore/sctp.c	Thu Jun 06 19:06:00 2013 +0800
+++ b/libfdcore/sctp.c	Fri Jun 07 18:48:34 2013 +0800
@@ -1079,11 +1079,10 @@
 	return 0;
 }
 
-/* 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
 	struct sctp_sndrcvinfo	*sndrcv;
@@ -1095,17 +1094,12 @@
 	ssize_t ret;
 	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 */
 	hdr = (struct cmsghdr *)anci;
 	hdr->cmsg_len   = sizeof(anci);
@@ -1123,13 +1117,13 @@
 	
 	/* 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);
 	/* Handle special case of timeout */
@@ -1143,10 +1137,9 @@
 		}
 	}
 	
-	CHECK_SYS( ret );
-	ASSERT( ret == len ); /* There should not be partial delivery with sendmsg... */
+	CHECK_SYS_DO( ret, ); /* for tracing error only */
 	
-	return 0;
+	return ret;
 }
 
 /* Receive the next data from the socket, or next notification */
--- a/libfdcore/sctp3436.c	Thu Jun 06 19:06:00 2013 +0800
+++ b/libfdcore/sctp3436.c	Fri Jun 07 18:48:34 2013 +0800
@@ -51,12 +51,12 @@
  - the push function sends the data on a certain stream.
  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 */
 
 /*************************************************************/
 /*                      threads                              */
@@ -163,18 +163,63 @@
 /*                     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)
+{
+	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 );
+	iov.iov_base = (void *)data;
+	iov.iov_len  = len;
 	
-	return 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 */
 static ssize_t sctp3436_pull(gnutls_transport_ptr_t tr, void * buf, size_t len)
@@ -237,11 +282,18 @@
 #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 */
+	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;
 }
--- a/libfdproto/fifo.c	Thu Jun 06 19:06:00 2013 +0800
+++ b/libfdproto/fifo.c	Fri Jun 07 18:48:34 2013 +0800
@@ -645,3 +645,37 @@
 	return fifo_tget(queue, item, 1, abstime);
 }
 
+/* Test if data is available in the queue, without pulling it */
+int fd_fifo_select ( struct fifo * queue, const struct timespec *abstime )
+{
+	int ret = 0;
+	TRACE_ENTRY( "%p %p", queue, abstime );
+	
+	CHECK_PARAMS_DO( CHECK_FIFO( queue ), return -EINVAL );
+	
+	/* lock the queue */
+	CHECK_POSIX_DO(  pthread_mutex_lock( &queue->mtx ), return -__ret__  );
+	
+awaken:	
+	ret = (queue->count > 0 ) ? queue->count : 0;
+	if ((ret == 0) && (abstime != NULL)) {
+		/* We have to wait for a new item */
+		queue->thrs++ ;
+		pthread_cleanup_push( fifo_cleanup, queue);
+		ret = pthread_cond_timedwait( &queue->cond_pull, &queue->mtx, abstime );
+		pthread_cleanup_pop(0);
+		queue->thrs-- ;
+		if (ret == 0)
+			goto awaken;  /* test for spurious wake-ups */
+		
+		if (ret == ETIMEDOUT)
+			ret = 0;
+		else 
+			ret = -ret;
+	}
+	
+	/* Unlock */
+	CHECK_POSIX_DO(  pthread_mutex_unlock( &queue->mtx ), return -__ret__  );
+	
+	return ret;
+}
--- a/tests/testcnx.c	Thu Jun 06 19:06:00 2013 +0800
+++ b/tests/testcnx.c	Fri Jun 07 18:48:34 2013 +0800
@@ -743,14 +743,14 @@
 		CHECK( 0, fd_cnx_start_clear(client_side, 0) );
 		
 		/* Send a message and receive it */
-		CHECK( 0, fd_cnx_send(server_side, cer_buf, cer_sz, 0));
+		CHECK( 0, fd_cnx_send(server_side, cer_buf, cer_sz));
 		CHECK( 0, fd_cnx_receive(client_side, NULL, &rcv_buf, &rcv_sz));
 		CHECK( cer_sz, rcv_sz );
 		CHECK( 0, memcmp( rcv_buf, cer_buf, cer_sz ) );
 		free(rcv_buf);
 		
 		/* Do it in the other direction */
-		CHECK( 0, fd_cnx_send(client_side, cer_buf, cer_sz, 0));
+		CHECK( 0, fd_cnx_send(client_side, cer_buf, cer_sz));
 		CHECK( 0, fd_cnx_receive(server_side, NULL, &rcv_buf, &rcv_sz));
 		CHECK( cer_sz, rcv_sz );
 		CHECK( 0, memcmp( rcv_buf, cer_buf, cer_sz ) );
@@ -783,7 +783,7 @@
 		CHECK( 0, fd_cnx_start_clear(server_side, 1) );
 		
 		/* Send a message and receive it */
-		CHECK( 0, fd_cnx_send(server_side, cer_buf, cer_sz, 0));
+		CHECK( 0, fd_cnx_send(server_side, cer_buf, cer_sz));
 		CHECK( EINVAL, fd_cnx_receive(client_side, NULL, &rcv_buf, &rcv_sz));
 		CHECK( 0, fd_cnx_start_clear(client_side, 0) );
 		CHECK( 0, fd_cnx_receive(client_side, NULL, &rcv_buf, &rcv_sz));
@@ -792,14 +792,14 @@
 		free(rcv_buf);
 		
 		/* Do it in the other direction */
-		CHECK( 0, fd_cnx_send(client_side, cer_buf, cer_sz, 0));
+		CHECK( 0, fd_cnx_send(client_side, cer_buf, cer_sz));
 		CHECK( 0, fd_cnx_receive(server_side, NULL, &rcv_buf, &rcv_sz));
 		CHECK( cer_sz, rcv_sz );
 		CHECK( 0, memcmp( rcv_buf, cer_buf, cer_sz ) );
 		free(rcv_buf);
 		
 		/* Do it one more time to use another stream */
-		CHECK( 0, fd_cnx_send(client_side, cer_buf, cer_sz, 0));
+		CHECK( 0, fd_cnx_send(client_side, cer_buf, cer_sz));
 		CHECK( 0, fd_cnx_receive(server_side, NULL, &rcv_buf, &rcv_sz));
 		CHECK( cer_sz, rcv_sz );
 		CHECK( 0, memcmp( rcv_buf, cer_buf, cer_sz ) );
@@ -849,14 +849,14 @@
 		CHECK( 0, fd_cnx_start_clear(client_side, 0) );
 		
 		/* Send a message and receive it */
-		CHECK( 0, fd_cnx_send(server_side, cer_buf, cer_sz, 0));
+		CHECK( 0, fd_cnx_send(server_side, cer_buf, cer_sz));
 		CHECK( 0, fd_cnx_receive(client_side, NULL, &rcv_buf, &rcv_sz));
 		CHECK( cer_sz, rcv_sz );
 		CHECK( 0, memcmp( rcv_buf, cer_buf, cer_sz ) );
 		free(rcv_buf);
 		
 		/* And the supposed reply */
-		CHECK( 0, fd_cnx_send(client_side, cer_buf, cer_sz, 0));
+		CHECK( 0, fd_cnx_send(client_side, cer_buf, cer_sz));
 		CHECK( 0, fd_cnx_receive(server_side, NULL, &rcv_buf, &rcv_sz));
 		CHECK( cer_sz, rcv_sz );
 		CHECK( 0, memcmp( rcv_buf, cer_buf, cer_sz ) );
@@ -870,13 +870,13 @@
 		
 		/* Send a few TLS protected message, and replies */
 		for (i = 0; i < 2 * NB_STREAMS; i++) {
-			CHECK( 0, fd_cnx_send(server_side, cer_buf, cer_sz, 0));
+			CHECK( 0, fd_cnx_send(server_side, cer_buf, cer_sz));
 			CHECK( 0, fd_cnx_receive(client_side, NULL, &rcv_buf, &rcv_sz));
 			CHECK( cer_sz, rcv_sz );
 			CHECK( 0, memcmp( rcv_buf, cer_buf, cer_sz ) );
 			free(rcv_buf);
 
-			CHECK( 0, fd_cnx_send(client_side, cer_buf, cer_sz, 0));
+			CHECK( 0, fd_cnx_send(client_side, cer_buf, cer_sz));
 			CHECK( 0, fd_cnx_receive(server_side, NULL, &rcv_buf, &rcv_sz));
 			CHECK( cer_sz, rcv_sz );
 			CHECK( 0, memcmp( rcv_buf, cer_buf, cer_sz ) );
@@ -935,14 +935,14 @@
 		CHECK( 0, fd_cnx_start_clear(client_side, 0) );
 		
 		/* Send a message and receive it */
-		CHECK( 0, fd_cnx_send(server_side, cer_buf, cer_sz, 0));
+		CHECK( 0, fd_cnx_send(server_side, cer_buf, cer_sz));
 		CHECK( 0, fd_cnx_receive(client_side, NULL, &rcv_buf, &rcv_sz));
 		CHECK( cer_sz, rcv_sz );
 		CHECK( 0, memcmp( rcv_buf, cer_buf, cer_sz ) );
 		free(rcv_buf);
 		
 		/* And the supposed reply */
-		CHECK( 0, fd_cnx_send(client_side, cer_buf, cer_sz, 0));
+		CHECK( 0, fd_cnx_send(client_side, cer_buf, cer_sz));
 		CHECK( 0, fd_cnx_receive(server_side, NULL, &rcv_buf, &rcv_sz));
 		CHECK( cer_sz, rcv_sz );
 		CHECK( 0, memcmp( rcv_buf, cer_buf, cer_sz ) );
@@ -956,13 +956,13 @@
 		
 		/* Send a few TLS protected message, and replies */
 		for (i = 0; i < 2 * NB_STREAMS; i++) {
-			CHECK( 0, fd_cnx_send(server_side, cer_buf, cer_sz, 0));
+			CHECK( 0, fd_cnx_send(server_side, cer_buf, cer_sz));
 			CHECK( 0, fd_cnx_receive(client_side, NULL, &rcv_buf, &rcv_sz));
 			CHECK( cer_sz, rcv_sz );
 			CHECK( 0, memcmp( rcv_buf, cer_buf, cer_sz ) );
 			free(rcv_buf);
 
-			CHECK( 0, fd_cnx_send(client_side, cer_buf, cer_sz, 0));
+			CHECK( 0, fd_cnx_send(client_side, cer_buf, cer_sz));
 			CHECK( 0, fd_cnx_receive(server_side, NULL, &rcv_buf, &rcv_sz));
 			CHECK( cer_sz, rcv_sz );
 			CHECK( 0, memcmp( rcv_buf, cer_buf, cer_sz ) );
@@ -1022,13 +1022,13 @@
 		
 		/* Send a few TLS protected message, and replies */
 		for (i = 0; i < 2 * NB_STREAMS; i++) {
-			CHECK( 0, fd_cnx_send(server_side, cer_buf, cer_sz, 0));
+			CHECK( 0, fd_cnx_send(server_side, cer_buf, cer_sz));
 			CHECK( 0, fd_cnx_receive(client_side, NULL, &rcv_buf, &rcv_sz));
 			CHECK( cer_sz, rcv_sz );
 			CHECK( 0, memcmp( rcv_buf, cer_buf, cer_sz ) );
 			free(rcv_buf);
 
-			CHECK( 0, fd_cnx_send(client_side, cer_buf, cer_sz, 0));
+			CHECK( 0, fd_cnx_send(client_side, cer_buf, cer_sz));
 			CHECK( 0, fd_cnx_receive(server_side, NULL, &rcv_buf, &rcv_sz));
 			CHECK( cer_sz, rcv_sz );
 			CHECK( 0, memcmp( rcv_buf, cer_buf, cer_sz ) );
@@ -1091,13 +1091,13 @@
 		
 		/* Send a few TLS protected messages, and replies */
 		for (i = 0; i < 2 * NB_STREAMS; i++) {
-			CHECK( 0, fd_cnx_send(server_side, cer_buf, cer_sz, 0));
+			CHECK( 0, fd_cnx_send(server_side, cer_buf, cer_sz));
 			CHECK( 0, fd_cnx_receive(client_side, NULL, &rcv_buf, &rcv_sz));
 			CHECK( cer_sz, rcv_sz );
 			CHECK( 0, memcmp( rcv_buf, cer_buf, cer_sz ) );
 			free(rcv_buf);
 
-			CHECK( 0, fd_cnx_send(client_side, cer_buf, cer_sz, 0));
+			CHECK( 0, fd_cnx_send(client_side, cer_buf, cer_sz));
 			CHECK( 0, fd_cnx_receive(server_side, NULL, &rcv_buf, &rcv_sz));
 			CHECK( cer_sz, rcv_sz );
 			CHECK( 0, memcmp( rcv_buf, cer_buf, cer_sz ) );
@@ -1157,13 +1157,13 @@
 		
 		/* Send a few TLS protected messages, and replies */
 		for (i = 0; i < 2 * NB_STREAMS; i++) {
-			CHECK( 0, fd_cnx_send(server_side, cer_buf, cer_sz, 0));
+			CHECK( 0, fd_cnx_send(server_side, cer_buf, cer_sz));
 			CHECK( 0, fd_cnx_receive(client_side, NULL, &rcv_buf, &rcv_sz));
 			CHECK( cer_sz, rcv_sz );
 			CHECK( 0, memcmp( rcv_buf, cer_buf, cer_sz ) );
 			free(rcv_buf);
 
-			CHECK( 0, fd_cnx_send(client_side, cer_buf, cer_sz, 0));
+			CHECK( 0, fd_cnx_send(client_side, cer_buf, cer_sz));
 			CHECK( 0, fd_cnx_receive(server_side, NULL, &rcv_buf, &rcv_sz));
 			CHECK( cer_sz, rcv_sz );
 			CHECK( 0, memcmp( rcv_buf, cer_buf, cer_sz ) );
@@ -1228,13 +1228,13 @@
 		
 		/* Send a few TLS protected message, and replies */
 		for (i = 0; i < 4 * NB_STREAMS; i++) {
-			CHECK( 0, fd_cnx_send(server_side, cer_buf, cer_sz, 0));
+			CHECK( 0, fd_cnx_send(server_side, cer_buf, cer_sz));
 			CHECK( 0, fd_cnx_receive(client_side, NULL, &rcv_buf, &rcv_sz));
 			CHECK( cer_sz, rcv_sz );
 			CHECK( 0, memcmp( rcv_buf, cer_buf, cer_sz ) );
 			free(rcv_buf);
 
-			CHECK( 0, fd_cnx_send(client_side, cer_buf, cer_sz, 0));
+			CHECK( 0, fd_cnx_send(client_side, cer_buf, cer_sz));
 			CHECK( 0, fd_cnx_receive(server_side, NULL, &rcv_buf, &rcv_sz));
 			CHECK( cer_sz, rcv_sz );
 			CHECK( 0, memcmp( rcv_buf, cer_buf, cer_sz ) );
@@ -1267,13 +1267,13 @@
 		
 		/* Send a few TLS protected message, and replies */
 		for (i = 0; i < 2 * NB_STREAMS; i++) {
-			CHECK( 0, fd_cnx_send(server_side, cer_buf, cer_sz, 0));
+			CHECK( 0, fd_cnx_send(server_side, cer_buf, cer_sz));
 			CHECK( 0, fd_cnx_receive(client_side, NULL, &rcv_buf, &rcv_sz));
 			CHECK( cer_sz, rcv_sz );
 			CHECK( 0, memcmp( rcv_buf, cer_buf, cer_sz ) );
 			free(rcv_buf);
 
-			CHECK( 0, fd_cnx_send(client_side, cer_buf, cer_sz, 0));
+			CHECK( 0, fd_cnx_send(client_side, cer_buf, cer_sz));
 			CHECK( 0, fd_cnx_receive(server_side, NULL, &rcv_buf, &rcv_sz));
 			CHECK( cer_sz, rcv_sz );
 			CHECK( 0, memcmp( rcv_buf, cer_buf, cer_sz ) );
@@ -1334,13 +1334,13 @@
 		
 		/* Send a few TLS protected message, and replies */
 		for (i = 0; i < 4 * NB_STREAMS; i++) {
-			CHECK( 0, fd_cnx_send(server_side, cer_buf, cer_sz, 0));
+			CHECK( 0, fd_cnx_send(server_side, cer_buf, cer_sz));
 			CHECK( 0, fd_cnx_receive(client_side, NULL, &rcv_buf, &rcv_sz));
 			CHECK( cer_sz, rcv_sz );
 			CHECK( 0, memcmp( rcv_buf, cer_buf, cer_sz ) );
 			free(rcv_buf);
 
-			CHECK( 0, fd_cnx_send(client_side, cer_buf, cer_sz, 0));
+			CHECK( 0, fd_cnx_send(client_side, cer_buf, cer_sz));
 			CHECK( 0, fd_cnx_receive(server_side, NULL, &rcv_buf, &rcv_sz));
 			CHECK( cer_sz, rcv_sz );
 			CHECK( 0, memcmp( rcv_buf, cer_buf, cer_sz ) );
@@ -1373,13 +1373,13 @@
 		
 		/* Send a few TLS protected message, and replies */
 		for (i = 0; i < 2 * NB_STREAMS; i++) {
-			CHECK( 0, fd_cnx_send(server_side, cer_buf, cer_sz, 0));
+			CHECK( 0, fd_cnx_send(server_side, cer_buf, cer_sz));
 			CHECK( 0, fd_cnx_receive(client_side, NULL, &rcv_buf, &rcv_sz));
 			CHECK( cer_sz, rcv_sz );
 			CHECK( 0, memcmp( rcv_buf, cer_buf, cer_sz ) );
 			free(rcv_buf);
 
-			CHECK( 0, fd_cnx_send(client_side, cer_buf, cer_sz, 0));
+			CHECK( 0, fd_cnx_send(client_side, cer_buf, cer_sz));
 			CHECK( 0, fd_cnx_receive(server_side, NULL, &rcv_buf, &rcv_sz));
 			CHECK( cer_sz, rcv_sz );
 			CHECK( 0, memcmp( rcv_buf, cer_buf, cer_sz ) );
@@ -1753,7 +1753,7 @@
 		CHECK( 1, (str[0] != '\0') ? 1 : 0 );
 		
 		/* fd_cnx_recv_setaltfifo */
-		CHECK( 0, fd_cnx_send(client_side, cer_buf, cer_sz, 0));
+		CHECK( 0, fd_cnx_send(client_side, cer_buf, cer_sz));
 		CHECK( 0, fd_fifo_new(&myfifo, 0) );
 		CHECK( 0, fd_cnx_recv_setaltfifo(server_side, myfifo) );
 		CHECK( 0, clock_gettime(CLOCK_REALTIME, &now) );
@@ -1848,7 +1848,7 @@
 		CHECK( 1, (str[0] != '\0') ? 1 : 0 );
 		
 		/* fd_cnx_recv_setaltfifo */
-		CHECK( 0, fd_cnx_send(client_side, cer_buf, cer_sz, 0));
+		CHECK( 0, fd_cnx_send(client_side, cer_buf, cer_sz));
 		CHECK( 0, fd_fifo_new(&myfifo, 0) );
 		CHECK( 0, fd_cnx_recv_setaltfifo(server_side, myfifo) );
 		CHECK( 0, clock_gettime(CLOCK_REALTIME, &now) );
@@ -1941,7 +1941,7 @@
 		CHECK( 1, (str[0] != '\0') ? 1 : 0 );
 		
 		/* fd_cnx_recv_setaltfifo */
-		CHECK( 0, fd_cnx_send(client_side, cer_buf, cer_sz, 0));
+		CHECK( 0, fd_cnx_send(client_side, cer_buf, cer_sz));
 		CHECK( 0, fd_fifo_new(&myfifo, 0) );
 		CHECK( 0, fd_cnx_recv_setaltfifo(server_side, myfifo) );
 		CHECK( 0, clock_gettime(CLOCK_REALTIME, &now) );
--- a/tests/testsctp.c	Thu Jun 06 19:06:00 2013 +0800
+++ b/tests/testsctp.c	Fri Jun 07 18:48:34 2013 +0800
@@ -60,6 +60,7 @@
 	char buf1[]="abcdef";
 	char *buf2;
 	size_t sz;
+	struct iovec iov;
 	struct fd_list eps = FD_LIST_INITIALIZER(eps);
 	uint16_t str;
 	int ev;
@@ -103,7 +104,9 @@
 	srv.cc_socket = accept(sock, NULL, NULL);
 	
 	/* Send a first message */
-	CHECK( 0, fd_sctp_sendstr(&srv, 1, (uint8_t *)buf1, sizeof(buf1) ) );
+	iov.iov_base = buf1;
+	iov.iov_len = sizeof(buf1);
+	CHECK( sizeof(buf1), fd_sctp_sendstrv(&srv, 1, &iov, 1 ) );
 	CHECK( 0, srv.cc_state);
 	
 	/* Receive this message */
@@ -119,7 +122,7 @@
 	free(buf2); buf2 = NULL;
 	
 	/* Send in the other direction */
-	CHECK( 0, fd_sctp_sendstr(&cli, 2, (uint8_t *)buf1, sizeof(buf1)) );
+	CHECK( sizeof(buf1), fd_sctp_sendstrv(&cli, 2, &iov, 1) );
 	CHECK( 0, cli.cc_state);
 	
 	/* Receive this message */
"Welcome to our mercurial repository"