diff 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 diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libfdcore/sctp_dtls.c	Tue Jun 11 18:13:29 2013 +0800
@@ -0,0 +1,315 @@
+/*********************************************************************************************************
+* 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"