/* -*- mode: C; c-file-style: "gnu" -*- */
/* dbus-transport.c DBusTransport object (internal to D-BUS implementation)
*
- * Copyright (C) 2002 Red Hat Inc.
+ * Copyright (C) 2002, 2003 Red Hat Inc.
*
* Licensed under the Academic Free License version 1.2
*
#include "dbus-transport-unix.h"
#include "dbus-connection-internal.h"
#include "dbus-watch.h"
+#include "dbus-auth.h"
+#include "dbus-address.h"
+#ifdef DBUS_BUILD_TESTS
+#include "dbus-transport-debug.h"
+#endif
/**
* @defgroup DBusTransport DBusTransport object
* or encryption schemes.
*/
+static void
+live_messages_size_notify (DBusCounter *counter,
+ void *user_data)
+{
+ DBusTransport *transport = user_data;
+
+ _dbus_transport_ref (transport);
+
+#if 0
+ _dbus_verbose ("Counter value is now %d\n",
+ (int) _dbus_counter_get_value (counter));
+#endif
+
+ /* disable or re-enable the read watch for the transport if
+ * required.
+ */
+ if (* transport->vtable->live_messages_changed)
+ (* transport->vtable->live_messages_changed) (transport);
+
+ _dbus_transport_unref (transport);
+}
+
/**
* Initializes the base class members of DBusTransport.
* Chained up to by subclasses in their constructor.
*
* @param transport the transport being created.
* @param vtable the subclass vtable.
+ * @param server #TRUE if this transport is on the server side of a connection
* @returns #TRUE on success.
*/
dbus_bool_t
_dbus_transport_init_base (DBusTransport *transport,
- const DBusTransportVTable *vtable)
+ const DBusTransportVTable *vtable,
+ dbus_bool_t server)
{
DBusMessageLoader *loader;
-
+ DBusAuth *auth;
+ DBusCounter *counter;
+
loader = _dbus_message_loader_new ();
if (loader == NULL)
return FALSE;
+ if (server)
+ auth = _dbus_auth_server_new ();
+ else
+ auth = _dbus_auth_client_new ();
+ if (auth == NULL)
+ {
+ _dbus_message_loader_unref (loader);
+ return FALSE;
+ }
+
+ counter = _dbus_counter_new ();
+ if (counter == NULL)
+ {
+ _dbus_auth_unref (auth);
+ _dbus_message_loader_unref (loader);
+ return FALSE;
+ }
+
transport->refcount = 1;
transport->vtable = vtable;
transport->loader = loader;
+ transport->auth = auth;
+ transport->live_messages_size = counter;
+ transport->authenticated = FALSE;
+ transport->messages_need_sending = FALSE;
+ transport->disconnected = FALSE;
+ transport->send_credentials_pending = !server;
+ transport->receive_credentials_pending = server;
+ transport->is_server = server;
+
+ /* Try to default to something that won't totally hose the system,
+ * but doesn't impose too much of a limitation.
+ */
+ transport->max_live_messages_size = _DBUS_ONE_MEGABYTE * 63;
+
+ transport->credentials.pid = -1;
+ transport->credentials.uid = -1;
+ transport->credentials.gid = -1;
+
+ _dbus_counter_set_notify (transport->live_messages_size,
+ transport->max_live_messages_size,
+ live_messages_size_notify,
+ transport);
return TRUE;
}
{
if (!transport->disconnected)
_dbus_transport_disconnect (transport);
-
+
_dbus_message_loader_unref (transport->loader);
+ _dbus_auth_unref (transport->auth);
+ _dbus_counter_set_notify (transport->live_messages_size,
+ 0, NULL, NULL);
+ _dbus_counter_unref (transport->live_messages_size);
}
/**
- * Opens a new transport for the given address.
+ * Opens a new transport for the given address. (This opens a
+ * client-side-of-the-connection transport.)
*
- * @todo right now the address is just a Unix domain socket path.
+ * @todo error messages on bad address could really be better.
+ * DBusResultCode is a bit limiting here.
*
* @param address the address.
* @param result location to store reason for failure.
DBusResultCode *result)
{
DBusTransport *transport;
+ DBusAddressEntry **entries;
+ int len, i;
- /* FIXME parse the address - whatever format
- * we decide addresses are in - and find the
- * appropriate transport.
- */
+ if (!dbus_parse_address (address, &entries, &len, result))
+ return NULL;
+
+ transport = NULL;
+
+ for (i = 0; i < len; i++)
+ {
+ const char *method = dbus_address_entry_get_method (entries[i]);
+
+ if (strcmp (method, "unix") == 0)
+ {
+ const char *path = dbus_address_entry_get_value (entries[i], "path");
+
+ if (path == NULL)
+ goto bad_address;
+
+ transport = _dbus_transport_new_for_domain_socket (path, FALSE, result);
+ }
+#ifdef DBUS_BUILD_TESTS
+ else if (strcmp (method, "debug") == 0)
+ {
+ const char *name = dbus_address_entry_get_value (entries[i], "name");
- /* Pretend it's just a unix domain socket name for now */
- transport = _dbus_transport_new_for_domain_socket (address, result);
+ if (name == NULL)
+ goto bad_address;
+
+ transport = _dbus_transport_debug_client_new (name, result);
+ }
+#endif
+ else
+ goto bad_address;
+
+ if (transport)
+ break;
+ }
+ dbus_address_entries_free (entries);
return transport;
+
+ bad_address:
+ dbus_address_entries_free (entries);
+ dbus_set_result (result, DBUS_RESULT_BAD_ADDRESS);
+
+ return NULL;
}
/**
if (transport->disconnected)
return;
-
- (* transport->vtable->disconnect) (transport);
+ _dbus_transport_ref (transport);
+ (* transport->vtable->disconnect) (transport);
+
transport->disconnected = TRUE;
+
+ _dbus_connection_notify_disconnected (transport->connection);
+
+ _dbus_transport_unref (transport);
}
/**
* or because the server drops its end of the connection.
*
* @param transport the transport.
+ * @returns whether we're connected
*/
dbus_bool_t
_dbus_transport_get_is_connected (DBusTransport *transport)
}
/**
+ * Returns #TRUE if we have been authenticated. Will return #TRUE
+ * even if the transport is disconnected.
+ *
+ * @param transport the transport
+ * @returns whether we're authenticated
+ */
+dbus_bool_t
+_dbus_transport_get_is_authenticated (DBusTransport *transport)
+{
+ if (transport->authenticated)
+ return TRUE;
+ else
+ {
+ if (transport->disconnected)
+ return FALSE;
+
+ transport->authenticated =
+ (!(transport->send_credentials_pending ||
+ transport->receive_credentials_pending)) &&
+ _dbus_auth_do_work (transport->auth) == DBUS_AUTH_STATE_AUTHENTICATED;
+
+ /* If we've authenticated as some identity, check that the auth
+ * identity is the same as our own identity. In the future, we
+ * may have API allowing applications to specify how this is
+ * done, for example they may allow connection as any identity,
+ * but then impose restrictions on certain identities.
+ * Or they may give certain identities extra privileges.
+ */
+
+ if (transport->authenticated && transport->is_server)
+ {
+ DBusCredentials auth_identity;
+ DBusCredentials our_identity;
+
+ _dbus_credentials_from_current_process (&our_identity);
+ _dbus_auth_get_identity (transport->auth, &auth_identity);
+
+ if (!_dbus_credentials_match (&our_identity,
+ &auth_identity))
+ {
+ _dbus_verbose ("Client authorized as UID %d but our UID is %d, disconnecting\n",
+ auth_identity.uid, our_identity.uid);
+ _dbus_transport_disconnect (transport);
+ return FALSE;
+ }
+ else
+ {
+ _dbus_verbose ("Client authorized as UID %d matching our UID %d\n",
+ auth_identity.uid, our_identity.uid);
+ }
+ }
+
+ return transport->authenticated;
+ }
+}
+
+/**
* Handles a watch by reading data, writing data, or disconnecting
* the transport, as appropriate for the given condition.
*
_dbus_assert (transport->vtable->handle_watch != NULL);
if (transport->disconnected)
+ return;
+
+ if (dbus_watch_get_fd (watch) < 0)
{
- _dbus_connection_transport_error (transport->connection,
- DBUS_RESULT_DISCONNECTED);
+ _dbus_warn ("Tried to handle an invalidated watch; this watch should have been removed\n");
return;
}
-
- _dbus_watch_sanitize_condition (watch, &condition);
+ _dbus_watch_sanitize_condition (watch, &condition);
+
+ _dbus_transport_ref (transport);
+ _dbus_watch_ref (watch);
(* transport->vtable->handle_watch) (transport, watch, condition);
+ _dbus_watch_unref (watch);
+ _dbus_transport_unref (transport);
}
/**
transport->connection = connection;
+ _dbus_transport_ref (transport);
(* transport->vtable->connection_set) (transport);
+ _dbus_transport_unref (transport);
}
/**
_dbus_assert (transport->vtable->messages_pending != NULL);
if (transport->disconnected)
- {
- _dbus_connection_transport_error (transport->connection,
- DBUS_RESULT_DISCONNECTED);
- return;
- }
-
+ return;
+
+ transport->messages_need_sending = queue_length > 0;
+
+ _dbus_transport_ref (transport);
(* transport->vtable->messages_pending) (transport,
queue_length);
+ _dbus_transport_unref (transport);
}
/**
return; /* Nothing to do */
if (transport->disconnected)
- {
- _dbus_connection_transport_error (transport->connection,
- DBUS_RESULT_DISCONNECTED);
- return;
- }
-
+ return;
+
+ _dbus_transport_ref (transport);
(* transport->vtable->do_iteration) (transport, flags,
timeout_milliseconds);
+ _dbus_transport_unref (transport);
+}
+
+/**
+ * See dbus_connection_set_max_message_size().
+ *
+ * @param transport the transport
+ * @param size the max size of a single message
+ */
+void
+_dbus_transport_set_max_message_size (DBusTransport *transport,
+ long size)
+{
+ _dbus_message_loader_set_max_message_size (transport->loader, size);
+}
+
+/**
+ * See dbus_connection_get_max_message_size().
+ *
+ * @param transport the transport
+ * @returns max message size
+ */
+long
+_dbus_transport_get_max_message_size (DBusTransport *transport)
+{
+ return _dbus_message_loader_get_max_message_size (transport->loader);
+}
+
+/**
+ * See dbus_connection_set_max_live_messages_size().
+ *
+ * @param transport the transport
+ * @param size the max size of all incoming messages
+ */
+void
+_dbus_transport_set_max_live_messages_size (DBusTransport *transport,
+ long size)
+{
+ transport->max_live_messages_size = size;
+ _dbus_counter_set_notify (transport->live_messages_size,
+ transport->max_live_messages_size,
+ live_messages_size_notify,
+ transport);
+}
+
+
+/**
+ * See dbus_connection_get_max_live_messages_size().
+ *
+ * @param transport the transport
+ * @returns max bytes for all live messages
+ */
+long
+_dbus_transport_get_max_live_messages_size (DBusTransport *transport)
+{
+ return transport->max_live_messages_size;
}
/** @} */