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