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