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



commit be41b57c86d8da7ad4c74db0bc398e25879efb2d
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.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=697908

 tls/gnutls/gtlsbackend-gnutls.c          |    2 +
 tls/gnutls/gtlsclientconnection-gnutls.c |   43 ++-
 tls/gnutls/gtlsconnection-gnutls.c       |  685 +++++++++++++++++++++++++++---
 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, 659 insertions(+), 78 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 6be9dbf..ffb8068 100644
--- a/tls/gnutls/gtlsclientconnection-gnutls.c
+++ b/tls/gnutls/gtlsclientconnection-gnutls.c
@@ -38,13 +38,14 @@ enum
   PROP_0,
   PROP_VALIDATION_FLAGS,
   PROP_SERVER_IDENTITY,
-  PROP_USE_SSL3,
+  PROP_ENABLE_NEGOTIATION,
   PROP_ACCEPTED_CAS
 };
 
 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,13 +60,15 @@ 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
 {
   GTlsCertificateFlags validation_flags;
   GSocketConnectable *server_identity;
-  gboolean use_ssl3;
+  gboolean enable_negotiation;
   gboolean session_data_override;
 
   GBytes *session_id;
@@ -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);
@@ -200,8 +203,8 @@ g_tls_client_connection_gnutls_get_property (GObject    *object,
       g_value_set_object (value, gnutls->priv->server_identity);
       break;
 
-    case PROP_USE_SSL3:
-      g_value_set_boolean (value, gnutls->priv->use_ssl3);
+    case PROP_ENABLE_NEGOTIATION:
+      g_value_set_boolean (value, gnutls->priv->enable_negotiation);
       break;
 
     case PROP_ACCEPTED_CAS:
@@ -256,8 +259,8 @@ g_tls_client_connection_gnutls_set_property (GObject      *object,
        }
       break;
 
-    case PROP_USE_SSL3:
-      gnutls->priv->use_ssl3 = g_value_get_boolean (value);
+    case PROP_ENABLE_NEGOTIATION:
+      gnutls->priv->enable_negotiation = g_value_get_boolean (value);
       break;
 
     default:
@@ -394,9 +397,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);
         }
     }
 }
@@ -421,6 +425,14 @@ g_tls_client_connection_gnutls_copy_session_state (GTlsClientConnection *conn,
 }
 
 static void
+g_tls_client_connection_gnutls_dtls_copy_session_state (GDtlsClientConnection *conn,
+                                                        GDtlsClientConnection *source)
+{
+  g_tls_client_connection_gnutls_copy_session_state (G_TLS_CLIENT_CONNECTION (conn),
+                                                     G_TLS_CLIENT_CONNECTION (source));
+}
+
+static void
 g_tls_client_connection_gnutls_class_init (GTlsClientConnectionGnutlsClass *klass)
 {
   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
@@ -439,7 +451,8 @@ g_tls_client_connection_gnutls_class_init (GTlsClientConnectionGnutlsClass *klas
 
   g_object_class_override_property (gobject_class, PROP_VALIDATION_FLAGS, "validation-flags");
   g_object_class_override_property (gobject_class, PROP_SERVER_IDENTITY, "server-identity");
-  g_object_class_override_property (gobject_class, PROP_USE_SSL3, "use-ssl3");
+  g_object_class_override_property (gobject_class, PROP_ENABLE_NEGOTIATION, "enable-negotiation");
+  g_object_class_override_property (gobject_class, PROP_ENABLE_NEGOTIATION, "use-ssl3");
   g_object_class_override_property (gobject_class, PROP_ACCEPTED_CAS, "accepted-cas");
 }
 
@@ -456,3 +469,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)
+{
+  iface->copy_session_state = g_tls_client_connection_gnutls_dtls_copy_session_state;
+}
diff --git a/tls/gnutls/gtlsconnection-gnutls.c b/tls/gnutls/gtlsconnection-gnutls.c
index f57dd2f..fcc8a23 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 Collabora, Ltd.
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -53,6 +54,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 +98,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 +122,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 +133,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 +144,21 @@ 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 datagram mode.
+   * Mutually exclusive with base_io_stream. */
+  GDatagramBased *base_socket;  /* owned */
+
   gnutls_certificate_credentials_t creds;
   gnutls_session_t session;
 
@@ -160,6 +205,8 @@ struct _GTlsConnectionGnutlsPrivate
   gboolean read_closing, read_closed;
   gboolean write_closing, write_closed;
 
+  /* When operating in stream mode; when operating in datagram mode, the
+   * GTlsConnectionGnutls itself is the DTLS GDatagramBased: */
   GInputStream *tls_istream;
   GOutputStream *tls_ostream;
 
@@ -297,6 +344,8 @@ g_tls_connection_gnutls_set_handshake_priority (GTlsConnectionGnutls *gnutls)
 
   if (G_IS_TLS_CLIENT_CONNECTION (gnutls))
     fallback = g_tls_client_connection_get_use_ssl3 (G_TLS_CLIENT_CONNECTION (gnutls));
+  else if (G_IS_DTLS_CLIENT_CONNECTION (gnutls))
+    fallback = g_dtls_client_connection_get_enable_negotiation (G_DTLS_CLIENT_CONNECTION (gnutls));
   else
     fallback = FALSE;
   unsafe_rehandshake = (gnutls->priv->rehandshake_mode == G_TLS_REHANDSHAKE_UNSAFELY);
@@ -304,18 +353,32 @@ g_tls_connection_gnutls_set_handshake_priority (GTlsConnectionGnutls *gnutls)
                       priorities[fallback][unsafe_rehandshake]);
 }
 
+/* Hacky as anything, but this is just the way the GTlsConnectionGnutls classes
+ * are glued together. */
+static gboolean
+is_client_connection (GTlsConnectionGnutls *gnutls)
+{
+  return G_IS_TLS_CLIENT_CONNECTION (gnutls) ||
+         G_IS_DTLS_CLIENT_CONNECTION (gnutls);
+}
+
 static gboolean
 g_tls_connection_gnutls_initable_init (GInitable     *initable,
                                       GCancellable  *cancellable,
                                       GError       **error)
 {
   GTlsConnectionGnutls *gnutls = G_TLS_CONNECTION_GNUTLS (initable);
-  gboolean client = G_IS_TLS_CLIENT_CONNECTION (gnutls);
+  gboolean client = is_client_connection (gnutls);
   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);
 
@@ -342,8 +405,12 @@ g_tls_connection_gnutls_initable_init (GInitable     *initable,
   if (flags & GNUTLS_DATAGRAM)
     gnutls_dtls_set_mtu (gnutls->priv->session, 65535);
 
-  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;
 }
@@ -354,6 +421,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);
@@ -410,6 +478,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;
@@ -468,6 +540,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);
@@ -489,6 +564,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;
@@ -893,6 +976,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)
@@ -911,16 +1011,16 @@ 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;
+  /* Either a GDatagramBased (datagram mode), or a GPollableInputStream or
+   * GPollableOutputStream (streaming mode): */
   GObject              *stream;
 
   GSource              *child_source;
@@ -984,6 +1084,8 @@ 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_DATAGRAM_BASED (gnutls_source->stream))
+    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->stream))
     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))
@@ -1000,11 +1102,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->stream))
+    ret = (*datagram_based_func) (G_DATAGRAM_BASED (gnutls_source->stream),
+                                  gnutls_source->condition, user_data);
+  else
+    ret = (*pollable_func) (gnutls_source->stream, user_data);
+
   if (ret)
     gnutls_source_sync (gnutls_source);
 
@@ -1067,9 +1175,11 @@ g_tls_connection_gnutls_create_source (GTlsConnectionGnutls  *gnutls,
   gnutls_source = (GTlsConnectionGnutlsSource *)source;
   gnutls_source->gnutls = g_object_ref (gnutls);
   gnutls_source->condition = condition;
-  if (condition & G_IO_IN)
+  if (gnutls->priv->base_socket != NULL)
+    gnutls_source->stream = G_OBJECT (gnutls);
+  else if (gnutls->priv->tls_istream != NULL && condition & G_IO_IN)
     gnutls_source->stream = G_OBJECT (gnutls->priv->tls_istream);
-  else if (condition & G_IO_OUT)
+  else if (gnutls->priv->tls_ostream != NULL && condition & G_IO_OUT)
     gnutls_source->stream = G_OBJECT (gnutls->priv->tls_ostream);
 
   gnutls_source->op_waiting = (gboolean) -1;
@@ -1087,6 +1197,87 @@ 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_timed_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 ();
+
+  if (!g_cancellable_make_pollfd (gnutls->priv->waiting_for_op, &fds[0]))
+    {
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                   _("Failed to set up poll FD."));
+      return FALSE;
+    }
+
+  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)
@@ -1126,11 +1317,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);
@@ -1153,11 +1361,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);
 
@@ -1165,11 +1391,26 @@ 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;
 
   g_assert (G_IS_POLLABLE_INPUT_STREAM (istream));
+g_assert_not_reached ();
+  *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;
+
+  g_assert (G_IS_DATAGRAM_BASED (datagram_based));
 
   *read_done = TRUE;
 
@@ -1181,28 +1422,43 @@ g_tls_connection_gnutls_pull_timeout_func (gnutls_transport_ptr_t transport_data
                                            unsigned int ms)
 {
   GTlsConnectionGnutls *gnutls = transport_data;
-  GMainContext *ctx = g_main_context_new ();
-  GSource *read_source;
-  gboolean read_done = FALSE;
 
-  read_source = g_pollable_input_stream_create_source (G_POLLABLE_INPUT_STREAM (gnutls->priv->base_istream),
-                                                       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);
+  /* 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 = g_main_context_new ();
+      GSource *read_source;
+      gboolean read_done = FALSE;
+
+      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);
+        }
 
-  while (!read_done)
-    g_main_context_iteration (ctx, TRUE);
+      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 (G_POLLABLE_INPUT_STREAM (gnutls->priv->base_istream)) ||
+  if (g_tls_connection_gnutls_base_check (gnutls, G_IO_IN) ||
       g_cancellable_is_cancelled (gnutls->priv->read_cancellable))
     return 1;
 
@@ -1237,9 +1493,11 @@ verify_peer_certificate (GTlsConnectionGnutls *gnutls,
   GTlsCertificateFlags errors;
   gboolean is_client;
 
-  is_client = G_IS_TLS_CLIENT_CONNECTION (gnutls);
-  if (is_client)
+  is_client = is_client_connection (gnutls);
+  if (G_IS_TLS_CLIENT_CONNECTION (gnutls))
     peer_identity = g_tls_client_connection_get_server_identity (G_TLS_CLIENT_CONNECTION (gnutls));
+  else if (G_IS_DTLS_CLIENT_CONNECTION (gnutls))
+    peer_identity = g_dtls_client_connection_get_server_identity (G_DTLS_CLIENT_CONNECTION (gnutls));
   else
     peer_identity = NULL;
 
@@ -1297,7 +1555,7 @@ handshake_thread (GTask        *task,
 
   g_clear_error (&gnutls->priv->handshake_error);
 
-  is_client = G_IS_TLS_CLIENT_CONNECTION (gnutls);
+  is_client = is_client_connection (gnutls);
 
   if (!is_client && gnutls->priv->ever_handshaked &&
       !gnutls->priv->implicit_handshake)
@@ -1345,7 +1603,7 @@ handshake_thread (GTask        *task,
       gnutls->priv->peer_certificate_tmp = get_peer_certificate_from_session (gnutls);
       if (gnutls->priv->peer_certificate_tmp)
        gnutls->priv->peer_certificate_errors_tmp = verify_peer_certificate (gnutls, 
gnutls->priv->peer_certificate_tmp);
-      else if (G_IS_TLS_CLIENT_CONNECTION (gnutls))
+      else if (is_client_connection (gnutls))
        {
          g_set_error_literal (&error, G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE,
                               _("Server did not return a valid TLS certificate"));
@@ -1372,10 +1630,16 @@ accept_peer_certificate (GTlsConnectionGnutls *gnutls,
 {
   gboolean accepted = FALSE;
 
-  if (G_IS_TLS_CLIENT_CONNECTION (gnutls))
+  if (is_client_connection (gnutls))
     {
-      GTlsCertificateFlags validation_flags =
-       g_tls_client_connection_get_validation_flags (G_TLS_CLIENT_CONNECTION (gnutls));
+      GTlsCertificateFlags validation_flags;
+
+      if (G_IS_TLS_CLIENT_CONNECTION (gnutls))
+        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;
@@ -1457,6 +1721,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().
@@ -1553,6 +1826,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,
@@ -1638,6 +1931,95 @@ 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);
+
+      if (message->flags != G_SOCKET_MSG_NONE)
+        {
+          g_set_error (&child_error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
+                       _("Receive flags are not supported"));
+          break;
+        }
+
+      n_bytes_read = g_tls_connection_gnutls_read (gnutls,
+                                                   message->vectors[0].buffer,
+                                                   message->vectors[0].size,
+                                                   timeout != 0,
+                                                   cancellable,
+                                                   &child_error);
+
+      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;
+}
+
+static gssize
+g_tls_connection_gnutls_get_available_bytes (GDatagramBased *datagram_based)
+{
+  GTlsConnectionGnutls *gnutls = G_TLS_CONNECTION_GNUTLS (datagram_based);
+
+  return gnutls_record_check_pending (gnutls->priv->session);
+}
+
 gssize
 g_tls_connection_gnutls_write (GTlsConnectionGnutls  *gnutls,
                               const void            *buffer,
@@ -1679,6 +2061,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)
 {
@@ -1698,6 +2150,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)
 {
@@ -1706,12 +2159,14 @@ g_tls_connection_gnutls_close_internal (GIOStream     *stream,
   gboolean success = TRUE;
   int ret = 0;
 
-  /* 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_datagram_based_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)
@@ -1741,15 +2196,43 @@ 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, 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, 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, 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, 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, error);
+    }
+  else if (gnutls->priv->base_socket != NULL)
+    {
+      success = g_datagram_based_shutdown (gnutls->priv->base_socket,
+                                           direction & G_TLS_DIRECTION_READ,
+                                           direction & G_TLS_DIRECTION_WRITE,
+                                           timeout,
+                                           cancellable,
+                                           error);
+
+      if (!success && error != NULL &&
+          g_error_matches (*error, G_IO_ERROR, G_IO_ERROR_NOT_CONNECTED))
+        {
+          /* Since we’re a datagram service, the user could legitimately set
+           * the @base_socket to a non-connected datagram socket. As far
+           * as I can understand from the DTLS specification (RFC 6347), there
+           * is no requirement to check the sender address of incoming
+           * datagrams; that will all be handled by the DTLS headers. */
+          g_clear_error (error);
+          success = TRUE;
+        }
+    }
+  else
+    {
+      g_assert_not_reached ();
+    }
 
   yield_op (gnutls, op);
 
@@ -1763,6 +2246,7 @@ g_tls_connection_gnutls_close (GIOStream     *stream,
 {
        return g_tls_connection_gnutls_close_internal (stream,
                                                       G_TLS_DIRECTION_BOTH,
+                                                      -1,  /* blocking */
                                                       cancellable, error);
 }
 
@@ -1777,9 +2261,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
@@ -1787,21 +2275,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,
@@ -1812,6 +2314,34 @@ g_tls_connection_gnutls_close_finish (GIOStream           *stream,
   return g_task_propagate_boolean (G_TASK (result), error);
 }
 
+static gboolean
+g_tls_connection_gnutls_shutdown (GDatagramBased  *datagram_based,
+                                  gboolean         shutdown_read,
+                                  gboolean         shutdown_write,
+                                  gint64           timeout,
+                                  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 (datagram_based),
+                                                 direction, timeout,
+                                                 cancellable, error);
+}
+
+static gboolean
+g_tls_connection_gnutls_is_closed (GDatagramBased *datagram_based)
+{
+  GTlsConnectionGnutls *gnutls = G_TLS_CONNECTION_GNUTLS (datagram_based);
+
+  return gnutls->priv->read_closed;
+}
+
 #ifdef HAVE_PKCS11
 
 static P11KitPin*
@@ -1888,7 +2418,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");
@@ -1905,6 +2437,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;
+}
+
+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->shutdown = g_tls_connection_gnutls_shutdown;
+  iface->is_closed = g_tls_connection_gnutls_is_closed;
+  iface->create_source = g_tls_connection_gnutls_dtls_create_source;
+  iface->condition_check = g_tls_connection_gnutls_condition_check;
+  iface->condition_timed_wait = g_tls_connection_gnutls_condition_timed_wait;
+  iface->get_available_bytes = g_tls_connection_gnutls_get_available_bytes;
+}
+
 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]