This patch fixes the timeout handling. The method dtls1_get_timeout() was intended to determine the next handshake message timeout when using select() calls, to set their timeout. This method is renamed to DTLSv1_get_timeout(), to fit the common naming scheme and the declaration is moved. It was declared in the wrong file, so the user was unable to use it. Additionally, the timeout handling is moved to the method DTLSv1_handle_timeout(), which can be called by the user when a select() timer expires. Until now expired timers were handled not until a SSL_read() is called. This is a problem when using select() on the reading socket, because no call of SSL_read() is done before data from the peer arrived.
The test programs s_server and s_client are also modified to make use of the new timeout handling to fully support timeouts. --- apps/s_client.c 30 Jun 2009 15:56:35 -0000 1.125 +++ apps/s_client.c 27 Jul 2009 12:28:55 -0000 @@ -411,6 +411,7 @@ BIO *sbio; char *inrand=NULL; int mbuf_len=0; + struct timeval timeout, *timeoutp; #ifndef OPENSSL_NO_ENGINE char *engine_id=NULL; char *ssl_client_engine_id=NULL; @@ -1196,6 +1197,12 @@ FD_ZERO(&readfds); FD_ZERO(&writefds); + if ((SSL_version(con) == DTLS1_VERSION) && + (DTLSv1_get_timeout(con, &timeout) != NULL)) + timeoutp = &timeout; + else + timeoutp = NULL; + if (SSL_in_init(con) && !SSL_total_renegotiations(con)) { in_init=1; @@ -1300,7 +1307,7 @@ if(!i && (!((_kbhit()) || (WAIT_OBJECT_0 == WaitForSingleObject(GetStdHandle(STD_INPUT_HANDLE), 0))) || ! read_tty) ) continue; #endif } else i=select(width,(void *)&readfds,(void *)&writefds, - NULL,NULL); + NULL,timeoutp); } #elif defined(OPENSSL_SYS_NETWARE) if(!write_tty) { @@ -1310,7 +1317,7 @@ i=select(width,(void *)&readfds,(void *)&writefds, NULL,&tv); } else i=select(width,(void *)&readfds,(void *)&writefds, - NULL,NULL); + NULL,timeoutp); } #elif defined(OPENSSL_SYS_BEOS_R5) /* Under BeOS-R5 the situation is similar to DOS */ @@ -1328,12 +1335,12 @@ if (!i && (stdin_set != 1 || !read_tty)) continue; } else i=select(width,(void *)&readfds,(void *)&writefds, - NULL,NULL); + NULL,timeoutp); } (void)fcntl(fileno(stdin), F_SETFL, 0); #else i=select(width,(void *)&readfds,(void *)&writefds, - NULL,NULL); + NULL,timeoutp); #endif if ( i < 0) { @@ -1344,6 +1351,11 @@ } } + if ((SSL_version(con) == DTLS1_VERSION) && DTLSv1_handle_timeout(con) > 0) + { + BIO_printf(bio_err,"TIMEOUT occured\n"); + } + if (!ssl_pending && FD_ISSET(SSL_get_fd(con),&writefds)) { k=SSL_write(con,&(cbuf[cbuf_off]), --- apps/s_server.c 30 Jun 2009 15:56:35 -0000 1.140 +++ apps/s_server.c 27 Jul 2009 12:28:55 -0000 @@ -1750,6 +1750,7 @@ unsigned long l; SSL *con=NULL; BIO *sbio; + struct timeval timeout, *timeoutp; #if defined(OPENSSL_SYS_WINDOWS) || defined(OPENSSL_SYS_MSDOS) || defined(OPENSSL_SYS_NETWARE) || defined(OPENSSL_SYS_BEOS_R5) struct timeval tv; #endif @@ -1919,7 +1920,19 @@ read_from_terminal = 1; (void)fcntl(fileno(stdin), F_SETFL, 0); #else - i=select(width,(void *)&readfds,NULL,NULL,NULL); + if ((SSL_version(con) == DTLS1_VERSION) && + (DTLSv1_get_timeout(con, &timeout) != NULL)) + timeoutp = &timeout; + else + timeoutp = NULL; + + i=select(width,(void *)&readfds,NULL,NULL,timeoutp); + + if ((SSL_version(con) == DTLS1_VERSION) && DTLSv1_handle_timeout(con) > 0) + { + BIO_printf(bio_err,"TIMEOUT occured\n"); + } + if (i <= 0) continue; if (FD_ISSET(fileno(stdin),&readfds)) read_from_terminal = 1; --- ssl/d1_both.c 15 Jul 2009 11:33:24 -0000 1.23 +++ ssl/d1_both.c 27 Jul 2009 12:28:56 -0000 @@ -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 DTLSv1_handle_timeout(s); } int --- ssl/d1_lib.c 31 May 2009 17:13:09 -0000 1.14 +++ ssl/d1_lib.c 27 Jul 2009 12:28:56 -0000 @@ -224,7 +224,7 @@ BIO_ctrl(SSL_get_rbio(s), BIO_CTRL_DGRAM_SET_NEXT_TIMEOUT, 0, &(s- >d1->next_timeout)); } -struct timeval* dtls1_get_timeout(SSL *s, struct timeval* timeleft) +struct timeval* DTLSv1_get_timeout(SSL *s, struct timeval* timeleft) { struct timeval timenow; @@ -264,7 +264,7 @@ struct timeval timeleft; /* Get time left until timeout, return false if no timer running */ - if (dtls1_get_timeout(s, &timeleft) == NULL) + if (DTLSv1_get_timeout(s, &timeleft) == NULL) { return 0; } @@ -295,6 +295,36 @@ BIO_ctrl(SSL_get_rbio(s), BIO_CTRL_DGRAM_SET_NEXT_TIMEOUT, 0, &(s- >d1->next_timeout)); } +int DTLSv1_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 --- ssl/d1_pkt.c 24 Jul 2009 11:52:55 -0000 1.38 +++ ssl/d1_pkt.c 27 Jul 2009 12:28:56 -0000 @@ -773,11 +773,8 @@ } /* Check for timeout */ - if (dtls1_is_timer_expired(s)) - { - if (dtls1_read_failed(s, -1) > 0) - goto start; - } + if (DTLSv1_handle_timeout(s) > 0) + goto start; /* get new packet if necessary */ if ((rr->length == 0) || (s->rstate == SSL_ST_READ_BODY)) --- ssl/ssl.h 15 Jul 2009 11:33:24 -0000 1.228 +++ ssl/ssl.h 27 Jul 2009 12:28:56 -0000 @@ -1622,6 +1622,9 @@ const SSL_METHOD *DTLSv1_server_method(void); /* DTLSv1.0 */ const SSL_METHOD *DTLSv1_client_method(void); /* DTLSv1.0 */ +struct timeval* DTLSv1_get_timeout(SSL *s, struct timeval* timeleft); +int DTLSv1_handle_timeout(SSL *s); + STACK_OF(SSL_CIPHER) *SSL_get_ciphers(const SSL *s); int SSL_do_handshake(SSL *s); --- ssl/ssl_locl.h 16 May 2009 11:15:42 -0000 1.104 +++ ssl/ssl_locl.h 27 Jul 2009 12:28:56 -0000 @@ -942,7 +942,6 @@ void dtls1_get_ccs_header(unsigned char *data, struct ccs_header_st *ccs_hdr); void dtls1_reset_seq_numbers(SSL *s, int rw); long dtls1_default_timeout(void); -struct timeval* dtls1_get_timeout(SSL *s, struct timeval* timeleft); const SSL_CIPHER *dtls1_get_cipher(unsigned int u); void dtls1_start_timer(SSL *s); void dtls1_stop_timer(SSL *s);
dtls-timeout-handling-bug.patch
Description: Binary data