[glib-networking/wip/pwithnall/dtls: 3/3] gnutls: Internally support per-operation timeouts



commit 6531acc18859ce952eec0190b9a0ff541693da50
Author: Philip Withnall <philip withnall collabora co uk>
Date:   Thu Jul 30 15:56:00 2015 +0100

    gnutls: Internally support per-operation timeouts
    
    For every operation except handshaking (which is complex), convert the
    per-operation blocking parameter to a timeout parameter, supporting
    blocking, non-blocking and timeout behaviour. This can be used by the
    GDatagramBased interface to support its per-operation timeouts.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=697908

 tls/gnutls/gtlsconnection-gnutls.c   |  102 ++++++++++++++++++++++------------
 tls/gnutls/gtlsconnection-gnutls.h   |    4 +-
 tls/gnutls/gtlsinputstream-gnutls.c  |    5 +-
 tls/gnutls/gtlsoutputstream-gnutls.c |    5 +-
 4 files changed, 74 insertions(+), 42 deletions(-)
---
diff --git a/tls/gnutls/gtlsconnection-gnutls.c b/tls/gnutls/gtlsconnection-gnutls.c
index 4e1baf2..6c1c2bc 100644
--- a/tls/gnutls/gtlsconnection-gnutls.c
+++ b/tls/gnutls/gtlsconnection-gnutls.c
@@ -115,7 +115,7 @@ static P11KitPin*    on_pin_prompt_callback  (const char     *pinfile,
 static void g_tls_connection_gnutls_init_priorities (void);
 
 static gboolean do_implicit_handshake (GTlsConnectionGnutls  *gnutls,
-                                      gboolean               blocking,
+                                      gint64                 timeout,
                                       GCancellable          *cancellable,
                                       GError               **error);
 static gboolean finish_handshake (GTlsConnectionGnutls  *gnutls,
@@ -220,12 +220,12 @@ struct _GTlsConnectionGnutlsPrivate
   GCancellable *waiting_for_op;
 
   gboolean      reading;
-  gboolean      read_blocking;
+  gint64        read_timeout;
   GError       *read_error;
   GCancellable *read_cancellable;
 
   gboolean      writing;
-  gboolean      write_blocking;
+  gint64        write_timeout;
   GError       *write_error;
   GCancellable *write_cancellable;
 
@@ -668,7 +668,7 @@ typedef enum {
 static gboolean
 claim_op (GTlsConnectionGnutls    *gnutls,
          GTlsConnectionGnutlsOp   op,
-         gboolean                 blocking,
+         gint64                   timeout,
          GCancellable            *cancellable,
          GError                 **error)
 {
@@ -711,7 +711,7 @@ claim_op (GTlsConnectionGnutls    *gnutls,
        {
          gnutls->priv->need_handshake = FALSE;
          gnutls->priv->handshaking = TRUE;
-         if (!do_implicit_handshake (gnutls, blocking, cancellable, error))
+         if (!do_implicit_handshake (gnutls, timeout, cancellable, error))
            {
              g_mutex_unlock (&gnutls->priv->op_mutex);
              return FALSE;
@@ -751,12 +751,14 @@ claim_op (GTlsConnectionGnutls    *gnutls,
     {
       GPollFD fds[2];
       int nfds;
+      gint64 start_time;
+      gint result;
 
       g_cancellable_reset (gnutls->priv->waiting_for_op);
 
       g_mutex_unlock (&gnutls->priv->op_mutex);
 
-      if (!blocking)
+      if (timeout == 0)
        {
          g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK,
                               _("Operation would block"));
@@ -769,11 +771,39 @@ claim_op (GTlsConnectionGnutls    *gnutls,
       else
        nfds = 1;
 
-      g_poll (fds, nfds, -1);
+      /* Convert from microseconds to milliseconds. */
+      if (timeout != -1)
+        timeout = timeout / 1000;
+
+      /* Poll until cancellation or the timeout is reached. */
+      start_time = g_get_monotonic_time ();
+
+      while (!g_cancellable_is_cancelled (gnutls->priv->waiting_for_op) &&
+             !g_cancellable_is_cancelled (cancellable))
+        {
+          result = g_poll (fds, nfds, timeout);
+
+          if (result != -1 || errno != EINTR)
+            continue;
+
+          if (timeout != -1)
+            {
+              timeout -= (g_get_monotonic_time () - start_time) / 1000;
+              if (timeout < 0)
+                timeout = 0;
+            }
+        }
 
       if (nfds > 1)
         g_cancellable_release_fd (cancellable);
 
+      if (result == 0)
+        {
+          g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_TIMED_OUT,
+                               _("Socket I/O timed out"));
+          return FALSE;
+        }
+
       goto try_again;
     }
 
@@ -825,21 +855,21 @@ yield_op (GTlsConnectionGnutls   *gnutls,
 static void
 begin_gnutls_io (GTlsConnectionGnutls  *gnutls,
                 GIOCondition           direction,
-                gboolean               blocking,
+                gint64                 timeout,
                 GCancellable          *cancellable)
 {
   g_assert (direction & (G_IO_IN | G_IO_OUT));
 
   if (direction & G_IO_IN)
     {
-      gnutls->priv->read_blocking = blocking;
+      gnutls->priv->read_timeout = timeout;
       gnutls->priv->read_cancellable = cancellable;
       g_clear_error (&gnutls->priv->read_error);
     }
 
   if (direction & G_IO_OUT)
     {
-      gnutls->priv->write_blocking = blocking;
+      gnutls->priv->write_timeout = timeout;
       gnutls->priv->write_cancellable = cancellable;
       g_clear_error (&gnutls->priv->write_error);
     }
@@ -979,8 +1009,8 @@ end_gnutls_io (GTlsConnectionGnutls  *gnutls,
   return status;
 }
 
-#define BEGIN_GNUTLS_IO(gnutls, direction, blocking, cancellable)      \
-  begin_gnutls_io (gnutls, direction, blocking, cancellable);          \
+#define BEGIN_GNUTLS_IO(gnutls, direction, timeout, cancellable)       \
+  begin_gnutls_io (gnutls, direction, timeout, cancellable);           \
   do {
 
 #define END_GNUTLS_IO(gnutls, direction, ret, errmsg, err)             \
@@ -1334,7 +1364,7 @@ g_tls_connection_gnutls_pull_func (gnutls_transport_ptr_t  transport_data,
 
       ret = g_datagram_based_receive_messages (gnutls->priv->base_socket,
                                                &message, 1, 0,
-                                               gnutls->priv->read_blocking ? -1 : 0,
+                                               gnutls->priv->read_timeout,
                                                gnutls->priv->read_cancellable,
                                                &gnutls->priv->read_error);
 
@@ -1345,7 +1375,7 @@ g_tls_connection_gnutls_pull_func (gnutls_transport_ptr_t  transport_data,
     {
       ret = g_pollable_stream_read (G_INPUT_STREAM (gnutls->priv->base_istream),
                                     buf, buflen,
-                                    gnutls->priv->read_blocking,
+                                    (gnutls->priv->read_timeout != 0),
                                     gnutls->priv->read_cancellable,
                                     &gnutls->priv->read_error);
     }
@@ -1378,7 +1408,7 @@ g_tls_connection_gnutls_push_func (gnutls_transport_ptr_t  transport_data,
 
       ret = g_datagram_based_send_messages (gnutls->priv->base_socket,
                                             &message, 1, 0,
-                                            gnutls->priv->write_blocking ? -1 : 0,
+                                            gnutls->priv->write_timeout,
                                             gnutls->priv->write_cancellable,
                                             &gnutls->priv->write_error);
 
@@ -1389,7 +1419,7 @@ g_tls_connection_gnutls_push_func (gnutls_transport_ptr_t  transport_data,
     {
       ret = g_pollable_stream_write (G_OUTPUT_STREAM (gnutls->priv->base_ostream),
                                      buf, buflen,
-                                     gnutls->priv->write_blocking,
+                                     (gnutls->priv->write_timeout != 0),
                                      gnutls->priv->write_cancellable,
                                      &gnutls->priv->write_error);
     }
@@ -1445,7 +1475,7 @@ g_tls_connection_gnutls_vec_push_func (gnutls_transport_ptr_t  transport_data,
 
   ret = g_datagram_based_send_messages (gnutls->priv->base_socket,
                                         &message, 1, 0,
-                                        gnutls->priv->write_blocking,
+                                        gnutls->priv->write_timeout,
                                         gnutls->priv->write_cancellable,
                                         &gnutls->priv->write_error);
 
@@ -1614,7 +1644,7 @@ handshake_thread (GTask        *task,
   gnutls->priv->started_handshake = FALSE;
 
   if (!claim_op (gnutls, G_TLS_CONNECTION_GNUTLS_OP_HANDSHAKE,
-                TRUE, cancellable, &error))
+                -1  /* blocking */, cancellable, &error))
     {
       g_task_return_error (task, error);
       return;
@@ -1627,7 +1657,8 @@ handshake_thread (GTask        *task,
   if (!is_client && gnutls->priv->ever_handshaked &&
       !gnutls->priv->implicit_handshake)
     {
-      BEGIN_GNUTLS_IO (gnutls, G_IO_IN | G_IO_OUT, TRUE, cancellable);
+      BEGIN_GNUTLS_IO (gnutls, G_IO_IN | G_IO_OUT, -1  /* blocking */,
+                       cancellable);
       ret = gnutls_rehandshake (gnutls->priv->session);
       END_GNUTLS_IO (gnutls, G_IO_IN | G_IO_OUT, ret,
                     _("Error performing TLS handshake: %s"), &error);
@@ -1646,7 +1677,7 @@ handshake_thread (GTask        *task,
 
   g_tls_connection_gnutls_set_handshake_priority (gnutls);
 
-  BEGIN_GNUTLS_IO (gnutls, G_IO_IN | G_IO_OUT, TRUE, cancellable);
+  BEGIN_GNUTLS_IO (gnutls, G_IO_IN | G_IO_OUT, -1  /* blocking */, cancellable);
   ret = gnutls_handshake (gnutls->priv->session);
   if (ret == GNUTLS_E_GOT_APPLICATION_DATA)
     {
@@ -1915,7 +1946,7 @@ g_tls_connection_gnutls_dtls_handshake_finish (GDtlsConnection       *conn,
 
 static gboolean
 do_implicit_handshake (GTlsConnectionGnutls  *gnutls,
-                      gboolean               blocking,
+                      gint64                 timeout,
                       GCancellable          *cancellable,
                       GError               **error)
 {
@@ -1927,7 +1958,8 @@ do_implicit_handshake (GTlsConnectionGnutls  *gnutls,
 
   begin_handshake (gnutls);
 
-  if (blocking)
+  /* FIXME: Support (timeout > 0). */
+  if (timeout != 0)
     {
       GError *my_error = NULL;
       gboolean success;
@@ -1962,7 +1994,7 @@ gssize
 g_tls_connection_gnutls_read (GTlsConnectionGnutls  *gnutls,
                              void                  *buffer,
                              gsize                  count,
-                             gboolean               blocking,
+                             gint64                 timeout,
                              GCancellable          *cancellable,
                              GError               **error)
 {
@@ -1981,10 +2013,10 @@ g_tls_connection_gnutls_read (GTlsConnectionGnutls  *gnutls,
 
  again:
   if (!claim_op (gnutls, G_TLS_CONNECTION_GNUTLS_OP_READ,
-                blocking, cancellable, error))
+                timeout, cancellable, error))
     return -1;
 
-  BEGIN_GNUTLS_IO (gnutls, G_IO_IN, blocking, cancellable);
+  BEGIN_GNUTLS_IO (gnutls, G_IO_IN, timeout, cancellable);
   ret = gnutls_record_recv (gnutls->priv->session, buffer, count);
   END_GNUTLS_IO (gnutls, G_IO_IN, ret, _("Error reading data from TLS socket: %s"), error);
 
@@ -2068,10 +2100,10 @@ g_tls_connection_gnutls_read_message (GTlsConnectionGnutls  *gnutls,
 
  again:
   if (!claim_op (gnutls, G_TLS_CONNECTION_GNUTLS_OP_READ,
-                timeout != 0, cancellable, error))
+                timeout, cancellable, error))
     return -1;
 
-  BEGIN_GNUTLS_IO (gnutls, G_IO_IN, timeout != 0, cancellable);
+  BEGIN_GNUTLS_IO (gnutls, G_IO_IN, timeout, cancellable);
 
   /* Receive the entire datagram (zero-copy). */
   ret = gnutls_record_recv_packet (gnutls->priv->session, &packet);
@@ -2180,7 +2212,7 @@ gssize
 g_tls_connection_gnutls_write (GTlsConnectionGnutls  *gnutls,
                               const void            *buffer,
                               gsize                  count,
-                              gboolean               blocking,
+                              gint64                 timeout,
                               GCancellable          *cancellable,
                               GError               **error)
 {
@@ -2188,7 +2220,7 @@ g_tls_connection_gnutls_write (GTlsConnectionGnutls  *gnutls,
 
  again:
   if (!claim_op (gnutls, G_TLS_CONNECTION_GNUTLS_OP_WRITE,
-                blocking, cancellable, error))
+                timeout, cancellable, error))
     return -1;
 
   if (gnutls_dtls_get_data_mtu (gnutls->priv->session) < count)
@@ -2202,7 +2234,7 @@ g_tls_connection_gnutls_write (GTlsConnectionGnutls  *gnutls,
       goto done;
     }
 
-  BEGIN_GNUTLS_IO (gnutls, G_IO_OUT, blocking, cancellable);
+  BEGIN_GNUTLS_IO (gnutls, G_IO_OUT, timeout, cancellable);
   ret = gnutls_record_send (gnutls->priv->session, buffer, count);
   END_GNUTLS_IO (gnutls, G_IO_OUT, ret, _("Error writing data to TLS socket: %s"), error);
 
@@ -2231,7 +2263,7 @@ g_tls_connection_gnutls_write_message (GTlsConnectionGnutls  *gnutls,
 
  again:
   if (!claim_op (gnutls, G_TLS_CONNECTION_GNUTLS_OP_WRITE,
-                 timeout != 0, cancellable, error))
+                 timeout, cancellable, error))
     return -1;
 
   /* Calculate the total message size and check it’s not too big. */
@@ -2265,7 +2297,7 @@ g_tls_connection_gnutls_write_message (GTlsConnectionGnutls  *gnutls,
         }
     }
 
-  BEGIN_GNUTLS_IO (gnutls, G_IO_OUT, timeout != 0, cancellable);
+  BEGIN_GNUTLS_IO (gnutls, G_IO_OUT, timeout, cancellable);
   ret = gnutls_record_uncork (gnutls->priv->session, 0  /* flags */);
   END_GNUTLS_IO (gnutls, G_IO_OUT, ret, _("Error writing data to TLS socket: %s"), error);
 
@@ -2379,8 +2411,6 @@ g_tls_connection_gnutls_close_internal (GIOStream     *stream,
    * this class and how the underlying stream is closed.
    */
 
-  /* FIXME: This does not properly support the @timeout parameter. */
-
   g_return_val_if_fail (direction != G_TLS_DIRECTION_NONE, FALSE);
 
   if (direction == G_TLS_DIRECTION_BOTH)
@@ -2390,13 +2420,13 @@ g_tls_connection_gnutls_close_internal (GIOStream     *stream,
   else
     op = G_TLS_CONNECTION_GNUTLS_OP_CLOSE_WRITE;
 
-  if (!claim_op (gnutls, op, TRUE, cancellable, error))
+  if (!claim_op (gnutls, op, timeout, cancellable, error))
     return FALSE;
 
   if (gnutls->priv->ever_handshaked && !gnutls->priv->write_closed &&
       direction & G_TLS_DIRECTION_WRITE)
     {
-      BEGIN_GNUTLS_IO (gnutls, G_IO_IN | G_IO_OUT, TRUE, cancellable);
+      BEGIN_GNUTLS_IO (gnutls, G_IO_IN | G_IO_OUT, timeout, cancellable);
       ret = gnutls_bye (gnutls->priv->session, GNUTLS_SHUT_WR);
       END_GNUTLS_IO (gnutls, G_IO_IN | G_IO_OUT, ret,
                     _("Error performing TLS close: %s"), error);
diff --git a/tls/gnutls/gtlsconnection-gnutls.h b/tls/gnutls/gtlsconnection-gnutls.h
index b4a8480..1559b58 100644
--- a/tls/gnutls/gtlsconnection-gnutls.h
+++ b/tls/gnutls/gtlsconnection-gnutls.h
@@ -63,13 +63,13 @@ gboolean g_tls_connection_gnutls_request_certificate (GTlsConnectionGnutls  *gnu
 gssize   g_tls_connection_gnutls_read          (GTlsConnectionGnutls  *gnutls,
                                                void                  *buffer,
                                                gsize                  size,
-                                               gboolean               blocking,
+                                               gint64                 timeout,
                                                GCancellable          *cancellable,
                                                GError               **error);
 gssize   g_tls_connection_gnutls_write         (GTlsConnectionGnutls  *gnutls,
                                                const void            *buffer,
                                                gsize                  size,
-                                               gboolean               blocking,
+                                               gint64                 timeout,
                                                GCancellable          *cancellable,
                                                GError               **error);
 
diff --git a/tls/gnutls/gtlsinputstream-gnutls.c b/tls/gnutls/gtlsinputstream-gnutls.c
index 4b1685d..417f4af 100644
--- a/tls/gnutls/gtlsinputstream-gnutls.c
+++ b/tls/gnutls/gtlsinputstream-gnutls.c
@@ -69,7 +69,7 @@ g_tls_input_stream_gnutls_read (GInputStream  *stream,
   g_return_val_if_fail (conn != NULL, -1);
 
   ret = g_tls_connection_gnutls_read (conn,
-                                      buffer, count, TRUE,
+                                      buffer, count, -1  /* blocking */,
                                       cancellable, error);
   g_object_unref (conn);
   return ret;
@@ -120,7 +120,8 @@ g_tls_input_stream_gnutls_pollable_read_nonblocking (GPollableInputStream  *poll
   conn = g_weak_ref_get (&tls_stream->priv->weak_conn);
   g_return_val_if_fail (conn != NULL, -1);
 
-  ret = g_tls_connection_gnutls_read (conn, buffer, size, FALSE, NULL, error);
+  ret = g_tls_connection_gnutls_read (conn, buffer, size,
+                                      0  /* non-blocking */, NULL, error);
 
   g_object_unref (conn);
   return ret;
diff --git a/tls/gnutls/gtlsoutputstream-gnutls.c b/tls/gnutls/gtlsoutputstream-gnutls.c
index 4c7853b..b9bd638 100644
--- a/tls/gnutls/gtlsoutputstream-gnutls.c
+++ b/tls/gnutls/gtlsoutputstream-gnutls.c
@@ -68,7 +68,7 @@ g_tls_output_stream_gnutls_write (GOutputStream  *stream,
   conn = g_weak_ref_get (&tls_stream->priv->weak_conn);
   g_return_val_if_fail (conn != NULL, -1);
 
-  ret = g_tls_connection_gnutls_write (conn, buffer, count, TRUE,
+  ret = g_tls_connection_gnutls_write (conn, buffer, count, -1  /* blocking */,
                                        cancellable, error);
   g_object_unref (conn);
   return ret;
@@ -122,7 +122,8 @@ g_tls_output_stream_gnutls_pollable_write_nonblocking (GPollableOutputStream  *p
   conn = g_weak_ref_get (&tls_stream->priv->weak_conn);
   g_return_val_if_fail (conn != NULL, -1);
 
-  ret = g_tls_connection_gnutls_write (conn, buffer, size, FALSE, NULL, error);
+  ret = g_tls_connection_gnutls_write (conn, buffer, size,
+                                       0  /* non-blocking */, NULL, error);
 
   g_object_unref (conn);
   return ret;


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]