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