2003-03-14 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  */
431 void
432 _dbus_transport_set_connection (DBusTransport  *transport,
433                                 DBusConnection *connection)
434 {
435   _dbus_assert (transport->vtable->connection_set != NULL);
436   _dbus_assert (transport->connection == NULL);
437   
438   transport->connection = connection;
439
440   _dbus_transport_ref (transport);
441   (* transport->vtable->connection_set) (transport);
442   _dbus_transport_unref (transport);
443 }
444
445 /**
446  * Notifies the transport when the outgoing message queue goes from
447  * empty to non-empty or vice versa. Typically causes the transport to
448  * add or remove its DBUS_WATCH_WRITABLE watch.
449  *
450  * @param transport the transport.
451  * @param queue_length the length of the outgoing message queue.
452  *
453  */
454 void
455 _dbus_transport_messages_pending (DBusTransport  *transport,
456                                   int             queue_length)
457 {
458   _dbus_assert (transport->vtable->messages_pending != NULL);
459
460   if (transport->disconnected)
461     return;
462
463   transport->messages_need_sending = queue_length > 0;
464
465   _dbus_transport_ref (transport);
466   (* transport->vtable->messages_pending) (transport,
467                                            queue_length);
468   _dbus_transport_unref (transport);
469 }
470
471 /**
472  * Performs a single poll()/select() on the transport's file
473  * descriptors and then reads/writes data as appropriate,
474  * queueing incoming messages and sending outgoing messages.
475  * This is the backend for _dbus_connection_do_iteration().
476  * See _dbus_connection_do_iteration() for full details.
477  *
478  * @param transport the transport.
479  * @param flags indicates whether to read or write, and whether to block.
480  * @param timeout_milliseconds if blocking, timeout or -1 for no timeout.
481  */
482 void
483 _dbus_transport_do_iteration (DBusTransport  *transport,
484                               unsigned int    flags,
485                               int             timeout_milliseconds)
486 {
487   _dbus_assert (transport->vtable->do_iteration != NULL);
488
489   _dbus_verbose ("Transport iteration flags 0x%x timeout %d connected = %d\n",
490                  flags, timeout_milliseconds, !transport->disconnected);
491   
492   if ((flags & (DBUS_ITERATION_DO_WRITING |
493                 DBUS_ITERATION_DO_READING)) == 0)
494     return; /* Nothing to do */
495
496   if (transport->disconnected)
497     return;
498
499   _dbus_transport_ref (transport);
500   (* transport->vtable->do_iteration) (transport, flags,
501                                        timeout_milliseconds);
502   _dbus_transport_unref (transport);
503 }
504
505 /**
506  * See dbus_connection_set_max_message_size().
507  *
508  * @param transport the transport
509  * @param size the max size of a single message
510  */
511 void
512 _dbus_transport_set_max_message_size (DBusTransport  *transport,
513                                       long            size)
514 {
515   _dbus_message_loader_set_max_message_size (transport->loader, size);
516 }
517
518 /**
519  * See dbus_connection_get_max_message_size().
520  *
521  * @param transport the transport
522  * @returns max message size
523  */
524 long
525 _dbus_transport_get_max_message_size (DBusTransport  *transport)
526 {
527   return _dbus_message_loader_get_max_message_size (transport->loader);
528 }
529
530 /**
531  * See dbus_connection_set_max_live_messages_size().
532  *
533  * @param transport the transport
534  * @param size the max size of all incoming messages
535  */
536 void
537 _dbus_transport_set_max_live_messages_size (DBusTransport  *transport,
538                                             long            size)
539 {
540   transport->max_live_messages_size = size;
541   _dbus_counter_set_notify (transport->live_messages_size,
542                             transport->max_live_messages_size,
543                             live_messages_size_notify,
544                             transport);
545 }
546
547
548 /**
549  * See dbus_connection_get_max_live_messages_size().
550  *
551  * @param transport the transport
552  * @returns max bytes for all live messages
553  */
554 long
555 _dbus_transport_get_max_live_messages_size (DBusTransport  *transport)
556 {
557   return transport->max_live_messages_size;
558 }
559
560 /** @} */