2003-02-19 Mikael Hallendal <micke@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       else if (strcmp (method, "tcp") == 0)
203         {
204           const char *host = dbus_address_entry_get_value (entries[i], "host");
205           const char *port = dbus_address_entry_get_value (entries[i], "port");
206           DBusString  str;
207           long lport;
208           dbus_bool_t sresult;
209           
210           if (port == NULL)
211             goto bad_address;
212
213           _dbus_string_init_const (&str, port);
214           sresult = _dbus_string_parse_int (&str, 0, &lport, NULL);
215           _dbus_string_free (&str);
216           
217           if (sresult == FALSE || lport <= 0 || lport > 65535)
218             goto bad_address;
219           
220           transport = _dbus_transport_new_for_tcp_socket (host, lport, FALSE, result);
221         }
222       else if (strcmp (method, "tcp") == 0)
223         {
224           const char *path = dbus_address_entry_get_value (entries[i], "path");
225
226           if (path == NULL)
227             goto bad_address;
228
229           transport = _dbus_transport_new_for_domain_socket (path, FALSE, result);
230         }
231 #ifdef DBUS_BUILD_TESTS
232       else if (strcmp (method, "debug") == 0)
233         {
234           const char *name = dbus_address_entry_get_value (entries[i], "name");
235
236           if (name == NULL)
237             goto bad_address;
238
239           transport = _dbus_transport_debug_client_new (name, result);
240         }
241 #endif      
242       else
243         goto bad_address;
244
245       if (transport)
246         break;    
247     }
248   
249   dbus_address_entries_free (entries);
250   return transport;
251
252  bad_address:
253   dbus_address_entries_free (entries);
254   dbus_set_result (result, DBUS_RESULT_BAD_ADDRESS);
255
256   return NULL;
257 }
258
259 /**
260  * Increments the reference count for the transport.
261  *
262  * @param transport the transport.
263  */
264 void
265 _dbus_transport_ref (DBusTransport *transport)
266 {
267   transport->refcount += 1;
268 }
269
270 /**
271  * Decrements the reference count for the transport.
272  * Disconnects and finalizes the transport if
273  * the reference count reaches zero.
274  *
275  * @param transport the transport.
276  */
277 void
278 _dbus_transport_unref (DBusTransport *transport)
279 {
280   _dbus_assert (transport != NULL);
281   _dbus_assert (transport->refcount > 0);
282
283   transport->refcount -= 1;
284   if (transport->refcount == 0)
285     {
286       _dbus_assert (transport->vtable->finalize != NULL);
287       
288       (* transport->vtable->finalize) (transport);
289     }
290 }
291
292 /**
293  * Closes our end of the connection to a remote application. Further
294  * attempts to use this transport will fail. Only the first call to
295  * _dbus_transport_disconnect() will have an effect.
296  *
297  * @param transport the transport.
298  * 
299  */
300 void
301 _dbus_transport_disconnect (DBusTransport *transport)
302 {
303   _dbus_assert (transport->vtable->disconnect != NULL);
304
305   if (transport->disconnected)
306     return;
307
308   _dbus_transport_ref (transport);
309   (* transport->vtable->disconnect) (transport);
310   
311   transport->disconnected = TRUE;
312
313   _dbus_connection_notify_disconnected (transport->connection);
314   
315   _dbus_transport_unref (transport);
316 }
317
318 /**
319  * Returns #TRUE if the transport has not been disconnected.
320  * Disconnection can result from _dbus_transport_disconnect()
321  * or because the server drops its end of the connection.
322  *
323  * @param transport the transport.
324  * @returns whether we're connected
325  */
326 dbus_bool_t
327 _dbus_transport_get_is_connected (DBusTransport *transport)
328 {
329   return !transport->disconnected;
330 }
331
332 /**
333  * Returns #TRUE if we have been authenticated.  Will return #TRUE
334  * even if the transport is disconnected.
335  *
336  * @param transport the transport
337  * @returns whether we're authenticated
338  */
339 dbus_bool_t
340 _dbus_transport_get_is_authenticated (DBusTransport *transport)
341 {  
342   if (transport->authenticated)
343     return TRUE;
344   else
345     {
346       if (transport->disconnected)
347         return FALSE;
348       
349       transport->authenticated =
350         (!(transport->send_credentials_pending ||
351            transport->receive_credentials_pending)) &&
352         _dbus_auth_do_work (transport->auth) == DBUS_AUTH_STATE_AUTHENTICATED;
353
354       /* If we've authenticated as some identity, check that the auth
355        * identity is the same as our own identity.  In the future, we
356        * may have API allowing applications to specify how this is
357        * done, for example they may allow connection as any identity,
358        * but then impose restrictions on certain identities.
359        * Or they may give certain identities extra privileges.
360        */
361       
362       if (transport->authenticated && transport->is_server)
363         {
364           DBusCredentials auth_identity;
365           DBusCredentials our_identity;
366
367           _dbus_credentials_from_current_process (&our_identity);
368           _dbus_auth_get_identity (transport->auth, &auth_identity);
369           
370           if (!_dbus_credentials_match (&our_identity,
371                                         &auth_identity))
372             {
373               _dbus_verbose ("Client authorized as UID %d but our UID is %d, disconnecting\n",
374                              auth_identity.uid, our_identity.uid);
375               _dbus_transport_disconnect (transport);
376               return FALSE;
377             }
378           else
379             {
380               _dbus_verbose ("Client authorized as UID %d matching our UID %d\n",
381                              auth_identity.uid, our_identity.uid);
382             }
383         }
384       
385       return transport->authenticated;
386     }
387 }
388
389 /**
390  * Handles a watch by reading data, writing data, or disconnecting
391  * the transport, as appropriate for the given condition.
392  *
393  * @param transport the transport.
394  * @param watch the watch.
395  * @param condition the current state of the watched file descriptor.
396  */
397 void
398 _dbus_transport_handle_watch (DBusTransport           *transport,
399                               DBusWatch               *watch,
400                               unsigned int             condition)
401 {
402   _dbus_assert (transport->vtable->handle_watch != NULL);
403
404   if (transport->disconnected)
405     return;
406
407   if (dbus_watch_get_fd (watch) < 0)
408     {
409       _dbus_warn ("Tried to handle an invalidated watch; this watch should have been removed\n");
410       return;
411     }
412   
413   _dbus_watch_sanitize_condition (watch, &condition);
414
415   _dbus_transport_ref (transport);
416   _dbus_watch_ref (watch);
417   (* transport->vtable->handle_watch) (transport, watch, condition);
418   _dbus_watch_unref (watch);
419   _dbus_transport_unref (transport);
420 }
421
422 /**
423  * Sets the connection using this transport. Allows the transport
424  * to add watches to the connection, queue incoming messages,
425  * and pull outgoing messages.
426  *
427  * @param transport the transport.
428  * @param connection the connection.
429  */
430 void
431 _dbus_transport_set_connection (DBusTransport  *transport,
432                                 DBusConnection *connection)
433 {
434   _dbus_assert (transport->vtable->connection_set != NULL);
435   _dbus_assert (transport->connection == NULL);
436   
437   transport->connection = connection;
438
439   _dbus_transport_ref (transport);
440   (* transport->vtable->connection_set) (transport);
441   _dbus_transport_unref (transport);
442 }
443
444 /**
445  * Notifies the transport when the outgoing message queue goes from
446  * empty to non-empty or vice versa. Typically causes the transport to
447  * add or remove its DBUS_WATCH_WRITABLE watch.
448  *
449  * @param transport the transport.
450  * @param queue_length the length of the outgoing message queue.
451  *
452  */
453 void
454 _dbus_transport_messages_pending (DBusTransport  *transport,
455                                   int             queue_length)
456 {
457   _dbus_assert (transport->vtable->messages_pending != NULL);
458
459   if (transport->disconnected)
460     return;
461
462   transport->messages_need_sending = queue_length > 0;
463
464   _dbus_transport_ref (transport);
465   (* transport->vtable->messages_pending) (transport,
466                                            queue_length);
467   _dbus_transport_unref (transport);
468 }
469
470 /**
471  * Performs a single poll()/select() on the transport's file
472  * descriptors and then reads/writes data as appropriate,
473  * queueing incoming messages and sending outgoing messages.
474  * This is the backend for _dbus_connection_do_iteration().
475  * See _dbus_connection_do_iteration() for full details.
476  *
477  * @param transport the transport.
478  * @param flags indicates whether to read or write, and whether to block.
479  * @param timeout_milliseconds if blocking, timeout or -1 for no timeout.
480  */
481 void
482 _dbus_transport_do_iteration (DBusTransport  *transport,
483                               unsigned int    flags,
484                               int             timeout_milliseconds)
485 {
486   _dbus_assert (transport->vtable->do_iteration != NULL);
487
488   if ((flags & (DBUS_ITERATION_DO_WRITING |
489                 DBUS_ITERATION_DO_READING)) == 0)
490     return; /* Nothing to do */
491
492   if (transport->disconnected)
493     return;
494
495   _dbus_transport_ref (transport);
496   (* transport->vtable->do_iteration) (transport, flags,
497                                        timeout_milliseconds);
498   _dbus_transport_unref (transport);
499 }
500
501 /**
502  * See dbus_connection_set_max_message_size().
503  *
504  * @param transport the transport
505  * @param size the max size of a single message
506  */
507 void
508 _dbus_transport_set_max_message_size (DBusTransport  *transport,
509                                       long            size)
510 {
511   _dbus_message_loader_set_max_message_size (transport->loader, size);
512 }
513
514 /**
515  * See dbus_connection_get_max_message_size().
516  *
517  * @param transport the transport
518  * @returns max message size
519  */
520 long
521 _dbus_transport_get_max_message_size (DBusTransport  *transport)
522 {
523   return _dbus_message_loader_get_max_message_size (transport->loader);
524 }
525
526 /**
527  * See dbus_connection_set_max_live_messages_size().
528  *
529  * @param transport the transport
530  * @param size the max size of all incoming messages
531  */
532 void
533 _dbus_transport_set_max_live_messages_size (DBusTransport  *transport,
534                                             long            size)
535 {
536   transport->max_live_messages_size = size;
537   _dbus_counter_set_notify (transport->live_messages_size,
538                             transport->max_live_messages_size,
539                             live_messages_size_notify,
540                             transport);
541 }
542
543
544 /**
545  * See dbus_connection_get_max_live_messages_size().
546  *
547  * @param transport the transport
548  * @returns max bytes for all live messages
549  */
550 long
551 _dbus_transport_get_max_live_messages_size (DBusTransport  *transport)
552 {
553   return transport->max_live_messages_size;
554 }
555
556 /** @} */