Merge "Optional autogen.sh flag --enable-kdbus-transport added allowing to compile...
[platform/upstream/dbus.git] / test / corrupt.c
index eb8e049..1a7d446 100644 (file)
 #include <gio/gio.h>
 
 #include <dbus/dbus.h>
-#include <dbus/dbus-glib-lowlevel.h>
+
+#include "test-utils.h"
 
 typedef struct {
     DBusError e;
+    TestMainContext *ctx;
 
     DBusServer *server;
     DBusConnection *server_conn;
@@ -72,13 +74,14 @@ new_conn_cb (DBusServer *server,
 
   g_assert (f->server_conn == NULL);
   f->server_conn = dbus_connection_ref (server_conn);
-  dbus_connection_setup_with_g_main (server_conn, NULL);
+  test_connection_setup (f->ctx, server_conn);
 }
 
 static void
 setup (Fixture *f,
     gconstpointer addr)
 {
+  f->ctx = test_main_context_get ();
   dbus_error_init (&f->e);
   g_queue_init (&f->client_messages);
 
@@ -88,7 +91,7 @@ setup (Fixture *f,
 
   dbus_server_set_new_connection_function (f->server,
       new_conn_cb, f, NULL);
-  dbus_server_setup_with_g_main (f->server, NULL);
+  test_server_setup (f->ctx, f->server);
 }
 
 static void
@@ -103,12 +106,12 @@ test_connect (Fixture *f,
       dbus_server_get_address (f->server), &f->e);
   assert_no_error (&f->e);
   g_assert (f->client_conn != NULL);
-  dbus_connection_setup_with_g_main (f->client_conn, NULL);
+  test_connection_setup (f->ctx, f->client_conn);
 
   while (f->server_conn == NULL)
     {
       g_print (".");
-      g_main_context_iteration (NULL, TRUE);
+      test_main_context_iterate (f->ctx, TRUE);
     }
 
   have_mem = dbus_connection_add_filter (f->client_conn,
@@ -137,7 +140,7 @@ test_message (Fixture *f,
   while (g_queue_is_empty (&f->client_messages))
     {
       g_print (".");
-      g_main_context_iteration (NULL, TRUE);
+      test_main_context_iterate (f->ctx, TRUE);
     }
 
   g_assert_cmpuint (g_queue_get_length (&f->client_messages), ==, 1);
@@ -160,6 +163,38 @@ test_message (Fixture *f,
   dbus_message_unref (outgoing);
 }
 
+static void
+send_n_bytes (GSocket *socket,
+              const gchar *blob,
+              gssize blob_len)
+{
+  gssize len, total_sent;
+  GError *gerror = NULL;
+
+  total_sent = 0;
+
+  while (total_sent < blob_len)
+    {
+      len = g_socket_send (socket,
+                           blob + total_sent,
+                           blob_len - total_sent,
+                           NULL, &gerror);
+
+      /* this is NULL-safe: a NULL error does not match */
+      if (g_error_matches (gerror, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK))
+        {
+          /* we could wait for G_IO_OUT, but life's too short; just sleep */
+          g_clear_error (&gerror);
+          g_usleep (G_USEC_PER_SEC / 10);
+          continue;
+        }
+
+      g_assert_no_error (gerror);
+      g_assert (len >= 0);
+      total_sent += len;
+    }
+}
+
 /* Enough bytes for it to be obvious that this connection is broken */
 #define CORRUPT_LEN 1024
 
@@ -174,7 +209,6 @@ test_corrupt (Fixture *f,
   GSocket *socket;
   GError *gerror = NULL;
   int fd;
-  gssize len, total_sent;
   DBusMessage *incoming;
 
   test_message (f, addr);
@@ -191,24 +225,14 @@ test_corrupt (Fixture *f,
   g_assert_no_error (gerror);
   g_assert (socket != NULL);
 
-  total_sent = 0;
-
-  while (total_sent < CORRUPT_LEN)
-    {
-      len = g_socket_send_with_blocking (socket,
-          not_a_dbus_message + total_sent, CORRUPT_LEN - total_sent,
-          TRUE, NULL, &gerror);
-      g_assert_no_error (gerror);
-      g_assert (len >= 0);
-      total_sent += len;
-    }
+  send_n_bytes (socket, not_a_dbus_message, CORRUPT_LEN);
 
   /* Now spin on the client connection: the server just sent it complete
    * rubbish, so it should disconnect */
   while (g_queue_is_empty (&f->client_messages))
     {
       g_print (".");
-      g_main_context_iteration (NULL, TRUE);
+      test_main_context_iterate (f->ctx, TRUE);
     }
 
   incoming = g_queue_pop_head (&f->client_messages);
@@ -225,6 +249,102 @@ test_corrupt (Fixture *f,
       "/org/freedesktop/DBus/Local");
 
   dbus_message_unref (incoming);
+
+  /* Free the DBusConnection before the GSocket, because GSocket is
+   * going to close our fd. GSocket tolerates closing an already-closed
+   * fd, whereas DBusLoop + DBusSocketSetEpoll doesn't. On Unix
+   * we could use dup() but that isn't portable to Windows :-(
+   */
+  dbus_connection_close (f->server_conn);
+  dbus_connection_unref (f->server_conn);
+  f->server_conn = NULL;
+
+  g_object_unref (socket);
+}
+
+static void
+test_byte_order (Fixture *f,
+    gconstpointer addr)
+{
+  GSocket *socket;
+  GError *gerror = NULL;
+  int fd;
+  char *blob;
+  const gchar *arg = not_a_dbus_message;
+  const gchar * const *args = &arg;
+  int blob_len;
+  DBusMessage *message;
+  dbus_bool_t mem;
+
+  test_message (f, addr);
+
+  message = dbus_message_new_signal ("/", "a.b", "c");
+  g_assert (message != NULL);
+  /* Append 0xFF bytes, so that the length of the body when byte-swapped
+   * is 0xFF000000, which is invalid */
+  mem = dbus_message_append_args (message,
+      DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &args, 0xFF,
+      DBUS_TYPE_INVALID);
+  g_assert (mem);
+  mem = dbus_message_marshal (message, &blob, &blob_len);
+  g_assert (mem);
+  g_assert_cmpuint (blob_len, >, 0xFF);
+  g_assert (blob != NULL);
+
+  dbus_message_unref (message);
+
+  /* Break the message by changing its claimed byte order, without actually
+   * byteswapping anything. We happen to know that byte order is the first
+   * byte. */
+  if (blob[0] == 'B')
+    blob[0] = 'l';
+  else
+    blob[0] = 'B';
+
+  /* OK, now the connection is working, let's break it */
+
+  dbus_connection_flush (f->server_conn);
+
+  if (!dbus_connection_get_socket (f->server_conn, &fd))
+    g_error ("failed to steal fd from server connection");
+
+  socket = g_socket_new_from_fd (fd, &gerror);
+  g_assert_no_error (gerror);
+  g_assert (socket != NULL);
+
+  send_n_bytes (socket, blob, blob_len);
+
+  dbus_free (blob);
+
+  /* Now spin on the client connection: the server just sent it a faulty
+   * message, so it should disconnect */
+  while (g_queue_is_empty (&f->client_messages))
+    {
+      g_print (".");
+      test_main_context_iterate (f->ctx, TRUE);
+    }
+
+  message = g_queue_pop_head (&f->client_messages);
+
+  g_assert (!dbus_message_contains_unix_fds (message));
+  g_assert_cmpstr (dbus_message_get_destination (message), ==, NULL);
+  g_assert_cmpstr (dbus_message_get_error_name (message), ==, NULL);
+  g_assert_cmpstr (dbus_message_get_interface (message), ==,
+      "org.freedesktop.DBus.Local");
+  g_assert_cmpstr (dbus_message_get_member (message), ==, "Disconnected");
+  g_assert_cmpstr (dbus_message_get_sender (message), ==, NULL);
+  g_assert_cmpstr (dbus_message_get_signature (message), ==, "");
+  g_assert_cmpstr (dbus_message_get_path (message), ==,
+      "/org/freedesktop/DBus/Local");
+
+  dbus_message_unref (message);
+
+  /* Free the DBusConnection before the GSocket, as above. */
+  dbus_connection_close (f->server_conn);
+  dbus_connection_unref (f->server_conn);
+  f->server_conn = NULL;
+
+  g_object_unref (socket);
 }
 
 static void
@@ -251,6 +371,8 @@ teardown (Fixture *f,
       dbus_server_unref (f->server);
       f->server = NULL;
     }
+
+  test_main_context_unref (f->ctx);
 }
 
 int
@@ -268,5 +390,8 @@ main (int argc,
       test_corrupt, teardown);
 #endif
 
+  g_test_add ("/corrupt/byte-order/tcp", Fixture, "tcp:host=127.0.0.1", setup,
+      test_byte_order, teardown);
+
   return g_test_run ();
 }