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