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