]> git.ipfire.org Git - thirdparty/gnutls.git/commitdiff
several updates in the heartbeat handling code.
authorNikos Mavrogiannopoulos <nmav@gnutls.org>
Thu, 20 Sep 2012 20:59:49 +0000 (22:59 +0200)
committerNikos Mavrogiannopoulos <nmav@gnutls.org>
Thu, 20 Sep 2012 21:37:12 +0000 (23:37 +0200)
14 files changed:
lib/ext/heartbeat.c
lib/ext/heartbeat.h
lib/gnutls_buffers.c
lib/gnutls_buffers.h
lib/gnutls_dtls.c
lib/gnutls_errors.c
lib/gnutls_handshake.c
lib/gnutls_handshake.h
lib/gnutls_int.h
lib/gnutls_record.c
lib/gnutls_record.h
lib/gnutls_state.c
lib/includes/gnutls/gnutls.h.in
lib/libgnutls.map

index 677a91dc7767c21c9ead7b85f239ea88b294ce8d..d27b94f1bde80f5b8519a4d138a8b2ce74d75801 100644 (file)
@@ -65,10 +65,10 @@ _gnutls_heartbeat (unsigned policy)
 /**
   * gnutls_heartbeat_allowed:
   * @session: is a #gnutls_session_t structure.
-  * @type: non zero is for sending, and zero for receiving
+  * @type: one of %GNUTLS_HB_LOCAL_ALLOWED_TO_SEND and %GNUTLS_HB_PEER_ALLOWED_TO_SEND
   *
   * This function will check whether heartbeats are allowed
-  * in this session.
+  * to be sent or received in this session. 
   *
   * Returns: Non zero if heartbeats are allowed.
   *
@@ -82,7 +82,7 @@ gnutls_heartbeat_allowed (gnutls_session_t session, unsigned int type)
       (session, GNUTLS_EXTENSION_HEARTBEAT, &epriv) < 0)
     return 0;                   /* Not enabled */
 
-  if (type != 0)
+  if (type == GNUTLS_HB_LOCAL_ALLOWED_TO_SEND)
     {
       if (epriv.num & LOCAL_ALLOWED_TO_SEND)
         return 1;
@@ -105,12 +105,12 @@ heartbeat_allow_send (gnutls_session_t session)
   return gnutls_heartbeat_allowed(session, 1);
 }
 
-/**
- * _gnutls_heartbeat_send_data:
+/*-
+ * heartbeat_send_data:
  * @session: is a #gnutls_session_t structure.
  * @data: contains the data to send.
  * @data_size: is the length of the data.
- * @request: true if Request message is about to be send.
+ * @start_timer: true if the heartbeat timer is to be started.
  *
  * This function has the similar semantics with gnutls_record_send() The only
  * difference is that it uses GNUTLS_HEARTBEAT content type.
@@ -123,16 +123,14 @@ heartbeat_allow_send (gnutls_session_t session)
  *   number of bytes sent might be less than @data_size.  The maximum
  *   number of bytes this function can send in a single call depends
  *   on the negotiated maximum record size.
**/
-ssize_t
-_gnutls_heartbeat_send_data (gnutls_session_t session, const void *data,
-                             size_t data_size, int request)
-*/
+static int
+heartbeat_send_data (gnutls_session_t session, const void *data,
+                     size_t data_size, uint8_t type)
 {
   int ret;
-  char pr[128];
   gnutls_buffer_st response;
   uint8_t payload[16];
-  uint8_t type = request ? HEARTBEAT_REQUEST : HEARTBEAT_RESPONSE;
   _gnutls_buffer_init (&response);
 
   ret = gnutls_rnd (GNUTLS_RND_RANDOM, payload, 16);
@@ -146,23 +144,7 @@ _gnutls_heartbeat_send_data (gnutls_session_t session, const void *data,
   ret = _gnutls_send_int (session, GNUTLS_HEARTBEAT, -1,
                           EPOCH_WRITE_CURRENT, response.data,
                           response.length, MBUFFER_FLUSH);
-  if (request)
-    {
-      if (ret >= 0)
-        {
-          _gnutls_record_log
-              ("REC[%p]: HB %zu bytes sent OK [%d in packet], saved for response verification:%s\n",
-               session, data_size, ret, _gnutls_bin2hex ((uint8_t *) data,
-                                                         data_size, pr,
-                                                         sizeof (pr),
-                                                         NULL));
-          session->internals.dtls.heartbeat_timeout = HEARTBEAT_TIMEOUT;
-          gettime (&session->internals.dtls.heartbeat_sent);
-          _gnutls_buffer_reset (&session->internals.heartbeat_payload);
-          BUFFER_APPEND (&session->internals.heartbeat_payload, data,
-                         data_size);
-        }
-    }
+
   _gnutls_record_log ("REC[%p]: HB sent: %d\n", session, ret);
 
   _gnutls_buffer_clear (&response);
@@ -172,27 +154,25 @@ _gnutls_heartbeat_send_data (gnutls_session_t session, const void *data,
 /**
  * gnutls_heartbeat_ping:
  * @session: is a #gnutls_session_t structure.
- * @data_size: is the length of the random data.
- * @wait_for_it: wait for pong or timeout instead of returning
- * immediately.
- *
- * This function has the similar semantics with gnutls_record_send().
- * The only difference is that it uses GNUTLS_HEARTBEAT content type
- * and auto-generate data to send.
+ * @data_size: is the length of the ping payload.
+ * @max_tries: if flags is %GNUTLS_HEARTBEAT_WAIT then this sets the number of retransmissions.
+ * @flags: if %GNUTLS_HEARTBEAT_WAIT then wait for pong or timeout instead of returning immediately.
  *
- * This function send HeartBeat request message with proper padding.
+ * This function sends a ping to the peer. If the @flags is set
+ * to %GNUTLS_HEARTBEAT_WAIT then it waits for a reply from the peer.
+ * 
+ * Note that it is highly recommended to use this function with the
+ * flag %GNUTLS_HEARTBEAT_WAIT, or you need to handle retransmissions
+ * and timeouts manually.
  *
- *
- * Returns: The number of bytes sent, or a negative error code. The
- *   number of bytes sent might be less than @data_size.  The maximum
- *   number of bytes this function can send in a single call depends
- *   on the negotiated maximum record size. GNUTLS_E_HEARTBEAT_FLIGHT
- *   is returned if HB Request is alredy in flight.
+ * Returns: %GNUTLS_E_SUCCESS on success, otherwise a negative error code.
  **/
-ssize_t
-gnutls_heartbeat_ping (gnutls_session_t session, size_t data_size)
+int
+gnutls_heartbeat_ping (gnutls_session_t session, size_t data_size, 
+                       unsigned int max_tries, unsigned int flags)
 {
-  int ret = GNUTLS_E_HEARTBEAT_FLIGHT;
+  int ret;
+  unsigned int retries = 0;
 
   if (data_size > MAX_HEARTBEAT_LENGTH)
     return gnutls_assert_val (GNUTLS_E_UNEXPECTED_PACKET_LENGTH);
@@ -200,42 +180,101 @@ gnutls_heartbeat_ping (gnutls_session_t session, size_t data_size)
   if (!heartbeat_allow_send (session))
     return GNUTLS_E_INVALID_REQUEST;
 
-  _gnutls_record_log
-      ("REC[%p]: sending HB_REQUEST with length: %zu to peer\n",
-       session, data_size);
-
-  if (gnutls_heartbeat_timeout (session, 1) == GNUTLS_E_ILLEGAL_PARAMETER)
+  switch(session->internals.hb_state)
     {
-      uint8_t data[data_size];
-      ret = _gnutls_rnd (GNUTLS_RND_NONCE, data, data_size);
-      if (ret >= 0)
-        ret = _gnutls_heartbeat_send_data (session, data, data_size, 1);
+      case SHB_SEND1:
+        _gnutls_record_log
+         ("REC[%p]: sending HB_REQUEST with length: %zu to peer\n", session, data_size);
+
+        _gnutls_buffer_reset(&session->internals.hb_local_data);
+
+        ret = _gnutls_buffer_resize (&session->internals.hb_local_data, data_size);
+        if (ret < 0)
+          return gnutls_assert_val(ret);
+
+        ret = _gnutls_rnd (GNUTLS_RND_NONCE, session->internals.hb_local_data.data, data_size);
+        if (ret < 0)
+          return gnutls_assert_val(ret);
+        session->internals.hb_local_data.length = data_size;
+
+        session->internals.hb_state = SHB_SEND2;
+      case SHB_SEND2:
+        session->internals.hb_timeout = HEARTBEAT_TIMEOUT;
+retry:
+        ret = heartbeat_send_data (session, session->internals.hb_local_data.data, 
+                                   session->internals.hb_local_data.length, 
+                                   HEARTBEAT_REQUEST);
+        if (ret < 0)
+          return gnutls_assert_val(ret);
+
+        gettime (&session->internals.hb_ping_sent);
+            
+        if (!(flags & GNUTLS_HEARTBEAT_WAIT))
+          {
+            session->internals.hb_state = SHB_SEND1;
+            break;
+          }
+        
+        session->internals.hb_state = SHB_RECV;
+
+      case SHB_RECV:
+        ret = _gnutls_recv_int(session, GNUTLS_HEARTBEAT, -1, NULL, 0, NULL, session->internals.hb_timeout);
+        if (ret == GNUTLS_E_HEARTBEAT_PONG_RECEIVED)
+          {
+            session->internals.hb_state = SHB_SEND1;
+            break;
+          }
+        else if (ret == GNUTLS_E_TIMEDOUT)
+          {
+            retries++;
+            if (retries > max_tries)
+              {
+                session->internals.hb_state = SHB_SEND1;
+                return gnutls_assert_val(ret);
+              }
+
+            session->internals.hb_timeout *= 2;
+            session->internals.hb_timeout %= MAX_DTLS_TIMEOUT;
+            
+            session->internals.hb_state = SHB_SEND2;
+            goto retry;
+          }
+        else if (ret < 0)
+          {
+            session->internals.hb_state = SHB_SEND1;
+            return gnutls_assert_val(ret);
+          }
     }
-  else
-    _gnutls_record_log
-        ("REC[%p]: HB_REQUEST with length %zu already in-flight: %d\n",
-         session, data_size, gnutls_heartbeat_timeout (session, 1));
 
-  return ret;
+  return 0;
 }
 
 /**
- * simple wrapper for ping with random length
+ * gnutls_heartbeat_pong:
+ * @session: is a #gnutls_session_t structure.
+ * @flags: should be zero
+ *
+ * This function replies to a ping by sending a pong to the peer.
+ *
+ * Returns: %GNUTLS_E_SUCCESS on success, otherwise a negative error code.
  **/
-ssize_t
-gnutls_heartbeat_ping_rnd (gnutls_session_t session)
+int
+gnutls_heartbeat_pong (gnutls_session_t session, unsigned int flags)
 {
-  uint8_t rnd;
-  int ret;
-  
-  ret = gnutls_rnd (GNUTLS_RND_NONCE, &rnd, 1);
-  if (ret < 0)
-    return gnutls_assert_val(ret);
-  
-  return gnutls_heartbeat_ping (session, rnd + 1);
+int ret;
+
+  if (session->internals.hb_remote_data.length == 0)
+    return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+
+  ret = heartbeat_send_data (session, session->internals.hb_remote_data.data, 
+                             session->internals.hb_remote_data.length, 
+                             HEARTBEAT_RESPONSE);
+
+  _gnutls_buffer_reset (&session->internals.hb_remote_data);
+  return ret;
 }
 
-/**
+/*-
  * Process HB message in buffer.
  * @bufel: the (suspected) HeartBeat message (TLV+padding)
  *
@@ -244,11 +283,12 @@ gnutls_heartbeat_ping_rnd (gnutls_session_t session)
  * GNUTLS_E_AGAIN if processed OK
  * GNUTLS_E_HEARTBEAT_PONG_FAILED on response send failure for request message
  * GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER on payload mismatch for response message
**/
-*/
 int
 _gnutls_heartbeat_handle (gnutls_session_t session, mbuffer_st * bufel)
 {
   char pr[128];
+  int ret;
   uint8_t *msg = _mbuffer_get_udata_ptr (bufel);
   size_t hb_len, len = _mbuffer_get_udata_size (bufel);
 
@@ -261,108 +301,49 @@ _gnutls_heartbeat_handle (gnutls_session_t session, mbuffer_st * bufel)
   hb_len = _gnutls_read_uint16 (msg + 1);
   if (hb_len > len - 3)
     return gnutls_assert_val (GNUTLS_E_UNEXPECTED_PACKET);
-
+  
   switch (msg[0])
     {
     case HEARTBEAT_REQUEST:
       _gnutls_record_log
-          ("REC[%p]: received HEARTBEAT_REQUEST, responding...\n",
-           session);
-      if (_gnutls_heartbeat_send_data (session, msg + 3, hb_len, 0) >= 0)
-        return GNUTLS_E_AGAIN;  /* HB responded, no APP_DATA so needs to be called again */
-      else
-        {                       /* immediate response failed, TBD: save received data somewhere and let upper layers handle it, loosing single ping is non-critical for HB */
-          return GNUTLS_E_HEARTBEAT_PONG_FAILED;
-        }
-      break;
+          ("REC[%p]: received HEARTBEAT_REQUEST\n", session);
+
+      _gnutls_buffer_reset(&session->internals.hb_remote_data);
+
+      ret = _gnutls_buffer_resize (&session->internals.hb_remote_data, hb_len);
+      if (ret < 0)
+        return gnutls_assert_val(ret);
+      
+      memcpy(session->internals.hb_remote_data.data, msg+3, hb_len);
+      session->internals.hb_remote_data.length = hb_len;
+
+      return GNUTLS_E_HEARTBEAT_PING_RECEIVED;
 
     case HEARTBEAT_RESPONSE:
 
-      if (session->internals.heartbeat_payload.length != hb_len)
-        return gnutls_assert_val (GNUTLS_E_UNEXPECTED_PACKET_LENGTH);
+      if (hb_len != session->internals.hb_local_data.length)
+        return gnutls_assert_val (GNUTLS_E_UNEXPECTED_PACKET);
 
-      if (memcmp (msg + 3, session->internals.heartbeat_payload.data,
+      if (memcmp (msg + 3, session->internals.hb_local_data.data,
                   hb_len) != 0)
         {
           _gnutls_record_log ("REC[%p]: HB: %s - received\n", session,
                               _gnutls_bin2hex (msg + 3, hb_len, pr,
                                                sizeof (pr), NULL));
-          _gnutls_record_log ("REC[%p]: HB: %s - expected\n", session,
-                              _gnutls_bin2hex (session->internals.
-                                               heartbeat_payload.data,
-                                               hb_len, pr, sizeof (pr),
-                                               NULL));
-          return gnutls_assert_val (GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER);
+          return gnutls_assert_val (GNUTLS_E_UNEXPECTED_PACKET);
         }
-      _gnutls_record_log
-          ("REC[%p]: HB: %zu response bytes received OK (%d msec. left before timeout)\n",
-           session, hb_len, gnutls_heartbeat_timeout (session, 1));
 
-      session->internals.dtls.heartbeat_timeout = HEARTBEAT_TIMEOUT;
-      _gnutls_buffer_reset (&session->internals.heartbeat_payload);
-      return GNUTLS_E_AGAIN;
+      _gnutls_buffer_reset (&session->internals.hb_local_data);
 
+      return GNUTLS_E_HEARTBEAT_PONG_RECEIVED;
     default:
       _gnutls_record_log
           ("REC[%p]: HB: received unknown type %u\n",
            session, msg[0]);
       return gnutls_assert_val (GNUTLS_E_UNEXPECTED_PACKET);
     }
-
-  return gnutls_assert_val (GNUTLS_E_INTERNAL_ERROR);
 }
 
-/**
- * Update HB timeouts: should be called on retransmit to set new timeout.
- * @check_only: guarantees lack of side-effects (no variables are written)
- *
- * Returns:
- * number of milliseconds left before timeout expiration OR
- * GNUTLS_E_TIMEDOUT if timeout expired
- * GNUTLS_E_SUCCESS if timeout updated
- * GNUTLS_E_ILLEGAL_PARAMETER if no HB message is in-flight
- **/
-int
-gnutls_heartbeat_timeout (gnutls_session_t session, int check_only)
-{
-  struct timespec now;
-  unsigned int ms;
-  if (session->internals.heartbeat_payload.length)
-    {
-      _gnutls_debug_log
-          ("HB: %zu bytes are in-flight already: %u msec timeout.\n",
-           session->internals.heartbeat_payload.length,
-           (unsigned int) session->internals.dtls.heartbeat_timeout);
-      gettime (&now);
-      ms = _dtls_timespec_sub_ms (&now,
-                                  &session->internals.dtls.heartbeat_sent);
-      if (ms > session->internals.dtls.heartbeat_timeout)
-        {
-          if (check_only)
-            return GNUTLS_E_TIMEDOUT;
-          _gnutls_buffer_reset (&session->internals.heartbeat_payload);
-
-          if (session->internals.dtls.heartbeat_timeout * 2 >
-              MAX_HEARTBEAT_TIMEOUT)
-            {                   /* update impossible */
-              session->internals.dtls.heartbeat_timeout =
-                  HEARTBEAT_TIMEOUT;
-              return GNUTLS_E_TIMEDOUT;
-            }
-          else
-            {
-              session->internals.dtls.heartbeat_timeout *= 2;
-              gettime (&session->internals.dtls.heartbeat_sent);
-              return GNUTLS_E_SUCCESS;
-            }
-        }
-      else
-        {
-          return ms;
-        }
-    }
-  return GNUTLS_E_ILLEGAL_PARAMETER;
-}
 
 static int
 _gnutls_heartbeat_recv_params (gnutls_session_t session,
index 7c4b2991d7b65bb1c075ac9c5437123df0161329..085d14c2a96cec19d88b876cb367c4e217442c9d 100644 (file)
@@ -28,7 +28,7 @@
 #define HEARTBEAT_REQUEST 1
 #define HEARTBEAT_RESPONSE 2
 
-#define MAX_HEARTBEAT_LENGTH 16384
+#define MAX_HEARTBEAT_LENGTH DEFAULT_MAX_RECORD_SIZE
 #define HEARTBEAT_TIMEOUT 1000
 #define MAX_HEARTBEAT_TIMEOUT 60000
 
@@ -42,11 +42,6 @@ extern extension_entry_st ext_mod_heartbeat;
 typedef uint8_t heartbeat_policy_t;
 
 int _gnutls_heartbeat_handle (gnutls_session_t session, mbuffer_st * bufel);
-ssize_t _gnutls_heartbeat_send_data (gnutls_session_t session,
-                                     const void *data, size_t data_size,
-                                     int request);
-ssize_t gnutls_heartbeat_ping (gnutls_session_t session, size_t data_size);
-ssize_t gnutls_heartbeat_ping_rnd (gnutls_session_t session);
 int _gnutls_heartbeat_enabled (gnutls_session_t session, int local);
 int gnutls_heartbeat_timeout (gnutls_session_t session, int check_only);
 #endif
index 3dc10d2a2c71d78bbc51490c569c7765595e37a3..30db0336ed0dc4ddf3a5a67cb985f4f67829f309 100644 (file)
@@ -49,6 +49,7 @@
 #include <gnutls_dtls.h>
 #include <system.h>
 #include <gnutls_constate.h> /* gnutls_epoch_get */
+#include <gnutls_handshake.h> /* remaining_time() */
 #include <errno.h>
 #include <system.h>
 #include "debug.h"
@@ -174,24 +175,27 @@ int errno_to_gerr(int err)
 
 static ssize_t
 _gnutls_dgram_read (gnutls_session_t session, mbuffer_st **bufel,
-                   gnutls_pull_func pull_func, unsigned int ms)
+                   gnutls_pull_func pull_func, unsigned int *ms)
 {
   ssize_t i, ret;
   uint8_t *ptr;
+  struct timespec t1, t2;
   size_t max_size = _gnutls_get_max_decrypted_data(session);
   size_t recv_size = MAX_RECV_SIZE(session);
   gnutls_transport_ptr_t fd = session->internals.transport_recv_ptr;
+  unsigned int diff;
 
   if (recv_size > max_size)
     recv_size = max_size;
 
   session->internals.direction = 0;
 
-  if (ms)
+  if (ms && *ms > 0)
     {
-      ret = _gnutls_io_check_recv(session, ms);
+      ret = _gnutls_io_check_recv(session, *ms);
       if (ret < 0)
         return gnutls_assert_val(ret);
+      gettime(&t1);
     }
 
   *bufel = _mbuffer_alloc (0, max_size);
@@ -228,6 +232,16 @@ _gnutls_dgram_read (gnutls_session_t session, mbuffer_st **bufel,
       _mbuffer_set_udata_size (*bufel, i);
     }
 
+  if (ms && *ms > 0)
+    {
+      gettime(&t2);
+      diff = _dtls_timespec_sub_ms(&t2, &t1);
+      if (diff < *ms)
+        *ms -= diff;
+      else
+        return gnutls_assert_val(GNUTLS_E_TIMEDOUT);
+    }
+
   _gnutls_read_log ("READ: read %d bytes from %p\n", (int) i, fd);
   
   return i;
@@ -239,7 +253,7 @@ cleanup:
 
 static ssize_t
 _gnutls_stream_read (gnutls_session_t session, mbuffer_st **bufel,
-                    size_t size, gnutls_pull_func pull_func, unsigned int ms)
+                    size_t size, gnutls_pull_func pull_func, unsigned int *ms)
 {
   size_t left;
   ssize_t i = 0;
@@ -263,9 +277,9 @@ _gnutls_stream_read (gnutls_session_t session, mbuffer_st **bufel,
   left = size;
   while (left > 0)
     {
-      if (ms)
+      if (ms && *ms > 0)
         {
-          ret = _gnutls_io_check_recv(session, ms);
+          ret = _gnutls_io_check_recv(session, *ms);
           if (ret < 0)
             return gnutls_assert_val(ret);
             
@@ -314,12 +328,12 @@ _gnutls_stream_read (gnutls_session_t session, mbuffer_st **bufel,
       left -= i;
       (*bufel)->msg.size += i;
 
-      if (ms && left)
+      if (ms && *ms > 0)
         {
           gettime(&t2);
           diff = _dtls_timespec_sub_ms(&t2, &t1);
-          if (diff < ms)
-            ms -= diff;
+          if (diff < *ms)
+            *ms -= diff;
           else
             return gnutls_assert_val(GNUTLS_E_TIMEDOUT);
         }
@@ -341,7 +355,7 @@ finish:
  */
 static ssize_t
 _gnutls_read (gnutls_session_t session, mbuffer_st **bufel,
-             size_t size, gnutls_pull_func pull_func, unsigned int ms)
+             size_t size, gnutls_pull_func pull_func, unsigned int *ms)
 {
   if (IS_DTLS (session))
     /* Size is not passed, since a whole datagram will be read. */
@@ -402,7 +416,10 @@ _gnutls_writev (gnutls_session_t session, const giovec_t * giovec,
   return i;
 }
 
-/* This function is like recv(with MSG_PEEK). But it does not return -1 on error.
+/* 
+ * @ms: a pointer to the number of milliseconds to wait for data. Use zero or NULL for indefinite.
+ *
+ * This function is like recv(with MSG_PEEK). But it does not return -1 on error.
  * It does return gnutls_errno instead.
  * This function reads data from the socket and keeps them in a buffer, of up to
  * MAX_RECV_SIZE. 
@@ -416,7 +433,7 @@ _gnutls_writev (gnutls_session_t session, const giovec_t * giovec,
  */
 ssize_t
 _gnutls_io_read_buffered (gnutls_session_t session, size_t total,
-                          content_type_t recv_type, unsigned int ms)
+                          content_type_t recv_type, unsigned int *ms)
 {
   ssize_t ret = 0;
   size_t min;
@@ -1170,6 +1187,7 @@ _gnutls_handshake_io_recv_int (gnutls_session_t session,
                                handshake_buffer_st * hsk, unsigned int optional)
 {
   int ret;
+  unsigned int tleft = 0;
 
   ret = get_last_packet(session, htype, hsk, optional);
   if (ret != GNUTLS_E_AGAIN && ret != GNUTLS_E_INTERRUPTED && ret != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE)
@@ -1195,9 +1213,17 @@ _gnutls_handshake_io_recv_int (gnutls_session_t session,
         return gnutls_assert_val(ret);
     }
 
+  if (htype != (unsigned)-1)
+    {
+      ret = handshake_remaining_time(session);
+      if (ret < 0)
+        return gnutls_assert_val(ret);
+      tleft = ret;
+    }
+
   /* if we don't have a complete message waiting for us, try 
    * receiving more */
-  ret = _gnutls_recv_in_buffers(session, GNUTLS_HANDSHAKE, htype);
+  ret = _gnutls_recv_in_buffers(session, GNUTLS_HANDSHAKE, htype, tleft);
   if (ret < 0)
     return gnutls_assert_val_fatal(ret);
 
index 52106acf4116d5029cf4436754843fe6dab2e342..193e97f21fa14825f62bb5a37b38595a03375329 100644 (file)
@@ -53,7 +53,7 @@ record_check_unprocessed (gnutls_session_t session)
 int _gnutls_record_buffer_get (content_type_t type,
                                gnutls_session_t session, uint8_t * data,
                                size_t length, uint8_t seq[8]);
-ssize_t _gnutls_io_read_buffered (gnutls_session_t, size_t n, content_type_t, unsigned int ms);
+ssize_t _gnutls_io_read_buffered (gnutls_session_t, size_t n, content_type_t, unsigned int *ms);
 int _gnutls_io_clear_peeked_data (gnutls_session_t session);
 
 ssize_t _gnutls_io_write_buffered (gnutls_session_t session,
@@ -109,7 +109,7 @@ _gnutls_parse_record_buffered_msgs (gnutls_session_t session);
 
 ssize_t
 _gnutls_recv_in_buffers (gnutls_session_t session, content_type_t type,
-                         gnutls_handshake_description_t htype);
+                         gnutls_handshake_description_t htype, unsigned int ms);
 
 #define _gnutls_handshake_io_buffer_clear( session) \
         _mbuffer_head_clear( &session->internals.handshake_send_buffer); \
index ae6faf9aebfd859209611dd82c1ea185c7e45759..54c56815e8f2cf1e2f7de8d0b44c9776a0a4c41f 100644 (file)
@@ -179,7 +179,7 @@ static int is_next_hpacket_expected(gnutls_session_t session)
 int ret;
 
   /* htype is arbitrary */
-  ret = _gnutls_recv_in_buffers(session, GNUTLS_HANDSHAKE, GNUTLS_HANDSHAKE_FINISHED);
+  ret = _gnutls_recv_in_buffers(session, GNUTLS_HANDSHAKE, GNUTLS_HANDSHAKE_FINISHED, 0);
   if (ret < 0)
     return gnutls_assert_val(ret);
   
index b43771f11c0652277dc63ab3bd8d9f3ab294e931..2a14f74a13acb3aa358f097a5fc22308a57dd0fd 100644 (file)
@@ -93,10 +93,10 @@ static const gnutls_error_entry error_algorithms[] = {
                GNUTLS_E_NO_CERTIFICATE_FOUND, 1),
   ERROR_ENTRY (N_("The given DSA key is incompatible with the selected TLS protocol."),
                GNUTLS_E_INCOMPAT_DSA_KEY_WITH_TLS_PROTOCOL, 1),
-  ERROR_ENTRY (N_("HeartBeat Response to Request has failed for some reason: pong dropped."),
-               GNUTLS_E_HEARTBEAT_PONG_FAILED, 1),
-  ERROR_ENTRY (N_("HeartBeat message is already in-flight."),
-               GNUTLS_E_HEARTBEAT_FLIGHT, 1),
+  ERROR_ENTRY (N_("A heartbeat pong message was received."),
+               GNUTLS_E_HEARTBEAT_PONG_RECEIVED, 0),
+  ERROR_ENTRY (N_("A heartbeat ping message was received."),
+               GNUTLS_E_HEARTBEAT_PING_RECEIVED, 0),
   ERROR_ENTRY (N_("There is already a crypto algorithm with lower priority."),
                GNUTLS_E_CRYPTO_ALREADY_REGISTERED, 1),
 
index edb736b4c57088490af2ec446bdbec1f659795cf..1eb5d6b4546e459f419701e9041e447562305cf7 100644 (file)
@@ -2725,6 +2725,12 @@ _gnutls_recv_handshake_final (gnutls_session_t session, int init)
   int ret = 0;
   uint8_t ch;
   unsigned int ccs_len = 1;
+  unsigned int tleft;
+  
+  ret = handshake_remaining_time(session);
+  if (ret < 0)
+    return gnutls_assert_val(ret);
+  tleft = ret;
 
   switch (STATE)
     {
@@ -2747,7 +2753,8 @@ _gnutls_recv_handshake_final (gnutls_session_t session, int init)
       if (gnutls_protocol_get_version (session) == GNUTLS_DTLS0_9)
         ccs_len = 3;
 
-      ret = _gnutls_recv_int (session, GNUTLS_CHANGE_CIPHER_SPEC, -1, &ch, ccs_len, NULL);
+      ret = _gnutls_recv_int (session, GNUTLS_CHANGE_CIPHER_SPEC, -1, &ch, ccs_len, NULL, 
+                              tleft);
       if (ret <= 0)
         {
           ERR ("recv ChangeCipherSpec", ret);
index 22a55f7decc5646426f81bda66e70101aa3cc520..4fa14f3d9cf8adce92172a861f2d180eb77dae1c 100644 (file)
@@ -23,6 +23,8 @@
 #ifndef HANDSHAKE_H
 #define HANDSHAKE_H
 
+#include <gnutls_errors.h>
+
 int _gnutls_send_handshake (gnutls_session_t session, mbuffer_st * bufel,
                             gnutls_handshake_description_t type);
 int _gnutls_recv_hello_request (gnutls_session_t session, void *data,
@@ -55,4 +57,17 @@ void _gnutls_handshake_hash_buffers_clear (gnutls_session_t session);
 #define AGAIN(target) (STATE==target?1:0)
 #define AGAIN2(state, target) (state==target?1:0)
 
+inline static int handshake_remaining_time(gnutls_session_t session)
+{
+  if (session->internals.handshake_endtime)
+    {
+      time_t now = gnutls_time(0);
+      if (now < session->internals.handshake_endtime)
+        return (session->internals.handshake_endtime - now) * 1000;
+      else 
+        return gnutls_assert_val(GNUTLS_E_TIMEDOUT);
+    }
+  return 0;
+}
+
 #endif
index d80c2a725550e370ec2826620ba7edb8bc047cef..5462789096e9247d31a761e38103053ed2fa9924 100644 (file)
@@ -230,6 +230,13 @@ typedef enum handshake_state_t
   STATE60 = 60, STATE61, STATE62, STATE70, STATE71
 } handshake_state_t;
 
+typedef enum heartbeat_state_t
+{  
+  SHB_SEND1 = 0, 
+  SHB_SEND2, 
+  SHB_RECV, 
+} heartbeat_state_t;
+
 #include <gnutls_str.h>
 
 /* This is the maximum number of algorithms (ciphers or macs etc).
@@ -660,9 +667,6 @@ typedef struct
    */
   time_t async_term;
 
-  struct timespec heartbeat_sent; /* timestamp: when last HeartBeat Request was sent*/
-  time_t heartbeat_timeout; /* current timeout, in milliseconds*/
-
   /* last retransmission triggered by record layer */
   struct timespec last_retransmit;
   unsigned int packets_dropped;
@@ -890,14 +894,19 @@ typedef struct
   /* if set it means that the master key was set using
    * gnutls_session_set_master() rather than being negotiated. */
   unsigned int premaster_set:1;
-
+  
   unsigned int cb_tls_unique_len;
   unsigned char cb_tls_unique[MAX_VERIFY_DATA_SIZE];
   
   unsigned int handshake_endtime; /* end time in seconds */
   unsigned int handshake_timeout_ms; /* timeout in milliseconds */
 
-  gnutls_buffer_st heartbeat_payload; /* store in-flight payload for heartbeat extension*/
+  gnutls_buffer_st hb_local_data;
+  gnutls_buffer_st hb_remote_data;
+  struct timespec hb_ping_sent; /* timestamp: when last HeartBeat ping was sent*/
+  unsigned int hb_timeout; /* current timeout, in milliseconds*/
+
+  heartbeat_state_t hb_state; /* for ping */
   
   /* If you add anything here, check _gnutls_handshake_internal_state_clear().
    */
index 2a616338e0023fde07b1decc4382b3b7f29349b8..9f91f3d15c93c91e4bcab54a06ae24ea6a54ca0a 100644 (file)
@@ -220,7 +220,7 @@ gnutls_bye (gnutls_session_t session, gnutls_close_request_t how)
         {
           do
             {
-              ret = _gnutls_recv_int (session, GNUTLS_ALERT, -1, NULL, 0, NULL);
+              ret = _gnutls_recv_int (session, GNUTLS_ALERT, -1, NULL, 0, NULL, 0);
             }
           while (ret == GNUTLS_E_GOT_APPLICATION_DATA);
 
@@ -874,7 +874,7 @@ record_read_headers (gnutls_session_t session,
 static int recv_headers( gnutls_session_t session, content_type_t type, 
                          gnutls_handshake_description_t htype, 
                          struct tls_record_st* record,
-                         unsigned int ms)
+                         unsigned int *ms)
 {
 int ret;
 gnutls_datum_t raw; /* raw headers */
@@ -952,7 +952,9 @@ gnutls_datum_t raw; /* raw headers */
 
 #define MAX_EMPTY_PACKETS_SEQUENCE 4
 
-/* This will receive record layer packets and add them to 
+/* @ms: is the number of milliseconds to wait for data. Use zero for indefinite.
+ *
+ * This will receive record layer packets and add them to 
  * application_data_buffer and handshake_data_buffer.
  *
  * If the htype is not -1 then handshake timeouts
@@ -960,7 +962,7 @@ gnutls_datum_t raw; /* raw headers */
  */
 ssize_t
 _gnutls_recv_in_buffers (gnutls_session_t session, content_type_t type,
-                         gnutls_handshake_description_t htype)
+                         gnutls_handshake_description_t htype, unsigned int ms)
 {
   uint64 *packet_sequence;
   uint8_t *ciphertext;
@@ -970,7 +972,6 @@ _gnutls_recv_in_buffers (gnutls_session_t session, content_type_t type,
   record_parameters_st *record_params;
   record_state_st *record_state;
   struct tls_record_st record;
-  time_t now, tleft = 0;
 
 begin:
 
@@ -1003,17 +1004,8 @@ begin:
 
   record_state = &record_params->read;
 
-  if (htype != (unsigned)-1 && session->internals.handshake_endtime > 0)
-    {
-      now = gnutls_time(0);
-      if (now < session->internals.handshake_endtime)
-        tleft = (session->internals.handshake_endtime - now) * 1000;
-      else
-        return gnutls_assert_val(GNUTLS_E_TIMEDOUT);
-    }
-
   /* receive headers */
-  ret = recv_headers(session, type, htype, &record, tleft);
+  ret = recv_headers(session, type, htype, &record, &ms);
   if (ret < 0)
     {
       ret = gnutls_assert_val_fatal(ret);
@@ -1025,20 +1017,11 @@ begin:
   else
     packet_sequence = &record_state->sequence_number;
 
-  if (htype != (unsigned)-1 && session->internals.handshake_endtime > 0)
-    {
-      now = gnutls_time(0);
-      if (now < session->internals.handshake_endtime)
-        tleft = (session->internals.handshake_endtime - now) * 1000;
-      else
-        return gnutls_assert_val(GNUTLS_E_TIMEDOUT);
-    }
-
   /* Read the packet data and insert it to record_recv_buffer.
    */
   ret =
        _gnutls_io_read_buffered (session, record.packet_size,
-                                 record.type, tleft);
+                                 record.type, &ms);
   if (ret != record.packet_size)
     {
       gnutls_assert();
@@ -1210,7 +1193,8 @@ recv_error:
 ssize_t
 _gnutls_recv_int (gnutls_session_t session, content_type_t type,
                   gnutls_handshake_description_t htype,
-                  uint8_t * data, size_t data_size, void* seq)
+                  uint8_t * data, size_t data_size, void* seq,
+                  unsigned int ms)
 {
   int ret;
 
@@ -1241,7 +1225,7 @@ _gnutls_recv_int (gnutls_session_t session, content_type_t type,
   if (ret != 0)
     return ret;
 
-  ret = _gnutls_recv_in_buffers(session, type, htype);
+  ret = _gnutls_recv_in_buffers(session, type, htype, ms);
   if (ret < 0 && ret != GNUTLS_E_SESSION_EOF)
     return gnutls_assert_val(ret);
 
@@ -1312,8 +1296,9 @@ gnutls_record_send (gnutls_session_t session, const void *data,
  * The number of bytes received might be less than the requested @data_size.
  **/
 ssize_t
-gnutls_record_recv (gnutls_session_t session, void *data, size_t data_size) {
-    return _gnutls_recv_int (session, GNUTLS_APPLICATION_DATA, -1, data, data_size, NULL);
+gnutls_record_recv (gnutls_session_t session, void *data, size_t data_size) 
+{
+    return _gnutls_recv_int (session, GNUTLS_APPLICATION_DATA, -1, data, data_size, NULL, 0);
 }
 
 /**
@@ -1341,5 +1326,5 @@ gnutls_record_recv_seq (gnutls_session_t session, void *data, size_t data_size,
   unsigned char *seq)
 {
   return _gnutls_recv_int (session, GNUTLS_APPLICATION_DATA, -1, data,
-                           data_size, seq);
+                           data_size, seq, 0);
 }
index 4151673f1da4f276298656b9e66062a465e0264b..12cc31a5923c8dc166ea7e9c9d648736cf1ae3d6 100644 (file)
@@ -32,7 +32,7 @@ ssize_t _gnutls_send_int (gnutls_session_t session, content_type_t type,
                           size_t sizeofdata, unsigned int mflags);
 ssize_t _gnutls_recv_int (gnutls_session_t session, content_type_t type,
                           gnutls_handshake_description_t, uint8_t * data,
-                          size_t sizeofdata, void* seq);
+                          size_t sizeofdata, void* seq, unsigned int ms);
 int _gnutls_get_max_decrypted_data(gnutls_session_t session);
 
 #endif
index a2feb5b53eb79d3016fe8c69c91218e316994097..6adfd417fa6dc44d381e8b5ea75a5d20a59c4b27 100644 (file)
@@ -326,7 +326,8 @@ gnutls_init (gnutls_session_t * session, unsigned int flags)
 
   /* Initialize buffers */
   _gnutls_buffer_init (&(*session)->internals.handshake_hash_buffer);
-  _gnutls_buffer_init (&(*session)->internals.heartbeat_payload);
+  _gnutls_buffer_init (&(*session)->internals.hb_remote_data);
+  _gnutls_buffer_init (&(*session)->internals.hb_local_data);
 
   _mbuffer_head_init (&(*session)->internals.record_buffer);
   _mbuffer_head_init (&(*session)->internals.record_send_buffer);
@@ -441,7 +442,8 @@ gnutls_deinit (gnutls_session_t session)
       }
 
   _gnutls_buffer_clear (&session->internals.handshake_hash_buffer);
-  _gnutls_buffer_clear (&session->internals.heartbeat_payload);
+  _gnutls_buffer_clear (&session->internals.hb_remote_data);
+  _gnutls_buffer_clear (&session->internals.hb_local_data);
 
   _mbuffer_head_clear (&session->internals.record_buffer);
   _mbuffer_head_clear (&session->internals.record_recv_buffer);
index b1685cd80a9fbeb9adc83c161ff3e58cd4cc55c5..ce77a8a300d005637784979e74764cfe29427fcc 100644 (file)
@@ -857,8 +857,11 @@ gnutls_ecc_curve_t gnutls_ecc_curve_get(gnutls_session_t session);
 
 /* Record layer functions.
  */
-  ssize_t gnutls_heartbeat_ping (gnutls_session_t session, size_t data_size);
-  ssize_t gnutls_heartbeat_ping_rnd (gnutls_session_t session);
+#define GNUTLS_HEARTBEAT_WAIT 1
+  int gnutls_heartbeat_ping (gnutls_session_t session, size_t data_size, 
+                       unsigned int max_tries, unsigned int flags);
+  int gnutls_heartbeat_pong (gnutls_session_t session, unsigned int flags);
+
   ssize_t gnutls_record_send (gnutls_session_t session, const void *data,
                               size_t data_size);
   ssize_t gnutls_record_recv (gnutls_session_t session, void *data,
@@ -912,10 +915,11 @@ gnutls_ecc_curve_t gnutls_ecc_curve_get(gnutls_session_t session);
 #define GNUTLS_HB_PEER_ALLOWED_TO_SEND (1)
 #define GNUTLS_HB_PEER_NOT_ALLOWED_TO_SEND (1<<1)
 
-    /* Heartbeat */
-    void gnutls_heartbeat_enable (gnutls_session_t session, unsigned int type);
-    int gnutls_heartbeat_allowed (gnutls_session_t session, unsigned int type);
-    int gnutls_heartbeat_timeout (gnutls_session_t session, int check_only);
+  /* Heartbeat */
+  void gnutls_heartbeat_enable (gnutls_session_t session, unsigned int type);
+
+#define GNUTLS_HB_LOCAL_ALLOWED_TO_SEND (1<<2)
+  int gnutls_heartbeat_allowed (gnutls_session_t session, unsigned int type);
 
   /* Safe renegotiation */
   int gnutls_safe_renegotiation_status (gnutls_session_t session);
@@ -1977,8 +1981,8 @@ typedef int (*gnutls_pin_callback_t) (void *userdata, int attempt,
 #define GNUTLS_E_OPENPGP_PREFERRED_KEY_ERROR -215
 #define GNUTLS_E_INCOMPAT_DSA_KEY_WITH_TLS_PROTOCOL -216
 
-#define GNUTLS_E_HEARTBEAT_FLIGHT -298
-#define GNUTLS_E_HEARTBEAT_PONG_FAILED -299
+#define GNUTLS_E_HEARTBEAT_PONG_RECEIVED -292
+#define GNUTLS_E_HEARTBEAT_PING_RECEIVED -293
 
 /* PKCS11 related */
 #define GNUTLS_E_PKCS11_ERROR -300
index 032ee644cb80ce011a0ce99c25eb68b409ea94b3..110209cff9fa9eceaabf921d4e5946e6c204a402 100644 (file)
@@ -302,7 +302,7 @@ GNUTLS_1_4
     gnutls_record_send;
     gnutls_record_set_max_size;
     gnutls_heartbeat_ping;
-    gnutls_heartbeat_ping_rnd;
+    gnutls_heartbeat_pong;
     gnutls_rehandshake;
     gnutls_rsa_export_get_modulus_bits;
     gnutls_rsa_export_get_pubkey;
@@ -337,7 +337,6 @@ GNUTLS_1_4
     gnutls_sign_list;
     gnutls_heartbeat_enable;
     gnutls_heartbeat_allowed;
-    gnutls_heartbeat_timeout;
     gnutls_srp_1024_group_generator;
     gnutls_srp_1024_group_prime;
     gnutls_srp_1536_group_generator;