[glib-networking/mcatanzaro/tls-thread] ridicuous unstable wip progress



commit 656ffbd15e4e84f92f1d54f01b044918b0cbaed4
Author: Michael Catanzaro <mcatanzaro gnome org>
Date:   Sat Dec 21 13:45:42 2019 -0600

    ridicuous unstable wip progress

 tls/base/gtlsconnection-base.c             | 200 +--------
 tls/base/gtlsconnection-base.h             |  45 +-
 tls/base/gtlsoperationsthread-base.c       |   7 +-
 tls/base/gtlsoperationsthread-base.h       |  12 +-
 tls/gnutls/gtlsclientconnection-gnutls.c   |  84 ----
 tls/gnutls/gtlsconnection-gnutls.c         | 657 ++---------------------------
 tls/gnutls/gtlsconnection-gnutls.h         |   3 -
 tls/gnutls/gtlsoperationsthread-gnutls.c   | 603 +++++++++++++++++++++++++-
 tls/gnutls/gtlsoperationsthread-gnutls.h   |   8 +-
 tls/openssl/gtlsconnection-openssl.c       | 227 +---------
 tls/openssl/gtlsconnection-openssl.h       |   2 +
 tls/openssl/gtlsoperationsthread-openssl.c |  30 ++
 12 files changed, 700 insertions(+), 1178 deletions(-)
---
diff --git a/tls/base/gtlsconnection-base.c b/tls/base/gtlsconnection-base.c
index 83e0765..d2dcfef 100644
--- a/tls/base/gtlsconnection-base.c
+++ b/tls/base/gtlsconnection-base.c
@@ -143,14 +143,7 @@ typedef struct
   gboolean       write_closing, write_closed;
 
   gboolean       reading;
-  gint64         read_timeout;
-  GError        *read_error;
-  GCancellable  *read_cancellable;
-
   gboolean       writing;
-  gint64         write_timeout;
-  GError        *write_error;
-  GCancellable  *write_cancellable;
 
   gboolean       successful_posthandshake_op;
 
@@ -291,10 +284,6 @@ g_tls_connection_base_finalize (GObject *object)
   g_clear_object (&priv->async_implicit_handshake);
 
   g_clear_error (&priv->handshake_error);
-  g_clear_error (&priv->read_error);
-  g_clear_error (&priv->write_error);
-  g_clear_object (&priv->read_cancellable);
-  g_clear_object (&priv->write_cancellable);
 
   g_clear_object (&priv->waiting_for_op);
   g_mutex_clear (&priv->op_mutex);
@@ -766,29 +755,7 @@ yield_op (GTlsConnectionBase       *tls,
   g_mutex_unlock (&priv->op_mutex);
 }
 
-static void
-g_tls_connection_base_real_push_io (GTlsConnectionBase *tls,
-                                    GIOCondition        direction,
-                                    gint64              timeout,
-                                    GCancellable       *cancellable)
-{
-  GTlsConnectionBasePrivate *priv = g_tls_connection_base_get_instance_private (tls);
-
-  if (direction & G_IO_IN)
-    {
-      priv->read_timeout = timeout;;
-      priv->read_cancellable = cancellable;
-      g_clear_error (&priv->read_error);
-    }
-
-  if (direction & G_IO_OUT)
-    {
-      priv->write_timeout = timeout;
-      priv->write_cancellable = cancellable;
-      g_clear_error (&priv->write_error);
-    }
-}
-
+/* FIXME: removable? */
 void
 g_tls_connection_base_push_io (GTlsConnectionBase *tls,
                                GIOCondition        direction,
@@ -802,6 +769,7 @@ g_tls_connection_base_push_io (GTlsConnectionBase *tls,
                                                   timeout, cancellable);
 }
 
+/* FIXME: rename, if push_io is removed? */
 static GTlsConnectionBaseStatus
 g_tls_connection_base_real_pop_io (GTlsConnectionBase  *tls,
                                    GIOCondition         direction,
@@ -813,30 +781,6 @@ g_tls_connection_base_real_pop_io (GTlsConnectionBase  *tls,
 
   /* This function MAY or MAY NOT set error when it fails! */
 
-  if (direction & G_IO_IN)
-    {
-      priv->read_cancellable = NULL;
-      if (!success)
-        {
-          my_error = priv->read_error;
-          priv->read_error = NULL;
-        }
-      else
-        g_clear_error (&priv->read_error);
-    }
-
-  if (direction & G_IO_OUT)
-    {
-      priv->write_cancellable = NULL;
-      if (!success && !my_error)
-        {
-          my_error = priv->write_error;
-          priv->write_error = NULL;
-        }
-      else
-        g_clear_error (&priv->write_error);
-    }
-
   if (success)
     return G_TLS_CONNECTION_BASE_OK;
 
@@ -902,7 +846,8 @@ g_tls_connection_base_pop_io (GTlsConnectionBase  *tls,
 }
 
 /* Checks whether the underlying base stream or GDatagramBased meets
- * @condition. */
+ * @condition.
+ */
 gboolean
 g_tls_connection_base_base_check (GTlsConnectionBase *tls,
                                   GIOCondition        condition)
@@ -922,7 +867,8 @@ g_tls_connection_base_base_check (GTlsConnectionBase *tls,
 }
 
 /* Checks whether the (D)TLS stream meets @condition; not the underlying base
- * stream or GDatagramBased. */
+ * stream or GDatagramBased.
+ */
 gboolean
 g_tls_connection_base_check (GTlsConnectionBase  *tls,
                              GIOCondition         condition)
@@ -1474,13 +1420,9 @@ handshake (GTlsConnectionBase  *tls,
            GError             **error)
 {
   GTlsConnectionBasePrivate *priv = g_tls_connection_base_get_instance_private (tls);
-  GTlsConnectionBaseClass *tls_class = G_TLS_CONNECTION_BASE_GET_CLASS (tls);
-  gint64 start_time;
 
   g_tls_log_debug (tls, "TLS handshake starts");
 
-  start_time = g_get_monotonic_time ();
-
   priv->started_handshake = FALSE;
   priv->missing_requested_client_certificate = FALSE;
 
@@ -1498,44 +1440,17 @@ handshake (GTlsConnectionBase  *tls,
 
   if (priv->ever_handshaked && !priv->need_handshake)
     {
-      GTlsConnectionBaseStatus status;
-
-      /* FIXME: no longer handshake thread */
-      if (tls_class->handshake_thread_safe_renegotiation_status (tls) != 
G_TLS_SAFE_RENEGOTIATION_SUPPORTED_BY_PEER)
-        {
-          g_set_error_literal (error, G_TLS_ERROR, G_TLS_ERROR_MISC,
-                               _("Peer does not support safe renegotiation"));
-          g_tls_log_debug (tls, "TLS handshake failed: peer does not support safe renegotiation");
-          return FALSE;
-        }
-
-      /* Adjust the timeout for the next operation in the sequence. */
-      if (timeout > 0)
-        {
-          timeout -= (g_get_monotonic_time () - start_time);
-          if (timeout <= 0)
-            timeout = 1;
-        }
-
-      /* FIXME: no longer handshake thread */
-      status = tls_class->handshake_thread_request_rehandshake (tls, timeout, cancellable, error);
-      if (status != G_TLS_CONNECTION_BASE_OK)
-        {
-          g_tls_log_debug (tls, "TLS handshake failed: %s", *error ? (*error)->message : "no error");
-          return FALSE;
-        }
-    }
-
-  /* Adjust the timeout for the next operation in the sequence. */
-  if (timeout > 0)
-    {
-      timeout -= (g_get_monotonic_time () - start_time);
-      if (timeout <= 0)
-        timeout = 1;
+      /* Once upon a time, we allowed calling g_tls_connection_handshake()
+       * twice in order to request a rehandshake. Now that rehandshaking has
+       * been removed from TLS 1.3, we'll instead just ignore the request. We
+       * can't throw an error here because this used to be allowed.
+       */
+      g_tls_log_debug (tls, "Ignoring duplicate TLS handshake request");
+      return TRUE;
     }
 
   priv->started_handshake = TRUE;
-  g_tls_operations_thread_base_handshake (priv->thread, timeout, cancellable, error);
+  g_tls_operations_thread_base_handshake (priv->thread, priv->advertised_protocols, timeout, cancellable, 
error);
   priv->need_handshake = FALSE;
 
   if (error && *error)
@@ -1632,9 +1547,6 @@ g_tls_connection_base_handshake (GTlsConnection   *conn,
 
   g_main_context_push_thread_default (priv->handshake_context);
 
-  if (tls_class->prepare_handshake)
-    tls_class->prepare_handshake (tls, priv->advertised_protocols);
-
   success = handshake (tls, -1 /* blocking */, cancellable, error);
 
   g_mutex_lock (&priv->op_mutex);
@@ -1783,9 +1695,6 @@ g_tls_connection_base_handshake_async (GTlsConnection      *conn,
   g_assert (!priv->handshake_context);
   priv->handshake_context = g_main_context_ref_thread_default ();
 
-  if (tls_class->prepare_handshake)
-    tls_class->prepare_handshake (tls, priv->advertised_protocols);
-
   caller_task = g_task_new (conn, cancellable, callback, user_data);
   g_task_set_source_tag (caller_task, g_tls_connection_base_handshake_async);
   g_task_set_name (caller_task, "[glib-networking] g_tls_connection_base_handshake_async (caller task)");
@@ -1852,9 +1761,6 @@ start_async_implicit_handshake (GTlsConnectionBase  *tls,
   g_task_set_source_tag (priv->async_implicit_handshake, do_implicit_handshake);
   g_task_set_name (priv->async_implicit_handshake, "[glib-networking] do_implicit_handshake");
 
-  if (tls_class->prepare_handshake)
-    tls_class->prepare_handshake (tls, priv->advertised_protocols);
-
   /* In the non-blocking case, start the asynchronous handshake operation
    * and return EWOULDBLOCK to the caller, who will handle polling for
    * completion of the handshake and whatever operation they actually cared
@@ -1889,9 +1795,6 @@ do_sync_implicit_handshake (GTlsConnectionBase  *tls,
   priv->handshake_context = g_main_context_new ();
   g_main_context_push_thread_default (priv->handshake_context);
 
-  if (tls_class->prepare_handshake)
-    tls_class->prepare_handshake (tls, priv->advertised_protocols);
-
   g_mutex_unlock (&priv->op_mutex);
 
   success = handshake (tls, timeout, cancellable, &my_error);
@@ -2561,70 +2464,6 @@ g_tls_connection_base_handshake_thread_set_missing_requested_client_certificate
   priv->missing_requested_client_certificate = TRUE;
 }
 
-GError **
-g_tls_connection_base_get_read_error (GTlsConnectionBase *tls)
-{
-  GTlsConnectionBasePrivate *priv = g_tls_connection_base_get_instance_private (tls);
-
-  return &priv->read_error;
-}
-
-GError **
-g_tls_connection_base_get_write_error (GTlsConnectionBase *tls)
-{
-  GTlsConnectionBasePrivate *priv = g_tls_connection_base_get_instance_private (tls);
-
-  return &priv->write_error;
-}
-
-gint64
-g_tls_connection_base_get_read_timeout (GTlsConnectionBase *tls)
-{
-  GTlsConnectionBasePrivate *priv = g_tls_connection_base_get_instance_private (tls);
-
-  return priv->read_timeout;
-}
-
-gint64
-g_tls_connection_base_get_write_timeout (GTlsConnectionBase *tls)
-{
-  GTlsConnectionBasePrivate *priv = g_tls_connection_base_get_instance_private (tls);
-
-  return priv->write_timeout;
-}
-
-GCancellable *
-g_tls_connection_base_get_read_cancellable (GTlsConnectionBase *tls)
-{
-  GTlsConnectionBasePrivate *priv = g_tls_connection_base_get_instance_private (tls);
-
-  return priv->read_cancellable;
-}
-
-GCancellable *
-g_tls_connection_base_get_write_cancellable (GTlsConnectionBase *tls)
-{
-  GTlsConnectionBasePrivate *priv = g_tls_connection_base_get_instance_private (tls);
-
-  return priv->write_cancellable;
-}
-
-gboolean
-g_tls_connection_base_is_handshaking (GTlsConnectionBase *tls)
-{
-  GTlsConnectionBasePrivate *priv = g_tls_connection_base_get_instance_private (tls);
-
-  return priv->handshaking;
-}
-
-gboolean
-g_tls_connection_base_ever_handshaked (GTlsConnectionBase *tls)
-{
-  GTlsConnectionBasePrivate *priv = g_tls_connection_base_get_instance_private (tls);
-
-  return priv->ever_handshaked;
-}
-
 gboolean
 g_tls_connection_base_handshake_thread_request_certificate (GTlsConnectionBase *tls)
 {
@@ -2644,7 +2483,7 @@ g_tls_connection_base_handshake_thread_request_certificate (GTlsConnectionBase *
     return FALSE;
 
   res = g_tls_interaction_invoke_request_certificate (interaction, conn, 0,
-                                                      priv->read_cancellable,
+                                                      /* FIXME: priv->read_cancellable */ NULL,
                                                       &priv->interaction_error);
   return res != G_TLS_INTERACTION_FAILED;
 }
@@ -2662,6 +2501,14 @@ g_tls_connection_base_handshake_thread_buffer_application_data (GTlsConnectionBa
   g_byte_array_append (priv->app_data_buf, data, length);
 }
 
+GTlsOperationsThreadBase *
+g_tls_connection_base_get_op_thread (GTlsConnectionBase *tls)
+{
+  GTlsConnectionBasePrivate *priv = g_tls_connection_base_get_instance_private (tls);
+
+  return priv->thread;
+}
+
 static void
 g_tls_connection_base_class_init (GTlsConnectionBaseClass *klass)
 {
@@ -2683,7 +2530,6 @@ g_tls_connection_base_class_init (GTlsConnectionBaseClass *klass)
   iostream_class->close_async       = g_tls_connection_base_close_async;
   iostream_class->close_finish      = g_tls_connection_base_close_finish;
 
-  klass->push_io = g_tls_connection_base_real_push_io;
   klass->pop_io = g_tls_connection_base_real_pop_io;
 
   /* For GTlsConnection and GDtlsConnection: */
diff --git a/tls/base/gtlsconnection-base.h b/tls/base/gtlsconnection-base.h
index fa155f3..65058d0 100644
--- a/tls/base/gtlsconnection-base.h
+++ b/tls/base/gtlsconnection-base.h
@@ -48,11 +48,6 @@ typedef enum {
   G_TLS_DIRECTION_WRITE = 1 << 1,
 } GTlsDirection;
 
-typedef enum {
-  G_TLS_SAFE_RENEGOTIATION_SUPPORTED_BY_PEER,
-  G_TLS_SAFE_RENEGOTIATION_UNSUPPORTED
-} GTlsSafeRenegotiationStatus;
-
 #define G_TLS_DIRECTION_BOTH (G_TLS_DIRECTION_READ | G_TLS_DIRECTION_WRITE)
 
 typedef struct _GTlsOperationsThreadBase GTlsOperationsThreadBase;
@@ -63,28 +58,6 @@ struct _GTlsConnectionBaseClass
 
   GTlsOperationsThreadBase   *(*create_op_thread)           (GTlsConnectionBase   *tls);
 
-  /* FIXME: deal with all the handshaking stuff */
-  void                        (*prepare_handshake)          (GTlsConnectionBase   *tls,
-                                                             gchar               **advertised_protocols);
-  GTlsSafeRenegotiationStatus (*handshake_thread_safe_renegotiation_status)
-                                                            (GTlsConnectionBase    *tls);
-  GTlsConnectionBaseStatus    (*handshake_thread_request_rehandshake)
-                                                            (GTlsConnectionBase   *tls,
-                                                             gint64                timeout,
-                                                             GCancellable         *cancellable,
-                                                             GError              **error);
-  GTlsConnectionBaseStatus    (*handshake_thread_handshake) (GTlsConnectionBase   *tls,
-                                                             gint64                timeout,
-                                                             GCancellable         *cancellable,
-                                                             GError              **error);
-  GTlsCertificate            *(*retrieve_peer_certificate)  (GTlsConnectionBase   *tls);
-  GTlsCertificateFlags        (*verify_peer_certificate)    (GTlsConnectionBase   *tls,
-                                                             GTlsCertificate      *certificate,
-                                                             GTlsCertificateFlags  flags);
-  void                        (*complete_handshake)         (GTlsConnectionBase   *tls,
-                                                             gchar               **negotiated_protocol,
-                                                             GError              **error);
-
   gboolean                    (*is_session_resumed)         (GTlsConnectionBase   *tls);
 
   void                        (*push_io)                    (GTlsConnectionBase   *tls,
@@ -139,6 +112,8 @@ gboolean                  g_tls_connection_base_close_internal          (GIOStre
                                                                          GCancellable   *cancellable,
                                                                          GError        **error);
 
+/* FIXME: audit, which are still needed? */
+
 gboolean                  g_tls_connection_base_is_dtls                 (GTlsConnectionBase *tls);
 
 GDatagramBased           *g_tls_connection_base_get_base_socket         (GTlsConnectionBase *tls);
@@ -150,19 +125,6 @@ GPollableOutputStream    *g_tls_connection_base_get_base_ostream        (GTlsCon
 void                      g_tls_connection_base_handshake_thread_set_missing_requested_client_certificate
                                                                         (GTlsConnectionBase *tls);
 
-GError                  **g_tls_connection_base_get_read_error          (GTlsConnectionBase *tls);
-GError                  **g_tls_connection_base_get_write_error         (GTlsConnectionBase *tls);
-
-gint64                    g_tls_connection_base_get_read_timeout        (GTlsConnectionBase *tls);
-gint64                    g_tls_connection_base_get_write_timeout       (GTlsConnectionBase *tls);
-
-GCancellable             *g_tls_connection_base_get_read_cancellable    (GTlsConnectionBase *tls);
-GCancellable             *g_tls_connection_base_get_write_cancellable   (GTlsConnectionBase *tls);
-
-gboolean                  g_tls_connection_base_is_handshaking          (GTlsConnectionBase *tls);
-
-gboolean                  g_tls_connection_base_ever_handshaked         (GTlsConnectionBase *tls);
-
 gboolean                  g_tls_connection_base_handshake_thread_request_certificate
                                                                         (GTlsConnectionBase  *tls);
 
@@ -171,4 +133,7 @@ void                      g_tls_connection_base_handshake_thread_buffer_applicat
                                                                          guint8             *data,
                                                                          gsize               length);
 
+/* FIXME: needed? */
+GTlsOperationsThreadBase *g_tls_connection_base_get_op_thread           (GTlsConnectionBase *tls);
+
 G_END_DECLS
diff --git a/tls/base/gtlsoperationsthread-base.c b/tls/base/gtlsoperationsthread-base.c
index 18cf84c..fc37fd2 100644
--- a/tls/base/gtlsoperationsthread-base.c
+++ b/tls/base/gtlsoperationsthread-base.c
@@ -133,7 +133,7 @@ enum
 
 static GParamSpec *obj_properties[LAST_PROP];
 
-G_DEFINE_TYPE_WITH_PRIVATE (GTlsOperationsThreadBase, g_tls_operations_thread_base, G_TYPE_OBJECT)
+G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (GTlsOperationsThreadBase, g_tls_operations_thread_base, G_TYPE_OBJECT)
 
 GTlsConnectionBase *
 g_tls_operations_thread_base_get_connection (GTlsOperationsThreadBase *self)
@@ -898,7 +898,7 @@ g_tls_operations_thread_base_set_property (GObject      *object,
 
       /* This weak pointer is not required for correctness, because the
        * thread should never outlive its GTlsConnection. It's only here
-       * as a sanity-check and debugging aid, to ensure self->connection
+       * as a sanity-check and debugging aid, to ensure priv->connection
        * isn't ever dangling.
        */
       g_object_add_weak_pointer (G_OBJECT (priv->connection),
@@ -952,6 +952,9 @@ g_tls_operations_thread_base_class_init (GTlsOperationsThreadBaseClass *klass)
   gobject_class->get_property = g_tls_operations_thread_base_get_property;
   gobject_class->set_property = g_tls_operations_thread_base_set_property;
 
+  /* FIXME: remove this. subclass has been designed to not need it!
+   * Move base_iostream and base_socket up to this level.
+   */
   obj_properties[PROP_TLS_CONNECTION] =
     g_param_spec_object ("tls-connection",
                          "TLS Connection",
diff --git a/tls/base/gtlsoperationsthread-base.h b/tls/base/gtlsoperationsthread-base.h
index 63f7a34..a26dd7d 100644
--- a/tls/base/gtlsoperationsthread-base.h
+++ b/tls/base/gtlsoperationsthread-base.h
@@ -38,10 +38,19 @@ struct _GTlsOperationsThreadBaseClass
 {
   GObjectClass parent_class;
 
+/* FIXME: working on these... */
   GTlsConnectionBaseStatus    (*handshake_fn)               (GTlsOperationsThreadBase  *self,
+                                                             gchar                    **advertised_protocols,
                                                              gint64                     timeout,
                                                              GCancellable              *cancellable,
                                                              GError                   **error);
+  GTlsCertificate            *(*retrieve_peer_certificate)  (GTlsOperationsThreadBase  *self);
+  GTlsCertificateFlags        (*verify_peer_certificate)    (GTlsOperationsThreadBase  *self,
+                                                             GTlsCertificate           *certificate,
+                                                             GTlsCertificateFlags       flags);
+  void                        (*complete_handshake)         (GTlsOperationsThreadBase  *self,
+                                                             gchar                    **negotiated_protocol,
+                                                             GError                   **error);
 
   GTlsConnectionBaseStatus    (*read_fn)                    (GTlsOperationsThreadBase  *self,
                                                              void                      *buffer,
@@ -74,10 +83,11 @@ struct _GTlsOperationsThreadBaseClass
                                                              GError                   **error);
 };
 
-/* FIXME: remove? */
+/* FIXME: remove!!! */
 GTlsConnectionBase       *g_tls_operations_thread_base_get_connection (GTlsOperationsThreadBase  *self);
 
 GTlsConnectionBaseStatus  g_tls_operations_thread_base_handshake      (GTlsOperationsThreadBase  *self,
+                                                                       gchar                    
**advertised_protocols,
                                                                        gint64                     timeout,
                                                                        GCancellable              
*cancellable,
                                                                        GError                   **error);
diff --git a/tls/gnutls/gtlsclientconnection-gnutls.c b/tls/gnutls/gtlsclientconnection-gnutls.c
index 1d2ba78..e74b43f 100644
--- a/tls/gnutls/gtlsclientconnection-gnutls.c
+++ b/tls/gnutls/gtlsclientconnection-gnutls.c
@@ -120,87 +120,6 @@ get_server_identity (GTlsClientConnectionGnutls *gnutls)
     return NULL;
 }
 
-static void
-g_tls_client_connection_gnutls_compute_session_id (GTlsClientConnectionGnutls *gnutls)
-{
-  GSocketConnection *base_conn;
-  GSocketAddress *remote_addr;
-  GInetAddress *iaddr;
-  guint port;
-
-  /* The testsuite expects handshakes to actually happen. E.g. a test might
-   * check to see that a handshake succeeds and then later check that a new
-   * handshake fails. If we get really unlucky and the same port number is
-   * reused for the server socket between connections, then we'll accidentally
-   * resume the old session and skip certificate verification. Such failures
-   * are difficult to debug because they require running the tests hundreds of
-   * times simultaneously to reproduce (the port number does not get reused
-   * quickly enough if the tests are run sequentially).
-   *
-   * So session resumption will just need to be tested manually.
-   */
-  if (g_test_initialized ())
-    return;
-
-  /* Create a TLS "session ID." We base it on the IP address since
-   * different hosts serving the same hostname/service will probably
-   * not share the same session cache. We base it on the
-   * server-identity because at least some servers will fail (rather
-   * than just failing to resume the session) if we don't.
-   * (https://bugs.launchpad.net/bugs/823325)
-   *
-   * Note that our session IDs have no relation to TLS protocol
-   * session IDs, e.g. as provided by gnutls_session_get_id2(). Unlike
-   * our session IDs, actual TLS session IDs can no longer be used for
-   * session resumption.
-   */
-  g_object_get (G_OBJECT (gnutls), "base-io-stream", &base_conn, NULL);
-  if (G_IS_SOCKET_CONNECTION (base_conn))
-    {
-      remote_addr = g_socket_connection_get_remote_address (base_conn, NULL);
-      if (G_IS_INET_SOCKET_ADDRESS (remote_addr))
-        {
-          GInetSocketAddress *isaddr = G_INET_SOCKET_ADDRESS (remote_addr);
-          const gchar *server_hostname;
-          gchar *addrstr, *session_id;
-          GTlsCertificate *cert = NULL;
-          gchar *cert_hash = NULL;
-
-          iaddr = g_inet_socket_address_get_address (isaddr);
-          port = g_inet_socket_address_get_port (isaddr);
-
-          addrstr = g_inet_address_to_string (iaddr);
-          server_hostname = get_server_identity (gnutls);
-
-          /* If we have a certificate, make its hash part of the session ID, so
-           * that different connections to the same server can use different
-           * certificates.
-           */
-          g_object_get (G_OBJECT (gnutls), "certificate", &cert, NULL);
-          if (cert)
-            {
-              GByteArray *der = NULL;
-              g_object_get (G_OBJECT (cert), "certificate", &der, NULL);
-              if (der)
-                {
-                  cert_hash = g_compute_checksum_for_data (G_CHECKSUM_SHA256, der->data, der->len);
-                  g_byte_array_unref (der);
-                }
-              g_object_unref (cert);
-            }
-          session_id = g_strdup_printf ("%s/%s/%d/%s", addrstr,
-                                        server_hostname ? server_hostname : "",
-                                        port,
-                                        cert_hash ? cert_hash : "");
-          gnutls->session_id = g_bytes_new_take (session_id, strlen (session_id));
-          g_free (addrstr);
-          g_free (cert_hash);
-        }
-      g_object_unref (remote_addr);
-    }
-  g_clear_object (&base_conn);
-}
-
 static int
 handshake_thread_session_ticket_received_cb (gnutls_session_t      session,
                                              guint                 htype,
@@ -478,8 +397,6 @@ g_tls_client_connection_gnutls_prepare_handshake (GTlsConnectionBase  *tls,
         }
     }
 
-  G_TLS_CONNECTION_BASE_CLASS (g_tls_client_connection_gnutls_parent_class)->
-    prepare_handshake (tls, advertised_protocols);
 }
 
 static void
@@ -535,7 +452,6 @@ g_tls_client_connection_gnutls_class_init (GTlsClientConnectionGnutlsClass *klas
   gobject_class->set_property = g_tls_client_connection_gnutls_set_property;
   gobject_class->finalize     = g_tls_client_connection_gnutls_finalize;
 
-  base_class->prepare_handshake  = g_tls_client_connection_gnutls_prepare_handshake;
   base_class->complete_handshake = g_tls_client_connection_gnutls_complete_handshake;
 
   g_object_class_override_property (gobject_class, PROP_VALIDATION_FLAGS, "validation-flags");
diff --git a/tls/gnutls/gtlsconnection-gnutls.c b/tls/gnutls/gtlsconnection-gnutls.c
index 3bccc98..d1a6b68 100644
--- a/tls/gnutls/gtlsconnection-gnutls.c
+++ b/tls/gnutls/gtlsconnection-gnutls.c
@@ -27,6 +27,8 @@
 #include "config.h"
 #include "glib.h"
 
+/* FIXME: audit includes to remove */
+
 #include <errno.h>
 #include <stdarg.h>
 #include <gnutls/dtls.h>
@@ -52,19 +54,6 @@
 #include <glib/gi18n-lib.h>
 #include <glib/gprintf.h>
 
-static ssize_t g_tls_connection_gnutls_push_func (gnutls_transport_ptr_t  transport_data,
-                                                  const void             *buf,
-                                                  size_t                  buflen);
-static ssize_t g_tls_connection_gnutls_vec_push_func (gnutls_transport_ptr_t  transport_data,
-                                                      const giovec_t         *iov,
-                                                      int                     iovcnt);
-static ssize_t g_tls_connection_gnutls_pull_func (gnutls_transport_ptr_t  transport_data,
-                                                  void                   *buf,
-                                                  size_t                  buflen);
-
-static int     g_tls_connection_gnutls_pull_timeout_func (gnutls_transport_ptr_t transport_data,
-                                                          unsigned int           ms);
-
 static GInitableIface *g_tls_connection_gnutls_parent_initable_iface;
 
 static void g_tls_connection_gnutls_initable_iface_init (GInitableIface *iface);
@@ -75,7 +64,6 @@ static gnutls_priority_t priority;
 
 typedef struct
 {
-  gnutls_certificate_credentials_t creds;
   gnutls_session_t session; /* FIXME: should be used only by GTlsOperationsThreadGnutls */
   gchar *interaction_id;
   GCancellable *cancellable;
@@ -121,65 +109,14 @@ g_tls_connection_gnutls_initable_init (GInitable     *initable,
 {
   GTlsConnectionGnutls *gnutls = G_TLS_CONNECTION_GNUTLS (initable);
   GTlsConnectionGnutlsPrivate *priv = g_tls_connection_gnutls_get_instance_private (gnutls);
-  GIOStream *base_io_stream = NULL;
-  GDatagramBased *base_socket = NULL;
-  gboolean client = G_IS_TLS_CLIENT_CONNECTION (gnutls);
-  guint flags = client ? GNUTLS_CLIENT : GNUTLS_SERVER;
-  int status;
-  int ret;
 
-  g_object_get (gnutls,
-                "base-io-stream", &base_io_stream,
-                "base-socket", &base_socket,
-                NULL);
-
-  /* Ensure we are in TLS mode or DTLS mode. */
-  g_return_val_if_fail (!!base_io_stream != !!base_socket, FALSE);
-
-  if (base_socket)
-    flags |= GNUTLS_DATAGRAM;
-
-  ret = gnutls_certificate_allocate_credentials (&priv->creds);
-  if (ret != GNUTLS_E_SUCCESS)
+  if (!g_tls_connection_gnutls_parent_initable_iface->init (initable, cancellable, error))
     return FALSE;
 
-  gnutls_init (&priv->session, flags);
-
-  gnutls_session_set_ptr (priv->session, gnutls);
-  gnutls_session_set_verify_function (priv->session, verify_certificate_cb);
-
-  status = gnutls_credentials_set (priv->session,
-                                   GNUTLS_CRD_CERTIFICATE,
-                                   priv->creds);
-  if (status != 0)
-    {
-      g_set_error (error, G_TLS_ERROR, G_TLS_ERROR_MISC,
-                   _("Could not create TLS connection: %s"),
-                   gnutls_strerror (status));
-      return FALSE;
-    }
-
-  gnutls_transport_set_push_function (priv->session,
-                                      g_tls_connection_gnutls_push_func);
-  gnutls_transport_set_pull_function (priv->session,
-                                      g_tls_connection_gnutls_pull_func);
-  /* FIXME: remove timeout func and switch to GNUTLS_NONBLOCK */
-  gnutls_transport_set_pull_timeout_function (priv->session,
-                                              g_tls_connection_gnutls_pull_timeout_func);
-  gnutls_transport_set_ptr (priv->session, gnutls);
-
-  /* GDatagramBased supports vectored I/O; GPollableOutputStream does not. */
-  if (base_socket)
-    {
-      gnutls_transport_set_vec_push_function (priv->session,
-                                              g_tls_connection_gnutls_vec_push_func);
-    }
-
-  /* Set reasonable MTU */
-  if (flags & GNUTLS_DATAGRAM)
-    gnutls_dtls_set_mtu (priv->session, 1400);
+  /* FIXME bad */
+  priv->session = g_tls_operations_thread_gnutls_get_session (G_TLS_OPERATIONS_THREAD_GNUTLS 
(g_tls_connection_base_get_op_thread (G_TLS_CONNECTION_BASE (gnutls))));
 
-  return g_tls_connection_gnutls_parent_initable_iface->init (initable, cancellable, error);
+  return TRUE;
 }
 
 static void
@@ -188,11 +125,6 @@ g_tls_connection_gnutls_finalize (GObject *object)
   GTlsConnectionGnutls *gnutls = G_TLS_CONNECTION_GNUTLS (object);
   GTlsConnectionGnutlsPrivate *priv = g_tls_connection_gnutls_get_instance_private (gnutls);
 
-  if (priv->session)
-    gnutls_deinit (priv->session);
-  if (priv->creds)
-    gnutls_certificate_free_credentials (priv->creds);
-
   if (priv->cancellable)
     {
       g_cancellable_cancel (priv->cancellable);
@@ -209,15 +141,7 @@ g_tls_connection_gnutls_get_credentials (GTlsConnectionGnutls *gnutls)
 {
   GTlsConnectionGnutlsPrivate *priv = g_tls_connection_gnutls_get_instance_private (gnutls);
 
-  return priv->creds;
-}
-
-gnutls_session_t
-g_tls_connection_gnutls_get_session (GTlsConnectionGnutls *gnutls)
-{
-  GTlsConnectionGnutlsPrivate *priv = g_tls_connection_gnutls_get_instance_private (gnutls);
-
-  return priv->session;
+  return priv->creds; /* FIXME: get via op thread? */
 }
 
 static int
@@ -316,489 +240,35 @@ g_tls_connection_gnutls_handshake_thread_get_certificate (GTlsConnectionGnutls
     }
 }
 
-// FIXME: remove
-static GTlsConnectionBaseStatus
-end_gnutls_io (GTlsConnectionGnutls  *gnutls,
-               GIOCondition           direction,
-               int                    ret,
-               GError               **error,
-               const char            *err_prefix)
-{
-  GTlsConnectionGnutlsPrivate *priv = g_tls_connection_gnutls_get_instance_private (gnutls);
-  GTlsConnectionBase *tls = G_TLS_CONNECTION_BASE (gnutls);
-  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;
-
-  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 (gnutls)))
-        {
-          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 (priv->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 (priv->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;
-}
-
-// FIXME: remove
-
-#define BEGIN_GNUTLS_IO(gnutls, direction, timeout, cancellable)        \
-  g_tls_connection_base_push_io (G_TLS_CONNECTION_BASE (gnutls),        \
-                                 direction, timeout, cancellable);      \
-  do {
-
-#define END_GNUTLS_IO(gnutls, direction, ret, status, errmsg, err)      \
-    status = end_gnutls_io (gnutls, direction, ret, err, errmsg);       \
-  } while (status == G_TLS_CONNECTION_BASE_TRY_AGAIN);
-
-static void
-set_gnutls_error (GTlsConnectionGnutls *gnutls,
-                  GError               *error)
-{
-  GTlsConnectionBase *tls = G_TLS_CONNECTION_BASE (gnutls);
-  GTlsConnectionGnutlsPrivate *priv = g_tls_connection_gnutls_get_instance_private (gnutls);
-
-  /* We set EINTR rather than EAGAIN for G_IO_ERROR_WOULD_BLOCK so
-   * that GNUTLS_E_AGAIN only gets returned for gnutls-internal
-   * reasons, not for actual socket EAGAINs (and we have access
-   * to @error at the higher levels, so we can distinguish them
-   * that way later).
-   */
-
-  if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
-    gnutls_transport_set_errno (priv->session, EINTR);
-  else if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK))
-    {
-      /* Return EAGAIN while handshaking so that GnuTLS handles retries for us
-       * internally in its handshaking code. */
-      if (g_tls_connection_base_is_dtls (tls) && g_tls_connection_base_is_handshaking (tls))
-        gnutls_transport_set_errno (priv->session, EAGAIN);
-      else
-        gnutls_transport_set_errno (priv->session, EINTR);
-    }
-  else if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_TIMED_OUT))
-    gnutls_transport_set_errno (priv->session, EINTR);
-  else if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_MESSAGE_TOO_LARGE))
-    gnutls_transport_set_errno (priv->session, EMSGSIZE);
-  else
-    gnutls_transport_set_errno (priv->session, EIO);
-}
-
-/* FIXME: remove timeouts, make these always nonblocking */
-
-static ssize_t
-g_tls_connection_gnutls_pull_func (gnutls_transport_ptr_t  transport_data,
-                                   void                   *buf,
-                                   size_t                  buflen)
-{
-  GTlsConnectionBase *tls = transport_data;
-  GTlsConnectionGnutls *gnutls = transport_data;
-  ssize_t ret;
-
-  /* If read_error is nonnull when we're called, it means
-   * that an error previously occurred, but GnuTLS decided not to
-   * propagate it. So it's correct for us to just clear it. (Usually
-   * this means it ignored an EAGAIN after a short read, and now
-   * we'll return EAGAIN again, which it will obey this time.)
-   */
-  g_clear_error (g_tls_connection_base_get_read_error (tls));
-
-  if (g_tls_connection_base_is_dtls (tls))
-    {
-      GInputVector vector = { buf, buflen };
-      GInputMessage message = { NULL, &vector, 1, 0, 0, NULL, NULL };
-
-      ret = g_datagram_based_receive_messages (g_tls_connection_base_get_base_socket (tls),
-                                               &message, 1, 0,
-                                               g_tls_connection_base_is_handshaking (tls) ? 0 : 
g_tls_connection_base_get_read_timeout (tls),
-                                               g_tls_connection_base_get_read_cancellable (tls),
-                                               g_tls_connection_base_get_read_error (tls));
-
-      if (ret > 0)
-        ret = message.bytes_received;
-    }
-  else
-    {
-      ret = g_pollable_stream_read (G_INPUT_STREAM (g_tls_connection_base_get_base_istream (tls)),
-                                    buf, buflen,
-                                    g_tls_connection_base_get_read_timeout (tls) != 0,
-                                    g_tls_connection_base_get_read_cancellable (tls),
-                                    g_tls_connection_base_get_read_error (tls));
-    }
-
-  if (ret < 0)
-    set_gnutls_error (gnutls, *g_tls_connection_base_get_read_error (tls));
-
-  return ret;
-}
-
-static ssize_t
-g_tls_connection_gnutls_push_func (gnutls_transport_ptr_t  transport_data,
-                                   const void             *buf,
-                                   size_t                  buflen)
-{
-  GTlsConnectionBase *tls = transport_data;
-  GTlsConnectionGnutls *gnutls = transport_data;
-  ssize_t ret;
-
-  /* See comment in pull_func. */
-  g_clear_error (g_tls_connection_base_get_write_error (tls));
-
-  if (g_tls_connection_base_is_dtls (tls))
-    {
-      GOutputVector vector = { buf, buflen };
-      GOutputMessage message = { NULL, &vector, 1, 0, NULL, 0 };
-
-      ret = g_datagram_based_send_messages (g_tls_connection_base_get_base_socket (tls),
-                                            &message, 1, 0,
-                                            g_tls_connection_base_get_write_timeout (tls),
-                                            g_tls_connection_base_get_write_cancellable (tls),
-                                            g_tls_connection_base_get_write_error (tls));
-
-      if (ret > 0)
-        ret = message.bytes_sent;
-    }
-  else
-    {
-      ret = g_pollable_stream_write (G_OUTPUT_STREAM (g_tls_connection_base_get_base_ostream (tls)),
-                                     buf, buflen,
-                                     g_tls_connection_base_get_write_timeout (tls) != 0,
-                                     g_tls_connection_base_get_write_cancellable (tls),
-                                     g_tls_connection_base_get_write_error (tls));
-    }
-
-  if (ret < 0)
-    set_gnutls_error (gnutls, *g_tls_connection_base_get_write_error (tls));
-
-  return ret;
-}
-
-static ssize_t
-g_tls_connection_gnutls_vec_push_func (gnutls_transport_ptr_t  transport_data,
-                                       const giovec_t         *iov,
-                                       int                     iovcnt)
-{
-  GTlsConnectionBase *tls = transport_data;
-  GTlsConnectionGnutls *gnutls = transport_data;
-  ssize_t ret;
-  GOutputMessage message = { NULL, };
-  GOutputVector *vectors;
-
-  g_assert (g_tls_connection_base_is_dtls (tls));
-
-  /* See comment in pull_func. */
-  g_clear_error (g_tls_connection_base_get_write_error (tls));
-
-  /* this entire expression will be evaluated at compile time */
-  if (sizeof *iov == sizeof *vectors &&
-      sizeof iov->iov_base == sizeof vectors->buffer &&
-      G_STRUCT_OFFSET (giovec_t, iov_base) ==
-      G_STRUCT_OFFSET (GOutputVector, buffer) &&
-      sizeof iov->iov_len == sizeof vectors->size &&
-      G_STRUCT_OFFSET (giovec_t, iov_len) ==
-      G_STRUCT_OFFSET (GOutputVector, size))
-    /* ABI is compatible */
-    {
-      message.vectors = (GOutputVector *)iov;
-      message.num_vectors = iovcnt;
-    }
-  else
-    /* ABI is incompatible */
-    {
-      gint i;
-
-      message.vectors = g_newa (GOutputVector, iovcnt);
-      for (i = 0; i < iovcnt; i++)
-        {
-          message.vectors[i].buffer = (void *)iov[i].iov_base;
-          message.vectors[i].size = iov[i].iov_len;
-        }
-      message.num_vectors = iovcnt;
-    }
-
-  ret = g_datagram_based_send_messages (g_tls_connection_base_get_base_socket (tls),
-                                        &message, 1, 0,
-                                        g_tls_connection_base_get_write_timeout (tls),
-                                        g_tls_connection_base_get_write_cancellable (tls),
-                                        g_tls_connection_base_get_write_error (tls));
-
-  if (ret > 0)
-    ret = message.bytes_sent;
-  else if (ret < 0)
-    set_gnutls_error (gnutls, *g_tls_connection_base_get_write_error (tls));
-
-  return ret;
-}
-
-static gboolean
-read_pollable_cb (GPollableInputStream *istream,
-                  gpointer              user_data)
-{
-  gboolean *read_done = user_data;
-
-  *read_done = TRUE;
-
-  return G_SOURCE_CONTINUE;
-}
-
-static gboolean
-read_datagram_based_cb (GDatagramBased *datagram_based,
-                        GIOCondition    condition,
-                        gpointer        user_data)
-{
-  gboolean *read_done = user_data;
-
-  *read_done = TRUE;
-
-  return G_SOURCE_CONTINUE;
-}
-
-static gboolean
-read_timeout_cb (gpointer user_data)
-{
-  gboolean *timed_out = user_data;
-
-  *timed_out = TRUE;
-
-  return G_SOURCE_REMOVE;
-}
-
-static int
-g_tls_connection_gnutls_pull_timeout_func (gnutls_transport_ptr_t transport_data,
-                                           unsigned int           ms)
-{
-  GTlsConnectionBase *tls = transport_data;
-
-  /* Fast path. */
-  if (g_tls_connection_base_base_check (tls, G_IO_IN) ||
-      g_cancellable_is_cancelled (g_tls_connection_base_get_read_cancellable (tls)))
-    return 1;
-
-  /* If @ms is 0, GnuTLS wants an instant response, so there’s no need to
-   * construct and query a #GSource. */
-  if (ms > 0)
-    {
-      GMainContext *ctx = NULL;
-      GSource *read_source = NULL, *timeout_source = NULL;
-      gboolean read_done = FALSE, timed_out = FALSE;
-
-      ctx = g_main_context_new ();
-
-      /* Create a timeout source. */
-      timeout_source = g_timeout_source_new (ms);
-      g_source_set_callback (timeout_source, (GSourceFunc)read_timeout_cb,
-                             &timed_out, NULL);
-
-      /* Create a read source. We cannot use g_source_set_ready_time() on this
-       * to combine it with the @timeout_source, as that could mess with the
-       * internals of the #GDatagramBased’s #GSource implementation. */
-      if (g_tls_connection_base_is_dtls (tls))
-        {
-          read_source = g_datagram_based_create_source (g_tls_connection_base_get_base_socket (tls),
-                                                        G_IO_IN, NULL);
-          g_source_set_callback (read_source, (GSourceFunc)read_datagram_based_cb,
-                                 &read_done, NULL);
-        }
-      else
-        {
-          read_source = g_pollable_input_stream_create_source (g_tls_connection_base_get_base_istream (tls),
-                                                               NULL);
-          g_source_set_callback (read_source, (GSourceFunc)read_pollable_cb,
-                                 &read_done, NULL);
-        }
-
-      g_source_attach (read_source, ctx);
-      g_source_attach (timeout_source, ctx);
-
-      while (!read_done && !timed_out)
-        g_main_context_iteration (ctx, TRUE);
-
-      g_source_destroy (read_source);
-      g_source_destroy (timeout_source);
-
-      g_main_context_unref (ctx);
-      g_source_unref (read_source);
-      g_source_unref (timeout_source);
-
-      /* If @read_source was dispatched due to cancellation, the resulting error
-       * will be handled in g_tls_connection_gnutls_pull_func(). */
-      if (g_tls_connection_base_base_check (tls, G_IO_IN) ||
-          g_cancellable_is_cancelled (g_tls_connection_base_get_read_cancellable (tls)))
-        return 1;
-    }
-
-  return 0;
-}
-
 static GTlsOperationsThreadBase *
 g_tls_connection_gnutls_create_op_thread (GTlsConnectionBase *tls)
 {
-  return g_tls_operations_thread_gnutls_new (G_TLS_CONNECTION_GNUTLS (tls));
-}
+  GIOStream *base_io_stream = NULL;
+  GDatagramBased *base_socket = NULL;
+  gboolean client = G_IS_TLS_CLIENT_CONNECTION (tls);
+  guint flags = client ? GNUTLS_CLIENT : GNUTLS_SERVER;
+  GTlsOperationsThreadGnutls *thread;
 
-static GTlsSafeRenegotiationStatus
-g_tls_connection_gnutls_handshake_thread_safe_renegotiation_status (GTlsConnectionBase *tls)
-{
-  GTlsConnectionGnutls *gnutls = G_TLS_CONNECTION_GNUTLS (tls);
-  GTlsConnectionGnutlsPrivate *priv = g_tls_connection_gnutls_get_instance_private (gnutls);
+  g_object_get (tls,
+                "base-io-stream", &base_io_stream,
+                "base-socket", &base_socket,
+                NULL);
 
-  return gnutls_safe_renegotiation_status (priv->session) ? G_TLS_SAFE_RENEGOTIATION_SUPPORTED_BY_PEER
-                                                          : G_TLS_SAFE_RENEGOTIATION_UNSUPPORTED;
-}
+  /* Ensure we are in TLS mode or DTLS mode. */
+  g_return_val_if_fail (!!base_io_stream != !!base_socket, FALSE);
 
-static GTlsConnectionBaseStatus
-g_tls_connection_gnutls_handshake_thread_request_rehandshake (GTlsConnectionBase  *tls,
-                                                              gint64               timeout,
-                                                              GCancellable        *cancellable,
-                                                              GError             **error)
-{
-  GTlsConnectionGnutls *gnutls = G_TLS_CONNECTION_GNUTLS (tls);
-  GTlsConnectionGnutlsPrivate *priv = g_tls_connection_gnutls_get_instance_private (gnutls);
-  GTlsConnectionBaseStatus status;
-  int ret;
+  if (base_socket)
+    flags |= GNUTLS_DATAGRAM;
 
-  /* On a client-side connection, gnutls_handshake() itself will start
-   * a rehandshake, so we only need to do something special here for
-   * server-side connections.
-   */
-  if (!G_IS_TLS_SERVER_CONNECTION (tls))
-    return G_TLS_CONNECTION_BASE_OK;
+  thread = g_tls_operations_thread_gnutls_new (G_TLS_CONNECTION_GNUTLS (tls),
+                                               base_io_stream,
+                                               base_socket,
+                                               flags);
 
-  BEGIN_GNUTLS_IO (gnutls, G_IO_IN | G_IO_OUT, timeout, cancellable);
-  ret = gnutls_rehandshake (priv->session);
-  END_GNUTLS_IO (gnutls, G_IO_IN | G_IO_OUT, ret, status, _("Error performing TLS handshake: %s"), error);
+  g_clear_object (&base_io_stream);
+  g_clear_object (&base_socket);
 
-  return status;
+  return thread;
 }
 
 static GTlsCertificate *
@@ -834,76 +304,6 @@ verify_certificate_cb (gnutls_session_t session)
   return !g_tls_connection_base_handshake_thread_verify_certificate (tls);
 }
 
-static void
-g_tls_connection_gnutls_prepare_handshake (GTlsConnectionBase  *tls,
-                                           gchar              **advertised_protocols)
-{
-  GTlsConnectionGnutls *gnutls = G_TLS_CONNECTION_GNUTLS (tls);
-  GTlsConnectionGnutlsPrivate *priv = g_tls_connection_gnutls_get_instance_private (gnutls);
-
-  if (advertised_protocols)
-    {
-      gnutls_datum_t *protocols;
-      int n_protos, i;
-
-      n_protos = g_strv_length (advertised_protocols);
-      protocols = g_new (gnutls_datum_t, n_protos);
-      for (i = 0; advertised_protocols[i]; i++)
-        {
-          protocols[i].size = strlen (advertised_protocols[i]);
-          protocols[i].data = (guchar *)advertised_protocols[i];
-        }
-      gnutls_alpn_set_protocols (priv->session, protocols, n_protos, 0);
-      g_free (protocols);
-    }
-}
-
-static GTlsConnectionBaseStatus
-g_tls_connection_gnutls_handshake_thread_handshake (GTlsConnectionBase  *tls,
-                                                    gint64               timeout,
-                                                    GCancellable        *cancellable,
-                                                    GError             **error)
-{
-  GTlsConnectionGnutls *gnutls = G_TLS_CONNECTION_GNUTLS (tls);
-  GTlsConnectionGnutlsPrivate *priv = g_tls_connection_gnutls_get_instance_private (gnutls);
-  GTlsConnectionBaseStatus status;
-  int ret;
-
-  if (!g_tls_connection_base_ever_handshaked (tls))
-    g_tls_connection_gnutls_set_handshake_priority (gnutls);
-
-  if (timeout > 0)
-    {
-      unsigned int timeout_ms;
-
-      /* Convert from microseconds to milliseconds, but ensure the timeout
-       * remains positive. */
-      timeout_ms = (timeout + 999) / 1000;
-
-      gnutls_handshake_set_timeout (priv->session, timeout_ms);
-      gnutls_dtls_set_timeouts (priv->session, 1000 /* default */, timeout_ms);
-    }
-
-  BEGIN_GNUTLS_IO (gnutls, G_IO_IN | G_IO_OUT, timeout, cancellable);
-  ret = gnutls_handshake (priv->session);
-  if (ret == GNUTLS_E_GOT_APPLICATION_DATA)
-    {
-      guint8 buf[1024];
-
-      /* Got app data while waiting for rehandshake; buffer it and try again */
-      ret = gnutls_record_recv (priv->session, buf, sizeof (buf));
-      if (ret > -1)
-        {
-          g_tls_connection_base_handshake_thread_buffer_application_data (tls, buf, ret);
-          ret = GNUTLS_E_AGAIN;
-        }
-    }
-  END_GNUTLS_IO (gnutls, G_IO_IN | G_IO_OUT, ret, status,
-                 _("Error performing TLS handshake"), error);
-
-  return status;
-}
-
 static void
 g_tls_connection_gnutls_complete_handshake (GTlsConnectionBase  *tls,
                                             gchar              **negotiated_protocol,
@@ -961,10 +361,7 @@ g_tls_connection_gnutls_class_init (GTlsConnectionGnutlsClass *klass)
   gobject_class->finalize                                = g_tls_connection_gnutls_finalize;
 
   base_class->create_op_thread                           = g_tls_connection_gnutls_create_op_thread;
-  base_class->prepare_handshake                          = g_tls_connection_gnutls_prepare_handshake;
   base_class->handshake_thread_safe_renegotiation_status = 
g_tls_connection_gnutls_handshake_thread_safe_renegotiation_status;
-  base_class->handshake_thread_request_rehandshake       = 
g_tls_connection_gnutls_handshake_thread_request_rehandshake;
-  base_class->handshake_thread_handshake                 = 
g_tls_connection_gnutls_handshake_thread_handshake;
   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;
diff --git a/tls/gnutls/gtlsconnection-gnutls.h b/tls/gnutls/gtlsconnection-gnutls.h
index 3e9fa9b..edb617f 100644
--- a/tls/gnutls/gtlsconnection-gnutls.h
+++ b/tls/gnutls/gtlsconnection-gnutls.h
@@ -43,9 +43,6 @@ 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,
                                                                        gnutls_pcert_st      **pcert,
                                                                        unsigned int          *pcert_length,
diff --git a/tls/gnutls/gtlsoperationsthread-gnutls.c b/tls/gnutls/gtlsoperationsthread-gnutls.c
index 11b1bc9..c6559d6 100644
--- a/tls/gnutls/gtlsoperationsthread-gnutls.c
+++ b/tls/gnutls/gtlsoperationsthread-gnutls.c
@@ -30,19 +30,52 @@
 
 #include "gtlsconnection-gnutls.h"
 
+#include <errno.h>
 #include <glib/gi18n-lib.h>
 #include <gnutls/dtls.h>
+#include <limits.h>
 
 struct _GTlsOperationsThreadGnutls {
   GTlsOperationsThreadBase parent_instance;
 
+  guint                            init_flags;
+  gnutls_certificate_credentials_t creds; /* owned by GTlsConnectionGnutls */
+
   gnutls_session_t         session;
+
+  GIOStream               *base_iostream;
+  GInputStream            *base_istream;
+  GOutputStream           *base_ostream;
+  GDatagramBased          *base_socket;
+
+  gboolean                 handshaking;
+  gboolean                 ever_handshaked;
+
+  GCancellable            *op_cancellable;
+  GError                  *op_error;
+};
+
+enum
+{
+  PROP_0,
+  PROP_BASE_IO_STREAM,
+  PROP_BASE_SOCKET,
+  PROP_GNUTLS_FLAGS,
+  LAST_PROP
 };
 
+static GParamSpec *obj_properties[LAST_PROP];
+
 static gnutls_priority_t priority;
 
 G_DEFINE_TYPE (GTlsOperationsThreadGnutls, g_tls_operations_thread_gnutls, G_TYPE_TLS_OPERATIONS_THREAD_BASE)
 
+static inline gboolean
+is_dtls (GTlsOperationsThreadGnutls *self)
+{
+  return self->init_flags & GNUTLS_DATAGRAM;
+}
+
 static GTlsConnectionBaseStatus
 end_gnutls_io (GTlsOperationsThreadGnutls  *self,
                GIOCondition                 direction,
@@ -52,8 +85,6 @@ end_gnutls_io (GTlsOperationsThreadGnutls  *self,
 {
   GTlsConnectionBase *tls;
   GTlsConnectionBaseStatus status;
-  gboolean handshaking;
-  gboolean ever_handshaked;
   GError *my_error = NULL;
 
   /* We intentionally do not check for GNUTLS_E_INTERRUPTED here
@@ -80,10 +111,7 @@ end_gnutls_io (GTlsOperationsThreadGnutls  *self,
 
   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 (self->handshaking && !self->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))
@@ -110,7 +138,7 @@ end_gnutls_io (GTlsOperationsThreadGnutls  *self,
 
   if (ret == GNUTLS_E_PREMATURE_TERMINATION)
     {
-      if (handshaking && !ever_handshaked)
+      if (self->handshaking && !self->ever_handshaked)
         {
           g_clear_error (&my_error);
           g_set_error (error, G_TLS_ERROR, G_TLS_ERROR_NOT_TLS,
@@ -193,14 +221,24 @@ end_gnutls_io (GTlsOperationsThreadGnutls  *self,
   return G_TLS_CONNECTION_BASE_ERROR;
 }
 
+/* FIXME: do not use GTlsConnectionBase at all. */
+
 #define BEGIN_GNUTLS_IO(self, direction, cancellable)          \
+  g_assert (!self->op_error);                                  \
+  g_assert (!self->op_cancellable);                            \
+  self->op_cancellable = cancellable;                          \
   g_tls_connection_base_push_io (g_tls_operations_thread_base_get_connection (G_TLS_OPERATIONS_THREAD_BASE 
(self)),        \
-                                 direction, 0, cancellable);    \
+                                 direction, 0, 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);
+  } while (status == G_TLS_CONNECTION_BASE_TRY_AGAIN);                \
+  self->op_cancellable = NULL;                                        \
+  if (self->op_error) {                                               \
+    g_propagate_error (err, self->op_error);                          \
+    self->op_error = NULL;                                            \
+  }
 
 static void
 initialize_gnutls_priority (void)
@@ -237,11 +275,91 @@ set_handshake_priority (GTlsOperationsThreadGnutls *self)
     g_warning ("Failed to set GnuTLS session priority: %s", gnutls_strerror (ret));
 }
 
+static void
+compute_session_id (GTlsOperationsThreadGnutls *self)
+{
+  GSocketAddress *remote_addr;
+  GInetAddress *iaddr;
+  guint port;
+
+  /* The testsuite expects handshakes to actually happen. E.g. a test might
+   * check to see that a handshake succeeds and then later check that a new
+   * handshake fails. If we get really unlucky and the same port number is
+   * reused for the server socket between connections, then we'll accidentally
+   * resume the old session and skip certificate verification. Such failures
+   * are difficult to debug because they require running the tests hundreds of
+   * times simultaneously to reproduce (the port number does not get reused
+   * quickly enough if the tests are run sequentially).
+   *
+   * So session resumption will just need to be tested manually.
+   */
+  if (g_test_initialized ())
+    return;
+
+  /* Create a TLS "session ID." We base it on the IP address since
+   * different hosts serving the same hostname/service will probably
+   * not share the same session cache. We base it on the
+   * server-identity because at least some servers will fail (rather
+   * than just failing to resume the session) if we don't.
+   * (https://bugs.launchpad.net/bugs/823325)
+   *
+   * Note that our session IDs have no relation to TLS protocol
+   * session IDs, e.g. as provided by gnutls_session_get_id2(). Unlike
+   * our session IDs, actual TLS session IDs can no longer be used for
+   * session resumption.
+   */
+  if (G_IS_SOCKET_CONNECTION (self->base_iostream))
+    {
+      remote_addr = g_socket_connection_get_remote_address (G_SOCKET_CONNECTION (self->base_iostream), NULL);
+      if (G_IS_INET_SOCKET_ADDRESS (remote_addr))
+        {
+          GInetSocketAddress *isaddr = G_INET_SOCKET_ADDRESS (remote_addr);
+          const gchar *server_hostname;
+          gchar *addrstr, *session_id;
+          GTlsCertificate *cert = NULL;
+          gchar *cert_hash = NULL;
+
+          iaddr = g_inet_socket_address_get_address (isaddr);
+          port = g_inet_socket_address_get_port (isaddr);
+
+          addrstr = g_inet_address_to_string (iaddr);
+          server_hostname = get_server_identity (gnutls);
+
+          /* If we have a certificate, make its hash part of the session ID, so
+           * that different connections to the same server can use different
+           * certificates.
+           */
+          g_object_get (G_OBJECT (gnutls), "certificate", &cert, NULL);
+          if (cert)
+            {
+              GByteArray *der = NULL;
+              g_object_get (G_OBJECT (cert), "certificate", &der, NULL);
+              if (der)
+                {
+                  cert_hash = g_compute_checksum_for_data (G_CHECKSUM_SHA256, der->data, der->len);
+                  g_byte_array_unref (der);
+                }
+              g_object_unref (cert);
+            }
+          session_id = g_strdup_printf ("%s/%s/%d/%s", addrstr,
+                                        server_hostname ? server_hostname : "",
+                                        port,
+                                        cert_hash ? cert_hash : "");
+          gnutls->session_id = g_bytes_new_take (session_id, strlen (session_id));
+          g_free (addrstr);
+          g_free (cert_hash);
+        }
+      g_object_unref (remote_addr);
+    }
+  g_clear_object (&base_conn);
+}
+
 static GTlsConnectionBaseStatus
-g_tls_operations_thread_gnutls_handshake (GTlsOperationsThreadBase *base,
-                                          gint64                    timeout,
-                                          GCancellable             *cancellable,
-                                          GError                  **error)
+g_tls_operations_thread_gnutls_handshake (GTlsOperationsThreadBase  *base,
+                                          gchar                    **advertised_protocols,
+                                          gint64                     timeout,
+                                          GCancellable              *cancellable,
+                                          GError                   **error)
 {
   GTlsOperationsThreadGnutls *self = G_TLS_OPERATIONS_THREAD_GNUTLS (base);
   GTlsConnectionBase *tls;
@@ -250,7 +368,7 @@ g_tls_operations_thread_gnutls_handshake (GTlsOperationsThreadBase *base,
 
   tls = g_tls_operations_thread_base_get_connection (base);
 
-  if (!g_tls_connection_base_ever_handshaked (tls))
+  if (!self->ever_handshaked)
     set_handshake_priority (self);
 
   if (timeout > 0)
@@ -258,13 +376,32 @@ g_tls_operations_thread_gnutls_handshake (GTlsOperationsThreadBase *base,
       unsigned int timeout_ms;
 
       /* Convert from microseconds to milliseconds, but ensure the timeout
-       * remains positive. */
+       * remains positive.
+       */
       timeout_ms = (timeout + 999) / 1000;
 
       gnutls_handshake_set_timeout (self->session, timeout_ms);
       gnutls_dtls_set_timeouts (self->session, 1000 /* default */, timeout_ms);
     }
 
+  if (advertised_protocols)
+    {
+      gnutls_datum_t *protocols;
+      int n_protos, i;
+
+      n_protos = g_strv_length (advertised_protocols);
+      protocols = g_new (gnutls_datum_t, n_protos);
+      for (i = 0; advertised_protocols[i]; i++)
+        {
+          protocols[i].size = strlen (advertised_protocols[i]);
+          protocols[i].data = (guchar *)advertised_protocols[i];
+        }
+      gnutls_alpn_set_protocols (self->session, protocols, n_protos, 0);
+      g_free (protocols);
+    }
+
+  self->handshaking = TRUE;
+
   BEGIN_GNUTLS_IO (self, G_IO_IN | G_IO_OUT, cancellable);
   ret = gnutls_handshake (self->session);
   if (ret == GNUTLS_E_GOT_APPLICATION_DATA)
@@ -275,7 +412,7 @@ g_tls_operations_thread_gnutls_handshake (GTlsOperationsThreadBase *base,
       ret = gnutls_record_recv (self->session, buf, sizeof (buf));
       if (ret > -1)
         {
-          /* FIXME: no longer belongs in GTlsConnectionBase? */
+          /* FIXME: no longer belongs in GTlsConnectionBase, don't use GTlsConnectionBase */
           g_tls_connection_base_handshake_thread_buffer_application_data (tls, buf, ret);
           ret = GNUTLS_E_AGAIN;
         }
@@ -283,6 +420,9 @@ g_tls_operations_thread_gnutls_handshake (GTlsOperationsThreadBase *base,
   END_GNUTLS_IO (self, G_IO_IN | G_IO_OUT, ret, status,
                  _("Error performing TLS handshake"), error);
 
+  self->handshaking = FALSE;
+  self->ever_handshaked = TRUE;
+
   return status;
 }
 
@@ -465,16 +605,402 @@ g_tls_operations_thread_gnutls_close (GTlsOperationsThreadBase  *base,
   return status;
 }
 
+static void
+set_gnutls_error (GTlsOperationsThreadGnutls *self,
+                  GError                     *error)
+{
+  /* We set EINTR rather than EAGAIN for G_IO_ERROR_WOULD_BLOCK so
+   * that GNUTLS_E_AGAIN only gets returned for gnutls-internal
+   * reasons, not for actual socket EAGAINs (and we have access
+   * to @error at the higher levels, so we can distinguish them
+   * that way later).
+   */
+
+  if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+    gnutls_transport_set_errno (self->session, EINTR);
+  else if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK))
+    {
+      /* Return EAGAIN while handshaking so that GnuTLS handles retries for us
+       * internally in its handshaking code.
+       */
+      if (is_dtls (self) && self->handshaking)
+        gnutls_transport_set_errno (self->session, EAGAIN);
+      else
+        gnutls_transport_set_errno (self->session, EINTR);
+    }
+  else if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_TIMED_OUT))
+    gnutls_transport_set_errno (self->session, EINTR);
+  else if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_MESSAGE_TOO_LARGE))
+    gnutls_transport_set_errno (self->session, EMSGSIZE);
+  else
+    gnutls_transport_set_errno (self->session, EIO);
+}
+
+static ssize_t
+g_tls_operations_thread_gnutls_pull_func (gnutls_transport_ptr_t  transport_data,
+                                          void                   *buf,
+                                          size_t                  buflen)
+{
+  GTlsOperationsThreadGnutls *self = transport_data;
+  ssize_t ret;
+
+  /* If op_error is nonnull when we're called, it means
+   * that an error previously occurred, but GnuTLS decided not to
+   * propagate it. So it's correct for us to just clear it. (Usually
+   * this means it ignored an EAGAIN after a short read, and now
+   * we'll return EAGAIN again, which it will obey this time.)
+   */
+  g_clear_error (&self->op_error);
+
+  if (is_dtls (self))
+    {
+      GInputVector vector = { buf, buflen };
+      GInputMessage message = { NULL, &vector, 1, 0, 0, NULL, NULL };
+
+      ret = g_datagram_based_receive_messages (self->base_socket,
+                                               &message, 1,
+                                               0, 0,
+                                               self->op_cancellable,
+                                               &self->op_error);
+
+      if (ret > 0)
+        ret = message.bytes_received;
+    }
+  else
+    {
+      ret = g_pollable_stream_read (self->base_istream,
+                                    buf, buflen,
+                                    FALSE,
+                                    self->op_cancellable,
+                                    &self->op_error);
+    }
+
+  if (ret < 0)
+    set_gnutls_error (self, self->op_error);
+
+  return ret;
+}
+
+static ssize_t
+g_tls_operations_thread_gnutls_push_func (gnutls_transport_ptr_t  transport_data,
+                                          const void             *buf,
+                                          size_t                  buflen)
+{
+  GTlsOperationsThreadGnutls *self = transport_data;
+  ssize_t ret;
+
+  /* See comment in pull_func. */
+  g_clear_error (&self->op_error);
+
+  if (is_dtls (self))
+    {
+      GOutputVector vector = { buf, buflen };
+      GOutputMessage message = { NULL, &vector, 1, 0, NULL, 0 };
+
+      ret = g_datagram_based_send_messages (self->base_socket,
+                                            &message, 1,
+                                            0, 0,
+                                            self->op_cancellable,
+                                            &self->op_error);
+
+      if (ret > 0)
+        ret = message.bytes_sent;
+    }
+  else
+    {
+      ret = g_pollable_stream_write (self->base_ostream,
+                                     buf, buflen,
+                                     FALSE,
+                                     self->op_cancellable,
+                                     &self->op_error);
+    }
+
+  if (ret < 0)
+    set_gnutls_error (self, self->op_error);
+
+  return ret;
+}
+
+static ssize_t
+g_tls_operations_thread_gnutls_vec_push_func (gnutls_transport_ptr_t  transport_data,
+                                              const giovec_t         *iov,
+                                              int                     iovcnt)
+{
+  GTlsOperationsThreadGnutls *self = transport_data;
+  ssize_t ret;
+  GOutputMessage message = { NULL, };
+  GOutputVector *vectors;
+
+  g_assert (is_dtls (self));
+
+  /* See comment in pull_func. */
+  g_clear_error (&self->op_error);
+
+  /* this entire expression will be evaluated at compile time */
+  if (sizeof *iov == sizeof *vectors &&
+      sizeof iov->iov_base == sizeof vectors->buffer &&
+      G_STRUCT_OFFSET (giovec_t, iov_base) ==
+      G_STRUCT_OFFSET (GOutputVector, buffer) &&
+      sizeof iov->iov_len == sizeof vectors->size &&
+      G_STRUCT_OFFSET (giovec_t, iov_len) ==
+      G_STRUCT_OFFSET (GOutputVector, size))
+    /* ABI is compatible */
+    {
+      message.vectors = (GOutputVector *)iov;
+      message.num_vectors = iovcnt;
+    }
+  else
+    /* ABI is incompatible */
+    {
+      gint i;
+
+      message.vectors = g_newa (GOutputVector, iovcnt);
+      for (i = 0; i < iovcnt; i++)
+        {
+          message.vectors[i].buffer = (void *)iov[i].iov_base;
+          message.vectors[i].size = iov[i].iov_len;
+        }
+      message.num_vectors = iovcnt;
+    }
+
+  ret = g_datagram_based_send_messages (self->base_socket,
+                                        &message, 1,
+                                        0, 0,
+                                        self->op_cancellable,
+                                        &self->op_error);
+
+  if (ret > 0)
+    ret = message.bytes_sent;
+  else if (ret < 0)
+    set_gnutls_error (self, self->op_error);
+
+  return ret;
+}
+
+static gboolean
+read_pollable_cb (GPollableInputStream *istream,
+                  gpointer              user_data)
+{
+  gboolean *read_done = user_data;
+
+  *read_done = TRUE;
+
+  return G_SOURCE_CONTINUE;
+}
+
+static gboolean
+read_datagram_based_cb (GDatagramBased *datagram_based,
+                        GIOCondition    condition,
+                        gpointer        user_data)
+{
+  gboolean *read_done = user_data;
+
+  *read_done = TRUE;
+
+  return G_SOURCE_CONTINUE;
+}
+
+static gboolean
+read_timeout_cb (gpointer user_data)
+{
+  gboolean *timed_out = user_data;
+
+  *timed_out = TRUE;
+
+  return G_SOURCE_REMOVE;
+}
+
+static int /* FIXME: can this be removed? switch to GNUTLS_NONBLOCK, right? */
+g_tls_operations_thread_gnutls_pull_timeout_func (gnutls_transport_ptr_t transport_data,
+                                                  unsigned int           ms)
+{
+  GTlsOperationsThreadGnutls *self = transport_data;
+  /* FIXME: don't use GTlsConnection */
+  GTlsConnectionBase *tls = g_tls_operations_thread_base_get_connection (G_TLS_OPERATIONS_THREAD_BASE 
(self));
+
+  /* Fast path. */
+  if (g_tls_connection_base_base_check (tls, G_IO_IN) ||
+      g_cancellable_is_cancelled (self->op_cancellable))
+    return 1;
+
+  /* If @ms is 0, GnuTLS wants an instant response, so there’s no need to
+   * construct and query a #GSource. */
+  if (ms > 0)
+    {
+      GMainContext *ctx = NULL;
+      GSource *read_source = NULL, *timeout_source = NULL;
+      gboolean read_done = FALSE, timed_out = FALSE;
+
+      ctx = g_main_context_new ();
+
+      /* Create a timeout source. */
+      timeout_source = g_timeout_source_new (ms);
+      g_source_set_callback (timeout_source, (GSourceFunc)read_timeout_cb,
+                             &timed_out, NULL);
+
+      /* Create a read source. We cannot use g_source_set_ready_time() on this
+       * to combine it with the @timeout_source, as that could mess with the
+       * internals of the #GDatagramBased’s #GSource implementation. */
+      if (is_dtls (self))
+        {
+          read_source = g_datagram_based_create_source (self->base_socket,
+                                                        G_IO_IN, NULL);
+          g_source_set_callback (read_source, (GSourceFunc)read_datagram_based_cb,
+                                 &read_done, NULL);
+        }
+      else
+        {
+          read_source = g_pollable_input_stream_create_source (G_POLLABLE_INPUT_STREAM (self->base_istream),
+                                                               NULL);
+          g_source_set_callback (read_source, (GSourceFunc)read_pollable_cb,
+                                 &read_done, NULL);
+        }
+
+      g_source_attach (read_source, ctx);
+      g_source_attach (timeout_source, ctx);
+
+      while (!read_done && !timed_out)
+        g_main_context_iteration (ctx, TRUE);
+
+      g_source_destroy (read_source);
+      g_source_destroy (timeout_source);
+
+      g_main_context_unref (ctx);
+      g_source_unref (read_source);
+      g_source_unref (timeout_source);
+
+      /* If @read_source was dispatched due to cancellation, the resulting error
+       * will be handled in g_tls_connection_gnutls_pull_func(). */
+      if (g_tls_connection_base_base_check (tls, G_IO_IN) ||
+          g_cancellable_is_cancelled (self->op_cancellable))
+        return 1;
+    }
+
+  return 0;
+}
+
+static void
+g_tls_operations_thread_gnutls_get_property (GObject    *object,
+                                             guint       prop_id,
+                                             GValue     *value,
+                                             GParamSpec *pspec)
+{
+  GTlsOperationsThreadGnutls *self = G_TLS_OPERATIONS_THREAD_GNUTLS (object);
+
+  switch (prop_id)
+    {
+    case PROP_BASE_IO_STREAM:
+      g_value_set_object (value, self->base_iostream);
+      break;
+
+    case PROP_BASE_SOCKET:
+      g_value_set_object (value, self->base_socket);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+g_tls_operations_thread_gnutls_set_property (GObject      *object,
+                                             guint         prop_id,
+                                             const GValue *value,
+                                             GParamSpec   *pspec)
+{
+  GTlsOperationsThreadGnutls *self = G_TLS_OPERATIONS_THREAD_GNUTLS (object);
+
+  switch (prop_id)
+    {
+    case PROP_BASE_IO_STREAM:
+      g_assert (!self->base_socket);
+      self->base_iostream = g_value_get_object (value);
+      self->base_istream = g_io_stream_get_input_stream (self->base_iostream);
+      self->base_ostream = g_io_stream_get_output_stream (self->base_iostream);
+      break;
+
+    case PROP_BASE_SOCKET:
+      g_assert (!self->base_iostream);
+      self->base_socket = g_value_get_object (value);
+      break;
+
+    case PROP_GNUTLS_FLAGS:
+      self->init_flags = g_value_get_uint (value);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+g_tls_operations_thread_gnutls_finalize (GObject *object)
+{
+  GTlsOperationsThreadGnutls *self = G_TLS_OPERATIONS_THREAD_GNUTLS (object);
+
+  if (self->session)
+    gnutls_deinit (self->session);
+  if (self->creds)
+    gnutls_certificate_free_credentials (self->creds);
+
+  G_OBJECT_CLASS (g_tls_operations_thread_gnutls_parent_class)->finalize (object);
+}
+
 static void
 g_tls_operations_thread_gnutls_constructed (GObject *object)
 {
   GTlsOperationsThreadGnutls *self = G_TLS_OPERATIONS_THREAD_GNUTLS (object);
   GTlsConnectionBase *tls;
+  int ret;
 
   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));
+
+  ret = gnutls_certificate_allocate_credentials (&self->creds);
+  /* FIXME: GInitable? */
+#if 0
+  if (ret != GNUTLS_E_SUCCESS)
+    return FALSE;
+#endif
+
+  gnutls_init (&self->session, self->init_flags);
+
+  gnutls_session_set_ptr (self->session, self);
+  gnutls_session_set_verify_function (self->session, verify_certificate_cb);
+
+  ret = gnutls_credentials_set (self->session,
+                                GNUTLS_CRD_CERTIFICATE,
+                                self->creds);
+  /* FIXME: GInitable? */
+#if 0
+  if (ret != 0)
+    {
+      g_set_error (error, G_TLS_ERROR, G_TLS_ERROR_MISC,
+                   _("Could not create TLS connection: %s"),
+                   gnutls_strerror (status));
+      return FALSE;
+    }
+#endif
+
+  gnutls_transport_set_push_function (self->session,
+                                      g_tls_operations_thread_gnutls_push_func);
+  gnutls_transport_set_pull_function (self->session,
+                                      g_tls_operations_thread_gnutls_pull_func);
+  /* FIXME: remove timeout func and switch to GNUTLS_NONBLOCK */
+  gnutls_transport_set_pull_timeout_function (self->session,
+                                              g_tls_operations_thread_gnutls_pull_timeout_func);
+  gnutls_transport_set_ptr (self->session, self);
+
+  if (is_dtls (self))
+    {
+      /* GDatagramBased supports vectored I/O; GPollableOutputStream does not. */
+      gnutls_transport_set_vec_push_function (self->session,
+                                              g_tls_operations_thread_gnutls_vec_push_func);
+
+      /* Set reasonable MTU */
+      gnutls_dtls_set_mtu (self->session, 1400);
+    }
 }
 
 static void
@@ -489,6 +1015,9 @@ g_tls_operations_thread_gnutls_class_init (GTlsOperationsThreadGnutlsClass *klas
   GTlsOperationsThreadBaseClass *base_class = G_TLS_OPERATIONS_THREAD_BASE_CLASS (klass);
 
   gobject_class->constructed   = g_tls_operations_thread_gnutls_constructed;
+  gobject_class->finalize      = g_tls_operations_thread_gnutls_finalize;
+  gobject_class->get_property  = g_tls_operations_thread_gnutls_get_property;
+  gobject_class->set_property  = g_tls_operations_thread_gnutls_set_property;
 
   base_class->handshake_fn     = g_tls_operations_thread_gnutls_handshake;
   base_class->read_fn          = g_tls_operations_thread_gnutls_read;
@@ -497,13 +1026,49 @@ g_tls_operations_thread_gnutls_class_init (GTlsOperationsThreadGnutlsClass *klas
   base_class->write_message_fn = g_tls_operations_thread_gnutls_write_message;
   base_class->close_fn         = g_tls_operations_thread_gnutls_close;
 
+  obj_properties[PROP_BASE_IO_STREAM] =
+    g_param_spec_object ("base-io-stream",
+                         "Base IOStream",
+                         "The underlying GIOStream, for TLS connections",
+                         G_TYPE_IO_STREAM,
+                         G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
+
+  obj_properties[PROP_BASE_SOCKET] =
+    g_param_spec_object ("base-socket",
+                         "Base socket",
+                         "The underlying GDatagramBased, for DTLS connections",
+                         G_TYPE_DATAGRAM_BASED,
+                         G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
+
+  obj_properties[PROP_GNUTLS_FLAGS] =
+    g_param_spec_uint ("gnutls-flags",
+                       "GnuTLS flags",
+                       "Flags for initializing GnuTLS session",
+                       0, UINT_MAX, 0,
+                       G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
+
+  g_object_class_install_properties (gobject_class, LAST_PROP, obj_properties);
+
   initialize_gnutls_priority ();
 }
 
 GTlsOperationsThreadBase *
-g_tls_operations_thread_gnutls_new (GTlsConnectionGnutls *tls)
+g_tls_operations_thread_gnutls_new (GTlsConnectionGnutls *connection,
+                                    GIOStream            *base_iostream,
+                                    GDatagramBased       *base_socket,
+                                    guint                 flags)
 {
   return G_TLS_OPERATIONS_THREAD_BASE (g_object_new (G_TYPE_TLS_OPERATIONS_THREAD_GNUTLS,
-                                                     "tls-connection", tls,
+                                                     "base-iostream", base_iostream,
+                                                     "base-socket", base_socket,
+                                                     "gnutls-flags", flags,
+                                                     "tls-connection", connection,
                                                      NULL));
 }
+
+/* FIXME: must remove this! */
+gnutls_session_t
+g_tls_operations_thread_gnutls_get_session (GTlsOperationsThreadGnutls *self)
+{
+  return self->session;
+}
diff --git a/tls/gnutls/gtlsoperationsthread-gnutls.h b/tls/gnutls/gtlsoperationsthread-gnutls.h
index b3fce2d..7637a42 100644
--- a/tls/gnutls/gtlsoperationsthread-gnutls.h
+++ b/tls/gnutls/gtlsoperationsthread-gnutls.h
@@ -35,6 +35,12 @@ G_BEGIN_DECLS
 
 G_DECLARE_FINAL_TYPE (GTlsOperationsThreadGnutls, g_tls_operations_thread_gnutls, G, 
TLS_OPERATIONS_THREAD_GNUTLS, GTlsOperationsThreadBase)
 
-GTlsOperationsThreadBase *g_tls_operations_thread_gnutls_new (GTlsConnectionGnutls *tls);
+GTlsOperationsThreadBase *g_tls_operations_thread_gnutls_new (GTlsConnectionGnutls *connection,
+                                                              GIOStream            *base_iostream,
+                                                              GDatagramBased       *base_socket,
+                                                              guint                 flags);
+
+/* FIXME: must remove this!!! */
+gnutls_session_t g_tls_operations_thread_gnutls_get_session (GTlsOperationsThreadGnutls *self);
 
 G_END_DECLS
diff --git a/tls/openssl/gtlsconnection-openssl.c b/tls/openssl/gtlsconnection-openssl.c
index b06465e..bb9f507 100644
--- a/tls/openssl/gtlsconnection-openssl.c
+++ b/tls/openssl/gtlsconnection-openssl.c
@@ -59,191 +59,6 @@ g_tls_connection_openssl_create_op_thread (GTlsConnectionBase *tls)
   return g_tls_operations_thread_openssl_new (G_TLS_CONNECTION_OPENSSL (tls));
 }
 
-static GTlsSafeRenegotiationStatus
-g_tls_connection_openssl_handshake_thread_safe_renegotiation_status (GTlsConnectionBase *tls)
-{
-  GTlsConnectionOpenssl *openssl = G_TLS_CONNECTION_OPENSSL (tls);
-  SSL *ssl;
-
-  ssl = g_tls_connection_openssl_get_ssl (openssl);
-
-  return SSL_get_secure_renegotiation_support (ssl) ? G_TLS_SAFE_RENEGOTIATION_SUPPORTED_BY_PEER
-                                                    : G_TLS_SAFE_RENEGOTIATION_UNSUPPORTED;
-}
-
-static GTlsConnectionBaseStatus
-end_openssl_io (GTlsConnectionOpenssl  *openssl,
-                GIOCondition            direction,
-                int                     ret,
-                gboolean                blocking,
-                GError                **error,
-                const char             *err_prefix,
-                const char             *err_str)
-{
-  GTlsConnectionBase *tls = G_TLS_CONNECTION_BASE (openssl);
-  int err_code, err, err_lib, reason;
-  GError *my_error = NULL;
-  GTlsConnectionBaseStatus status;
-  SSL *ssl;
-
-  ssl = g_tls_connection_openssl_get_ssl (openssl);
-
-  err_code = SSL_get_error (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 */
-  /* FIXME: broke this, but entire func should be deleted so OK */
-  if (err_code == SSL_ERROR_SYSCALL &&
-      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 (openssl) ? "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(openssl, direction, timeout, cancellable)          \
-  do {                                                                      \
-    char error_str[256];                                                    \
-    g_tls_connection_base_push_io (G_TLS_CONNECTION_BASE (openssl),         \
-                                   direction, timeout, cancellable);
-
-#define END_OPENSSL_IO(openssl, direction, ret, timeout, status, errmsg, err) \
-    ERR_error_string_n (SSL_get_error (ssl, ret), error_str, sizeof(error_str)); \
-    status = end_openssl_io (openssl, direction, ret, timeout == -1, err, errmsg, error_str); \
-  } while (status == G_TLS_CONNECTION_BASE_TRY_AGAIN);
-
-static GTlsConnectionBaseStatus
-g_tls_connection_openssl_handshake_thread_request_rehandshake (GTlsConnectionBase  *tls,
-                                                               gint64               timeout,
-                                                               GCancellable        *cancellable,
-                                                               GError             **error)
-{
-  GTlsConnectionOpenssl *openssl;
-  GTlsConnectionBaseStatus status;
-  SSL *ssl;
-  int ret;
-
-  /* On a client-side connection, SSL_renegotiate() itself will start
-   * a rehandshake, so we only need to do something special here for
-   * server-side connections.
-   */
-  if (!G_IS_TLS_SERVER_CONNECTION (tls))
-    return G_TLS_CONNECTION_BASE_OK;
-
-  openssl = G_TLS_CONNECTION_OPENSSL (tls);
-
-  ssl = g_tls_connection_openssl_get_ssl (openssl);
-
-  BEGIN_OPENSSL_IO (openssl, G_IO_IN | G_IO_OUT, timeout, cancellable);
-  ret = SSL_renegotiate (ssl);
-  END_OPENSSL_IO (openssl, G_IO_IN | G_IO_OUT, ret, timeout, status,
-                  _("Error performing TLS handshake"), error);
-
-  return status;
-}
-
 static GTlsCertificate *
 g_tls_connection_openssl_retrieve_peer_certificate (GTlsConnectionBase *tls)
 {
@@ -273,33 +88,7 @@ g_tls_connection_openssl_retrieve_peer_certificate (GTlsConnectionBase *tls)
   return G_TLS_CERTIFICATE (chain);
 }
 
-static GTlsConnectionBaseStatus
-g_tls_connection_openssl_handshake_thread_handshake (GTlsConnectionBase  *tls,
-                                                     gint64               timeout,
-                                                     GCancellable        *cancellable,
-                                                     GError             **error)
-{
-  GTlsConnectionOpenssl *openssl = G_TLS_CONNECTION_OPENSSL (tls);
-  GTlsConnectionBaseStatus status;
-  SSL *ssl;
-  int ret;
-
-  ssl = g_tls_connection_openssl_get_ssl (openssl);
-
-  BEGIN_OPENSSL_IO (openssl, G_IO_IN | G_IO_OUT, timeout, cancellable);
-  ret = SSL_do_handshake (ssl);
-  END_OPENSSL_IO (openssl, G_IO_IN | G_IO_OUT, ret, timeout, status,
-                  _("Error performing TLS handshake"), error);
-
-  if (ret > 0)
-    {
-      if (!g_tls_connection_base_handshake_thread_verify_certificate (G_TLS_CONNECTION_BASE (openssl)))
-        return G_TLS_CONNECTION_BASE_ERROR;
-    }
-
-  return status;
-}
-
+/* FIXME: move to op thread? */
 static void
 g_tls_connection_openssl_push_io (GTlsConnectionBase *tls,
                                   GIOCondition        direction,
@@ -310,13 +99,11 @@ g_tls_connection_openssl_push_io (GTlsConnectionBase *tls,
   GTlsConnectionOpensslPrivate *priv;
   GError **error;
 
-  priv = g_tls_connection_openssl_get_instance_private (openssl);
-
-  G_TLS_CONNECTION_BASE_CLASS (g_tls_connection_openssl_parent_class)->push_io (tls, direction,
-                                                                                timeout, cancellable);
+  priv = g_tls_connection_openssl_get_instance_private (openssl);;
 
   /* FIXME: need to support timeout > 0
-   * This will require changes in GTlsBio */
+   * This will require changes in GTlsBio
+   */
 
   if (direction & G_IO_IN)
     {
@@ -364,11 +151,9 @@ g_tls_connection_openssl_class_init (GTlsConnectionOpensslClass *klass)
   GTlsConnectionBaseClass *base_class = G_TLS_CONNECTION_BASE_CLASS (klass);
 
   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;
-  base_class->handshake_thread_handshake                 = 
g_tls_connection_openssl_handshake_thread_handshake;
+  /* FIXME: remove */
   base_class->retrieve_peer_certificate                  = 
g_tls_connection_openssl_retrieve_peer_certificate;
-  base_class->push_io                                    = g_tls_connection_openssl_push_io;
+  base_class->push_io                                    = g_tls_connection_openssl_push_io; /* FIXME: move 
to op thread */
   base_class->pop_io                                     = g_tls_connection_openssl_pop_io;
 }
 
diff --git a/tls/openssl/gtlsconnection-openssl.h b/tls/openssl/gtlsconnection-openssl.h
index 10f93c3..ba5a6bc 100644
--- a/tls/openssl/gtlsconnection-openssl.h
+++ b/tls/openssl/gtlsconnection-openssl.h
@@ -40,9 +40,11 @@ struct _GTlsConnectionOpensslClass
 {
   GTlsConnectionBaseClass parent_class;
 
+  /* FIXME: remove this, or entire refactor is a failure */
   SSL      *(*get_ssl)          (GTlsConnectionOpenssl *connection);
 };
 
+/* FIXME: remove this, or entire refactor is a failure */
 SSL *g_tls_connection_openssl_get_ssl (GTlsConnectionOpenssl *connection);
 
 GTlsConnectionOpenssl *g_tls_connection_openssl_get_connection_from_ssl (SSL *ssl);
diff --git a/tls/openssl/gtlsoperationsthread-openssl.c b/tls/openssl/gtlsoperationsthread-openssl.c
index d7ecc0b..f3f0fd8 100644
--- a/tls/openssl/gtlsoperationsthread-openssl.c
+++ b/tls/openssl/gtlsoperationsthread-openssl.c
@@ -172,6 +172,35 @@ end_openssl_io (GTlsOperationsThreadOpenssl  *self,
     status = end_openssl_io (self, direction, ret, err, errmsg, error_str); \
   } while (status == G_TLS_CONNECTION_BASE_TRY_AGAIN);
 
+static GTlsConnectionBaseStatus
+g_tls_operations_thread_openssl_handshake (GTlsOperationsThreadBase  *base,
+                                           gint64                     timeout,
+                                           GCancellable              *cancellable,
+                                           GError                   **error)
+{
+  GTlsOperationsThreadOpenssl *self = G_TLS_OPERATIONS_THREAD_OPENSSL (base);
+  GTlsConnectionBaseStatus status;
+  int ret;
+
+  /* FIXME: doesn't respect timeout */
+
+  BEGIN_OPENSSL_IO (self, G_IO_IN | G_IO_OUT, cancellable);
+  ret = SSL_do_handshake (ssl);
+  END_OPENSSL_IO (self, G_IO_IN | G_IO_OUT, ret, status,
+                  _("Error performing TLS handshake"), error);
+
+  /* FIXME: sabotage */
+#if 0
+  if (ret > 0)
+    {
+      if (!g_tls_connection_base_handshake_thread_verify_certificate (G_TLS_CONNECTION_BASE (openssl)))
+        return G_TLS_CONNECTION_BASE_ERROR;
+    }
+#endif
+
+  return status;
+}
+
 static GTlsConnectionBaseStatus
 g_tls_operations_thread_openssl_read (GTlsOperationsThreadBase   *base,
                                       void                       *buffer,
@@ -262,6 +291,7 @@ g_tls_operations_thread_openssl_class_init (GTlsOperationsThreadOpensslClass *kl
 
   gobject_class->constructed = g_tls_operations_thread_openssl_constructed;
 
+  base_class->handshake_fn   = g_tls_operations_thread_openssl_handshake;
   base_class->read_fn        = g_tls_operations_thread_openssl_read;
   base_class->write_fn       = g_tls_operations_thread_openssl_write;
   base_class->close_fn       = g_tls_operations_thread_openssl_close;



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