PR: 1997
Submitted by: Robin Seggelmann <seggelmann@fh-muenster.de>
Approved by: steve@openssl.org

DTLS timeout handling fix.
diff --git a/ssl/d1_both.c b/ssl/d1_both.c
index d11d6d5..5bb0a4f 100644
--- a/ssl/d1_both.c
+++ b/ssl/d1_both.c
@@ -890,9 +890,6 @@
 
 int dtls1_read_failed(SSL *s, int code)
 	{
-	DTLS1_STATE *state;
-	int send_alert = 0;
-
 	if ( code > 0)
 		{
 		fprintf( stderr, "invalid state reached %s:%d", __FILE__, __LINE__);
@@ -912,24 +909,6 @@
 		return code;
 		}
 
-	dtls1_double_timeout(s);
-	state = s->d1;
-	state->timeout.num_alerts++;
-	if ( state->timeout.num_alerts > DTLS1_TMO_ALERT_COUNT)
-		{
-		/* fail the connection, enough alerts have been sent */
-		SSLerr(SSL_F_DTLS1_READ_FAILED,SSL_R_READ_TIMEOUT_EXPIRED);
-		return 0;
-		}
-
-	state->timeout.read_timeouts++;
-	if ( state->timeout.read_timeouts > DTLS1_TMO_READ_COUNT)
-		{
-		send_alert = 1;
-		state->timeout.read_timeouts = 1;
-		}
-
-
 #if 0 /* for now, each alert contains only one record number */
 	item = pqueue_peek(state->rcvd_records);
 	if ( item )
@@ -940,12 +919,12 @@
 #endif
 
 #if 0  /* no more alert sending, just retransmit the last set of messages */
-		if ( send_alert)
-			ssl3_send_alert(s,SSL3_AL_WARNING,
-				DTLS1_AD_MISSING_HANDSHAKE_MESSAGE);
+	if ( state->timeout.read_timeouts >= DTLS1_TMO_READ_COUNT)
+		ssl3_send_alert(s,SSL3_AL_WARNING,
+			DTLS1_AD_MISSING_HANDSHAKE_MESSAGE);
 #endif
 
-	return dtls1_retransmit_buffered_messages(s) ;
+	return dtls1_handle_timeout(s);
 	}
 
 int
diff --git a/ssl/d1_lib.c b/ssl/d1_lib.c
index 6450c1d..8039740 100644
--- a/ssl/d1_lib.c
+++ b/ssl/d1_lib.c
@@ -188,6 +188,29 @@
 		s->version=DTLS1_VERSION;
 	}
 
+long dtls1_ctrl(SSL *s, int cmd, long larg, void *parg)
+	{
+	int ret=0;
+
+	switch (cmd)
+		{
+	case DTLS_CTRL_GET_TIMEOUT:
+		if (dtls1_get_timeout(s, (struct timeval*) parg) != NULL)
+			{
+			ret = 1;
+			}
+		break;
+	case DTLS_CTRL_HANDLE_TIMEOUT:
+		ret = dtls1_handle_timeout(s);
+		break;
+
+	default:
+		ret = ssl3_ctrl(s, cmd, larg, parg);
+		break;
+		}
+	return(ret);
+	}
+
 /*
  * As it's impossible to use stream ciphers in "datagram" mode, this
  * simple filter is designed to disengage them in DTLS. Unfortunately
@@ -295,6 +318,36 @@
 	BIO_ctrl(SSL_get_rbio(s), BIO_CTRL_DGRAM_SET_NEXT_TIMEOUT, 0, &(s->d1->next_timeout));
 	}
 
+int dtls1_handle_timeout(SSL *s)
+	{
+	DTLS1_STATE *state;
+
+	/* if no timer is expired, don't do anything */
+	if (!dtls1_is_timer_expired(s))
+		{
+		return 0;
+		}
+
+	dtls1_double_timeout(s);
+	state = s->d1;
+	state->timeout.num_alerts++;
+	if ( state->timeout.num_alerts > DTLS1_TMO_ALERT_COUNT)
+		{
+		/* fail the connection, enough alerts have been sent */
+		SSLerr(SSL_F_DTLS1_READ_FAILED,SSL_R_READ_TIMEOUT_EXPIRED);
+		return 0;
+		}
+
+	state->timeout.read_timeouts++;
+	if ( state->timeout.read_timeouts > DTLS1_TMO_READ_COUNT)
+		{
+		state->timeout.read_timeouts = 1;
+		}
+
+	dtls1_start_timer(s);
+	return dtls1_retransmit_buffered_messages(s);
+	}
+
 static void get_current_time(struct timeval *t)
 {
 #ifdef OPENSSL_SYS_WIN32
diff --git a/ssl/d1_pkt.c b/ssl/d1_pkt.c
index d9a8114..0664636 100644
--- a/ssl/d1_pkt.c
+++ b/ssl/d1_pkt.c
@@ -773,11 +773,8 @@
 		}
 
 	/* Check for timeout */
-	if (dtls1_is_timer_expired(s))
-		{
-		if (dtls1_read_failed(s, -1) > 0)
-			goto start;
-		}
+	if (dtls1_handle_timeout(s) > 0)
+		goto start;
 
 	/* get new packet if necessary */
 	if ((rr->length == 0) || (s->rstate == SSL_ST_READ_BODY))
diff --git a/ssl/ssl.h b/ssl/ssl.h
index 7ed8226..117071c 100644
--- a/ssl/ssl.h
+++ b/ssl/ssl.h
@@ -1396,6 +1396,14 @@
 #define SSL_CTRL_SET_TLSEXT_TICKET_KEY_CB	72
 #endif
 
+#define DTLS_CTRL_GET_TIMEOUT		73
+#define DTLS_CTRL_HANDLE_TIMEOUT	74
+
+#define DTLSv1_get_timeout(ssl, arg) \
+	SSL_ctrl(ssl,DTLS_CTRL_GET_TIMEOUT,0, (void *)arg)
+#define DTLSv1_handle_timeout(ssl) \
+	SSL_ctrl(ssl,DTLS_CTRL_HANDLE_TIMEOUT,0, NULL)
+
 #define SSL_session_reused(ssl) \
 	SSL_ctrl((ssl),SSL_CTRL_GET_SESSION_REUSED,0,NULL)
 #define SSL_num_renegotiations(ssl) \
diff --git a/ssl/ssl_locl.h b/ssl/ssl_locl.h
index 2b362a4..74fc972 100644
--- a/ssl/ssl_locl.h
+++ b/ssl/ssl_locl.h
@@ -759,7 +759,7 @@
 		dtls1_read_bytes, \
 		dtls1_write_app_data_bytes, \
 		dtls1_dispatch_alert, \
-		ssl3_ctrl, \
+		dtls1_ctrl, \
 		ssl3_ctx_ctrl, \
 		ssl3_get_cipher_by_char, \
 		ssl3_put_cipher_by_char, \
@@ -943,6 +943,7 @@
 void dtls1_reset_seq_numbers(SSL *s, int rw);
 long dtls1_default_timeout(void);
 struct timeval* dtls1_get_timeout(SSL *s, struct timeval* timeleft);
+int dtls1_handle_timeout(SSL *s);
 const SSL_CIPHER *dtls1_get_cipher(unsigned int u);
 void dtls1_start_timer(SSL *s);
 void dtls1_stop_timer(SSL *s);