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