diff freeDiameter/cnxctx.c @ 194:d1af490d6e85

Change in sockets options to avoid waiting forever on closing connections
author Sebastien Decugis <sdecugis@nict.go.jp>
date Mon, 08 Feb 2010 17:46:40 +0900
parents 65f5bc7ad0bd
children bc530e9dae04
line wrap: on
line diff
--- a/freeDiameter/cnxctx.c	Fri Feb 05 18:43:33 2010 +0900
+++ b/freeDiameter/cnxctx.c	Mon Feb 08 17:46:40 2010 +0900
@@ -230,6 +230,9 @@
 	cli->cc_socket = cli_sock;
 	cli->cc_proto = serv->cc_proto;
 	
+	/* Set the timeout */
+	fd_cnx_s_setto(cli->cc_socket);
+	
 	/* Generate the name for the connection object */
 	{
 		char addrbuf[INET6_ADDRSTRLEN];
@@ -296,6 +299,9 @@
 	cnx->cc_socket = sock;
 	cnx->cc_proto  = IPPROTO_TCP;
 	
+	/* Set the timeout */
+	fd_cnx_s_setto(cnx->cc_socket);
+	
 	/* Generate the names for the object */
 	{
 		char addrbuf[INET6_ADDRSTRLEN];
@@ -347,6 +353,9 @@
 	cnx->cc_socket = sock;
 	cnx->cc_proto  = IPPROTO_SCTP;
 	
+	/* Set the timeout */
+	fd_cnx_s_setto(cnx->cc_socket);
+	
 	/* Retrieve the number of streams and primary address */
 	CHECK_FCT_DO( fd_sctp_get_str_info( sock, &cnx->cc_sctp_para.str_in, &cnx->cc_sctp_para.str_out, &primary ), goto error );
 	if (cnx->cc_sctp_para.str_out > cnx->cc_sctp_para.str_in)
@@ -488,6 +497,45 @@
 /*     Use of a connection object     */
 /**************************************/
 
+/* Set the timeout option on the socket */
+void fd_cnx_s_setto(int sock) 
+{
+	struct timeval tv;
+	
+	/* Set a timeout on the socket so that in any case we are not stuck waiting for something */
+	memset(&tv, 0, sizeof(tv));
+	tv.tv_sec = 3;	/* allow 3 seconds timeout for TLS session cleanup */
+	CHECK_SYS_DO( setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)), /* best effort only */ );
+}	
+
+/* A recv-like function, taking a cnxctx object instead of socket as entry. Only used to filter timeouts error (GNUTLS does not like these...) */
+ssize_t fd_cnx_s_recv(struct cnxctx * conn, void *buffer, size_t length)
+{
+	ssize_t ret = 0;
+	int timedout = 0;
+again:
+	ret = recv(conn->cc_socket, buffer, length, 0);
+	/* Handle special case of timeout */
+	if ((ret < 0) && (errno == ETIMEDOUT)) {
+		if (!conn->cc_closing)
+			goto again; /* don't care, just ignore */
+		if (!timedout) {
+			timedout ++; /* allow for one timeout while closing */
+			goto again;
+		}
+		CHECK_SYS_DO(ret, /* continue */);
+		return 0; /* so that the connection appears closed */
+	}
+	
+	return ret;
+}
+
+/* Send */
+static ssize_t fd_cnx_s_send(struct cnxctx * conn, void *buffer, size_t length)
+{
+	return send(conn->cc_socket, buffer, length, 0);
+}
+
 /* Receiver thread (TCP & noTLS) : incoming message is directly saved into the target queue */
 static void * rcvthr_notls_tcp(void * arg)
 {
@@ -516,7 +564,7 @@
 		size_t	received = 0;
 
 		do {
-			ret = recv(conn->cc_socket, &header[received], sizeof(header) - received, 0);
+			ret = fd_cnx_s_recv(conn, &header[received], sizeof(header) - received);
 			if (ret <= 0) {
 				CHECK_SYS_DO(ret, /* continue */);
 				goto error; /* Stop the thread, the recipient of the event will cleanup */
@@ -541,7 +589,7 @@
 
 		while (received < length) {
 			pthread_cleanup_push(free, newmsg); /* In case we are canceled, clean the partialy built buffer */
-			ret = recv(conn->cc_socket, newmsg + received, length - received, 0);
+			ret = fd_cnx_s_recv(conn, newmsg + received, length - received);
 			pthread_cleanup_pop(0);
 
 			if (ret <= 0) {
@@ -591,7 +639,7 @@
 	ASSERT( Target_Queue(conn) );
 	
 	do {
-		CHECK_FCT_DO( fd_sctp_recvmeta(conn->cc_socket, NULL, &buf, &bufsz, &event), goto error );
+		CHECK_FCT_DO( fd_sctp_recvmeta(conn->cc_socket, NULL, &buf, &bufsz, &event, &conn->cc_closing), goto error );
 		if (event == FDEVP_CNX_ERROR) {
 			goto error;
 		}
@@ -1020,8 +1068,12 @@
 		CHECK_FCT( fd_sctps_init(conn) );
 #endif /* DISABLE_SCTP */
 	} else {
-		/* Set the socket info in the session */
-		gnutls_transport_set_ptr (conn->cc_tls_para.session, (gnutls_transport_ptr_t) (long) conn->cc_socket);
+		/* Set the transport pointer passed to push & pull callbacks */
+		gnutls_transport_set_ptr( conn->cc_tls_para.session, (gnutls_transport_ptr_t) conn );
+
+		/* Set the push and pull callbacks */
+		gnutls_transport_set_pull_function(conn->cc_tls_para.session, (void *)fd_cnx_s_recv);
+		gnutls_transport_set_push_function(conn->cc_tls_para.session, (void *)fd_cnx_s_send);
 	}
 
 	/* Handshake master session */
@@ -1253,15 +1305,6 @@
 	
 	conn->cc_closing = 1;
 	
-	/* Set a timeout on the socket so that in any case we are not stuck waiting for something */
-	if (conn->cc_socket > 0) {
-		struct timeval tv;
-		memset(&tv, 0, sizeof(tv));
-		tv.tv_sec = 3;	/* allow 3 seconds timeout for TLS session cleanup */
-		CHECK_SYS_DO( setsockopt(conn->cc_socket, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)), /* best effort only */ );
-		CHECK_SYS_DO( setsockopt(conn->cc_socket, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)), /* best effort only */ );
-	}
-	
 	/* Initiate shutdown of the TLS session(s): call gnutls_bye(WR), then read until error */
 	if (conn->cc_tls) {
 #ifndef DISABLE_SCTP
"Welcome to our mercurial repository"