[glib-networking] tests: add test coverage for DTLS packet loss



commit daffbaac9d5906d471864bc21aa6e2df6ae101a5
Author: Ole André Vadla Ravnås <oleavr gmail com>
Date:   Thu May 27 09:56:06 2021 +0200

    tests: add test coverage for DTLS packet loss

 tls/tests/dtls-connection.c |  70 ++++++++++++++-
 tls/tests/lossy-socket.c    | 214 ++++++++++++++++++++++++++++++++++++++++++++
 tls/tests/lossy-socket.h    |  55 ++++++++++++
 tls/tests/meson.build       |   2 +-
 4 files changed, 337 insertions(+), 4 deletions(-)
---
diff --git a/tls/tests/dtls-connection.c b/tls/tests/dtls-connection.c
index 999f3e3..4f3cd3c 100644
--- a/tls/tests/dtls-connection.c
+++ b/tls/tests/dtls-connection.c
@@ -27,6 +27,7 @@
 
 #include "config.h"
 
+#include "lossy-socket.h"
 #include "mock-interaction.h"
 
 #include <gio/gio.h>
@@ -68,6 +69,8 @@ typedef struct {
   gboolean server_should_disappear;  /* whether the server should stop responding before sending a message */
   gboolean server_should_close;  /* whether the server should close gracefully once it’s sent a message */
   GTlsAuthenticationMode auth_mode;
+  IOPredicateFunc client_loss_inducer;
+  IOPredicateFunc server_loss_inducer;
 } TestData;
 
 typedef struct {
@@ -77,6 +80,7 @@ typedef struct {
   GMainContext *server_context;
   gboolean loop_finished;
   GSocket *server_socket;
+  GDatagramBased *server_transport;
   GSource *server_source;
   GTlsDatabase *database;
   GDatagramBased *server_connection;
@@ -155,6 +159,8 @@ teardown_connection (TestConnection *test, gconstpointer data)
       test->server_connection = NULL;
     }
 
+  g_clear_object (&test->server_transport);
+
   if (test->server_socket)
     {
       g_socket_close (test->server_socket, &error);
@@ -219,6 +225,16 @@ start_server (TestConnection *test)
                                           g_inet_socket_address_get_port (iaddr));
 
   test->server_socket = socket;
+  if (test->test_data->server_loss_inducer)
+    {
+      test->server_transport = lossy_socket_new (G_DATAGRAM_BASED (socket),
+                                                 test->test_data->server_loss_inducer,
+                                                 test);
+    }
+  else
+    {
+      test->server_transport = G_DATAGRAM_BASED (g_object_ref (socket));
+    }
   test->server_running = TRUE;
 }
 
@@ -396,7 +412,7 @@ on_incoming_connection (GSocket       *socket,
   cert = g_tls_certificate_new_from_file (tls_test_file_path ("server-and-key.pem"), &error);
   g_assert_no_error (error);
 
-  test->server_connection = g_dtls_server_connection_new (G_DATAGRAM_BASED (socket),
+  test->server_connection = g_dtls_server_connection_new (test->server_transport,
                                                           cert, &error);
   g_debug ("%s: Server connection %p on socket %p", G_STRFUNC, test->server_connection, socket);
   g_assert_no_error (error);
@@ -502,7 +518,7 @@ on_incoming_connection_threaded (GSocket      *socket,
   cert = g_tls_certificate_new_from_file (tls_test_file_path ("server-and-key.pem"), &error);
   g_assert_no_error (error);
 
-  test->server_connection = g_dtls_server_connection_new (G_DATAGRAM_BASED (socket),
+  test->server_connection = g_dtls_server_connection_new (test->server_transport,
                                                           cert, &error);
   g_debug ("%s: Server connection %p on socket %p", G_STRFUNC, test->server_connection, socket);
   g_assert_no_error (error);
@@ -603,6 +619,7 @@ start_server_and_connect_to_it (TestConnection         *test,
 {
   GError *error = NULL;
   GSocket *socket;
+  GDatagramBased *transport;
 
   start_server_service (test, threaded);
 
@@ -613,7 +630,19 @@ start_server_and_connect_to_it (TestConnection         *test,
   g_socket_connect (socket, test->address, NULL, &error);
   g_assert_no_error (error);
 
-  return G_DATAGRAM_BASED (socket);
+  if (test->test_data->client_loss_inducer)
+    {
+      transport = lossy_socket_new (G_DATAGRAM_BASED (socket),
+                                    test->test_data->client_loss_inducer,
+                                    test);
+      g_object_unref (socket);
+    }
+  else
+    {
+      transport = G_DATAGRAM_BASED (socket);
+    }
+
+  return transport;
 }
 
 static void
@@ -745,6 +774,16 @@ test_connection_timeouts_read (TestConnection *test,
   g_assert_error (test->read_error, G_IO_ERROR, G_IO_ERROR_TIMED_OUT);
 }
 
+static IODecision
+drop_first_outgoing (const IODetails *io,
+                     gpointer         user_data)
+{
+  if (io->direction == IO_OUT && io->serial == 1)
+    return IO_DROP;
+
+  return IO_KEEP;
+}
+
 int
 main (int   argc,
       char *argv[])
@@ -755,6 +794,7 @@ main (int   argc,
     FALSE,  /* server_should_disappear */
     TRUE, /* server_should_close */
     G_TLS_AUTHENTICATION_NONE,  /* auth_mode */
+    NULL, NULL, /* loss inducers */
   };
   const TestData server_timeout = {
     1000 * G_USEC_PER_SEC,  /* server_timeout */
@@ -762,6 +802,7 @@ main (int   argc,
     FALSE,  /* server_should_disappear */
     TRUE, /* server_should_close */
     G_TLS_AUTHENTICATION_NONE,  /* auth_mode */
+    NULL, NULL, /* loss inducers */
   };
   const TestData nonblocking = {
     0,  /* server_timeout */
@@ -769,6 +810,7 @@ main (int   argc,
     FALSE,  /* server_should_disappear */
     TRUE, /* server_should_close */
     G_TLS_AUTHENTICATION_NONE,  /* auth_mode */
+    NULL, NULL, /* loss inducers */
   };
   const TestData client_timeout = {
     0,  /* server_timeout */
@@ -776,6 +818,23 @@ main (int   argc,
     TRUE,  /* server_should_disappear */
     TRUE, /* server_should_close */
     G_TLS_AUTHENTICATION_NONE,  /* auth_mode */
+    NULL, NULL, /* loss inducers */
+  };
+  const TestData client_loss = {
+    -1,  /* server_timeout */
+    0,  /* client_timeout */
+    FALSE,  /* server_should_disappear */
+    TRUE, /* server_should_close */
+    G_TLS_AUTHENTICATION_NONE,  /* auth_mode */
+    drop_first_outgoing, NULL, /* loss inducers */
+  };
+  const TestData server_loss = {
+    -1,  /* server_timeout */
+    0,  /* client_timeout */
+    FALSE,  /* server_should_disappear */
+    TRUE, /* server_should_close */
+    G_TLS_AUTHENTICATION_NONE,  /* auth_mode */
+    NULL, drop_first_outgoing, /* loss inducers */
   };
   int ret;
   int i;
@@ -826,6 +885,11 @@ main (int   argc,
               setup_connection, test_connection_timeouts_read,
               teardown_connection);
 
+  g_test_add ("/dtls/" BACKEND "/connection/lossy/client", TestConnection, &client_loss,
+              setup_connection, test_basic_connection, teardown_connection);
+  g_test_add ("/dtls/" BACKEND "/connection/lossy/server", TestConnection, &server_loss,
+              setup_connection, test_basic_connection, teardown_connection);
+
   ret = g_test_run ();
 
   /* for valgrinding */
diff --git a/tls/tests/lossy-socket.c b/tls/tests/lossy-socket.c
new file mode 100644
index 0000000..7c20708
--- /dev/null
+++ b/tls/tests/lossy-socket.c
@@ -0,0 +1,214 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * Copyright (C) 2021 Ole André Vadla Ravnås <oleavr frida re>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * In addition, when the library is used with OpenSSL, a special
+ * exception applies. Refer to the LICENSE_EXCEPTION file for details.
+ */
+
+#include "config.h"
+
+#include "lossy-socket.h"
+
+struct _LossySocket
+{
+  GObject parent_instance;
+
+  GDatagramBased *base_socket;
+
+  IOPredicateFunc predicate_func;
+  gpointer predicate_data;
+
+  gint next_rx_serial;
+  gint next_tx_serial;
+};
+
+static void lossy_socket_datagram_based_iface_init (GDatagramBasedInterface *iface);
+
+G_DEFINE_TYPE_EXTENDED (LossySocket,
+                        lossy_socket,
+                        G_TYPE_OBJECT, 0,
+                        G_IMPLEMENT_INTERFACE (G_TYPE_DATAGRAM_BASED,
+                                               lossy_socket_datagram_based_iface_init))
+
+static gint
+lossy_socket_receive_messages (GDatagramBased  *datagram_based,
+                               GInputMessage   *messages,
+                               guint            num_messages,
+                               gint             flags,
+                               gint64           timeout,
+                               GCancellable    *cancellable,
+                               GError         **error)
+{
+  LossySocket *self = LOSSY_SOCKET (datagram_based);
+  gint ret;
+  gboolean skip;
+
+  do
+    {
+      IODetails d;
+
+      skip = FALSE;
+
+      ret = g_datagram_based_receive_messages (self->base_socket, messages,
+                                               num_messages, flags, timeout,
+                                               cancellable, error);
+      if (ret <= 0)
+        break;
+
+      d.direction = IO_IN;
+      d.serial = self->next_rx_serial++;
+
+      if (self->predicate_func (&d, self->predicate_data) == IO_DROP)
+        {
+          messages->bytes_received = 0;
+          messages->flags = 0;
+
+          if (timeout == 0)
+            {
+              ret = -1;
+              g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK,
+                                   "Operation would block");
+            }
+          else
+            {
+              skip = TRUE;
+            }
+        }
+    }
+  while (skip);
+
+  return ret;
+}
+
+static gint
+lossy_socket_send_messages (GDatagramBased  *datagram_based,
+                            GOutputMessage  *messages,
+                            guint            num_messages,
+                            gint             flags,
+                            gint64           timeout,
+                            GCancellable    *cancellable,
+                            GError         **error)
+{
+  LossySocket *self = LOSSY_SOCKET (datagram_based);
+  IODetails d;
+
+  d.direction = IO_OUT;
+  d.serial = self->next_tx_serial++;
+
+  if (self->predicate_func (&d, self->predicate_data) == IO_DROP)
+    {
+      guint i, j;
+
+      for (i = 0; i < num_messages; i++)
+        {
+          GOutputMessage *m = &messages[i];
+
+          for (j = 0; j < m->num_vectors; j++)
+            m->bytes_sent += m->vectors[j].size;
+        }
+
+      return num_messages;
+    }
+
+  return g_datagram_based_send_messages (self->base_socket, messages,
+                                         num_messages, flags, timeout,
+                                         cancellable, error);
+}
+
+static GSource *
+lossy_socket_create_source (GDatagramBased *datagram_based,
+                            GIOCondition    condition,
+                            GCancellable   *cancellable)
+{
+  LossySocket *self = LOSSY_SOCKET (datagram_based);
+
+  return g_datagram_based_create_source (self->base_socket, condition,
+                                         cancellable);
+}
+
+static GIOCondition
+lossy_socket_condition_check (GDatagramBased *datagram_based,
+                              GIOCondition    condition)
+{
+  LossySocket *self = LOSSY_SOCKET (datagram_based);
+
+  return g_datagram_based_condition_check (self->base_socket, condition);
+}
+
+static gboolean
+lossy_socket_condition_wait (GDatagramBased  *datagram_based,
+                             GIOCondition     condition,
+                             gint64           timeout,
+                             GCancellable    *cancellable,
+                             GError         **error)
+{
+  LossySocket *self = LOSSY_SOCKET (datagram_based);
+
+  return g_datagram_based_condition_wait (self->base_socket, condition, timeout,
+                                          cancellable, error);
+}
+
+static void
+lossy_socket_init (LossySocket *self)
+{
+  self->next_rx_serial = 1;
+  self->next_tx_serial = 1;
+}
+
+static void
+lossy_socket_dispose (GObject *object)
+{
+  LossySocket *self = LOSSY_SOCKET (object);
+
+  g_clear_object (&self->base_socket);
+
+  G_OBJECT_CLASS (lossy_socket_parent_class)->dispose (object);
+}
+
+static void
+lossy_socket_class_init (LossySocketClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->dispose = lossy_socket_dispose;
+}
+
+static void
+lossy_socket_datagram_based_iface_init (GDatagramBasedInterface *iface)
+{
+  iface->receive_messages = lossy_socket_receive_messages;
+  iface->send_messages = lossy_socket_send_messages;
+  iface->create_source = lossy_socket_create_source;
+  iface->condition_check = lossy_socket_condition_check;
+  iface->condition_wait = lossy_socket_condition_wait;
+}
+
+GDatagramBased *
+lossy_socket_new (GDatagramBased  *base_socket,
+                  IOPredicateFunc  predicate_func,
+                  gpointer         predicate_data)
+{
+  LossySocket *s;
+
+  s = g_object_new (LOSSY_TYPE_SOCKET, NULL);
+  s->base_socket = g_object_ref (base_socket);
+  s->predicate_func = predicate_func;
+  s->predicate_data = predicate_data;
+
+  return G_DATAGRAM_BASED (s);
+}
diff --git a/tls/tests/lossy-socket.h b/tls/tests/lossy-socket.h
new file mode 100644
index 0000000..1e74c2d
--- /dev/null
+++ b/tls/tests/lossy-socket.h
@@ -0,0 +1,55 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * Copyright (C) 2021 Ole André Vadla Ravnås <oleavr frida re>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, see
+ * <http://www.gnu.org/licenses/>.
+ */
+
+#include <gio/gio.h>
+
+#pragma once
+
+G_BEGIN_DECLS
+
+typedef enum {
+  IO_KEEP,
+  IO_DROP
+} IODecision;
+
+typedef enum {
+  IO_IN,
+  IO_OUT
+} IODirection;
+
+typedef struct _IODetails IODetails;
+
+typedef IODecision (*IOPredicateFunc) (const IODetails *io,
+                                       gpointer         user_data);
+
+struct _IODetails
+{
+  IODirection direction;
+  guint serial;
+};
+
+#define LOSSY_TYPE_SOCKET (lossy_socket_get_type ())
+
+G_DECLARE_FINAL_TYPE (LossySocket, lossy_socket, LOSSY, SOCKET, GObject)
+
+GDatagramBased *lossy_socket_new (GDatagramBased  *base_socket,
+                                  IOPredicateFunc  predicate_func,
+                                  gpointer         predicate_data);
+
+G_END_DECLS
diff --git a/tls/tests/meson.build b/tls/tests/meson.build
index 6f2af7e..b7c9cd1 100644
--- a/tls/tests/meson.build
+++ b/tls/tests/meson.build
@@ -46,7 +46,7 @@ test_programs = [
   ['file-database', [], deps, [], []],
   ['connection', ['mock-interaction.c'], deps, [], [mock_pkcs11_module]],
 # DTLS tests are disabled until we fix https://gitlab.gnome.org/GNOME/glib-networking/issues/49
-#  ['dtls-connection', ['mock-interaction.c'], deps, [], [mock_pkcs11_module]],
+#  ['dtls-connection', ['mock-interaction.c', 'lossy-socket.c'], deps, [], [mock_pkcs11_module]],
 ]
 
 foreach backend: backends


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