2003-02-16 Anders Carlsson <andersca@codefactory.se>
[platform/upstream/dbus.git] / dbus / dbus-transport.c
index 085b022..5a25a67 100644 (file)
@@ -1,7 +1,7 @@
 /* -*- 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-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.
  */
 
-/**
- * Refs a transport and associated connection for reentrancy.
- *
- * @todo this macro reflects a design mistake, which is that the
- * transport has a pointer to its connection. Ownership should move in
- * only one direction; the connection should push/pull from the
- * transport, rather than vice versa. Then the connection would take
- * care of referencing itself when needed.
- */
-#define DBUS_TRANSPORT_HOLD_REF(t) \
-  _dbus_transport_ref (t); if ((t)->connection) dbus_connection_ref ((t)->connection)
+static void
+live_messages_size_notify (DBusCounter *counter,
+                           void        *user_data)
+{
+  DBusTransport *transport = user_data;
 
-/**
- * Inverse of DBUS_TRANSPORT_HOLD_REF().
- */
-#define DBUS_TRANSPORT_RELEASE_REF(t) \
-  if ((t)->connection) dbus_connection_unref ((t)->connection); _dbus_transport_unref (t)
+  _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.
@@ -83,11 +90,12 @@ _dbus_transport_init_base (DBusTransport             *transport,
 {
   DBusMessageLoader *loader;
   DBusAuth *auth;
+  DBusCounter *counter;
   
   loader = _dbus_message_loader_new ();
   if (loader == NULL)
     return FALSE;
-
+  
   if (server)
     auth = _dbus_auth_server_new ();
   else
@@ -97,14 +105,40 @@ _dbus_transport_init_base (DBusTransport             *transport,
       _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;
 }
@@ -120,16 +154,20 @@ _dbus_transport_finalize_base (DBusTransport *transport)
 {
   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.  (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.
@@ -140,18 +178,53 @@ _dbus_transport_open (const char     *address,
                       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");
 
-  /* Pretend it's just a unix domain socket name for now */
-  transport = _dbus_transport_new_for_domain_socket (address,
-                                                     FALSE,
-                                                     result);
+         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");
+
+         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;
 }
 
 /**
@@ -203,11 +276,14 @@ _dbus_transport_disconnect (DBusTransport *transport)
   if (transport->disconnected)
     return;
 
-  DBUS_TRANSPORT_HOLD_REF (transport);
+  _dbus_transport_ref (transport);
   (* transport->vtable->disconnect) (transport);
-
+  
   transport->disconnected = TRUE;
-  DBUS_TRANSPORT_RELEASE_REF (transport);
+
+  _dbus_connection_notify_disconnected (transport->connection);
+  
+  _dbus_transport_unref (transport);
 }
 
 /**
@@ -238,9 +314,45 @@ _dbus_transport_get_is_authenticated (DBusTransport *transport)
     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;
     }
 }
@@ -261,11 +373,7 @@ _dbus_transport_handle_watch (DBusTransport           *transport,
   _dbus_assert (transport->vtable->handle_watch != NULL);
 
   if (transport->disconnected)
-    {
-      _dbus_connection_transport_error (transport->connection,
-                                        DBUS_RESULT_DISCONNECTED);
-      return;
-    }
+    return;
 
   if (dbus_watch_get_fd (watch) < 0)
     {
@@ -275,11 +383,11 @@ _dbus_transport_handle_watch (DBusTransport           *transport,
   
   _dbus_watch_sanitize_condition (watch, &condition);
 
-  DBUS_TRANSPORT_HOLD_REF (transport);
+  _dbus_transport_ref (transport);
   _dbus_watch_ref (watch);
   (* transport->vtable->handle_watch) (transport, watch, condition);
   _dbus_watch_unref (watch);
-  DBUS_TRANSPORT_RELEASE_REF (transport);
+  _dbus_transport_unref (transport);
 }
 
 /**
@@ -299,9 +407,9 @@ _dbus_transport_set_connection (DBusTransport  *transport,
   
   transport->connection = connection;
 
-  DBUS_TRANSPORT_HOLD_REF (transport);
+  _dbus_transport_ref (transport);
   (* transport->vtable->connection_set) (transport);
-  DBUS_TRANSPORT_RELEASE_REF (transport);
+  _dbus_transport_unref (transport);
 }
 
 /**
@@ -320,18 +428,14 @@ _dbus_transport_messages_pending (DBusTransport  *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_HOLD_REF (transport);
+  _dbus_transport_ref (transport);
   (* transport->vtable->messages_pending) (transport,
                                            queue_length);
-  DBUS_TRANSPORT_RELEASE_REF (transport);
+  _dbus_transport_unref (transport);
 }
 
 /**
@@ -357,16 +461,67 @@ _dbus_transport_do_iteration (DBusTransport  *transport,
     return; /* Nothing to do */
 
   if (transport->disconnected)
-    {
-      _dbus_connection_transport_error (transport->connection,
-                                        DBUS_RESULT_DISCONNECTED);
-      return;
-    }
+    return;
 
-  DBUS_TRANSPORT_HOLD_REF (transport);
+  _dbus_transport_ref (transport);
   (* transport->vtable->do_iteration) (transport, flags,
                                        timeout_milliseconds);
-  DBUS_TRANSPORT_RELEASE_REF (transport);
+  _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;
 }
 
 /** @} */