[glib-networking/wip/pwithnall/dtls: 3/12] gnutls: Add support for DTLS to the GnuTLS backend



commit 94ce2649106431e38e63f221de8b27ceefcfc351
Author: Philip Withnall <philip withnall collabora co uk>
Date:   Fri Jul 3 12:05:13 2015 +0100

    gnutls: Add support for DTLS to the GnuTLS backend
    
    Implement DTLS support in the GnuTLS backend by adding it to
    GTlsConnectionGnutls, so that it supports both TLS and DTLS (despite its
    name). This means the GTlsBackendGnutls returns the same type for client
    and server connection types — the protocol to use is determined by
    whether the GTlsConnection:base-io-stream or
    GDtlsConnection:base-socket property is set on the
    GTlsConnectionGnutls. Exactly one of the two must be set.
    
    This makes the following internal API changes:
     • Implement GDtlsClientConnection on GTlsClientConnectionGnutls
     • Implement GDtlsServerConnection on GTlsServerConnectionGnutls
     • Implement GDatagramBased on GTlsConnectionGnutls
     • Implement GDtlsConnection on GTlsConnectionGnutls
    
    The patch doesn’t support vectored I/O; support will follow in a later
    commit.
    
    It includes contributions by Olivier Crête
    <olivier crete collabora com>.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=697908

 tls/gnutls/gtlsbackend-gnutls.c          |    2 +
 tls/gnutls/gtlsclientconnection-gnutls.c |   20 +-
 tls/gnutls/gtlsconnection-gnutls.c       |  743 ++++++++++++++++++++++++++----
 tls/gnutls/gtlsconnection-gnutls.h       |    1 +
 tls/gnutls/gtlsinputstream-gnutls.c      |    2 +
 tls/gnutls/gtlsoutputstream-gnutls.c     |    2 +
 tls/gnutls/gtlsserverconnection-gnutls.c |    2 +
 7 files changed, 685 insertions(+), 87 deletions(-)
---
diff --git a/tls/gnutls/gtlsbackend-gnutls.c b/tls/gnutls/gtlsbackend-gnutls.c
index 332ca05..6e7f09d 100644
--- a/tls/gnutls/gtlsbackend-gnutls.c
+++ b/tls/gnutls/gtlsbackend-gnutls.c
@@ -167,6 +167,8 @@ g_tls_backend_gnutls_interface_init (GTlsBackendInterface *iface)
   iface->get_server_connection_type = g_tls_server_connection_gnutls_get_type;
   iface->get_file_database_type =     g_tls_file_database_gnutls_get_type;
   iface->get_default_database =       g_tls_backend_gnutls_get_default_database;
+  iface->get_dtls_client_connection_type = g_tls_client_connection_gnutls_get_type;
+  iface->get_dtls_server_connection_type = g_tls_server_connection_gnutls_get_type;
 }
 
 /* Session cache support; all the details are sort of arbitrary. Note
diff --git a/tls/gnutls/gtlsclientconnection-gnutls.c b/tls/gnutls/gtlsclientconnection-gnutls.c
index d5d63fa..3cf7d91 100644
--- a/tls/gnutls/gtlsclientconnection-gnutls.c
+++ b/tls/gnutls/gtlsclientconnection-gnutls.c
@@ -45,6 +45,7 @@ enum
 static void     g_tls_client_connection_gnutls_initable_interface_init (GInitableIface  *iface);
 
 static void g_tls_client_connection_gnutls_client_connection_interface_init (GTlsClientConnectionInterface 
*iface);
+static void g_tls_client_connection_gnutls_dtls_client_connection_interface_init 
(GDtlsClientConnectionInterface *iface);
 
 static int g_tls_client_connection_gnutls_retrieve_function (gnutls_session_t             session,
                                                             const gnutls_datum_t        *req_ca_rdn,
@@ -59,7 +60,9 @@ G_DEFINE_TYPE_WITH_CODE (GTlsClientConnectionGnutls, g_tls_client_connection_gnu
                         G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,
                                                g_tls_client_connection_gnutls_initable_interface_init)
                         G_IMPLEMENT_INTERFACE (G_TYPE_TLS_CLIENT_CONNECTION,
-                                               
g_tls_client_connection_gnutls_client_connection_interface_init));
+                                               
g_tls_client_connection_gnutls_client_connection_interface_init);
+                         G_IMPLEMENT_INTERFACE (G_TYPE_DTLS_CLIENT_CONNECTION,
+                                                
g_tls_client_connection_gnutls_dtls_client_connection_interface_init));
 
 struct _GTlsClientConnectionGnutlsPrivate
 {
@@ -138,7 +141,7 @@ g_tls_client_connection_gnutls_constructed (GObject *object)
        }
       g_object_unref (remote_addr);
     }
-  g_object_unref (base_conn);
+  g_clear_object (&base_conn);
 
   if (G_OBJECT_CLASS (g_tls_client_connection_gnutls_parent_class)->constructed)
     G_OBJECT_CLASS (g_tls_client_connection_gnutls_parent_class)->constructed (object);
@@ -398,9 +401,10 @@ g_tls_client_connection_gnutls_finish_handshake (GTlsConnectionGnutls  *conn,
                                                                    (GDestroyNotify)gnutls_free,
                                                                    session_datum.data);
 
-          g_tls_backend_gnutls_store_session (GNUTLS_CLIENT,
-                                              gnutls->priv->session_id,
-                                              gnutls->priv->session_data);
+          if (gnutls->priv->session_id)
+            g_tls_backend_gnutls_store_session (GNUTLS_CLIENT,
+                                                gnutls->priv->session_id,
+                                                gnutls->priv->session_data);
         }
     }
 }
@@ -460,3 +464,9 @@ g_tls_client_connection_gnutls_initable_interface_init (GInitableIface  *iface)
 
   iface->init = g_tls_client_connection_gnutls_initable_init;
 }
+
+static void
+g_tls_client_connection_gnutls_dtls_client_connection_interface_init (GDtlsClientConnectionInterface *iface)
+{
+  /* Nothing here. */
+}
diff --git a/tls/gnutls/gtlsconnection-gnutls.c b/tls/gnutls/gtlsconnection-gnutls.c
index 3666d7e..8afaf4c 100644
--- a/tls/gnutls/gtlsconnection-gnutls.c
+++ b/tls/gnutls/gtlsconnection-gnutls.c
@@ -1,6 +1,7 @@
 /* GIO - GLib Input, Output and Streaming Library
  *
  * Copyright 2009 Red Hat, Inc
+ * Copyright 2015, 2016 Collabora, Ltd.
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -32,6 +33,7 @@
 #include "gtlsconnection-gnutls.h"
 #include "gtlsbackend-gnutls.h"
 #include "gtlscertificate-gnutls.h"
+#include "gtlsclientconnection-gnutls.h"
 #include "gtlsinputstream-gnutls.h"
 #include "gtlsoutputstream-gnutls.h"
 #include "gtlsserverconnection-gnutls.h"
@@ -53,6 +55,35 @@
 
 #include <glib/gi18n-lib.h>
 
+/*
+ * GTlsConnectionGnutls is the base abstract implementation of TLS and DTLS
+ * support, for both the client and server side of a connection. The choice
+ * between TLS and DTLS is made by setting the base-io-stream or
+ * base-socket properties — exactly one of them must be set at
+ * construction time.
+ *
+ * Client and server specific code is in the GTlsClientConnectionGnutls and
+ * GTlsServerConnectionGnutls concrete subclasses, although the line about where
+ * code is put is a little blurry, and there are various places in
+ * GTlsConnectionGnutls which check G_IS_TLS_CLIENT_CONNECTION(self) to switch
+ * to a client-only code path.
+ *
+ * This abstract class implements a lot of interfaces:
+ *  • Derived from GTlsConnection (itself from GIOStream), for TLS and streaming
+ *    communications.
+ *  • Implements GDtlsConnection and GDatagramBased, for DTLS and datagram
+ *    communications.
+ *  • Implements GInitable for failable GnuTLS initialisation.
+ *
+ * The GTlsClientConnectionGnutls and GTlsServerConnectionGnutls subclasses are
+ * both derived from GTlsConnectionGnutls (and hence GIOStream), and both
+ * implement the relevant TLS and DTLS interfaces:
+ *  • GTlsClientConnection
+ *  • GDtlsClientConnection
+ *  • GTlsServerConnection
+ *  • GDtlsServerConnection
+ */
+
 static ssize_t g_tls_connection_gnutls_push_func (gnutls_transport_ptr_t  transport_data,
                                                  const void             *buf,
                                                  size_t                  buflen);
@@ -68,6 +99,8 @@ static void     g_tls_connection_gnutls_initable_iface_init (GInitableIface  *if
 static gboolean g_tls_connection_gnutls_initable_init       (GInitable       *initable,
                                                             GCancellable    *cancellable,
                                                             GError         **error);
+static void     g_tls_connection_gnutls_dtls_connection_iface_init (GDtlsConnectionInterface *iface);
+static void     g_tls_connection_gnutls_datagram_based_iface_init  (GDatagramBasedInterface  *iface);
 
 #ifdef HAVE_PKCS11
 static P11KitPin*    on_pin_prompt_callback  (const char     *pinfile,
@@ -90,6 +123,10 @@ static gboolean finish_handshake (GTlsConnectionGnutls  *gnutls,
 G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GTlsConnectionGnutls, g_tls_connection_gnutls, G_TYPE_TLS_CONNECTION,
                                  G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,
                                                         g_tls_connection_gnutls_initable_iface_init);
+                                  G_IMPLEMENT_INTERFACE (G_TYPE_DATAGRAM_BASED,
+                                                         g_tls_connection_gnutls_datagram_based_iface_init);
+                                  G_IMPLEMENT_INTERFACE (G_TYPE_DTLS_CONNECTION,
+                                                         g_tls_connection_gnutls_dtls_connection_iface_init);
                                  g_tls_connection_gnutls_init_priorities ();
                                  );
 
@@ -97,7 +134,10 @@ G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GTlsConnectionGnutls, g_tls_connection_gnutls,
 enum
 {
   PROP_0,
+  /* For this class: */
   PROP_BASE_IO_STREAM,
+  PROP_BASE_SOCKET,
+  /* For GTlsConnection and GDtlsConnection: */
   PROP_REQUIRE_CLOSE_NOTIFY,
   PROP_REHANDSHAKE_MODE,
   PROP_USE_SYSTEM_CERTDB,
@@ -105,15 +145,29 @@ enum
   PROP_CERTIFICATE,
   PROP_INTERACTION,
   PROP_PEER_CERTIFICATE,
-  PROP_PEER_CERTIFICATE_ERRORS
+  PROP_PEER_CERTIFICATE_ERRORS,
 };
 
 struct _GTlsConnectionGnutlsPrivate
 {
+  /* When operating in stream mode.
+   * Mutually exclusive with base_socket.
+   */
   GIOStream *base_io_stream;
   GPollableInputStream *base_istream;
   GPollableOutputStream *base_ostream;
 
+  /* When operating in stream mode; when operating in datagram mode, the
+   * GTlsConnectionGnutls itself is the DTLS GDatagramBased:
+   */
+  GInputStream *tls_istream;
+  GOutputStream *tls_ostream;
+
+  /* When operating in datagram mode.
+   * Mutually exclusive with base_io_stream.
+   */
+  GDatagramBased *base_socket;  /* owned */
+
   gnutls_certificate_credentials_t creds;
   gnutls_session_t session;
 
@@ -160,9 +214,6 @@ struct _GTlsConnectionGnutlsPrivate
   gboolean read_closing, read_closed;
   gboolean write_closing, write_closed;
 
-  GInputStream *tls_istream;
-  GOutputStream *tls_ostream;
-
   GTlsInteraction *interaction;
   gchar *interaction_id;
 
@@ -314,8 +365,13 @@ g_tls_connection_gnutls_initable_init (GInitable     *initable,
   guint flags = client ? GNUTLS_CLIENT : GNUTLS_SERVER;
   int status;
 
-  g_return_val_if_fail (gnutls->priv->base_istream != NULL &&
-                       gnutls->priv->base_ostream != NULL, FALSE);
+  g_return_val_if_fail (gnutls->priv->base_socket != NULL ||
+                        (gnutls->priv->base_istream != NULL &&
+                         gnutls->priv->base_ostream != NULL), FALSE);
+
+  /* Check whether to use DTLS or TLS. */
+  if (gnutls->priv->base_socket != NULL)
+    flags |= GNUTLS_DATAGRAM;
 
   gnutls_init (&gnutls->priv->session, flags);
 
@@ -338,8 +394,12 @@ g_tls_connection_gnutls_initable_init (GInitable     *initable,
                                               g_tls_connection_gnutls_pull_timeout_func);
   gnutls_transport_set_ptr (gnutls->priv->session, gnutls);
 
-  gnutls->priv->tls_istream = g_tls_input_stream_gnutls_new (gnutls);
-  gnutls->priv->tls_ostream = g_tls_output_stream_gnutls_new (gnutls);
+  /* Create output streams if operating in streaming mode. */
+  if (!(flags & GNUTLS_DATAGRAM))
+    {
+      gnutls->priv->tls_istream = g_tls_input_stream_gnutls_new (gnutls);
+      gnutls->priv->tls_ostream = g_tls_output_stream_gnutls_new (gnutls);
+    }
 
   return TRUE;
 }
@@ -350,6 +410,7 @@ g_tls_connection_gnutls_finalize (GObject *object)
   GTlsConnectionGnutls *gnutls = G_TLS_CONNECTION_GNUTLS (object);
 
   g_clear_object (&gnutls->priv->base_io_stream);
+  g_clear_object (&gnutls->priv->base_socket);
 
   g_clear_object (&gnutls->priv->tls_istream);
   g_clear_object (&gnutls->priv->tls_ostream);
@@ -406,6 +467,10 @@ g_tls_connection_gnutls_get_property (GObject    *object,
       g_value_set_object (value, gnutls->priv->base_io_stream);
       break;
 
+    case PROP_BASE_SOCKET:
+      g_value_set_object (value, gnutls->priv->base_socket);
+      break;
+
     case PROP_REQUIRE_CLOSE_NOTIFY:
       g_value_set_boolean (value, gnutls->priv->require_close_notify);
       break;
@@ -464,6 +529,9 @@ g_tls_connection_gnutls_set_property (GObject      *object,
   switch (prop_id)
     {
     case PROP_BASE_IO_STREAM:
+      g_assert (g_value_get_object (value) == NULL ||
+                gnutls->priv->base_socket == NULL);
+
       if (gnutls->priv->base_io_stream)
        {
          g_object_unref (gnutls->priv->base_io_stream);
@@ -485,6 +553,14 @@ g_tls_connection_gnutls_set_property (GObject      *object,
        gnutls->priv->base_ostream = G_POLLABLE_OUTPUT_STREAM (ostream);
       break;
 
+    case PROP_BASE_SOCKET:
+      g_assert (g_value_get_object (value) == NULL ||
+                gnutls->priv->base_io_stream == NULL);
+
+      g_clear_object (&gnutls->priv->base_socket);
+      gnutls->priv->base_socket = g_value_dup_object (value);
+      break;
+
     case PROP_REQUIRE_CLOSE_NOTIFY:
       gnutls->priv->require_close_notify = g_value_get_boolean (value);
       break;
@@ -897,6 +973,23 @@ end_gnutls_io (GTlsConnectionGnutls  *gnutls,
 #define END_GNUTLS_IO(gnutls, direction, ret, errmsg, err)             \
   } while ((ret = end_gnutls_io (gnutls, direction, ret, err, errmsg, gnutls_strerror (ret))) == 
GNUTLS_E_AGAIN);
 
+/* Checks whether the underlying base stream or GDatagramBased meets
+ * @condition. */
+static gboolean
+g_tls_connection_gnutls_base_check (GTlsConnectionGnutls  *gnutls,
+                                    GIOCondition           condition)
+{
+  if (gnutls->priv->base_socket != NULL)
+    return g_datagram_based_condition_check (gnutls->priv->base_socket,
+                                             condition);
+  else if (condition & G_IO_IN)
+    return g_pollable_input_stream_is_readable (gnutls->priv->base_istream);
+  else
+    return g_pollable_output_stream_is_writable (gnutls->priv->base_ostream);
+}
+
+/* Checks whether the (D)TLS stream meets @condition; not the underlying base
+ * stream or GDatagramBased. */
 gboolean
 g_tls_connection_gnutls_check (GTlsConnectionGnutls  *gnutls,
                               GIOCondition           condition)
@@ -915,17 +1008,18 @@ g_tls_connection_gnutls_check (GTlsConnectionGnutls  *gnutls,
       ((condition & G_IO_OUT) && gnutls->priv->write_closing))
     return FALSE;
 
-  if (condition & G_IO_IN)
-    return g_pollable_input_stream_is_readable (gnutls->priv->base_istream);
-  else
-    return g_pollable_output_stream_is_writable (gnutls->priv->base_ostream);
+  /* Defer to the base stream or GDatagramBased. */
+  return g_tls_connection_gnutls_base_check (gnutls, condition);
 }
 
 typedef struct {
   GSource               source;
 
   GTlsConnectionGnutls *gnutls;
-  GObject              *stream;
+  /* Either a GDatagramBased (datagram mode), or a GPollableInputStream or
+   * GPollableOutputStream (streaming mode):
+   */
+  GObject              *base;
 
   GSource              *child_source;
   GIOCondition          condition;
@@ -988,9 +1082,11 @@ gnutls_source_sync (GTlsConnectionGnutlsSource *gnutls_source)
 
   if (op_waiting)
     gnutls_source->child_source = g_cancellable_source_new (gnutls->priv->waiting_for_op);
-  else if (io_waiting && G_IS_POLLABLE_INPUT_STREAM (gnutls_source->stream))
+  else if (io_waiting && G_IS_DATAGRAM_BASED (gnutls_source->base))
+    gnutls_source->child_source = g_datagram_based_create_source (gnutls->priv->base_socket, 
gnutls_source->condition, NULL);
+  else if (io_waiting && G_IS_POLLABLE_INPUT_STREAM (gnutls_source->base))
     gnutls_source->child_source = g_pollable_input_stream_create_source (gnutls->priv->base_istream, NULL);
-  else if (io_waiting && G_IS_POLLABLE_OUTPUT_STREAM (gnutls_source->stream))
+  else if (io_waiting && G_IS_POLLABLE_OUTPUT_STREAM (gnutls_source->base))
     gnutls_source->child_source = g_pollable_output_stream_create_source (gnutls->priv->base_ostream, NULL);
   else
     gnutls_source->child_source = g_timeout_source_new (0);
@@ -1004,11 +1100,17 @@ gnutls_source_dispatch (GSource     *source,
                        GSourceFunc  callback,
                        gpointer     user_data)
 {
-  GPollableSourceFunc func = (GPollableSourceFunc)callback;
+  GDatagramBasedSourceFunc datagram_based_func = (GDatagramBasedSourceFunc) callback;
+  GPollableSourceFunc pollable_func = (GPollableSourceFunc)callback;
   GTlsConnectionGnutlsSource *gnutls_source = (GTlsConnectionGnutlsSource *)source;
   gboolean ret;
 
-  ret = (*func) (gnutls_source->stream, user_data);
+  if (G_IS_DATAGRAM_BASED (gnutls_source->base))
+    ret = (*datagram_based_func) (G_DATAGRAM_BASED (gnutls_source->base),
+                                  gnutls_source->condition, user_data);
+  else
+    ret = (*pollable_func) (gnutls_source->base, user_data);
+
   if (ret)
     gnutls_source_sync (gnutls_source);
 
@@ -1048,6 +1150,33 @@ g_tls_connection_gnutls_source_closure_callback (GObject  *stream,
   return result;
 }
 
+static gboolean
+g_tls_connection_gnutls_source_dtls_closure_callback (GObject  *stream,
+                                                      GIOCondition condition,
+                                                      gpointer  data)
+{
+  GClosure *closure = data;
+
+  GValue param[2] = { G_VALUE_INIT, G_VALUE_INIT };
+  GValue result_value = G_VALUE_INIT;
+  gboolean result;
+
+  g_value_init (&result_value, G_TYPE_BOOLEAN);
+
+  g_value_init (&param[0], G_TYPE_DATAGRAM_BASED);
+  g_value_set_object (&param[0], stream);
+  g_value_init (&param[1], G_TYPE_IO_CONDITION);
+  g_value_set_flags (&param[1], condition);
+
+  g_closure_invoke (closure, &result_value, 2, param, NULL);
+
+  result = g_value_get_boolean (&result_value);
+  g_value_unset (&result_value);
+  g_value_unset (&param[0]);
+
+  return result;
+}
+
 static GSourceFuncs gnutls_source_funcs =
 {
   gnutls_source_prepare,
@@ -1058,6 +1187,16 @@ static GSourceFuncs gnutls_source_funcs =
   (GSourceDummyMarshal)g_cclosure_marshal_generic
 };
 
+static GSourceFuncs gnutls_source_dtls_funcs =
+{
+  gnutls_source_prepare,
+  gnutls_source_check,
+  gnutls_source_dispatch,
+  gnutls_source_finalize,
+  (GSourceFunc)g_tls_connection_gnutls_source_dtls_closure_callback,
+  (GSourceDummyMarshal)g_cclosure_marshal_generic
+};
+
 GSource *
 g_tls_connection_gnutls_create_source (GTlsConnectionGnutls  *gnutls,
                                       GIOCondition           condition,
@@ -1066,15 +1205,26 @@ g_tls_connection_gnutls_create_source (GTlsConnectionGnutls  *gnutls,
   GSource *source, *cancellable_source;
   GTlsConnectionGnutlsSource *gnutls_source;
 
-  source = g_source_new (&gnutls_source_funcs, sizeof (GTlsConnectionGnutlsSource));
+  if (gnutls->priv->base_socket != NULL)
+    {
+      source = g_source_new (&gnutls_source_dtls_funcs,
+                             sizeof (GTlsConnectionGnutlsSource));
+    }
+  else
+    {
+      source = g_source_new (&gnutls_source_funcs,
+                             sizeof (GTlsConnectionGnutlsSource));
+    }
   g_source_set_name (source, "GTlsConnectionGnutlsSource");
   gnutls_source = (GTlsConnectionGnutlsSource *)source;
   gnutls_source->gnutls = g_object_ref (gnutls);
   gnutls_source->condition = condition;
-  if (condition & G_IO_IN)
-    gnutls_source->stream = G_OBJECT (gnutls->priv->tls_istream);
-  else if (condition & G_IO_OUT)
-    gnutls_source->stream = G_OBJECT (gnutls->priv->tls_ostream);
+  if (gnutls->priv->base_socket != NULL)
+    gnutls_source->base = G_OBJECT (gnutls);
+  else if (gnutls->priv->tls_istream != NULL && condition & G_IO_IN)
+    gnutls_source->base = G_OBJECT (gnutls->priv->tls_istream);
+  else if (gnutls->priv->tls_ostream != NULL && condition & G_IO_OUT)
+    gnutls_source->base = G_OBJECT (gnutls->priv->tls_ostream);
 
   gnutls_source->op_waiting = (gboolean) -1;
   gnutls_source->io_waiting = (gboolean) -1;
@@ -1091,6 +1241,81 @@ g_tls_connection_gnutls_create_source (GTlsConnectionGnutls  *gnutls,
   return source;
 }
 
+static GSource *
+g_tls_connection_gnutls_dtls_create_source (GDatagramBased  *datagram_based,
+                                            GIOCondition     condition,
+                                            GCancellable    *cancellable)
+{
+  GTlsConnectionGnutls *gnutls = G_TLS_CONNECTION_GNUTLS (datagram_based);
+
+  return g_tls_connection_gnutls_create_source (gnutls, condition, cancellable);
+}
+
+static GIOCondition
+g_tls_connection_gnutls_condition_check (GDatagramBased  *datagram_based,
+                                         GIOCondition     condition)
+{
+  GTlsConnectionGnutls *gnutls = G_TLS_CONNECTION_GNUTLS (datagram_based);
+
+  return (g_tls_connection_gnutls_check (gnutls, condition)) ? condition : 0;
+}
+
+static gboolean
+g_tls_connection_gnutls_condition_wait (GDatagramBased  *datagram_based,
+                                        GIOCondition     condition,
+                                        gint64           timeout,
+                                        GCancellable    *cancellable,
+                                        GError         **error)
+{
+  GTlsConnectionGnutls *gnutls = G_TLS_CONNECTION_GNUTLS (datagram_based);
+  GPollFD fds[2];
+  guint n_fds;
+  gint result;
+  gint64 start_time;
+
+  if (g_cancellable_set_error_if_cancelled (cancellable, error))
+    return FALSE;
+
+  /* Convert from microseconds to milliseconds. */
+  if (timeout != -1)
+    timeout = timeout / 1000;
+
+  start_time = g_get_monotonic_time ();
+
+  g_cancellable_make_pollfd (gnutls->priv->waiting_for_op, &fds[0]);
+  n_fds = 1;
+
+  if (g_cancellable_make_pollfd (cancellable, &fds[1]))
+    n_fds++;
+
+  while (!g_tls_connection_gnutls_condition_check (datagram_based, condition) &&
+         !g_cancellable_is_cancelled (cancellable))
+    {
+      result = g_poll (fds, n_fds, timeout);
+      if (result != -1 || errno != EINTR)
+        continue;
+
+      if (timeout != -1)
+        {
+          timeout -= (g_get_monotonic_time () - start_time) / 1000;
+          if (timeout < 0)
+            timeout = 0;
+        }
+    }
+
+  if (n_fds > 1)
+    g_cancellable_release_fd (cancellable);
+
+  if (result == 0)
+    {
+      g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_TIMED_OUT,
+                           _("Socket I/O timed out"));
+      return FALSE;
+    }
+
+  return !g_cancellable_set_error_if_cancelled (cancellable, error);
+}
+
 static void
 set_gnutls_error (GTlsConnectionGnutls *gnutls,
                  GError               *error)
@@ -1130,11 +1355,28 @@ g_tls_connection_gnutls_pull_func (gnutls_transport_ptr_t  transport_data,
    */
   g_clear_error (&gnutls->priv->read_error);
 
-  ret = g_pollable_stream_read (G_INPUT_STREAM (gnutls->priv->base_istream),
-                               buf, buflen,
-                               gnutls->priv->read_blocking,
-                               gnutls->priv->read_cancellable,
-                               &gnutls->priv->read_error);
+  if (gnutls->priv->base_socket != NULL)
+    {
+      GInputVector vector = { buf, buflen };
+      GInputMessage message = { NULL, &vector, 1, 0, 0, NULL, NULL };
+
+      ret = g_datagram_based_receive_messages (gnutls->priv->base_socket,
+                                               &message, 1, 0,
+                                               gnutls->priv->read_blocking ? -1 : 0,
+                                               gnutls->priv->read_cancellable,
+                                               &gnutls->priv->read_error);
+
+      if (ret > 0)
+        ret = message.bytes_received;
+    }
+  else
+    {
+      ret = g_pollable_stream_read (G_INPUT_STREAM (gnutls->priv->base_istream),
+                                    buf, buflen,
+                                    gnutls->priv->read_blocking,
+                                    gnutls->priv->read_cancellable,
+                                    &gnutls->priv->read_error);
+    }
 
   if (ret < 0)
     set_gnutls_error (gnutls, gnutls->priv->read_error);
@@ -1157,11 +1399,29 @@ g_tls_connection_gnutls_push_func (gnutls_transport_ptr_t  transport_data,
   /* See comment in pull_func. */
   g_clear_error (&gnutls->priv->write_error);
 
-  ret = g_pollable_stream_write (G_OUTPUT_STREAM (gnutls->priv->base_ostream),
-                                buf, buflen,
-                                gnutls->priv->write_blocking,
-                                gnutls->priv->write_cancellable,
-                                &gnutls->priv->write_error);
+  if (gnutls->priv->base_socket != NULL)
+    {
+      GOutputVector vector = { buf, buflen };
+      GOutputMessage message = { NULL, &vector, 1, 0, NULL, 0 };
+
+      ret = g_datagram_based_send_messages (gnutls->priv->base_socket,
+                                            &message, 1, 0,
+                                            gnutls->priv->write_blocking ? -1 : 0,
+                                            gnutls->priv->write_cancellable,
+                                            &gnutls->priv->write_error);
+
+      if (ret > 0)
+        ret = message.bytes_sent;
+    }
+  else
+    {
+      ret = g_pollable_stream_write (G_OUTPUT_STREAM (gnutls->priv->base_ostream),
+                                     buf, buflen,
+                                     gnutls->priv->write_blocking,
+                                     gnutls->priv->write_cancellable,
+                                     &gnutls->priv->write_error);
+    }
+
   if (ret < 0)
     set_gnutls_error (gnutls, gnutls->priv->write_error);
 
@@ -1169,7 +1429,20 @@ g_tls_connection_gnutls_push_func (gnutls_transport_ptr_t  transport_data,
 }
 
 static gboolean
-read_cb (GPollableInputStream *istream, gpointer user_data)
+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;
 
@@ -1183,41 +1456,50 @@ g_tls_connection_gnutls_pull_timeout_func (gnutls_transport_ptr_t transport_data
                                            unsigned int ms)
 {
   GTlsConnectionGnutls *gnutls = transport_data;
-  GMainContext *ctx = NULL;
-  GSource *read_source;
-  gboolean read_done = FALSE;
-  GPollableInputStream *pollable_stream;
-
-  pollable_stream = G_POLLABLE_INPUT_STREAM (gnutls->priv->base_istream);
 
   /* Fast path. */
-  if (g_pollable_input_stream_is_readable (pollable_stream) ||
+  if (g_tls_connection_gnutls_base_check (gnutls, G_IO_IN) ||
       g_cancellable_is_cancelled (gnutls->priv->read_cancellable))
     return 1;
-  else if (ms == 0)
-    return 0;
 
-  /* Slow path: wait for readability. */
-  ctx = g_main_context_new ();
+  /* 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;
+      gboolean read_done = FALSE;
 
-  read_source = g_pollable_input_stream_create_source (pollable_stream,
-                                                       gnutls->priv->read_cancellable);
-  g_source_set_ready_time (read_source, g_get_monotonic_time () + ms * 1000);
-  g_source_set_callback (read_source, (GSourceFunc) read_cb,
-                         &read_done, NULL);
-  g_source_attach (read_source, ctx);
+      ctx = g_main_context_new ();
 
-  while (!read_done)
-    g_main_context_iteration (ctx, TRUE);
+      if (gnutls->priv->base_socket != NULL)
+        {
+          read_source = g_datagram_based_create_source (gnutls->priv->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 (gnutls->priv->base_istream, NULL);
+          g_source_set_callback (read_source, (GSourceFunc) read_pollable_cb,
+                                 &read_done, NULL);
+        }
+
+      g_source_set_ready_time (read_source, g_get_monotonic_time () + ms * 1000);
+      g_source_attach (read_source, ctx);
 
-  g_source_destroy (read_source);
+      while (!read_done)
+        g_main_context_iteration (ctx, TRUE);
 
-  g_main_context_unref (ctx);
-  g_source_unref (read_source);
+      g_source_destroy (read_source);
+
+      g_main_context_unref (ctx);
+      g_source_unref (read_source);
+    }
 
   /* If @read_source was dispatched due to cancellation, the resulting error
    * will be handled in g_tls_connection_gnutls_pull_func(). */
-  if (g_pollable_input_stream_is_readable (pollable_stream) ||
+  if (g_tls_connection_gnutls_base_check (gnutls, G_IO_IN) ||
       g_cancellable_is_cancelled (gnutls->priv->read_cancellable))
     return 1;
 
@@ -1253,10 +1535,13 @@ verify_peer_certificate (GTlsConnectionGnutls *gnutls,
   gboolean is_client;
 
   is_client = G_IS_TLS_CLIENT_CONNECTION (gnutls);
-  if (is_client)
+
+  if (!is_client)
+    peer_identity = NULL;
+  else if (gnutls->priv->base_socket == NULL)
     peer_identity = g_tls_client_connection_get_server_identity (G_TLS_CLIENT_CONNECTION (gnutls));
   else
-    peer_identity = NULL;
+    peer_identity = g_dtls_client_connection_get_server_identity (G_DTLS_CLIENT_CONNECTION (gnutls));
 
   errors = 0;
 
@@ -1389,8 +1674,14 @@ accept_peer_certificate (GTlsConnectionGnutls *gnutls,
 
   if (G_IS_TLS_CLIENT_CONNECTION (gnutls))
     {
-      GTlsCertificateFlags validation_flags =
-       g_tls_client_connection_get_validation_flags (G_TLS_CLIENT_CONNECTION (gnutls));
+      GTlsCertificateFlags validation_flags;
+
+      if (gnutls->priv->base_socket == NULL)
+        validation_flags =
+          g_tls_client_connection_get_validation_flags (G_TLS_CLIENT_CONNECTION (gnutls));
+      else
+        validation_flags =
+          g_dtls_client_connection_get_validation_flags (G_DTLS_CLIENT_CONNECTION (gnutls));
 
       if ((peer_certificate_errors & validation_flags) == 0)
        accepted = TRUE;
@@ -1472,6 +1763,15 @@ g_tls_connection_gnutls_handshake (GTlsConnection   *conn,
   return success;
 }
 
+static gboolean
+g_tls_connection_gnutls_dtls_handshake (GDtlsConnection       *conn,
+                                        GCancellable          *cancellable,
+                                        GError               **error)
+{
+  return g_tls_connection_gnutls_handshake (G_TLS_CONNECTION (conn),
+                                            cancellable, error);
+}
+
 /* In the async version we use two GTasks; one to run handshake_thread() and
  * then call handshake_thread_completed(), and a second to call the caller's
  * original callback after we call finish_handshake().
@@ -1568,6 +1868,26 @@ g_tls_connection_gnutls_handshake_finish (GTlsConnection       *conn,
   return g_task_propagate_boolean (G_TASK (result), error);
 }
 
+static void
+g_tls_connection_gnutls_dtls_handshake_async (GDtlsConnection       *conn,
+                                              int                    io_priority,
+                                              GCancellable          *cancellable,
+                                              GAsyncReadyCallback    callback,
+                                              gpointer               user_data)
+{
+  g_tls_connection_gnutls_handshake_async (G_TLS_CONNECTION (conn), io_priority,
+                                           cancellable, callback, user_data);
+}
+
+static gboolean
+g_tls_connection_gnutls_dtls_handshake_finish (GDtlsConnection       *conn,
+                                               GAsyncResult          *result,
+                                               GError               **error)
+{
+  return g_tls_connection_gnutls_handshake_finish (G_TLS_CONNECTION (conn),
+                                                   result, error);
+}
+
 static gboolean
 do_implicit_handshake (GTlsConnectionGnutls  *gnutls,
                       gboolean               blocking,
@@ -1653,6 +1973,87 @@ g_tls_connection_gnutls_read (GTlsConnectionGnutls  *gnutls,
     return -1;
 }
 
+static gint
+g_tls_connection_gnutls_receive_messages (GDatagramBased  *datagram_based,
+                                          GInputMessage   *messages,
+                                          guint            num_messages,
+                                          gint             flags,
+                                          gint64           timeout,
+                                          GCancellable    *cancellable,
+                                          GError         **error)
+{
+  GTlsConnectionGnutls *gnutls;
+  guint i;
+  GError *child_error = NULL;
+
+  gnutls = G_TLS_CONNECTION_GNUTLS (datagram_based);
+
+  if (flags != G_SOCKET_MSG_NONE)
+    {
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
+                   _("Receive flags are not supported"));
+      return -1;
+    }
+
+  for (i = 0; i < num_messages && child_error == NULL; i++)
+    {
+      GInputMessage *message = &messages[i];
+      gssize n_bytes_read;
+
+      /* FIXME: Unfortunately GnuTLS doesn’t have a vectored read function.
+       * See: https://gitlab.com/gnutls/gnutls/issues/16 */
+      g_assert (message->num_vectors == 1);
+
+      n_bytes_read = g_tls_connection_gnutls_read (gnutls,
+                                                   message->vectors[0].buffer,
+                                                   message->vectors[0].size,
+                                                   timeout != 0,
+                                                   cancellable,
+                                                   &child_error);
+
+      if (message->address != NULL)
+        *message->address = NULL;
+      message->flags = G_SOCKET_MSG_NONE;
+      if (message->control_messages != NULL)
+        *message->control_messages = NULL;
+      message->num_control_messages = 0;
+
+      if (n_bytes_read > 0)
+        {
+          message->bytes_received = n_bytes_read;
+        }
+      else if (n_bytes_read == 0)
+        {
+          /* EOS. */
+          break;
+        }
+      else if (i > 0 &&
+               (g_error_matches (child_error,
+                                 G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK) ||
+                g_error_matches (child_error,
+                                 G_IO_ERROR, G_IO_ERROR_TIMED_OUT)))
+        {
+          /* Blocked or timed out after receiving some messages successfully. */
+          g_clear_error (&child_error);
+          break;
+        }
+      else
+        {
+          /* Error, including G_IO_ERROR_WOULD_BLOCK or G_IO_ERROR_TIMED_OUT on
+           * the first message; or G_IO_ERROR_CANCELLED at any time. */
+          break;
+        }
+    }
+
+  if (child_error != NULL)
+    {
+      g_propagate_error (error, child_error);
+      return -1;
+    }
+
+  return i;
+}
+
 gssize
 g_tls_connection_gnutls_write (GTlsConnectionGnutls  *gnutls,
                               const void            *buffer,
@@ -1682,6 +2083,76 @@ g_tls_connection_gnutls_write (GTlsConnectionGnutls  *gnutls,
     return -1;
 }
 
+static gint
+g_tls_connection_gnutls_send_messages (GDatagramBased  *datagram_based,
+                                       GOutputMessage  *messages,
+                                       guint            num_messages,
+                                       gint             flags,
+                                       gint64           timeout,
+                                       GCancellable    *cancellable,
+                                       GError         **error)
+{
+  GTlsConnectionGnutls *gnutls;
+  guint i;
+  GError *child_error = NULL;
+
+  gnutls = G_TLS_CONNECTION_GNUTLS (datagram_based);
+
+  if (flags != G_SOCKET_MSG_NONE)
+    {
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
+                   _("Send flags are not supported"));
+      return -1;
+    }
+
+  for (i = 0; i < num_messages && child_error == NULL; i++)
+    {
+      GOutputMessage *message = &messages[i];
+      gssize n_bytes_sent;
+
+      /* FIXME: Unfortunately GnuTLS doesn’t have a vectored write function.
+       * See: https://gitlab.com/gnutls/gnutls/issues/16 */
+      /* TODO: gnutls_record_cork(), gnutls_record_uncork(), 3.3.0 */
+      g_assert (message->num_vectors == 1);
+
+      n_bytes_sent = g_tls_connection_gnutls_write (gnutls,
+                                                    message->vectors[0].buffer,
+                                                    message->vectors[0].size,
+                                                    timeout != 0,
+                                                    cancellable,
+                                                    &child_error);
+
+      if (n_bytes_sent >= 0)
+        {
+          message->bytes_sent = n_bytes_sent;
+        }
+      else if (i > 0 &&
+               (g_error_matches (child_error,
+                                 G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK) ||
+                g_error_matches (child_error,
+                                 G_IO_ERROR, G_IO_ERROR_TIMED_OUT)))
+        {
+          /* Blocked or timed out after sending some messages successfully. */
+          g_clear_error (&child_error);
+          break;
+        }
+      else
+        {
+          /* Error, including G_IO_ERROR_WOULD_BLOCK or G_IO_ERROR_TIMED_OUT
+           * on the first message; or G_IO_ERROR_CANCELLED at any time. */
+          break;
+        }
+    }
+
+  if (child_error != NULL)
+    {
+      g_propagate_error (error, child_error);
+      return -1;
+    }
+
+  return i;
+}
+
 static GInputStream  *
 g_tls_connection_gnutls_get_input_stream (GIOStream *stream)
 {
@@ -1701,6 +2172,7 @@ g_tls_connection_gnutls_get_output_stream (GIOStream *stream)
 gboolean
 g_tls_connection_gnutls_close_internal (GIOStream     *stream,
                                         GTlsDirection  direction,
+                                        gint64         timeout,
                                         GCancellable  *cancellable,
                                         GError       **error)
 {
@@ -1710,12 +2182,14 @@ g_tls_connection_gnutls_close_internal (GIOStream     *stream,
   int ret = 0;
   GError *gnutls_error = NULL, *stream_error = NULL;
 
-  /* This can be called from g_io_stream_close(), g_input_stream_close() or
-   * g_output_stream_close(). In all cases, we only do the gnutls_bye() for
-   * writing. The difference is how we set the flags on this class and how
-   * the underlying stream is closed.
+  /* This can be called from g_io_stream_close(), g_input_stream_close(),
+   * g_output_stream_close() or g_tls_connection_close(). In all cases, we only
+   * do the gnutls_bye() for writing. The difference is how we set the flags on
+   * this class and how the underlying stream is closed.
    */
 
+  /* FIXME: This does not properly support the @timeout parameter. */
+
   g_return_val_if_fail (direction != G_TLS_DIRECTION_NONE, FALSE);
 
   if (direction == G_TLS_DIRECTION_BOTH)
@@ -1745,15 +2219,27 @@ g_tls_connection_gnutls_close_internal (GIOStream     *stream,
   /* Close the underlying streams. Do this even if the gnutls_bye() call failed,
    * as the parent GIOStream will have set its internal closed flag and hence
    * this implementation will never be called again. */
-  if (direction == G_TLS_DIRECTION_BOTH)
-    success = g_io_stream_close (gnutls->priv->base_io_stream,
-                                 cancellable, &stream_error);
-  else if (direction & G_TLS_DIRECTION_READ)
-    success = g_input_stream_close (g_io_stream_get_input_stream (gnutls->priv->base_io_stream),
-                                    cancellable, &stream_error);
-  else if (direction & G_TLS_DIRECTION_WRITE)
-    success = g_output_stream_close (g_io_stream_get_output_stream (gnutls->priv->base_io_stream),
+  if (gnutls->priv->base_io_stream != NULL)
+    {
+      if (direction == G_TLS_DIRECTION_BOTH)
+        success = g_io_stream_close (gnutls->priv->base_io_stream,
                                      cancellable, &stream_error);
+      else if (direction & G_TLS_DIRECTION_READ)
+        success = g_input_stream_close (g_io_stream_get_input_stream (gnutls->priv->base_io_stream),
+                                        cancellable, &stream_error);
+      else if (direction & G_TLS_DIRECTION_WRITE)
+        success = g_output_stream_close (g_io_stream_get_output_stream (gnutls->priv->base_io_stream),
+                                         cancellable, &stream_error);
+    }
+  else if (gnutls->priv->base_socket != NULL)
+    {
+      /* We do not close underlying #GDatagramBaseds. */
+      success = TRUE;
+    }
+  else
+    {
+      g_assert_not_reached ();
+    }
 
   yield_op (gnutls, op);
 
@@ -1777,9 +2263,30 @@ g_tls_connection_gnutls_close (GIOStream     *stream,
                                GCancellable  *cancellable,
                                GError       **error)
 {
-       return g_tls_connection_gnutls_close_internal (stream,
-                                                      G_TLS_DIRECTION_BOTH,
-                                                      cancellable, error);
+  return g_tls_connection_gnutls_close_internal (stream,
+                                                 G_TLS_DIRECTION_BOTH,
+                                                 -1,  /* blocking */
+                                                 cancellable, error);
+}
+
+static gboolean
+g_tls_connection_gnutls_dtls_shutdown (GDtlsConnection  *conn,
+                                      gboolean          shutdown_read,
+                                      gboolean          shutdown_write,
+                                       GCancellable     *cancellable,
+                                       GError          **error)
+{
+  GTlsDirection direction = G_TLS_DIRECTION_NONE;
+
+  if (shutdown_read)
+    direction |= G_TLS_DIRECTION_READ;
+  if (shutdown_write)
+    direction |= G_TLS_DIRECTION_WRITE;
+
+  return g_tls_connection_gnutls_close_internal (G_IO_STREAM (conn),
+                                                 direction,
+                                                 -1,  /* blocking */
+                                                 cancellable, error);
 }
 
 /* We do async close as synchronous-in-a-thread so we don't need to
@@ -1793,9 +2300,13 @@ close_thread (GTask        *task,
              GCancellable *cancellable)
 {
   GIOStream *stream = object;
+  GTlsDirection direction;
   GError *error = NULL;
 
-  if (!g_tls_connection_gnutls_close_internal (stream, G_TLS_DIRECTION_BOTH,
+  direction = GPOINTER_TO_INT (g_task_get_task_data (task));
+
+  if (!g_tls_connection_gnutls_close_internal (stream, direction,
+                                               -1,  /* blocking */
                                                cancellable, &error))
     g_task_return_error (task, error);
   else
@@ -1803,21 +2314,35 @@ close_thread (GTask        *task,
 }
 
 static void
-g_tls_connection_gnutls_close_async (GIOStream           *stream,
-                                    int                  io_priority,
-                                    GCancellable        *cancellable,
-                                    GAsyncReadyCallback  callback,
-                                    gpointer             user_data)
+g_tls_connection_gnutls_close_internal_async (GIOStream           *stream,
+                                              GTlsDirection        direction,
+                                              int                  io_priority,
+                                              GCancellable        *cancellable,
+                                              GAsyncReadyCallback  callback,
+                                              gpointer             user_data)
 {
   GTask *task;
 
   task = g_task_new (stream, cancellable, callback, user_data);
-  g_task_set_source_tag (task, g_tls_connection_gnutls_close_async);
+  g_task_set_source_tag (task, g_tls_connection_gnutls_close_internal_async);
   g_task_set_priority (task, io_priority);
+  g_task_set_task_data (task, GINT_TO_POINTER (direction), NULL);
   g_task_run_in_thread (task, close_thread);
   g_object_unref (task);
 }
 
+static void
+g_tls_connection_gnutls_close_async (GIOStream           *stream,
+                                     int                  io_priority,
+                                     GCancellable        *cancellable,
+                                     GAsyncReadyCallback  callback,
+                                     gpointer             user_data)
+{
+  g_tls_connection_gnutls_close_internal_async (stream, G_TLS_DIRECTION_BOTH,
+                                                io_priority, cancellable,
+                                                callback, user_data);
+}
+
 static gboolean
 g_tls_connection_gnutls_close_finish (GIOStream           *stream,
                                      GAsyncResult        *result,
@@ -1828,6 +2353,37 @@ g_tls_connection_gnutls_close_finish (GIOStream           *stream,
   return g_task_propagate_boolean (G_TASK (result), error);
 }
 
+static void
+g_tls_connection_gnutls_dtls_shutdown_async (GDtlsConnection     *conn,
+                                            gboolean             shutdown_read,
+                                            gboolean             shutdown_write,
+                                             int                  io_priority,
+                                             GCancellable        *cancellable,
+                                             GAsyncReadyCallback  callback,
+                                             gpointer             user_data)
+{
+  GTlsDirection direction = G_TLS_DIRECTION_NONE;
+
+  if (shutdown_read)
+    direction |= G_TLS_DIRECTION_READ;
+  if (shutdown_write)
+    direction |= G_TLS_DIRECTION_WRITE;
+
+  g_tls_connection_gnutls_close_internal_async (G_IO_STREAM (conn), direction,
+                                               io_priority, cancellable,
+                                                callback, user_data);
+}
+
+static gboolean
+g_tls_connection_gnutls_dtls_shutdown_finish (GDtlsConnection  *conn,
+                                              GAsyncResult     *result,
+                                              GError          **error)
+{
+  g_return_val_if_fail (g_task_is_valid (result, conn), FALSE);
+
+  return g_task_propagate_boolean (G_TASK (result), error);
+}
+
 #ifdef HAVE_PKCS11
 
 static P11KitPin*
@@ -1904,7 +2460,9 @@ g_tls_connection_gnutls_class_init (GTlsConnectionGnutlsClass *klass)
   iostream_class->close_async       = g_tls_connection_gnutls_close_async;
   iostream_class->close_finish      = g_tls_connection_gnutls_close_finish;
 
+  /* For GTlsConnection and GDtlsConnection: */
   g_object_class_override_property (gobject_class, PROP_BASE_IO_STREAM, "base-io-stream");
+  g_object_class_override_property (gobject_class, PROP_BASE_SOCKET, "base-socket");
   g_object_class_override_property (gobject_class, PROP_REQUIRE_CLOSE_NOTIFY, "require-close-notify");
   g_object_class_override_property (gobject_class, PROP_REHANDSHAKE_MODE, "rehandshake-mode");
   g_object_class_override_property (gobject_class, PROP_USE_SYSTEM_CERTDB, "use-system-certdb");
@@ -1921,6 +2479,27 @@ g_tls_connection_gnutls_initable_iface_init (GInitableIface *iface)
   iface->init = g_tls_connection_gnutls_initable_init;
 }
 
+static void
+g_tls_connection_gnutls_dtls_connection_iface_init (GDtlsConnectionInterface *iface)
+{
+  iface->handshake = g_tls_connection_gnutls_dtls_handshake;
+  iface->handshake_async = g_tls_connection_gnutls_dtls_handshake_async;
+  iface->handshake_finish = g_tls_connection_gnutls_dtls_handshake_finish;
+  iface->shutdown = g_tls_connection_gnutls_dtls_shutdown;
+  iface->shutdown_async = g_tls_connection_gnutls_dtls_shutdown_async;
+  iface->shutdown_finish = g_tls_connection_gnutls_dtls_shutdown_finish;
+}
+
+static void
+g_tls_connection_gnutls_datagram_based_iface_init (GDatagramBasedInterface *iface)
+{
+  iface->receive_messages = g_tls_connection_gnutls_receive_messages;
+  iface->send_messages = g_tls_connection_gnutls_send_messages;
+  iface->create_source = g_tls_connection_gnutls_dtls_create_source;
+  iface->condition_check = g_tls_connection_gnutls_condition_check;
+  iface->condition_wait = g_tls_connection_gnutls_condition_wait;
+}
+
 gboolean
 g_tls_connection_gnutls_request_certificate (GTlsConnectionGnutls  *self,
                                             GError               **error)
diff --git a/tls/gnutls/gtlsconnection-gnutls.h b/tls/gnutls/gtlsconnection-gnutls.h
index a7323a8..b4a8480 100644
--- a/tls/gnutls/gtlsconnection-gnutls.h
+++ b/tls/gnutls/gtlsconnection-gnutls.h
@@ -89,6 +89,7 @@ typedef enum {
 
 gboolean g_tls_connection_gnutls_close_internal (GIOStream            *stream,
                                                  GTlsDirection         direction,
+                                                 gint64                timeout,
                                                  GCancellable         *cancellable,
                                                  GError              **error);
 
diff --git a/tls/gnutls/gtlsinputstream-gnutls.c b/tls/gnutls/gtlsinputstream-gnutls.c
index ca9cbe2..4b1685d 100644
--- a/tls/gnutls/gtlsinputstream-gnutls.c
+++ b/tls/gnutls/gtlsinputstream-gnutls.c
@@ -144,6 +144,7 @@ g_tls_input_stream_gnutls_close (GInputStream            *stream,
     return TRUE;
 
   ret = g_tls_connection_gnutls_close_internal (conn, G_TLS_DIRECTION_READ,
+                                                -1,  /* blocking */
                                                 cancellable, error);
 
   g_object_unref (conn);
@@ -168,6 +169,7 @@ close_thread (GTask        *task,
 
   if (conn && !g_tls_connection_gnutls_close_internal (conn,
                                                        G_TLS_DIRECTION_READ,
+                                                       -1,  /* blocking */
                                                        cancellable, &error))
     g_task_return_error (task, error);
   else
diff --git a/tls/gnutls/gtlsoutputstream-gnutls.c b/tls/gnutls/gtlsoutputstream-gnutls.c
index aa60f08..4c7853b 100644
--- a/tls/gnutls/gtlsoutputstream-gnutls.c
+++ b/tls/gnutls/gtlsoutputstream-gnutls.c
@@ -146,6 +146,7 @@ g_tls_output_stream_gnutls_close (GOutputStream            *stream,
     return TRUE;
 
   ret = g_tls_connection_gnutls_close_internal (conn, G_TLS_DIRECTION_WRITE,
+                                                -1,  /* blocking */
                                                 cancellable, error);
 
   g_object_unref (conn);
@@ -170,6 +171,7 @@ close_thread (GTask        *task,
 
   if (conn && !g_tls_connection_gnutls_close_internal (conn,
                                                        G_TLS_DIRECTION_WRITE,
+                                                       -1,  /* blocking */
                                                        cancellable, &error))
     g_task_return_error (task, error);
   else
diff --git a/tls/gnutls/gtlsserverconnection-gnutls.c b/tls/gnutls/gtlsserverconnection-gnutls.c
index aea76fb..8ab7724 100644
--- a/tls/gnutls/gtlsserverconnection-gnutls.c
+++ b/tls/gnutls/gtlsserverconnection-gnutls.c
@@ -64,6 +64,8 @@ G_DEFINE_TYPE_WITH_CODE (GTlsServerConnectionGnutls, g_tls_server_connection_gnu
                                                g_tls_server_connection_gnutls_initable_interface_init)
                         G_IMPLEMENT_INTERFACE (G_TYPE_TLS_SERVER_CONNECTION,
                                                
g_tls_server_connection_gnutls_server_connection_interface_init)
+                         G_IMPLEMENT_INTERFACE (G_TYPE_DTLS_SERVER_CONNECTION,
+                                                NULL)
 )
 
 struct _GTlsServerConnectionGnutlsPrivate


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