changeset 127:6e655f5ae656

Wrapper functions and test program for doing TLS over SCTP.
author Sebastien Decugis <sdecugis@nict.go.jp>
date Fri, 15 Aug 2008 17:53:04 +0900
parents b82a482f0ed8
children 21a5b37c6f35
files extensions/sec_tls_gnutls/Makefile.am extensions/sec_tls_gnutls/gnutls_sctp_wrapper.c extensions/sec_tls_gnutls/gnutls_sctp_wrapper.h extensions/sec_tls_gnutls/sec_tls_gnutls.h
diffstat 4 files changed, 2665 insertions(+), 1 deletions(-) [+]
line wrap: on
line diff
--- a/extensions/sec_tls_gnutls/Makefile.am	Fri Aug 15 17:52:26 2008 +0900
+++ b/extensions/sec_tls_gnutls/Makefile.am	Fri Aug 15 17:53:04 2008 +0900
@@ -4,9 +4,20 @@
 AM_CPPFLAGS = -I$(srcdir)/../../include
 
 pkglib_LTLIBRARIES = sec_tls_gnutls.la
-sec_tls_gnutls_la_LDFLAGS = -module $(GNUTLS)
+sec_tls_gnutls_la_LDFLAGS = -module $(LIBGNUTLS)
 
 sec_tls_gnutls_la_SOURCES = sec_tls_gnutls.c	\
+				gnutls_sctp_wrapper.c \
 				todo.c
 
+check_PROGRAMS = sctp_tls
+TESTS = $(check_PROGRAMS)
 
+# Standalone test program for TLS over SCTP
+sctp_tls_SOURCES  = gnutls_sctp_wrapper.c
+# sctp_tls_CPPFLAGS = -DSTANDALONE_WRAPPER -DDEBUG_SCTP
+sctp_tls_CPPFLAGS = -DSTANDALONE_WRAPPER
+sctp_tls_LDFLAGS  = $(LIBGNUTLS)
+
+# For development, it is convernient to have this program built also:
+noinst_PROGRAMS = sctp_tls
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/extensions/sec_tls_gnutls/gnutls_sctp_wrapper.c	Fri Aug 15 17:53:04 2008 +0900
@@ -0,0 +1,2332 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@nict.go.jp>							 *
+*													 *
+* Copyright (c) 2008, 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.								 *
+*********************************************************************************************************/
+
+
+/*
+ *  Here are some design insights on the wrapper layer:
+ *
+ *  One thread (demux) is actually receiving messages and notifications from the socket object.
+ * When a message is received, along with the stream it is received from, it is saved 
+ * in the buffer queue (FIFO dynamic lists of buffers) corresponding to this stream.
+ * 
+ *  The "pull" callback that the gnutls library expects for transport will actually pull from the
+ * buffer queue. Conditional variables are used to block and wakeup when messages are received on
+ * the appropriate stream.
+ *  
+ *  The "push" callback is more straight-forward.
+ *
+ *  Once the handshake is complete, one thread per stream is created and looping on the 
+ * gnutls_record_recv function, using the appropriate gnutls session. When a message is
+ * received successfuly, it is queued in the global FIFO for decrypted messages. The
+ * tls_sctpw_recv function picks up messages from this queue.
+ */
+
+
+#include <pthread.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netinet/sctp.h>
+#include <sys/uio.h>
+
+#ifdef STANDALONE_WRAPPER
+
+# include "gnutls_sctp_wrapper.h"
+
+/* define to anything to issue debug messages */
+static int is_client = 0;
+
+# define TRACE_DEBUG(level,format,args... ) \
+	printf("%s( %-15s): " format "\n", is_client?"                   [CLI] ":"[SRV] ", __FUNCTION__, ## args);
+/* define to anything to issue trace messages */
+# define TRACE_ENTRY(_format,_args... )	\
+	TRACE_DEBUG(1, "Enter (" _format ")" , ##_args );
+
+#else /* STANDALONE_WRAPPER */
+
+# include "sec_tls_gnutls.h"
+
+#endif /* STANDALONE_WRAPPER */
+
+/****************************************************************************
+*****************************************************************************
+**        Data structures (internal to this module)                        **
+*****************************************************************************
+****************************************************************************/
+
+/* A buffer received on the socket, with its meta-data */
+typedef struct  {
+	unsigned char * data;	 /* Pointer to the allocated data */
+	size_t 		len;	 /* Length of the data */
+	size_t 		offset;	 /* start of unread data */
+	int    		streamid;/* The stream id on which the buffer was received */
+} buffer_t;
+
+/* An item from a buffer queue list */
+typedef struct _bqi {
+	buffer_t	 buf;	/* The actual buffer data and meta-data */
+	struct _bqi	*next;	/* Next buffer in the queue */
+	struct _bqi	*prev;	/* Previous buffer in the queue */
+} bqi_t;
+
+/* A buffer queue head */
+typedef struct {
+	bqi_t		sentinel;	/* a dummy element to initialize the chain */
+	pthread_mutex_t	mtx;		/* mutex to protect the list */
+	pthread_cond_t	cond;		/* condvar to signal when new message is posted */
+	int		count;		/* The number of messages in the queue */
+} bq_t;
+
+/* forward declaration */
+struct _tls_sctpw_ctx;
+
+/* For each pair of streams in the association, we need the following data: */
+typedef struct {
+	int		 	 stream; /* The stream id of this object. This is also the index in the array */
+	struct _tls_sctpw_ctx	*ctx;	 /* Pointer to the head of the context object */
+	bq_t		 	 queue;	 /* buffer queue of the encrypted chunks of data received and demux'd */
+	gnutls_session_t 	 session;/* The gnutls session corresponding to this pair of streams. */
+	pthread_t		 th;	 /* After handshake, the thread that receives and decrypts the data */
+	int			 th_run; /* Is the thread created? */
+} pairinfo_t;
+
+/* State of the wrapper context */
+typedef enum {
+	SCTPW_CTX_INIT,	/* The context has been initialized */
+	SCTPW_CTX_OPEN,	/* Handshake has been done */
+	SCTPW_CTX_BROKEN/* An error occurred on the socket */
+} ctx_state;
+
+/* Header of the context structure, that contain the data unique to each association */
+typedef struct {
+	int		sock;	/* The SCTP socket */
+	int		nbstrm;	/* Number of streams to use */
+	int		strauto;/* id of the stream to use when "-1" is specified */
+	int		side;	/* 0: server, !0: client */
+	ctx_state	state;	/* The state of this context */
+	pthread_t	demux;	/* The demux thread */
+	bq_t		rcvd;	/* queue of the deciphered chunks of data */
+} ctx_hdr_t;
+
+/* Now the definition of the wrapper context, corresponding to opaque type tls_sctpw_ctx: */
+typedef struct _tls_sctpw_ctx {
+	ctx_hdr_t	hdr;	 /* Header of the structure */
+	pairinfo_t	pairs[]; /* array of hdr.nbstrm * pairinfo_t structures */
+} tls_sctpw_ctx_t;
+
+
+/****************************************************************************
+*****************************************************************************
+**       TLS over SCTP wrapper library -- to use GNU TLS over SCTP         **
+*****************************************************************************
+****************************************************************************/ 
+
+/***** Macro to use when dumping sockaddr ******/
+/* char addrbuf[INET6_ADDRLEN] must exist in the context 
+ *  sa is an address of a sockaddr
+ *  res is a char * that points to the string in the end.
+ */
+#define DUMP_SA( sa, res ) {						\
+	struct sockaddr * _sa_ = (struct sockaddr *)(sa);		\
+	struct sockaddr_in * _sin_ = (struct sockaddr_in *) (sa);	\
+	struct sockaddr_in6 * _sin6_ = (struct sockaddr_in6 *) (sa);	\
+	switch (_sa_->sa_family) {					\
+		case AF_INET:						\
+			res = (char *)inet_ntop(AF_INET, 		\
+					&(_sin_->sin_addr.s_addr), 	\
+					addrbuf, 			\
+					sizeof(addrbuf));		\
+			break;						\
+		case AF_INET6:						\
+			res = (char *)inet_ntop(AF_INET6, 		\
+					&(_sin6_->sin6_addr.s6_addr), 	\
+					addrbuf, 			\
+					sizeof(addrbuf));		\
+			break;						\
+		default:						\
+			snprintf(addrbuf, 				\
+				 sizeof(addrbuf), 			\
+				 "Unknown AF: %d", 			\
+				 _sa_->sa_family);			\
+			res = (char *)addrbuf;				\
+	}								\
+}
+
+
+/***** Buffer Queues Management ******/
+
+#define BQI_LINK_INIT( _item_ ) { 				\
+	((bqi_t *)( _item_ ))->next = (bqi_t *)( _item_ );	\
+	((bqi_t *)( _item_ ))->prev = (bqi_t *)( _item_ );	\
+}
+
+#define BQ_PUSH( bq, bqi ) {				\
+	bqi_t * _sent_ = &(((bq_t *)( bq ))->sentinel);	\
+	bqi_t * _new_  = (bqi_t *)( bqi );		\
+	_new_->next = _sent_;				\
+	_new_->prev = _sent_->prev;			\
+	_sent_->prev->next = _new_;			\
+	_sent_->prev=_new_;				\
+	((bq_t *)( bq ))->count++;			\
+}
+#define BQ_POP( bq, bqi ) {				\
+	bqi_t * _rem_  = (bqi_t *)( bqi );		\
+	_rem_->next->prev = _rem_->prev;		\
+	_rem_->prev->next = _rem_->next;		\
+	BQI_LINK_INIT(_rem_);				\
+	((bq_t *)( bq ))->count--;			\
+}
+
+/* Initialize a queue */
+static int bq_init(bq_t * bq)
+{
+	int ret = 0;
+	
+	memset(bq, 0, sizeof(bq_t));
+	BQI_LINK_INIT( &bq->sentinel );
+	
+	ret = pthread_mutex_init(&bq->mtx, NULL);
+	if (ret != 0) {
+		TRACE_DEBUG(INFO, "pthread_mutex_init failed: %s", strerror(ret));
+		return ret;
+	}
+	
+	ret = pthread_cond_init(&bq->cond, NULL);
+	if (ret != 0) {
+		TRACE_DEBUG(INFO, "pthread_cond_init failed: %s", strerror(ret));
+		return ret;
+	}
+	
+	return 0;
+}
+
+/* in case of cancellation */
+static void bq_cleanup(void * _bq)
+{
+	int ret;
+	bq_t * bq = (bq_t *)_bq;
+	
+	TRACE_ENTRY( "%p", bq );
+	
+	/* Now unlock the queue, and we're done */
+	ret = pthread_mutex_unlock( &bq->mtx );
+	if (ret != 0) {
+		TRACE_DEBUG(INFO, "pthread_mutex_unlock failed: %s", strerror(ret));
+	}
+	
+	/* End of cleanup handler */
+	return;
+}
+
+/* Post a message in a queue (we use directly a bqi_t to avoid re-allocation) */
+static int bq_post(bq_t * bq, bqi_t *buf)
+{
+	int ret = 0;
+	
+	if ((bq == NULL) || (buf == NULL)) {
+		TRACE_DEBUG(INFO, "Invalid parameters");
+		return EINVAL;
+	}
+	
+	/* lock the queue */
+	ret = pthread_mutex_lock(&(bq->mtx));
+	if (ret != 0) {
+		TRACE_DEBUG(INFO, "pthread_mutex_lock failed: %s", strerror(ret));
+		return ret;
+	}
+	
+	/* Push the element */
+	BQ_PUSH(bq, buf);
+	
+	/* Signal the condition */
+	ret = pthread_cond_signal(&(bq->cond));
+	if (ret != 0) {
+		TRACE_DEBUG(INFO, "pthread_cond_signal failed: %s", strerror(ret));
+		return ret;
+	}
+	
+	/* unlock the queue */
+	ret = pthread_mutex_unlock(&(bq->mtx));
+	if (ret != 0) {
+		TRACE_DEBUG(INFO, "pthread_mutex_unlock failed: %s", strerror(ret));
+		return ret;
+	}
+	
+	return 0;
+}
+
+/* Wait and retrieve a complete message from a queue */
+static int bq_get(bq_t * bq, bqi_t **buf)
+{
+	int ret = 0;
+	
+	if ((bq == NULL) || (buf == NULL)) {
+		TRACE_DEBUG(INFO, "Invalid parameters");
+		return EINVAL;
+	}
+	
+	/* lock the queue */
+	ret = pthread_mutex_lock(&(bq->mtx));
+	if (ret != 0) {
+		TRACE_DEBUG(INFO, "pthread_mutex_lock failed: %s", strerror(ret));
+		return ret;
+	}
+	
+recheck:
+	if (bq->count > 0) {
+		*buf = bq->sentinel.next;
+		BQ_POP( bq, *buf );
+	} else {
+		/* wait for a message to be posted */
+		pthread_cleanup_push(bq_cleanup, bq);
+		ret = pthread_cond_wait(&(bq->cond), &(bq->mtx));
+		pthread_cleanup_pop(0);
+		if (ret != 0) {
+			TRACE_DEBUG(INFO, "pthread_cond_wait failed: %s", strerror(ret));
+			return ret;
+		}
+		/* Need to recheck in case of spurious wake up */
+		goto recheck;
+	}
+	
+	/* unlock the queue */
+	ret = pthread_mutex_unlock(&(bq->mtx));
+	if (ret != 0) {
+		TRACE_DEBUG(INFO, "pthread_mutex_unlock failed: %s", strerror(ret));
+		return ret;
+	}
+	
+	return 0;
+}
+
+/* Read data into a provided buffer (len is in/out parameter) */
+static int bq_read(bq_t * bq, void * data, size_t *len)
+{
+	int ret = 0;
+	bqi_t *bqi = NULL;
+	
+	if ((bq == NULL) || (data == NULL) || (len == NULL)) {
+		TRACE_DEBUG(INFO, "Invalid parameters");
+		return EINVAL;
+	}
+	
+	/* lock the queue */
+	ret = pthread_mutex_lock(&(bq->mtx));
+	if (ret != 0) {
+		TRACE_DEBUG(INFO, "pthread_mutex_lock failed: %s", strerror(ret));
+		return ret;
+	}
+	
+recheck:
+	if (bq->count > 0) {
+		/* Now we have data to read */
+		bqi = bq->sentinel.next;
+		if (*len >= bqi->buf.len - bqi->buf.offset) {
+			/* we can read the whole item at once */
+			BQ_POP( bq, bqi );
+			memcpy(data, bqi->buf.data + bqi->buf.offset, bqi->buf.len - bqi->buf.offset);
+			*len = bqi->buf.len - bqi->buf.offset;
+			/* free the bqi, we don't need it anymore */
+			free(bqi->buf.data);
+			free(bqi);
+		} else {
+			/* supplied buffer is too small, read only partial buffer */
+			memcpy(data, bqi->buf.data + bqi->buf.offset, *len);
+			bqi->buf.offset += *len;
+		}
+	} else {
+		/* wait for a message to be posted */
+		pthread_cleanup_push(bq_cleanup, bq);
+		ret = pthread_cond_wait(&(bq->cond), &(bq->mtx));
+		pthread_cleanup_pop(0);
+		if (ret != 0) {
+			TRACE_DEBUG(INFO, "pthread_cond_wait failed: %s", strerror(ret));
+			return ret;
+		}
+		/* Need to recheck in case of spurious wake up */
+		goto recheck;
+	}
+	
+	/* unlock the queue */
+	ret = pthread_mutex_unlock(&(bq->mtx));
+	if (ret != 0) {
+		TRACE_DEBUG(INFO, "pthread_mutex_unlock failed: %s", strerror(ret));
+		return ret;
+	}
+	
+	return 0;
+}
+
+/* Destroy a queue (must be empty) */
+static int bq_fini(bq_t * bq)
+{
+	int ret;
+	
+	if (bq == NULL) {
+		TRACE_DEBUG(INFO, "Invalid parameters");
+		return EINVAL;
+	}
+	
+	while (bq->count) {
+		bqi_t * del = NULL;
+		ret = bq_get(bq, &del);
+		if (ret != 0) {
+			TRACE_DEBUG(INFO, "bq_get failed: %s", strerror(ret));
+			return ret;
+		}
+		free(del->buf.data);
+		free(del);
+	}
+	
+	ret = pthread_cond_destroy(&(bq->cond));
+	if (ret != 0) {
+		TRACE_DEBUG(INFO, "pthread_cond_destroy failed: %s", strerror(ret));
+		return ret;
+	}
+	
+	ret = pthread_mutex_destroy(&(bq->mtx));
+	if (ret != 0) {
+		TRACE_DEBUG(INFO, "pthread_mutex_destroy failed: %s", strerror(ret));
+		return ret;
+	}
+	
+	return 0;
+}
+
+/***** Push and pull functions ******/
+
+/* Send data over the connection, called by gnutls */
+static ssize_t sctpw_push (gnutls_transport_ptr_t pair, const void * data, size_t len)
+{
+	int ret = 0;
+	int sock;
+	int stream;
+	
+	/* TRACE_ENTRY("%p %p %d", pair, data, len); */
+	
+	if ((pair == NULL) || (data == NULL)) {
+		TRACE_DEBUG(INFO, "Received invalid NULL parameter");
+		return -1;
+	}
+	
+	sock   = ((pairinfo_t *)pair)->ctx->hdr.sock;
+	stream = ((pairinfo_t *)pair)->stream;
+		
+	return tls_sctp_send(sock, stream, data, len);
+}
+
+/* Retrieve data received on a stream and demultiplexed already */
+static ssize_t sctpw_pull (gnutls_transport_ptr_t pair, void * buf, size_t buflen)
+{
+	int ret = 0;
+	size_t len = buflen;
+	
+	/* TRACE_ENTRY("%p %p %d", pair, buf, buflen); */
+	
+	ret = bq_read( &(((pairinfo_t *)pair)->queue), buf, &len );
+	if (ret != 0) {
+		TRACE_DEBUG(INFO, "bq_read failed: %s", strerror(ret));
+		return -1;
+	}
+	
+	return len;
+}
+
+/* Read data on the socket and queue it to the appropriate buffer queue */
+static void * sctpw_demux (void * arg)
+{
+	tls_sctpw_ctx_t * ctx = (tls_sctpw_ctx_t *) arg;
+	ssize_t ret;
+	int 	reti;
+	tls_sctp_recvevents event;
+	bqi_t * new;
+	
+	/* loop forever; the thread will be canceled when we want to terminate */
+	while (1) {
+		/* Prepare a structure to save the buffer */
+		new = (bqi_t *) malloc(sizeof(bqi_t));
+		if (new == NULL) {
+			TRACE_DEBUG(INFO, "malloc failed");
+			return NULL;
+		}
+		memset(new, 0, sizeof(bqi_t));
+		BQI_LINK_INIT(new);
+		
+		/* Now receive data from the SCTP socket */
+		ret = tls_sctp_recv( ctx->hdr.sock, &event, &(new->buf.streamid), (void **)&(new->buf.data), &(new->buf.len), 4096 );
+		if (ret <= 0)
+			free(new);
+			
+		if (ret < 0) {
+			/* The socket is no more valid */
+			TRACE_DEBUG(INFO, "tls_sctp_recv failed");
+			
+			/* we should signal a control thread here */
+			
+			return NULL;
+		}
+		
+		if ((ret == 0) && ((event == RCV_ERROR) || (event == RCV_SHUTDOWN))) {
+			/* The socket is no more valid */
+			TRACE_DEBUG(INFO, "tls_sctp_recv received a termination notification");
+			
+			/* we should signal a control thread here */
+			
+			return NULL;
+		}
+		
+		if (ret == 0)
+			continue;
+		
+		if (new->buf.streamid >= ctx->hdr.nbstrm) {
+			TRACE_DEBUG(INFO, "Received data on a non-protected stream, discarding.");
+			free(new);
+			continue;
+		}
+			
+		/* ok we have received a real message, demux and save it for sctpw_pull */
+		reti = bq_post( &(ctx->pairs[new->buf.streamid].queue), new);
+		if (reti != 0) {
+			TRACE_DEBUG(INFO, "bq_post failed");
+			
+			/* we should signal a control thread here */
+			
+			return NULL;
+		}
+	}
+	return NULL;
+}
+
+/* Receive data from a pair of streams, once TLS handshake is done */
+#define BUF_SIZE	2048
+static void * sctpw_recv_data (void * arg)
+{
+	int ret = 0;
+	pairinfo_t * pair = (pairinfo_t *)arg;
+	ctx_hdr_t * hdr;
+	bqi_t * new;
+	
+	hdr = &(pair->ctx->hdr);
+	
+	while (1) {
+		new = (bqi_t *) malloc(sizeof(bqi_t));
+		if (new == NULL) {
+			TRACE_DEBUG(INFO, "Memory allocation failed");
+			goto term;
+		}
+		memset(new, 0, sizeof(bqi_t));
+		BQI_LINK_INIT( new );
+		new->buf.streamid = pair->stream;
+		
+		new->buf.data = malloc( BUF_SIZE );
+		if (new->buf.data == NULL) {
+			TRACE_DEBUG(INFO, "Memory allocation failed");
+			goto term;
+		}
+		
+		memset(new->buf.data, 0, BUF_SIZE );
+		
+		ret = gnutls_record_recv ( pair->session, new->buf.data, BUF_SIZE );
+		if (ret <= 0) {
+			TRACE_DEBUG(INFO, "gnutls_record_recv failed (%d)", ret);
+			goto term;
+		}
+		
+		new->buf.len = ret;
+		
+		/* Save the decrypted data in hdr->rcvd */
+		ret = bq_post( &(hdr->rcvd), new);
+		if (ret != 0) {
+			TRACE_DEBUG(INFO, "bq_post failed: %s", strerror(ret));
+			goto term;
+		}
+		
+	}
+term:
+	/* We should signal a control thread here */
+	
+	hdr->state = SCTPW_CTX_BROKEN;
+	return NULL;
+}
+
+/* terminating / destroying a session */		
+static int _tls_sctpw_end( tls_sctpw_ctx ** sctx, int graceful )
+{
+	int ret = 0;
+	int i;
+	tls_sctpw_ctx_t * _sctx;
+	
+	TRACE_ENTRY("%p", sctx);
+	
+	/* check parameter. for close call, only OPEN objects are valid */
+	if ((sctx == NULL) || (*sctx == NULL)) {
+		TRACE_DEBUG(INFO, "Invalid parameter");
+		return EINVAL;
+	}
+	_sctx = (tls_sctpw_ctx_t *) *sctx;
+	
+	if (graceful && (_sctx->hdr.state != SCTPW_CTX_OPEN)) {
+		TRACE_DEBUG(INFO, "Invalid parameter");
+		return EINVAL;
+	}
+	
+	/* First, stop the demux thread */
+	if (_sctx->hdr.state != SCTPW_CTX_BROKEN) {
+		ret = pthread_cancel(_sctx->hdr.demux);
+		if (ret != 0) {
+			TRACE_DEBUG(INFO, "pthread_cancel failed: %s", strerror(ret));
+			return ret;
+		}
+	}
+
+	ret = pthread_join(_sctx->hdr.demux, NULL);
+	if (ret != 0) {
+		TRACE_DEBUG(INFO, "pthread_join failed: %s", strerror(ret));
+		return ret;
+	}
+	
+	/* for each pair */
+	for ( i= 0; i < _sctx->hdr.nbstrm; i++) {
+		/* Shutdown the GNU TLS session */
+		if (graceful) {
+			ret = gnutls_bye(_sctx->pairs[i].session, GNUTLS_SHUT_WR);
+			if (ret != GNUTLS_E_SUCCESS) {
+				TRACE_DEBUG(INFO, "gnutls_bye returned: %s", gnutls_strerror(ret));
+				/* we continue anyway */
+			}
+		}
+		
+		/* Terminate (cancel) the thread */
+		if (_sctx->pairs[i].th_run) {
+			ret = pthread_cancel(_sctx->pairs[i].th);
+			if (ret != 0) {
+				TRACE_DEBUG(INFO, "pthread_cancel failed: %s", strerror(ret));
+				return ret;
+			}
+			
+			ret = pthread_join(_sctx->pairs[i].th, NULL);
+			if (ret != 0) {
+				TRACE_DEBUG(INFO, "pthread_join failed: %s", strerror(ret));
+				return ret;
+			}
+			
+			_sctx->pairs[i].th_run = 0;
+			
+			/* Destroy the gnutls session */
+			gnutls_deinit (_sctx->pairs[i].session);
+		}
+		
+		/* Empty and destroy the queue */
+		ret = bq_fini( &(_sctx->pairs[i].queue) );
+		if (ret != 0) {
+			TRACE_DEBUG(INFO, "bq_fini failed: %s", strerror(ret));
+			return ret;
+		}
+	}
+		
+	/* Destroy the global objects */
+	ret = bq_fini(&(_sctx->hdr.rcvd) );
+	if (ret != 0) {
+		TRACE_DEBUG(INFO, "bq_fini failed: %s", strerror(ret));
+		return ret;
+	}
+			
+	free(_sctx);
+	*sctx = NULL;
+	
+	return 0;
+}
+
+/**********************
+ * External functions 
+ *********************/
+
+/* Initialize a wrapper context */
+int tls_sctpw_init( int sock, int streams, tls_sctpw_ctx ** sctx, int client, gnutls_credentials_type_t credtype, void ** creds, int (*priocb)(gnutls_session_t *) )
+{
+	int ret = 0, i;
+	tls_sctpw_ctx_t * _sctx = NULL;
+	
+	TRACE_ENTRY("%d %d %p %d", sock, streams, sctx, client);
+	
+	/* Check parameters */
+	if ((sock < 0) || (streams < 0) || (sctx == NULL)) {
+		TRACE_DEBUG(INFO, "Invalid parameters");
+		return EINVAL;
+	}
+	
+	/* Allocate storage for the new context object */
+	_sctx = (tls_sctpw_ctx_t *) malloc( sizeof(ctx_hdr_t) + streams * sizeof(pairinfo_t) );
+	if (_sctx == NULL) {
+		ret = errno;
+		TRACE_DEBUG(INFO, "Memory allocation failed: %s", strerror(ret));
+		return ret;
+	}
+	
+	memset(_sctx, 0, sizeof(ctx_hdr_t) + streams * sizeof(pairinfo_t));
+	
+	/* Initialize the header */
+	_sctx->hdr.sock   = sock;
+	_sctx->hdr.nbstrm = streams;
+	_sctx->hdr.side   = client;
+	_sctx->hdr.state  = SCTPW_CTX_INIT;
+	/* _sctx->hdr.demux The demux thread is created in the end of this function */
+	ret = bq_init( &(_sctx->hdr.rcvd) );
+	if (ret != 0) {
+		TRACE_DEBUG(INFO, "bq_init failed: %s", strerror(ret));
+		free(_sctx);
+		return ret;
+	}
+	
+	/* Now initialize all pairs */
+	for (i = 0; i < streams; i++) {
+		_sctx->pairs[i].stream = i;
+		_sctx->pairs[i].ctx    = _sctx;
+		
+		ret = bq_init( &(_sctx->pairs[i].queue) );
+		if (ret != 0) {
+			TRACE_DEBUG(INFO, "bq_init failed: %s", strerror(ret));
+			free(_sctx);
+			return ret;
+		}
+		
+		/* Now initialize the GNU TLS session for this pair of streams */
+		ret = gnutls_init (&(_sctx->pairs[i].session), client ? GNUTLS_CLIENT : GNUTLS_SERVER);
+		if (ret != GNUTLS_E_SUCCESS) {
+			TRACE_DEBUG(INFO, "gnutls_init failed: %d", ret);
+			return ret;
+		}
+		
+		/* The priority setting of the session */
+		if (priocb != NULL) {
+			ret = (*priocb)(&(_sctx->pairs[i].session));
+			if (ret != 0) {
+				TRACE_DEBUG(INFO, "Setting priority with callback failed: %d", ret);
+				return ret;
+			}
+		} else {
+			ret = gnutls_set_default_priority (_sctx->pairs[i].session);
+			if (ret != GNUTLS_E_SUCCESS) {
+				TRACE_DEBUG(INFO, "gnutls_set_default_priority failed: %d", ret);
+				return ret;
+			}
+		}
+		
+		/* The credentials settings of the session */
+		ret = gnutls_credentials_set (_sctx->pairs[i].session, credtype, *creds);
+		if (ret != GNUTLS_E_SUCCESS) {
+			TRACE_DEBUG(INFO, "gnutls_credentials_set failed: %d", ret);
+			return ret;
+		}
+		
+		/* The transport settings of the session */
+		gnutls_transport_set_ptr  (_sctx->pairs[i].session, (gnutls_transport_ptr_t) &(_sctx->pairs[i]));
+		gnutls_transport_set_lowat(_sctx->pairs[i].session, 0 );
+		gnutls_transport_set_pull_function(_sctx->pairs[i].session, sctpw_pull);
+		gnutls_transport_set_push_function(_sctx->pairs[i].session, sctpw_push);
+	
+		/* The thread will be created at handshake time */
+	}
+	
+	/* Now create the demux thread */
+	ret = pthread_create( &(_sctx->hdr.demux), NULL, sctpw_demux, (void *)_sctx );
+	if (ret != 0) {
+		TRACE_DEBUG(INFO, "Unable to create a thread: %s", strerror(ret));
+		return ret;
+	}
+	
+	*sctx = (tls_sctpw_ctx *) _sctx;
+	
+	return 0;
+}
+
+/* Handshake on all streams */
+int tls_sctpw_handshake( tls_sctpw_ctx * sctx, int (*checkcb)(gnutls_session_t session, void * parm), void * parm )
+{
+	int ret = 0;
+	int i;
+	
+	tls_sctpw_ctx_t * _sctx = (tls_sctpw_ctx_t *) sctx;
+	
+	TRACE_ENTRY("%p %p %p", sctx, checkcb, parm);
+	
+	if ((sctx == NULL) || (_sctx->hdr.state != SCTPW_CTX_INIT)) {
+		TRACE_DEBUG(INFO, "Invalid parameter");
+		return EINVAL;
+	}
+	
+	/* For each pair of stream  -- we could create separate threads to process all handshakes in parallel */
+	for ( i= 0; i < _sctx->hdr.nbstrm; i++) {
+		/* handshake */
+		ret = gnutls_handshake( _sctx->pairs[i].session );
+		if (ret < 0) {
+			TRACE_DEBUG(INFO, "Handshake failed on stream %d: %s", i, gnutls_strerror(ret));
+			return ret;
+		}
+		
+		TRACE_DEBUG(FULL, "Handskake complete on stream pair %d", i);
+		
+		if (checkcb != NULL) {
+			/* Now verifying the credentials */
+			ret = (*checkcb)(_sctx->pairs[i].session, parm);
+			if (ret != 0) {
+				TRACE_DEBUG(INFO, "The callback to verify the credentials returned an error: %d", ret);
+				return ret;
+			}
+		}
+	
+		/* create the thread that receives the data */
+		ret = pthread_create( &(_sctx->pairs[i].th), NULL, sctpw_recv_data, (void *)&(_sctx->pairs[i]));
+		if (ret != 0) {
+			TRACE_DEBUG(INFO, "pthread_create failed: %s", strerror(ret));
+			return ret;
+		}
+		_sctx->pairs[i].th_run = 1;
+	}
+	
+	/* Mark the state as open now */
+	_sctx->hdr.state  = SCTPW_CTX_OPEN;
+		
+	return 0;
+}
+
+/* Send data protected with TLS */
+int tls_sctpw_send( tls_sctpw_ctx * sctx, int stream, unsigned char * data, size_t len )
+{
+	int ret = 0;
+	ssize_t sent;
+	size_t rem = len;
+	tls_sctpw_ctx_t * _sctx = (tls_sctpw_ctx_t *) sctx;
+	
+	TRACE_ENTRY("%p %d %p %d", sctx, stream, data, len);
+	
+	/* check parameters */
+	if ((sctx == NULL) || (_sctx->hdr.state != SCTPW_CTX_OPEN)
+	    || (stream >= _sctx->hdr.nbstrm)) {
+		TRACE_DEBUG(INFO, "Invalid parameter");
+		return EINVAL;
+	}
+	
+	/* For automatic selection of a stream */
+	if (stream < 0) {
+		stream = _sctx->hdr.strauto;
+	}
+	/* This is not protected against multithreading, but it is not very important... */
+	_sctx->hdr.strauto = ( stream + 1 ) % _sctx->hdr.nbstrm;
+	
+	/* now send the data */
+	while (rem) {
+		sent = gnutls_record_send(_sctx->pairs[stream].session, (const void *) data + (len - rem), rem);
+		if (sent < 0) {
+			TRACE_DEBUG(INFO, "gnutls_record_send returned %d: %s", sent, gnutls_strerror(sent));
+			return sent;
+		}
+		rem -= sent;
+	}
+	
+	return 0;
+}
+
+/* Receive protected data */
+int tls_sctpw_recv( tls_sctpw_ctx * sctx, int *stream, void ** data, size_t *len )
+{
+	int ret = 0;
+	
+	tls_sctpw_ctx_t * _sctx = (tls_sctpw_ctx_t *) sctx;
+	bqi_t * got = NULL;
+	
+	TRACE_ENTRY("%p %p %p %p", sctx, stream, data, len);
+	
+	/* check parameters */
+	if ((data == NULL) || (len == NULL) || (stream == NULL) ||
+	    (sctx == NULL) || (_sctx->hdr.state != SCTPW_CTX_OPEN)) {
+		TRACE_DEBUG(INFO, "Invalid parameter");
+		return EINVAL;
+	}
+	
+	ret = bq_get( &(_sctx->hdr.rcvd), &got );
+	if (ret != 0) {
+		TRACE_DEBUG(INFO, "bq_get failed: %s", strerror(ret));
+		return ret;
+	}
+	
+	*stream = got->buf.streamid;
+	*len    = got->buf.len;
+	*data   = got->buf.data;
+	free(got);
+	
+	return 0;
+}
+
+int tls_sctpw_close( tls_sctpw_ctx ** sctx )
+{
+	TRACE_ENTRY("%p", sctx);
+	return _tls_sctpw_end(sctx, 1);
+}
+	
+int tls_sctpw_destroy( tls_sctpw_ctx ** sctx )
+{
+	TRACE_ENTRY("%p", sctx);
+	return _tls_sctpw_end(sctx, 0);
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+/****************************************************************************
+*****************************************************************************
+**                TLS over SCTP wrapper test program                       **
+*****************************************************************************
+****************************************************************************/ 
+#ifdef STANDALONE_WRAPPER
+
+#include <signal.h>
+
+#define TEST_PORT	4433
+#define TEST_SRV_STR	4
+#define TEST_CLI_STR	6
+
+#define MSG0	"cli -> srv, stream 0"
+#define MSG1	"srv -> cli, stream 2"
+#define MSG2	"cli -> srv, stream -1"
+#define MSG3	"srv -> cli, stream 3"
+
+static int set_dh_prio(gnutls_session_t * session)
+{
+	const int kx_prio[] = { GNUTLS_KX_ANON_DH, 0 };
+	
+	gnutls_set_default_priority (*session);
+	gnutls_kx_set_priority (*session, kx_prio);
+	
+	if (!is_client)
+		gnutls_dh_set_prime_bits (*session, 1024);
+
+	return 0;
+}
+
+/* The client function */
+int client_process()
+{
+	int ret;
+	struct sockaddr_in sin;
+	int streams = TEST_CLI_STR;
+	int sock;
+	tls_sctpw_ctx *tls_ctx = NULL;
+	gnutls_anon_client_credentials_t anoncred;
+
+	TRACE_ENTRY();
+	
+	ret = gnutls_anon_allocate_client_credentials (&anoncred);
+	if (ret != GNUTLS_E_SUCCESS) {
+		TRACE_DEBUG(INFO,"Unable to initialize client credentials: %s", gnutls_strerror(ret));
+		return -1;
+	}
+	
+	/* Prepare the server information */
+	memset(&sin, 0, sizeof(sin));
+	sin.sin_family = AF_INET;
+	sin.sin_port   = htons(TEST_PORT);
+	inet_pton(AF_INET, "127.0.0.1", &sin.sin_addr.s_addr);
+	
+	/* Now connect */
+	TRACE_DEBUG(FULL, "Connecting to server ... ");
+	ret = tls_sctp_connect( &sock, (struct sockaddr *)&sin, sizeof(sin), &streams );
+	if (ret != 0) {
+		TRACE_DEBUG(INFO, "Failed to connect to server: %s", strerror(ret));
+		return -1;
+	}
+	
+	/* SCTP socket is ready */
+	TRACE_DEBUG(FULL, "Connected; TLS can be run over %d streams", streams);
+	
+	/* test exchanging data without TLS */
+	TRACE_DEBUG(FULL, "Sending data over SCTP streams 0 and 1 to test basic communication...");
+	ret = tls_sctp_send(sock, 0, MSG0, strlen(MSG0));
+	if (ret != strlen(MSG0)) {
+		TRACE_DEBUG(INFO, "Partial send of data: %d / %d", ret, strlen(MSG0));
+		return -1;
+	}
+	
+	ret = tls_sctp_send(sock, 1, MSG2, strlen(MSG2));
+	if (ret != strlen(MSG2)) {
+		TRACE_DEBUG(INFO, "Partial send of data: %d / %d", ret, strlen(MSG0));
+		return -1;
+	}
+	
+	/* Initialize the wrapper context */
+	TRACE_DEBUG(FULL, "Data sent; now creating wrapper context ...");
+	ret = tls_sctpw_init( sock, streams, &tls_ctx, 1, GNUTLS_CRD_ANON, (void **)&anoncred, set_dh_prio );
+	if (ret != 0) {
+		TRACE_DEBUG(INFO, "Unable to create wrapper context: %s", strerror(ret));
+		return -1;
+	}
+	
+	/* TLS handshake */
+	TRACE_DEBUG(FULL, "Wrapper created, now initiating handshake ...");
+	ret = tls_sctpw_handshake( tls_ctx, NULL, NULL );
+	if (ret != 0) {
+		TRACE_DEBUG(INFO, "Handshake failed!");
+		return -1;
+	}
+	
+	/* Send first message */
+	TRACE_DEBUG(FULL, "Handshake complete, now sending data over stream 0...");
+	ret = tls_sctpw_send( tls_ctx, 0, MSG0, strlen(MSG0) );
+	if (ret != 0) {
+		TRACE_DEBUG(INFO, "Sending data over encrypted link failed!");
+		return -1;
+	}
+	
+	/* To be continued... */
+	sleep (2);
+	
+	/* Done, free the wrapper context */
+	TRACE_DEBUG(FULL, "Test complete, now terminating the wrapper context...");
+	ret = tls_sctpw_close( &tls_ctx );
+	if (ret != 0) {
+		TRACE_DEBUG(INFO, "Failed to close the wrapper: %s", strerror(ret));
+		return -1;
+	}
+	
+	/* and close the socket */
+	TRACE_DEBUG(FULL,"Closing the socket ...");
+	ret = close(sock);
+	if (ret != 0) {
+		TRACE_DEBUG(FULL, "Failed to close the socket!");
+		return -1;
+	}
+	
+	return 0;
+}
+
+/* The server function */
+int server_process()
+{
+	TRACE_ENTRY();
+	
+	int sock;
+	int skcli;
+	int streams;
+	int ret = 0;
+	tls_sctpw_ctx *tls_ctx = NULL;
+	gnutls_dh_params_t dh_params;
+	gnutls_anon_server_credentials_t anoncred;
+	
+	/* Initialize credentials */
+	ret = gnutls_anon_allocate_server_credentials (&anoncred);
+	if (ret != GNUTLS_E_SUCCESS) {
+		TRACE_DEBUG(INFO,"Unable to initialize server credentials: %s", gnutls_strerror(ret));
+		return -1;
+	}
+	ret = gnutls_dh_params_init (&dh_params);
+	if (ret != GNUTLS_E_SUCCESS) {
+		TRACE_DEBUG(INFO,"Unable to initialize Diffie-Hellman parameters: %s", gnutls_strerror(ret));
+		return -1;
+	}
+	ret = gnutls_dh_params_generate2 (dh_params, 1024);
+	if (ret != GNUTLS_E_SUCCESS) {
+		TRACE_DEBUG(INFO,"Unable to generate Diffie-Hellman parameters: %s", gnutls_strerror(ret));
+		return -1;
+	}
+	gnutls_anon_set_server_dh_params (anoncred, dh_params);
+	
+	/* Create the server socket */
+	TRACE_DEBUG(FULL, "Creating server socket on port %d with %d streams ...", TEST_PORT, TEST_SRV_STR);
+	ret = tls_sctp_create_server( &sock, TEST_PORT, TEST_SRV_STR );
+	if (ret != 0) {
+		TRACE_DEBUG(INFO, "Failed to create server: %s", strerror(ret));
+		return -1;
+	}
+	
+	/* Accept a client connection. */
+	TRACE_DEBUG(FULL, "Socket created, now waiting for a client connection ...");
+	ret = tls_sctp_accept( sock, &skcli, &streams );
+	if (ret != 0) {
+		TRACE_DEBUG(INFO, "Failed to accept connection: %s", strerror(ret));
+		return -1;
+	}
+	
+	/* Connection with a client is ready */
+	TRACE_DEBUG(FULL, "Client connected, TLS can be run over %d streams", streams);
+	
+	/* test exchanging data without TLS */
+	{
+		TRACE_DEBUG(FULL, "Receiving data over SCTP streams 0 and 1 to test basic communication...");
+		/* Client:
+		ret = tls_sctp_send(sock, 0, MSG0, strlen(MSG0));
+		ret = tls_sctp_send(sock, 1, MSG2, strlen(MSG2));
+		*/
+		tls_sctp_recvevents event;
+		int strid;
+		char * recvd;
+		size_t len;
+		int nmsg = 0;
+		
+		while (nmsg != 3) {
+			ret = tls_sctp_recv(skcli, &event, &strid, (void **)&recvd, &len, 5);
+			if (ret < 0) {
+				TRACE_DEBUG(INFO,"tls_sctp_recv: %d", ret);
+				return -1;
+			}
+			
+			if (ret == 0) {
+				TRACE_DEBUG(INFO,"tls_sctp_recv: event %d", event);
+				continue;
+			}
+			
+			if ((strid == 0) && (memcmp(recvd, MSG0, strlen(MSG0)) == 0))
+				nmsg |= 1;
+			if ((strid == 1) && (memcmp(recvd, MSG2, strlen(MSG2)) == 0))
+				nmsg |= 2;
+			
+			free(recvd);
+		}
+	}	
+	
+	/* Now initialize the wrapper object */
+	TRACE_DEBUG(FULL, "Data received; now creating wrapper context ...");
+	ret = tls_sctpw_init( skcli, streams, &tls_ctx, 0, GNUTLS_CRD_ANON, (void **)&anoncred, set_dh_prio );
+	if (ret != 0) {
+		TRACE_DEBUG(INFO, "Unable to create wrapper context: %s", strerror(ret));
+		return -1;
+	}
+	
+	/* TLS Handshake */
+	TRACE_DEBUG(FULL, "Wrapper created, now initiating handshake ...");
+	ret = tls_sctpw_handshake( tls_ctx, NULL, NULL );
+	if (ret != 0) {
+		TRACE_DEBUG(INFO, "Handshake failed!");
+		return -1;
+	}
+	
+	/* Receive the first message sent by the client */
+	{
+		void * buf;
+		int str;
+		size_t len;
+		
+		TRACE_DEBUG(FULL, "Handshake complete, now receiving TLS-protected data...");
+		ret = tls_sctpw_recv( tls_ctx, &str, &buf, &len );
+		if (ret != 0) {
+			TRACE_DEBUG(INFO, "Failed to receive data: %s", strerror(ret));
+			return -1;
+		}
+
+		if ((str != 0) || (strcmp((char *)buf, MSG0))) {
+			TRACE_DEBUG(INFO, "Received bad data (%d bytes over stream %d)...", len, str);
+			return EBADMSG;
+		}
+	}
+	
+	/* Now send our first message */	
+	TRACE_DEBUG(FULL, "Now sending data over stream 2 to client ...");
+	ret = tls_sctpw_send( tls_ctx, 2, MSG1, strlen(MSG1) );
+	if (ret != 0) {
+		TRACE_DEBUG(INFO, "Sending data over encrypted link failed!");
+		return -1;
+	}
+	
+	/* To be continued... */
+	sleep (2);
+	
+	/* Finished, free the wrapper context */
+	TRACE_DEBUG(FULL, "Test complete, now terminating the wrapper context...");
+	ret = tls_sctpw_close( &tls_ctx );
+	if (ret != 0) {
+		TRACE_DEBUG(INFO, "Failed to close the wrapper: %s", strerror(ret));
+		return -1;
+	}
+	
+	/* and close the socket */
+	TRACE_DEBUG(FULL,"Closing the connected socket ...");
+	ret = close(skcli);
+	if (ret != 0) {
+		TRACE_DEBUG(FULL, "Failed to close the socket!");
+		return -1;
+	}
+	
+	/* and close the server */
+	TRACE_DEBUG(FULL,"Closing the server socket ...");
+	ret = close(sock);
+	if (ret != 0) {
+		TRACE_DEBUG(FULL, "Failed to close the socket!");
+		return -1;
+	}
+	
+	return 0;
+}
+
+GCRY_THREAD_OPTION_PTHREAD_IMPL;
+
+static void my_gnutls_log(int level, const char * str) 
+{
+	printf("%s[ gnutls * %d ] : %s", is_client?"                   [CLI] ":"[SRV] ", level, str);
+	return;
+}
+
+/* Main */
+int main(int argc, char * argv[])
+{
+	int ret = 0;
+	pid_t client;
+	
+	TRACE_ENTRY("%d %p", argc, argv);
+	
+	/* Initialize gnutls */
+ 	// gcry_control (GCRYCTL_SET_THREAD_CBS, &gcry_threads_pthread);
+	gnutls_global_init ();
+	gnutls_global_set_log_function(my_gnutls_log);
+	gnutls_global_set_log_level(0);
+	
+	client = fork();
+	if (client == 0) {
+		/* Client process */
+		is_client = 1;
+		/* Wait for the server to start */
+		sleep(2);
+		/* Then start the client */
+		return client_process();
+	} else {
+		if (client == (pid_t) -1) {
+			TRACE_DEBUG(INFO, "Fork failed!");
+			return -1;
+		}
+		
+		/* Set a timeout just in case */
+		alarm(20);
+		
+		/* server process */
+		ret = server_process();
+
+		TRACE_DEBUG(FULL, "Server process termination code: %d", ret);
+		
+		if (ret != 0) {
+			/* Let's kill the client */
+			kill(client, SIGTERM);
+		}
+		
+		/* Wait for child to terminate */
+		wait(&ret);
+		
+		TRACE_DEBUG(FULL, "Client process termination code: %x", ret);
+	}
+	
+	TRACE_DEBUG(INFO, "%s is terminated.", argv[0]);
+	return 0;
+}
+
+#endif /* STANDALONE_WRAPPER */
+
+
+
+
+
+
+
+
+
+
+
+
+/****************************************************************************
+*****************************************************************************
+**        SCTP helper functions, to manage SCTP connections                **
+*****************************************************************************
+****************************************************************************/ 
+#ifndef SKIP_SCTP_WRAPPERS /* In case it is defined elsewhere */
+
+/* #define DEBUG_SCTP */
+
+/* Set socket options */
+static int _tls_sctp_sockopt(int sk, uint16_t nstreams)
+{
+	int ret = 0;
+	socklen_t sz;
+	
+	/* Subscribe to all notifications */
+	{
+		struct sctp_event_subscribe event;
+
+		memset(&event, 0, sizeof(event));
+		event.sctp_data_io_event	= 1;	/* to receive the stream ID in SCTP_SNDRCV ancilliary data on message reception */
+		event.sctp_association_event	= 1;	/* new or closed assotiations (mostly for one-to-many style sockets) */
+		event.sctp_address_event	= 1;	/* address changes */
+		event.sctp_send_failure_event	= 1;	/* delivery failures */
+		event.sctp_peer_error_event	= 1;	/* remote peer sends an error */
+		event.sctp_shutdown_event	= 1;	/* peer has sent a SHUTDOWN */
+		event.sctp_partial_delivery_event = 1;	/* a partial delivery is aborted, probably indicating the connection is being shutdown */
+		event.sctp_adaptation_layer_event = 1;	/* adaptation layer notifications */
+		// event.sctp_authentication_event = 1;	/* when new key is made active */
+
+		/* Set the option to the socket */
+		ret = setsockopt(sk, IPPROTO_SCTP, SCTP_EVENTS, &event, sizeof(event));
+		if (ret != 0) {
+			ret = errno;
+			TRACE_DEBUG(INFO, "Unable to set the socket SCTP_EVENTS options: %s", strerror(ret));
+			return ret;
+		}
+		
+#ifdef DEBUG_SCTP
+		#if 0
+		sz = sizeof(event);
+		ret = getsockopt(sk, IPPROTO_SCTP, SCTP_EVENTS, &event, &sz);
+		if (ret != 0) {
+			ret = errno;
+			TRACE_DEBUG(INFO, "Unable to get the socket SCTP_EVENTS values: %s", strerror(ret));
+			return ret;
+		}
+		if (sz != sizeof(event))
+		{
+			ret = ENOTSUP;
+			TRACE_DEBUG(INFO, "Invalid size of socket option: %d / %d", sz, (socklen_t)sizeof(event));
+			return ret;
+		}
+		#endif /* 0 */
+		
+		TRACE_DEBUG(FULL, "SCTP_EVENTS sctp_data_io_event          : %hhu", event.sctp_data_io_event);
+		TRACE_DEBUG(FULL, "SCTP_EVENTS sctp_association_event      : %hhu", event.sctp_association_event);
+		TRACE_DEBUG(FULL, "SCTP_EVENTS sctp_address_event	   : %hhu", event.sctp_address_event);
+		TRACE_DEBUG(FULL, "SCTP_EVENTS sctp_send_failure_event     : %hhu", event.sctp_send_failure_event);
+		TRACE_DEBUG(FULL, "SCTP_EVENTS sctp_peer_error_event	   : %hhu", event.sctp_peer_error_event);
+		TRACE_DEBUG(FULL, "SCTP_EVENTS sctp_shutdown_event	   : %hhu", event.sctp_shutdown_event);
+		TRACE_DEBUG(FULL, "SCTP_EVENTS sctp_partial_delivery_event : %hhu", event.sctp_partial_delivery_event);
+		TRACE_DEBUG(FULL, "SCTP_EVENTS sctp_adaptation_layer_event : %hhu", event.sctp_adaptation_layer_event);
+		// TRACE_DEBUG(FULL, "SCTP_EVENTS sctp_authentication_event   : %hhu", event.sctp_authentication_event);
+#endif /* DEBUG_SCTP */
+		
+	}
+	
+	/* Set the INIT parameters, such as number of streams */
+	{
+		struct sctp_initmsg init;
+		memset(&init, 0, sizeof(init));
+		
+#ifdef DEBUG_SCTP
+		sz = sizeof(init);
+		
+		/* Read socket defaults */
+		ret = getsockopt(sk, IPPROTO_SCTP, SCTP_INITMSG, &init, &sz);
+		if (ret != 0) {
+			ret = errno;
+			TRACE_DEBUG(INFO, "Unable to get the socket SCTP_INITMSG values: %s", strerror(ret));
+			return ret;
+		}
+		if (sz != sizeof(init))
+		{
+			ret = ENOTSUP;
+			TRACE_DEBUG(INFO, "Invalid size of socket option: %d / %d", sz, (socklen_t)sizeof(init));
+			return ret;
+		}
+		TRACE_DEBUG(FULL, "Def SCTP_INITMSG sinit_num_ostreams   : %hu", init.sinit_num_ostreams);
+		TRACE_DEBUG(FULL, "Def SCTP_INITMSG sinit_max_instreams  : %hu", init.sinit_max_instreams);
+		TRACE_DEBUG(FULL, "Def SCTP_INITMSG sinit_max_attempts   : %hu", init.sinit_max_attempts);
+		TRACE_DEBUG(FULL, "Def SCTP_INITMSG sinit_max_init_timeo : %hu", init.sinit_max_init_timeo);
+#endif /* DEBUG_SCTP */
+
+		/* Set the init options -- need to receive SCTP_COMM_UP to confirm the requested parameters */
+		init.sinit_num_ostreams	  = nstreams;	/* desired number of outgoing streams */
+		init.sinit_max_instreams  = nstreams;	/* limit for the number of incoming streams */
+
+		/* Set the option to the socket */
+		ret = setsockopt(sk, IPPROTO_SCTP, SCTP_INITMSG, &init, sizeof(init));
+		if (ret != 0) {
+			ret = errno;
+			TRACE_DEBUG(INFO, "Unable to set the socket SCTP_INITMSG options: %s", strerror(ret));
+			return ret;
+		}
+		
+#ifdef DEBUG_SCTP
+		/* Check new values */
+		ret = getsockopt(sk, IPPROTO_SCTP, SCTP_INITMSG, &init, &sz);
+		if (ret != 0) {
+			ret = errno;
+			TRACE_DEBUG(INFO, "Unable to get the socket SCTP_INITMSG values: %s", strerror(ret));
+			return ret;
+		}
+		TRACE_DEBUG(FULL, "New SCTP_INITMSG sinit_num_ostreams   : %hu", init.sinit_num_ostreams);
+		TRACE_DEBUG(FULL, "New SCTP_INITMSG sinit_max_instreams  : %hu", init.sinit_max_instreams);
+		TRACE_DEBUG(FULL, "New SCTP_INITMSG sinit_max_attempts   : %hu", init.sinit_max_attempts);
+		TRACE_DEBUG(FULL, "New SCTP_INITMSG sinit_max_init_timeo : %hu", init.sinit_max_init_timeo);
+#endif /* DEBUG_SCTP */
+	}
+	
+	/* Set the SCTP_DISABLE_FRAGMENTS option, required for TLS */
+	#ifdef SCTP_DISABLE_FRAGMENTS
+	{
+		int nofrag;
+		
+#ifdef DEBUG_SCTP
+		sz = sizeof(nofrag);
+		/* Read socket defaults */
+		ret = getsockopt(sk, IPPROTO_SCTP, SCTP_DISABLE_FRAGMENTS, &nofrag, &sz);
+		if (ret != 0) {
+			ret = errno;
+			TRACE_DEBUG(INFO, "Unable to get the socket SCTP_DISABLE_FRAGMENTS value: %s", strerror(ret));
+			return ret;
+		}
+		if (sz != sizeof(nofrag))
+		{
+			ret = ENOTSUP;
+			TRACE_DEBUG(INFO, "Invalid size of socket option: %d / %d", sz, (socklen_t)sizeof(nofrag));
+			return ret;
+		}
+		TRACE_DEBUG(FULL, "Def SCTP_DISABLE_FRAGMENTS value : %s", nofrag ? "true" : "false");
+#endif /* DEBUG_SCTP */
+
+		nofrag = 0;	/* We turn ON the fragmentation */
+		
+		/* Set the option to the socket */
+		ret = setsockopt(sk, IPPROTO_SCTP, SCTP_DISABLE_FRAGMENTS, &nofrag, sizeof(nofrag));
+		if (ret != 0) {
+			ret = errno;
+			TRACE_DEBUG(INFO, "Unable to set the socket SCTP_DISABLE_FRAGMENTS options: %s", strerror(ret));
+			return ret;
+		}
+		
+#ifdef DEBUG_SCTP
+		/* Check new values */
+		ret = getsockopt(sk, IPPROTO_SCTP, SCTP_DISABLE_FRAGMENTS, &nofrag, &sz);
+		if (ret != 0) {
+			ret = errno;
+			TRACE_DEBUG(INFO, "Unable to get the socket SCTP_DISABLE_FRAGMENTS value: %s", strerror(ret));
+			return ret;
+		}
+		TRACE_DEBUG(FULL, "New SCTP_DISABLE_FRAGMENTS value : %s", nofrag ? "true" : "false");
+#endif /* DEBUG_SCTP */
+	}
+	#else /* SCTP_DISABLE_FRAGMENTS */
+	# error "TLS requires support of SCTP_DISABLE_FRAGMENTS"
+	#endif /* SCTP_DISABLE_FRAGMENTS */
+	
+	
+	/* Set the RETRANSMIT parameters */
+	#ifdef SCTP_RTOINFO
+	{
+		struct sctp_rtoinfo rtoinfo;
+		memset(&rtoinfo, 0, sizeof(rtoinfo));
+
+#ifdef DEBUG_SCTP
+		sz = sizeof(rtoinfo);
+		/* Read socket defaults */
+		ret = getsockopt(sk, IPPROTO_SCTP, SCTP_RTOINFO, &rtoinfo, &sz);
+		if (ret != 0) {
+			ret = errno;
+			TRACE_DEBUG(INFO, "Unable to get the socket SCTP_RTOINFO values: %s", strerror(ret));
+			return ret;
+		}
+		if (sz != sizeof(rtoinfo))
+		{
+			ret = ENOTSUP;
+			TRACE_DEBUG(INFO, "Invalid size of socket option: %d / %d", sz, (socklen_t)sizeof(rtoinfo));
+			return ret;
+		}
+		TRACE_DEBUG(FULL, "Def SCTP_RTOINFO srto_initial : %u", rtoinfo.srto_initial);
+		TRACE_DEBUG(FULL, "Def SCTP_RTOINFO srto_max     : %u", rtoinfo.srto_max);
+		TRACE_DEBUG(FULL, "Def SCTP_RTOINFO srto_min     : %u", rtoinfo.srto_min);
+#endif /* DEBUG_SCTP */
+
+		/* rtoinfo.srto_assoc_id; */	/* ignored on 1-to-1 sockets */
+		rtoinfo.srto_initial = 0;	/* Initial retransmit timer (in ms) (0: do not change) */
+		rtoinfo.srto_max     = 0;	/* Maximum retransmit timer (in ms) (0: do not change) */
+		rtoinfo.srto_min     = 0;	/* Minimum retransmit timer (in ms) (0: do not change) */
+
+		/* Set the option to the socket */
+		ret = setsockopt(sk, IPPROTO_SCTP, SCTP_RTOINFO, &rtoinfo, sizeof(rtoinfo));
+		if (ret != 0) {
+			ret = errno;
+			TRACE_DEBUG(INFO, "Unable to set the socket SCTP_RTOINFO options: %s", strerror(ret));
+			return ret;
+		}
+		
+#ifdef DEBUG_SCTP
+		/* Check new values */
+		ret = getsockopt(sk, IPPROTO_SCTP, SCTP_RTOINFO, &rtoinfo, &sz);
+		if (ret != 0) {
+			ret = errno;
+			TRACE_DEBUG(INFO, "Unable to get the socket SCTP_RTOINFO values: %s", strerror(ret));
+			return ret;
+		}
+		TRACE_DEBUG(FULL, "New SCTP_RTOINFO srto_initial : %u", rtoinfo.srto_initial);
+		TRACE_DEBUG(FULL, "New SCTP_RTOINFO srto_max     : %u", rtoinfo.srto_max);
+		TRACE_DEBUG(FULL, "New SCTP_RTOINFO srto_min     : %u", rtoinfo.srto_min);
+#endif /* DEBUG_SCTP */
+	}
+	#else /* SCTP_RTOINFO */
+	TRACE_DEBUG(FULL, "Skipping SCTP_RTOINFO");
+	#endif /* SCTP_RTOINFO */
+	
+	/* Set the ASSOCIATION parameters */
+	#ifdef SCTP_ASSOCINFO
+	{
+		struct sctp_assocparams assoc;
+		memset(&assoc, 0, sizeof(assoc));
+
+#ifdef DEBUG_SCTP
+		sz = sizeof(assoc);
+		/* Read socket defaults */
+		ret = getsockopt(sk, IPPROTO_SCTP, SCTP_ASSOCINFO, &assoc, &sz);
+		if (ret != 0) {
+			ret = errno;
+			TRACE_DEBUG(INFO, "Unable to get the socket SCTP_ASSOCINFO values: %s", strerror(ret));
+			return ret;
+		}
+		if (sz != sizeof(assoc))
+		{
+			ret = ENOTSUP;
+			TRACE_DEBUG(INFO, "Invalid size of socket option: %d / %d", sz, (socklen_t)sizeof(assoc));
+			return ret;
+		}
+		TRACE_DEBUG(FULL, "Def SCTP_ASSOCINFO sasoc_asocmaxrxt               : %hu", assoc.sasoc_asocmaxrxt);
+		TRACE_DEBUG(FULL, "Def SCTP_ASSOCINFO sasoc_number_peer_destinations : %hu", assoc.sasoc_number_peer_destinations);
+		TRACE_DEBUG(FULL, "Def SCTP_ASSOCINFO sasoc_peer_rwnd                : %u" , assoc.sasoc_peer_rwnd);
+		TRACE_DEBUG(FULL, "Def SCTP_ASSOCINFO sasoc_local_rwnd               : %u" , assoc.sasoc_local_rwnd);
+		TRACE_DEBUG(FULL, "Def SCTP_ASSOCINFO sasoc_cookie_life              : %u" , assoc.sasoc_cookie_life);
+#endif /* DEBUG_SCTP */
+
+		/* assoc.sasoc_assoc_id; */	/* ignored on 1-to-1 sockets */
+		assoc.sasoc_asocmaxrxt 		     = 0;	/* Maximum retransmission attempts */
+		/* Following more read-only :
+		   sasoc_number_peer_destinations 
+		   sasoc_peer_rwnd
+		   sasoc_local_rwnd
+		 */
+		assoc.sasoc_cookie_life              = 0;	/* Lifetime for cookies */
+		
+		/* Set the option to the socket */
+		ret = setsockopt(sk, IPPROTO_SCTP, SCTP_ASSOCINFO, &assoc, sizeof(assoc));
+		if (ret != 0) {
+			ret = errno;
+			TRACE_DEBUG(INFO, "Unable to set the socket SCTP_ASSOCINFO options: %s", strerror(ret));
+			return ret;
+		}
+		
+#ifdef DEBUG_SCTP
+		/* Check new values */
+		ret = getsockopt(sk, IPPROTO_SCTP, SCTP_ASSOCINFO, &assoc, &sz);
+		if (ret != 0) {
+			ret = errno;
+			TRACE_DEBUG(INFO, "Unable to get the socket SCTP_ASSOCINFO values: %s", strerror(ret));
+			return ret;
+		}
+		TRACE_DEBUG(FULL, "New SCTP_ASSOCINFO sasoc_asocmaxrxt               : %hu", assoc.sasoc_asocmaxrxt);
+		TRACE_DEBUG(FULL, "New SCTP_ASSOCINFO sasoc_number_peer_destinations : %hu", assoc.sasoc_number_peer_destinations);
+		TRACE_DEBUG(FULL, "New SCTP_ASSOCINFO sasoc_peer_rwnd                : %u" , assoc.sasoc_peer_rwnd);
+		TRACE_DEBUG(FULL, "New SCTP_ASSOCINFO sasoc_local_rwnd               : %u" , assoc.sasoc_local_rwnd);
+		TRACE_DEBUG(FULL, "New SCTP_ASSOCINFO sasoc_cookie_life              : %u" , assoc.sasoc_cookie_life);
+#endif /* DEBUG_SCTP */
+	}
+	#else /* SCTP_ASSOCINFO */
+	TRACE_DEBUG(FULL, "Skipping SCTP_ASSOCINFO");
+	#endif /* SCTP_ASSOCINFO */
+	
+	
+	/* The SO_LINGER option will be reset if we want to perform SCTP ABORT */
+	#ifdef SO_LINGER
+	{
+		struct linger linger;
+		memset(&linger, 0, sizeof(linger));
+		
+#ifdef DEBUG_SCTP
+		sz = sizeof(linger);
+		/* Read socket defaults */
+		ret = getsockopt(sk, SOL_SOCKET, SO_LINGER, &linger, &sz);
+		if (ret != 0) {
+			ret = errno;
+			TRACE_DEBUG(INFO, "Unable to get the socket SO_LINGER values: %s", strerror(ret));
+			return ret;
+		}
+		if (sz != sizeof(linger))
+		{
+			ret = ENOTSUP;
+			TRACE_DEBUG(INFO, "Invalid size of socket option: %d / %d", sz, (socklen_t)sizeof(linger));
+			return ret;
+		}
+		TRACE_DEBUG(FULL, "Def SO_LINGER l_onoff  : %d", linger.l_onoff);
+		TRACE_DEBUG(FULL, "Def SO_LINGER l_linger : %d", linger.l_linger);
+#endif /* DEBUG_SCTP */
+		
+		linger.l_onoff	= 0;	/* Do not activate the linger */
+		linger.l_linger = 0;	/* Return immediately when closing (=> abort) */
+		
+		/* Set the option */
+		ret = setsockopt(sk, SOL_SOCKET, SO_LINGER, &linger, sizeof(linger));
+		if (ret != 0) {
+			ret = errno;
+			TRACE_DEBUG(INFO, "Unable to set the socket SO_LINGER options: %s", strerror(ret));
+			return ret;
+		}
+		
+#ifdef DEBUG_SCTP
+		/* Check new values */
+		ret = getsockopt(sk, SOL_SOCKET, SO_LINGER, &linger, &sz);
+		if (ret != 0) {
+			ret = errno;
+			TRACE_DEBUG(INFO, "Unable to get the socket SO_LINGER values: %s", strerror(ret));
+			return ret;
+		}
+		TRACE_DEBUG(FULL, "New SO_LINGER l_onoff  : %d", linger.l_onoff);
+		TRACE_DEBUG(FULL, "New SO_LINGER l_linger : %d", linger.l_linger);
+#endif /* DEBUG_SCTP */
+	}
+	#else /* SO_LINGER */
+	TRACE_DEBUG(FULL, "Skipping SO_LINGER");
+	#endif /* SO_LINGER */
+	
+	/* Set the NODELAY option (Nagle-like algorithm) */
+	#ifdef SCTP_NODELAY
+	{
+		int nodelay;
+		
+#ifdef DEBUG_SCTP
+		sz = sizeof(nodelay);
+		/* Read socket defaults */
+		ret = getsockopt(sk, IPPROTO_SCTP, SCTP_NODELAY, &nodelay, &sz);
+		if (ret != 0) {
+			ret = errno;
+			TRACE_DEBUG(INFO, "Unable to get the socket SCTP_NODELAY value: %s", strerror(ret));
+			return ret;
+		}
+		if (sz != sizeof(nodelay))
+		{
+			ret = ENOTSUP;
+			TRACE_DEBUG(INFO, "Invalid size of socket option: %d / %d", sz, (socklen_t)sizeof(nodelay));
+			return ret;
+		}
+		TRACE_DEBUG(FULL, "Def SCTP_NODELAY value : %s", nodelay ? "true" : "false");
+#endif /* DEBUG_SCTP */
+
+		nodelay = 0;	/* We turn ON the Nagle algorithm */
+		
+		/* Set the option to the socket */
+		ret = setsockopt(sk, IPPROTO_SCTP, SCTP_NODELAY, &nodelay, sizeof(nodelay));
+		if (ret != 0) {
+			ret = errno;
+			TRACE_DEBUG(INFO, "Unable to set the socket SCTP_NODELAY options: %s", strerror(ret));
+			return ret;
+		}
+		
+#ifdef DEBUG_SCTP
+		/* Check new values */
+		ret = getsockopt(sk, IPPROTO_SCTP, SCTP_NODELAY, &nodelay, &sz);
+		if (ret != 0) {
+			ret = errno;
+			TRACE_DEBUG(INFO, "Unable to get the socket SCTP_NODELAY value: %s", strerror(ret));
+			return ret;
+		}
+		TRACE_DEBUG(FULL, "New SCTP_NODELAY value : %s", nodelay ? "true" : "false");
+#endif /* DEBUG_SCTP */
+	}
+	#else /* SCTP_NODELAY */
+	TRACE_DEBUG(FULL, "Skipping SCTP_NODELAY");
+	#endif /* SCTP_NODELAY */
+	
+	/* Set the interleaving option */
+	#ifdef SCTP_FRAGMENT_INTERLEAVE
+	{
+		int interleave;
+		
+#ifdef DEBUG_SCTP
+		sz = sizeof(interleave);
+		/* Read socket defaults */
+		ret = getsockopt(sk, IPPROTO_SCTP, SCTP_FRAGMENT_INTERLEAVE, &interleave, &sz);
+		if (ret != 0) {
+			ret = errno;
+			TRACE_DEBUG(INFO, "Unable to get the socket SCTP_FRAGMENT_INTERLEAVE value: %s", strerror(ret));
+			return ret;
+		}
+		if (sz != sizeof(interleave))
+		{
+			ret = ENOTSUP;
+			TRACE_DEBUG(INFO, "Invalid size of socket option: %d / %d", sz, (socklen_t)sizeof(interleave));
+			return ret;
+		}
+		TRACE_DEBUG(FULL, "Def SCTP_FRAGMENT_INTERLEAVE value : %d", interleave);
+#endif /* DEBUG_SCTP */
+
+		interleave = 2;	/* Allow partial delivery on several streams at the same time, since we are stream-aware in TLS */
+		
+		/* Set the option to the socket */
+		ret = setsockopt(sk, IPPROTO_SCTP, SCTP_FRAGMENT_INTERLEAVE, &interleave, sizeof(interleave));
+		if (ret != 0) {
+			ret = errno;
+			TRACE_DEBUG(INFO, "Unable to set the socket SCTP_FRAGMENT_INTERLEAVE options: %s", strerror(ret));
+			return ret;
+		}
+		
+#ifdef DEBUG_SCTP
+		/* Check new values */
+		ret = getsockopt(sk, IPPROTO_SCTP, SCTP_FRAGMENT_INTERLEAVE, &interleave, &sz);
+		if (ret != 0) {
+			ret = errno;
+			TRACE_DEBUG(INFO, "Unable to get the socket SCTP_FRAGMENT_INTERLEAVE value: %s", strerror(ret));
+			return ret;
+		}
+		TRACE_DEBUG(FULL, "New SCTP_FRAGMENT_INTERLEAVE value : %d", interleave);
+#endif /* DEBUG_SCTP */
+	}
+	#else /* SCTP_FRAGMENT_INTERLEAVE */
+	TRACE_DEBUG(FULL, "Skipping SCTP_FRAGMENT_INTERLEAVE");
+	#endif /* SCTP_FRAGMENT_INTERLEAVE */
+	
+	/* Set the ASCONF option */
+	#ifdef SCTP_AUTO_ASCONF
+	{
+		int asconf;
+		
+#ifdef DEBUG_SCTP
+		sz = sizeof(asconf);
+		/* Read socket defaults */
+		ret = getsockopt(sk, IPPROTO_SCTP, SCTP_AUTO_ASCONF, &asconf, &sz);
+		if (ret != 0) {
+			ret = errno;
+			TRACE_DEBUG(INFO, "Unable to get the socket SCTP_AUTO_ASCONF value: %s", strerror(ret));
+			return ret;
+		}
+		if (sz != sizeof(asconf))
+		{
+			ret = ENOTSUP;
+			TRACE_DEBUG(INFO, "Invalid size of socket option: %d / %d", sz, (socklen_t)sizeof(asconf));
+			return ret;
+		}
+		TRACE_DEBUG(FULL, "Def SCTP_I_WANT_MAPPED_V4_ADDR value : %s", asconf ? "true" : "false");
+#endif /* DEBUG_SCTP */
+
+		asconf = 1;	/* allow automatic use of added or removed addresses in the association */
+		
+		/* Set the option to the socket */
+		ret = setsockopt(sk, IPPROTO_SCTP, SCTP_AUTO_ASCONF, &asconf, sizeof(asconf));
+		if (ret != 0) {
+			ret = errno;
+			TRACE_DEBUG(INFO, "Unable to set the socket SCTP_AUTO_ASCONF options: %s", strerror(ret));
+			return ret;
+		}
+		
+#ifdef DEBUG_SCTP
+		/* Check new values */
+		ret = getsockopt(sk, IPPROTO_SCTP, SCTP_AUTO_ASCONF, &asconf, &sz);
+		if (ret != 0) {
+			ret = errno;
+			TRACE_DEBUG(INFO, "Unable to get the socket SCTP_AUTO_ASCONF value: %s", strerror(ret));
+			return ret;
+		}
+		TRACE_DEBUG(FULL, "New SCTP_AUTO_ASCONF value : %s", asconf ? "true" : "false");
+#endif /* DEBUG_SCTP */
+	}
+	#else /* SCTP_AUTO_ASCONF */
+	TRACE_DEBUG(FULL, "Skipping SCTP_AUTO_ASCONF");
+	#endif /* SCTP_AUTO_ASCONF */
+	
+	/* Set the v4 mapped addresses option */
+	#ifdef SCTP_I_WANT_MAPPED_V4_ADDR
+	{
+		int v4mapped;
+		
+#ifdef DEBUG_SCTP
+		sz = sizeof(v4mapped);
+		/* Read socket defaults */
+		ret = getsockopt(sk, IPPROTO_SCTP, SCTP_I_WANT_MAPPED_V4_ADDR, &v4mapped, &sz);
+		if (ret != 0) {
+			ret = errno;
+			TRACE_DEBUG(INFO, "Unable to get the socket SCTP_I_WANT_MAPPED_V4_ADDR value: %s", strerror(ret));
+			return ret;
+		}
+		if (sz != sizeof(v4mapped))
+		{
+			ret = ENOTSUP;
+			TRACE_DEBUG(INFO, "Invalid size of socket option: %d / %d", sz, (socklen_t)sizeof(v4mapped));
+			return ret;
+		}
+		TRACE_DEBUG(FULL, "Def SCTP_I_WANT_MAPPED_V4_ADDR value : %s", v4mapped ? "true" : "false");
+#endif /* DEBUG_SCTP */
+
+		v4mapped = 0;	/* We don't want v4 mapped addresses */
+		v4mapped = 1;	/* but we have to, otherwise the bind fails under linux currently ... */
+		
+		/* Set the option to the socket */
+		ret = setsockopt(sk, IPPROTO_SCTP, SCTP_I_WANT_MAPPED_V4_ADDR, &v4mapped, sizeof(v4mapped));
+		if (ret != 0) {
+			ret = errno;
+			TRACE_DEBUG(INFO, "Unable to set the socket SCTP_I_WANT_MAPPED_V4_ADDR options: %s", strerror(ret));
+			return ret;
+		}
+		
+#ifdef DEBUG_SCTP
+		/* Check new values */
+		ret = getsockopt(sk, IPPROTO_SCTP, SCTP_I_WANT_MAPPED_V4_ADDR, &v4mapped, &sz);
+		if (ret != 0) {
+			ret = errno;
+			TRACE_DEBUG(INFO, "Unable to get the socket SCTP_I_WANT_MAPPED_V4_ADDR value: %s", strerror(ret));
+			return ret;
+		}
+		TRACE_DEBUG(FULL, "New SCTP_I_WANT_MAPPED_V4_ADDR value : %s", v4mapped ? "true" : "false");
+#endif /* DEBUG_SCTP */
+	}
+	#else /* SCTP_I_WANT_MAPPED_V4_ADDR */
+	TRACE_DEBUG(FULL, "Skipping SCTP_I_WANT_MAPPED_V4_ADDR");
+	#endif /* SCTP_I_WANT_MAPPED_V4_ADDR */
+			   
+			   
+	/* Other settable options (draft-ietf-tsvwg-sctpsocket-17):
+	   SO_RCVBUF			size of receiver window
+	   SO_SNDBUF			size of pending data to send
+	   SCTP_AUTOCLOSE		for one-to-many only
+	   SCTP_SET_PEER_PRIMARY_ADDR	ask remote peer to use this local address as primary
+	   SCTP_PRIMARY_ADDR		use this address as primary locally
+	   SCTP_ADAPTATION_LAYER	set adaptation layer indication 
+	   SCTP_PEER_ADDR_PARAMS	control heartbeat per peer address
+	   SCTP_DEFAULT_SEND_PARAM	parameters for the sendto() call
+	   SCTP_MAXSEG			max size of fragmented segments -- bound to PMTU
+	   SCTP_AUTH_CHUNK		request authentication of some type of chunk
+	    SCTP_HMAC_IDENT		authentication algorithms
+	    SCTP_AUTH_KEY		set a shared key
+	    SCTP_AUTH_ACTIVE_KEY	set the active key
+	    SCTP_AUTH_DELETE_KEY	remove a key
+	    SCTP_AUTH_DEACTIVATE_KEY	will not use that key anymore
+	   SCTP_DELAYED_SACK		control delayed acks
+	   SCTP_PARTIAL_DELIVERY_POINT	control partial delivery size
+	   SCTP_USE_EXT_RCVINFO		use extended receive info structure (information about the following message)
+	   SCTP_MAX_BURST		number of packets that can be burst emitted
+	   SCTP_CONTEXT			save a context information along with the association.
+	   SCTP_EXPLICIT_EOR		enable sending one message across several send calls
+	   SCTP_REUSE_PORT		share one listening port with several sockets
+	   
+	   read-only options:
+	   SCTP_STATUS			retrieve info such as number of streams, pending packets, state, ...
+	   SCTP_GET_PEER_ADDR_INFO	get information about a specific peer address of the association.
+	   SCTP_PEER_AUTH_CHUNKS	list of chunks the remote peer wants authenticated
+	   SCTP_LOCAL_AUTH_CHUNKS	list of chunks the local peer wants authenticated
+	   SCTP_GET_ASSOC_NUMBER	number of associations in a one-to-many socket
+	   SCTP_GET_ASSOC_ID_LIST	list of these associations
+	*/
+	
+	return 0;
+}	
+
+/**********************
+ * External functions 
+ *********************/
+ 
+/* Create a server listening SCTP socket */
+int tls_sctp_create_server( int * sock, uint16_t port, int streams )
+{
+/*
+ *     -> Create the socket        :  int socket(AF_INET6, SOCK_STREAM, IPPROTO_SCTP);
+ *     -> Set options (streams,...):  setsockopt(SCTP_INITMSG, ...)
+ *		 Some options are mandatory for TLS, such as SCTP_DISABLE_FRAGMENTS=OFF
+ *		 Some others are useful, such as SCTP_EVENTS.
+ *     -> (server) Bind the socket :  int bind( sd, addr, addrlen);
+ *     ->                   and/or :  int sctp_bindx( sd, addrs, addrcnt, flags);
+ *     -> (server) Listen for cnx  :  int listen( sd,  backlog);
+ */
+	int ret = 0;
+ 	int sk;
+	struct sockaddr_in sin;
+ 
+	TRACE_ENTRY("%p %hd %d", sock, port, streams);
+	
+	/* Check parameters */
+ 	if (sock == NULL) {
+		TRACE_DEBUG(INFO, "Invalid parameter");
+		return EINVAL;
+	}
+	
+	/* Create the socket */
+	sk = socket(AF_INET6, SOCK_STREAM, IPPROTO_SCTP);
+	if (sk == -1) {
+		ret = errno;
+		TRACE_DEBUG(INFO, "Unable to create SCTP socket: %s", strerror(ret));
+		return ret;
+	}
+	
+	/* Subscribe to notifications on this socket, so all children socket will inherit the options */
+	ret = _tls_sctp_sockopt(sk, (uint16_t) streams);
+	if (ret != 0) {
+		TRACE_DEBUG(INFO, "Unable to set all the socket options: %s", strerror(ret));
+		return ret;
+	}
+	
+	/* Address and port where we are listening */
+	memset(&sin, 0, sizeof(sin));
+	sin.sin_family = AF_INET;
+	sin.sin_port = htons(port);
+	sin.sin_addr.s_addr = INADDR_ANY;
+	
+	/* Bind the socket -- wildcard will allow multihoming to be activated. */
+	ret = bind(sk, (struct sockaddr *)&sin, sizeof(sin));
+	/* note: if more precise binding is needed, use sctp_bindx function */
+	if (ret == -1) {
+		ret = errno;
+		TRACE_DEBUG(INFO, "Unable to bind the socket: %s", strerror(ret));
+		return ret;
+	}
+	
+#ifdef DEBUG_SCTP
+	/* Debug: show all local listening addresses */
+	{
+		struct sockaddr *ss, *cur;
+		int i, sz;
+		sz = sctp_getladdrs(sk, 0, &ss);
+		if (sz == -1) {
+			ret = errno;
+			TRACE_DEBUG(INFO, "Unable to retrieve list of local addresses: %s", strerror(ret));
+			return ret;
+		}
+		cur = ss;
+		for (i=0; i<sz; i++) {
+			switch (cur->sa_family) {
+				case AF_INET:
+					{
+						char buf[INET_ADDRSTRLEN];
+						struct sockaddr_in *sin = (struct sockaddr_in *)cur;
+						cur = (struct sockaddr *)(sin + 1);
+						i++;
+						
+						TRACE_DEBUG(FULL, "Bound on local IP: %s", inet_ntop(AF_INET, &sin->sin_addr.s_addr, buf, sizeof(buf)));
+					}
+					break;
+		
+				case AF_INET6:
+					{
+						char buf[INET6_ADDRSTRLEN];
+						struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)cur;
+						cur = (struct sockaddr *)(sin6 + 1);
+						i++;
+						
+						TRACE_DEBUG(FULL, "Bound on local IPv6: %s", inet_ntop(AF_INET6, &sin6->sin6_addr.s6_addr, buf, sizeof(buf)));
+					}
+					break;
+		
+				default:
+					TRACE_DEBUG(FULL, "Bound on unknown address family: %d", cur->sa_family);
+					i=sz;
+			}
+		}
+				
+		sctp_freeladdrs(ss);
+	}
+#endif /* DEBUG_SCTP */
+	
+	/* Listen, to enable remote peers to connect */
+	ret = listen(sk, 2);
+	if (ret == -1) {
+		ret = errno;
+		TRACE_DEBUG(INFO, "Unable to listen on the socket: %s", strerror(ret));
+		return ret;
+	}
+	
+	*sock = sk;
+	return 0;
+}
+
+/* Accept client on a server listening SCTP socket */
+int tls_sctp_accept( int sock, int * new_sd, int *streams )
+{
+	int ret = 0;
+	int skcli;
+	struct sctp_status status;
+	socklen_t sz = sizeof(status);
+	
+	TRACE_ENTRY("%i %p %p", sock, new_sd, streams);
+	
+	/* Check params */
+	if ((new_sd == NULL) || (streams == NULL)) {
+		TRACE_DEBUG(INFO, "Invalid parameter");
+		return EINVAL;
+	}
+	
+	/* Now wait for a client connection */
+	TRACE_DEBUG(FULL, "Waiting for client connection...");
+	skcli = accept(sock, NULL, NULL);
+	if (skcli == -1) {
+		ret = errno;
+		TRACE_DEBUG(INFO, "Unable to accept incoming connection: %s", strerror(ret));
+		return ret;
+	}
+	
+	/* We now have a client connected, get some information */
+	
+	/* Read the association parameters */
+	memset(&status, 0, sizeof(status));
+	ret = getsockopt(skcli, IPPROTO_SCTP, SCTP_STATUS, &status, &sz);
+	if (ret != 0) {
+		ret = errno;
+		TRACE_DEBUG(INFO, "Unable to get the socket SCTP_STATUS value: %s", strerror(ret));
+		return ret;
+	}
+	if (sz != sizeof(status))
+	{
+		ret = ENOTSUP;
+		TRACE_DEBUG(INFO, "Invalid size of socket option: %d / %d", sz, (socklen_t)sizeof(status));
+		return ret;
+	}
+#ifdef DEBUG_SCTP
+	TRACE_DEBUG(FULL, "Accepted Cli SCTP_STATUS sstat_state               : %i" , status.sstat_state);
+	TRACE_DEBUG(FULL, "Accepted Cli SCTP_STATUS sstat_rwnd                : %u" , status.sstat_rwnd);
+	TRACE_DEBUG(FULL, "Accepted Cli SCTP_STATUS sstat_unackdata           : %hu", status.sstat_unackdata);
+	TRACE_DEBUG(FULL, "Accepted Cli SCTP_STATUS sstat_penddata            : %hu", status.sstat_penddata);
+	TRACE_DEBUG(FULL, "Accepted Cli SCTP_STATUS sstat_instrms             : %hu", status.sstat_instrms);
+	TRACE_DEBUG(FULL, "Accepted Cli SCTP_STATUS sstat_outstrms            : %hu", status.sstat_outstrms);
+	TRACE_DEBUG(FULL, "Accepted Cli SCTP_STATUS sstat_fragmentation_point : %u" , status.sstat_fragmentation_point);
+	{
+		char *str, addrbuf[INET6_ADDRSTRLEN];
+		DUMP_SA( &status.sstat_primary.spinfo_address, str );
+		TRACE_DEBUG(FULL, "Accepted Cli SCTP_STATUS sstat_primary.spinfo_address : %s" , str);
+	}
+	TRACE_DEBUG(FULL, "Accepted Cli SCTP_STATUS sstat_primary.spinfo_state   : %d" , status.sstat_primary.spinfo_state);
+	TRACE_DEBUG(FULL, "Accepted Cli SCTP_STATUS sstat_primary.spinfo_cwnd    : %u" , status.sstat_primary.spinfo_cwnd);
+	TRACE_DEBUG(FULL, "Accepted Cli SCTP_STATUS sstat_primary.spinfo_srtt    : %u" , status.sstat_primary.spinfo_srtt);
+	TRACE_DEBUG(FULL, "Accepted Cli SCTP_STATUS sstat_primary.spinfo_rto     : %u" , status.sstat_primary.spinfo_rto);
+	TRACE_DEBUG(FULL, "Accepted Cli SCTP_STATUS sstat_primary.spinfo_mtu     : %u" , status.sstat_primary.spinfo_mtu);
+#endif /* DEBUG_SCTP */
+	
+	if (status.sstat_instrms > status.sstat_outstrms)
+		*streams = status.sstat_outstrms;
+	else
+		*streams = status.sstat_instrms;
+	
+	*new_sd = skcli;
+	
+	return 0;
+}
+
+/* Connect client to a remote server listening SCTP socket */
+int tls_sctp_connect( int *sock, const struct sockaddr *addr, socklen_t addrlen, int *streams )
+{
+	int ret = 0;
+ 	int sk;
+	struct sctp_status status;
+	socklen_t sz = sizeof(status);
+ 
+	TRACE_ENTRY("%p %p %d %p", sock, addr, addrlen, streams);
+	
+	/* Check parameters */
+	if ((sock == NULL) || (addr == NULL) || (streams == NULL)) {
+		TRACE_DEBUG(INFO, "Invalid parameter");
+		return EINVAL;
+	}
+	
+	/* Create the socket */
+	sk = socket(AF_INET6, SOCK_STREAM, IPPROTO_SCTP);
+	if (sk == -1) {
+		ret = errno;
+		TRACE_DEBUG(INFO, "Unable to create SCTP socket: %s", strerror(ret));
+		return ret;
+	}
+	
+	/* Subscribe to notifications on this socket */
+	ret = _tls_sctp_sockopt(sk, (uint16_t) *streams);
+	if (ret != 0) {
+		TRACE_DEBUG(INFO, "Unable to set all the socket options: %s", strerror(ret));
+		return ret;
+	}
+	
+	/* Now connect to the server */
+	{
+		char *str, addrbuf[INET6_ADDRSTRLEN];
+		DUMP_SA( addr, str );
+		TRACE_DEBUG(FULL, "Connection to %s ..." , str);
+	}
+	
+	ret = connect(sk, addr, addrlen);
+	if (ret == -1) {
+		ret = errno;
+		TRACE_DEBUG(INFO, "Unable to connect to server: %s", strerror(ret));
+		return ret;
+	}
+	
+	*sock = sk;
+	
+	/* Now that we are connected, retrieve the number of streams */
+	
+	/* Read the association parameters */
+	memset(&status, 0, sizeof(status));
+	ret = getsockopt(sk, IPPROTO_SCTP, SCTP_STATUS, &status, &sz);
+	if (ret != 0) {
+		ret = errno;
+		TRACE_DEBUG(INFO, "Unable to get the socket SCTP_STATUS value: %s", strerror(ret));
+		return ret;
+	}
+	if (sz != sizeof(status))
+	{
+		ret = ENOTSUP;
+		TRACE_DEBUG(INFO, "Invalid size of socket option: %d / %d", sz, (socklen_t)sizeof(status));
+		return ret;
+	}
+#ifdef DEBUG_SCTP
+	TRACE_DEBUG(FULL, "Cli SCTP_STATUS sstat_state               : %i" , status.sstat_state);
+	TRACE_DEBUG(FULL, "Cli SCTP_STATUS sstat_rwnd                : %u" , status.sstat_rwnd);
+	TRACE_DEBUG(FULL, "Cli SCTP_STATUS sstat_unackdata           : %hu", status.sstat_unackdata);
+	TRACE_DEBUG(FULL, "Cli SCTP_STATUS sstat_penddata            : %hu", status.sstat_penddata);
+	TRACE_DEBUG(FULL, "Cli SCTP_STATUS sstat_instrms             : %hu", status.sstat_instrms);
+	TRACE_DEBUG(FULL, "Cli SCTP_STATUS sstat_outstrms            : %hu", status.sstat_outstrms);
+	TRACE_DEBUG(FULL, "Cli SCTP_STATUS sstat_fragmentation_point : %u" , status.sstat_fragmentation_point);
+	{
+		char *str, addrbuf[INET6_ADDRSTRLEN];
+		DUMP_SA( &status.sstat_primary.spinfo_address, str );
+		TRACE_DEBUG(FULL, "Cli SCTP_STATUS sstat_primary.spinfo_address : %s" , str);
+	}
+	TRACE_DEBUG(FULL, "Cli SCTP_STATUS sstat_primary.spinfo_state	: %d" , status.sstat_primary.spinfo_state);
+	TRACE_DEBUG(FULL, "Cli SCTP_STATUS sstat_primary.spinfo_cwnd	: %u" , status.sstat_primary.spinfo_cwnd);
+	TRACE_DEBUG(FULL, "Cli SCTP_STATUS sstat_primary.spinfo_srtt	: %u" , status.sstat_primary.spinfo_srtt);
+	TRACE_DEBUG(FULL, "Cli SCTP_STATUS sstat_primary.spinfo_rto	: %u" , status.sstat_primary.spinfo_rto);
+	TRACE_DEBUG(FULL, "Cli SCTP_STATUS sstat_primary.spinfo_mtu	: %u" , status.sstat_primary.spinfo_mtu);
+#endif /* DEBUG_SCTP */
+	
+	if (status.sstat_instrms > status.sstat_outstrms)
+		*streams = status.sstat_outstrms;
+	else
+		*streams = status.sstat_instrms;
+	
+	return 0;
+}
+
+
+/* wrapper to sendmsg */
+ssize_t tls_sctp_send(int socket, int streamid, const void * data, size_t len)
+{
+	struct msghdr mhdr;
+	struct iovec  iov;
+	struct {
+		struct cmsghdr 		hdr;
+		struct sctp_sndrcvinfo	sndrcv;
+	} anci;
+	
+	memset(&mhdr, 0, sizeof(mhdr));
+	memset(&iov,  0, sizeof(iov));
+	memset(&anci, 0, sizeof(anci));
+	
+	/* IO Vector: message data */
+	iov.iov_base = (unsigned char *)data;
+	iov.iov_len  = len;
+	
+	/* Anciliary data: specify SCTP stream */
+	anci.hdr.cmsg_len   = sizeof(anci);
+	anci.hdr.cmsg_level = IPPROTO_SCTP;
+	anci.hdr.cmsg_type  = SCTP_SNDRCV;
+	anci.sndrcv.sinfo_stream = (uint16_t) streamid;
+	  /* note : we can store some data also in .sinfo_ppid (for remote peer) and .sinfo_context (for errors) */
+	
+	/* We don't use mhdr.msg_name here; it could be used to specify an address different of the primary */
+	
+	mhdr.msg_iov    = &iov;
+	mhdr.msg_iovlen = 1;
+	
+	mhdr.msg_control    = &anci;
+	mhdr.msg_controllen = sizeof(anci);
+	
+	TRACE_DEBUG(FULL, "Sending SCTP packet, len: %d, stream %d", len, streamid);
+	
+	return sendmsg(socket, &mhdr, 0);
+}
+
+#define CMSG_BUF_LEN	1024
+
+/* wrapper to recvmsg */
+ssize_t tls_sctp_recv(int socket, tls_sctp_recvevents * event, int * streamid, void ** data, size_t *len, size_t init)
+{
+	ssize_t ret = 0;
+	struct msghdr mhdr;
+	char   ancidata[ CMSG_BUF_LEN ];	/* Prepare to receive lot of anciliary data ... this probably may be shrinked */
+	struct cmsghdr 		*hdr;
+	struct sctp_sndrcvinfo	*sndrcv;
+	struct iovec  iov;
+	size_t sz = init;
+	
+	*event = RCV_NO_EVENT;
+	*streamid = -1;
+	*data = NULL;
+	*len = 0;
+	
+	*data = malloc(sz);
+	if (*data == NULL) {
+		TRACE_DEBUG(INFO, "Malloc failed");
+		return -1;
+	}
+	
+	memset(&mhdr, 0, sizeof(mhdr));
+	mhdr.msg_iov    = &iov;
+	mhdr.msg_iovlen = 1;
+	
+	mhdr.msg_control    = &ancidata;
+	mhdr.msg_controllen = sizeof(ancidata);
+	
+	/* We will loop while all data is not received. */
+incomplete:
+
+	/* the new data will be received following the preceding */
+	memset(&iov,  0, sizeof(iov));
+	iov.iov_base = ((unsigned char *)*data) + *len ;
+	iov.iov_len  = sz - *len;
+
+	ret = recvmsg(socket, &mhdr, 0);
+	if (ret <= 0) {
+		TRACE_DEBUG(INFO, "recvmsg return %d, errno: %s", ret, strerror(errno));
+		return ret;
+	}
+
+	/* we have received new data */
+	*len += ret;
+	
+	/* loop if we have not received a full message yet */
+	if ( (mhdr.msg_flags & MSG_EOR) == 0 ) {
+		/* if buffer is full */
+		if (*len == sz) {
+			
+			sz *=2;
+			
+			/* enlarge the buffer to receive more data */
+			*data = realloc(*data, sz);
+			if (*data == NULL) {
+				TRACE_DEBUG(INFO, "Malloc failed");
+				return -1;
+			}
+		}
+		goto incomplete;
+	}
+	
+	/* Now we have a complete message in *data */
+	
+	/* Now handle notifications */
+	if (mhdr.msg_flags & MSG_NOTIFICATION) {
+		char *str, addrbuf[INET6_ADDRSTRLEN];
+		union sctp_notification * notif = (union sctp_notification *) *data;
+		switch (notif->sn_header.sn_type) {
+			case SCTP_ASSOC_CHANGE:
+				TRACE_DEBUG(FULL, "Received SCTP_ASSOC_CHANGE notification");
+				TRACE_DEBUG(FULL, "    state : %hu", notif->sn_assoc_change.sac_state);
+				TRACE_DEBUG(FULL, "    error : %hu", notif->sn_assoc_change.sac_error);
+				TRACE_DEBUG(FULL, "    instr : %hu", notif->sn_assoc_change.sac_inbound_streams);
+				TRACE_DEBUG(FULL, "   outstr : %hu", notif->sn_assoc_change.sac_outbound_streams);
+				
+				*event = RCV_CHANGE;
+				break;
+	
+			case SCTP_PEER_ADDR_CHANGE:
+				TRACE_DEBUG(FULL, "Received SCTP_PEER_ADDR_CHANGE notification");
+				DUMP_SA( &(notif->sn_paddr_change.spc_aaddr), str );
+				TRACE_DEBUG(FULL, "    intf_change : %s", str);
+				TRACE_DEBUG(FULL, "          state : %d", notif->sn_paddr_change.spc_state);
+				TRACE_DEBUG(FULL, "          error : %d", notif->sn_paddr_change.spc_error);
+				
+				*event = RCV_CHANGE;
+				break;
+	
+			case SCTP_SEND_FAILED:
+				TRACE_DEBUG(FULL, "Received SCTP_SEND_FAILED notification");
+				TRACE_DEBUG(FULL, "    len : %hu", notif->sn_send_failed.ssf_length);
+				TRACE_DEBUG(FULL, "    err : %d",  notif->sn_send_failed.ssf_error);
+				
+				*event = RCV_ERROR;
+				break;
+			
+			case SCTP_REMOTE_ERROR:
+				TRACE_DEBUG(FULL, "Received SCTP_REMOTE_ERROR notification");
+				TRACE_DEBUG(FULL, "    err : %hu", ntohs(notif->sn_remote_error.sre_error));
+				TRACE_DEBUG(FULL, "    len : %hu", ntohs(notif->sn_remote_error.sre_length));
+				
+				*event = RCV_ERROR;
+				break;
+	
+			case SCTP_SHUTDOWN_EVENT:
+				TRACE_DEBUG(FULL, "Received SCTP_SHUTDOWN_EVENT notification");
+				
+				*event = RCV_SHUTDOWN;
+				break;
+			
+			default:	
+				TRACE_DEBUG(FULL, "Received unknown notification %d", notif->sn_header.sn_type);
+		}
+		
+		/* Free the data buffer and return 0 */
+		free(*data);
+		*data = NULL;
+		*len = 0;
+		return 0;
+	}
+	
+	/* Now handle the anciliary data */
+	for (hdr = CMSG_FIRSTHDR(&mhdr); hdr; hdr = CMSG_NXTHDR(&mhdr, hdr)) {
+		
+		/* We deal only we anciliary data at SCTP level */
+		if (hdr->cmsg_level != IPPROTO_SCTP) {
+			TRACE_DEBUG(FULL, "Received some anciliary data at level %d, skipped", hdr->cmsg_level);
+			continue;
+		}
+		
+		switch (hdr->cmsg_type) {
+			case SCTP_SNDRCV:
+				sndrcv = (struct sctp_sndrcvinfo *) CMSG_DATA(hdr);
+#ifdef DEBUG_SCTP
+				TRACE_DEBUG(FULL, "Anciliary block IPPROTO_SCTP / SCTP_SNDRCV");
+				TRACE_DEBUG(FULL, "    sinfo_stream    : %hu", sndrcv->sinfo_stream);
+				TRACE_DEBUG(FULL, "    sinfo_ssn       : %hu", sndrcv->sinfo_ssn);
+				TRACE_DEBUG(FULL, "    sinfo_flags     : %hu", sndrcv->sinfo_flags);
+				// TRACE_DEBUG(FULL, "    sinfo_pr_policy : %hu", sndrcv->sinfo_pr_policy);
+				TRACE_DEBUG(FULL, "    sinfo_ppid      : %u" , sndrcv->sinfo_ppid);
+				TRACE_DEBUG(FULL, "    sinfo_context   : %u" , sndrcv->sinfo_context);
+				// TRACE_DEBUG(FULL, "    sinfo_pr_value  : %u" , sndrcv->sinfo_pr_value);
+				TRACE_DEBUG(FULL, "    sinfo_tsn       : %u" , sndrcv->sinfo_tsn);
+				TRACE_DEBUG(FULL, "    sinfo_cumtsn    : %u" , sndrcv->sinfo_cumtsn);
+#endif /* DEBUG_SCTP */
+				
+				*streamid = (int) sndrcv->sinfo_stream;
+				break;
+				
+			default:
+				TRACE_DEBUG(FULL, "Anciliary block IPPROTO_SCTP / %d, skipped", hdr->cmsg_type);
+		}
+	}
+				
+	/* Okay, we have received a normal message */
+	TRACE_DEBUG(FULL, "Received SCTP packet, len: %d, stream %d", *len, *streamid);
+	
+	return *len;
+}		
+
+#endif /* SKIP_SCTP_WRAPPERS */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/extensions/sec_tls_gnutls/gnutls_sctp_wrapper.h	Fri Aug 15 17:53:04 2008 +0900
@@ -0,0 +1,319 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@nict.go.jp>							 *
+*													 *
+* Copyright (c) 2008, 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.								 *
+*********************************************************************************************************/
+
+/* 
+ * Wrapper functions to use GNU TLS library to protect multi-stream SCTP association.
+ *
+ *
+ * This wrapper creates N+1 threads, where N is the number of streams in the association.
+ *
+ * To use this wrapper one must:
+ *  - first, create a one-to-one SCTP association with another peer (draft-ietf-tsvwg-sctpsocket-17):
+ *     -> Create the socket        :  int socket(PF_INET6, SOCK_STREAM, IPPROTO_SCTP);
+ *     -> Set options (streams,...):  setsockopt(SCTP_INITMSG, ...)
+ *		 Some options are mandatory for TLS, such as SCTP_DISABLE_FRAGMENTS=OFF
+ *		 Some others are useful, such as SCTP_EVENTS.
+ *     -> (server) Bind the socket :  int bind( sd, addr, addrlen);
+ *     ->                   and/or :  int sctp_bindx( sd, addrs, addrcnt, flags);
+ *     -> (server) Listen for cnx  :  int listen( sd,  backlog);
+ *     -> (server) Accept clients  :  new_sd = accept( sd, *addr, *addrlen);
+ *     -> (client) Connect to serv :  int connect( sd, addr, addrlen);
+ *    Note that the helper functions tls_sctp_create_server / tls_sctp_accept / tls_sctp_connect can be used for this.
+ * 
+ *  - Initialize the wrapper context for this association:
+ *     int tls_sctpw_init ( new_sd, streams, &psctx );
+ *
+ *  - Handshake. One may need to change content to adapt to one's need for security (authentication of certificates, ...)
+ *	int tls_sctpw_handshake( tls_sctpw_ctx * sctx );
+ *
+ *  - Send and receive data on the association, protected with TLS.
+ *      int tls_sctpw_send( tls_sctpw_ctx * sctx, int stream, void * data, size_t len );
+ *      int tls_sctpw_recv( tls_sctpw_ctx * sctx, int *stream, void ** data, size_t *len );
+ *
+ *  - Terminate after use, either properly or on error.
+ *      int tls_sctpw_close( tls_sctpw_ctx ** sctx );
+ *      int tls_sctpw_destroy( tls_sctpw_ctx ** sctx );
+ *
+ *  - Finally, close or abord the SCTP association
+ *      int close(int sd);
+ *      see SO_LINGER sockopt to abord...
+ */
+
+#ifndef _SCTP_WRAPPER_H
+#define _SCTP_WRAPPER_H
+
+#include <sys/socket.h>
+#include <sys/types.h>
+
+/****************************************************************************
+*         SCTP helper functions, to manage SCTP connections                 *
+****************************************************************************/ 
+/*
+ * FUNCTION:	tls_sctp_create_server
+ *
+ * PARAMETERS:
+ *  sock    : (out) On return, the identifier of the bound socket is stored here.
+ *  port    : (in) The port on which the server is listening for connections.
+ *  streams : (in) The maximum number of pairs of streams that we will use.
+ *
+ * DESCRIPTION: 
+ *  Creates a listening SCTP bound socket, with some default options.
+ *
+ * RETURN VALUE:
+ *   0 : The socket has been created, bound, and is listening for connections.
+ *  !0 : an error occurred (standard errno are returned, such as ENOTSUP)
+ */
+int tls_sctp_create_server( int * sock, uint16_t port, int streams );
+		 
+/*
+ * FUNCTION:	tls_sctp_accept
+ *
+ * PARAMETERS:
+ *  sock    : The listening server socket from which to accept clients.
+ *  new_sd  : on return, the new socket for this client.
+ *  streams : the number of streams that can be used for TLS with this client.
+ *
+ * DESCRIPTION: 
+ *  Accepts an incoming connection and get the number of bi-lateral streams.
+ *
+ * RETURN VALUE:
+ *   0 : A new client has arrived.
+ *  !0 : an error occurred (standard errno are returned)
+ */
+int tls_sctp_accept( int sock, int * new_sd, int *streams );
+
+/*
+ * FUNCTION:	tls_sctp_connect
+ *
+ * PARAMETERS:
+ *  sock    : The client socket is stored here on return.
+ *  addr    : address of the server, that is passed to connect() function.
+ *  addrlen : length of addr structure.
+ *  streams : (in/out) the number or streams desired / usable for TLS.
+ *
+ * DESCRIPTION: 
+ *  Connect to a server and retrieve the number of usable pairs of streams.
+ *
+ * RETURN VALUE:
+ *   0 : The client is connected.
+ *  !0 : an error occurred (standard errno are returned)
+ */
+int tls_sctp_connect( int *sock, const struct sockaddr *addr, socklen_t addrlen, int *streams );
+
+/*
+ * FUNCTION:	tls_sctp_send
+ *
+ * PARAMETERS:
+ *  socket  : The socket on which to send a message.
+ *  streamid: The stream on which the message must be sent.
+ *  data    : pointer to the data that must be sent.
+ *  len     : length of the data buffer.
+ *
+ * DESCRIPTION: 
+ *  Send some data over an SCTP association on a given stream.
+ * The return values and error codes are the same as sendmsg().
+ *
+ * RETURN VALUE:
+ *   -1 : error (errno is set)
+ *  !-1 : number of bytes sent.
+ */
+ssize_t tls_sctp_send(int socket, int streamid, const void * data, size_t len);
+
+/* Some events that may be received as notifications in next function */
+typedef enum {
+	RCV_NO_EVENT = 0,	/* no special event was received */
+	RCV_CHANGE,		/* Notified of a change on association or address */
+	RCV_ERROR,		/* Notified of an error in sending or remote handling */
+	RCV_SHUTDOWN,		/* Notified of a shutdown */
+	RCV_MAX			/* the last defined value */
+} tls_sctp_recvevents;
+
+/*
+ * FUNCTION:	tls_sctp_recv
+ *
+ * PARAMETERS:
+ *  socket  : The socket on which to receive the next message.
+ *  event   : if an event is received, it is stored here.
+ *  streamid: if relevant, the stream id on which data or event is received.
+ *  data    : a buffer continaing the received data.
+ *  len     : the size of the data buffer
+ *  init    : the initial size of the buffer
+ *
+ * DESCRIPTION: 
+ *  Receive data and notifications on a SCTP socket.
+ * The data buffer is allocated and must be freed after use.
+ * The return values and error codes are the same as recvmsg().
+ *
+ * RETURN VALUE:
+ *  -1 : an error occurred
+ *   0 : an event was received or shutdown is in progress.
+ *  >0 : length of data written in the data buffer.
+ */
+ssize_t tls_sctp_recv(int socket, tls_sctp_recvevents * event, int * streamid, void ** data, size_t *len, size_t init);
+
+#endif /* _SCTP_WRAPPER_H */
+
+#ifndef _GNUTLS_SCTP_WRAPPER_H
+#define _GNUTLS_SCTP_WRAPPER_H
+
+#include <gnutls/gnutls.h>
+#include <gcrypt.h>
+
+
+/****************************************************************************
+*        TLS over SCTP wrapper library -- to use GNU TLS over SCTP          *
+****************************************************************************/ 
+/* A wrapper context object, to be used in the functions of this file */
+typedef void tls_sctpw_ctx;
+
+/*
+ * FUNCTION:	tls_sctpw_init
+ *
+ * PARAMETERS:
+ *  sock    : The socket object representing the (one-to-one style) SCTP association.
+ *  streams : The number of bi-lateral streams in this association.
+ *  sctx    : On success, the wrapper context is stored here.
+ *  client  : if 0, this side is server, otherwise this side is client
+ *  credtype: type of credentials, GNUTLS_CRD_ANON, GNUTLS_CRD_SRP or GNUTLS_CRD_CERTIFICATE
+ *  creds   : the credentials to present to the remote peer
+ *  priocb  : optional, a callback to set the priority of algorithms in the session
+ *            prototype is:     int priocb (gnutls_session_t * session);
+ *
+ * DESCRIPTION: 
+ *  Creates a wrapper context for one SCTP multi-stream association.
+ *
+ * RETURN VALUE:
+ *   0 : The context has been created sucessfully.
+ *  !0 : an error occurred (standard errno are returned, such as ENOMEM)
+ */
+int tls_sctpw_init( int sock, int streams, tls_sctpw_ctx ** sctx, int client, gnutls_credentials_type_t credtype, void ** creds, int (*priocb)(gnutls_session_t *) );
+
+/*
+ * FUNCTION:	tls_sctpw_handshake
+ *
+ * PARAMETERS:
+ *  sctx    : The wrapper context of the connection where handshake must be started.
+ *  checkcb : a callback to verify the credentials of the remote peer.
+ *  parm    : argument to pass to the checkcb function
+ *
+ * DESCRIPTION: 
+ *  Will handshake on all pairs of streams of the association.
+ * A full handshake is done on each pair. This can be improved by resuming sessions
+ * on other streams after the first handshake is achieved.
+ * The checkcb callback can be provided to verify the credentials of the remote peer.
+ * the prototype is:
+ *     int checkcb ( gnutls_session_t session, void * parm)
+ * The function must return 0 if credentials are valid, or a standard errno value otherwise.
+ *
+ * RETURN VALUE:
+ *   0 : Ok.
+ *  !0 : An error occurred (standard errno are returned)
+ */
+int tls_sctpw_handshake( tls_sctpw_ctx * sctx, int (*checkcb)(gnutls_session_t session, void * parm), void * parm );
+
+/*
+ * FUNCTION:	tls_sctpw_send
+ *
+ * PARAMETERS:
+ *  sctx    : The wrapper context of the connection.
+ *  stream  : The stream id on which the data must be sent.
+ *  data    : The data that must be sent.
+ *  len     : the size of data to send.
+ *
+ * DESCRIPTION: 
+ *  Will send data over the association, protected by TLS.
+ * If the stream is "-1", the next stream for the previously sent message is used.
+ * Otherwise, the stream must be between 0 and the number of streams of the association.
+ * Note that despite we are using SCTP, the message boundaries are not preserved with TLS.
+ *
+ * RETURN VALUE:
+ *   0 : Ok.
+ *  !0 : An error occurred (standard errno are returned)
+ */
+int tls_sctpw_send( tls_sctpw_ctx * sctx, int stream, unsigned char * data, size_t len );
+
+/*
+ * FUNCTION:	tls_sctpw_recv
+ *
+ * PARAMETERS:
+ *  sctx    : The wrapper context of the connection.
+ *  stream  : on return, the stream id on which the data was received.
+ *  data    : on return, pointer to received data. This must be freed after use.
+ *  len     : on return, the length of the received data.
+ *
+ * DESCRIPTION: 
+ *  Receive data from any stream on an SCTP association.
+ * The buffer is dynamically allocated and must be freed after use.
+ *
+ * RETURN VALUE:
+ *   0 : Ok.
+ *  !0 : An error occurred (standard errno are returned)
+ */
+int tls_sctpw_recv( tls_sctpw_ctx * sctx, int *stream, void ** data, size_t *len );
+
+/*
+ * FUNCTION:	tls_sctpw_close
+ *
+ * PARAMETERS:
+ *  sctx    : The wrapper context of the connection.
+ *
+ * DESCRIPTION: 
+ *  Terminates the TLS sessions properly, then free the wrapper resources.
+ *
+ * RETURN VALUE:
+ *   0 : Ok.
+ *  !0 : An error occurred (standard errno are returned)
+ */
+int tls_sctpw_close( tls_sctpw_ctx ** sctx );
+
+/*
+ * FUNCTION:	tls_sctpw_destroy
+ *
+ * PARAMETERS:
+ *  sctx    : The wrapper context of the connection.
+ *
+ * DESCRIPTION: 
+ *  Terminates the TLS sessions abruptly, and free the wrapper resources.
+ *  Use this when the connection is broken and no data can be exchanged anymore with remote peer.
+ *
+ * RETURN VALUE:
+ *   0 : Ok.
+ *  !0 : An error occurred (standard errno are returned)
+ */
+int tls_sctpw_destroy( tls_sctpw_ctx ** sctx );
+
+
+
+#endif /* _GNUTLS_SCTP_WRAPPER_H */
--- a/extensions/sec_tls_gnutls/sec_tls_gnutls.h	Fri Aug 15 17:52:26 2008 +0900
+++ b/extensions/sec_tls_gnutls/sec_tls_gnutls.h	Fri Aug 15 17:53:04 2008 +0900
@@ -45,6 +45,8 @@
 
 #include <string.h>
 
+#include "gnutls_sctp_wrapper.h"
+
 /* The value of the Inband-Security-Id that this extension provides */
 #define SEC_TLS_INBAND_SECURITY_ID	1
 
"Welcome to our mercurial repository"