Commit 502fbda2 authored by Havoc Pennington's avatar Havoc Pennington

2003-01-18 Havoc Pennington <hp@pobox.com>

	* dbus/dbus-transport-unix.c (unix_do_iteration): only do the
	reading/writing if read_watch != NULL or write_watch != NULL.

	* dbus/dbus-message.c (_dbus_message_loader_return_buffer): fix
	the message loader code to actually load message->header and
	message->body into the newly-created message.

	* dbus/dbus-transport-unix.c (check_write_watch): fix a mem leak
	in OOM case

	* dbus/dbus-connection.c (dbus_connection_set_max_message_size)
	(dbus_connection_get_max_message_size)
	(dbus_connection_set_max_live_messages_size)
	(dbus_connection_get_max_live_messages_size): implement some
	resource limitation functions

	* dbus/dbus-resources.c: new file implementing some of the
	resource limits stuff

	* dbus/dbus-message.c (dbus_message_iter_get_byte_array): add
	missing docs, add @todo to handle OOM etc.

	* dbus/dbus-marshal.c (_dbus_demarshal_byte_array): add missing
	docs
parent 0363701c
2003-01-18 Havoc Pennington <hp@pobox.com>
* dbus/dbus-transport-unix.c (unix_do_iteration): only do the
reading/writing if read_watch != NULL or write_watch != NULL.
* dbus/dbus-message.c (_dbus_message_loader_return_buffer): fix
the message loader code to actually load message->header and
message->body into the newly-created message.
* dbus/dbus-transport-unix.c (check_write_watch): fix a mem leak
in OOM case
* dbus/dbus-connection.c (dbus_connection_set_max_message_size)
(dbus_connection_get_max_message_size)
(dbus_connection_set_max_live_messages_size)
(dbus_connection_get_max_live_messages_size): implement some
resource limitation functions
* dbus/dbus-resources.c: new file implementing some of the
resource limits stuff
* dbus/dbus-message.c (dbus_message_iter_get_byte_array): add
missing docs, add @todo to handle OOM etc.
* dbus/dbus-marshal.c (_dbus_demarshal_byte_array): add missing
docs
2003-01-18 Havoc Pennington <hp@pobox.com>
* dbus/dbus-connection.c (dbus_connection_unref): disconnect the
......
......@@ -28,6 +28,8 @@ libdbus_1_la_SOURCES= \
dbus-message.c \
dbus-message-handler.c \
dbus-message-internal.h \
dbus-resources.c \
dbus-resources.h \
dbus-server.c \
dbus-server-protected.h \
dbus-server-unix.c \
......
/* -*- mode: C; c-file-style: "gnu" -*- */
/* dbus-connection.c DBusConnection object
*
* Copyright (C) 2002 Red Hat Inc.
* Copyright (C) 2002, 2003 Red Hat Inc.
*
* Licensed under the Academic Free License version 1.2
*
......@@ -1274,4 +1274,74 @@ _dbus_connection_free_data_slots (DBusConnection *connection)
connection->n_slots = 0;
}
/**
* Specifies the maximum size message this connection is allowed to
* receive. Larger messages will result in disconnecting the
* connection.
*
* @param connection a #DBusConnection
* @param size maximum message size the connection can receive, in bytes
*/
void
dbus_connection_set_max_message_size (DBusConnection *connection,
long size)
{
_dbus_transport_set_max_message_size (connection->transport,
size);
}
/**
* Gets the value set by dbus_connection_set_max_message_size().
*
* @param connection the connection
* @returns the max size of a single message
*/
long
dbus_connection_get_max_message_size (DBusConnection *connection)
{
return _dbus_transport_get_max_message_size (connection->transport);
}
/**
* Sets the maximum total number of bytes that can be used for all messages
* received on this connection. Messages count toward the maximum until
* they are finalized. When the maximum is reached, the connection will
* not read more data until some messages are finalized.
*
* The semantics of the maximum are: if outstanding messages are
* already above the maximum, additional messages will not be read.
* The semantics are not: if the next message would cause us to exceed
* the maximum, we don't read it. The reason is that we don't know the
* size of a message until after we read it.
*
* Thus, the max live messages size can actually be exceeded
* by up to the maximum size of a single message.
*
* Also, if we read say 1024 bytes off the wire in a single read(),
* and that contains a half-dozen small messages, we may exceed the
* size max by that amount. But this should be inconsequential.
*
* @param connection the connection
* @param size the maximum size in bytes of all outstanding messages
*/
void
dbus_connection_set_max_live_messages_size (DBusConnection *connection,
long size)
{
_dbus_transport_set_max_live_messages_size (connection->transport,
size);
}
/**
* Gets the value set by dbus_connection_set_max_live_messages_size().
*
* @param connection the connection
* @returns the max size of all live messages
*/
long
dbus_connection_get_max_live_messages_size (DBusConnection *connection)
{
return _dbus_transport_get_max_live_messages_size (connection->transport);
}
/** @} */
......@@ -132,6 +132,14 @@ dbus_bool_t dbus_connection_set_data (DBusConnection *connection,
void* dbus_connection_get_data (DBusConnection *connection,
int slot);
void dbus_connection_set_max_message_size (DBusConnection *connection,
long size);
long dbus_connection_get_max_message_size (DBusConnection *connection);
void dbus_connection_set_max_live_messages_size (DBusConnection *connection,
long size);
long dbus_connection_get_max_live_messages_size (DBusConnection *connection);
DBUS_END_DECLS;
#endif /* DBUS_CONNECTION_H */
......@@ -401,6 +401,19 @@ _dbus_demarshal_string (DBusString *str,
return retval;
}
/**
* Demarshals a byte array.
*
* @todo Should probably demarshal to a DBusString,
* having memcpy() in here is Evil(tm).
*
* @param str the string containing the data
* @param byte_order the byte order
* @param pos the position in the string
* @param new_pos the new position of the string
* @param array_len length of the demarshaled data
* @returns the demarshaled data.
*/
unsigned char *
_dbus_demarshal_byte_array (DBusString *str,
int byte_order,
......
......@@ -24,6 +24,7 @@
#define DBUS_MESSAGE_INTERNAL_H
#include <dbus/dbus-message.h>
#include <dbus/dbus-resources.h>
DBUS_BEGIN_DECLS;
......@@ -38,6 +39,9 @@ void _dbus_message_lock (DBusMessage *message);
void _dbus_message_set_client_serial (DBusMessage *message,
dbus_int32_t client_serial);
void _dbus_message_add_size_counter (DBusMessage *message,
DBusCounter *counter);
DBusMessageLoader* _dbus_message_loader_new (void);
void _dbus_message_loader_ref (DBusMessageLoader *loader);
void _dbus_message_loader_unref (DBusMessageLoader *loader);
......@@ -52,6 +56,10 @@ DBusMessage* _dbus_message_loader_pop_message (DBusMessageLoader
dbus_bool_t _dbus_message_loader_get_is_corrupted (DBusMessageLoader *loader);
void _dbus_message_loader_set_max_message_size (DBusMessageLoader *loader,
long size);
long _dbus_message_loader_get_max_message_size (DBusMessageLoader *loader);
DBUS_END_DECLS;
#endif /* DBUS_MESSAGE_H */
/* -*- mode: C; c-file-style: "gnu" -*- */
/* dbus-message.c DBusMessage object
*
* Copyright (C) 2002 Red Hat Inc.
* Copyright (C) 2002, 2003 Red Hat Inc.
* Copyright (C) 2002, 2003 CodeFactory AB
*
* Licensed under the Academic Free License version 1.2
......@@ -40,13 +40,6 @@
* @{
*/
/**
* The largest-length message we allow
*
* @todo match this up with whatever the protocol spec says.
*/
#define _DBUS_MAX_MESSAGE_LENGTH (_DBUS_INT_MAX/16)
/**
* @brief Internals of DBusMessage
*
......@@ -73,6 +66,9 @@ struct DBusMessage
dbus_int32_t client_serial; /**< Client serial or -1 if not set */
dbus_int32_t reply_serial; /**< Reply serial or -1 if not set */
DBusCounter *size_counter; /**< Counter for the size of the message, or #NULL */
long size_counter_delta; /**< Size we incremented the size counter by. */
unsigned int locked : 1; /**< Message being sent, no modifications allowed. */
};
......@@ -127,6 +123,39 @@ _dbus_message_set_client_serial (DBusMessage *message,
message->client_serial = client_serial;
}
/**
* Adds a counter to be incremented immediately with the
* size of this message, and decremented by the size
* of this message when this message if finalized.
*
* @param message the message
* @param counter the counter
*/
void
_dbus_message_add_size_counter (DBusMessage *message,
DBusCounter *counter)
{
_dbus_assert (message->size_counter == NULL); /* If this fails we may need to keep a list of
* counters instead of just one
*/
message->size_counter = counter;
_dbus_counter_ref (message->size_counter);
/* When we can change message size, we may want to
* update this each time we do so, or we may want to
* just KISS like this.
*/
message->size_counter_delta =
_dbus_string_get_length (&message->header) +
_dbus_string_get_length (&message->body);
_dbus_verbose ("message has size %ld\n",
message->size_counter_delta);
_dbus_counter_adjust (message->size_counter, message->size_counter_delta);
}
static void
dbus_message_write_header (DBusMessage *message)
{
......@@ -253,7 +282,7 @@ dbus_message_new (const char *service,
message->client_serial = -1;
message->reply_serial = -1;
if (!_dbus_string_init (&message->header, _DBUS_MAX_MESSAGE_LENGTH))
if (!_dbus_string_init (&message->header, _DBUS_INT_MAX))
{
dbus_free (message->service);
dbus_free (message->name);
......@@ -261,7 +290,7 @@ dbus_message_new (const char *service,
return NULL;
}
if (!_dbus_string_init (&message->body, _DBUS_MAX_MESSAGE_LENGTH))
if (!_dbus_string_init (&message->body, _DBUS_INT_MAX))
{
dbus_free (message->service);
dbus_free (message->name);
......@@ -302,6 +331,13 @@ dbus_message_unref (DBusMessage *message)
message->refcount -= 1;
if (message->refcount == 0)
{
if (message->size_counter != NULL)
{
_dbus_counter_adjust (message->size_counter,
- message->size_counter_delta);
_dbus_counter_unref (message->size_counter);
}
_dbus_string_free (&message->header);
_dbus_string_free (&message->body);
......@@ -330,7 +366,7 @@ dbus_message_get_name (DBusMessage *message)
* The list is terminated with 0.
*
* @param message the message
* @param type of the first field
* @param first_field_type type of the first field
* @param ... value of first field, list of additional type-value pairs
* @returns #TRUE on success
*/
......@@ -886,8 +922,21 @@ dbus_message_iter_get_double (DBusMessageIter *iter)
iter->pos + 1, NULL);
}
/**
* Returns the byte array that the iterator may point to.
* Note that you need to check that the iterator points
* to a byte array prior to using this function.
*
* @todo this function should probably take "unsigned char **" as
* an out param argument, and return boolean or result code.
*
* @param iter the iterator
* @param len return location for length of byte array
* @returns the byte array
*/
unsigned char *
dbus_message_iter_get_byte_array (DBusMessageIter *iter, int *len)
dbus_message_iter_get_byte_array (DBusMessageIter *iter,
int *len)
{
_dbus_assert (dbus_message_iter_get_field_type (iter) == DBUS_TYPE_BYTE_ARRAY);
......@@ -927,6 +976,8 @@ struct DBusMessageLoader
DBusList *messages; /**< Complete messages. */
long max_message_size; /**< Maximum size of a message */
unsigned int buffer_outstanding : 1; /**< Someone is using the buffer to read */
unsigned int corrupted : 1; /**< We got broken data, and are no longer working */
......@@ -961,6 +1012,11 @@ _dbus_message_loader_new (void)
loader->refcount = 1;
/* Try to cap message size at something that won't *totally* hose
* the system if we have a couple of them.
*/
loader->max_message_size = _DBUS_ONE_MEGABYTE * 32;
if (!_dbus_string_init (&loader->data, _DBUS_INT_MAX))
{
dbus_free (loader);
......@@ -1190,10 +1246,10 @@ _dbus_message_loader_return_buffer (DBusMessageLoader *loader,
header_len = _dbus_unpack_int32 (byte_order, header_data + 4);
body_len = _dbus_unpack_int32 (byte_order, header_data + 8);
if (header_len + body_len > _DBUS_MAX_MESSAGE_LENGTH)
if (header_len + body_len > loader->max_message_size)
{
_dbus_verbose ("Message claimed length header = %d body = %d exceeds max message length %d\n",
header_len, body_len, _DBUS_MAX_MESSAGE_LENGTH);
header_len, body_len, loader->max_message_size);
loader->corrupted = TRUE;
return;
}
......@@ -1221,11 +1277,38 @@ _dbus_message_loader_return_buffer (DBusMessageLoader *loader,
if (message == NULL)
break; /* ugh, postpone this I guess. */
_dbus_string_copy (&loader->data, header_len, &message->body, 0);
_dbus_message_set_client_serial (message, client_serial);
if (!_dbus_list_append (&loader->messages, message))
{
dbus_message_unref (message);
break;
}
_dbus_assert (_dbus_string_get_length (&message->header) == 0);
_dbus_assert (_dbus_string_get_length (&message->body) == 0);
if (!_dbus_string_move_len (&loader->data, 0, header_len, &message->header, 0))
{
_dbus_list_remove_last (&loader->messages, message);
dbus_message_unref (message);
break;
}
if (!_dbus_string_move_len (&loader->data, 0, body_len, &message->body, 0))
{
dbus_bool_t result;
/* put the header back, we'll try again later */
result = _dbus_string_copy_len (&message->header, 0, header_len,
&loader->data, 0);
_dbus_assert (result); /* because DBusString never reallocs smaller */
_dbus_list_remove_last (&loader->messages, message);
dbus_message_unref (message);
break;
}
_dbus_list_append (&loader->messages, message);
_dbus_string_delete (&loader->data, 0, header_len + body_len);
_dbus_assert (_dbus_string_get_length (&message->header) == header_len);
_dbus_assert (_dbus_string_get_length (&message->body) == body_len);
_dbus_verbose ("Loaded message %p\n", message);
}
......@@ -1264,6 +1347,31 @@ _dbus_message_loader_get_is_corrupted (DBusMessageLoader *loader)
return loader->corrupted;
}
/**
* Sets the maximum size message we allow.
*
* @param loader the loader
* @param size the max message size in bytes
*/
void
_dbus_message_loader_set_max_message_size (DBusMessageLoader *loader,
long size)
{
loader->max_message_size = size;
}
/**
* Gets the maximum allowed message size in bytes.
*
* @param loader the loader
* @returns max size in bytes
*/
long
_dbus_message_loader_get_max_message_size (DBusMessageLoader *loader)
{
return loader->max_message_size;
}
/** @} */
#ifdef DBUS_BUILD_TESTS
#include "dbus-test.h"
......
/* -*- mode: C; c-file-style: "gnu" -*- */
/* dbus-resources.c Resource tracking/limits
*
* Copyright (C) 2003 Red Hat Inc.
*
* Licensed under the Academic Free License version 1.2
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include <dbus/dbus-resources.h>
#include <dbus/dbus-internals.h>
/**
* @defgroup DBusResources Resource limits related code
* @ingroup DBusInternals
* @brief DBusCounter and other stuff related to resource limits
*
* Types and functions related to tracking resource limits,
* such as the maximum amount of memory a connection can use
* for messages, etc.
*/
/**
* @defgroup DBusResourcesInternals Resource limits implementation details
* @ingroup DBusInternals
* @brief Resource limits implementation details
*
* Implementation details of resource limits code.
*
* @{
*/
/**
* @brief Internals of DBusCounter.
*
* DBusCounter internals. DBusCounter is an opaque object, it must be
* used via accessor functions.
*/
struct DBusCounter
{
int refcount; /**< reference count */
long value; /**< current counter value */
long notify_guard_value; /**< call notify function when crossing this value */
DBusCounterNotifyFunction notify_function; /**< notify function */
void *notify_data; /**< data for notify function */
};
/** @} */ /* end of resource limits internals docs */
/**
* @addtogroup DBusResources
* @{
*/
/**
* Creates a new DBusCounter. DBusCounter is used
* to count usage of some resource such as memory.
*
* @returns new counter or #NULL on failure
*/
DBusCounter*
_dbus_counter_new (void)
{
DBusCounter *counter;
counter = dbus_new (DBusCounter, 1);
if (counter == NULL)
return NULL;
counter->refcount = 1;
counter->value = 0;
counter->notify_guard_value = 0;
counter->notify_function = NULL;
counter->notify_data = NULL;
return counter;
}
/**
* Increments refcount of the counter
*
* @param counter the counter
*/
void
_dbus_counter_ref (DBusCounter *counter)
{
_dbus_assert (counter->refcount > 0);
counter->refcount += 1;
}
/**
* Decrements refcount of the counter and possibly
* finalizes the counter.
*
* @param counter the counter
*/
void
_dbus_counter_unref (DBusCounter *counter)
{
_dbus_assert (counter->refcount > 0);
counter->refcount -= 1;
if (counter->refcount == 0)
{
dbus_free (counter);
}
}
/**
* Adjusts the value of the counter by the given
* delta which may be positive or negative.
* Calls the notify function from _dbus_counter_set_notify()
* if that function has been specified.
*
* @param counter the counter
* @param delta value to add to the counter's current value
*/
void
_dbus_counter_adjust (DBusCounter *counter,
long delta)
{
long old = counter->value;
counter->value += delta;
#if 0
_dbus_verbose ("Adjusting counter %ld by %ld = %ld\n",
old, delta, counter->value);
#endif
if (counter->notify_function != NULL &&
((old < counter->notify_guard_value &&
counter->value >= counter->notify_guard_value) ||
(old >= counter->notify_guard_value &&
counter->value < counter->notify_guard_value)))
(* counter->notify_function) (counter, counter->notify_data);
}
/**
* Gets the current value of the counter.
*
* @param counter the counter
* @returns its current value
*/
long
_dbus_counter_get_value (DBusCounter *counter)
{
return counter->value;
}
/**
* Sets the notify function for this counter; the notify function is
* called whenever the counter's value crosses the guard value in
* either direction (moving up, or moving down).
*
* @param counter the counter
* @param guard_value the value we're notified if the counter crosses
* @param function function to call in order to notify
* @param user_data data to pass to the function
*/
void
_dbus_counter_set_notify (DBusCounter *counter,
long guard_value,
DBusCounterNotifyFunction function,
void *user_data)
{
counter->notify_guard_value = guard_value;
counter->notify_function = function;
counter->notify_data = user_data;
}
/** @} */ /* end of resource limits exported API */
/* -*- mode: C; c-file-style: "gnu" -*- */
/* dbus-resources.h Resource tracking/limits
*
* Copyright (C) 2003 Red Hat Inc.
*
* Licensed under the Academic Free License version 1.2
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#ifndef DBUS_RESOURCES_H
#define DBUS_RESOURCES_H
#include <dbus/dbus-macros.h>
#include <dbus/dbus-errors.h>
#include <dbus/dbus-connection.h>
DBUS_BEGIN_DECLS;
typedef struct DBusCounter DBusCounter;
typedef void (* DBusCounterNotifyFunction) (DBusCounter *counter,
void *user_data);
DBusCounter* _dbus_counter_new (void);
void _dbus_counter_ref (DBusCounter *counter);
void _dbus_counter_unref (DBusCounter *counter);
void _dbus_counter_adjust (DBusCounter *counter,
long delta);
long _dbus_counter_get_value (DBusCounter *counter);
void _dbus_counter_set_notify (DBusCounter *counter,
long guard_value,
DBusCounterNotifyFunction function,
void *user_data);
DBUS_END_DECLS;
#endif /* DBUS_RESOURCES_H */
......@@ -28,6 +28,7 @@
#include <dbus/dbus-transport.h>
#include <dbus/dbus-message-internal.h>
#include <dbus/dbus-auth.h>
#include <dbus/dbus-resources.h>
DBUS_BEGIN_DECLS;
......@@ -63,6 +64,9 @@ struct DBusTransportVTable
/**< Called to do a single "iteration" (block on select/poll
* followed by reading or writing data).
*/
void (* live_messages_changed) (DBusTransport *transport);
/**< Outstanding messages counter changed */
};
struct DBusTransport
......@@ -79,6 +83,10 @@ struct DBusTransport
DBusCredentials credentials; /**< Credentials of other end */
long max_live_messages_size; /**< Max total size of received messages. */
DBusCounter *live_messages_size; /**< Counter for size of all live messages. */
unsigned int disconnected : 1; /**< #TRUE if we are disconnected. */
unsigned int authenticated : 1; /**< Cache of auth state; use _dbus_transport_get_is_authenticated() to query value */
unsigned int messages_need_sending : 1; /**< #TRUE if we need to write messages out */
......
/* -*- mode: C; c-file-style: "gnu" -*- */
/* dbus-transport-unix.c UNIX socket subclasses of DBusTransport
*
* Copyright (C) 2002 Red Hat Inc.
* Copyright (C) 2002, 2003 Red Hat Inc.
*
* Licensed under the Academic Free License version 1.2
*
......@@ -51,7 +51,7 @@ struct DBusTransportUnix
{
DBusTransport base; /**< Parent instance */
int fd; /**< File descriptor. */
DBusWatch *watch; /**< Watch for readability. */
DBusWatch *read_watch; /**< Watch for readability. */
DBusWatch *write_watch; /**< Watch for writability. */
int max_bytes_read_per_iteration; /**< To avoid blocking too long. */
......@@ -71,14 +71,14 @@ free_watches (DBusTransport *transport)
{
DBusTransportUnix *unix_transport = (DBusTransportUnix*) transport;
if (unix_transport->watch)
if (unix_transport->read_watch)
{
if (transport->connection)
_dbus_connection_remove_watch (transport->connection,
unix_transport->watch);
_dbus_watch_invalidate (unix_transport->watch);
_dbus_watch_unref (unix_transport->watch);
unix_transport->watch = NULL;
unix_transport->read_watch);
_dbus_watch_invalidate (unix_transport->read_watch);
_dbus_watch_unref (unix_transport->read_watch);
unix_transport->read_watch = NULL;
}
if (unix_transport->write_watch)
......@@ -103,7 +103,7 @@ unix_finalize (DBusTransport *transport)
_dbus_transport_finalize_base (transport);
_dbus_assert (unix_transport->watch == NULL);
_dbus_assert (unix_transport->read_watch == NULL);
_dbus_assert (unix_transport->write_watch == NULL);
dbus_free (transport);
......@@ -138,7 +138,7 @@ check_write_watch (DBusTransport *transport)
/* we can maybe add it some other time, just silently bomb */
if (unix_transport->write_watch == NULL)
return;
goto out;
if (!_dbus_connection_add_watch (transport->connection,
unix_transport->write_watch))
......@@ -158,6 +158,63 @@ check_write_watch (DBusTransport *transport)
unix_transport->write_watch = NULL;
}
out: