From: Nikos Mavrogiannopoulos Date: Fri, 21 Sep 2012 17:05:29 +0000 (+0200) Subject: Correctly restore gnutls_record_recv() in DTLS mode if interrupted during the retrasm... X-Git-Tag: gnutls_3_1_2~22 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=a58bfa74085aa130c991ec18598583918e10c0d2;p=thirdparty%2Fgnutls.git Correctly restore gnutls_record_recv() in DTLS mode if interrupted during the retrasmition of handshake data. --- diff --git a/NEWS b/NEWS index 1e67f0b58c..71466897af 100644 --- a/NEWS +++ b/NEWS @@ -21,6 +21,9 @@ does not override it). an insecure level. If the %COMPAT priority flag is not specified the TLS connection fails. +** libgnutls: Correctly restore gnutls_record_recv() in DTLS mode +if interrupted during the retrasmition of handshake data. + ** libgnutls: Better mingw32 support (patch by LRN). ** libgnutls: The %COMPAT keyword, if specified, will tolerate diff --git a/doc/cha-gtls-app.texi b/doc/cha-gtls-app.texi index 3e84ba2cb0..7b24915380 100644 --- a/doc/cha-gtls-app.texi +++ b/doc/cha-gtls-app.texi @@ -686,7 +686,7 @@ function implies to restoring the same function that was interrupted, in the DTLS protocol this requirement isn't true. There are cases where a retransmission is required, which are indicated by a received message and thus @funcref{gnutls_record_get_direction} must be called -to decide which operation to restore, i.e., send or receive. +to decide which direction to check prior to restoring a function call. @showfuncdesc{gnutls_record_get_direction} Moreover, to prevent blocking from DTLS' retransmission timers to block a diff --git a/lib/gnutls_dtls.c b/lib/gnutls_dtls.c index 54c56815e8..78e21fbf74 100644 --- a/lib/gnutls_dtls.c +++ b/lib/gnutls_dtls.c @@ -157,15 +157,6 @@ static int drop_usage_count(gnutls_session_t session, mbuffer_head_st *const sen return 0; } -/* This function is to be called from record layer once - * a handshake replay is detected. It will make sure - * it transmits only once per few seconds. Otherwise - * it is the same as _dtls_transmit(). - */ -int _dtls_retransmit(gnutls_session_t session) -{ - return _dtls_transmit(session); -} /* Checks whether the received packet contains a handshake * packet with sequence higher that the previously received. diff --git a/lib/gnutls_dtls.h b/lib/gnutls_dtls.h index cd15f89643..c97d470421 100644 --- a/lib/gnutls_dtls.h +++ b/lib/gnutls_dtls.h @@ -31,7 +31,6 @@ #include int _dtls_transmit(gnutls_session_t session); -int _dtls_retransmit(gnutls_session_t session); int _dtls_record_check(struct record_parameters_st *rp, uint64 * _seq); void _dtls_reset_hsk_state(gnutls_session_t session); @@ -122,4 +121,14 @@ inline static int _dtls_async_timer_active(gnutls_session_t session) return session->internals.dtls.async_term; } +/* This function is to be called from record layer once + * a handshake replay is detected. It will make sure + * it transmits only once per few seconds. Otherwise + * it is the same as _dtls_transmit(). + */ +inline static int _dtls_retransmit(gnutls_session_t session) +{ + return _dtls_transmit(session); +} + #endif diff --git a/lib/gnutls_int.h b/lib/gnutls_int.h index bf5280f51a..1949402953 100644 --- a/lib/gnutls_int.h +++ b/lib/gnutls_int.h @@ -237,6 +237,12 @@ typedef enum heartbeat_state_t SHB_RECV, } heartbeat_state_t; +typedef enum recv_state_t +{ + RECV_STATE_0 = 0, + RECV_STATE_DTLS_RETRANSMIT, +} recv_state_t; + #include /* This is the maximum number of algorithms (ciphers or macs etc). @@ -911,6 +917,8 @@ typedef struct heartbeat_state_t hb_state; /* for ping */ + recv_state_t recv_state; /* state of the receive function */ + /* If you add anything here, check _gnutls_handshake_internal_state_clear(). */ } internals_st; diff --git a/lib/gnutls_record.c b/lib/gnutls_record.c index 79b4904030..a361e05421 100644 --- a/lib/gnutls_record.c +++ b/lib/gnutls_record.c @@ -703,7 +703,7 @@ record_add_to_buffers (gnutls_session_t session, ret = gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET); goto unexpected_packet; } - + if (_dtls_is_async(session) && _dtls_async_timer_active(session)) { if (session->security_parameters.entity == GNUTLS_SERVER && @@ -714,9 +714,11 @@ record_add_to_buffers (gnutls_session_t session, } else { + session->internals.recv_state = RECV_STATE_DTLS_RETRANSMIT; ret = _dtls_retransmit(session); if (ret == 0) { + session->internals.recv_state = RECV_STATE_0; ret = gnutls_assert_val(GNUTLS_E_AGAIN); goto unexpected_packet; } @@ -1211,21 +1213,33 @@ _gnutls_recv_int (gnutls_session_t session, content_type_t type, gnutls_assert (); return GNUTLS_E_INVALID_SESSION; } + + switch(session->internals.recv_state) + { + case RECV_STATE_DTLS_RETRANSMIT: + ret = _dtls_retransmit(session); + if (ret < 0) + return gnutls_assert_val(ret); + + session->internals.recv_state = RECV_STATE_0; + case RECV_STATE_0: + + _dtls_async_timer_check(session); + /* If we have enough data in the cache do not bother receiving + * a new packet. (in order to flush the cache) + */ + ret = check_buffers (session, type, data, data_size, seq); + if (ret != 0) + return ret; - _dtls_async_timer_check(session); - - /* If we have enough data in the cache do not bother receiving - * a new packet. (in order to flush the cache) - */ - ret = check_buffers (session, type, data, data_size, seq); - if (ret != 0) - return ret; - - ret = _gnutls_recv_in_buffers(session, type, htype, ms); - if (ret < 0 && ret != GNUTLS_E_SESSION_EOF) - return gnutls_assert_val(ret); + ret = _gnutls_recv_in_buffers(session, type, htype, ms); + if (ret < 0 && ret != GNUTLS_E_SESSION_EOF) + return gnutls_assert_val(ret); - return check_buffers (session, type, data, data_size, seq); + return check_buffers (session, type, data, data_size, seq); + default: + return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR); + } } /**