new test for being disconnected by a corrupt message stream
[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 #include <dbus/dbus-glib-lowlevel.h>
34
35 typedef struct {
36     DBusError e;
37
38     DBusServer *server;
39     DBusConnection *server_conn;
40     /* queue of DBusMessage */
41     GQueue client_messages;
42
43     DBusConnection *client_conn;
44 } Fixture;
45
46 static void
47 assert_no_error (const DBusError *e)
48 {
49   if (G_UNLIKELY (dbus_error_is_set (e)))
50     g_error ("expected success but got error: %s: %s", e->name, e->message);
51 }
52
53 static DBusHandlerResult
54 client_message_cb (DBusConnection *client_conn,
55     DBusMessage *message,
56     void *data)
57 {
58   Fixture *f = data;
59
60   g_assert (client_conn == f->client_conn);
61   g_queue_push_tail (&f->client_messages, dbus_message_ref (message));
62
63   return DBUS_HANDLER_RESULT_HANDLED;
64 }
65
66 static void
67 new_conn_cb (DBusServer *server,
68     DBusConnection *server_conn,
69     void *data)
70 {
71   Fixture *f = data;
72
73   g_assert (f->server_conn == NULL);
74   f->server_conn = dbus_connection_ref (server_conn);
75   dbus_connection_setup_with_g_main (server_conn, NULL);
76 }
77
78 static void
79 setup (Fixture *f,
80     gconstpointer addr)
81 {
82   dbus_error_init (&f->e);
83   g_queue_init (&f->client_messages);
84
85   f->server = dbus_server_listen (addr, &f->e);
86   assert_no_error (&f->e);
87   g_assert (f->server != NULL);
88
89   dbus_server_set_new_connection_function (f->server,
90       new_conn_cb, f, NULL);
91   dbus_server_setup_with_g_main (f->server, NULL);
92 }
93
94 static void
95 test_connect (Fixture *f,
96     gconstpointer addr G_GNUC_UNUSED)
97 {
98   dbus_bool_t have_mem;
99
100   g_assert (f->server_conn == NULL);
101
102   f->client_conn = dbus_connection_open_private (
103       dbus_server_get_address (f->server), &f->e);
104   assert_no_error (&f->e);
105   g_assert (f->client_conn != NULL);
106   dbus_connection_setup_with_g_main (f->client_conn, NULL);
107
108   while (f->server_conn == NULL)
109     {
110       g_print (".");
111       g_main_context_iteration (NULL, TRUE);
112     }
113
114   have_mem = dbus_connection_add_filter (f->client_conn,
115       client_message_cb, f, NULL);
116   g_assert (have_mem);
117 }
118
119 static void
120 test_message (Fixture *f,
121     gconstpointer addr)
122 {
123   dbus_bool_t have_mem;
124   dbus_uint32_t serial;
125   DBusMessage *outgoing, *incoming;
126
127   test_connect (f, addr);
128
129   outgoing = dbus_message_new_signal ("/com/example/Hello",
130       "com.example.Hello", "Greeting");
131   g_assert (outgoing != NULL);
132
133   have_mem = dbus_connection_send (f->server_conn, outgoing, &serial);
134   g_assert (have_mem);
135   g_assert (serial != 0);
136
137   while (g_queue_is_empty (&f->client_messages))
138     {
139       g_print (".");
140       g_main_context_iteration (NULL, TRUE);
141     }
142
143   g_assert_cmpuint (g_queue_get_length (&f->client_messages), ==, 1);
144
145   incoming = g_queue_pop_head (&f->client_messages);
146
147   g_assert (!dbus_message_contains_unix_fds (incoming));
148   g_assert_cmpstr (dbus_message_get_destination (incoming), ==, NULL);
149   g_assert_cmpstr (dbus_message_get_error_name (incoming), ==, NULL);
150   g_assert_cmpstr (dbus_message_get_interface (incoming), ==,
151       "com.example.Hello");
152   g_assert_cmpstr (dbus_message_get_member (incoming), ==, "Greeting");
153   g_assert_cmpstr (dbus_message_get_sender (incoming), ==, NULL);
154   g_assert_cmpstr (dbus_message_get_signature (incoming), ==, "");
155   g_assert_cmpstr (dbus_message_get_path (incoming), ==, "/com/example/Hello");
156   g_assert_cmpuint (dbus_message_get_serial (incoming), ==, serial);
157
158   dbus_message_unref (incoming);
159
160   dbus_message_unref (outgoing);
161 }
162
163 /* Enough bytes for it to be obvious that this connection is broken */
164 #define CORRUPT_LEN 1024
165
166 /* All-zero is not a valid D-Bus message header - for a start, this is
167  * protocol version 1, not 0 */
168 static const gchar not_a_dbus_message[CORRUPT_LEN] = { 0 };
169
170 static void
171 test_corrupt (Fixture *f,
172     gconstpointer addr)
173 {
174   GSocket *socket;
175   GError *gerror = NULL;
176   int fd;
177   gssize len, total_sent;
178   DBusMessage *incoming;
179
180   test_message (f, addr);
181
182   dbus_connection_flush (f->server_conn);
183
184   /* OK, now the connection is working, let's break it! Don't try this
185    * at home; splicing arbitrary bytes into the middle of the stream is
186    * specifically documented as not a valid thing to do. Who'd have thought? */
187   if (!dbus_connection_get_socket (f->server_conn, &fd))
188     g_error ("failed to steal fd from server connection");
189
190   socket = g_socket_new_from_fd (fd, &gerror);
191   g_assert_no_error (gerror);
192   g_assert (socket != NULL);
193
194   total_sent = 0;
195
196   while (total_sent < CORRUPT_LEN)
197     {
198       len = g_socket_send_with_blocking (socket,
199           not_a_dbus_message + total_sent, CORRUPT_LEN - total_sent,
200           TRUE, NULL, &gerror);
201       g_assert_no_error (gerror);
202       g_assert (len >= 0);
203       total_sent += len;
204     }
205
206   /* Now spin on the client connection: the server just sent it complete
207    * rubbish, so it should disconnect */
208   while (g_queue_is_empty (&f->client_messages))
209     {
210       g_print (".");
211       g_main_context_iteration (NULL, TRUE);
212     }
213
214   incoming = g_queue_pop_head (&f->client_messages);
215
216   g_assert (!dbus_message_contains_unix_fds (incoming));
217   g_assert_cmpstr (dbus_message_get_destination (incoming), ==, NULL);
218   g_assert_cmpstr (dbus_message_get_error_name (incoming), ==, NULL);
219   g_assert_cmpstr (dbus_message_get_interface (incoming), ==,
220       "org.freedesktop.DBus.Local");
221   g_assert_cmpstr (dbus_message_get_member (incoming), ==, "Disconnected");
222   g_assert_cmpstr (dbus_message_get_sender (incoming), ==, NULL);
223   g_assert_cmpstr (dbus_message_get_signature (incoming), ==, "");
224   g_assert_cmpstr (dbus_message_get_path (incoming), ==,
225       "/org/freedesktop/DBus/Local");
226
227   dbus_message_unref (incoming);
228 }
229
230 static void
231 teardown (Fixture *f,
232     gconstpointer addr G_GNUC_UNUSED)
233 {
234   if (f->client_conn != NULL)
235     {
236       dbus_connection_close (f->client_conn);
237       dbus_connection_unref (f->client_conn);
238       f->client_conn = NULL;
239     }
240
241   if (f->server_conn != NULL)
242     {
243       dbus_connection_close (f->server_conn);
244       dbus_connection_unref (f->server_conn);
245       f->server_conn = NULL;
246     }
247
248   if (f->server != NULL)
249     {
250       dbus_server_disconnect (f->server);
251       dbus_server_unref (f->server);
252       f->server = NULL;
253     }
254 }
255
256 int
257 main (int argc,
258     char **argv)
259 {
260   g_test_init (&argc, &argv, NULL);
261   g_type_init ();
262
263   g_test_add ("/corrupt/tcp", Fixture, "tcp:host=127.0.0.1", setup,
264       test_corrupt, teardown);
265
266 #ifdef DBUS_UNIX
267   g_test_add ("/corrupt/unix", Fixture, "unix:tmpdir=/tmp", setup,
268       test_corrupt, teardown);
269 #endif
270
271   return g_test_run ();
272 }