]> git.ipfire.org Git - thirdparty/gnutls.git/commitdiff
Better handling of timeouts.
authorNikos Mavrogiannopoulos <nmav@gnutls.org>
Fri, 21 Sep 2012 07:00:01 +0000 (09:00 +0200)
committerNikos Mavrogiannopoulos <nmav@gnutls.org>
Fri, 21 Sep 2012 07:02:01 +0000 (09:02 +0200)
doc/cha-intro-tls.texi
lib/ext/heartbeat.c
lib/ext/heartbeat.h
lib/gnutls_int.h
lib/gnutls_state.c
lib/includes/gnutls/gnutls.h.in
lib/libgnutls.map

index 581a52777cb76afab329d6df09a4c2b11bbe255a..33f6b16dcab542d4f90b4cfcf37896ef3697d7ca 100644 (file)
@@ -464,7 +464,7 @@ Policy-related functions:
 @showfuncB{gnutls_heartbeat_allowed,gnutls_heartbeat_enable}
 
 Operational functions:
-@showfuncB{gnutls_heartbeat_ping,gnutls_heartbeat_pong}
+@showfuncD{gnutls_heartbeat_ping,gnutls_heartbeat_pong,gnutls_heartbeat_set_timeouts,gnutls_heartbeat_get_timeout}
 
 @node Safe renegotiation
 @subsection Safe renegotiation
index 1cce81c32d982dfb0b9b82f9836fd50ebf28519c..b2401c8841db507abc5c49b82dcfe3fd5486faa9 100644 (file)
@@ -35,6 +35,8 @@
   *
   * This function will allow heartbeat messages to be
   * received.
+  *
+  * Since: 3.1.2
   **/
 void
 gnutls_heartbeat_enable (gnutls_session_t session, unsigned int type)
@@ -72,6 +74,7 @@ _gnutls_heartbeat (unsigned policy)
   *
   * Returns: Non zero if heartbeats are allowed.
   *
+  * Since: 3.1.2
   **/
 int
 gnutls_heartbeat_allowed (gnutls_session_t session, unsigned int type)
@@ -166,13 +169,16 @@ heartbeat_send_data (gnutls_session_t session, const void *data,
  * and timeouts manually.
  *
  * Returns: %GNUTLS_E_SUCCESS on success, otherwise a negative error code.
+ *
+ * Since: 3.1.2
  **/
 int
 gnutls_heartbeat_ping (gnutls_session_t session, size_t data_size, 
                        unsigned int max_tries, unsigned int flags)
 {
   int ret;
-  unsigned int retries = 1;
+  unsigned int retries = 1, diff;
+  struct timespec now;
 
   if (data_size > MAX_HEARTBEAT_LENGTH)
     return gnutls_assert_val (GNUTLS_E_UNEXPECTED_PACKET_LENGTH);
@@ -196,10 +202,11 @@ gnutls_heartbeat_ping (gnutls_session_t session, size_t data_size,
         if (ret < 0)
           return gnutls_assert_val(ret);
 
+        gettime (&session->internals.hb_ping_start);
         session->internals.hb_local_data.length = data_size;
         session->internals.hb_state = SHB_SEND2;
       case SHB_SEND2:
-        session->internals.hb_timeout = HEARTBEAT_TIMEOUT;
+        session->internals.hb_actual_retrans_timeout_ms = session->internals.hb_retrans_timeout_ms;
 retry:
         ret = heartbeat_send_data (session, session->internals.hb_local_data.data, 
                                    session->internals.hb_local_data.length, 
@@ -218,7 +225,7 @@ retry:
         session->internals.hb_state = SHB_RECV;
 
       case SHB_RECV:
-        ret = _gnutls_recv_int(session, GNUTLS_HEARTBEAT, -1, NULL, 0, NULL, session->internals.hb_timeout);
+        ret = _gnutls_recv_int(session, GNUTLS_HEARTBEAT, -1, NULL, 0, NULL, session->internals.hb_actual_retrans_timeout_ms);
         if (ret == GNUTLS_E_HEARTBEAT_PONG_RECEIVED)
           {
             session->internals.hb_state = SHB_SEND1;
@@ -233,8 +240,15 @@ retry:
                 return gnutls_assert_val(ret);
               }
 
-            session->internals.hb_timeout *= 2;
-            session->internals.hb_timeout %= MAX_DTLS_TIMEOUT;
+            diff = _dtls_timespec_sub_ms(&now, &session->internals.hb_ping_start);
+            if (diff > session->internals.hb_total_timeout_ms)
+              {
+                session->internals.hb_state = SHB_SEND1;
+                return gnutls_assert_val(GNUTLS_E_TIMEDOUT);
+              }
+
+            session->internals.hb_actual_retrans_timeout_ms *= 2;
+            session->internals.hb_actual_retrans_timeout_ms %= MAX_DTLS_TIMEOUT;
             
             session->internals.hb_state = SHB_SEND2;
             goto retry;
@@ -257,6 +271,8 @@ retry:
  * This function replies to a ping by sending a pong to the peer.
  *
  * Returns: %GNUTLS_E_SUCCESS on success, otherwise a negative error code.
+ *
+ * Since: 3.1.2
  **/
 int
 gnutls_heartbeat_pong (gnutls_session_t session, unsigned int flags)
@@ -344,6 +360,60 @@ _gnutls_heartbeat_handle (gnutls_session_t session, mbuffer_st * bufel)
     }
 }
 
+/**
+ * gnutls_heartbeat_get_timeout:
+ * @session: is a #gnutls_session_t structure.
+ *
+ * This function will return the milliseconds remaining
+ * for a retransmission of the previously sent ping
+ * message. This function is useful when ping is used in
+ * non-blocking mode, to estimate when to call gnutls_heartbeat_ping()
+ * if no packets have been received.
+ *
+ * Returns: the remaining time in milliseconds.
+ *
+ * Since: 3.1.2
+ **/
+unsigned int gnutls_heartbeat_get_timeout (gnutls_session_t session)
+{
+struct timespec now;
+unsigned int diff;
+
+  gettime(&now);
+  diff = _dtls_timespec_sub_ms(&now, &session->internals.hb_ping_sent);
+  if (diff >= session->internals.hb_actual_retrans_timeout_ms)
+    return 0;
+  else
+    return session->internals.hb_actual_retrans_timeout_ms - diff;
+}
+
+/**
+ * gnutls_heartbeat_set_timeouts:
+ * @session: is a #gnutls_session_t structure.
+ * @retrans_timeout: The time at which a retransmission will occur in milliseconds
+ * @total_timeout: The time at which the connection will be aborted, in milliseconds.
+ *
+ * This function will set the timeouts required for the DTLS handshake
+ * protocol. The retransmission timeout is the time after which a
+ * message from the peer is not received, the previous messages will
+ * be retransmitted. The total timeout is the time after which the
+ * handshake will be aborted with %GNUTLS_E_TIMEDOUT.
+ *
+ * The DTLS protocol recommends the values of 1 sec and 60 seconds
+ * respectively.
+ *
+ * If the retransmission timeout is zero then the handshake will operate
+ * in a non-blocking way, i.e., return %GNUTLS_E_AGAIN.
+ *
+ * Since: 3.1.2
+ **/
+void gnutls_heartbeat_set_timeouts (gnutls_session_t session, unsigned int retrans_timeout,
+                                    unsigned int total_timeout)
+{
+  session->internals.hb_retrans_timeout_ms = retrans_timeout;
+  session->internals.hb_total_timeout_ms = total_timeout;
+}
+
 
 static int
 _gnutls_heartbeat_recv_params (gnutls_session_t session,
index 085d14c2a96cec19d88b876cb367c4e217442c9d..ee7a534ae6c80353bbafadd62c9c85dfb3c2c9bc 100644 (file)
@@ -29,8 +29,6 @@
 #define HEARTBEAT_RESPONSE 2
 
 #define MAX_HEARTBEAT_LENGTH DEFAULT_MAX_RECORD_SIZE
-#define HEARTBEAT_TIMEOUT 1000
-#define MAX_HEARTBEAT_TIMEOUT 60000
 
 #define LOCAL_ALLOWED_TO_SEND (1<<2)
 #define LOCAL_NOT_ALLOWED_TO_SEND (1<<3)
index 5462789096e9247d31a761e38103053ed2fa9924..bf5280f51af950580631a465d6aaf8624b88a1bb 100644 (file)
@@ -903,8 +903,11 @@ typedef struct
 
   gnutls_buffer_st hb_local_data;
   gnutls_buffer_st hb_remote_data;
+  struct timespec hb_ping_start; /* timestamp: when first HeartBeat ping was sent*/
   struct timespec hb_ping_sent; /* timestamp: when last HeartBeat ping was sent*/
-  unsigned int hb_timeout; /* current timeout, in milliseconds*/
+  unsigned int hb_actual_retrans_timeout_ms; /* current timeout, in milliseconds*/
+  unsigned int hb_retrans_timeout_ms; /* the default timeout, in milliseconds*/
+  unsigned int hb_total_timeout_ms; /* the total timeout, in milliseconds*/
 
   heartbeat_state_t hb_state; /* for ping */
   
index 6adfd417fa6dc44d381e8b5ea75a5d20a59c4b27..5793c5c0294d0ce0390ddbf8fb53f9b351462f8d 100644 (file)
@@ -383,6 +383,9 @@ gnutls_init (gnutls_session_t * session, unsigned int flags)
   gnutls_transport_set_errno_function (*session, system_errno);
   gnutls_transport_set_pull_timeout_function (*session, system_recv_timeout);
 
+  (*session)->internals.hb_retrans_timeout_ms = 1000;
+  (*session)->internals.hb_total_timeout_ms = 60000;
+
   if (flags & GNUTLS_DATAGRAM)
     {
       (*session)->internals.dtls.mtu = DTLS_DEFAULT_MTU;
index 5ca00ae320109529131cf905fb974ec282a85ea3..ef4f1269e79ac7cde50c81ec10249f37f28dc81a 100644 (file)
@@ -915,6 +915,10 @@ gnutls_ecc_curve_t gnutls_ecc_curve_get(gnutls_session_t session);
                               void *data, size_t * data_length,
                               unsigned int *type, unsigned int indx);
 
+  unsigned int gnutls_heartbeat_get_timeout (gnutls_session_t session);
+  void gnutls_heartbeat_set_timeouts (gnutls_session_t session, unsigned int retrans_timeout,
+                                      unsigned int total_timeout);
+
 #define GNUTLS_HB_PEER_ALLOWED_TO_SEND (1)
 #define GNUTLS_HB_PEER_NOT_ALLOWED_TO_SEND (1<<1)
 
index 110209cff9fa9eceaabf921d4e5946e6c204a402..8df7823577c4ccca67c0d075ccc9159f3cec4ca7 100644 (file)
@@ -301,8 +301,6 @@ GNUTLS_1_4
     gnutls_record_recv;
     gnutls_record_send;
     gnutls_record_set_max_size;
-    gnutls_heartbeat_ping;
-    gnutls_heartbeat_pong;
     gnutls_rehandshake;
     gnutls_rsa_export_get_modulus_bits;
     gnutls_rsa_export_get_pubkey;
@@ -335,8 +333,6 @@ GNUTLS_1_4
     gnutls_sign_get_id;
     gnutls_sign_get_name;
     gnutls_sign_list;
-    gnutls_heartbeat_enable;
-    gnutls_heartbeat_allowed;
     gnutls_srp_1024_group_generator;
     gnutls_srp_1024_group_prime;
     gnutls_srp_1536_group_generator;
@@ -834,6 +830,12 @@ GNUTLS_3_1_0 {
        gnutls_sign_get_hash_algorithm;
        gnutls_sign_algorithm_get;
        gnutls_x509_privkey_get_pk_algorithm2;
+        gnutls_heartbeat_ping;
+        gnutls_heartbeat_pong;
+        gnutls_heartbeat_enable;
+        gnutls_heartbeat_allowed;
+       gnutls_heartbeat_get_timeout;
+       gnutls_heartbeat_set_timeouts;
 } GNUTLS_3_0_0;
 
 GNUTLS_PRIVATE {