view waaad/peer-sctp.c @ 338:be43a4344cf9

sctp_adaptation_layer_event not supported on some platform, and this code was not needed anyway. Reported by Chris Brody.
author Sebastien Decugis <sdecugis@nict.go.jp>
date Wed, 01 Apr 2009 11:17:11 +0900
parents 436975f6ef4d
children e86dba02630a
line wrap: on
line source

/*********************************************************************************************************
* 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.								 *
*********************************************************************************************************/

/* Peers facility.
 *
 * This file contains the code to handle SCTP connections to a peer.
 *
 */

#include "waaad-internal.h"
#include "peer-internal.h"

#include <netinet/sctp.h>
#include <sys/uio.h>
#include <unistd.h>

/* Size of buffer to receive ancilliary data. May need to be enlarged if more sockopt are set... */
#define CMSG_BUF_LEN	1024


/*******************************************************************************************************/
/* local functions */

static int _sctp_setsockopt_pre(int sk, uint16_t nstreams)
{
	#ifdef DEBUG_SCTP
	socklen_t sz;
	#endif /* DEBUG_SCTP */
	
	TRACE_ENTRY( "%d %hd", sk, nstreams);
	
	CHECK_PARAMS( (sk > 0) && nstreams );
	
	/* Subscribe to some 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	= 0;	/* new or closed associations (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 = 0;	/* adaptation layer notifications */
		// event.sctp_authentication_event = 0;	/* when new key is made active */

		/* Set the option to the socket */
		CHECK_SYS(  setsockopt(sk, IPPROTO_SCTP, SCTP_EVENTS, &event, sizeof(event)) );
		
		#ifdef DEBUG_SCTP
		#if 0
		sz = sizeof(event);
		CHECK_SYS(  getsockopt(sk, IPPROTO_SCTP, SCTP_EVENTS, &event, &sz) );
		if (sz != sizeof(event))
		{
			TRACE_DEBUG(INFO, "Invalid size of socket option: %d / %d", sz, (socklen_t)sizeof(event));
			return ENOTSUP;
		}
		#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 */
		CHECK_SYS(  getsockopt(sk, IPPROTO_SCTP, SCTP_INITMSG, &init, &sz)  );
		if (sz != sizeof(init))
		{
			TRACE_DEBUG(INFO, "Invalid size of socket option: %d / %d", sz, (socklen_t)sizeof(init));
			return ENOTSUP;
		}
		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_init_timeo = CNX_TIMEOUT * 1000;

		/* Set the option to the socket */
		CHECK_SYS(  setsockopt(sk, IPPROTO_SCTP, SCTP_INITMSG, &init, sizeof(init))  );
		
		#ifdef DEBUG_SCTP
		/* Check new values */
		CHECK_SYS(  getsockopt(sk, IPPROTO_SCTP, SCTP_INITMSG, &init, &sz)  );
		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 */
		CHECK_SYS(  getsockopt(sk, IPPROTO_SCTP, SCTP_DISABLE_FRAGMENTS, &nofrag, &sz)  );
		if (sz != sizeof(nofrag))
		{
			TRACE_DEBUG(INFO, "Invalid size of socket option: %d / %d", sz, (socklen_t)sizeof(nofrag));
			return ENOTSUP;
		}
		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 */
		CHECK_SYS(  setsockopt(sk, IPPROTO_SCTP, SCTP_DISABLE_FRAGMENTS, &nofrag, sizeof(nofrag))  );
		
		#ifdef DEBUG_SCTP
		/* Check new values */
		CHECK_SYS(  getsockopt(sk, IPPROTO_SCTP, SCTP_DISABLE_FRAGMENTS, &nofrag, &sz)  );
		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 */
		CHECK_SYS(  getsockopt(sk, IPPROTO_SCTP, SCTP_RTOINFO, &rtoinfo, &sz)  );
		if (sz != sizeof(rtoinfo))
		{
			TRACE_DEBUG(INFO, "Invalid size of socket option: %d / %d", sz, (socklen_t)sizeof(rtoinfo));
			return ENOTSUP;
		}
		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_max     = g_pconf->twtimer * 500;	/* Maximum retransmit timer (in ms) (set to Tw / 2) */

		/* Set the option to the socket */
		CHECK_SYS(  setsockopt(sk, IPPROTO_SCTP, SCTP_RTOINFO, &rtoinfo, sizeof(rtoinfo))  );
		
		#ifdef DEBUG_SCTP
		/* Check new values */
		CHECK_SYS(  getsockopt(sk, IPPROTO_SCTP, SCTP_RTOINFO, &rtoinfo, &sz)  );
		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 */
		CHECK_SYS(  getsockopt(sk, IPPROTO_SCTP, SCTP_ASSOCINFO, &assoc, &sz)  );
		if (sz != sizeof(assoc))
		{
			TRACE_DEBUG(INFO, "Invalid size of socket option: %d / %d", sz, (socklen_t)sizeof(assoc));
			return ENOTSUP;
		}
		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_asocmaxrxt = 5;	/* Maximum retransmission attempts: we want fast detection of errors */
		
		/* Set the option to the socket */
		CHECK_SYS(  setsockopt(sk, IPPROTO_SCTP, SCTP_ASSOCINFO, &assoc, sizeof(assoc))  );
		
		#ifdef DEBUG_SCTP
		/* Check new values */
		CHECK_SYS(  getsockopt(sk, IPPROTO_SCTP, SCTP_ASSOCINFO, &assoc, &sz)  );
		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 re-set 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 */
		CHECK_SYS(  getsockopt(sk, SOL_SOCKET, SO_LINGER, &linger, &sz)  );
		if (sz != sizeof(linger))
		{
			TRACE_DEBUG(INFO, "Invalid size of socket option: %d / %d", sz, (socklen_t)sizeof(linger));
			return ENOTSUP;
		}
		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 */
		CHECK_SYS(  setsockopt(sk, SOL_SOCKET, SO_LINGER, &linger, sizeof(linger))  );
		
		#ifdef DEBUG_SCTP
		/* Check new values */
		CHECK_SYS(  getsockopt(sk, SOL_SOCKET, SO_LINGER, &linger, &sz)  );
		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 */
		CHECK_SYS(  getsockopt(sk, IPPROTO_SCTP, SCTP_NODELAY, &nodelay, &sz)  );
		if (sz != sizeof(nodelay))
		{
			TRACE_DEBUG(INFO, "Invalid size of socket option: %d / %d", sz, (socklen_t)sizeof(nodelay));
			return ENOTSUP;
		}
		TRACE_DEBUG(FULL, "Def SCTP_NODELAY value : %s", nodelay ? "true" : "false");
		#endif /* DEBUG_SCTP */

		nodelay = 0;	/* We turn ON the Nagle algorithm (probably the default already) */
		
		/* Set the option to the socket */
		CHECK_SYS(  setsockopt(sk, IPPROTO_SCTP, SCTP_NODELAY, &nodelay, sizeof(nodelay))  );
		
		#ifdef DEBUG_SCTP
		/* Check new values */
		CHECK_SYS(  getsockopt(sk, IPPROTO_SCTP, SCTP_NODELAY, &nodelay, &sz)  );
		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 */
		CHECK_SYS(  getsockopt(sk, IPPROTO_SCTP, SCTP_FRAGMENT_INTERLEAVE, &interleave, &sz)  );
		if (sz != sizeof(interleave))
		{
			TRACE_DEBUG(INFO, "Invalid size of socket option: %d / %d", sz, (socklen_t)sizeof(interleave));
			return ENOTSUP;
		}
		TRACE_DEBUG(FULL, "Def SCTP_FRAGMENT_INTERLEAVE value : %d", interleave);
		#endif /* DEBUG_SCTP */

		#if 0
		interleave = 2;	/* Allow partial delivery on several streams at the same time, since we are stream-aware in our security modules */
		#else /* 0 */
		interleave = 1;	/* hmmm actually, we are no more capable of handling this... */
		#endif /* 0 */
		
		/* Set the option to the socket */
		CHECK_SYS(  setsockopt(sk, IPPROTO_SCTP, SCTP_FRAGMENT_INTERLEAVE, &interleave, sizeof(interleave))  );
		
		#ifdef DEBUG_SCTP
		/* Check new values */
		CHECK_SYS(  getsockopt(sk, IPPROTO_SCTP, SCTP_FRAGMENT_INTERLEAVE, &interleave, &sz)  );
		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 v4 mapped addresses option */
	#ifdef SCTP_I_WANT_MAPPED_V4_ADDR
	{
		int v4mapped;
		
		#ifdef DEBUG_SCTP
		sz = sizeof(v4mapped);
		/* Read socket defaults */
		CHECK_SYS(  getsockopt(sk, IPPROTO_SCTP, SCTP_I_WANT_MAPPED_V4_ADDR, &v4mapped, &sz)  );
		if (sz != sizeof(v4mapped))
		{
			TRACE_DEBUG(INFO, "Invalid size of socket option: %d / %d", sz, (socklen_t)sizeof(v4mapped));
			return ENOTSUP;
		}
		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 in linux currently ... (Ok, It'd be better to replace with an autoconf test!) */
		
		/* Set the option to the socket */
		CHECK_SYS(  setsockopt(sk, IPPROTO_SCTP, SCTP_I_WANT_MAPPED_V4_ADDR, &v4mapped, sizeof(v4mapped))  );
		
		#ifdef DEBUG_SCTP
		/* Check new values */
		CHECK_SYS(  getsockopt(sk, IPPROTO_SCTP, SCTP_I_WANT_MAPPED_V4_ADDR, &v4mapped, &sz)  );
		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 next message if available)
	   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;
}	

static int _sctp_setsockopt_post(int sk, int bound_all)
{
	#ifdef DEBUG_SCTP
	socklen_t sz;
	#endif /* DEBUG_SCTP */
	
	TRACE_ENTRY( "%d", sk);
	
	CHECK_PARAMS( (sk > 0) );
	
	/* Set the ASCONF option */
	#ifdef SCTP_AUTO_ASCONF
	if (bound_all) {
		int asconf;
		
		#ifdef DEBUG_SCTP
		sz = sizeof(asconf);
		/* Read socket defaults */
		CHECK_SYS(  getsockopt(sk, IPPROTO_SCTP, SCTP_AUTO_ASCONF, &asconf, &sz)  );
		if (sz != sizeof(asconf))
		{
			TRACE_DEBUG(INFO, "Invalid size of socket option: %d / %d", sz, (socklen_t)sizeof(asconf));
			return ENOTSUP;
		}
		TRACE_DEBUG(FULL, "Def SCTP_AUTO_ASCONF value : %s", asconf ? "true" : "false");
		#endif /* DEBUG_SCTP */

		asconf = 1;	/* allow automatic use of added or removed addresses in the association (for bound-all sockets) */
		
		/* Set the option to the socket */
		CHECK_SYS(  setsockopt(sk, IPPROTO_SCTP, SCTP_AUTO_ASCONF, &asconf, sizeof(asconf))  );
		
		#ifdef DEBUG_SCTP
		/* Check new values */
		CHECK_SYS(  getsockopt(sk, IPPROTO_SCTP, SCTP_AUTO_ASCONF, &asconf, &sz)  );
		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 */
	
	return 0;
}	
	

/* Read the list of bound addresses from a socket and save it to g_pconf->local_addr_sec_sa.
  Note that the local addresses (127.0.0.1, ::1) are not saved. */
static int _sctp_getboundaddrs(int sk)
{
	int cnt, i = 0;
	sSA * list = NULL;
	sSA * sa;
	sSS * ss;
	
	ASSERT(g_pconf->local_addr_sec_sa == NULL);
	
	/* retrieve the addresses */
	CHECK_SYS( cnt = sctp_getladdrs(sk, 0, &list)  );
	if (cnt == 0) {
		TRACE_DEBUG(INFO, "Invalid parameter: socket was not bound");
		return EINVAL;
	}
	
	/* Now allocate an array of sSS to save the list */
	CHECK_MALLOC(  g_pconf->local_addr_sec_sa = calloc(cnt + 1, sizeof(sSS))  );
	
	/* and finally copy the SA information -- we filter out the local addresses */
	ss = g_pconf->local_addr_sec_sa;
	sa = list;
	
	while ( i < cnt ) {
		switch ( sa->sa_family ) {
			case AF_INET:
				{
					sSA4 * sin = (sSA4 *)sa;
					if (ntohl(sin->sin_addr.s_addr) != INADDR_LOOPBACK) {
						((sSA4 *)ss)->sin_family = AF_INET;
						((sSA4 *)ss)->sin_addr.s_addr = sin->sin_addr.s_addr;
						ss++;
					}
					/* update the loop pointers */
					sa = (sSA *)(sin + 1);
					i++;
				}
				break;
	
			case AF_INET6:
				{
					sSA6 * sin6 = (sSA6 *)sa;
					/* Special case: mapped addresses -> unmap it. */
					if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) {
						in_addr_t v4m = IN6_ADDR_V4UNMAP( &sin6->sin6_addr );
						if (ntohl(v4m) != INADDR_LOOPBACK) {
							((sSA4 *)ss)->sin_family = AF_INET;
							((sSA4 *)ss)->sin_addr.s_addr = v4m;
							ss++;
						}
					} else {
						/* maybe we should also remove link-local addresses??? */
						if ( ! IN6_IS_ADDR_LOOPBACK(&sin6->sin6_addr) ) {
							((sSA6 *)ss)->sin6_family = AF_INET6;
							memcpy(&((sSA6 *)ss)->sin6_addr, &sin6->sin6_addr, 16);
							ss++;
						}
					}
					/* update the loop pointers */
					sa = (sSA *)(sin6 + 1);
					i++;
				}
				break;
				
			default:
				TRACE_DEBUG(INFO, "Unexpected AF returned by sctp_getladdrs: %d", sa->sa_family);
				return ENOTSUP;
		}
	}
	
	/* And now, free the list and return */
	sctp_freeladdrs(list);
	
	return 0;
}


/*******************************************************************************************************/
/* Exported functions */

/*
 * FUNCTION:	_peer_sctp_create_server
 *
 * PARAMETERS:
 *  sock    : (out) On return, the identifier of the bound socket is stored here.
 *
 * DESCRIPTION: 
 *  Creates a listening SCTP bound socket.
 * It uses the data from configuration (local_addr_pri_sa, ...) to configure the server.
 *
 * 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 _peer_sctp_create_server( int * sock )
{
	int af;
	int sk;
	int bound_all = 0;
	sSS * ss;
	
	TRACE_ENTRY("%p", sock);
	
	CHECK_PARAMS(  sock  );
	
	if (g_pconf->disable_inet6)
		af = AF_INET;
	else
		af = AF_INET6;
	
	/* Create the socket */
	CHECK_SYS( sk = socket(af, SOCK_STREAM, IPPROTO_SCTP) );
	
	/* Set the number of streams and other common options to the socket */
	CHECK_FCT(  _sctp_setsockopt_pre(sk, g_pconf->sctp_streams)  );
	
	/* Now bind to all the requested addresses if specified, or default set otherwise */
	if ((g_pconf->local_addr_pri_sa == NULL) && (g_pconf->local_addr_sec_sa == NULL)) {
		/* No address passed in configuration file, use a default set then save it */
		sSA4 sin;
		
		TRACE_DEBUG(FULL, "Binding server socket to default system addresses");
		
		memset(&sin, 0, sizeof(sin));
		
		sin.sin_family = AF_INET;
		sin.sin_port = htons(g_pconf->local_port);
		sin.sin_addr.s_addr = INADDR_ANY;

		CHECK_SYS(  bind(sk, (sSA *)&sin, sizeof(sin))  );
		
		/* And now save the list of addresses to which we are bound */
		bound_all = 1;
		CHECK_FCT(  _sctp_getboundaddrs(sk)  );
	} else {
		/* we have the list of addresses to bind to, use sctp_bindx */
		int cnt = 0;
		
		TRACE_DEBUG(FULL, "Binding server socket to addresses specified in the configuration");
		
		/* first count the addresses */
		ss = g_pconf->local_addr_pri_sa;
		if (ss != NULL) {
			while (ss->ss_family != 0) {
				if ((ss->ss_family == AF_INET) || (ss->ss_family == AF_INET6))
					cnt++;
				ss++;
			}
		}
		ss = g_pconf->local_addr_sec_sa;
		if (ss != NULL) {
			while (ss->ss_family != 0) {
				if ((ss->ss_family == AF_INET) || (ss->ss_family == AF_INET6))
					cnt++;
				ss++;
			}
		}
		
		TRACE_DEBUG(FULL, "Counted %d addresses", cnt);
/*
Some bugs are remaining bellow this point, probably in SCTP implementation:

[DBG] ../../waaad/peer-sctp.c:874 ( _peer_sctp_create_server): Calling sctp_bindx with the following array:
[DBG] ../../waaad/peer-sctp.c:875 ( _peer_sctp_create_server):             IPv6: ::ffff:133.243.146.201
[DBG] ../../waaad/peer-sctp.c:875 ( _peer_sctp_create_server):             IPv6: ::ffff:10.0.0.1
[DBG] ../../waaad/peer-sctp.c:901 ( _peer_sctp_create_server): SCTP server bound on IPv6: ::ffff:133.243.146.201
[DBG] ../../waaad/peer-sctp.c:901 ( _peer_sctp_create_server): SCTP server bound on IPv6: ::ffff:0.0.0.0

*/		
		
		/* Now allocate the array for the function and fill with the addresses */
		if (g_pconf->disable_inet6) {
			sSA4 * sar;
			int recnt = 0;
			
			CHECK_MALLOC(  sar = calloc(cnt, sizeof(sSA4))  );
			
			ss = g_pconf->local_addr_pri_sa;
			if (ss != NULL) {
				while (ss->ss_family != 0) {
					if (ss->ss_family == AF_INET) {
						sar[recnt].sin_family      = AF_INET;
						sar[recnt].sin_port        = htons(g_pconf->local_port);
						sar[recnt].sin_addr.s_addr = ((sSA4 *)ss)->sin_addr.s_addr;
						recnt++;
					}
					ss++;
				}
			}
			ss = g_pconf->local_addr_sec_sa;
			if (ss != NULL) {
				while (ss->ss_family != 0) {
					if (ss->ss_family == AF_INET) {
						sar[recnt].sin_family      = AF_INET;
						sar[recnt].sin_port        = htons(g_pconf->local_port);
						sar[recnt].sin_addr.s_addr = ((sSA4 *)ss)->sin_addr.s_addr;
						recnt++;
					}
					ss++;
				}
			}
			
			TRACE_DEBUG(FULL, "Calling sctp_bindx with the following array:");
			sSA_DUMP_ARRAY(FULL, "            ", sar, recnt);
			
			CHECK_SYS(  sctp_bindx(sk, (sSA *)sar, recnt, SCTP_BINDX_ADD_ADDR)  );
			
		} else {
			sSA6 * sar;
			int recnt = 0;

			CHECK_MALLOC(  sar = calloc(cnt, sizeof(sSA6))  );
			
			ss = g_pconf->local_addr_pri_sa;
			if (ss != NULL) {
				while (ss->ss_family != 0) {
					if ((ss->ss_family == AF_INET) || (ss->ss_family == AF_INET6)) {
						sar[recnt].sin6_family      = AF_INET6;
						sar[recnt].sin6_port        = htons(g_pconf->local_port);
						if (ss->ss_family == AF_INET) {
							/* map the address to IPv6 */
							IN6_ADDR_V4MAP( &sar[recnt].sin6_addr.s6_addr, ((sSA4 *)ss)->sin_addr.s_addr );
						} else {
							/* directly copy the address */
							memcpy(&sar[recnt].sin6_addr.s6_addr, &(((sSA6 *)ss)->sin6_addr.s6_addr), 16);
						}
						recnt++;
					}
					ss++;
				}
			}
			ss = g_pconf->local_addr_sec_sa;
			if (ss != NULL) {
				while (ss->ss_family != 0) {
					if ((ss->ss_family == AF_INET) || (ss->ss_family == AF_INET6)) {
						sar[recnt].sin6_family      = AF_INET6;
						sar[recnt].sin6_port        = htons(g_pconf->local_port);
						if (ss->ss_family == AF_INET) {
							/* map the address to IPv6 */
							IN6_ADDR_V4MAP( &sar[recnt].sin6_addr.s6_addr, ((sSA4 *)ss)->sin_addr.s_addr );
						} else {
							/* directly copy the address */
							memcpy(&sar[recnt].sin6_addr.s6_addr, &(((sSA6 *)ss)->sin6_addr.s6_addr), 16);
						}
						recnt++;
					}
					ss++;
				}
			}
			
			TRACE_DEBUG(FULL, "Calling sctp_bindx with the following array:");
			sSA_DUMP_ARRAY(FULL, "            ", sar, recnt);
			
			CHECK_SYS(  sctp_bindx(sk, (sSA *)sar, recnt, SCTP_BINDX_ADD_ADDR)  );
		}
	}
	
	/* Ok, the server is bound */
	#ifdef DEBUG_SCTP
	/* Debug: show all local listening addresses */
	{
		sSA *sa;
		int sz;
		CHECK_SYS(  sz = sctp_getladdrs(sk, 0, &sa)  );
		
		sSA_DUMP_ARRAY(FULL, "SCTP server bound on ", sa, sz);
		sctp_freeladdrs(sa);
		
		/* Now dump the primary and secondary lists from the config */
		sSS_DUMP_ARRAY(FULL, "Dump of g_pconf->local_addr_pri_sa: ", g_pconf->local_addr_pri_sa);
		sSS_DUMP_ARRAY(FULL, "Dump of g_pconf->local_addr_sec_sa: ", g_pconf->local_addr_sec_sa);
		
		TRACE_DEBUG(FULL, "SCTP socket created: %d", sk);
	}
	#endif /* DEBUG_SCTP */

	CHECK_FCT(  _sctp_setsockopt_post(sk, bound_all)  );
	
	*sock = sk;
	return 0;
}

/*
 * FUNCTION:	_peer_sctp_accept
 *
 * PARAMETERS:
 *  sock    : The listening server socket from which to accept clients.
 *  new_sd  : on return, the new socket for this client.
 *  ostreams: the number of outgoing streams.
 *  istreams: the number of incoming streams.
 *
 * 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 _peer_sctp_accept( int sock, int * new_sd, uint16_t *ostreams, uint16_t *istreams )
{
	int skcli;
	struct sctp_status status;
	socklen_t sz = sizeof(status);
	
	TRACE_ENTRY("%d %p %p %p", sock, new_sd, ostreams, istreams);
	
	/* Check params */
	CHECK_PARAMS(  new_sd && ostreams && istreams  );
	
	/* Now wait for a client connection */
	TRACE_DEBUG(FULL, "Waiting for client connection...");
	CHECK_SYS(  skcli = accept(sock, NULL, NULL)  );
	
	/* We now have a client connected, get some information... */
	
	/* Read the association parameters */
	memset(&status, 0, sizeof(status));
	CHECK_SYS(  getsockopt(skcli, IPPROTO_SCTP, SCTP_STATUS, &status, &sz) );
	if (sz != sizeof(status))
	{
		TRACE_DEBUG(INFO, "Invalid size of socket option: %d / %d", sz, (socklen_t)sizeof(status));
		return ENOTSUP;
	}
	#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);
	sSA_DUMP(   FULL, "Accepted Cli SCTP_STATUS sstat_primary.spinfo_address : ", &status.sstat_primary.spinfo_address );
	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 */
	
	*ostreams = status.sstat_outstrms;
	*istreams = status.sstat_instrms;
	*new_sd = skcli;
	return 0;
}

/*
 * FUNCTION:	_peer_sctp_connect
 *
 * PARAMETERS:
 *  addr    : primary address of the server, that is passed to connect() function.
 *  addrlen : length of addr structure.
 *  sock    : (out) The client socket is stored here on return.
 *  ostreams: (in/out) the number of outbound streams (desired / negociated).
 *  istreams: (out) the number of inbound streams.
 *
 * DESCRIPTION: 
 *  Try and connect to a server and retrieve the number of streams in each direction.
 *
 * RETURN VALUE:
 *   0 : The client is connected.
 *  !0 : an error occurred (standard errno are returned)
 */
int _peer_sctp_connect( const sSA *addr, socklen_t addrlen, int *sock, uint16_t *ostreams, uint16_t *istreams )
{
	int af;
	int sk;
	struct sctp_status status;
	socklen_t sz = sizeof(status);
	
	TRACE_ENTRY("%p %d %p %p %p", addr, addrlen, sock, ostreams, istreams);
	
	/* Check params */
	CHECK_PARAMS(  sock && addr && ostreams && istreams  );
	
	if (g_pconf->disable_inet6)
		af = AF_INET;
	else
		af = AF_INET6;
	
	/* Create the socket */
	CHECK_SYS(  sk = socket(af, SOCK_STREAM, IPPROTO_SCTP)  );
	
	/* Set the number of streams and other common options to the socket */
	CHECK_FCT(  _sctp_setsockopt_pre(sk, *ostreams)  );
	
	sSA_DUMP( FULL, "Connecting to SCTP server: ", addr );
	
	CHECK_SYS(  connect(sk, addr, addrlen)  );
	
	/* Now that we are connected, retrieve the number of streams */
	CHECK_FCT(  _sctp_setsockopt_post(sk, 1)  );
	
	/* Read the association parameters */
	memset(&status, 0, sizeof(status));
	CHECK_SYS(  getsockopt(sk, IPPROTO_SCTP, SCTP_STATUS, &status, &sz)  );
	if (sz != sizeof(status))
	{
		TRACE_DEBUG(INFO, "Invalid size of socket option: %d / %d", sz, sizeof(status));
		return ENOTSUP;
	}
	#ifdef DEBUG_SCTP
	TRACE_DEBUG(FULL, "Connected server SCTP_STATUS sstat_state               : %i" , status.sstat_state);
	TRACE_DEBUG(FULL, "Connected server SCTP_STATUS sstat_rwnd		  : %u" , status.sstat_rwnd);
	TRACE_DEBUG(FULL, "Connected server SCTP_STATUS sstat_unackdata 	  : %hu", status.sstat_unackdata);
	TRACE_DEBUG(FULL, "Connected server SCTP_STATUS sstat_penddata  	  : %hu", status.sstat_penddata);
	TRACE_DEBUG(FULL, "Connected server SCTP_STATUS sstat_instrms		  : %hu", status.sstat_instrms);
	TRACE_DEBUG(FULL, "Connected server SCTP_STATUS sstat_outstrms  	  : %hu", status.sstat_outstrms);
	TRACE_DEBUG(FULL, "Connected server SCTP_STATUS sstat_fragmentation_point : %u" , status.sstat_fragmentation_point);
	sSA_DUMP(   FULL, "Connected server SCTP_STATUS sstat_primary.spinfo_address : ", &status.sstat_primary.spinfo_address );
	TRACE_DEBUG(FULL, "Connected server SCTP_STATUS sstat_primary.spinfo_state   : %d" , status.sstat_primary.spinfo_state);
	TRACE_DEBUG(FULL, "Connected server SCTP_STATUS sstat_primary.spinfo_cwnd    : %u" , status.sstat_primary.spinfo_cwnd);
	TRACE_DEBUG(FULL, "Connected server SCTP_STATUS sstat_primary.spinfo_srtt    : %u" , status.sstat_primary.spinfo_srtt);
	TRACE_DEBUG(FULL, "Connected server SCTP_STATUS sstat_primary.spinfo_rto     : %u" , status.sstat_primary.spinfo_rto);
	TRACE_DEBUG(FULL, "Connected server SCTP_STATUS sstat_primary.spinfo_mtu     : %u" , status.sstat_primary.spinfo_mtu);
	#endif /* DEBUG_SCTP */
	
	*ostreams = status.sstat_outstrms;
	*istreams = status.sstat_instrms;
	*sock = sk;
	
	return 0;
}

/*
 * FUNCTION:	_peer_sctp_send
 *
 * PARAMETERS:
 *  peer    : The peer to 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().
 * If streamid is '-1' on function entry, a valid stream is picked
 * and set on function return. This can be use to automatically rotate
 * the streams on which messages are sent.
 *
 * RETURN VALUE:
 *   -1 : error (errno is set)
 *    0 : the socket has been close.
 *   >0 : number of bytes sent.
 */
ssize_t _peer_sctp_send(sec_session_t * sess, int *streamid, const void * data, size_t len)
{
	struct msghdr mhdr;
	struct iovec  iov;
	struct {
		struct cmsghdr 		hdr;
		struct sctp_sndrcvinfo	sndrcv;
	} anci;
	_peer_t *peer;
	
	TRACE_ENTRY("%p %p(%d) %p %d", sess, streamid, streamid?*streamid:0, data, len);
	
	/* Check parameters */
	CHECK_PARAMS_DO(  sess  &&  VALIDATE_PEER(sess->peer)  && streamid && data && len, { errno = EINVAL; return -1; }  );

	peer = _P(sess->peer);
	
	/* We do not check the peer state now, the sending will fail anyway if connection is closed or invalid... */
	
	if ((*streamid < 0) || (*streamid >= peer->p_ostr)) {
		if (peer->p_ostr > 1)
			/* we don't lock, only this thread is supposed to modify this value... and it's not a problem otherwise. */
			peer->p_curstr = (peer->p_curstr + 1) % peer->p_ostr;
		*streamid = (int) peer->p_curstr;
	}
	
	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 from the primary */
	
	mhdr.msg_iov    = &iov;
	mhdr.msg_iovlen = 1;
	
	mhdr.msg_control    = &anci;
	mhdr.msg_controllen = sizeof(anci);
	
	#ifdef DEBUG_SCTP
	TRACE_DEBUG(FULL, "Sending SCTP packet (%d bytes on stream %d) to '%s'", len, *streamid, peer->p_diamid ? peer->p_diamid : "(unknown)");
	#endif /* DEBUG_SCTP */
	
	return sendmsg(*(int*)(sess->conn), &mhdr, 0);
}

/*
 * FUNCTION:	_peer_sctp_recv
 *
 * PARAMETERS:
 *  peer    : The peer from which to receive the next message.
 *  streamid: the stream id on which data is received.
 *  data    : a buffer containing the received data.
 *  len     : the size of the data buffer
 *
 * DESCRIPTION: 
 *  Receive data and notifications on a SCTP socket.
 * When notifications or errors are received, the corresponding event is sent to the peer.
 * 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 : shutdown is in progress.
 *  >0 : length of data written in the data buffer ( also is *len ).
 */
ssize_t _peer_sctp_recv(sec_session_t * sess, uint16_t * streamid, void ** data, size_t *len)
{
	ssize_t ret = 0;
	int iret = 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 = sysconf(_SC_PAGESIZE);
	_peer_t *peer;
	
	
	TRACE_ENTRY("%p %p %p %p", sess, streamid,  data, len);
	
	/* Check parameters */
	CHECK_PARAMS_DO(  sess  &&  VALIDATE_PEER(sess->peer)  && streamid && data && len, { errno = EINVAL; return -1; }  );

	peer = _P(sess->peer);
	
	/* Initialize our values */
	*streamid = -1;	/* stream on which the data is received */
	*data = NULL;	/* pointer to the received data */
	*len = 0;	/* amount of data already received */
	
	/* Allocate initial buffer; we use one page which should be enough in almost all cases -- and not waste too much memory. */
	CHECK_MALLOC_DO(  *data = malloc(sz),  { errno = ENOMEM; return -1;  }  );
	
	/* We free this buffer if we are cancelled during this call */
	pthread_cleanup_push( cleanup_buffer_i, data );

nextmsg:	
	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(*(int*)(sess->conn), &mhdr, 0);
	if (ret == 0) { /* Socket is closed */
		TRACE_DEBUG(INFO, "recvmsg returned %d, errno: %s", ret, strerror(errno));
		CHECK_FCT_DO(  _peer_events_send(peer, PEVENT_DISCONNECTED, NULL), /* nothing */ );
		free(*data);
		goto out;
	}
		
	if (ret < 0) {
		TRACE_DEBUG(INFO, "recvmsg returned %d, errno: %s", ret, strerror(errno));
		free(*data);
		goto out;
	}

	/* we have received new data */
	*len += ret;
	ret = -1;
	
	/* loop if we have not received a full message yet */
	if ( (mhdr.msg_flags & MSG_EOR) == 0 ) {
		/* if buffer is full */
		if (*len == sz) {
			
			/* add another memory page */
			sz += sysconf(_SC_PAGESIZE);
			
			/* enlarge the buffer to receive more data */
			CHECK_MALLOC_DO(  *data = realloc(*data, sz),  goto out );
		}
		goto incomplete;
	}
	
	/* Now we have a complete message in *data */
	/* Handle the case where we received a notification */
	if (mhdr.msg_flags & MSG_NOTIFICATION) {
		
		union sctp_notification * notif = (union sctp_notification *) *data;
		
		switch (notif->sn_header.sn_type) {
			
			case SCTP_ASSOC_CHANGE:
				#ifdef DEBUG_SCTP
				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);
				#endif /* DEBUG_SCTP */
				
				CHECK_FCT_DO(  _peer_events_send(peer, PEVENT_ASSOC_CHG, NULL), /* continue, unless this is a real problem... */ );
				break;
	
			case SCTP_PEER_ADDR_CHANGE:
				#ifdef DEBUG_SCTP
				TRACE_DEBUG(FULL, "Received SCTP_PEER_ADDR_CHANGE notification");
				sSA_DUMP(   FULL, "    intf_change : ", &(notif->sn_paddr_change.spc_aaddr) );
				TRACE_DEBUG(FULL, "          state : %d", notif->sn_paddr_change.spc_state);
				TRACE_DEBUG(FULL, "          error : %d", notif->sn_paddr_change.spc_error);
				#endif /* DEBUG_SCTP */
				
				CHECK_FCT_DO(  _peer_events_send(peer, PEVENT_PEERADDR_CHG, NULL), /* continue, unless this is a real problem... */ );
				break;
	
			case SCTP_SEND_FAILED:
				#ifdef DEBUG_SCTP
				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);
				#endif /* DEBUG_SCTP */
				
				CHECK_FCT_DO( iret =  _peer_events_send(peer, PEVENT_SND_FAILED, NULL /* could put ssf_error here if useful */), 
						{  errno = iret; goto out; }  );
				break;
			
			case SCTP_REMOTE_ERROR:
				#ifdef DEBUG_SCTP
				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));
				#endif /* DEBUG_SCTP */
				
				CHECK_FCT_DO(  _peer_events_send(peer, PEVENT_REMOTE_ERR, NULL /* sre_error ? */ ), /* continue, unless this is a real problem... */ );
				break;
	
			case SCTP_SHUTDOWN_EVENT:
				#ifdef DEBUG_SCTP
				TRACE_DEBUG(FULL, "Received SCTP_SHUTDOWN_EVENT notification");
				#endif /* DEBUG_SCTP */
				
				CHECK_FCT_DO( iret =  _peer_events_send(peer, PEVENT_DISCONNECTED, NULL), 
						{  errno = iret; goto out; }  );
				break;
			
			default:	
				TRACE_DEBUG(FULL, "Received unknown notification %d", notif->sn_header.sn_type);
		}
		
		/* Restart to receive next message. We continue with this buffer */
		*len = 0;
		goto nextmsg;
	}
	
	/* Now handle the anciliary data */
	for (hdr = CMSG_FIRSTHDR(&mhdr); hdr; hdr = CMSG_NXTHDR(&mhdr, hdr)) {
		
		/* We deal only with 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 = sndrcv->sinfo_stream;
				break;
				
			default:
				TRACE_DEBUG(FULL, "Anciliary block IPPROTO_SCTP / %d, skipped", hdr->cmsg_type);
		}
	}
	
	/* Okay, we have received a normal message */
	#ifdef DEBUG_SCTP
	TRACE_DEBUG(FULL, "Received SCTP packet (%d bytes on stream %hu) from '%s'", *len, *streamid, peer->p_diamid ? peer->p_diamid : "(unknown)");
	#endif /* DEBUG_SCTP */
	
	ret = *len;
	
out:	
	/* gcc... */ ;
	pthread_cleanup_pop(0);
	return ret;
}		
"Welcome to our mercurial repository"