diff libfdcore/cnxctx.c @ 1186:56c36d1007b4

Further preparation of the DTLS integration. Some cleanups in the GNUTLS handling.
author Sebastien Decugis <sdecugis@freediameter.net>
date Fri, 07 Jun 2013 18:48:34 +0800
parents 22de21feec64
children 6a1042d8075b
line wrap: on
line diff
--- a/libfdcore/cnxctx.c	Thu Jun 06 19:06:00 2013 +0800
+++ b/libfdcore/cnxctx.c	Fri Jun 07 18:48:34 2013 +0800
@@ -38,6 +38,7 @@
 
 #include <net/if.h>
 #include <ifaddrs.h> /* for getifaddrs */
+#include <sys/uio.h> /* writev */
 
 /* The maximum size of Diameter message we accept to receive (<= 2^24) to avoid too big mallocs in case of trashed headers */
 #ifndef DIAMETER_MSG_SIZE_MAX
@@ -478,13 +479,21 @@
 	return fd_cnx_teststate(conn, CC_STATUS_TLS);
 }
 
+/* Mark the connection to tell if OOO delivery is permitted (only for SCTP) */
+int fd_cnx_unordered_delivery(struct cnxctx * conn, int is_allowed)
+{
+	CHECK_PARAMS( conn );
+	conn->cc_sctp_para.unordered = is_allowed;
+	return 0;
+}
+
 /* Return true if the connection supports unordered delivery of messages */
-int fd_cnx_isMultichan(struct cnxctx * conn)
+int fd_cnx_is_unordered_delivery_supported(struct cnxctx * conn)
 {
 	CHECK_PARAMS_DO( conn, return 0 );
 	#ifndef DISABLE_SCTP
 	if (conn->cc_proto == IPPROTO_SCTP)
-		return (conn->cc_sctp_para.str_in > 1) || (conn->cc_sctp_para.str_out > 1);
+		return (conn->cc_sctp_para.str_out > 1);
 	#endif /* DISABLE_SCTP */
 	return 0;
 }
@@ -530,6 +539,22 @@
 	return conn->cc_remid;
 }
 
+static int fd_cnx_may_dtls(struct cnxctx * conn);
+
+/* Get a short string representing the connection */
+int fd_cnx_proto_info(struct cnxctx * conn, char * buf, size_t len) 
+{
+	CHECK_PARAMS( conn );
+	
+	if (fd_cnx_teststate(conn, CC_STATUS_TLS)) {
+		snprintf(buf, len, "%s,%s,soc#%d", IPPROTO_NAME(conn->cc_proto), fd_cnx_may_dtls(conn) ? "DTLS" : "TLS", conn->cc_socket);
+	} else {
+		snprintf(buf, len, "%s,soc#%d", IPPROTO_NAME(conn->cc_proto), conn->cc_socket);
+	}
+	
+	return 0;
+}
+
 /* Retrieve a list of all IP addresses of the local system from the kernel, using getifaddrs */
 int fd_cnx_get_local_eps(struct fd_list * list)
 {
@@ -600,6 +625,24 @@
 	CHECK_SYS_DO( setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)), /* Also timeout for sending, to avoid waiting forever */ );
 }
 
+
+#ifdef GNUTLS_VERSION_300
+/* The pull_timeout function for gnutls */
+static int fd_cnx_s_select (struct cnxctx * conn, unsigned int ms)
+{
+	fd_set rfds;
+	struct timeval tv;
+	
+	FD_ZERO (&rfds);
+	FD_SET (conn->cc_socket, &rfds);
+	
+	tv.tv_sec = ms / 1000;
+	tv.tv_usec = (ms * 1000) % 1000000;
+	
+	return select (conn->cc_socket + 1, &rfds, NULL, NULL, &tv);
+}		
+#endif /* GNUTLS_VERSION_300 */
+
 /* A recv-like function, taking a cnxctx object instead of socket as entry. We use it to quickly react to timeouts without traversing GNUTLS wrapper each time */
 ssize_t fd_cnx_s_recv(struct cnxctx * conn, void *buffer, size_t length)
 {
@@ -627,8 +670,9 @@
 	return ret;
 }
 
-/* Send */
-static ssize_t fd_cnx_s_send(struct cnxctx * conn, void *buffer, size_t length)
+/* Send, for older GNUTLS */
+#ifndef GNUTLS_VERSION_212
+static ssize_t fd_cnx_s_send(struct cnxctx * conn, const void *buffer, size_t length)
 {
 	ssize_t ret = 0;
 	int timedout = 0;
@@ -652,6 +696,33 @@
 	
 	return ret;
 }
+#endif /* GNUTLS_VERSION_212 */
+
+/* Send */
+static ssize_t fd_cnx_s_sendv(struct cnxctx * conn, const struct iovec * iov, int iovcnt)
+{
+	ssize_t ret = 0;
+	int timedout = 0;
+again:
+	ret = writev(conn->cc_socket, iov, iovcnt);
+	/* Handle special case of timeout */
+	if ((ret < 0) && ((errno == EAGAIN) || (errno == EINTR))) {
+		pthread_testcancel();
+		if (! fd_cnx_teststate(conn, CC_STATUS_CLOSING ))
+			goto again; /* don't care, just ignore */
+		if (!timedout) {
+			timedout ++; /* allow for one timeout while closing */
+			goto again;
+		}
+		CHECK_SYS_DO(ret, /* continue */);
+	}
+	
+	/* Mark the error */
+	if (ret <= 0)
+		fd_cnx_markerror(conn);
+	
+	return ret;
+}
 
 #define ALIGNOF(t) ((char *)(&((struct { char c; t _h; } *)0)->_h) - (char *)0)  /* Could use __alignof__(t) on some systems but this is more portable probably */
 #define PMDL_PADDED(len) ( ((len) + ALIGNOF(struct fd_msg_pmdl) - 1) & ~(ALIGNOF(struct fd_msg_pmdl) - 1) )
@@ -1604,8 +1675,15 @@
 
 		/* Set the push and pull callbacks */
 		if (!dtls) {
+			#ifdef GNUTLS_VERSION_300
+			GNUTLS_TRACE( gnutls_transport_set_pull_timeout_function( conn->cc_tls_para.session, (void *)fd_cnx_s_select ) );
+			#endif /* GNUTLS_VERSION_300 */
 			GNUTLS_TRACE( gnutls_transport_set_pull_function(conn->cc_tls_para.session, (void *)fd_cnx_s_recv) );
+			#ifndef GNUTLS_VERSION_212
 			GNUTLS_TRACE( gnutls_transport_set_push_function(conn->cc_tls_para.session, (void *)fd_cnx_s_send) );
+			#else /* GNUTLS_VERSION_212 */
+			GNUTLS_TRACE( gnutls_transport_set_vec_push_function(conn->cc_tls_para.session, (void *)fd_cnx_s_sendv) );
+			#endif /* GNUTLS_VERSION_212 */
 		} else {
 			TODO("DTLS push/pull functions");
 			return ENOTSUP;
@@ -1783,8 +1861,10 @@
 		if (fd_cnx_teststate(conn, CC_STATUS_TLS)) {
 			CHECK_GNUTLS_DO( ret = fd_tls_send_handle_error(conn, conn->cc_tls_para.session, buf + sent, len - sent),  );
 		} else {
-			/* Maybe better to replace this call with sendmsg for atomic sending? */
-			CHECK_SYS_DO( ret = fd_cnx_s_send(conn, buf + sent, len - sent), );
+			struct iovec iov;
+			iov.iov_base = buf + sent;
+			iov.iov_len  = len - sent;
+			CHECK_SYS_DO( ret = fd_cnx_s_sendv(conn, &iov, 1), );
 		}
 		if (ret <= 0)
 			return ENOTCONN;
@@ -1795,9 +1875,9 @@
 }
 
 /* Send a message -- this is synchronous -- and we assume it's never called by several threads at the same time (on the same conn), so we don't protect. */
-int fd_cnx_send(struct cnxctx * conn, unsigned char * buf, size_t len, uint32_t flags)
+int fd_cnx_send(struct cnxctx * conn, unsigned char * buf, size_t len)
 {
-	TRACE_ENTRY("%p %p %zd %x", conn, buf, len, flags);
+	TRACE_ENTRY("%p %p %zd", conn, buf, len);
 	
 	CHECK_PARAMS(conn && (conn->cc_socket > 0) && (! fd_cnx_teststate(conn, CC_STATUS_ERROR)) && buf && len);
 
@@ -1811,48 +1891,50 @@
 #ifndef DISABLE_SCTP
 		case IPPROTO_SCTP: {
 			int dtls = fd_cnx_uses_dtls(conn);
-
 			if (!dtls) {
-				if (flags & FD_CNX_ORDERED) {
-					/* We send over stream #0 */
+				int stream = 0;
+				if (conn->cc_sctp_para.unordered) {
+					int limit;
+					if (fd_cnx_teststate(conn, CC_STATUS_TLS))
+						limit = conn->cc_sctp_para.pairs;
+					else
+						limit = conn->cc_sctp_para.str_out;
+					
+					if (limit > 1) {
+						conn->cc_sctp_para.next += 1;
+						conn->cc_sctp_para.next %= limit;
+						stream = conn->cc_sctp_para.next;
+					}
+				}
+				
+				if (stream == 0) {
+					/* We can use default function, it sends over stream #0 */
 					CHECK_FCT( send_simple(conn, buf, len) );
 				} else {
-					/* Default case : no flag specified */
-			
-					int another_str = 0; /* do we send over stream #0 ? */
-
-					if ((conn->cc_sctp_para.str_out > 1) && ((!fd_cnx_teststate(conn, CC_STATUS_TLS)) || (conn->cc_sctp_para.pairs > 1)))  {
-						/* Update the id of the stream we will send this message over */
-						conn->cc_sctp_para.next += 1;
-						conn->cc_sctp_para.next %= (fd_cnx_teststate(conn, CC_STATUS_TLS) ? conn->cc_sctp_para.pairs : conn->cc_sctp_para.str_out);
-						another_str = (conn->cc_sctp_para.next ? 1 : 0);
-					}
-
-					if ( ! another_str ) {
-						CHECK_FCT( send_simple(conn, buf, len) );
+					if (!fd_cnx_teststate(conn, CC_STATUS_TLS)) {
+						struct iovec iov;
+						iov.iov_base = buf;
+						iov.iov_len  = len;
+						
+						CHECK_SYS_DO( fd_sctp_sendstrv(conn, stream, &iov, 1), { fd_cnx_markerror(conn); return ENOTCONN; } );
 					} else {
-						if (!fd_cnx_teststate(conn, CC_STATUS_TLS)) {
-							CHECK_FCT_DO( fd_sctp_sendstr(conn, conn->cc_sctp_para.next, buf, len), { fd_cnx_markerror(conn); return ENOTCONN; } );
-						} else {
-							/* push the record to the appropriate session */
-							ssize_t ret;
-							size_t sent = 0;
-							ASSERT(conn->cc_sctp3436_data.array != NULL);
-							do {
-								CHECK_GNUTLS_DO( ret = fd_tls_send_handle_error(conn, conn->cc_sctp3436_data.array[conn->cc_sctp_para.next].session, buf + sent, len - sent), );
-								if (ret <= 0)
-									return ENOTCONN;
+						/* push the data to the appropriate session */
+						ssize_t ret;
+						size_t sent = 0;
+						ASSERT(conn->cc_sctp3436_data.array != NULL);
+						do {
+							CHECK_GNUTLS_DO( ret = fd_tls_send_handle_error(conn, conn->cc_sctp3436_data.array[stream].session, buf + sent, len - sent), );
+							if (ret <= 0)
+								return ENOTCONN;
 
-								sent += ret;
-							} while ( sent < len );
-						}
+							sent += ret;
+						} while ( sent < len );
 					}
 				}
 			} else {
 				/* DTLS */
-				/* We signal the push function directly to tell if using stream 0 or round-robin */
-				TODO("DTLS send");
-				return ENOTSUP;
+				/* Multistream is handled at lower layer in the push/pull function */
+				CHECK_FCT( send_simple(conn, buf, len) );
 			}
 		}
 		break;
"Welcome to our mercurial repository"