]> git.ipfire.org Git - thirdparty/gnutls.git/commitdiff
DTLS is more tolerant in packet loss during last flight.
authorNikos Mavrogiannopoulos <nmav@gnutls.org>
Wed, 8 Feb 2012 20:51:08 +0000 (21:51 +0100)
committerNikos Mavrogiannopoulos <nmav@gnutls.org>
Wed, 8 Feb 2012 20:51:08 +0000 (21:51 +0100)
lib/gnutls_dtls.c
lib/gnutls_dtls.h
lib/gnutls_handshake.c
lib/gnutls_record.c

index 316897e2dbda5ae4401c259911537f0c9af68ccc..e6cf0e44c8caac145ea453fa227cb4ed70f26e13 100644 (file)
@@ -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
 
index a18833654d98b584eb1c153bfd61200df2ccd03a..3c8754fa9d965093d47bd6677adc9b4b08c61290 100644 (file)
@@ -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.
index b8edb574c9317a7419a39699dd0248744f266e31..f362be81e3fe308841d09810f593dfcdc2178c35 100644 (file)
@@ -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);
index 7510fbf1db540aa3fd3afe3cee4e39a179d059ff..963966f9eb287c3198fce6b8fee7b8d0f96de100 100644 (file)
@@ -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);