Merge "Optional autogen.sh flag --enable-kdbus-transport added allowing to compile...
[platform/upstream/dbus.git] / test / corrupt.c
1 /* Regression test for being disconnected by a corrupt message (fd.o #15578)
2  *
3  * Author: Simon McVittie <simon.mcvittie@collabora.co.uk>
4  * Copyright © 2010-2011 Nokia Corporation
5  *
6  * Permission is hereby granted, free of charge, to any person
7  * obtaining a copy of this software and associated documentation files
8  * (the "Software"), to deal in the Software without restriction,
9  * including without limitation the rights to use, copy, modify, merge,
10  * publish, distribute, sublicense, and/or sell copies of the Software,
11  * and to permit persons to whom the Software is furnished to do so,
12  * subject to the following conditions:
13  *
14  * The above copyright notice and this permission notice shall be
15  * included in all copies or substantial portions of the Software.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
21  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
22  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
23  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24  * SOFTWARE.
25  */
26
27 #include <config.h>
28
29 #include <glib.h>
30 #include <gio/gio.h>
31
32 #include <dbus/dbus.h>
33
34 #include "test-utils.h"
35
36 typedef struct {
37     DBusError e;
38     TestMainContext *ctx;
39
40     DBusServer *server;
41     DBusConnection *server_conn;
42     /* queue of DBusMessage */
43     GQueue client_messages;
44
45     DBusConnection *client_conn;
46 } Fixture;
47
48 static void
49 assert_no_error (const DBusError *e)
50 {
51   if (G_UNLIKELY (dbus_error_is_set (e)))
52     g_error ("expected success but got error: %s: %s", e->name, e->message);
53 }
54
55 static DBusHandlerResult
56 client_message_cb (DBusConnection *client_conn,
57     DBusMessage *message,
58     void *data)
59 {
60   Fixture *f = data;
61
62   g_assert (client_conn == f->client_conn);
63   g_queue_push_tail (&f->client_messages, dbus_message_ref (message));
64
65   return DBUS_HANDLER_RESULT_HANDLED;
66 }
67
68 static void
69 new_conn_cb (DBusServer *server,
70     DBusConnection *server_conn,
71     void *data)
72 {
73   Fixture *f = data;
74
75   g_assert (f->server_conn == NULL);
76   f->server_conn = dbus_connection_ref (server_conn);
77   test_connection_setup (f->ctx, server_conn);
78 }
79
80 static void
81 setup (Fixture *f,
82     gconstpointer addr)
83 {
84   f->ctx = test_main_context_get ();
85   dbus_error_init (&f->e);
86   g_queue_init (&f->client_messages);
87
88   f->server = dbus_server_listen (addr, &f->e);
89   assert_no_error (&f->e);
90   g_assert (f->server != NULL);
91
92   dbus_server_set_new_connection_function (f->server,
93       new_conn_cb, f, NULL);
94   test_server_setup (f->ctx, f->server);
95 }
96
97 static void
98 test_connect (Fixture *f,
99     gconstpointer addr G_GNUC_UNUSED)
100 {
101   dbus_bool_t have_mem;
102
103   g_assert (f->server_conn == NULL);
104
105   f->client_conn = dbus_connection_open_private (
106       dbus_server_get_address (f->server), &f->e);
107   assert_no_error (&f->e);
108   g_assert (f->client_conn != NULL);
109   test_connection_setup (f->ctx, f->client_conn);
110
111   while (f->server_conn == NULL)
112     {
113       g_print (".");
114       test_main_context_iterate (f->ctx, TRUE);
115     }
116
117   have_mem = dbus_connection_add_filter (f->client_conn,
118       client_message_cb, f, NULL);
119   g_assert (have_mem);
120 }
121
122 static void
123 test_message (Fixture *f,
124     gconstpointer addr)
125 {
126   dbus_bool_t have_mem;
127   dbus_uint32_t serial;
128   DBusMessage *outgoing, *incoming;
129
130   test_connect (f, addr);
131
132   outgoing = dbus_message_new_signal ("/com/example/Hello",
133       "com.example.Hello", "Greeting");
134   g_assert (outgoing != NULL);
135
136   have_mem = dbus_connection_send (f->server_conn, outgoing, &serial);
137   g_assert (have_mem);
138   g_assert (serial != 0);
139
140   while (g_queue_is_empty (&f->client_messages))
141     {
142       g_print (".");
143       test_main_context_iterate (f->ctx, TRUE);
144     }
145
146   g_assert_cmpuint (g_queue_get_length (&f->client_messages), ==, 1);
147
148   incoming = g_queue_pop_head (&f->client_messages);
149
150   g_assert (!dbus_message_contains_unix_fds (incoming));
151   g_assert_cmpstr (dbus_message_get_destination (incoming), ==, NULL);
152   g_assert_cmpstr (dbus_message_get_error_name (incoming), ==, NULL);
153   g_assert_cmpstr (dbus_message_get_interface (incoming), ==,
154       "com.example.Hello");
155   g_assert_cmpstr (dbus_message_get_member (incoming), ==, "Greeting");
156   g_assert_cmpstr (dbus_message_get_sender (incoming), ==, NULL);
157   g_assert_cmpstr (dbus_message_get_signature (incoming), ==, "");
158   g_assert_cmpstr (dbus_message_get_path (incoming), ==, "/com/example/Hello");
159   g_assert_cmpuint (dbus_message_get_serial (incoming), ==, serial);
160
161   dbus_message_unref (incoming);
162
163   dbus_message_unref (outgoing);
164 }
165
166 static void
167 send_n_bytes (GSocket *socket,
168               const gchar *blob,
169               gssize blob_len)
170 {
171   gssize len, total_sent;
172   GError *gerror = NULL;
173
174   total_sent = 0;
175
176   while (total_sent < blob_len)
177     {
178       len = g_socket_send (socket,
179                            blob + total_sent,
180                            blob_len - total_sent,
181                            NULL, &gerror);
182
183       /* this is NULL-safe: a NULL error does not match */
184       if (g_error_matches (gerror, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK))
185         {
186           /* we could wait for G_IO_OUT, but life's too short; just sleep */
187           g_clear_error (&gerror);
188           g_usleep (G_USEC_PER_SEC / 10);
189           continue;
190         }
191
192       g_assert_no_error (gerror);
193       g_assert (len >= 0);
194       total_sent += len;
195     }
196 }
197
198 /* Enough bytes for it to be obvious that this connection is broken */
199 #define CORRUPT_LEN 1024
200
201 /* All-zero is not a valid D-Bus message header - for a start, this is
202  * protocol version 1, not 0 */
203 static const gchar not_a_dbus_message[CORRUPT_LEN] = { 0 };
204
205 static void
206 test_corrupt (Fixture *f,
207     gconstpointer addr)
208 {
209   GSocket *socket;
210   GError *gerror = NULL;
211   int fd;
212   DBusMessage *incoming;
213
214   test_message (f, addr);
215
216   dbus_connection_flush (f->server_conn);
217
218   /* OK, now the connection is working, let's break it! Don't try this
219    * at home; splicing arbitrary bytes into the middle of the stream is
220    * specifically documented as not a valid thing to do. Who'd have thought? */
221   if (!dbus_connection_get_socket (f->server_conn, &fd))
222     g_error ("failed to steal fd from server connection");
223
224   socket = g_socket_new_from_fd (fd, &gerror);
225   g_assert_no_error (gerror);
226   g_assert (socket != NULL);
227
228   send_n_bytes (socket, not_a_dbus_message, CORRUPT_LEN);
229
230   /* Now spin on the client connection: the server just sent it complete
231    * rubbish, so it should disconnect */
232   while (g_queue_is_empty (&f->client_messages))
233     {
234       g_print (".");
235       test_main_context_iterate (f->ctx, TRUE);
236     }
237
238   incoming = g_queue_pop_head (&f->client_messages);
239
240   g_assert (!dbus_message_contains_unix_fds (incoming));
241   g_assert_cmpstr (dbus_message_get_destination (incoming), ==, NULL);
242   g_assert_cmpstr (dbus_message_get_error_name (incoming), ==, NULL);
243   g_assert_cmpstr (dbus_message_get_interface (incoming), ==,
244       "org.freedesktop.DBus.Local");
245   g_assert_cmpstr (dbus_message_get_member (incoming), ==, "Disconnected");
246   g_assert_cmpstr (dbus_message_get_sender (incoming), ==, NULL);
247   g_assert_cmpstr (dbus_message_get_signature (incoming), ==, "");
248   g_assert_cmpstr (dbus_message_get_path (incoming), ==,
249       "/org/freedesktop/DBus/Local");
250
251   dbus_message_unref (incoming);
252
253   /* Free the DBusConnection before the GSocket, because GSocket is
254    * going to close our fd. GSocket tolerates closing an already-closed
255    * fd, whereas DBusLoop + DBusSocketSetEpoll doesn't. On Unix
256    * we could use dup() but that isn't portable to Windows :-(
257    */
258   dbus_connection_close (f->server_conn);
259   dbus_connection_unref (f->server_conn);
260   f->server_conn = NULL;
261
262   g_object_unref (socket);
263 }
264
265 static void
266 test_byte_order (Fixture *f,
267     gconstpointer addr)
268 {
269   GSocket *socket;
270   GError *gerror = NULL;
271   int fd;
272   char *blob;
273   const gchar *arg = not_a_dbus_message;
274   const gchar * const *args = &arg;
275   int blob_len;
276   DBusMessage *message;
277   dbus_bool_t mem;
278
279   test_message (f, addr);
280
281   message = dbus_message_new_signal ("/", "a.b", "c");
282   g_assert (message != NULL);
283   /* Append 0xFF bytes, so that the length of the body when byte-swapped
284    * is 0xFF000000, which is invalid */
285   mem = dbus_message_append_args (message,
286       DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &args, 0xFF,
287       DBUS_TYPE_INVALID);
288   g_assert (mem);
289   mem = dbus_message_marshal (message, &blob, &blob_len);
290   g_assert (mem);
291   g_assert_cmpuint (blob_len, >, 0xFF);
292   g_assert (blob != NULL);
293
294   dbus_message_unref (message);
295
296   /* Break the message by changing its claimed byte order, without actually
297    * byteswapping anything. We happen to know that byte order is the first
298    * byte. */
299   if (blob[0] == 'B')
300     blob[0] = 'l';
301   else
302     blob[0] = 'B';
303
304   /* OK, now the connection is working, let's break it */
305
306   dbus_connection_flush (f->server_conn);
307
308   if (!dbus_connection_get_socket (f->server_conn, &fd))
309     g_error ("failed to steal fd from server connection");
310
311   socket = g_socket_new_from_fd (fd, &gerror);
312   g_assert_no_error (gerror);
313   g_assert (socket != NULL);
314
315   send_n_bytes (socket, blob, blob_len);
316
317   dbus_free (blob);
318
319   /* Now spin on the client connection: the server just sent it a faulty
320    * message, so it should disconnect */
321   while (g_queue_is_empty (&f->client_messages))
322     {
323       g_print (".");
324       test_main_context_iterate (f->ctx, TRUE);
325     }
326
327   message = g_queue_pop_head (&f->client_messages);
328
329   g_assert (!dbus_message_contains_unix_fds (message));
330   g_assert_cmpstr (dbus_message_get_destination (message), ==, NULL);
331   g_assert_cmpstr (dbus_message_get_error_name (message), ==, NULL);
332   g_assert_cmpstr (dbus_message_get_interface (message), ==,
333       "org.freedesktop.DBus.Local");
334   g_assert_cmpstr (dbus_message_get_member (message), ==, "Disconnected");
335   g_assert_cmpstr (dbus_message_get_sender (message), ==, NULL);
336   g_assert_cmpstr (dbus_message_get_signature (message), ==, "");
337   g_assert_cmpstr (dbus_message_get_path (message), ==,
338       "/org/freedesktop/DBus/Local");
339
340   dbus_message_unref (message);
341
342   /* Free the DBusConnection before the GSocket, as above. */
343   dbus_connection_close (f->server_conn);
344   dbus_connection_unref (f->server_conn);
345   f->server_conn = NULL;
346
347   g_object_unref (socket);
348 }
349
350 static void
351 teardown (Fixture *f,
352     gconstpointer addr G_GNUC_UNUSED)
353 {
354   if (f->client_conn != NULL)
355     {
356       dbus_connection_close (f->client_conn);
357       dbus_connection_unref (f->client_conn);
358       f->client_conn = NULL;
359     }
360
361   if (f->server_conn != NULL)
362     {
363       dbus_connection_close (f->server_conn);
364       dbus_connection_unref (f->server_conn);
365       f->server_conn = NULL;
366     }
367
368   if (f->server != NULL)
369     {
370       dbus_server_disconnect (f->server);
371       dbus_server_unref (f->server);
372       f->server = NULL;
373     }
374
375   test_main_context_unref (f->ctx);
376 }
377
378 int
379 main (int argc,
380     char **argv)
381 {
382   g_test_init (&argc, &argv, NULL);
383   g_type_init ();
384
385   g_test_add ("/corrupt/tcp", Fixture, "tcp:host=127.0.0.1", setup,
386       test_corrupt, teardown);
387
388 #ifdef DBUS_UNIX
389   g_test_add ("/corrupt/unix", Fixture, "unix:tmpdir=/tmp", setup,
390       test_corrupt, teardown);
391 #endif
392
393   g_test_add ("/corrupt/byte-order/tcp", Fixture, "tcp:host=127.0.0.1", setup,
394       test_byte_order, teardown);
395
396   return g_test_run ();
397 }