2003-03-16 Havoc Pennington <hp@pobox.com>
[platform/upstream/dbus.git] / dbus / dbus-transport.c
1 /* -*- mode: C; c-file-style: "gnu" -*- */
2 /* dbus-transport.c DBusTransport object (internal to D-BUS implementation)
3  *
4  * Copyright (C) 2002, 2003  Red Hat Inc.
5  *
6  * Licensed under the Academic Free License version 1.2
7  * 
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  * 
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21  *
22  */
23
24 #include "dbus-transport-protected.h"
25 #include "dbus-transport-unix.h"
26 #include "dbus-connection-internal.h"
27 #include "dbus-watch.h"
28 #include "dbus-auth.h"
29 #include "dbus-address.h"
30 #ifdef DBUS_BUILD_TESTS
31 #include "dbus-transport-debug.h"
32 #include "dbus-server-debug-pipe.h"
33 #endif
34
35 /**
36  * @defgroup DBusTransport DBusTransport object
37  * @ingroup  DBusInternals
38  * @brief "Backend" for a DBusConnection.
39  *
40  * Types and functions related to DBusTransport.  A transport is an
41  * abstraction that can send and receive data via various kinds of
42  * network connections or other IPC mechanisms.
43  * 
44  * @{
45  */
46
47 /**
48  * @typedef DBusTransport
49  *
50  * Opaque object representing a way message stream.
51  * DBusTransport abstracts various kinds of actual
52  * transport mechanism, such as different network protocols,
53  * or encryption schemes.
54  */
55
56 static void
57 live_messages_size_notify (DBusCounter *counter,
58                            void        *user_data)
59 {
60   DBusTransport *transport = user_data;
61
62   _dbus_transport_ref (transport);
63
64 #if 0
65   _dbus_verbose ("Counter value is now %d\n",
66                  (int) _dbus_counter_get_value (counter));
67 #endif
68   
69   /* disable or re-enable the read watch for the transport if
70    * required.
71    */
72   if (* transport->vtable->live_messages_changed)
73     (* transport->vtable->live_messages_changed) (transport);
74
75   _dbus_transport_unref (transport);
76 }
77
78 /**
79  * Initializes the base class members of DBusTransport.
80  * Chained up to by subclasses in their constructor.
81  *
82  * @param transport the transport being created.
83  * @param vtable the subclass vtable.
84  * @param server #TRUE if this transport is on the server side of a connection
85  * @returns #TRUE on success.
86  */
87 dbus_bool_t
88 _dbus_transport_init_base (DBusTransport             *transport,
89                            const DBusTransportVTable *vtable,
90                            dbus_bool_t                server)
91 {
92   DBusMessageLoader *loader;
93   DBusAuth *auth;
94   DBusCounter *counter;
95   
96   loader = _dbus_message_loader_new ();
97   if (loader == NULL)
98     return FALSE;
99   
100   if (server)
101     auth = _dbus_auth_server_new ();
102   else
103     auth = _dbus_auth_client_new ();
104   if (auth == NULL)
105     {
106       _dbus_message_loader_unref (loader);
107       return FALSE;
108     }
109
110   counter = _dbus_counter_new ();
111   if (counter == NULL)
112     {
113       _dbus_auth_unref (auth);
114       _dbus_message_loader_unref (loader);
115       return FALSE;
116     }
117   
118   transport->refcount = 1;
119   transport->vtable = vtable;
120   transport->loader = loader;
121   transport->auth = auth;
122   transport->live_messages_size = counter;
123   transport->authenticated = FALSE;
124   transport->messages_need_sending = FALSE;
125   transport->disconnected = FALSE;
126   transport->send_credentials_pending = !server;
127   transport->receive_credentials_pending = server;
128   transport->is_server = server;
129
130   /* Try to default to something that won't totally hose the system,
131    * but doesn't impose too much of a limitation.
132    */
133   transport->max_live_messages_size = _DBUS_ONE_MEGABYTE * 63;
134   
135   transport->credentials.pid = -1;
136   transport->credentials.uid = -1;
137   transport->credentials.gid = -1;
138
139   _dbus_counter_set_notify (transport->live_messages_size,
140                             transport->max_live_messages_size,
141                             live_messages_size_notify,
142                             transport);
143   
144   return TRUE;
145 }
146
147 /**
148  * Finalizes base class members of DBusTransport.
149  * Chained up to from subclass finalizers.
150  *
151  * @param transport the transport.
152  */
153 void
154 _dbus_transport_finalize_base (DBusTransport *transport)
155 {
156   if (!transport->disconnected)
157     _dbus_transport_disconnect (transport);
158   
159   _dbus_message_loader_unref (transport->loader);
160   _dbus_auth_unref (transport->auth);
161   _dbus_counter_set_notify (transport->live_messages_size,
162                             0, NULL, NULL);
163   _dbus_counter_unref (transport->live_messages_size);
164 }
165
166 /**
167  * Opens a new transport for the given address.  (This opens a
168  * client-side-of-the-connection transport.)
169  *
170  * @todo error messages on bad address could really be better.
171  * DBusResultCode is a bit limiting here.
172  * 
173  * @param address the address.
174  * @param result location to store reason for failure.
175  * @returns new transport of #NULL on failure.
176  */
177 DBusTransport*
178 _dbus_transport_open (const char     *address,
179                       DBusResultCode *result)
180 {
181   DBusTransport *transport;
182   DBusAddressEntry **entries;
183   int len, i;
184   
185   if (!dbus_parse_address (address, &entries, &len, result))
186     return NULL;
187
188   transport = NULL;
189   
190   for (i = 0; i < len; i++)
191     {
192       const char *method = dbus_address_entry_get_method (entries[i]);
193
194       if (strcmp (method, "unix") == 0)
195         {
196           const char *path = dbus_address_entry_get_value (entries[i], "path");
197
198           if (path == NULL)
199             goto bad_address;
200
201           transport = _dbus_transport_new_for_domain_socket (path, FALSE, result);
202         }
203       else if (strcmp (method, "tcp") == 0)
204         {
205           const char *host = dbus_address_entry_get_value (entries[i], "host");
206           const char *port = dbus_address_entry_get_value (entries[i], "port");
207           DBusString  str;
208           long lport;
209           dbus_bool_t sresult;
210           
211           if (port == NULL)
212             goto bad_address;
213
214           _dbus_string_init_const (&str, port);
215           sresult = _dbus_string_parse_int (&str, 0, &lport, NULL);
216           _dbus_string_free (&str);
217           
218           if (sresult == FALSE || lport <= 0 || lport > 65535)
219             goto bad_address;
220           
221           transport = _dbus_transport_new_for_tcp_socket (host, lport, FALSE, result);
222         }
223 #ifdef DBUS_BUILD_TESTS
224       else if (strcmp (method, "debug") == 0)
225         {
226           const char *name = dbus_address_entry_get_value (entries[i], "name");
227
228           if (name == NULL)
229             goto bad_address;
230
231           transport = _dbus_transport_debug_client_new (name, result);
232         }
233       else if (strcmp (method, "debug-pipe") == 0)
234         {
235           const char *name = dbus_address_entry_get_value (entries[i], "name");
236
237           if (name == NULL)
238             goto bad_address;
239
240           transport = _dbus_transport_debug_pipe_new (name, result);
241         }
242 #endif
243       else
244         goto bad_address;
245
246       if (transport)
247         break;    
248     }
249   
250   dbus_address_entries_free (entries);
251   return transport;
252
253  bad_address:
254   dbus_address_entries_free (entries);
255   dbus_set_result (result, DBUS_RESULT_BAD_ADDRESS);
256
257   return NULL;
258 }
259
260 /**
261  * Increments the reference count for the transport.
262  *
263  * @param transport the transport.
264  */
265 void
266 _dbus_transport_ref (DBusTransport *transport)
267 {
268   transport->refcount += 1;
269 }
270
271 /**
272  * Decrements the reference count for the transport.
273  * Disconnects and finalizes the transport if
274  * the reference count reaches zero.
275  *
276  * @param transport the transport.
277  */
278 void
279 _dbus_transport_unref (DBusTransport *transport)
280 {
281   _dbus_assert (transport != NULL);
282   _dbus_assert (transport->refcount > 0);
283
284   transport->refcount -= 1;
285   if (transport->refcount == 0)
286     {
287       _dbus_assert (transport->vtable->finalize != NULL);
288       
289       (* transport->vtable->finalize) (transport);
290     }
291 }
292
293 /**
294  * Closes our end of the connection to a remote application. Further
295  * attempts to use this transport will fail. Only the first call to
296  * _dbus_transport_disconnect() will have an effect.
297  *
298  * @param transport the transport.
299  * 
300  */
301 void
302 _dbus_transport_disconnect (DBusTransport *transport)
303 {
304   _dbus_assert (transport->vtable->disconnect != NULL);
305
306   if (transport->disconnected)
307     return;
308
309   _dbus_transport_ref (transport);
310   (* transport->vtable->disconnect) (transport);
311   
312   transport->disconnected = TRUE;
313
314   _dbus_connection_notify_disconnected (transport->connection);
315   
316   _dbus_transport_unref (transport);
317 }
318
319 /**
320  * Returns #TRUE if the transport has not been disconnected.
321  * Disconnection can result from _dbus_transport_disconnect()
322  * or because the server drops its end of the connection.
323  *
324  * @param transport the transport.
325  * @returns whether we're connected
326  */
327 dbus_bool_t
328 _dbus_transport_get_is_connected (DBusTransport *transport)
329 {
330   return !transport->disconnected;
331 }
332
333 /**
334  * Returns #TRUE if we have been authenticated.  Will return #TRUE
335  * even if the transport is disconnected.
336  *
337  * @param transport the transport
338  * @returns whether we're authenticated
339  */
340 dbus_bool_t
341 _dbus_transport_get_is_authenticated (DBusTransport *transport)
342 {  
343   if (transport->authenticated)
344     return TRUE;
345   else
346     {
347       if (transport->disconnected)
348         return FALSE;
349       
350       transport->authenticated =
351         (!(transport->send_credentials_pending ||
352            transport->receive_credentials_pending)) &&
353         _dbus_auth_do_work (transport->auth) == DBUS_AUTH_STATE_AUTHENTICATED;
354
355       /* If we've authenticated as some identity, check that the auth
356        * identity is the same as our own identity.  In the future, we
357        * may have API allowing applications to specify how this is
358        * done, for example they may allow connection as any identity,
359        * but then impose restrictions on certain identities.
360        * Or they may give certain identities extra privileges.
361        */
362       
363       if (transport->authenticated && transport->is_server)
364         {
365           DBusCredentials auth_identity;
366           DBusCredentials our_identity;
367
368           _dbus_credentials_from_current_process (&our_identity);
369           _dbus_auth_get_identity (transport->auth, &auth_identity);
370           
371           if (!_dbus_credentials_match (&our_identity,
372                                         &auth_identity))
373             {
374               _dbus_verbose ("Client authorized as UID %d but our UID is %d, disconnecting\n",
375                              auth_identity.uid, our_identity.uid);
376               _dbus_transport_disconnect (transport);
377               return FALSE;
378             }
379           else
380             {
381               _dbus_verbose ("Client authorized as UID %d matching our UID %d\n",
382                              auth_identity.uid, our_identity.uid);
383             }
384         }
385       
386       return transport->authenticated;
387     }
388 }
389
390 /**
391  * Handles a watch by reading data, writing data, or disconnecting
392  * the transport, as appropriate for the given condition.
393  *
394  * @param transport the transport.
395  * @param watch the watch.
396  * @param condition the current state of the watched file descriptor.
397  */
398 void
399 _dbus_transport_handle_watch (DBusTransport           *transport,
400                               DBusWatch               *watch,
401                               unsigned int             condition)
402 {
403   _dbus_assert (transport->vtable->handle_watch != NULL);
404
405   if (transport->disconnected)
406     return;
407
408   if (dbus_watch_get_fd (watch) < 0)
409     {
410       _dbus_warn ("Tried to handle an invalidated watch; this watch should have been removed\n");
411       return;
412     }
413   
414   _dbus_watch_sanitize_condition (watch, &condition);
415
416   _dbus_transport_ref (transport);
417   _dbus_watch_ref (watch);
418   (* transport->vtable->handle_watch) (transport, watch, condition);
419   _dbus_watch_unref (watch);
420   _dbus_transport_unref (transport);
421 }
422
423 /**
424  * Sets the connection using this transport. Allows the transport
425  * to add watches to the connection, queue incoming messages,
426  * and pull outgoing messages.
427  *
428  * @param transport the transport.
429  * @param connection the connection.
430  * @returns #FALSE if not enough memory
431  */
432 dbus_bool_t
433 _dbus_transport_set_connection (DBusTransport  *transport,
434                                 DBusConnection *connection)
435 {
436   _dbus_assert (transport->vtable->connection_set != NULL);
437   _dbus_assert (transport->connection == NULL);
438   
439   transport->connection = connection;
440
441   _dbus_transport_ref (transport);
442   if (!(* transport->vtable->connection_set) (transport))
443     transport->connection = NULL;
444   _dbus_transport_unref (transport);
445
446   return transport->connection != NULL;
447 }
448
449 /**
450  * Notifies the transport when the outgoing message queue goes from
451  * empty to non-empty or vice versa. Typically causes the transport to
452  * add or remove its DBUS_WATCH_WRITABLE watch.
453  *
454  * @param transport the transport.
455  * @param queue_length the length of the outgoing message queue.
456  *
457  */
458 void
459 _dbus_transport_messages_pending (DBusTransport  *transport,
460                                   int             queue_length)
461 {
462   _dbus_assert (transport->vtable->messages_pending != NULL);
463
464   if (transport->disconnected)
465     return;
466
467   transport->messages_need_sending = queue_length > 0;
468
469   _dbus_transport_ref (transport);
470   (* transport->vtable->messages_pending) (transport,
471                                            queue_length);
472   _dbus_transport_unref (transport);
473 }
474
475 /**
476  * Performs a single poll()/select() on the transport's file
477  * descriptors and then reads/writes data as appropriate,
478  * queueing incoming messages and sending outgoing messages.
479  * This is the backend for _dbus_connection_do_iteration().
480  * See _dbus_connection_do_iteration() for full details.
481  *
482  * @param transport the transport.
483  * @param flags indicates whether to read or write, and whether to block.
484  * @param timeout_milliseconds if blocking, timeout or -1 for no timeout.
485  */
486 void
487 _dbus_transport_do_iteration (DBusTransport  *transport,
488                               unsigned int    flags,
489                               int             timeout_milliseconds)
490 {
491   _dbus_assert (transport->vtable->do_iteration != NULL);
492
493   _dbus_verbose ("Transport iteration flags 0x%x timeout %d connected = %d\n",
494                  flags, timeout_milliseconds, !transport->disconnected);
495   
496   if ((flags & (DBUS_ITERATION_DO_WRITING |
497                 DBUS_ITERATION_DO_READING)) == 0)
498     return; /* Nothing to do */
499
500   if (transport->disconnected)
501     return;
502
503   _dbus_transport_ref (transport);
504   (* transport->vtable->do_iteration) (transport, flags,
505                                        timeout_milliseconds);
506   _dbus_transport_unref (transport);
507 }
508
509 /**
510  * Reports our current dispatch status (whether there's buffered
511  * data to be queued as messages, or not, or we need memory).
512  *
513  * @param transport the transport
514  * @returns current status
515  */
516 DBusDispatchStatus
517 _dbus_transport_get_dispatch_status (DBusTransport *transport)
518 {
519   if (_dbus_counter_get_value (transport->live_messages_size) >= transport->max_live_messages_size)
520     return DBUS_DISPATCH_COMPLETE; /* complete for now */
521
522   if (!_dbus_message_loader_queue_messages (transport->loader))
523     return DBUS_DISPATCH_NEED_MEMORY;
524
525   if (_dbus_message_loader_peek_message (transport->loader) != NULL)
526     return DBUS_DISPATCH_DATA_REMAINS;
527   else
528     return DBUS_DISPATCH_COMPLETE;
529 }
530
531 /**
532  * Processes data we've read while handling a watch, potentially
533  * converting some of it to messages and queueing those messages on
534  * the connection.
535  *
536  * @param transport the transport
537  * @returns #TRUE if we had enough memory to queue all messages
538  */
539 dbus_bool_t
540 _dbus_transport_queue_messages (DBusTransport *transport)
541 {
542   DBusDispatchStatus status;
543   
544   /* Queue any messages */
545   while ((status = _dbus_transport_get_dispatch_status (transport)) == DBUS_DISPATCH_DATA_REMAINS)
546     {
547       DBusMessage *message;
548       DBusList *link;
549
550       link = _dbus_message_loader_pop_message_link (transport->loader);
551       _dbus_assert (link != NULL);
552       
553       message = link->data;
554       
555       _dbus_verbose ("queueing received message %p\n", message);
556
557       _dbus_message_add_size_counter (message, transport->live_messages_size);
558
559       /* pass ownership of link and message ref to connection */
560       _dbus_connection_queue_received_message_link (transport->connection,
561                                                     link);
562     }
563
564   if (_dbus_message_loader_get_is_corrupted (transport->loader))
565     {
566       _dbus_verbose ("Corrupted message stream, disconnecting\n");
567       _dbus_transport_disconnect (transport);
568     }
569
570   return status != DBUS_DISPATCH_NEED_MEMORY;
571 }
572
573 /**
574  * See dbus_connection_set_max_message_size().
575  *
576  * @param transport the transport
577  * @param size the max size of a single message
578  */
579 void
580 _dbus_transport_set_max_message_size (DBusTransport  *transport,
581                                       long            size)
582 {
583   _dbus_message_loader_set_max_message_size (transport->loader, size);
584 }
585
586 /**
587  * See dbus_connection_get_max_message_size().
588  *
589  * @param transport the transport
590  * @returns max message size
591  */
592 long
593 _dbus_transport_get_max_message_size (DBusTransport  *transport)
594 {
595   return _dbus_message_loader_get_max_message_size (transport->loader);
596 }
597
598 /**
599  * See dbus_connection_set_max_live_messages_size().
600  *
601  * @param transport the transport
602  * @param size the max size of all incoming messages
603  */
604 void
605 _dbus_transport_set_max_live_messages_size (DBusTransport  *transport,
606                                             long            size)
607 {
608   transport->max_live_messages_size = size;
609   _dbus_counter_set_notify (transport->live_messages_size,
610                             transport->max_live_messages_size,
611                             live_messages_size_notify,
612                             transport);
613 }
614
615
616 /**
617  * See dbus_connection_get_max_live_messages_size().
618  *
619  * @param transport the transport
620  * @returns max bytes for all live messages
621  */
622 long
623 _dbus_transport_get_max_live_messages_size (DBusTransport  *transport)
624 {
625   return transport->max_live_messages_size;
626 }
627
628 /** @} */