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