From: Nikos Mavrogiannopoulos Date: Wed, 8 Feb 2012 20:51:08 +0000 (+0100) Subject: DTLS is more tolerant in packet loss during last flight. X-Git-Tag: gnutls_3_0_13~110 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=128a3bafc38ebb69e67cd0f4d4951ea00652bbb3;p=thirdparty%2Fgnutls.git DTLS is more tolerant in packet loss during last flight. --- diff --git a/lib/gnutls_dtls.c b/lib/gnutls_dtls.c index 316897e2db..e6cf0e44c8 100644 --- a/lib/gnutls_dtls.c +++ b/lib/gnutls_dtls.c @@ -216,7 +216,7 @@ int ret; goto cleanup; } - _gnutls_dtls_log ("DTLS[%p]: %sstart of flight transmission.\n", session, (session->internals.dtls.flight_init == 0)?"":"re-"); + _gnutls_dtls_log ("DTLS[%p]: %sStart of flight transmission.\n", session, (session->internals.dtls.flight_init == 0)?"":"re-"); for (cur = send_buffer->head; cur != NULL; cur = cur->next) @@ -233,9 +233,9 @@ int ret; if (last_type == GNUTLS_HANDSHAKE_FINISHED) { - /* we cannot do anything here. We just return 0 and - * if a retransmission occurs because peer didn't receive it - * we rely on the record layer calling this function again. + /* On the last flight we cannot ensure retransmission + * from here. _dtls_wait_and_retransmit() is being called + * by handshake. */ session->internals.dtls.last_flight = 1; } @@ -248,11 +248,12 @@ int ret; return gnutls_assert_val(ret); /* last message in handshake -> no ack */ - if (session->internals.dtls.last_flight != 0 && _dtls_is_async(session)) + if (session->internals.dtls.last_flight != 0) { - /* we cannot do anything here. We just return 0 and + /* we don't wait here. We just return 0 and * if a retransmission occurs because peer didn't receive it - * we rely on the record layer calling this function again. + * we rely on the record or handshake + * layer calling this function again. */ return 0; } @@ -297,6 +298,33 @@ nb_timeout: RETURN_DTLS_EAGAIN_OR_TIMEOUT(session); } +/* Waits for the last flight or retransmits + * the previous on timeout. Returns 0 on success. + */ +int _dtls_wait_and_retransmit(gnutls_session_t session) +{ +int ret; + + if (session->internals.dtls.blocking != 0) + ret = _gnutls_io_check_recv(session, session->internals.dtls.actual_retrans_timeout_ms); + else + ret = _gnutls_io_check_recv(session, 0); + + UPDATE_TIMER; + + if (ret == GNUTLS_E_TIMEDOUT) + { + ret = _dtls_retransmit(session); + if (ret == 0) + { + RETURN_DTLS_EAGAIN_OR_TIMEOUT(session); + } + } + + return 0; +} + + #define window_table session->internals.dtls.record_sw #define window_size session->internals.dtls.record_sw_size diff --git a/lib/gnutls_dtls.h b/lib/gnutls_dtls.h index a18833654d..3c8754fa9d 100644 --- a/lib/gnutls_dtls.h +++ b/lib/gnutls_dtls.h @@ -33,7 +33,7 @@ int _dtls_record_check(gnutls_session_t session, uint64 * _seq); #define MAX_DTLS_TIMEOUT 60000 -#define RETURN_DTLS_EAGAIN_OR_TIMEOUT(session) \ +#define RETURN_DTLS_EAGAIN_OR_TIMEOUT(session) { \ if (gnutls_time(0) - session->internals.dtls.handshake_start_time > \ session->internals.dtls.total_timeout_ms/1000) \ return gnutls_assert_val(GNUTLS_E_TIMEDOUT); \ @@ -42,7 +42,11 @@ int _dtls_record_check(gnutls_session_t session, uint64 * _seq); if (session->internals.dtls.blocking != 0) \ millisleep(50); \ return gnutls_assert_val(GNUTLS_E_AGAIN); \ - } + } \ + } + + +int _dtls_wait_and_retransmit(gnutls_session_t session); /* returns true or false depending on whether we need to * handle asynchronously handshake data. diff --git a/lib/gnutls_handshake.c b/lib/gnutls_handshake.c index b8edb574c9..f362be81e3 100644 --- a/lib/gnutls_handshake.c +++ b/lib/gnutls_handshake.c @@ -2670,12 +2670,24 @@ _gnutls_recv_handshake_final (gnutls_session_t session, int init) int ret = 0; uint8_t ch; + switch (STATE) { case STATE0: case STATE30: - ret = _gnutls_recv_int (session, GNUTLS_CHANGE_CIPHER_SPEC, -1, &ch, 1, NULL); STATE = STATE30; + + /* This is the last flight and peer cannot be sure + * we have received it unless we notify him. So we + * wait for a message and retransmit if needed. */ + if (IS_DTLS(session) && !_dtls_is_async(session)) + { + ret = _dtls_wait_and_retransmit(session); + if (ret < 0) + return gnutls_assert_val(ret); + } + + ret = _gnutls_recv_int (session, GNUTLS_CHANGE_CIPHER_SPEC, -1, &ch, 1, NULL); if (ret <= 0) { ERR ("recv ChangeCipherSpec", ret); @@ -2700,10 +2712,18 @@ _gnutls_recv_handshake_final (gnutls_session_t session, int init) gnutls_assert (); return ret; } - + case STATE31: - ret = _gnutls_recv_finished (session); STATE = STATE31; + + if (IS_DTLS(session) && !_dtls_is_async(session)) + { + ret = _dtls_wait_and_retransmit(session); + if (ret < 0) + return gnutls_assert_val(ret); + } + + ret = _gnutls_recv_finished (session); if (ret < 0) { ERR ("recv finished", ret); diff --git a/lib/gnutls_record.c b/lib/gnutls_record.c index 7510fbf1db..963966f9eb 100644 --- a/lib/gnutls_record.c +++ b/lib/gnutls_record.c @@ -724,7 +724,10 @@ record_add_to_buffers (gnutls_session_t session, unexpected_packet: if (IS_DTLS(session) && ret != GNUTLS_E_REHANDSHAKE) - ret = GNUTLS_E_AGAIN; /* skip the packet */ + { + _mbuffer_xfree(&bufel); + RETURN_DTLS_EAGAIN_OR_TIMEOUT(session); + } cleanup: _mbuffer_xfree(&bufel);