[glib-networking/mcatanzaro/tls-thread: 7/14] wip



commit f962eb168fe9a2334c57931812c05461e138f72b
Author: Michael Catanzaro <mcatanzaro gnome org>
Date:   Wed Dec 4 10:29:55 2019 -0600

    wip

 tls/base/gtlsconnection-base.c             |   8 +-
 tls/base/gtlsconnection-base.h             |  28 ---
 tls/base/gtlsoperationsthread-base.c       |  91 ++++++--
 tls/base/gtlsoperationsthread-base.h       |  78 +++++--
 tls/gnutls/gtlsconnection-gnutls.c         | 175 +-------------
 tls/gnutls/gtlsconnection-gnutls.h         |   1 +
 tls/gnutls/gtlsoperationsthread-gnutls.c   | 358 ++++++++++++++++++++++++++++-
 tls/gnutls/gtlsoperationsthread-gnutls.h   |   3 +
 tls/openssl/gtlsbio.c                      |  52 -----
 tls/openssl/gtlsconnection-openssl.c       | 126 +---------
 tls/openssl/gtlsconnection-openssl.h       |   3 +
 tls/openssl/gtlsoperationsthread-openssl.c | 231 +++++++++++++++++++
 tls/openssl/gtlsoperationsthread-openssl.h |   3 +
 13 files changed, 748 insertions(+), 409 deletions(-)
---
diff --git a/tls/base/gtlsconnection-base.c b/tls/base/gtlsconnection-base.c
index ae0a37f..84cfe0a 100644
--- a/tls/base/gtlsconnection-base.c
+++ b/tls/base/gtlsconnection-base.c
@@ -2042,9 +2042,7 @@ g_tls_connection_base_read_message (GTlsConnectionBase  *tls,
       }
     else
       {
-        g_assert (G_TLS_CONNECTION_BASE_GET_CLASS (tls)->read_message_fn);
-        status = G_TLS_CONNECTION_BASE_GET_CLASS (tls)->
-          read_message_fn (tls, vectors, num_vectors, timeout, &nread, cancellable, error);
+        status = g_tls_operations_thread_base_read_message (priv->thread, vectors, num_vectors, timeout, 
&nread, cancellable, error);
       }
 
     yield_op (tls, G_TLS_CONNECTION_BASE_OP_READ, status);
@@ -2193,9 +2191,7 @@ g_tls_connection_base_write_message (GTlsConnectionBase  *tls,
                    timeout, cancellable, error))
       return -1;
 
-    g_assert (G_TLS_CONNECTION_BASE_GET_CLASS (tls)->read_message_fn);
-    status = G_TLS_CONNECTION_BASE_GET_CLASS (tls)->
-      write_message_fn (tls, vectors, num_vectors, timeout, &nwrote, cancellable, error);
+    status = g_tls_operations_thread_base_write_message (priv->thread, vectors, num_vectors, timeout, 
&nwrote, cancellable, error);
 
     yield_op (tls, G_TLS_CONNECTION_BASE_OP_WRITE, status);
   } while (status == G_TLS_CONNECTION_BASE_REHANDSHAKE);
diff --git a/tls/base/gtlsconnection-base.h b/tls/base/gtlsconnection-base.h
index 791c06b..a91ea40 100644
--- a/tls/base/gtlsconnection-base.h
+++ b/tls/base/gtlsconnection-base.h
@@ -96,34 +96,6 @@ struct _GTlsConnectionBaseClass
                                                              GError              **error);
 
   /* FIXME: must remove timeout parameters from all vfuncs, including handshake vfuncs */
-  GTlsConnectionBaseStatus    (*read_fn)                    (GTlsConnectionBase   *tls,
-                                                             void                 *buffer,
-                                                             gsize                 size,
-                                                             gssize               *nread,
-                                                             GCancellable         *cancellable,
-                                                             GError              **error);
-  GTlsConnectionBaseStatus    (*read_message_fn)            (GTlsConnectionBase   *tls,
-                                                             GInputVector         *vectors,
-                                                             guint                 num_vectors,
-                                                             gint64                timeout,
-                                                             gssize               *nread,
-                                                             GCancellable         *cancellable,
-                                                             GError              **error);
-
-  GTlsConnectionBaseStatus    (*write_fn)                   (GTlsConnectionBase   *tls,
-                                                             const void           *buffer,
-                                                             gsize                 size,
-                                                             gssize               *nwrote,
-                                                             GCancellable         *cancellable,
-                                                             GError              **error);
-  GTlsConnectionBaseStatus    (*write_message_fn)           (GTlsConnectionBase   *tls,
-                                                             GOutputVector        *vectors,
-                                                             guint                 num_vectors,
-                                                             gint64                timeout,
-                                                             gssize               *nwrote,
-                                                             GCancellable         *cancellable,
-                                                             GError              **error);
-
   GTlsConnectionBaseStatus    (*close_fn)                   (GTlsConnectionBase   *tls,
                                                              gint64                timeout,
                                                              GCancellable         *cancellable,
diff --git a/tls/base/gtlsoperationsthread-base.c b/tls/base/gtlsoperationsthread-base.c
index 1f77d3e..ce3e120 100644
--- a/tls/base/gtlsoperationsthread-base.c
+++ b/tls/base/gtlsoperationsthread-base.c
@@ -28,7 +28,6 @@
 #include "gtlsoperationsthread-base.h"
 
 #include <glib/gi18n-lib.h>
-#include <glib/gstdio.h>
 
 /* The purpose of this class is to ensure the underlying TLS library is only
  * ever used on a single thread. There are multiple benefits of this:
@@ -70,20 +69,23 @@
  * write operations on one thread without either one blocking the other.
  */
 typedef struct {
+  /* FIXME: remove to prevent misuse? */
   GTlsConnectionBase *connection; /* unowned */
 
   GThread *op_thread;
   GMainContext *op_thread_context;
 
-  /* When pushing ops onto the queue, call g_main_context_wakeup() on
-   * op_thread_context to ensure the op is noticed.
+  /* Important: when pushing ops onto the queue, call g_main_context_wakeup()
+   * on op_thread_context to ensure the op is noticed.
    */
   GAsyncQueue *queue;
 } GTlsOperationsThreadBasePrivate;
 
 typedef enum {
   G_TLS_THREAD_OP_READ,
+  G_TLS_THREAD_OP_READ_MESSAGE,
   G_TLS_THREAD_OP_WRITE,
+  G_TLS_THREAD_OP_WRITE_MESSAGE,
   G_TLS_THREAD_OP_SHUTDOWN
 } GTlsThreadOperationType;
 
@@ -91,6 +93,7 @@ typedef struct {
   GTlsThreadOperationType type;
   GIOCondition io_condition;
 
+  GTlsOperationsThreadBase *thread;
   GTlsConnectionBase *connection; /* FIXME: threadsafety nightmare, not OK */
 
   /* Input */
@@ -127,17 +130,19 @@ static GParamSpec *obj_properties[LAST_PROP];
 G_DEFINE_TYPE_WITH_PRIVATE (GTlsOperationsThreadBase, g_tls_operations_thread_base, G_TYPE_OBJECT)
 
 static GTlsThreadOperation *
-g_tls_thread_operation_new (GTlsThreadOperationType  type,
-                            GTlsConnectionBase      *connection,
-                            void                    *data,
-                            gsize                    size,
-                            gint64                   timeout,
-                            GCancellable            *cancellable)
+g_tls_thread_operation_new (GTlsThreadOperationType   type,
+                            GTlsOperationsThreadBase *thread,
+                            GTlsConnectionBase       *connection,
+                            void                     *data,
+                            gsize                     size,
+                            gint64                    timeout,
+                            GCancellable             *cancellable)
 {
   GTlsThreadOperation *op;
 
   op = g_new0 (GTlsThreadOperation, 1);
   op->type = type;
+  op->thread = thread; /* FIXME: use a weak ref? */
   op->connection = g_object_ref (connection);
   op->data = data;
   op->size = size;
@@ -150,9 +155,13 @@ g_tls_thread_operation_new (GTlsThreadOperationType  type,
   switch (type)
     {
     case G_TLS_THREAD_OP_READ:
+      /* fallthrough */
+    case G_TLS_THREAD_OP_READ_MESSAGE:
       op->io_condition = G_IO_IN;
       break;
     case G_TLS_THREAD_OP_WRITE:
+      /* fallthough */
+    case G_TLS_THREAD_OP_WRITE_MESSAGE:
       op->io_condition = G_IO_OUT;
       break;
     /* FIXME: more pls */
@@ -198,6 +207,14 @@ wait_for_op_completion (GTlsThreadOperation *op)
   g_mutex_unlock (&op->finished_mutex);
 }
 
+GTlsConnectionBase *
+g_tls_operations_thread_base_get_connection (GTlsOperationsThreadBase *self)
+{
+  GTlsOperationsThreadBasePrivate *priv = g_tls_operations_thread_base_get_instance_private (self);
+
+  return priv->connection;
+}
+
 GTlsConnectionBaseStatus
 g_tls_operations_thread_base_read (GTlsOperationsThreadBase  *self,
                                    void                      *buffer,
@@ -212,6 +229,7 @@ g_tls_operations_thread_base_read (GTlsOperationsThreadBase  *self,
   GTlsConnectionBaseStatus result;
 
   op = g_tls_thread_operation_new (G_TLS_THREAD_OP_READ,
+                                   self,
                                    priv->connection,
                                    buffer, size, timeout,
                                    cancellable);
@@ -235,6 +253,19 @@ g_tls_operations_thread_base_read (GTlsOperationsThreadBase  *self,
   return result;
 }
 
+GTlsConnectionBaseStatus
+g_tls_operations_thread_base_read_message (GTlsOperationsThreadBase  *self,
+                                           GInputVector              *vectors,
+                                           guint                      num_vectors,
+                                           gint64                     timeout,
+                                           gssize                    *nread,
+                                           GCancellable              *cancellable,
+                                           GError                   **error)
+{
+  /* FIXME: not enough room in GTlsThreadOperation to store the parameters */
+  g_assert_not_reached ();
+}
+
 GTlsConnectionBaseStatus
 g_tls_operations_thread_base_write (GTlsOperationsThreadBase  *self,
                                     const void                *buffer,
@@ -249,6 +280,7 @@ g_tls_operations_thread_base_write (GTlsOperationsThreadBase  *self,
   GTlsConnectionBaseStatus result;
 
   op = g_tls_thread_operation_new (G_TLS_THREAD_OP_WRITE,
+                                   self,
                                    priv->connection,
                                    (void *)buffer, size, timeout,
                                    cancellable);
@@ -272,6 +304,19 @@ g_tls_operations_thread_base_write (GTlsOperationsThreadBase  *self,
   return result;
 }
 
+GTlsConnectionBaseStatus
+g_tls_operations_thread_base_write_message (GTlsOperationsThreadBase  *self,
+                                            GOutputVector             *vectors,
+                                            guint                      num_vectors,
+                                            gint64                     timeout,
+                                            gssize                    *nwrote,
+                                            GCancellable              *cancellable,
+                                            GError                   **error)
+{
+  /* FIXME: not enough room in GTlsThreadOperation to store the parameters */
+  g_assert_not_reached ();
+}
+
 typedef struct {
   GSource source;
 
@@ -580,18 +625,26 @@ process_op (GAsyncQueue         *queue,
     {
     /* FIXME: handle all op types, including handshake and directional closes */
     case G_TLS_THREAD_OP_READ:
-      op->result = G_TLS_CONNECTION_BASE_GET_CLASS (op->connection)->read_fn (op->connection,
-                                                                              op->data, op->size,
-                                                                              &op->count,
-                                                                              op->cancellable,
-                                                                              &op->error);
+      op->result = G_TLS_OPERATIONS_THREAD_BASE_GET_CLASS (op->thread)->read_fn (op->thread,
+                                                                                 op->data, op->size,
+                                                                                 &op->count,
+                                                                                 op->cancellable,
+                                                                                 &op->error);
+      break;
+    case G_TLS_THREAD_OP_READ_MESSAGE:
+      g_assert (G_TLS_OPERATIONS_THREAD_BASE_GET_CLASS (op->thread)->read_message_fn);
+      g_assert_not_reached (); /* FIXME */
       break;
     case G_TLS_THREAD_OP_WRITE:
-      op->result = G_TLS_CONNECTION_BASE_GET_CLASS (op->connection)->write_fn (op->connection,
-                                                                               op->data, op->size,
-                                                                               &op->count,
-                                                                               op->cancellable,
-                                                                               &op->error);
+      op->result = G_TLS_OPERATIONS_THREAD_BASE_GET_CLASS (op->thread)->write_fn (op->thread,
+                                                                                  op->data, op->size,
+                                                                                  &op->count,
+                                                                                  op->cancellable,
+                                                                                  &op->error);
+      break;
+    case G_TLS_THREAD_OP_WRITE_MESSAGE:
+      g_assert (G_TLS_OPERATIONS_THREAD_BASE_GET_CLASS (op->thread)->write_message_fn);
+      g_assert_not_reached (); /* FIXME */
       break;
     case G_TLS_THREAD_OP_SHUTDOWN:
       g_assert_not_reached ();
diff --git a/tls/base/gtlsoperationsthread-base.h b/tls/base/gtlsoperationsthread-base.h
index f567b14..6962c85 100644
--- a/tls/base/gtlsoperationsthread-base.h
+++ b/tls/base/gtlsoperationsthread-base.h
@@ -37,22 +37,70 @@ G_DECLARE_DERIVABLE_TYPE (GTlsOperationsThreadBase, g_tls_operations_thread_base
 struct _GTlsOperationsThreadBaseClass
 {
   GObjectClass parent_class;
+
+  /* FIXME: must remove timeout parameters from all vfuncs, including handshake vfuncs */
+  GTlsConnectionBaseStatus    (*read_fn)                    (GTlsOperationsThreadBase  *self,
+                                                             void                      *buffer,
+                                                             gsize                      size,
+                                                             gssize                    *nread,
+                                                             GCancellable              *cancellable,
+                                                             GError                   **error);
+  GTlsConnectionBaseStatus    (*read_message_fn)            (GTlsOperationsThreadBase  *self,
+                                                             GInputVector              *vectors,
+                                                             guint                      num_vectors,
+                                                             gint64                     timeout,
+                                                             gssize                    *nread,
+                                                             GCancellable              *cancellable,
+                                                             GError                   **error);
+
+  GTlsConnectionBaseStatus    (*write_fn)                   (GTlsOperationsThreadBase  *self,
+                                                             const void                *buffer,
+                                                             gsize                      size,
+                                                             gssize                    *nwrote,
+                                                             GCancellable              *cancellable,
+                                                             GError                   **error);
+  GTlsConnectionBaseStatus    (*write_message_fn)           (GTlsOperationsThreadBase  *self,
+                                                             GOutputVector             *vectors,
+                                                             guint                      num_vectors,
+                                                             gint64                     timeout,
+                                                             gssize                    *nwrote,
+                                                             GCancellable              *cancellable,
+                                                             GError                   **error);
 };
 
-GTlsConnectionBaseStatus  g_tls_operations_thread_base_read  (GTlsOperationsThreadBase  *self,
-                                                              void                      *buffer,
-                                                              gsize                      size,
-                                                              gint64                     timeout,
-                                                              gssize                    *nread,
-                                                              GCancellable              *cancellable,
-                                                              GError                   **error);
-
-GTlsConnectionBaseStatus  g_tls_operations_thread_base_write (GTlsOperationsThreadBase  *self,
-                                                              const void                *buffer,
-                                                              gsize                      size,
-                                                              gint64                     timeout,
-                                                              gssize                    *nwrote,
-                                                              GCancellable              *cancellable,
-                                                              GError                   **error);
+/* FIXME: remove? */
+GTlsConnectionBase       *g_tls_operations_thread_base_get_connection (GTlsOperationsThreadBase *self);
+
+GTlsConnectionBaseStatus  g_tls_operations_thread_base_read           (GTlsOperationsThreadBase  *self,
+                                                                       void                      *buffer,
+                                                                       gsize                      size,
+                                                                       gint64                     timeout,
+                                                                       gssize                    *nread,
+                                                                       GCancellable              
*cancellable,
+                                                                       GError                   **error);
+
+GTlsConnectionBaseStatus  g_tls_operations_thread_base_read_message   (GTlsOperationsThreadBase  *self,
+                                                                       GInputVector              *vectors,
+                                                                       guint                      
num_vectors,
+                                                                       gint64                     timeout,
+                                                                       gssize                    *nread,
+                                                                       GCancellable              
*cancellable,
+                                                                       GError                   **error);
+
+GTlsConnectionBaseStatus  g_tls_operations_thread_base_write          (GTlsOperationsThreadBase  *self,
+                                                                       const void                *buffer,
+                                                                       gsize                      size,
+                                                                       gint64                     timeout,
+                                                                       gssize                    *nwrote,
+                                                                       GCancellable              
*cancellable,
+                                                                       GError                   **error);
+
+GTlsConnectionBaseStatus  g_tls_operations_thread_base_write_message  (GTlsOperationsThreadBase  *self,
+                                                                       GOutputVector             *vectors,
+                                                                       guint                      
num_vectors,
+                                                                       gint64                     timeout,
+                                                                       gssize                    *nwrote,
+                                                                       GCancellable              
*cancellable,
+                                                                       GError                   **error);
 
 G_END_DECLS
diff --git a/tls/gnutls/gtlsconnection-gnutls.c b/tls/gnutls/gtlsconnection-gnutls.c
index 0efb4b7..4852aec 100644
--- a/tls/gnutls/gtlsconnection-gnutls.c
+++ b/tls/gnutls/gtlsconnection-gnutls.c
@@ -74,7 +74,7 @@ static gnutls_priority_t priority;
 typedef struct
 {
   gnutls_certificate_credentials_t creds;
-  gnutls_session_t session;
+  gnutls_session_t session; /* FIXME: should be used only by GTlsOperationsThreadGnutls */
   gchar *interaction_id;
   GCancellable *cancellable;
 } GTlsConnectionGnutlsPrivate;
@@ -313,6 +313,7 @@ g_tls_connection_gnutls_handshake_thread_get_certificate (GTlsConnectionGnutls
     }
 }
 
+// FIXME: remove
 static GTlsConnectionBaseStatus
 end_gnutls_io (GTlsConnectionGnutls  *gnutls,
                GIOCondition           direction,
@@ -466,6 +467,8 @@ end_gnutls_io (GTlsConnectionGnutls  *gnutls,
   return G_TLS_CONNECTION_BASE_ERROR;
 }
 
+// FIXME: remove
+
 #define BEGIN_GNUTLS_IO(gnutls, direction, timeout, cancellable)        \
   g_tls_connection_base_push_io (G_TLS_CONNECTION_BASE (gnutls),        \
                                  direction, timeout, cancellable);      \
@@ -923,172 +926,6 @@ g_tls_connection_gnutls_is_session_resumed (GTlsConnectionBase *tls)
   return gnutls_session_is_resumed (priv->session);
 }
 
-static GTlsConnectionBaseStatus
-g_tls_connection_gnutls_read (GTlsConnectionBase  *tls,
-                              void                *buffer,
-                              gsize                size,
-                              gssize              *nread,
-                              GCancellable        *cancellable,
-                              GError             **error)
-{
-  GTlsConnectionGnutls *gnutls = G_TLS_CONNECTION_GNUTLS (tls);
-  GTlsConnectionGnutlsPrivate *priv = g_tls_connection_gnutls_get_instance_private (gnutls);
-  GTlsConnectionBaseStatus status;
-  gssize ret;
-
-  BEGIN_GNUTLS_IO (gnutls, G_IO_IN, 0, cancellable);
-  ret = gnutls_record_recv (priv->session, buffer, size);
-  END_GNUTLS_IO (gnutls, G_IO_IN, ret, status, _("Error reading data from TLS socket"), error);
-
-  *nread = MAX (ret, 0);
-  return status;
-}
-
-static gsize
-input_vectors_from_gnutls_datum_t (GInputVector         *vectors,
-                                   guint                 num_vectors,
-                                   const gnutls_datum_t *datum)
-{
-  guint i;
-  gsize total = 0;
-
-  /* Copy into the receive vectors. */
-  for (i = 0; i < num_vectors && total < datum->size; i++)
-    {
-      gsize count;
-      GInputVector *vec = &vectors[i];
-
-      count = MIN (vec->size, datum->size - total);
-
-      memcpy (vec->buffer, datum->data + total, count);
-      total += count;
-    }
-
-  g_assert (total <= datum->size);
-
-  return total;
-}
-
-static GTlsConnectionBaseStatus
-g_tls_connection_gnutls_read_message (GTlsConnectionBase  *tls,
-                                      GInputVector        *vectors,
-                                      guint                num_vectors,
-                                      gint64               timeout,
-                                      gssize              *nread,
-                                      GCancellable        *cancellable,
-                                      GError             **error)
-{
-  GTlsConnectionGnutls *gnutls = G_TLS_CONNECTION_GNUTLS (tls);
-  GTlsConnectionGnutlsPrivate *priv = g_tls_connection_gnutls_get_instance_private (gnutls);
-  GTlsConnectionBaseStatus status;
-  gssize ret;
-  gnutls_packet_t packet = { 0, };
-
-  BEGIN_GNUTLS_IO (gnutls, G_IO_IN, timeout, cancellable);
-
-  /* Receive the entire datagram (zero-copy). */
-  ret = gnutls_record_recv_packet (priv->session, &packet);
-
-  if (ret > 0)
-    {
-      gnutls_datum_t data = { 0, };
-
-      gnutls_packet_get (packet, &data, NULL);
-      ret = input_vectors_from_gnutls_datum_t (vectors, num_vectors, &data);
-      gnutls_packet_deinit (packet);
-    }
-
-  END_GNUTLS_IO (gnutls, G_IO_IN, ret, status, _("Error reading data from TLS socket"), error);
-
-  *nread = MAX (ret, 0);
-  return status;
-}
-
-static GTlsConnectionBaseStatus
-g_tls_connection_gnutls_write (GTlsConnectionBase  *tls,
-                               const void          *buffer,
-                               gsize                size,
-                               gssize              *nwrote,
-                               GCancellable        *cancellable,
-                               GError             **error)
-{
-  GTlsConnectionGnutls *gnutls = G_TLS_CONNECTION_GNUTLS (tls);
-  GTlsConnectionGnutlsPrivate *priv = g_tls_connection_gnutls_get_instance_private (gnutls);
-  GTlsConnectionBaseStatus status;
-  gssize ret;
-
-  BEGIN_GNUTLS_IO (gnutls, G_IO_OUT, 0, cancellable);
-  ret = gnutls_record_send (priv->session, buffer, size);
-  END_GNUTLS_IO (gnutls, G_IO_OUT, ret, status, _("Error writing data to TLS socket"), error);
-
-  *nwrote = MAX (ret, 0);
-  return status;
-}
-
-static GTlsConnectionBaseStatus
-g_tls_connection_gnutls_write_message (GTlsConnectionBase  *tls,
-                                       GOutputVector       *vectors,
-                                       guint                num_vectors,
-                                       gint64               timeout,
-                                       gssize              *nwrote,
-                                       GCancellable        *cancellable,
-                                       GError             **error)
-{
-  GTlsConnectionGnutls *gnutls = G_TLS_CONNECTION_GNUTLS (tls);
-  GTlsConnectionGnutlsPrivate *priv = g_tls_connection_gnutls_get_instance_private (gnutls);
-  GTlsConnectionBaseStatus status;
-  gssize ret;
-  guint i;
-  gsize total_message_size;
-
-  /* Calculate the total message size and check it’s not too big. */
-  for (i = 0, total_message_size = 0; i < num_vectors; i++)
-    total_message_size += vectors[i].size;
-
-  if (g_tls_connection_base_is_dtls (tls) &&
-      gnutls_dtls_get_data_mtu (priv->session) < total_message_size)
-    {
-      char *message;
-      guint mtu = gnutls_dtls_get_data_mtu (priv->session);
-
-      ret = GNUTLS_E_LARGE_PACKET;
-      message = g_strdup_printf("%s %s",
-                                ngettext ("Message of size %lu byte is too large for DTLS connection",
-                                          "Message of size %lu bytes is too large for DTLS connection", 
total_message_size),
-                                ngettext ("(maximum is %u byte)", "(maximum is %u bytes)", mtu));
-      g_set_error (error, G_IO_ERROR, G_IO_ERROR_MESSAGE_TOO_LARGE,
-                   message,
-                   total_message_size,
-                   mtu);
-      g_free (message);
-
-      return G_TLS_CONNECTION_BASE_ERROR;
-    }
-
-  /* Queue up the data from all the vectors. */
-  gnutls_record_cork (priv->session);
-
-  for (i = 0; i < num_vectors; i++)
-    {
-      ret = gnutls_record_send (priv->session,
-                                vectors[i].buffer, vectors[i].size);
-
-      if (ret < 0 || ret < vectors[i].size)
-        {
-          /* Uncork to restore state, then bail. The peer will receive a
-           * truncated datagram. */
-          break;
-        }
-    }
-
-  BEGIN_GNUTLS_IO (gnutls, G_IO_OUT, timeout, cancellable);
-  ret = gnutls_record_uncork (priv->session, 0  /* flags */);
-  END_GNUTLS_IO (gnutls, G_IO_OUT, ret, status, _("Error writing data to TLS socket"), error);
-
-  *nwrote = MAX (ret, 0);
-  return status;
-}
-
 static GTlsConnectionBaseStatus
 g_tls_connection_gnutls_close (GTlsConnectionBase  *tls,
                                gint64               timeout,
@@ -1146,10 +983,6 @@ g_tls_connection_gnutls_class_init (GTlsConnectionGnutlsClass *klass)
   base_class->retrieve_peer_certificate                  = g_tls_connection_gnutls_retrieve_peer_certificate;
   base_class->complete_handshake                         = g_tls_connection_gnutls_complete_handshake;
   base_class->is_session_resumed                         = g_tls_connection_gnutls_is_session_resumed;
-  base_class->read_fn                                    = g_tls_connection_gnutls_read;
-  base_class->read_message_fn                            = g_tls_connection_gnutls_read_message;
-  base_class->write_fn                                   = g_tls_connection_gnutls_write;
-  base_class->write_message_fn                           = g_tls_connection_gnutls_write_message;
   base_class->close_fn                                   = g_tls_connection_gnutls_close;
 
   initialize_gnutls_priority ();
diff --git a/tls/gnutls/gtlsconnection-gnutls.h b/tls/gnutls/gtlsconnection-gnutls.h
index 55ae5ee..3e9fa9b 100644
--- a/tls/gnutls/gtlsconnection-gnutls.h
+++ b/tls/gnutls/gtlsconnection-gnutls.h
@@ -43,6 +43,7 @@ struct _GTlsConnectionGnutlsClass
 
 gnutls_certificate_credentials_t g_tls_connection_gnutls_get_credentials (GTlsConnectionGnutls *connection);
 
+/* FIXME: remove to ensure threadsafety */
 gnutls_session_t                 g_tls_connection_gnutls_get_session     (GTlsConnectionGnutls *connection);
 
 void     g_tls_connection_gnutls_handshake_thread_get_certificate     (GTlsConnectionGnutls  *gnutls,
diff --git a/tls/gnutls/gtlsoperationsthread-gnutls.c b/tls/gnutls/gtlsoperationsthread-gnutls.c
index d48a356..81b118b 100644
--- a/tls/gnutls/gtlsoperationsthread-gnutls.c
+++ b/tls/gnutls/gtlsoperationsthread-gnutls.c
@@ -2,6 +2,8 @@
 /*
  * GIO - GLib Input, Output and Streaming Library
  *
+ * Copyright 2009 Red Hat, Inc
+ * Copyright 2015, 2016 Collabora, Ltd.
  * Copyright 2019 Igalia S.L.
  * Copyright 2019 Metrological Group B.V.
  *
@@ -25,15 +27,360 @@
 
 #include "config.h"
 
-#include "gtlsconnection-gnutls.h"
+#include <glib/gi18n-lib.h>
+#include <gnutls/dtls.h>
+
 #include "gtlsoperationsthread-gnutls.h"
+#include "gtlsconnection-gnutls.h"
 
 struct _GTlsOperationsThreadGnutls {
   GTlsOperationsThreadBase parent_instance;
+
+  gnutls_session_t         session;
 };
 
 G_DEFINE_TYPE (GTlsOperationsThreadGnutls, g_tls_operations_thread_gnutls, G_TYPE_TLS_OPERATIONS_THREAD_BASE)
 
+static GTlsConnectionBaseStatus
+end_gnutls_io (GTlsOperationsThreadGnutls  *self,
+               GIOCondition                 direction,
+               int                          ret,
+               GError                     **error,
+               const char                  *err_prefix)
+{
+  GTlsConnectionBase *tls;
+  GTlsConnectionBaseStatus status;
+  gboolean handshaking;
+  gboolean ever_handshaked;
+  GError *my_error = NULL;
+
+  /* We intentionally do not check for GNUTLS_E_INTERRUPTED here
+   * Instead, the caller may poll for the source to become ready again.
+   * (Note that GTlsOutputStreamGnutls and GTlsInputStreamGnutls inherit
+   * from GPollableOutputStream and GPollableInputStream, respectively.)
+   * See also the comment in set_gnutls_error().
+   */
+  if (ret == GNUTLS_E_AGAIN ||
+      ret == GNUTLS_E_WARNING_ALERT_RECEIVED)
+    return G_TLS_CONNECTION_BASE_TRY_AGAIN;
+
+  tls = g_tls_operations_thread_base_get_connection (G_TLS_OPERATIONS_THREAD_BASE (self));
+
+  status = g_tls_connection_base_pop_io (tls, direction, ret >= 0, &my_error);
+  if (status == G_TLS_CONNECTION_BASE_OK ||
+      status == G_TLS_CONNECTION_BASE_WOULD_BLOCK ||
+      status == G_TLS_CONNECTION_BASE_TIMED_OUT)
+    {
+      if (my_error)
+        g_propagate_error (error, my_error);
+      return status;
+    }
+
+  g_assert (status == G_TLS_CONNECTION_BASE_ERROR);
+
+  handshaking = g_tls_connection_base_is_handshaking (tls);
+  ever_handshaked = g_tls_connection_base_ever_handshaked (tls);
+
+  if (handshaking && !ever_handshaked)
+    {
+      if (g_error_matches (my_error, G_IO_ERROR, G_IO_ERROR_FAILED) ||
+          g_error_matches (my_error, G_IO_ERROR, G_IO_ERROR_BROKEN_PIPE))
+        {
+          g_set_error (error, G_TLS_ERROR, G_TLS_ERROR_NOT_TLS,
+                       _("Peer failed to perform TLS handshake: %s"), my_error->message);
+          g_clear_error (&my_error);
+          return G_TLS_CONNECTION_BASE_ERROR;
+        }
+
+      if (status == GNUTLS_E_UNEXPECTED_PACKET_LENGTH ||
+          status == GNUTLS_E_DECRYPTION_FAILED ||
+          status == GNUTLS_E_UNSUPPORTED_VERSION_PACKET)
+        {
+          g_clear_error (&my_error);
+          g_set_error (error, G_TLS_ERROR, G_TLS_ERROR_NOT_TLS,
+                       _("Peer failed to perform TLS handshake: %s"), gnutls_strerror (ret));
+          return G_TLS_CONNECTION_BASE_ERROR;
+        }
+    }
+
+  if (ret == GNUTLS_E_REHANDSHAKE)
+    return G_TLS_CONNECTION_BASE_REHANDSHAKE;
+
+  if (ret == GNUTLS_E_PREMATURE_TERMINATION)
+    {
+      if (handshaking && !ever_handshaked)
+        {
+          g_clear_error (&my_error);
+          g_set_error (error, G_TLS_ERROR, G_TLS_ERROR_NOT_TLS,
+                       _("Peer failed to perform TLS handshake: %s"), gnutls_strerror (ret));
+          return G_TLS_CONNECTION_BASE_ERROR;
+        }
+
+      if (g_tls_connection_get_require_close_notify (G_TLS_CONNECTION (tls)))
+        {
+          g_clear_error (&my_error);
+          g_set_error_literal (error, G_TLS_ERROR, G_TLS_ERROR_EOF,
+                               _("TLS connection closed unexpectedly"));
+          return G_TLS_CONNECTION_BASE_ERROR;
+        }
+
+      return G_TLS_CONNECTION_BASE_OK;
+    }
+
+  if (ret == GNUTLS_E_NO_CERTIFICATE_FOUND
+#ifdef GNUTLS_E_CERTIFICATE_REQUIRED
+           || ret == GNUTLS_E_CERTIFICATE_REQUIRED /* Added in GnuTLS 3.6.7 */
+#endif
+          )
+    {
+      g_clear_error (&my_error);
+      g_set_error_literal (error, G_TLS_ERROR, G_TLS_ERROR_CERTIFICATE_REQUIRED,
+                           _("TLS connection peer did not send a certificate"));
+      return G_TLS_CONNECTION_BASE_ERROR;
+    }
+
+  if (ret == GNUTLS_E_CERTIFICATE_ERROR)
+    {
+      g_clear_error (&my_error);
+      g_set_error_literal (error, G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE,
+                           _("Unacceptable TLS certificate"));
+      return G_TLS_CONNECTION_BASE_ERROR;
+    }
+
+  if (ret == GNUTLS_E_FATAL_ALERT_RECEIVED)
+    {
+      g_clear_error (&my_error);
+      g_set_error (error, G_TLS_ERROR, G_TLS_ERROR_MISC,
+                   _("Peer sent fatal TLS alert: %s"),
+                   gnutls_alert_get_name (gnutls_alert_get (self->session)));
+      return G_TLS_CONNECTION_BASE_ERROR;
+    }
+
+  if (ret == GNUTLS_E_INAPPROPRIATE_FALLBACK)
+    {
+      g_clear_error (&my_error);
+      g_set_error_literal (error, G_TLS_ERROR,
+                           G_TLS_ERROR_INAPPROPRIATE_FALLBACK,
+                           _("Protocol version downgrade attack detected"));
+      return G_TLS_CONNECTION_BASE_ERROR;
+    }
+
+  if (ret == GNUTLS_E_LARGE_PACKET)
+    {
+      guint mtu = gnutls_dtls_get_data_mtu (self->session);
+      g_clear_error (&my_error);
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_MESSAGE_TOO_LARGE,
+                   ngettext ("Message is too large for DTLS connection; maximum is %u byte",
+                             "Message is too large for DTLS connection; maximum is %u bytes", mtu), mtu);
+      return G_TLS_CONNECTION_BASE_ERROR;
+    }
+
+  if (ret == GNUTLS_E_TIMEDOUT)
+    {
+      g_clear_error (&my_error);
+      g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_TIMED_OUT,
+                           _("The operation timed out"));
+      return G_TLS_CONNECTION_BASE_ERROR;
+    }
+
+  if (error && my_error)
+    g_propagate_error (error, my_error);
+
+  if (error && !*error)
+    {
+      *error = g_error_new (G_TLS_ERROR, G_TLS_ERROR_MISC, "%s: %s",
+                            err_prefix, gnutls_strerror (ret));
+    }
+
+  return G_TLS_CONNECTION_BASE_ERROR;
+}
+
+#define BEGIN_GNUTLS_IO(self, direction, timeout, cancellable)        \
+  g_tls_connection_base_push_io (g_tls_operations_thread_base_get_connection (G_TLS_OPERATIONS_THREAD_BASE 
(self)),        \
+                                 direction, timeout, cancellable);    \
+  do {
+
+#define END_GNUTLS_IO(self, direction, ret, status, errmsg, err)      \
+    status = end_gnutls_io (self, direction, ret, err, errmsg);       \
+  } while (status == G_TLS_CONNECTION_BASE_TRY_AGAIN);
+
+static GTlsConnectionBaseStatus
+g_tls_operations_thread_gnutls_read (GTlsOperationsThreadBase  *base,
+                                     void                      *buffer,
+                                     gsize                      size,
+                                     gssize                    *nread,
+                                     GCancellable              *cancellable,
+                                     GError                   **error)
+{
+  GTlsOperationsThreadGnutls *self = G_TLS_OPERATIONS_THREAD_GNUTLS (base);
+  GTlsConnectionBaseStatus status;
+  gssize ret;
+
+  BEGIN_GNUTLS_IO (self, G_IO_IN, 0, cancellable);
+  ret = gnutls_record_recv (self->session, buffer, size);
+  END_GNUTLS_IO (self, G_IO_IN, ret, status, _("Error reading data from TLS socket"), error);
+
+  *nread = MAX (ret, 0);
+  return status;
+}
+
+static gsize
+input_vectors_from_gnutls_datum_t (GInputVector         *vectors,
+                                   guint                 num_vectors,
+                                   const gnutls_datum_t *datum)
+{
+  guint i;
+  gsize total = 0;
+
+  /* Copy into the receive vectors. */
+  for (i = 0; i < num_vectors && total < datum->size; i++)
+    {
+      gsize count;
+      GInputVector *vec = &vectors[i];
+
+      count = MIN (vec->size, datum->size - total);
+
+      memcpy (vec->buffer, datum->data + total, count);
+      total += count;
+    }
+
+  g_assert (total <= datum->size);
+
+  return total;
+}
+
+static GTlsConnectionBaseStatus
+g_tls_operations_thread_gnutls_read_message (GTlsOperationsThreadBase  *base,
+                                             GInputVector              *vectors,
+                                             guint                      num_vectors,
+                                             gint64                     timeout,
+                                             gssize                    *nread,
+                                             GCancellable              *cancellable,
+                                             GError                   **error)
+{
+  GTlsOperationsThreadGnutls *self = G_TLS_OPERATIONS_THREAD_GNUTLS (base);
+  GTlsConnectionBaseStatus status;
+  gssize ret;
+  gnutls_packet_t packet = { 0, };
+
+  BEGIN_GNUTLS_IO (self, G_IO_IN, timeout, cancellable);
+
+  /* Receive the entire datagram (zero-copy). */
+  ret = gnutls_record_recv_packet (self->session, &packet);
+
+  if (ret > 0)
+    {
+      gnutls_datum_t data = { 0, };
+
+      gnutls_packet_get (packet, &data, NULL);
+      ret = input_vectors_from_gnutls_datum_t (vectors, num_vectors, &data);
+      gnutls_packet_deinit (packet);
+    }
+
+  END_GNUTLS_IO (self, G_IO_IN, ret, status, _("Error reading data from TLS socket"), error);
+
+  *nread = MAX (ret, 0);
+  return status;
+}
+
+static GTlsConnectionBaseStatus
+g_tls_operations_thread_gnutls_write (GTlsOperationsThreadBase  *base,
+                                      const void                *buffer,
+                                      gsize                      size,
+                                      gssize                    *nwrote,
+                                      GCancellable              *cancellable,
+                                      GError                   **error)
+{
+  GTlsOperationsThreadGnutls *self = G_TLS_OPERATIONS_THREAD_GNUTLS (base);
+  GTlsConnectionBaseStatus status;
+  gssize ret;
+
+  BEGIN_GNUTLS_IO (self, G_IO_OUT, 0, cancellable);
+  ret = gnutls_record_send (self->session, buffer, size);
+  END_GNUTLS_IO (self, G_IO_OUT, ret, status, _("Error writing data to TLS socket"), error);
+
+  *nwrote = MAX (ret, 0);
+  return status;
+}
+
+static GTlsConnectionBaseStatus
+g_tls_operations_thread_gnutls_write_message (GTlsOperationsThreadBase  *base,
+                                              GOutputVector             *vectors,
+                                              guint                      num_vectors,
+                                              gint64                     timeout,
+                                              gssize                    *nwrote,
+                                              GCancellable              *cancellable,
+                                              GError                   **error)
+{
+  GTlsOperationsThreadGnutls *self = G_TLS_OPERATIONS_THREAD_GNUTLS (base);
+  GTlsConnectionBase *connection;
+  GTlsConnectionBaseStatus status;
+  gssize ret;
+  guint i;
+  gsize total_message_size;
+
+  connection = g_tls_operations_thread_base_get_connection (base);
+
+  /* Calculate the total message size and check it’s not too big. */
+  for (i = 0, total_message_size = 0; i < num_vectors; i++)
+    total_message_size += vectors[i].size;
+
+  if (g_tls_connection_base_is_dtls (connection) &&
+      gnutls_dtls_get_data_mtu (self->session) < total_message_size)
+    {
+      char *message;
+      guint mtu = gnutls_dtls_get_data_mtu (self->session);
+
+      ret = GNUTLS_E_LARGE_PACKET;
+      message = g_strdup_printf("%s %s",
+                                ngettext ("Message of size %lu byte is too large for DTLS connection",
+                                          "Message of size %lu bytes is too large for DTLS connection", 
total_message_size),
+                                ngettext ("(maximum is %u byte)", "(maximum is %u bytes)", mtu));
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_MESSAGE_TOO_LARGE,
+                   message,
+                   total_message_size,
+                   mtu);
+      g_free (message);
+
+      return G_TLS_CONNECTION_BASE_ERROR;
+    }
+
+  /* Queue up the data from all the vectors. */
+  gnutls_record_cork (self->session);
+
+  for (i = 0; i < num_vectors; i++)
+    {
+      ret = gnutls_record_send (self->session,
+                                vectors[i].buffer, vectors[i].size);
+
+      if (ret < 0 || ret < vectors[i].size)
+        {
+          /* Uncork to restore state, then bail. The peer will receive a
+           * truncated datagram. */
+          break;
+        }
+    }
+
+  BEGIN_GNUTLS_IO (self, G_IO_OUT, timeout, cancellable);
+  ret = gnutls_record_uncork (self->session, 0  /* flags */);
+  END_GNUTLS_IO (self, G_IO_OUT, ret, status, _("Error writing data to TLS socket"), error);
+
+  *nwrote = MAX (ret, 0);
+  return status;
+}
+
+static void
+g_tls_operations_thread_gnutls_constructed (GObject *object)
+{
+  GTlsOperationsThreadGnutls *self = G_TYPE_TLS_OPERATIONS_THREAD_GNUTLS (object);
+  GTlsConnectionBase *tls;
+
+  G_OBJECT_CLASS (g_tls_operations_thread_gnutls_parent_class)->constructed (object);
+
+  tls = g_tls_operations_thread_base_get_connection (G_TLS_OPERATIONS_THREAD_BASE (self));
+  self->session = g_tls_connection_gnutls_get_session (G_TLS_CONNECTION_GNUTLS (tls));
+}
+
 static void
 g_tls_operations_thread_gnutls_init (GTlsOperationsThreadGnutls *self)
 {
@@ -42,6 +389,15 @@ g_tls_operations_thread_gnutls_init (GTlsOperationsThreadGnutls *self)
 static void
 g_tls_operations_thread_gnutls_class_init (GTlsOperationsThreadGnutlsClass *klass)
 {
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+  GTlsOperationsThreadBaseClass *base_class = G_TLS_OPERATIONS_THREAD_BASE_CLASS (klass);
+
+  gobject_class->constructed   = g_tls_operations_thread_gnutls_constructed;
+
+  base_class->read_fn          = g_tls_operations_thread_gnutls_read;
+  base_class->read_message_fn  = g_tls_operations_thread_gnutls_read_message;
+  base_class->write_fn         = g_tls_operations_thread_gnutls_write;
+  base_class->write_message_fn = g_tls_operations_thread_gnutls_write_message;
 }
 
 GTlsOperationsThreadBase *
diff --git a/tls/gnutls/gtlsoperationsthread-gnutls.h b/tls/gnutls/gtlsoperationsthread-gnutls.h
index 4bffd58..8a0acad 100644
--- a/tls/gnutls/gtlsoperationsthread-gnutls.h
+++ b/tls/gnutls/gtlsoperationsthread-gnutls.h
@@ -24,6 +24,9 @@
 
 #pragma once
 
+#include <gio/gio.h>
+
+#include "gtlsconnection-gnutls.h"
 #include "gtlsoperationsthread-base.h"
 
 G_BEGIN_DECLS
diff --git a/tls/openssl/gtlsbio.c b/tls/openssl/gtlsbio.c
index b138432..e19edd2 100644
--- a/tls/openssl/gtlsbio.c
+++ b/tls/openssl/gtlsbio.c
@@ -35,8 +35,6 @@ typedef struct {
   gboolean write_blocking;
   GError **read_error;
   GError **write_error;
-  GMainContext *context;
-  GMainLoop *loop;
 } GTlsBio;
 
 static void
@@ -45,8 +43,6 @@ free_gbio (gpointer user_data)
   GTlsBio *bio = (GTlsBio *)user_data;
 
   g_object_unref (bio->io_stream);
-  g_main_context_unref (bio->context);
-  g_main_loop_unref (bio->loop);
   g_free (bio);
 }
 
@@ -294,8 +290,6 @@ g_tls_bio_new (GIOStream *io_stream)
 
   gbio = g_new0 (GTlsBio, 1);
   gbio->io_stream = g_object_ref (io_stream);
-  gbio->context = g_main_context_new ();
-  gbio->loop = g_main_loop_new (gbio->context, FALSE);
 
 #if OPENSSL_VERSION_NUMBER < 0x10100000L || defined (LIBRESSL_VERSION_NUMBER)
   ret->ptr = gbio;
@@ -403,49 +397,3 @@ g_tls_bio_set_write_error (BIO     *bio,
 #endif
   gbio->write_error = error;
 }
-
-static gboolean
-on_source_ready (GObject *pollable_stream,
-                 gpointer user_data)
-{
-  GMainLoop *loop = user_data;
-
-  g_main_loop_quit (loop);
-
-  return G_SOURCE_REMOVE;
-}
-
-void
-g_tls_bio_wait_available (BIO          *bio,
-                          GIOCondition  condition,
-                          GCancellable *cancellable)
-{
-  GTlsBio *gbio;
-  GSource *source;
-
-  g_return_if_fail (bio);
-
-#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined (LIBRESSL_VERSION_NUMBER)
-  gbio = (GTlsBio *)bio->ptr;
-#else
-  gbio = BIO_get_data (bio);
-#endif
-
-  g_main_context_push_thread_default (gbio->context);
-
-  if (condition & G_IO_IN)
-    source = g_pollable_input_stream_create_source (G_POLLABLE_INPUT_STREAM (g_io_stream_get_input_stream 
(gbio->io_stream)),
-                                                    cancellable);
-  else
-    source = g_pollable_output_stream_create_source (G_POLLABLE_OUTPUT_STREAM (g_io_stream_get_output_stream 
(gbio->io_stream)),
-                                                     cancellable);
-
-  g_source_set_callback (source, (GSourceFunc)on_source_ready, gbio->loop, NULL);
-  g_source_attach (source, gbio->context);
-
-  g_main_loop_run (gbio->loop);
-  g_main_context_pop_thread_default (gbio->context);
-
-  g_source_destroy (source);
-  g_source_unref (source);
-}
diff --git a/tls/openssl/gtlsconnection-openssl.c b/tls/openssl/gtlsconnection-openssl.c
index fc4603c..d356433 100644
--- a/tls/openssl/gtlsconnection-openssl.c
+++ b/tls/openssl/gtlsconnection-openssl.c
@@ -42,7 +42,6 @@
 typedef struct _GTlsConnectionOpensslPrivate
 {
   BIO *bio;
-  GMutex ssl_mutex;
 
   gboolean shutting_down;
 } GTlsConnectionOpensslPrivate;
@@ -54,19 +53,6 @@ G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GTlsConnectionOpenssl, g_tls_connection_openss
                                   G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,
                                                          g_tls_connection_openssl_initable_iface_init))
 
-static void
-g_tls_connection_openssl_finalize (GObject *object)
-{
-  GTlsConnectionOpenssl *openssl = G_TLS_CONNECTION_OPENSSL (object);
-  GTlsConnectionOpensslPrivate *priv;
-
-  priv = g_tls_connection_openssl_get_instance_private (openssl);
-
-  g_mutex_clear (&priv->ssl_mutex);
-
-  G_OBJECT_CLASS (g_tls_connection_openssl_parent_class)->finalize (object);
-}
-
 static GTlsOperationsThreadBase *
 g_tls_connection_openssl_create_op_thread (GTlsConnectionBase *tls)
 {
@@ -352,8 +338,6 @@ g_tls_connection_openssl_push_io (GTlsConnectionBase *tls,
       g_clear_error (error);
       g_tls_bio_set_write_error (priv->bio, error);
     }
-
-  g_mutex_lock (&priv->ssl_mutex);
 }
 
 static GTlsConnectionBaseStatus
@@ -367,8 +351,6 @@ g_tls_connection_openssl_pop_io (GTlsConnectionBase  *tls,
 
   priv = g_tls_connection_openssl_get_instance_private (openssl);
 
-  g_mutex_unlock (&priv->ssl_mutex);
-
   if (direction & G_IO_IN)
     g_tls_bio_set_read_cancellable (priv->bio, NULL);
 
@@ -379,95 +361,6 @@ g_tls_connection_openssl_pop_io (GTlsConnectionBase  *tls,
                                                                                       success, error);
 }
 
-static GTlsConnectionBaseStatus
-g_tls_connection_openssl_read (GTlsConnectionBase    *tls,
-                               void                  *buffer,
-                               gsize                  size,
-                               gssize                *nread,
-                               GCancellable          *cancellable,
-                               GError               **error)
-{
-  GTlsConnectionOpenssl *openssl = G_TLS_CONNECTION_OPENSSL (tls);
-  GTlsConnectionOpensslPrivate *priv;
-  GTlsConnectionBaseStatus status;
-  SSL *ssl;
-  gssize ret;
-
-  priv = g_tls_connection_openssl_get_instance_private (openssl);
-
-  ssl = g_tls_connection_openssl_get_ssl (openssl);
-
-  /* FIXME: revert back to use BEGIN/END_OPENSSL_IO once we move all the ssl
-   * operations into a worker thread
-   */
-  while (TRUE)
-    {
-      char error_str[256];
-
-      /* We want to always be non blocking here to avoid deadlocks */
-      g_tls_connection_base_push_io (G_TLS_CONNECTION_BASE (openssl),
-                                     G_IO_IN, 0, cancellable);
-
-      ret = SSL_read (ssl, buffer, size);
-
-      ERR_error_string_n (SSL_get_error (ssl, ret), error_str, sizeof (error_str));
-      status = end_openssl_io (openssl, G_IO_IN, ret, FALSE, error,
-                               _("Error reading data from TLS socket"), error_str);
-
-      if (status != G_TLS_CONNECTION_BASE_TRY_AGAIN)
-        break;
-
-      /* Wait for the socket to be available again to avoid an infinite loop */
-      g_tls_bio_wait_available (priv->bio, G_IO_IN, cancellable);
-    }
-
-  *nread = MAX (ret, 0);
-  return status;
-}
-
-static GTlsConnectionBaseStatus
-g_tls_connection_openssl_write (GTlsConnectionBase    *tls,
-                                const void            *buffer,
-                                gsize                  size,
-                                gssize                *nwrote,
-                                GCancellable          *cancellable,
-                                GError               **error)
-{
-  GTlsConnectionOpenssl *openssl = G_TLS_CONNECTION_OPENSSL (tls);
-  GTlsConnectionOpensslPrivate *priv;
-  GTlsConnectionBaseStatus status;
-  SSL *ssl;
-  gssize ret;
-
-  priv = g_tls_connection_openssl_get_instance_private (openssl);
-
-  ssl = g_tls_connection_openssl_get_ssl (openssl);
-
-  while (TRUE)
-    {
-      char error_str[256];
-
-      /* We want to always be non blocking here to avoid deadlocks */
-      g_tls_connection_base_push_io (G_TLS_CONNECTION_BASE (openssl),
-                                     G_IO_OUT, 0, cancellable);
-
-      ret = SSL_write (ssl, buffer, size);
-
-      ERR_error_string_n (SSL_get_error (ssl, ret), error_str, sizeof (error_str));
-      status = end_openssl_io (openssl, G_IO_OUT, ret, FALSE, error,
-                               _("Error writing data to TLS socket"), error_str);
-
-      if (status != G_TLS_CONNECTION_BASE_TRY_AGAIN)
-        break;
-
-      /* Wait for the socket to be available again to avoid an infinite loop */
-      g_tls_bio_wait_available (priv->bio, G_IO_OUT, cancellable);
-    }
-
-  *nwrote = MAX (ret, 0);
-  return status;
-}
-
 static GTlsConnectionBaseStatus
 g_tls_connection_openssl_close (GTlsConnectionBase  *tls,
                                 gint64               timeout,
@@ -497,14 +390,20 @@ g_tls_connection_openssl_close (GTlsConnectionBase  *tls,
   return status;
 }
 
+/* FIXME: remove */
+gboolean
+g_tls_connection_openssl_get_shutting_down (GTlsConnectionOpenssl *openssl)
+{
+  GTlsConnectionOpensslPrivate *priv = g_tls_connection_openssl_get_instance_private (openssl);
+
+  return priv->shutting_down;
+}
+
 static void
 g_tls_connection_openssl_class_init (GTlsConnectionOpensslClass *klass)
 {
-  GObjectClass *object_class = G_OBJECT_CLASS (klass);
   GTlsConnectionBaseClass *base_class = G_TLS_CONNECTION_BASE_CLASS (klass);
 
-  object_class->finalize                                 = g_tls_connection_openssl_finalize;
-
   base_class->create_op_thread                           = g_tls_connection_openssl_create_op_thread;
   base_class->handshake_thread_safe_renegotiation_status = 
g_tls_connection_openssl_handshake_thread_safe_renegotiation_status;
   base_class->handshake_thread_request_rehandshake       = 
g_tls_connection_openssl_handshake_thread_request_rehandshake;
@@ -512,8 +411,6 @@ g_tls_connection_openssl_class_init (GTlsConnectionOpensslClass *klass)
   base_class->retrieve_peer_certificate                  = 
g_tls_connection_openssl_retrieve_peer_certificate;
   base_class->push_io                                    = g_tls_connection_openssl_push_io;
   base_class->pop_io                                     = g_tls_connection_openssl_pop_io;
-  base_class->read_fn                                    = g_tls_connection_openssl_read;
-  base_class->write_fn                                   = g_tls_connection_openssl_write;
   base_class->close_fn                                   = g_tls_connection_openssl_close;
 }
 
@@ -561,11 +458,6 @@ g_tls_connection_openssl_initable_iface_init (GInitableIface *iface)
 static void
 g_tls_connection_openssl_init (GTlsConnectionOpenssl *openssl)
 {
-  GTlsConnectionOpensslPrivate *priv;
-
-  priv = g_tls_connection_openssl_get_instance_private (openssl);
-
-  g_mutex_init (&priv->ssl_mutex);
 }
 
 SSL *
diff --git a/tls/openssl/gtlsconnection-openssl.h b/tls/openssl/gtlsconnection-openssl.h
index 7b85fdc..c5aca9c 100644
--- a/tls/openssl/gtlsconnection-openssl.h
+++ b/tls/openssl/gtlsconnection-openssl.h
@@ -47,4 +47,7 @@ SSL *g_tls_connection_openssl_get_ssl (GTlsConnectionOpenssl *connection);
 
 GTlsConnectionOpenssl *g_tls_connection_openssl_get_connection_from_ssl (SSL *ssl);
 
+/* FIXME: remove */
+gboolean g_tls_connection_openssl_get_shutting_down (GTlsConnectionOpenssl *connection);
+
 G_END_DECLS
diff --git a/tls/openssl/gtlsoperationsthread-openssl.c b/tls/openssl/gtlsoperationsthread-openssl.c
index 22ae78c..43f4ca4 100644
--- a/tls/openssl/gtlsoperationsthread-openssl.c
+++ b/tls/openssl/gtlsoperationsthread-openssl.c
@@ -2,6 +2,7 @@
 /*
  * GIO - GLib Input, Output and Streaming Library
  *
+ * Copyright 2015 NICE s.r.l.
  * Copyright 2019 Igalia S.L.
  * Copyright 2019 Metrological Group B.V.
  *
@@ -25,15 +26,238 @@
 
 #include "config.h"
 
+#include <glib/gi18n-lib.h>
+
 #include "gtlsconnection-openssl.h"
 #include "gtlsoperationsthread-openssl.h"
 
 struct _GTlsOperationsThreadOpenssl {
   GTlsOperationsThreadBase parent_instance;
+
+  SSL *ssl;
 };
 
 G_DEFINE_TYPE (GTlsOperationsThreadOpenssl, g_tls_operations_thread_openssl, 
G_TYPE_TLS_OPERATIONS_THREAD_BASE)
 
+static GTlsConnectionBaseStatus
+end_openssl_io (GTlsOperationsThreadOpenssl  *self,
+                GIOCondition                  direction,
+                int                           ret,
+                gboolean                      blocking,
+                GError                      **error,
+                const char                   *err_prefix,
+                const char                   *err_str)
+{
+  GTlsConnectionBase *tls;
+  int err_code, err, err_lib, reason;
+  GError *my_error = NULL;
+  GTlsConnectionBaseStatus status;
+
+  tls = g_tls_operations_thread_base_get_connection (G_TLS_OPERATIONS_THREAD_BASE (self));
+
+  err_code = SSL_get_error (self->ssl, ret);
+
+  status = g_tls_connection_base_pop_io (tls, direction, ret > 0, &my_error);
+
+  if ((err_code == SSL_ERROR_WANT_READ ||
+       err_code == SSL_ERROR_WANT_WRITE) &&
+      blocking)
+    {
+      if (my_error)
+        g_error_free (my_error);
+      return G_TLS_CONNECTION_BASE_TRY_AGAIN;
+    }
+
+  if (err_code == SSL_ERROR_ZERO_RETURN)
+    return G_TLS_CONNECTION_BASE_OK;
+
+  if (status == G_TLS_CONNECTION_BASE_OK ||
+      status == G_TLS_CONNECTION_BASE_WOULD_BLOCK ||
+      status == G_TLS_CONNECTION_BASE_TIMED_OUT)
+    {
+      if (my_error)
+        g_propagate_error (error, my_error);
+      return status;
+    }
+
+  /* This case is documented that it may happen and that is perfectly fine */
+  if (err_code == SSL_ERROR_SYSCALL &&
+      ((g_tls_connection_openssl_get_shutting_down (G_TLS_CONNECTION_OPENSSL (tls)) && !my_error) ||
+       g_error_matches (my_error, G_IO_ERROR, G_IO_ERROR_BROKEN_PIPE)))
+    {
+      g_clear_error (&my_error);
+      return G_TLS_CONNECTION_BASE_OK;
+    }
+
+  err = ERR_get_error ();
+  err_lib = ERR_GET_LIB (err);
+  reason = ERR_GET_REASON (err);
+
+  if (g_tls_connection_base_is_handshaking (tls) && !g_tls_connection_base_ever_handshaked (tls))
+    {
+      if (reason == SSL_R_BAD_PACKET_LENGTH ||
+          reason == SSL_R_UNKNOWN_ALERT_TYPE ||
+          reason == SSL_R_DECRYPTION_FAILED ||
+          reason == SSL_R_DECRYPTION_FAILED_OR_BAD_RECORD_MAC ||
+          reason == SSL_R_BAD_PROTOCOL_VERSION_NUMBER ||
+          reason == SSL_R_SSLV3_ALERT_HANDSHAKE_FAILURE ||
+          reason == SSL_R_UNKNOWN_PROTOCOL)
+        {
+          g_clear_error (&my_error);
+          g_set_error (error, G_TLS_ERROR, G_TLS_ERROR_NOT_TLS,
+                       _("Peer failed to perform TLS handshake: %s"), ERR_reason_error_string (err));
+          return G_TLS_CONNECTION_BASE_ERROR;
+        }
+    }
+
+#ifdef SSL_R_SHUTDOWN_WHILE_IN_INIT
+  /* XXX: this error happens on ubuntu when shutting down the connection, it
+   * seems to be a bug in a specific version of openssl, so let's handle it
+   * gracefully
+   */
+  if (reason == SSL_R_SHUTDOWN_WHILE_IN_INIT)
+    {
+      g_clear_error (&my_error);
+      return G_TLS_CONNECTION_BASE_OK;
+    }
+#endif
+
+  if (reason == SSL_R_PEER_DID_NOT_RETURN_A_CERTIFICATE
+#ifdef SSL_R_TLSV13_ALERT_CERTIFICATE_REQUIRED
+      || reason == SSL_R_TLSV13_ALERT_CERTIFICATE_REQUIRED
+#endif
+     )
+    {
+      g_clear_error (&my_error);
+      g_set_error_literal (error, G_TLS_ERROR, G_TLS_ERROR_CERTIFICATE_REQUIRED,
+                           _("TLS connection peer did not send a certificate"));
+      return status;
+    }
+
+  if (reason == SSL_R_CERTIFICATE_VERIFY_FAILED)
+    {
+      g_clear_error (&my_error);
+      g_set_error (error, G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE,
+                   _("Unacceptable TLS certificate"));
+      return G_TLS_CONNECTION_BASE_ERROR;
+    }
+
+  if (reason == SSL_R_TLSV1_ALERT_UNKNOWN_CA)
+    {
+      g_clear_error (&my_error);
+      g_set_error (error, G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE,
+                   _("Unacceptable TLS certificate authority"));
+      return G_TLS_CONNECTION_BASE_ERROR;
+    }
+
+  if (err_lib == ERR_LIB_RSA && reason == RSA_R_DIGEST_TOO_BIG_FOR_RSA_KEY)
+    {
+      g_clear_error (&my_error);
+      g_set_error_literal (error, G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE,
+                           _("Digest too big for RSA key"));
+      return G_TLS_CONNECTION_BASE_ERROR;
+    }
+
+  if (my_error)
+    g_propagate_error (error, my_error);
+  else
+    /* FIXME: this is just for debug */
+    g_message ("end_openssl_io %s: %d, %d, %d", G_IS_TLS_CLIENT_CONNECTION (tls) ? "client" : "server", 
err_code, err_lib, reason);
+
+  if (error && !*error)
+    *error = g_error_new (G_TLS_ERROR, G_TLS_ERROR_MISC, "%s: %s", err_prefix, err_str);
+
+  return G_TLS_CONNECTION_BASE_ERROR;
+}
+
+#define BEGIN_OPENSSL_IO(self, direction, timeout, cancellable)          \
+  do {                                                                   \
+    char error_str[256];                                                 \
+    g_tls_connection_base_push_io (g_tls_operations_thread_base_get_connection (G_TLS_OPERATIONS_THREAD_BASE 
(self)), \
+                                   direction, timeout, cancellable);
+
+#define END_OPENSSL_IO(self, direction, ret, timeout, status, errmsg, err) \
+    ERR_error_string_n (SSL_get_error (self->ssl, ret), error_str, sizeof (error_str)); \
+    status = end_openssl_io (self, direction, ret, timeout == -1, err, errmsg, error_str); \
+  } while (status == G_TLS_CONNECTION_BASE_TRY_AGAIN);
+
+static GTlsConnectionBaseStatus
+g_tls_operations_thread_openssl_read (GTlsOperationsThreadBase   *base,
+                                      void                       *buffer,
+                                      gsize                       size,
+                                      gssize                     *nread,
+                                      GCancellable               *cancellable,
+                                      GError                    **error)
+{
+  GTlsOperationsThreadOpenssl *self = G_TYPE_TLS_OPERATIONS_THREAD_OPENSSL (base);
+  GTlsConnectionBaseStatus status;
+  gssize ret;
+
+  BEGIN_OPENSSL_IO (self, G_IO_OUT, timeout, cancellable);
+  ret = SSL_write (self->ssl, buffer, count);
+  END_OPENSSL_IO (self, G_IO_OUT, ret, status,
+                  _("Error writing data to TLS socket"), error);
+
+
+  *nread = MAX (ret, 0);
+  return status;
+}
+
+static GTlsConnectionBaseStatus
+g_tls_connection_openssl_write (GTlsConnectionBase    *tls,
+                                const void            *buffer,
+                                gsize                  size,
+                                gssize                *nwrote,
+                                GCancellable          *cancellable,
+                                GError               **error)
+{
+  GTlsConnectionOpenssl *openssl = G_TLS_CONNECTION_OPENSSL (tls);
+  GTlsConnectionOpensslPrivate *priv;
+  GTlsConnectionBaseStatus status;
+  SSL *ssl;
+  gssize ret;
+
+  priv = g_tls_connection_openssl_get_instance_private (openssl);
+
+  ssl = g_tls_connection_openssl_get_ssl (openssl);
+
+  while (TRUE)
+    {
+      char error_str[256];
+
+      /* We want to always be non blocking here to avoid deadlocks */
+      g_tls_connection_base_push_io (G_TLS_CONNECTION_BASE (openssl),
+                                     G_IO_OUT, 0, cancellable);
+
+      ret = SSL_write (ssl, buffer, size);
+
+      ERR_error_string_n (SSL_get_error (ssl, ret), error_str, sizeof (error_str));
+      status = end_openssl_io (openssl, G_IO_OUT, ret, FALSE, error,
+                               _("Error writing data to TLS socket"), error_str);
+
+      if (status != G_TLS_CONNECTION_BASE_TRY_AGAIN)
+        break;
+
+      /* Wait for the socket to be available again to avoid an infinite loop */
+      g_tls_bio_wait_available (priv->bio, G_IO_OUT, cancellable);
+    }
+
+  *nwrote = MAX (ret, 0);
+  return status;
+}
+
+static void
+g_tls_operations_thread_openssl_constructed (GObject *object)
+{
+  GTlsOperationsThreadOpenssl *self = G_TYPE_TLS_OPERATIONS_THREAD_OPENSSL (object);
+  GTlsConnectionOpenssl *openssl;
+
+  G_OBJECT_CLASS (g_tls_operations_thread_openssl_parent_class)->constructed (object);
+
+  openssl = g_tls_operations_thread_base_get_connection (G_TLS_OPERATIONS_THREAD_BASE (self));
+  self->ssl = g_tls_connection_openssl_get_ssl (openssl);
+}
+
 static void
 g_tls_operations_thread_openssl_init (GTlsOperationsThreadOpenssl *self)
 {
@@ -42,6 +266,13 @@ g_tls_operations_thread_openssl_init (GTlsOperationsThreadOpenssl *self)
 static void
 g_tls_operations_thread_openssl_class_init (GTlsOperationsThreadOpensslClass *klass)
 {
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+  GTlsOperationsThreadBaseClass *base_class = G_TLS_OPERATIONS_THREAD_BASE_CLASS (klass);
+
+  gobject_class->constructed = g_tls_operations_thread_openssl_constructed;
+
+  base_class->read_fn        = g_tls_operations_thread_openssl_read;
+  base_class->write_fn       = g_tls_operations_thread_openssl_write;
 }
 
 GTlsOperationsThreadBase *
diff --git a/tls/openssl/gtlsoperationsthread-openssl.h b/tls/openssl/gtlsoperationsthread-openssl.h
index 318c8a2..730bf75 100644
--- a/tls/openssl/gtlsoperationsthread-openssl.h
+++ b/tls/openssl/gtlsoperationsthread-openssl.h
@@ -24,6 +24,9 @@
 
 #pragma once
 
+#include <gio/gio.h>
+
+#include "gtlsconnection-openssl.h"
 #include "gtlsoperationsthread-base.h"
 
 G_BEGIN_DECLS


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