view libfdcore/sctp_dtls.c @ 1188:e1ced4db7f67

Backup work in progress on DTLS, not usable
author Sebastien Decugis <sdecugis@freediameter.net>
date Tue, 11 Jun 2013 18:13:29 +0800
parents
children 1e8267ad057c
line wrap: on
line source

/*********************************************************************************************************
* Software License Agreement (BSD License)                                                               *
* Author: Sebastien Decugis <sdecugis@freediameter.net>							 *
*													 *
* Copyright (c) 2013, WIDE Project and NICT								 *
* All rights reserved.											 *
* 													 *
* Redistribution and use of this software in source and binary forms, with or without modification, are  *
* permitted provided that the following conditions are met:						 *
* 													 *
* * Redistributions of source code must retain the above 						 *
*   copyright notice, this list of conditions and the 							 *
*   following disclaimer.										 *
*    													 *
* * Redistributions in binary form must reproduce the above 						 *
*   copyright notice, this list of conditions and the 							 *
*   following disclaimer in the documentation and/or other						 *
*   materials provided with the distribution.								 *
* 													 *
* * Neither the name of the WIDE Project or NICT nor the 						 *
*   names of its contributors may be used to endorse or 						 *
*   promote products derived from this software without 						 *
*   specific prior written permission of WIDE Project and 						 *
*   NICT.												 *
* 													 *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED *
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR *
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 	 *
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 	 *
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR *
* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF   *
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.								 *
*********************************************************************************************************/

/* This file contains the code for DTLS over multi-stream SCTP implementation */

#include "fdcore-internal.h"
#include "cnxctx.h"


#define DTLS_TYPE_application_data	23

#ifdef GNUTLS_VERSION_300
/* Check if data is available for gnutls on a given context */
static int sctp_dtls_pull_timeout(gnutls_transport_ptr_t tr, unsigned int ms)
{
	struct cnxctx * conn = (struct cnxctx *)tr;
	fd_set rfds;
	struct timeval tv;

	FD_ZERO (&rfds);
	FD_SET (conn->cc_socket, &rfds);

	tv.tv_sec = 0;
	tv.tv_usec = ms * 1000;

	while(tv.tv_usec >= 1000000)
	{
		tv.tv_usec -= 1000000;
		tv.tv_sec++;
	}

	return select (conn->cc_socket + 1, &rfds, NULL, NULL, &tv);
}
#endif /* GNUTLS_VERSION_300 */

/* Send data over the connection, called by gnutls */
static ssize_t sctp_dtls_pushv(gnutls_transport_ptr_t tr, const giovec_t * iov, int iovcnt)
{
	struct cnxctx * conn = (struct cnxctx *)tr;
	ssize_t ret;
	
	TRACE_ENTRY("%p %p %d", tr, iov, iovcnt);
	CHECK_PARAMS_DO( tr && iov, { errno = EINVAL; return -1; } );
	
	/* If no unordered delivery is allowed, send over stream 0 always */
	if (conn->cc_sctp_para.unordered == 0) {
		ret = fd_sctp_sendstrv(conn, 0, (const struct iovec *)iov, iovcnt);
	}
	/* Otherwise, we need to check the type of record. */
	else {
		if ((iovcnt > 0) 
		&& (iov->iov_len > 0) && 
		(*((uint8_t *)iov->iov_base) == DTLS_TYPE_application_data)) {
			/* Data is sent over different streams */
			if (conn->cc_sctp_para.str_out > 32) {
				TODO("Limiting to 32 streams. Remove this limit when anti-replay is disabled");
				conn->cc_sctp_para.str_out = 32;
			}
			if (conn->cc_sctp_para.str_out > 1) {
				conn->cc_sctp_para.next += 1;
				conn->cc_sctp_para.next %= conn->cc_sctp_para.str_out;
			} else {
				conn->cc_sctp_para.next = 0;
			}
			ret = fd_sctp_sendstrv(conn, conn->cc_sctp_para.next, (const struct iovec *)iov, iovcnt);
	
		} else {
		/* other TLS messages are always sent over stream 0 */
			ret = fd_sctp_sendstrv(conn, 0, (const struct iovec *)iov, iovcnt);
		}
	}
	
	return ret;
}

#ifndef GNUTLS_VERSION_212
static ssize_t sctp_dtls_push(gnutls_transport_ptr_t tr, const void * data, size_t len)
{
	giovec_t iov;
	iov.iov_base = (void *)data;
	iov.iov_len  = len;
	return sctp_dtls_pushv(tr, &iov, 1);
}
#endif /*  GNUTLS_VERSION_212 */

/* Retrieve data received on any stream */
static ssize_t sctp_dtls_pull(gnutls_transport_ptr_t tr, void * buf, size_t len)
{
	struct cnxctx * conn = (struct cnxctx *)tr;

	/* If needed we can use fd_sctp_recvmeta to retrieve more information here */
	return fd_cnx_s_recv(conn, buf, len);
}

/* Set the parameters of a session to use the cnxctx object */
#ifndef GNUTLS_VERSION_300
GCC_DIAG_OFF("-Wdeprecated-declarations")
#endif /* !GNUTLS_VERSION_300 */
int fd_sctp_dtls_settransport(gnutls_session_t session, struct cnxctx * conn)
{
	/* Set the transport pointer passed to push & pull callbacks */
	GNUTLS_TRACE( gnutls_transport_set_ptr( session, (gnutls_transport_ptr_t) conn ) );
	
	/* Reset the low water value, since we don't use sockets */
#ifndef GNUTLS_VERSION_300
	/* starting version 2.12, this call is not needed */
	GNUTLS_TRACE( gnutls_transport_set_lowat( session, 0 ) );
#else  /* GNUTLS_VERSION_300 */
	/* but in 3.0 we have to provide the pull_timeout callback */
	GNUTLS_TRACE( gnutls_transport_set_pull_timeout_function( session, sctp_dtls_pull_timeout ) );
#endif /* GNUTLS_VERSION_300 */
	
	/* Set the push and pull callbacks */
	GNUTLS_TRACE( gnutls_transport_set_pull_function(session, sctp_dtls_pull) );
#ifndef GNUTLS_VERSION_212
	GNUTLS_TRACE( gnutls_transport_set_push_function(session, sctp_dtls_push) );
#else /* GNUTLS_VERSION_212 */
	GNUTLS_TRACE( gnutls_transport_set_vec_push_function(session, sctp_dtls_pushv) );
#endif /* GNUTLS_VERSION_212 */

	return;
}
#ifndef GNUTLS_VERSION_300
GCC_DIAG_ON("-Wdeprecated-declarations")
#endif /* !GNUTLS_VERSION_300 */




/* Set additional session parameters before handshake. The GNUTLS_DATAGRAM is already set in fd_tls_prepare */
int fd_sctp_dtls_prepare(gnutls_session_t session)
{
	/* We do not use cookies at the moment. Not sure it is useful or not */
	TODO("Cookie exchange?");
	/* gnutls_dtls_prestate_set (session, &prestate); */

	gnutls_dtls_set_mtu(session, 2^14 /* as per RFC 6083 */);

	gnutls_dtls_set_timeouts(session, 70000, 60000); /* Set retrans > total so that there is no retransmission, since SCTP is reliable */

#ifdef GNUTLS_VERSION_320
	TODO("Disable replay protection");
#endif /* GNUTLS_VERSION_320 */
	
	
}


static ssize_t fd_dtls_recv_handle_error(struct cnxctx * conn, gnutls_session_t session, void * data, size_t sz, uint8_t seq[8])
{
	ssize_t ret;
again:	
	CHECK_GNUTLS_DO( ret = gnutls_record_recv_seq(session, data, sz, seq), 
		{
			switch (ret) {
				case GNUTLS_E_REHANDSHAKE: 
					if (!fd_cnx_teststate(conn, CC_STATUS_CLOSING)) {
						CHECK_GNUTLS_DO( ret = gnutls_handshake(session),
							{
								if (TRACE_BOOL(INFO)) {
									fd_log_debug("TLS re-handshake failed on socket %d (%s) : %s", conn->cc_socket, conn->cc_id, gnutls_strerror(ret));
								}
								goto end;
							} );
					}

				case GNUTLS_E_AGAIN:
				case GNUTLS_E_INTERRUPTED:
					if (!fd_cnx_teststate(conn, CC_STATUS_CLOSING))
						goto again;
					TRACE_DEBUG(FULL, "Connection is closing, so abord gnutls_record_recv now.");
					break;

				case GNUTLS_E_UNEXPECTED_PACKET_LENGTH:
					/* The connection is closed */
					TRACE_DEBUG(FULL, "Got 0 size while reading the socket, probably connection closed...");
					break;
				
				default:
					if (gnutls_error_is_fatal (ret) == 0) {
						LOG_N("Ignoring non-fatal GNU TLS error: %s", gnutls_strerror (ret));
						goto again;
					}
					LOG_E("Fatal GNUTLS error: %s", gnutls_strerror (ret));
			}
		} );
		
	if (ret == 0)
		CHECK_GNUTLS_DO( gnutls_bye(session, GNUTLS_SHUT_RDWR),  );
	
end:	
	if (ret <= 0)
		fd_cnx_markerror(conn);
	return ret;
}


/* Receiver thread that reassemble the decrypted messages (when size is > 2<<14) for upper layer */
void * fd_sctp_dtls_rcvthr(void * arg) {

	struct cnxctx * conn = arg;
	
	TRACE_ENTRY("%p", arg);
	CHECK_PARAMS_DO(conn && (conn->cc_socket > 0), return NULL );
	
	/* Set the thread name */
	{
		char buf[48];
		snprintf(buf, sizeof(buf), "Receiver (%d) DTLS", conn->cc_socket);
		fd_log_threadname ( buf );
	}
	
	ASSERT( fd_cnx_teststate(conn, CC_STATUS_TLS) );
	ASSERT( fd_cnx_target_queue(conn) );

	/* The next function only returns when there is an error on the socket */	
	do {
	
		TODO("Reassemble the packets based on their sequence number & stream");
		
		
#if 0	
		
		uint8_t header[4];
		struct fd_cnx_rcvdata rcv_data;
		struct fd_msg_pmdl *pmdl=NULL;
		ssize_t ret = 0;
		size_t	received = 0;
		uint8_t seq[8];

		do {
			ret = fd_dtls_recv_handle_error(conn, session, &header[received], sizeof(header) - received, seq);
			if (ret <= 0) {
				/* The connection is closed */
				goto out;
			}
			received += ret;
		} while (received < sizeof(header));

		rcv_data.length = ((size_t)header[1] << 16) + ((size_t)header[2] << 8) + (size_t)header[3];

		/* Check the received word is a valid beginning of a Diameter message */
		if ((header[0] != DIAMETER_VERSION)	/* defined in <libfreeDiameter.h> */
		   || (rcv_data.length > DIAMETER_MSG_SIZE_MAX)) { /* to avoid too big mallocs */
			/* The message is suspect */
			LOG_E( "Received suspect header [ver: %d, size: %zd] from '%s', assume disconnection", (int)header[0], rcv_data.length, conn->cc_remid);
			fd_cnx_markerror(conn);
			goto out;
		}

		/* Ok, now we can really receive the data */
		CHECK_MALLOC(  rcv_data.buffer = fd_cnx_alloc_msg_buffer( rcv_data.length, &pmdl ) );
		memcpy(rcv_data.buffer, header, sizeof(header));

		while (received < rcv_data.length) {
			pthread_cleanup_push(free_rcvdata, &rcv_data); /* In case we are canceled, clean the partialy built buffer */
			ret = fd_tls_recv_handle_error(conn, session, rcv_data.buffer + received, rcv_data.length - received);
			pthread_cleanup_pop(0);

			if (ret <= 0) {
				free_rcvdata(&rcv_data);
				goto out;
			}
			received += ret;
		}
		
		fd_hook_call(HOOK_DATA_RECEIVED, NULL, NULL, &rcv_data, pmdl);
		
		/* We have received a complete message, pass it to the daemon */
		CHECK_FCT_DO( ret = fd_event_send( fd_cnx_target_queue(conn), FDEVP_CNX_MSG_RECV, rcv_data.length, rcv_data.buffer), 
			{ 
				free_rcvdata(&rcv_data);
				CHECK_FCT_DO(fd_event_send(fd_g_config->cnf_main_ev, FDEV_TERMINATE, 0, NULL), );
				return ret; 
			} );
#endif // 0	
				
	} while (1);
	
out:
	TRACE_DEBUG(FULL, "Thread terminated");	
	return NULL;
}			
"Welcome to our mercurial repository"