1.9.3
[platform/upstream/dbus.git] / test / fdpass.c
1 /*
2  * Copyright © 2010-2012 Nokia Corporation
3  * Copyright © 2014 Collabora Ltd.
4  *
5  * Permission is hereby granted, free of charge, to any person
6  * obtaining a copy of this software and associated documentation files
7  * (the "Software"), to deal in the Software without restriction,
8  * including without limitation the rights to use, copy, modify, merge,
9  * publish, distribute, sublicense, and/or sell copies of the Software,
10  * and to permit persons to whom the Software is furnished to do so,
11  * subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be
14  * included in all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
20  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
21  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23  * SOFTWARE.
24  */
25
26 #include <config.h>
27
28 #include <dbus/dbus.h>
29 #include <dbus/dbus-internals.h>
30 #include <dbus/dbus-sysdeps.h>
31
32 #include <glib.h>
33
34 #include <string.h>
35
36 #ifdef G_OS_UNIX
37 # include <dbus/dbus-sysdeps-unix.h>
38
39 # include <errno.h>
40 # include <fcntl.h>
41 # ifdef HAVE_SYS_RESOURCE_H
42 #   include <sys/resource.h>
43 # endif
44 # include <sys/stat.h>
45 # include <sys/time.h>
46 # include <sys/types.h>
47 # include <unistd.h>
48 #endif
49
50 #include "test-utils.h"
51
52 /* Arbitrary; included here to avoid relying on the default */
53 #define MAX_MESSAGE_UNIX_FDS 20
54 /* This test won't work on Linux unless this is true. */
55 _DBUS_STATIC_ASSERT (MAX_MESSAGE_UNIX_FDS <= 253);
56
57 /* Arbitrary; included here to avoid relying on the default. */
58 #define MAX_INCOMING_UNIX_FDS (MAX_MESSAGE_UNIX_FDS * 4)
59
60 /* Arbitrary, except that MAX_MESSAGE_UNIX_FDS * SOME_MESSAGES must be
61  * less than the process's file descriptor limit. */
62 #define SOME_MESSAGES 50
63
64 /* Linux won't allow more than 253 fds per sendmsg(). */
65 #define TOO_MANY_FDS 255
66 _DBUS_STATIC_ASSERT (MAX_MESSAGE_UNIX_FDS < TOO_MANY_FDS);
67
68 /* As in test/relay.c, this is a miniature dbus-daemon: we relay messages
69  * from the client on the left to the client on the right.
70  *
71  * left      socket     left      dispatch     right    socket     right
72  * client ===========>  server --------------> server ===========> client
73  * conn                 conn                   conn                conn
74  */
75
76 typedef struct {
77     TestMainContext *ctx;
78     DBusError e;
79
80     DBusServer *server;
81
82     DBusConnection *left_client_conn;
83     DBusConnection *left_server_conn;
84
85     DBusConnection *right_server_conn;
86     DBusConnection *right_client_conn;
87     /* queue of DBusMessage received by right_client_conn */
88     GQueue messages;
89
90     int fd_before;
91 } Fixture;
92
93 #if !GLIB_CHECK_VERSION (2, 38, 0)
94 #define g_test_skip(s) my_test_skip (s)
95 static inline void my_test_skip (const gchar *s)
96 {
97   g_message ("SKIP: %s", s);
98 }
99 #endif
100
101 #ifdef HAVE_UNIX_FD_PASSING
102
103 static void oom (const gchar *doing) G_GNUC_NORETURN;
104
105 static void
106 oom (const gchar *doing)
107 {
108   g_error ("out of memory (%s)", doing);
109 }
110
111 static void
112 assert_no_error (const DBusError *e)
113 {
114   if (G_UNLIKELY (dbus_error_is_set (e)))
115     g_error ("expected success but got error: %s: %s", e->name, e->message);
116 }
117
118 static DBusHandlerResult
119 left_server_message_cb (DBusConnection *server_conn,
120     DBusMessage *message,
121     void *data)
122 {
123   Fixture *f = data;
124
125   g_assert (server_conn == f->left_server_conn);
126   g_assert (f->right_server_conn != NULL);
127
128   dbus_connection_send (f->right_server_conn, message, NULL);
129
130   return DBUS_HANDLER_RESULT_HANDLED;
131 }
132
133 static DBusHandlerResult
134 right_client_message_cb (DBusConnection *client_conn,
135     DBusMessage *message,
136     void *data)
137 {
138   Fixture *f = data;
139
140   g_assert (client_conn == f->right_client_conn);
141   g_queue_push_tail (&f->messages, dbus_message_ref (message));
142
143   return DBUS_HANDLER_RESULT_HANDLED;
144 }
145
146 static void
147 new_conn_cb (DBusServer *server,
148     DBusConnection *server_conn,
149     void *data)
150 {
151   Fixture *f = data;
152
153   dbus_connection_set_max_message_unix_fds (server_conn,
154       MAX_MESSAGE_UNIX_FDS);
155   dbus_connection_set_max_received_unix_fds (server_conn,
156       MAX_INCOMING_UNIX_FDS);
157
158   if (f->left_server_conn == NULL)
159     {
160       f->left_server_conn = dbus_connection_ref (server_conn);
161
162       if (!dbus_connection_add_filter (server_conn,
163           left_server_message_cb, f, NULL))
164         oom ("adding filter");
165     }
166   else
167     {
168       g_assert (f->right_server_conn == NULL);
169       f->right_server_conn = dbus_connection_ref (server_conn);
170     }
171
172   test_connection_setup (f->ctx, server_conn);
173 }
174
175 static void
176 test_connect (Fixture *f,
177     gconstpointer data G_GNUC_UNUSED)
178 {
179   char *address;
180
181   g_assert (f->left_server_conn == NULL);
182   g_assert (f->right_server_conn == NULL);
183
184   address = dbus_server_get_address (f->server);
185   g_assert (address != NULL);
186
187   f->left_client_conn = dbus_connection_open_private (address, &f->e);
188   assert_no_error (&f->e);
189   g_assert (f->left_client_conn != NULL);
190   test_connection_setup (f->ctx, f->left_client_conn);
191
192   /* The left client connection is allowed to behave abusively. */
193   dbus_connection_set_max_message_unix_fds (f->left_client_conn, 1000);
194   dbus_connection_set_max_received_unix_fds (f->left_client_conn, 1000000);
195
196   while (f->left_server_conn == NULL)
197     {
198       g_print (".");
199       test_main_context_iterate (f->ctx, TRUE);
200     }
201
202   f->right_client_conn = dbus_connection_open_private (address, &f->e);
203   assert_no_error (&f->e);
204   g_assert (f->right_client_conn != NULL);
205   test_connection_setup (f->ctx, f->right_client_conn);
206
207   dbus_free (address);
208
209   while (f->right_server_conn == NULL)
210     {
211       g_print (".");
212       test_main_context_iterate (f->ctx, TRUE);
213     }
214
215   if (!dbus_connection_add_filter (f->right_client_conn,
216       right_client_message_cb, f, NULL))
217     oom ("adding filter");
218
219   /* The right client connection is allowed to queue all the messages. */
220   dbus_connection_set_max_message_unix_fds (f->right_client_conn, 1000);
221   dbus_connection_set_max_received_unix_fds (f->right_client_conn, 1000000);
222
223   while (!dbus_connection_get_is_authenticated (f->left_client_conn) ||
224       !dbus_connection_get_is_authenticated (f->right_client_conn) ||
225       !dbus_connection_get_is_authenticated (f->left_server_conn) ||
226       !dbus_connection_get_is_authenticated (f->right_server_conn))
227     {
228       g_printerr ("*");
229       test_main_context_iterate (f->ctx, TRUE);
230     }
231
232   if (!dbus_connection_can_send_type (f->left_client_conn,
233         DBUS_TYPE_UNIX_FD))
234     g_error ("left client connection cannot send Unix fds");
235
236   if (!dbus_connection_can_send_type (f->left_server_conn,
237         DBUS_TYPE_UNIX_FD))
238     g_error ("left server connection cannot send Unix fds");
239
240   if (!dbus_connection_can_send_type (f->right_client_conn,
241         DBUS_TYPE_UNIX_FD))
242     g_error ("right client connection cannot send Unix fds");
243
244   if (!dbus_connection_can_send_type (f->right_server_conn,
245         DBUS_TYPE_UNIX_FD))
246     g_error ("right server connection cannot send Unix fds");
247 }
248 #endif
249
250 static void
251 setup (Fixture *f,
252     gconstpointer data G_GNUC_UNUSED)
253 {
254 #ifdef HAVE_UNIX_FD_PASSING
255   /* We assume that anything with fd-passing supports the unix: transport */
256
257   f->ctx = test_main_context_get ();
258   dbus_error_init (&f->e);
259   g_queue_init (&f->messages);
260
261   f->server = dbus_server_listen ("unix:tmpdir=/tmp", &f->e);
262   assert_no_error (&f->e);
263   g_assert (f->server != NULL);
264
265   dbus_server_set_new_connection_function (f->server,
266       new_conn_cb, f, NULL);
267   test_server_setup (f->ctx, f->server);
268
269   f->fd_before = open ("/dev/null", O_RDONLY);
270
271   /* this should succeed on any reasonable Unix */
272   if (f->fd_before < 0)
273     g_error ("cannot open /dev/null for reading: %s", g_strerror (errno));
274
275   _dbus_fd_set_close_on_exec (f->fd_before);
276 #endif
277 }
278
279 static void
280 test_relay (Fixture *f,
281     gconstpointer data)
282 {
283 #ifdef HAVE_UNIX_FD_PASSING
284   /* We assume that any platform with working fd-passing is POSIX,
285    * and therefore has open() and fstat() */
286   dbus_uint32_t serial;
287   DBusMessage *outgoing, *incoming;
288   int fd_after;
289   struct stat stat_before;
290   struct stat stat_after;
291
292   test_connect (f, data);
293
294   outgoing = dbus_message_new_signal ("/com/example/Hello",
295       "com.example.Hello", "Greeting");
296   g_assert (outgoing != NULL);
297
298   if (!dbus_message_append_args (outgoing,
299         DBUS_TYPE_UNIX_FD, &f->fd_before,
300         DBUS_TYPE_INVALID))
301     oom ("appending fd");
302
303   if (!dbus_connection_send (f->left_client_conn, outgoing, &serial))
304     oom ("sending message");
305
306   dbus_message_unref (outgoing);
307
308   while (g_queue_get_length (&f->messages) < 1)
309     {
310       g_print (".");
311       test_main_context_iterate (f->ctx, TRUE);
312     }
313
314   g_assert_cmpuint (g_queue_get_length (&f->messages), ==, 1);
315
316   incoming = g_queue_pop_head (&f->messages);
317
318   g_assert (dbus_message_contains_unix_fds (incoming));
319   g_assert_cmpstr (dbus_message_get_destination (incoming), ==, NULL);
320   g_assert_cmpstr (dbus_message_get_error_name (incoming), ==, NULL);
321   g_assert_cmpstr (dbus_message_get_interface (incoming), ==,
322       "com.example.Hello");
323   g_assert_cmpstr (dbus_message_get_member (incoming), ==, "Greeting");
324   g_assert_cmpstr (dbus_message_get_sender (incoming), ==, NULL);
325   g_assert_cmpstr (dbus_message_get_signature (incoming), ==,
326       DBUS_TYPE_UNIX_FD_AS_STRING);
327   g_assert_cmpstr (dbus_message_get_path (incoming), ==, "/com/example/Hello");
328   g_assert_cmpuint (dbus_message_get_serial (incoming), ==, serial);
329
330   if (!dbus_message_get_args (incoming,
331         &f->e,
332         DBUS_TYPE_UNIX_FD, &fd_after,
333         DBUS_TYPE_INVALID))
334     g_error ("%s: %s", f->e.name, f->e.message);
335
336   assert_no_error (&f->e);
337
338   if (fstat (f->fd_before, &stat_before) < 0)
339     g_error ("%s", g_strerror (errno));
340
341   if (fstat (fd_after, &stat_after) < 0)
342     g_error ("%s", g_strerror (errno));
343
344   /* this seems like enough to say "it's the same file" */
345   g_assert_cmpint (stat_before.st_dev, ==, stat_after.st_dev);
346   g_assert_cmpint (stat_before.st_ino, ==, stat_after.st_ino);
347   g_assert_cmpint (stat_before.st_rdev, ==, stat_after.st_rdev);
348
349   dbus_message_unref (incoming);
350
351   if (close (fd_after) < 0)
352     g_error ("%s", g_strerror (errno));
353
354   g_assert (dbus_connection_get_is_connected (f->right_client_conn));
355   g_assert (dbus_connection_get_is_connected (f->right_server_conn));
356   g_assert (dbus_connection_get_is_connected (f->left_client_conn));
357   g_assert (dbus_connection_get_is_connected (f->left_server_conn));
358 #else
359   g_test_skip ("fd-passing not supported on this platform");
360 #endif
361 }
362
363 static void
364 test_limit (Fixture *f,
365     gconstpointer data)
366 {
367 #ifdef HAVE_UNIX_FD_PASSING
368   dbus_uint32_t serial;
369   DBusMessage *outgoing, *incoming;
370   int i;
371
372   test_connect (f, data);
373
374   outgoing = dbus_message_new_signal ("/com/example/Hello",
375       "com.example.Hello", "Greeting");
376   g_assert (outgoing != NULL);
377
378   for (i = 0; i < MAX_MESSAGE_UNIX_FDS; i++)
379     {
380       if (!dbus_message_append_args (outgoing,
381             DBUS_TYPE_UNIX_FD, &f->fd_before,
382             DBUS_TYPE_INVALID))
383         oom ("appending fd");
384     }
385
386   if (!dbus_connection_send (f->left_client_conn, outgoing, &serial))
387     oom ("sending message");
388
389   dbus_message_unref (outgoing);
390
391   while (g_queue_get_length (&f->messages) < 1)
392     {
393       g_print (".");
394       test_main_context_iterate (f->ctx, TRUE);
395     }
396
397   g_assert_cmpuint (g_queue_get_length (&f->messages), ==, 1);
398
399   incoming = g_queue_pop_head (&f->messages);
400
401   g_assert (dbus_message_contains_unix_fds (incoming));
402   g_assert_cmpstr (dbus_message_get_destination (incoming), ==, NULL);
403   g_assert_cmpstr (dbus_message_get_error_name (incoming), ==, NULL);
404   g_assert_cmpstr (dbus_message_get_interface (incoming), ==,
405       "com.example.Hello");
406   g_assert_cmpstr (dbus_message_get_member (incoming), ==, "Greeting");
407   g_assert_cmpstr (dbus_message_get_sender (incoming), ==, NULL);
408   g_assert_cmpstr (dbus_message_get_path (incoming), ==, "/com/example/Hello");
409   g_assert_cmpuint (dbus_message_get_serial (incoming), ==, serial);
410
411   dbus_message_unref (incoming);
412
413   g_assert (dbus_connection_get_is_connected (f->right_client_conn));
414   g_assert (dbus_connection_get_is_connected (f->right_server_conn));
415   g_assert (dbus_connection_get_is_connected (f->left_client_conn));
416   g_assert (dbus_connection_get_is_connected (f->left_server_conn));
417 #else
418   g_test_skip ("fd-passing not supported on this platform");
419 #endif
420 }
421
422 static void
423 test_too_many (Fixture *f,
424     gconstpointer data)
425 {
426 #ifdef HAVE_UNIX_FD_PASSING
427   DBusMessage *outgoing;
428   int i;
429
430   test_connect (f, data);
431
432   outgoing = dbus_message_new_signal ("/com/example/Hello",
433       "com.example.Hello", "Greeting");
434   g_assert (outgoing != NULL);
435
436   for (i = 0; i < MAX_MESSAGE_UNIX_FDS + GPOINTER_TO_UINT (data); i++)
437     {
438       if (!dbus_message_append_args (outgoing,
439             DBUS_TYPE_UNIX_FD, &f->fd_before,
440             DBUS_TYPE_INVALID))
441         oom ("appending fd");
442     }
443
444   if (!dbus_connection_send (f->left_client_conn, outgoing, NULL))
445     oom ("sending message");
446
447   dbus_message_unref (outgoing);
448
449   /* The sender is unceremoniously disconnected. */
450   while (dbus_connection_get_is_connected (f->left_client_conn) ||
451       dbus_connection_get_is_connected (f->left_server_conn))
452     {
453       g_print (".");
454       test_main_context_iterate (f->ctx, TRUE);
455     }
456
457   /* The message didn't get through without its fds. */
458   g_assert_cmpuint (g_queue_get_length (&f->messages), ==, 0);
459
460   /* The intended victim is unaffected by the left connection's
461    * misbehaviour. */
462   g_assert (dbus_connection_get_is_connected (f->right_client_conn));
463   g_assert (dbus_connection_get_is_connected (f->right_server_conn));
464 #else
465   g_test_skip ("fd-passing not supported on this platform");
466 #endif
467 }
468
469 static void
470 test_too_many_split (Fixture *f,
471     gconstpointer data)
472 {
473 #ifdef HAVE_UNIX_FD_PASSING
474   DBusMessage *outgoing;
475   int i;
476   int left_client_socket;
477   char *payload;
478   int payload_len;
479   DBusString buffer;
480   int fds[TOO_MANY_FDS];
481   int done;
482
483   /* This test deliberately pushes up against OS limits, so skip it
484    * if we don't have enough fds. 4 times the maximum per message
485    * ought to be enough: that will cover the message, the dup'd fds
486    * we actually send, the copy that we potentially receive, and some
487    * spare capacity for everything else. */
488 #ifdef HAVE_GETRLIMIT
489   struct rlimit lim;
490
491   if (getrlimit (RLIMIT_NOFILE, &lim) == 0)
492     {
493       if (lim.rlim_cur != RLIM_INFINITY &&
494           lim.rlim_cur < 4 * TOO_MANY_FDS)
495         {
496           g_test_skip ("not enough RLIMIT_NOFILE");
497           return;
498         }
499     }
500 #endif
501
502   test_connect (f, data);
503
504   outgoing = dbus_message_new_signal ("/com/example/Hello",
505       "com.example.Hello", "Greeting");
506   g_assert (outgoing != NULL);
507
508   /* TOO_MANY_FDS fds are far too many: in particular, Linux doesn't allow
509    * sending this many in a single sendmsg(). libdbus never splits
510    * a message between two sendmsg() calls if it can help it, and
511    * in particular it always sends all the fds with the first sendmsg(),
512    * but malicious senders might not be so considerate. */
513   for (i = 0; i < TOO_MANY_FDS; i++)
514     {
515       if (!dbus_message_append_args (outgoing,
516             DBUS_TYPE_UNIX_FD, &f->fd_before,
517             DBUS_TYPE_INVALID))
518         oom ("appending fd");
519     }
520
521   /* This probably shouldn't work for messages with fds, but it does,
522    * which is convenient for this sort of trickery. */
523   if (!dbus_message_marshal (outgoing, &payload, &payload_len))
524     oom ("marshalling message");
525
526   _dbus_string_init_const_len (&buffer, payload, payload_len);
527
528   for (i = 0; i < TOO_MANY_FDS; i++)
529     {
530       fds[i] = dup (f->fd_before);
531
532       if (fds[i] < 0)
533         g_error ("could not dup fd: %s", g_strerror (errno));
534     }
535
536   /* This is blatant cheating, and the API documentation specifically
537    * tells you not use this function in this way. Never do this
538    * in application code. */
539   if (!dbus_connection_get_socket (f->left_client_conn, &left_client_socket))
540     g_error ("'unix:' DBusConnection should have had a socket");
541
542   /* Just to be sure that we're at a message boundary. */
543   dbus_connection_flush (f->left_client_conn);
544
545   /* We have too many fds for one sendmsg(), so send the first half
546    * (rounding down if odd) with the first byte... */
547   done = _dbus_write_socket_with_unix_fds (left_client_socket, &buffer, 0, 1,
548         &fds[0], TOO_MANY_FDS / 2);
549
550   if (done < 0)
551     g_error ("could not send first byte and first batch of fds: %s",
552         g_strerror (errno));
553
554   /* ... and the second half (rounding up if odd) with the rest of
555    * the message */
556   done = _dbus_write_socket_with_unix_fds (left_client_socket, &buffer, 1,
557         payload_len - 1, &fds[TOO_MANY_FDS / 2],
558         TOO_MANY_FDS - (TOO_MANY_FDS / 2));
559
560   if (done < 0)
561     {
562       g_error ("could not send rest of message and rest of fds: %s",
563           g_strerror (errno));
564     }
565   else if (done < payload_len - 1)
566     {
567       /* For simplicity, assume the socket buffer is big enough for the
568        * whole message, which should be < 2 KiB. If this fails on some
569        * OS, redo this test code to use a proper loop like the real
570        * libdbus does. */
571       g_error ("short write in sendmsg(), fix this test: %d/%d",
572           done, payload_len - 1);
573     }
574
575   dbus_free (payload);
576
577   for (i = 0; i < TOO_MANY_FDS; i++)
578     close (fds[i]);
579
580   dbus_message_unref (outgoing);
581
582   /* The sender is unceremoniously disconnected. */
583   while (dbus_connection_get_is_connected (f->left_client_conn) ||
584       dbus_connection_get_is_connected (f->left_server_conn))
585     {
586       g_print (".");
587       test_main_context_iterate (f->ctx, TRUE);
588     }
589
590   /* The message didn't get through without its fds. */
591   g_assert_cmpuint (g_queue_get_length (&f->messages), ==, 0);
592
593   /* The intended victim is unaffected by the left connection's
594    * misbehaviour. */
595   g_assert (dbus_connection_get_is_connected (f->right_client_conn));
596   g_assert (dbus_connection_get_is_connected (f->right_server_conn));
597 #else
598   g_test_skip ("fd-passing not supported on this platform");
599 #endif
600 }
601
602 static void
603 test_flood (Fixture *f,
604     gconstpointer data)
605 {
606 #ifdef HAVE_UNIX_FD_PASSING
607   int i, j;
608   DBusMessage *outgoing[SOME_MESSAGES];
609   dbus_uint32_t serial;
610
611   test_connect (f, data);
612
613   for (j = 0; j < SOME_MESSAGES; j++)
614     {
615       outgoing[j] = dbus_message_new_signal ("/com/example/Hello",
616           "com.example.Hello", "Greeting");
617       g_assert (outgoing[j] != NULL);
618
619       for (i = 0; i < GPOINTER_TO_UINT (data); i++)
620         {
621           if (!dbus_message_append_args (outgoing[j],
622                 DBUS_TYPE_UNIX_FD, &f->fd_before,
623                 DBUS_TYPE_INVALID))
624             oom ("appending fd");
625         }
626     }
627
628   /* This is in its own loop so we do it as fast as possible */
629   for (j = 0; j < SOME_MESSAGES; j++)
630     {
631       if (!dbus_connection_send (f->left_client_conn, outgoing[j], &serial))
632         oom ("sending message");
633     }
634
635   for (j = 0; j < SOME_MESSAGES; j++)
636     {
637       dbus_message_unref (outgoing[j]);
638     }
639
640   while (g_queue_get_length (&f->messages) < SOME_MESSAGES)
641     {
642       g_print (".");
643       test_main_context_iterate (f->ctx, TRUE);
644     }
645
646   g_assert_cmpuint (g_queue_get_length (&f->messages), ==, SOME_MESSAGES);
647
648   for (j = 0; j < SOME_MESSAGES; j++)
649     {
650       DBusMessage *incoming;
651
652       incoming = g_queue_pop_head (&f->messages);
653
654       g_assert (dbus_message_contains_unix_fds (incoming));
655       g_assert_cmpstr (dbus_message_get_destination (incoming), ==, NULL);
656       g_assert_cmpstr (dbus_message_get_error_name (incoming), ==, NULL);
657       g_assert_cmpstr (dbus_message_get_interface (incoming), ==,
658           "com.example.Hello");
659       g_assert_cmpstr (dbus_message_get_member (incoming), ==, "Greeting");
660       g_assert_cmpstr (dbus_message_get_sender (incoming), ==, NULL);
661       g_assert_cmpstr (dbus_message_get_path (incoming), ==, "/com/example/Hello");
662
663       dbus_message_unref (incoming);
664     }
665
666   g_assert (dbus_connection_get_is_connected (f->right_client_conn));
667   g_assert (dbus_connection_get_is_connected (f->right_server_conn));
668   g_assert (dbus_connection_get_is_connected (f->left_client_conn));
669   g_assert (dbus_connection_get_is_connected (f->left_server_conn));
670 #else
671   g_test_skip ("fd-passing not supported on this platform");
672 #endif
673 }
674
675 static void
676 test_odd_limit (Fixture *f,
677     gconstpointer data)
678 {
679 #ifdef HAVE_UNIX_FD_PASSING
680   DBusMessage *outgoing;
681   int i;
682
683   test_connect (f, data);
684   dbus_connection_set_max_message_unix_fds (f->left_server_conn, 7);
685   dbus_connection_set_max_message_unix_fds (f->right_server_conn, 7);
686
687   outgoing = dbus_message_new_signal ("/com/example/Hello",
688       "com.example.Hello", "Greeting");
689   g_assert (outgoing != NULL);
690
691   for (i = 0; i < 7 + GPOINTER_TO_INT (data); i++)
692     {
693       if (!dbus_message_append_args (outgoing,
694             DBUS_TYPE_UNIX_FD, &f->fd_before,
695             DBUS_TYPE_INVALID))
696         oom ("appending fd");
697     }
698
699   if (!dbus_connection_send (f->left_client_conn, outgoing, NULL))
700     oom ("sending message");
701
702   dbus_message_unref (outgoing);
703
704   if (GPOINTER_TO_INT (data) > 0)
705     {
706       /* The sender is unceremoniously disconnected. */
707       while (dbus_connection_get_is_connected (f->left_client_conn) ||
708           dbus_connection_get_is_connected (f->left_server_conn))
709         {
710           g_print (".");
711           test_main_context_iterate (f->ctx, TRUE);
712         }
713
714       /* The message didn't get through without its fds. */
715       g_assert_cmpuint (g_queue_get_length (&f->messages), ==, 0);
716
717       /* The intended victim is unaffected by the left connection's
718        * misbehaviour. */
719       g_assert (dbus_connection_get_is_connected (f->right_client_conn));
720       g_assert (dbus_connection_get_is_connected (f->right_server_conn));
721     }
722   else
723     {
724       DBusMessage *incoming;
725
726       /* We're at or under the limit. The message gets through intact. */
727       while (g_queue_get_length (&f->messages) < 1)
728         {
729           g_print (".");
730           test_main_context_iterate (f->ctx, TRUE);
731         }
732
733       g_assert_cmpuint (g_queue_get_length (&f->messages), ==, 1);
734
735       incoming = g_queue_pop_head (&f->messages);
736
737       g_assert (dbus_message_contains_unix_fds (incoming));
738       g_assert_cmpstr (dbus_message_get_destination (incoming), ==, NULL);
739       g_assert_cmpstr (dbus_message_get_error_name (incoming), ==, NULL);
740       g_assert_cmpstr (dbus_message_get_interface (incoming), ==,
741           "com.example.Hello");
742       g_assert_cmpstr (dbus_message_get_member (incoming), ==, "Greeting");
743       g_assert_cmpstr (dbus_message_get_sender (incoming), ==, NULL);
744       g_assert_cmpstr (dbus_message_get_path (incoming), ==,
745           "/com/example/Hello");
746
747       dbus_message_unref (incoming);
748
749       g_assert (dbus_connection_get_is_connected (f->right_client_conn));
750       g_assert (dbus_connection_get_is_connected (f->right_server_conn));
751       g_assert (dbus_connection_get_is_connected (f->left_client_conn));
752       g_assert (dbus_connection_get_is_connected (f->left_server_conn));
753     }
754 #else
755   g_test_skip ("fd-passing not supported on this platform");
756 #endif
757 }
758
759 static void
760 teardown (Fixture *f,
761     gconstpointer data G_GNUC_UNUSED)
762 {
763 #ifdef HAVE_UNIX_FD_PASSING
764   if (f->left_client_conn != NULL)
765     {
766       dbus_connection_close (f->left_client_conn);
767       dbus_connection_unref (f->left_client_conn);
768       f->left_client_conn = NULL;
769     }
770
771   if (f->right_client_conn != NULL)
772     {
773       dbus_connection_close (f->right_client_conn);
774       dbus_connection_unref (f->right_client_conn);
775       f->right_client_conn = NULL;
776     }
777
778   if (f->left_server_conn != NULL)
779     {
780       dbus_connection_close (f->left_server_conn);
781       dbus_connection_unref (f->left_server_conn);
782       f->left_server_conn = NULL;
783     }
784
785   if (f->right_server_conn != NULL)
786     {
787       dbus_connection_close (f->right_server_conn);
788       dbus_connection_unref (f->right_server_conn);
789       f->right_server_conn = NULL;
790     }
791
792   if (f->server != NULL)
793     {
794       dbus_server_disconnect (f->server);
795       dbus_server_unref (f->server);
796       f->server = NULL;
797     }
798
799   test_main_context_unref (f->ctx);
800
801   if (f->fd_before >= 0 && close (f->fd_before) < 0)
802     g_error ("%s", g_strerror (errno));
803 #endif
804 }
805
806 int
807 main (int argc,
808     char **argv)
809 {
810   g_test_init (&argc, &argv, NULL);
811   g_test_bug_base ("https://bugs.freedesktop.org/show_bug.cgi?id=");
812
813   g_test_add ("/relay", Fixture, NULL, setup,
814       test_relay, teardown);
815   g_test_add ("/limit", Fixture, NULL, setup,
816       test_limit, teardown);
817
818   g_test_add ("/too-many/plus1", Fixture, GUINT_TO_POINTER (1), setup,
819       test_too_many, teardown);
820   g_test_add ("/too-many/plus2", Fixture, GUINT_TO_POINTER (2), setup,
821       test_too_many, teardown);
822   g_test_add ("/too-many/plus17", Fixture, GUINT_TO_POINTER (17), setup,
823       test_too_many, teardown);
824
825   g_test_add ("/too-many/split", Fixture, NULL, setup,
826       test_too_many_split, teardown);
827
828   g_test_add ("/flood/1", Fixture, GUINT_TO_POINTER (1),
829       setup, test_flood, teardown);
830 #if MAX_MESSAGE_UNIX_FDS >= 2
831   g_test_add ("/flood/half-limit", Fixture,
832       GUINT_TO_POINTER (MAX_MESSAGE_UNIX_FDS / 2),
833       setup, test_flood, teardown);
834 #endif
835 #if MAX_MESSAGE_UNIX_FDS >= 4
836   g_test_add ("/flood/over-half-limit", Fixture,
837       GUINT_TO_POINTER (3 * MAX_MESSAGE_UNIX_FDS / 4),
838       setup, test_flood, teardown);
839 #endif
840   g_test_add ("/flood/limit", Fixture,
841       GUINT_TO_POINTER (MAX_MESSAGE_UNIX_FDS), setup, test_flood, teardown);
842
843   g_test_add ("/odd-limit/minus1", Fixture, GINT_TO_POINTER (-1), setup,
844       test_odd_limit, teardown);
845   g_test_add ("/odd-limit/at", Fixture, GINT_TO_POINTER (0), setup,
846       test_odd_limit, teardown);
847   g_test_add ("/odd-limit/plus1", Fixture, GINT_TO_POINTER (+1), setup,
848       test_odd_limit, teardown);
849   g_test_add ("/odd-limit/plus2", Fixture, GINT_TO_POINTER (+2), setup,
850       test_odd_limit, teardown);
851
852   return g_test_run ();
853 }