2003-01-30 Anders Carlsson <andersca@codefactory.se>
[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 #endif
33
34 /**
35  * @defgroup DBusTransport DBusTransport object
36  * @ingroup  DBusInternals
37  * @brief "Backend" for a DBusConnection.
38  *
39  * Types and functions related to DBusTransport.  A transport is an
40  * abstraction that can send and receive data via various kinds of
41  * network connections or other IPC mechanisms.
42  * 
43  * @{
44  */
45
46 /**
47  * @typedef DBusTransport
48  *
49  * Opaque object representing a way message stream.
50  * DBusTransport abstracts various kinds of actual
51  * transport mechanism, such as different network protocols,
52  * or encryption schemes.
53  */
54
55 /**
56  * Refs a transport and associated connection for reentrancy.
57  *
58  * @todo this macro reflects a design mistake, which is that the
59  * transport has a pointer to its connection. Ownership should move in
60  * only one direction; the connection should push/pull from the
61  * transport, rather than vice versa. Then the connection would take
62  * care of referencing itself when needed.
63  */
64 #define DBUS_TRANSPORT_HOLD_REF(t) \
65   _dbus_transport_ref (t); if ((t)->connection) dbus_connection_ref ((t)->connection)
66
67 /**
68  * Inverse of DBUS_TRANSPORT_HOLD_REF().
69  */
70 #define DBUS_TRANSPORT_RELEASE_REF(t) \
71   if ((t)->connection) dbus_connection_unref ((t)->connection); _dbus_transport_unref (t)
72
73 static void
74 live_messages_size_notify (DBusCounter *counter,
75                            void        *user_data)
76 {
77   DBusTransport *transport = user_data;
78
79   DBUS_TRANSPORT_HOLD_REF (transport);
80
81 #if 0
82   _dbus_verbose ("Counter value is now %d\n",
83                  (int) _dbus_counter_get_value (counter));
84 #endif
85   
86   /* disable or re-enable the read watch for the transport if
87    * required.
88    */
89   if (* transport->vtable->live_messages_changed)
90     (* transport->vtable->live_messages_changed) (transport);
91
92   DBUS_TRANSPORT_RELEASE_REF (transport);
93 }
94
95 /**
96  * Initializes the base class members of DBusTransport.
97  * Chained up to by subclasses in their constructor.
98  *
99  * @param transport the transport being created.
100  * @param vtable the subclass vtable.
101  * @param server #TRUE if this transport is on the server side of a connection
102  * @returns #TRUE on success.
103  */
104 dbus_bool_t
105 _dbus_transport_init_base (DBusTransport             *transport,
106                            const DBusTransportVTable *vtable,
107                            dbus_bool_t                server)
108 {
109   DBusMessageLoader *loader;
110   DBusAuth *auth;
111   DBusCounter *counter;
112   
113   loader = _dbus_message_loader_new ();
114   if (loader == NULL)
115     return FALSE;
116   
117   if (server)
118     auth = _dbus_auth_server_new ();
119   else
120     auth = _dbus_auth_client_new ();
121   if (auth == NULL)
122     {
123       _dbus_message_loader_unref (loader);
124       return FALSE;
125     }
126
127   counter = _dbus_counter_new ();
128   if (counter == NULL)
129     {
130       _dbus_auth_unref (auth);
131       _dbus_message_loader_unref (loader);
132       return FALSE;
133     }
134   
135   transport->refcount = 1;
136   transport->vtable = vtable;
137   transport->loader = loader;
138   transport->auth = auth;
139   transport->live_messages_size = counter;
140   transport->authenticated = FALSE;
141   transport->messages_need_sending = FALSE;
142   transport->disconnected = FALSE;
143   transport->send_credentials_pending = !server;
144   transport->receive_credentials_pending = server;
145   transport->is_server = server;
146
147   /* Try to default to something that won't totally hose the system,
148    * but doesn't impose too much of a limitation.
149    */
150   transport->max_live_messages_size = _DBUS_ONE_MEGABYTE * 63;
151   
152   transport->credentials.pid = -1;
153   transport->credentials.uid = -1;
154   transport->credentials.gid = -1;
155
156   _dbus_counter_set_notify (transport->live_messages_size,
157                             transport->max_live_messages_size,
158                             live_messages_size_notify,
159                             transport);
160   
161   return TRUE;
162 }
163
164 /**
165  * Finalizes base class members of DBusTransport.
166  * Chained up to from subclass finalizers.
167  *
168  * @param transport the transport.
169  */
170 void
171 _dbus_transport_finalize_base (DBusTransport *transport)
172 {
173   if (!transport->disconnected)
174     _dbus_transport_disconnect (transport);
175   
176   _dbus_message_loader_unref (transport->loader);
177   _dbus_auth_unref (transport->auth);
178   _dbus_counter_set_notify (transport->live_messages_size,
179                             0, NULL, NULL);
180   _dbus_counter_unref (transport->live_messages_size);
181 }
182
183 /**
184  * Opens a new transport for the given address.  (This opens a
185  * client-side-of-the-connection transport.)
186  *
187  * @todo right now the address is just a Unix domain socket path.
188  * 
189  * @param address the address.
190  * @param result location to store reason for failure.
191  * @returns new transport of #NULL on failure.
192  */
193 DBusTransport*
194 _dbus_transport_open (const char     *address,
195                       DBusResultCode *result)
196 {
197   DBusTransport *transport;
198   DBusAddressEntry **entries;
199   int len, i;
200   
201   if (!dbus_parse_address (address, &entries, &len, result))
202     return NULL;
203
204   transport = NULL;
205   
206   for (i = 0; i < len; i++)
207     {
208       const char *method = dbus_address_entry_get_method (entries[i]);
209
210       if (strcmp (method, "unix") == 0)
211         {
212           const char *path = dbus_address_entry_get_value (entries[i], "path");
213
214           if (path == NULL)
215             goto bad_address;
216
217           transport = _dbus_transport_new_for_domain_socket (path, FALSE, result);
218         }
219       else if (strcmp (method, "debug") == 0)
220         {
221           const char *name = dbus_address_entry_get_value (entries[i], "name");
222
223           if (name == NULL)
224             goto bad_address;
225
226           transport = _dbus_transport_debug_client_new (name, result);
227         }
228       else
229         goto bad_address;
230
231       if (transport)
232         break;    
233     }
234   
235   dbus_address_entries_free (entries);
236   return transport;
237
238  bad_address:
239   dbus_address_entries_free (entries);
240   dbus_set_result (result, DBUS_RESULT_BAD_ADDRESS);
241
242   return NULL;
243 }
244
245 /**
246  * Increments the reference count for the transport.
247  *
248  * @param transport the transport.
249  */
250 void
251 _dbus_transport_ref (DBusTransport *transport)
252 {
253   transport->refcount += 1;
254 }
255
256 /**
257  * Decrements the reference count for the transport.
258  * Disconnects and finalizes the transport if
259  * the reference count reaches zero.
260  *
261  * @param transport the transport.
262  */
263 void
264 _dbus_transport_unref (DBusTransport *transport)
265 {
266   _dbus_assert (transport != NULL);
267   _dbus_assert (transport->refcount > 0);
268
269   transport->refcount -= 1;
270   if (transport->refcount == 0)
271     {
272       _dbus_assert (transport->vtable->finalize != NULL);
273       
274       (* transport->vtable->finalize) (transport);
275     }
276 }
277
278 /**
279  * Closes our end of the connection to a remote application. Further
280  * attempts to use this transport will fail. Only the first call to
281  * _dbus_transport_disconnect() will have an effect.
282  *
283  * @param transport the transport.
284  * 
285  */
286 void
287 _dbus_transport_disconnect (DBusTransport *transport)
288 {
289   _dbus_assert (transport->vtable->disconnect != NULL);
290
291   if (transport->disconnected)
292     return;
293
294   DBUS_TRANSPORT_HOLD_REF (transport);
295   (* transport->vtable->disconnect) (transport);
296   
297   transport->disconnected = TRUE;
298
299   _dbus_connection_notify_disconnected (transport->connection);
300   
301   DBUS_TRANSPORT_RELEASE_REF (transport);
302 }
303
304 /**
305  * Returns #TRUE if the transport has not been disconnected.
306  * Disconnection can result from _dbus_transport_disconnect()
307  * or because the server drops its end of the connection.
308  *
309  * @param transport the transport.
310  * @returns whether we're connected
311  */
312 dbus_bool_t
313 _dbus_transport_get_is_connected (DBusTransport *transport)
314 {
315   return !transport->disconnected;
316 }
317
318 /**
319  * Returns #TRUE if we have been authenticated.  Will return #TRUE
320  * even if the transport is disconnected.
321  *
322  * @param transport the transport
323  * @returns whether we're authenticated
324  */
325 dbus_bool_t
326 _dbus_transport_get_is_authenticated (DBusTransport *transport)
327 {  
328   if (transport->authenticated)
329     return TRUE;
330   else
331     {
332       if (transport->disconnected)
333         return FALSE;
334       
335       transport->authenticated =
336         (!(transport->send_credentials_pending ||
337            transport->receive_credentials_pending)) &&
338         _dbus_auth_do_work (transport->auth) == DBUS_AUTH_STATE_AUTHENTICATED;
339
340       /* If we've authenticated as some identity, check that the auth
341        * identity is the same as our own identity.  In the future, we
342        * may have API allowing applications to specify how this is
343        * done, for example they may allow connection as any identity,
344        * but then impose restrictions on certain identities.
345        * Or they may give certain identities extra privileges.
346        */
347       
348       if (transport->authenticated && transport->is_server)
349         {
350           DBusCredentials auth_identity;
351           DBusCredentials our_identity;
352
353           _dbus_credentials_from_current_process (&our_identity);
354           _dbus_auth_get_identity (transport->auth, &auth_identity);
355           
356           if (!_dbus_credentials_match (&our_identity,
357                                         &auth_identity))
358             {
359               _dbus_verbose ("Client authorized as UID %d but our UID is %d, disconnecting\n",
360                              auth_identity.uid, our_identity.uid);
361               _dbus_transport_disconnect (transport);
362               return FALSE;
363             }
364           else
365             {
366               _dbus_verbose ("Client authorized as UID %d matching our UID %d\n",
367                              auth_identity.uid, our_identity.uid);
368             }
369         }
370       
371       return transport->authenticated;
372     }
373 }
374
375 /**
376  * Handles a watch by reading data, writing data, or disconnecting
377  * the transport, as appropriate for the given condition.
378  *
379  * @param transport the transport.
380  * @param watch the watch.
381  * @param condition the current state of the watched file descriptor.
382  */
383 void
384 _dbus_transport_handle_watch (DBusTransport           *transport,
385                               DBusWatch               *watch,
386                               unsigned int             condition)
387 {
388   _dbus_assert (transport->vtable->handle_watch != NULL);
389
390   if (transport->disconnected)
391     return;
392
393   if (dbus_watch_get_fd (watch) < 0)
394     {
395       _dbus_warn ("Tried to handle an invalidated watch; this watch should have been removed\n");
396       return;
397     }
398   
399   _dbus_watch_sanitize_condition (watch, &condition);
400
401   DBUS_TRANSPORT_HOLD_REF (transport);
402   _dbus_watch_ref (watch);
403   (* transport->vtable->handle_watch) (transport, watch, condition);
404   _dbus_watch_unref (watch);
405   DBUS_TRANSPORT_RELEASE_REF (transport);
406 }
407
408 /**
409  * Sets the connection using this transport. Allows the transport
410  * to add watches to the connection, queue incoming messages,
411  * and pull outgoing messages.
412  *
413  * @param transport the transport.
414  * @param connection the connection.
415  */
416 void
417 _dbus_transport_set_connection (DBusTransport  *transport,
418                                 DBusConnection *connection)
419 {
420   _dbus_assert (transport->vtable->connection_set != NULL);
421   _dbus_assert (transport->connection == NULL);
422   
423   transport->connection = connection;
424
425   DBUS_TRANSPORT_HOLD_REF (transport);
426   (* transport->vtable->connection_set) (transport);
427   DBUS_TRANSPORT_RELEASE_REF (transport);
428 }
429
430 /**
431  * Notifies the transport when the outgoing message queue goes from
432  * empty to non-empty or vice versa. Typically causes the transport to
433  * add or remove its DBUS_WATCH_WRITABLE watch.
434  *
435  * @param transport the transport.
436  * @param queue_length the length of the outgoing message queue.
437  *
438  */
439 void
440 _dbus_transport_messages_pending (DBusTransport  *transport,
441                                   int             queue_length)
442 {
443   _dbus_assert (transport->vtable->messages_pending != NULL);
444
445   if (transport->disconnected)
446     return;
447
448   transport->messages_need_sending = queue_length > 0;
449
450   DBUS_TRANSPORT_HOLD_REF (transport);
451   (* transport->vtable->messages_pending) (transport,
452                                            queue_length);
453   DBUS_TRANSPORT_RELEASE_REF (transport);
454 }
455
456 /**
457  * Performs a single poll()/select() on the transport's file
458  * descriptors and then reads/writes data as appropriate,
459  * queueing incoming messages and sending outgoing messages.
460  * This is the backend for _dbus_connection_do_iteration().
461  * See _dbus_connection_do_iteration() for full details.
462  *
463  * @param transport the transport.
464  * @param flags indicates whether to read or write, and whether to block.
465  * @param timeout_milliseconds if blocking, timeout or -1 for no timeout.
466  */
467 void
468 _dbus_transport_do_iteration (DBusTransport  *transport,
469                               unsigned int    flags,
470                               int             timeout_milliseconds)
471 {
472   _dbus_assert (transport->vtable->do_iteration != NULL);
473
474   if ((flags & (DBUS_ITERATION_DO_WRITING |
475                 DBUS_ITERATION_DO_READING)) == 0)
476     return; /* Nothing to do */
477
478   if (transport->disconnected)
479     return;
480
481   DBUS_TRANSPORT_HOLD_REF (transport);
482   (* transport->vtable->do_iteration) (transport, flags,
483                                        timeout_milliseconds);
484   DBUS_TRANSPORT_RELEASE_REF (transport);
485 }
486
487 /**
488  * See dbus_connection_set_max_message_size().
489  *
490  * @param transport the transport
491  * @param size the max size of a single message
492  */
493 void
494 _dbus_transport_set_max_message_size (DBusTransport  *transport,
495                                       long            size)
496 {
497   _dbus_message_loader_set_max_message_size (transport->loader, size);
498 }
499
500 /**
501  * See dbus_connection_get_max_message_size().
502  *
503  * @param transport the transport
504  * @returns max message size
505  */
506 long
507 _dbus_transport_get_max_message_size (DBusTransport  *transport)
508 {
509   return _dbus_message_loader_get_max_message_size (transport->loader);
510 }
511
512 /**
513  * See dbus_connection_set_max_live_messages_size().
514  *
515  * @param transport the transport
516  * @param size the max size of all incoming messages
517  */
518 void
519 _dbus_transport_set_max_live_messages_size (DBusTransport  *transport,
520                                             long            size)
521 {
522   transport->max_live_messages_size = size;
523   _dbus_counter_set_notify (transport->live_messages_size,
524                             transport->max_live_messages_size,
525                             live_messages_size_notify,
526                             transport);
527 }
528
529
530 /**
531  * See dbus_connection_get_max_live_messages_size().
532  *
533  * @param transport the transport
534  * @returns max bytes for all live messages
535  */
536 long
537 _dbus_transport_get_max_live_messages_size (DBusTransport  *transport)
538 {
539   return transport->max_live_messages_size;
540 }
541
542 /** @} */