Merge branch 'dbus-1.4'

This commit is contained in:
Simon McVittie 2011-06-10 18:52:07 +01:00
commit c83287898c
16 changed files with 1763 additions and 88 deletions

21
NEWS
View file

@ -1,8 +1,29 @@
D-Bus 1.5.4 (UNRELEASED)
==
Security (local denial of service):
• Byte-swap foreign-endian messages correctly, preventing a long-standing
local DoS if foreign-endian messages are relayed through the dbus-daemon
(backporters: this is git commit c3223ba6c401ba81df1305851312a47c485e6cd7)
(fd.o #38120, Debian #629938, no CVE number yet; Simon McVittie)
New things:
• The constant to use for an infinite timeout now has a name,
DBUS_TIMEOUT_INFINITE. It is numerically equivalent to 0x7fffffff (INT32_MAX)
which can be used for source compatibility with older versions of libdbus.
• If GLib and DBus-GLib are already installed, more tests will be built,
providing better coverage. They can be installed via
./configure --enable-installed-tests
for system integration testing, if required. (fd.o #34570, Simon McVittie)
Changes:
• Consistently use atomic operations for the DBusConnection's refcount
(fd.o #38005, Simon McVittie)
• Don't use -Wl,--gc-sections by default: in practice the size decrease is
small (300KiB on x86-64) and it frequently doesn't work in unusual
toolchains. To optimize for minimum installed size, you should benchmark

View file

@ -226,7 +226,7 @@ ENABLE_TESTING()
option (DBUS_BUILD_TESTS "enable unit test code" ON)
if(DBUS_BUILD_TESTS)
add_definitions(-DDBUS_BUILD_TESTS)
add_definitions(-DDBUS_BUILD_TESTS -DDBUS_ENABLE_EMBEDDED_TESTS)
endif(DBUS_BUILD_TESTS)
option (DBUS_USE_OUTPUT_DEBUG_STRING "enable win32 debug port for message output" OFF)

View file

@ -122,7 +122,6 @@ AM_CONDITIONAL(DBUS_WINCE, test "$dbus_wince" = yes)
AM_CONDITIONAL(DBUS_UNIX, test "$dbus_unix" = yes)
AM_CONDITIONAL(DBUS_CYGWIN, test "$dbus_cygwin" = yes)
AC_ARG_ENABLE(tests, AS_HELP_STRING([--enable-tests],[enable unit test code in the library and binaries]),enable_tests=$enableval,enable_tests=$USE_MAINTAINER_MODE)
AC_ARG_ENABLE(ansi, AS_HELP_STRING([--enable-ansi],[enable -ansi -pedantic gcc flags]),enable_ansi=$enableval,enable_ansi=no)
AC_ARG_ENABLE(verbose-mode, AS_HELP_STRING([--enable-verbose-mode],[support verbose debug mode]),enable_verbose_mode=$enableval,enable_verbose_mode=$USE_MAINTAINER_MODE)
AC_ARG_ENABLE(asserts, AS_HELP_STRING([--enable-asserts],[include assertion checks]),enable_asserts=$enableval,enable_asserts=$USE_MAINTAINER_MODE)
@ -151,20 +150,80 @@ AC_ARG_WITH(launchd-agent-dir, AS_HELP_STRING([--with-launchd-agent-dir=[dirname
AC_ARG_WITH(dbus_user, AS_HELP_STRING([--with-dbus-user=<user>],[User for running the DBUS daemon (messagebus)]))
AC_ARG_WITH(dbus_daemondir, AS_HELP_STRING([--with-dbus-daemondir=[dirname]],[Directory for installing the DBUS daemon]))
dnl DBUS_BUILD_TESTS controls unit tests built in to .c files
dnl and also some stuff in the test/ subdir
AM_CONDITIONAL(DBUS_BUILD_TESTS, test x$enable_tests = xyes)
if test x$enable_tests = xyes; then
AC_DEFINE(DBUS_BUILD_TESTS,1,[Build test code])
AC_ARG_ENABLE([embedded-tests],
AS_HELP_STRING([--enable-embedded-tests],
[enable unit test code in the library and binaries]),
[], [enable_embedded_tests=$USE_MAINTAINER_MODE])
AC_ARG_ENABLE([modular-tests],
AS_HELP_STRING([--enable-modular-tests],
[enable modular regression tests (requires GLib)]),
[], [enable_modular_tests=auto])
# --enable-tests overrides both --enable-embedded-tests and
# --enable-modular-tests
AC_ARG_ENABLE([tests],
AS_HELP_STRING([--enable-tests],
[enable/disable all tests, overriding embedded-tests/modular-tests]),
[enable_embedded_tests=$enableval; enable_modular_tests=$enableval],
[])
# DBUS_ENABLE_EMBEDDED_TESTS controls unit tests built in to .c files
# and also some stuff in the test/ subdir. DBUS_BUILD_TESTS was an older
# name for this.
AM_CONDITIONAL([DBUS_BUILD_TESTS], [test "x$enable_embedded_tests" = xyes])
AM_CONDITIONAL([DBUS_ENABLE_EMBEDDED_TESTS],
[test "x$enable_embedded_tests" = xyes])
if test "x$enable_embedded_tests" = xyes; then
AC_DEFINE([DBUS_ENABLE_EMBEDDED_TESTS], [1],
[Define to build test code into the library and binaries])
AC_DEFINE([DBUS_BUILD_TESTS], [1],
[Define to build test code into the library and binaries])
fi
# DBUS_ENABLE_MODULAR_TESTS controls tests that work based on public API.
# These use GTest, from GLib, because life's too short. They're enabled by
# default (unless you don't have GLib), because they don't bloat the library
# or binaries.
if test "x$enable_modular_tests" != xno; then
PKG_CHECK_MODULES([GLIB], [glib-2.0 >= 2.22, gio-2.0 >= 2.22],
[],
[if test "x$enable_modular_tests" = xyes; then
AC_MSG_ERROR([GLib is required by the modular tests])
else # assumed to be "auto"
enable_modular_tests=no
fi])
# If dbus-gmain.[ch] returned to libdbus then we wouldn't need this
PKG_CHECK_MODULES([DBUS_GLIB], [dbus-glib-1],
[],
[if test "x$enable_modular_tests" = xyes; then
AC_MSG_ERROR([dbus-glib is required by the modular tests (for now)])
else # assumed to be "auto"
enable_modular_tests=no
fi])
if test "x$enable_modular_tests" != xno; then
# dependencies checked, switch from auto to yes
enable_modular_tests=yes
fi
fi
if test "x$enable_modular_tests" = xyes; then
AC_DEFINE([DBUS_ENABLE_MODULAR_TESTS], [1],
[Define to build independent test binaries (requires GLib)])
fi
AM_CONDITIONAL([DBUS_ENABLE_MODULAR_TESTS],
[test "x$enable_modular_tests" = xyes])
AC_ARG_ENABLE([installed-tests],
AS_HELP_STRING([--enable-installed-tests],
[enable unit test code in the library and binaries]),
[], [enable_installed_tests=no])
AM_CONDITIONAL([DBUS_ENABLE_INSTALLED_TESTS],
[test "x$enable_installed_tests" = xyes])
if test x$enable_verbose_mode = xyes; then
AC_DEFINE(DBUS_ENABLE_VERBOSE_MODE,1,[Support a verbose mode])
fi
if test x$enable_asserts = xno; then
AC_DEFINE(DBUS_DISABLE_ASSERT,1,[Disable assertion checking])
AC_DEFINE(G_DISABLE_ASSERT,1,[Disable GLib assertion macros])
R_DYNAMIC_LDFLAG=""
else
# -rdynamic is needed for glibc's backtrace_symbols to work.
@ -1569,7 +1628,8 @@ echo "
echo "
Maintainer mode: ${USE_MAINTAINER_MODE}
gcc coverage profiling: ${enable_compiler_coverage}
Building unit tests: ${enable_tests}
Building embedded tests: ${enable_embedded_tests}
Building modular tests: ${enable_modular_tests}
Building verbose mode: ${enable_verbose_mode}
Building assertions: ${enable_asserts}
Building checks: ${enable_checks}
@ -1601,11 +1661,11 @@ if test x$have_launchd = xyes; then
fi
echo
if test x$enable_tests = xyes; then
if test x$enable_embedded_tests = xyes; then
echo "NOTE: building with unit tests increases the size of the installed library and renders it insecure."
fi
if test x$enable_tests = xyes -a x$enable_asserts = xno; then
echo "NOTE: building with unit tests but without assertions means tests may not properly report failures (this configuration is only useful when doing something like profiling the tests)"
if test x$enable_embedded_tests = xyes -a x$enable_asserts = xno; then
echo "NOTE: building with embedded tests but without assertions means tests may not properly report failures (this configuration is only useful when doing something like profiling the tests)"
fi
if test x$enable_compiler_coverage = xyes; then
echo "NOTE: building with coverage profiling is definitely for developers only."

View file

@ -1400,13 +1400,8 @@ _dbus_connection_ref_unlocked (DBusConnection *connection)
_dbus_assert (connection->generation == _dbus_current_generation);
HAVE_LOCK_CHECK (connection);
#ifdef DBUS_HAVE_ATOMIC_INT
_dbus_atomic_inc (&connection->refcount);
#else
_dbus_assert (connection->refcount.value > 0);
connection->refcount.value += 1;
#endif
return connection;
}
@ -1426,22 +1421,8 @@ _dbus_connection_unref_unlocked (DBusConnection *connection)
_dbus_assert (connection != NULL);
/* The connection lock is better than the global
* lock in the atomic increment fallback
*/
#ifdef DBUS_HAVE_ATOMIC_INT
last_unref = (_dbus_atomic_dec (&connection->refcount) == 1);
#else
_dbus_assert (connection->refcount.value > 0);
connection->refcount.value -= 1;
last_unref = (connection->refcount.value == 0);
#if 0
printf ("unref_unlocked() connection %p count = %d\n", connection, connection->refcount.value);
#endif
#endif
if (last_unref)
_dbus_connection_last_unref (connection);
}
@ -2127,11 +2108,23 @@ _dbus_connection_send_and_unlock (DBusConnection *connection,
void
_dbus_connection_close_if_only_one_ref (DBusConnection *connection)
{
CONNECTION_LOCK (connection);
_dbus_assert (connection->refcount.value > 0);
dbus_int32_t tmp_refcount;
if (connection->refcount.value == 1)
CONNECTION_LOCK (connection);
/* We increment and then decrement the refcount, because there is no
* _dbus_atomic_get (mirroring the fact that there's no InterlockedGet
* on Windows). */
_dbus_atomic_inc (&connection->refcount);
tmp_refcount = _dbus_atomic_dec (&connection->refcount);
/* The caller should have one ref, and this function temporarily took
* one more, which is reflected in this count even though we already
* released it (relying on the caller's ref) due to _dbus_atomic_dec
* semantics */
_dbus_assert (tmp_refcount >= 2);
if (tmp_refcount == 2)
_dbus_connection_close_possibly_shared_and_unlock (connection);
else
CONNECTION_UNLOCK (connection);
@ -2633,25 +2626,8 @@ dbus_connection_ref (DBusConnection *connection)
{
_dbus_return_val_if_fail (connection != NULL, NULL);
_dbus_return_val_if_fail (connection->generation == _dbus_current_generation, NULL);
/* The connection lock is better than the global
* lock in the atomic increment fallback
*
* (FIXME but for now we always use the atomic version,
* to avoid taking the connection lock, due to
* the mess with set_timeout_functions()/set_watch_functions()
* calling out to the app without dropping locks)
*/
#if 1
_dbus_atomic_inc (&connection->refcount);
#else
CONNECTION_LOCK (connection);
_dbus_assert (connection->refcount.value > 0);
connection->refcount.value += 1;
CONNECTION_UNLOCK (connection);
#endif
_dbus_atomic_inc (&connection->refcount);
return connection;
}
@ -2788,33 +2764,9 @@ dbus_connection_unref (DBusConnection *connection)
_dbus_return_if_fail (connection != NULL);
_dbus_return_if_fail (connection->generation == _dbus_current_generation);
/* The connection lock is better than the global
* lock in the atomic increment fallback
*
* (FIXME but for now we always use the atomic version,
* to avoid taking the connection lock, due to
* the mess with set_timeout_functions()/set_watch_functions()
* calling out to the app without dropping locks)
*/
#if 1
last_unref = (_dbus_atomic_dec (&connection->refcount) == 1);
#else
CONNECTION_LOCK (connection);
_dbus_assert (connection->refcount.value > 0);
connection->refcount.value -= 1;
last_unref = (connection->refcount.value == 0);
#if 0
printf ("unref() connection %p count = %d\n", connection, connection->refcount.value);
#endif
CONNECTION_UNLOCK (connection);
#endif
if (last_unref)
{
#ifndef DBUS_DISABLE_CHECKS
@ -3362,8 +3314,9 @@ reply_handler_timeout (void *data)
*
* If -1 is passed for the timeout, a sane default timeout is used. -1
* is typically the best value for the timeout for this reason, unless
* you want a very short or very long timeout. If INT_MAX is passed for
* the timeout, no timeout will be set and the call will block forever.
* you want a very short or very long timeout. If #DBUS_TIMEOUT_INFINITE is
* passed for the timeout, no timeout will be set and the call will block
* forever.
*
* @warning if the connection is disconnected or you try to send Unix
* file descriptors on a connection that does not support them, the
@ -3375,7 +3328,9 @@ reply_handler_timeout (void *data)
* object, or #NULL if connection is disconnected or when you try to
* send Unix file descriptors on a connection that does not support
* them.
* @param timeout_milliseconds timeout in milliseconds, -1 for default or INT_MAX for no timeout
* @param timeout_milliseconds timeout in milliseconds, -1 (or
* #DBUS_TIMEOUT_USE_DEFAULT) for default or #DBUS_TIMEOUT_INFINITE for no
* timeout
* @returns #FALSE if no memory, #TRUE otherwise.
*
*/
@ -3508,7 +3463,9 @@ dbus_connection_send_with_reply (DBusConnection *connection,
*
* @param connection the connection
* @param message the message to send
* @param timeout_milliseconds timeout in milliseconds, -1 for default or INT_MAX for no timeout.
* @param timeout_milliseconds timeout in milliseconds, -1 (or
* #DBUS_TIMEOUT_USE_DEFAULT) for default or #DBUS_TIMEOUT_INFINITE for no
* timeout
* @param error return location for error message
* @returns the message that is the reply or #NULL with an error code if the
* function fails.

View file

@ -1468,14 +1468,20 @@ void
_dbus_header_byteswap (DBusHeader *header,
int new_order)
{
unsigned char byte_order;
if (header->byte_order == new_order)
return;
byte_order = _dbus_string_get_byte (&header->data, BYTE_ORDER_OFFSET);
_dbus_assert (header->byte_order == byte_order);
_dbus_marshal_byteswap (&_dbus_header_signature_str,
0, header->byte_order,
new_order,
&header->data, 0);
_dbus_string_set_byte (&header->data, BYTE_ORDER_OFFSET, new_order);
header->byte_order = new_order;
}

View file

@ -4680,7 +4680,7 @@ dbus_message_demarshal_bytes_needed(const char *buf,
if (validity == DBUS_VALID)
{
_dbus_assert(have_message);
_dbus_assert (have_message || (header_len + body_len) > len);
return header_len + body_len;
}
else

View file

@ -84,7 +84,9 @@ static dbus_int32_t notify_user_data_slot = -1;
* Creates a new pending reply object.
*
* @param connection connection where reply will arrive
* @param timeout_milliseconds length of timeout, -1 for default, INT_MAX for no timeout
* @param timeout_milliseconds length of timeout, -1 (or
* #DBUS_TIMEOUT_USE_DEFAULT) for default,
* #DBUS_TIMEOUT_INFINITE for no timeout
* @param timeout_handler timeout handler, takes pending call as data
* @returns a new #DBusPendingCall or #NULL if no memory.
*/
@ -112,7 +114,7 @@ _dbus_pending_call_new_unlocked (DBusConnection *connection,
return NULL;
}
if (timeout_milliseconds != _DBUS_INT_MAX)
if (timeout_milliseconds != DBUS_TIMEOUT_INFINITE)
{
timeout = _dbus_timeout_new (timeout_milliseconds,
timeout_handler,
@ -515,6 +517,26 @@ _dbus_pending_call_set_data_unlocked (DBusPendingCall *pending,
* @{
*/
/**
* @def DBUS_TIMEOUT_INFINITE
*
* An integer constant representing an infinite timeout. This has the
* numeric value 0x7fffffff (the largest 32-bit signed integer).
*
* For source compatibility with D-Bus versions earlier than 1.4.12, use
* 0x7fffffff, or INT32_MAX (assuming your platform has it).
*/
/**
* @def DBUS_TIMEOUT_USE_DEFAULT
*
* An integer constant representing a request to use the default timeout.
* This has numeric value -1.
*
* For source compatibility with D-Bus versions earlier than 1.4.12, use a
* literal -1.
*/
/**
* @typedef DBusPendingCall
*

View file

@ -38,6 +38,9 @@ DBUS_BEGIN_DECLS
* @{
*/
#define DBUS_TIMEOUT_INFINITE ((int) 0x7fffffff)
#define DBUS_TIMEOUT_USE_DEFAULT (-1)
DBUS_EXPORT
DBusPendingCall* dbus_pending_call_ref (DBusPendingCall *pending);
DBUS_EXPORT

5
test/.gitignore vendored
View file

@ -15,6 +15,7 @@ unbase64
*.gcov
break-loader
spawn-test
test-corrupt
test-exit
test-segfault
test-service
@ -23,3 +24,7 @@ decode-gcov
shell-test
test-shell-service
test-names
test-loopback
test-relay
test-dbus-daemon
test-marshal

View file

@ -52,7 +52,8 @@ test_segfault_SOURCES = \
test_sleep_forever_SOURCES = \
test-sleep-forever.c
# When any programs are not linked to libdbus-internal, fix this.
# This assumes that most tests will be linked to libdbus-internal;
# tests linked to only the public libdbus have their own CPPFLAGS.
AM_CPPFLAGS=-DDBUS_STATIC_BUILD
TEST_LIBS=$(top_builddir)/dbus/libdbus-internal.la $(DBUS_TEST_LIBS)
@ -69,7 +70,89 @@ shell_test_LDFLAGS=@R_DYNAMIC_LDFLAG@
spawn_test_LDADD=$(TEST_LIBS)
spawn_test_LDFLAGS=@R_DYNAMIC_LDFLAG@
EXTRA_DIST=
EXTRA_DIST = dbus-test-runner
testexecdir = $(libdir)/dbus-1.0/test
testexec_PROGRAMS =
installable_tests = \
test-corrupt \
test-dbus-daemon \
test-loopback \
test-marshal \
test-relay \
$(NULL)
installcheck_tests =
installcheck_environment = \
DBUS_TEST_DAEMON=$(DESTDIR)$(DBUS_DAEMONDIR)/dbus-daemon$(EXEEXT) \
DBUS_TEST_SYSCONFDIR=$(DESTDIR)$(sysconfdir)
TESTS_ENVIRONMENT = \
DBUS_TEST_DAEMON=@abs_top_builddir@/bus/dbus-daemon$(EXEEXT) \
DBUS_TEST_DATA=@abs_top_builddir@/test/data
test_corrupt_SOURCES = corrupt.c
test_corrupt_CPPFLAGS = $(GLIB_CFLAGS) $(DBUS_GLIB_CFLAGS)
test_corrupt_LDFLAGS = @R_DYNAMIC_LDFLAG@
test_corrupt_LDADD = $(top_builddir)/dbus/libdbus-1.la \
$(GLIB_LIBS) \
$(DBUS_GLIB_LIBS)
test_loopback_SOURCES = loopback.c
test_loopback_CPPFLAGS = $(GLIB_CFLAGS) $(DBUS_GLIB_CFLAGS)
test_loopback_LDFLAGS = @R_DYNAMIC_LDFLAG@
test_loopback_LDADD = $(top_builddir)/dbus/libdbus-1.la \
$(GLIB_LIBS) \
$(DBUS_GLIB_LIBS)
test_relay_SOURCES = relay.c
test_relay_CPPFLAGS = $(GLIB_CFLAGS) $(DBUS_GLIB_CFLAGS)
test_relay_LDFLAGS = @R_DYNAMIC_LDFLAG@
test_relay_LDADD = $(top_builddir)/dbus/libdbus-1.la \
$(GLIB_LIBS) \
$(DBUS_GLIB_LIBS)
test_dbus_daemon_SOURCES = dbus-daemon.c
test_dbus_daemon_CPPFLAGS = $(GLIB_CFLAGS) $(DBUS_GLIB_CFLAGS)
test_dbus_daemon_LDFLAGS = @R_DYNAMIC_LDFLAG@
test_dbus_daemon_LDADD = $(top_builddir)/dbus/libdbus-1.la \
$(GLIB_LIBS) \
$(DBUS_GLIB_LIBS)
test_marshal_SOURCES = marshal.c
test_marshal_CPPFLAGS = $(GLIB_CFLAGS) $(DBUS_GLIB_CFLAGS)
test_marshal_LDFLAGS = @R_DYNAMIC_LDFLAG@
test_marshal_LDADD = $(top_builddir)/dbus/libdbus-1.la \
$(GLIB_LIBS) \
$(DBUS_GLIB_LIBS)
if DBUS_ENABLE_MODULAR_TESTS
TESTS += $(installable_tests)
installcheck_tests += $(installable_tests)
if DBUS_ENABLE_INSTALLED_TESTS
testexec_PROGRAMS += $(installable_tests)
else !DBUS_ENABLE_INSTALLED_TESTS
testexec_PROGRAMS += $(installable_tests)
endif !DBUS_ENABLE_INSTALLED_TESTS
endif DBUS_ENABLE_MODULAR_TESTS
# If we're installing the tests into a DESTDIR we can't run them
# again using the installed copy, because we don't know how to
# do a portable equivalent of setting LD_LIBRARY_PATH.
installcheck-local:
$(MAKE) check-TESTS TESTS='$$(installcheck_tests)' \
TESTS_ENVIRONMENT='$$(installcheck_environment)'
if DBUS_ENABLE_INSTALLED_TESTS
test -n "$(DESTDIR)" || \
$(installcheck_environment) \
$(srcdir)/dbus-test-runner \
$(testexecdir) \
$(testexec_PROGRAMS)
endif DBUS_ENABLE_INSTALLED_TESTS
## keep these in creation order, i.e. uppermost dirs first
TESTDIRS= \

362
test/corrupt.c Normal file
View file

@ -0,0 +1,362 @@
/* Regression test for being disconnected by a corrupt message (fd.o #15578)
*
* Author: Simon McVittie <simon.mcvittie@collabora.co.uk>
* Copyright © 2010-2011 Nokia Corporation
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation files
* (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge,
* publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include <config.h>
#include <glib.h>
#include <gio/gio.h>
#include <dbus/dbus.h>
#include <dbus/dbus-glib-lowlevel.h>
typedef struct {
DBusError e;
DBusServer *server;
DBusConnection *server_conn;
/* queue of DBusMessage */
GQueue client_messages;
DBusConnection *client_conn;
} Fixture;
static void
assert_no_error (const DBusError *e)
{
if (G_UNLIKELY (dbus_error_is_set (e)))
g_error ("expected success but got error: %s: %s", e->name, e->message);
}
static DBusHandlerResult
client_message_cb (DBusConnection *client_conn,
DBusMessage *message,
void *data)
{
Fixture *f = data;
g_assert (client_conn == f->client_conn);
g_queue_push_tail (&f->client_messages, dbus_message_ref (message));
return DBUS_HANDLER_RESULT_HANDLED;
}
static void
new_conn_cb (DBusServer *server,
DBusConnection *server_conn,
void *data)
{
Fixture *f = data;
g_assert (f->server_conn == NULL);
f->server_conn = dbus_connection_ref (server_conn);
dbus_connection_setup_with_g_main (server_conn, NULL);
}
static void
setup (Fixture *f,
gconstpointer addr)
{
dbus_error_init (&f->e);
g_queue_init (&f->client_messages);
f->server = dbus_server_listen (addr, &f->e);
assert_no_error (&f->e);
g_assert (f->server != NULL);
dbus_server_set_new_connection_function (f->server,
new_conn_cb, f, NULL);
dbus_server_setup_with_g_main (f->server, NULL);
}
static void
test_connect (Fixture *f,
gconstpointer addr G_GNUC_UNUSED)
{
dbus_bool_t have_mem;
g_assert (f->server_conn == NULL);
f->client_conn = dbus_connection_open_private (
dbus_server_get_address (f->server), &f->e);
assert_no_error (&f->e);
g_assert (f->client_conn != NULL);
dbus_connection_setup_with_g_main (f->client_conn, NULL);
while (f->server_conn == NULL)
{
g_print (".");
g_main_context_iteration (NULL, TRUE);
}
have_mem = dbus_connection_add_filter (f->client_conn,
client_message_cb, f, NULL);
g_assert (have_mem);
}
static void
test_message (Fixture *f,
gconstpointer addr)
{
dbus_bool_t have_mem;
dbus_uint32_t serial;
DBusMessage *outgoing, *incoming;
test_connect (f, addr);
outgoing = dbus_message_new_signal ("/com/example/Hello",
"com.example.Hello", "Greeting");
g_assert (outgoing != NULL);
have_mem = dbus_connection_send (f->server_conn, outgoing, &serial);
g_assert (have_mem);
g_assert (serial != 0);
while (g_queue_is_empty (&f->client_messages))
{
g_print (".");
g_main_context_iteration (NULL, TRUE);
}
g_assert_cmpuint (g_queue_get_length (&f->client_messages), ==, 1);
incoming = g_queue_pop_head (&f->client_messages);
g_assert (!dbus_message_contains_unix_fds (incoming));
g_assert_cmpstr (dbus_message_get_destination (incoming), ==, NULL);
g_assert_cmpstr (dbus_message_get_error_name (incoming), ==, NULL);
g_assert_cmpstr (dbus_message_get_interface (incoming), ==,
"com.example.Hello");
g_assert_cmpstr (dbus_message_get_member (incoming), ==, "Greeting");
g_assert_cmpstr (dbus_message_get_sender (incoming), ==, NULL);
g_assert_cmpstr (dbus_message_get_signature (incoming), ==, "");
g_assert_cmpstr (dbus_message_get_path (incoming), ==, "/com/example/Hello");
g_assert_cmpuint (dbus_message_get_serial (incoming), ==, serial);
dbus_message_unref (incoming);
dbus_message_unref (outgoing);
}
/* Enough bytes for it to be obvious that this connection is broken */
#define CORRUPT_LEN 1024
/* All-zero is not a valid D-Bus message header - for a start, this is
* protocol version 1, not 0 */
static const gchar not_a_dbus_message[CORRUPT_LEN] = { 0 };
static void
test_corrupt (Fixture *f,
gconstpointer addr)
{
GSocket *socket;
GError *gerror = NULL;
int fd;
gssize len, total_sent;
DBusMessage *incoming;
test_message (f, addr);
dbus_connection_flush (f->server_conn);
/* OK, now the connection is working, let's break it! Don't try this
* at home; splicing arbitrary bytes into the middle of the stream is
* specifically documented as not a valid thing to do. Who'd have thought? */
if (!dbus_connection_get_socket (f->server_conn, &fd))
g_error ("failed to steal fd from server connection");
socket = g_socket_new_from_fd (fd, &gerror);
g_assert_no_error (gerror);
g_assert (socket != NULL);
total_sent = 0;
while (total_sent < CORRUPT_LEN)
{
len = g_socket_send_with_blocking (socket,
not_a_dbus_message + total_sent, CORRUPT_LEN - total_sent,
TRUE, NULL, &gerror);
g_assert_no_error (gerror);
g_assert (len >= 0);
total_sent += len;
}
/* Now spin on the client connection: the server just sent it complete
* rubbish, so it should disconnect */
while (g_queue_is_empty (&f->client_messages))
{
g_print (".");
g_main_context_iteration (NULL, TRUE);
}
incoming = g_queue_pop_head (&f->client_messages);
g_assert (!dbus_message_contains_unix_fds (incoming));
g_assert_cmpstr (dbus_message_get_destination (incoming), ==, NULL);
g_assert_cmpstr (dbus_message_get_error_name (incoming), ==, NULL);
g_assert_cmpstr (dbus_message_get_interface (incoming), ==,
"org.freedesktop.DBus.Local");
g_assert_cmpstr (dbus_message_get_member (incoming), ==, "Disconnected");
g_assert_cmpstr (dbus_message_get_sender (incoming), ==, NULL);
g_assert_cmpstr (dbus_message_get_signature (incoming), ==, "");
g_assert_cmpstr (dbus_message_get_path (incoming), ==,
"/org/freedesktop/DBus/Local");
dbus_message_unref (incoming);
}
static void
test_byte_order (Fixture *f,
gconstpointer addr)
{
GSocket *socket;
GError *gerror = NULL;
int fd;
char *blob;
const gchar *arg = not_a_dbus_message;
const gchar * const *args = &arg;
int blob_len, len, total_sent;
DBusMessage *message;
dbus_bool_t mem;
test_message (f, addr);
message = dbus_message_new_signal ("/", "a.b", "c");
g_assert (message != NULL);
/* Append 0xFF bytes, so that the length of the body when byte-swapped
* is 0xFF000000, which is invalid */
mem = dbus_message_append_args (message,
DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &args, 0xFF,
DBUS_TYPE_INVALID);
g_assert (mem);
mem = dbus_message_marshal (message, &blob, &blob_len);
g_assert (mem);
g_assert_cmpuint (blob_len, >, 0xFF);
g_assert (blob != NULL);
dbus_message_unref (message);
/* Break the message by changing its claimed byte order, without actually
* byteswapping anything. We happen to know that byte order is the first
* byte. */
if (blob[0] == 'B')
blob[0] = 'l';
else
blob[0] = 'B';
/* OK, now the connection is working, let's break it */
dbus_connection_flush (f->server_conn);
if (!dbus_connection_get_socket (f->server_conn, &fd))
g_error ("failed to steal fd from server connection");
socket = g_socket_new_from_fd (fd, &gerror);
g_assert_no_error (gerror);
g_assert (socket != NULL);
total_sent = 0;
while (total_sent < blob_len)
{
len = g_socket_send_with_blocking (socket, blob + total_sent,
blob_len - total_sent, TRUE, NULL, &gerror);
g_assert_no_error (gerror);
g_assert (len >= 0);
total_sent += len;
}
dbus_free (blob);
/* Now spin on the client connection: the server just sent it a faulty
* message, so it should disconnect */
while (g_queue_is_empty (&f->client_messages))
{
g_print (".");
g_main_context_iteration (NULL, TRUE);
}
message = g_queue_pop_head (&f->client_messages);
g_assert (!dbus_message_contains_unix_fds (message));
g_assert_cmpstr (dbus_message_get_destination (message), ==, NULL);
g_assert_cmpstr (dbus_message_get_error_name (message), ==, NULL);
g_assert_cmpstr (dbus_message_get_interface (message), ==,
"org.freedesktop.DBus.Local");
g_assert_cmpstr (dbus_message_get_member (message), ==, "Disconnected");
g_assert_cmpstr (dbus_message_get_sender (message), ==, NULL);
g_assert_cmpstr (dbus_message_get_signature (message), ==, "");
g_assert_cmpstr (dbus_message_get_path (message), ==,
"/org/freedesktop/DBus/Local");
dbus_message_unref (message);
}
static void
teardown (Fixture *f,
gconstpointer addr G_GNUC_UNUSED)
{
if (f->client_conn != NULL)
{
dbus_connection_close (f->client_conn);
dbus_connection_unref (f->client_conn);
f->client_conn = NULL;
}
if (f->server_conn != NULL)
{
dbus_connection_close (f->server_conn);
dbus_connection_unref (f->server_conn);
f->server_conn = NULL;
}
if (f->server != NULL)
{
dbus_server_disconnect (f->server);
dbus_server_unref (f->server);
f->server = NULL;
}
}
int
main (int argc,
char **argv)
{
g_test_init (&argc, &argv, NULL);
g_type_init ();
g_test_add ("/corrupt/tcp", Fixture, "tcp:host=127.0.0.1", setup,
test_corrupt, teardown);
#ifdef DBUS_UNIX
g_test_add ("/corrupt/unix", Fixture, "unix:tmpdir=/tmp", setup,
test_corrupt, teardown);
#endif
g_test_add ("/corrupt/byte-order/tcp", Fixture, "tcp:host=127.0.0.1", setup,
test_byte_order, teardown);
return g_test_run ();
}

326
test/dbus-daemon.c Normal file
View file

@ -0,0 +1,326 @@
/* Integration tests for the dbus-daemon
*
* Author: Simon McVittie <simon.mcvittie@collabora.co.uk>
* Copyright © 2010-2011 Nokia Corporation
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation files
* (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge,
* publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include <config.h>
#include <glib.h>
#include <dbus/dbus.h>
#include <dbus/dbus-glib-lowlevel.h>
#include <string.h>
#ifdef DBUS_WIN
# include <windows.h>
#else
# include <signal.h>
# include <unistd.h>
#endif
typedef struct {
DBusError e;
GError *ge;
gint daemon_pid;
DBusConnection *left_conn;
DBusConnection *right_conn;
gboolean right_conn_echo;
} Fixture;
#define assert_no_error(e) _assert_no_error (e, __FILE__, __LINE__)
static void
_assert_no_error (const DBusError *e,
const char *file,
int line)
{
if (G_UNLIKELY (dbus_error_is_set (e)))
g_error ("%s:%d: expected success but got error: %s: %s",
file, line, e->name, e->message);
}
static gchar *
spawn_dbus_daemon (gchar *binary,
gchar *configuration,
gint *daemon_pid)
{
GError *error = NULL;
GString *address;
gint address_fd;
gchar *argv[] = {
binary,
configuration,
"--nofork",
"--print-address=1", /* stdout */
NULL
};
g_spawn_async_with_pipes (NULL, /* working directory */
argv,
NULL, /* envp */
G_SPAWN_DO_NOT_REAP_CHILD | G_SPAWN_SEARCH_PATH,
NULL, /* child_setup */
NULL, /* user data */
daemon_pid,
NULL, /* child's stdin = /dev/null */
&address_fd,
NULL, /* child's stderr = our stderr */
&error);
g_assert_no_error (error);
address = g_string_new (NULL);
/* polling until the dbus-daemon writes out its address is a bit stupid,
* but at least it's simple, unlike dbus-launch... in principle we could
* use select() here, but life's too short */
while (1)
{
gssize bytes;
gchar buf[4096];
gchar *newline;
bytes = read (address_fd, buf, sizeof (buf));
if (bytes > 0)
g_string_append_len (address, buf, bytes);
newline = strchr (address->str, '\n');
if (newline != NULL)
{
g_string_truncate (address, newline - address->str);
break;
}
g_usleep (G_USEC_PER_SEC / 10);
}
return g_string_free (address, FALSE);
}
static DBusConnection *
connect_to_bus (const gchar *address)
{
DBusConnection *conn;
DBusError error = DBUS_ERROR_INIT;
dbus_bool_t ok;
conn = dbus_connection_open_private (address, &error);
assert_no_error (&error);
g_assert (conn != NULL);
ok = dbus_bus_register (conn, &error);
assert_no_error (&error);
g_assert (ok);
g_assert (dbus_bus_get_unique_name (conn) != NULL);
dbus_connection_setup_with_g_main (conn, NULL);
return conn;
}
static DBusHandlerResult
echo_filter (DBusConnection *connection,
DBusMessage *message,
void *user_data)
{
DBusMessage *reply;
DBusError error = DBUS_ERROR_INIT;
int *sleep_ms = user_data;
if (dbus_message_get_type (message) != DBUS_MESSAGE_TYPE_METHOD_CALL)
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
reply = dbus_message_new_method_return (message);
if (reply == NULL)
g_error ("OOM");
if (!dbus_connection_send (connection, reply, NULL))
g_error ("OOM");
dbus_message_unref (reply);
return DBUS_HANDLER_RESULT_HANDLED;
}
static void
setup (Fixture *f,
gconstpointer context G_GNUC_UNUSED)
{
gchar *dbus_daemon;
gchar *config;
gchar *address;
f->ge = NULL;
dbus_error_init (&f->e);
dbus_daemon = g_strdup (g_getenv ("DBUS_TEST_DAEMON"));
if (dbus_daemon == NULL)
dbus_daemon = g_strdup ("dbus-daemon");
if (g_getenv ("DBUS_TEST_SYSCONFDIR") != NULL)
{
config = g_strdup_printf ("--config-file=%s/dbus-1/session.conf",
g_getenv ("DBUS_TEST_SYSCONFDIR"));
}
else if (g_getenv ("DBUS_TEST_DATA") != NULL)
{
config = g_strdup_printf (
"--config-file=%s/valid-config-files/session.conf",
g_getenv ("DBUS_TEST_DATA"));
}
else
{
config = g_strdup ("--session");
}
address = spawn_dbus_daemon (dbus_daemon, config, &f->daemon_pid);
g_free (dbus_daemon);
g_free (config);
f->left_conn = connect_to_bus (address);
f->right_conn = connect_to_bus (address);
g_free (address);
}
static void
add_echo_filter (Fixture *f)
{
if (!dbus_connection_add_filter (f->right_conn, echo_filter, NULL, NULL))
g_error ("OOM");
f->right_conn_echo = TRUE;
}
static void
pc_count (DBusPendingCall *pc,
void *data)
{
guint *received_p = data;
(*received_p)++;
}
static void
test_echo (Fixture *f,
gconstpointer context G_GNUC_UNUSED)
{
guint count = 2000;
guint sent;
guint received = 0;
double elapsed;
if (g_test_perf ())
count = 100000;
add_echo_filter (f);
g_test_timer_start ();
for (sent = 0; sent < count; sent++)
{
DBusMessage *m = dbus_message_new_method_call (
dbus_bus_get_unique_name (f->right_conn), "/",
"com.example", "Spam");
DBusPendingCall *pc;
if (m == NULL)
g_error ("OOM");
if (!dbus_connection_send_with_reply (f->left_conn, m, &pc,
DBUS_TIMEOUT_INFINITE) ||
pc == NULL)
g_error ("OOM");
if (dbus_pending_call_get_completed (pc))
pc_count (pc, &received);
else if (!dbus_pending_call_set_notify (pc, pc_count, &received,
NULL))
g_error ("OOM");
dbus_pending_call_unref (pc);
dbus_message_unref (m);
}
while (received < count)
g_main_context_iteration (NULL, TRUE);
elapsed = g_test_timer_elapsed ();
g_test_maximized_result (count / elapsed, "%u messages / %f seconds",
count, elapsed);
}
static void
teardown (Fixture *f,
gconstpointer context G_GNUC_UNUSED)
{
dbus_error_free (&f->e);
g_clear_error (&f->ge);
if (f->left_conn != NULL)
{
dbus_connection_close (f->left_conn);
dbus_connection_unref (f->left_conn);
f->left_conn = NULL;
}
if (f->right_conn != NULL)
{
if (f->right_conn_echo)
{
dbus_connection_remove_filter (f->right_conn, echo_filter, NULL);
f->right_conn_echo = FALSE;
}
dbus_connection_close (f->right_conn);
dbus_connection_unref (f->right_conn);
f->right_conn = NULL;
}
#ifdef DBUS_WIN
TerminateProcess (f->daemon_pid, 1);
#else
kill (f->daemon_pid, SIGTERM);
#endif
g_spawn_close_pid (f->daemon_pid);
}
int
main (int argc,
char **argv)
{
g_test_init (&argc, &argv, NULL);
g_test_bug_base ("https://bugs.freedesktop.org/show_bug.cgi?id=");
g_test_add ("/echo/session", Fixture, NULL, setup, test_echo, teardown);
return g_test_run ();
}

43
test/dbus-test-runner Executable file
View file

@ -0,0 +1,43 @@
#!/bin/sh
set -e
dir="$1"
shift
if ! test -d "$dir"; then
echo "Usage: dbus-test-runner directory [executable...]"
exit 0
fi
passed=0
failed=0
skipped=0
for prog in "$@"; do
e=0
"$dir/$prog" || e=$?
case $e in
(0)
echo "PASS: $prog"
passed=`expr $passed + 1`
;;
(77)
echo "SKIP: $prog"
skipped=`expr $skipped + 1`
;;
(*)
echo "FAIL: $prog"
failed=`expr $failed + 1`
;;
esac
done
if test $failed = 0; then
# avoid saying "FAIL", to make it easy to grep results!
echo "PASSED $passed / SKIPPED $skipped"
exit 0
else
echo "PASSED $passed / FAILED $failed / SKIPPED $skipped"
exit 1
fi

211
test/loopback.c Normal file
View file

@ -0,0 +1,211 @@
/* Simple sanity-check for loopback through TCP and Unix sockets.
*
* Author: Simon McVittie <simon.mcvittie@collabora.co.uk>
* Copyright © 2010-2011 Nokia Corporation
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation files
* (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge,
* publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include <config.h>
#include <glib.h>
#include <dbus/dbus.h>
#include <dbus/dbus-glib-lowlevel.h>
typedef struct {
DBusError e;
DBusServer *server;
DBusConnection *server_conn;
/* queue of DBusMessage */
GQueue server_messages;
DBusConnection *client_conn;
} Fixture;
static void
assert_no_error (const DBusError *e)
{
if (G_UNLIKELY (dbus_error_is_set (e)))
g_error ("expected success but got error: %s: %s", e->name, e->message);
}
static DBusHandlerResult
server_message_cb (DBusConnection *server_conn,
DBusMessage *message,
void *data)
{
Fixture *f = data;
g_assert (server_conn == f->server_conn);
g_queue_push_tail (&f->server_messages, dbus_message_ref (message));
return DBUS_HANDLER_RESULT_HANDLED;
}
static void
new_conn_cb (DBusServer *server,
DBusConnection *server_conn,
void *data)
{
Fixture *f = data;
dbus_bool_t have_mem;
g_assert (f->server_conn == NULL);
f->server_conn = dbus_connection_ref (server_conn);
dbus_connection_setup_with_g_main (server_conn, NULL);
have_mem = dbus_connection_add_filter (server_conn,
server_message_cb, f, NULL);
g_assert (have_mem);
}
static void
setup (Fixture *f,
gconstpointer addr)
{
dbus_error_init (&f->e);
g_queue_init (&f->server_messages);
f->server = dbus_server_listen (addr, &f->e);
assert_no_error (&f->e);
g_assert (f->server != NULL);
dbus_server_set_new_connection_function (f->server,
new_conn_cb, f, NULL);
dbus_server_setup_with_g_main (f->server, NULL);
}
static void
test_connect (Fixture *f,
gconstpointer addr G_GNUC_UNUSED)
{
g_assert (f->server_conn == NULL);
f->client_conn = dbus_connection_open_private (
dbus_server_get_address (f->server), &f->e);
assert_no_error (&f->e);
g_assert (f->client_conn != NULL);
dbus_connection_setup_with_g_main (f->client_conn, NULL);
while (f->server_conn == NULL)
{
g_print (".");
g_main_context_iteration (NULL, TRUE);
}
}
static void
test_message (Fixture *f,
gconstpointer addr)
{
dbus_bool_t have_mem;
dbus_uint32_t serial;
DBusMessage *outgoing, *incoming;
test_connect (f, addr);
outgoing = dbus_message_new_signal ("/com/example/Hello",
"com.example.Hello", "Greeting");
g_assert (outgoing != NULL);
have_mem = dbus_connection_send (f->client_conn, outgoing, &serial);
g_assert (have_mem);
g_assert (serial != 0);
while (g_queue_is_empty (&f->server_messages))
{
g_print (".");
g_main_context_iteration (NULL, TRUE);
}
g_assert_cmpuint (g_queue_get_length (&f->server_messages), ==, 1);
incoming = g_queue_pop_head (&f->server_messages);
g_assert (!dbus_message_contains_unix_fds (incoming));
g_assert_cmpstr (dbus_message_get_destination (incoming), ==, NULL);
g_assert_cmpstr (dbus_message_get_error_name (incoming), ==, NULL);
g_assert_cmpstr (dbus_message_get_interface (incoming), ==,
"com.example.Hello");
g_assert_cmpstr (dbus_message_get_member (incoming), ==, "Greeting");
g_assert_cmpstr (dbus_message_get_sender (incoming), ==, NULL);
g_assert_cmpstr (dbus_message_get_signature (incoming), ==, "");
g_assert_cmpstr (dbus_message_get_path (incoming), ==, "/com/example/Hello");
g_assert_cmpuint (dbus_message_get_serial (incoming), ==, serial);
dbus_message_unref (incoming);
dbus_message_unref (outgoing);
}
static void
teardown (Fixture *f,
gconstpointer addr G_GNUC_UNUSED)
{
if (f->client_conn != NULL)
{
dbus_connection_close (f->client_conn);
dbus_connection_unref (f->client_conn);
f->client_conn = NULL;
}
if (f->server_conn != NULL)
{
dbus_connection_close (f->server_conn);
dbus_connection_unref (f->server_conn);
f->server_conn = NULL;
}
if (f->server != NULL)
{
dbus_server_disconnect (f->server);
dbus_server_unref (f->server);
f->server = NULL;
}
}
int
main (int argc,
char **argv)
{
g_test_init (&argc, &argv, NULL);
g_test_add ("/connect/tcp", Fixture, "tcp:host=127.0.0.1", setup,
test_connect, teardown);
g_test_add ("/message/tcp", Fixture, "tcp:host=127.0.0.1", setup,
test_message, teardown);
g_test_add ("/connect/nonce-tcp", Fixture, "nonce-tcp:host=127.0.0.1", setup,
test_connect, teardown);
g_test_add ("/message/nonce-tcp", Fixture, "nonce-tcp:host=127.0.0.1", setup,
test_message, teardown);
#ifdef DBUS_UNIX
g_test_add ("/connect/unix", Fixture, "unix:tmpdir=/tmp", setup,
test_connect, teardown);
g_test_add ("/message/unix", Fixture, "unix:tmpdir=/tmp", setup,
test_message, teardown);
#endif
return g_test_run ();
}

258
test/marshal.c Normal file
View file

@ -0,0 +1,258 @@
/* Simple sanity-check for D-Bus message serialization.
*
* Author: Simon McVittie <simon.mcvittie@collabora.co.uk>
* Copyright © 2010-2011 Nokia Corporation
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation files
* (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge,
* publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include <config.h>
#include <glib.h>
#include <dbus/dbus.h>
#include <dbus/dbus-glib-lowlevel.h>
typedef struct {
DBusError e;
} Fixture;
static void
assert_no_error (const DBusError *e)
{
if (G_UNLIKELY (dbus_error_is_set (e)))
g_error ("expected success but got error: %s: %s", e->name, e->message);
}
static void
setup (Fixture *f,
gconstpointer arg G_GNUC_UNUSED)
{
dbus_error_init (&f->e);
}
/* this is meant to be obviously correct, not efficient! */
static guint32
get_uint32 (const gchar *blob,
gsize offset,
char endian)
{
if (endian == 'l')
{
return
blob[offset] |
(blob[offset + 1] << 8) |
(blob[offset + 2] << 16) |
(blob[offset + 3] << 24);
}
else if (endian == 'B')
{
return
(blob[offset] << 24) |
(blob[offset + 1] << 16) |
(blob[offset + 2] << 8) |
blob[offset + 3];
}
else
{
g_assert_not_reached ();
}
}
#define BLOB_LENGTH (sizeof (le_blob) - 1)
#define OFFSET_BODY_LENGTH (4)
#define OFFSET_SERIAL (8)
const gchar le_blob[] =
/* byte 0 */
/* yyyyuu fixed headers */
"l" /* little-endian */
"\2" /* reply (which is the simplest message) */
"\2" /* no auto-starting */
"\1" /* D-Bus version = 1 */
/* byte 4 */
"\4\0\0\0" /* bytes in body = 4 */
/* byte 8 */
"\x78\x56\x34\x12" /* serial number = 0x12345678 */
/* byte 12 */
/* a(uv) variable headers start here */
"\x0f\0\0\0" /* bytes in array of variable headers = 15 */
/* pad to 8-byte boundary = nothing */
/* byte 16 */
"\5" /* in reply to: */
"\1u\0" /* variant signature = u */
/* pad to 4-byte boundary = nothing */
"\x12\xef\xcd\xab" /* 0xabcdef12 */
/* pad to 8-byte boundary = nothing */
/* byte 24 */
"\x08" /* signature: */
"\1g\0" /* variant signature = g */
"\1u\0" /* 1 byte, u, NUL (no alignment needed) */
"\0" /* pad to 8-byte boundary for body */
/* body; byte 32 */
"\xef\xbe\xad\xde" /* 0xdeadbeef */
;
const gchar be_blob[] =
/* byte 0 */
/* yyyyuu fixed headers */
"B" /* big-endian */
"\2" /* reply (which is the simplest message) */
"\2" /* no auto-starting */
"\1" /* D-Bus version = 1 */
/* byte 4 */
"\0\0\0\4" /* bytes in body = 4 */
/* byte 8 */
"\x12\x34\x56\x78" /* serial number = 0x12345678 */
/* byte 12 */
/* a(uv) variable headers start here */
"\0\0\0\x0f" /* bytes in array of variable headers = 15 */
/* pad to 8-byte boundary = nothing */
/* byte 16 */
"\5" /* in reply to: */
"\1u\0" /* variant signature = u */
/* pad to 4-byte boundary = nothing */
"\xab\xcd\xef\x12" /* 0xabcdef12 */
/* pad to 8-byte boundary = nothing */
/* byte 24 */
"\x08" /* signature: */
"\1g\0" /* variant signature = g */
"\1u\0" /* 1 byte, u, NUL (no alignment needed) */
"\0" /* pad to 8-byte boundary for body */
/* body; byte 32 */
"\xde\xad\xbe\xef" /* 0xdeadbeef */
;
static void
test_endian (Fixture *f,
gconstpointer arg)
{
const gchar *blob = arg;
const gchar *native_blob;
char *output;
DBusMessage *m;
int len;
dbus_uint32_t u;
dbus_bool_t ok;
g_assert_cmpuint ((guint) sizeof (le_blob), ==, (guint) sizeof (be_blob));
g_assert_cmpuint (get_uint32 (blob, OFFSET_BODY_LENGTH, blob[0]), ==, 4);
g_assert_cmpuint (get_uint32 (blob, OFFSET_SERIAL, blob[0]), ==,
0x12345678u);
len = dbus_message_demarshal_bytes_needed (blob, sizeof (le_blob));
/* everything in the string except the implicit "\0" at the end is part of
* the message */
g_assert_cmpint (len, ==, BLOB_LENGTH);
m = dbus_message_demarshal (blob, sizeof (le_blob), &f->e);
assert_no_error (&f->e);
g_assert (m != NULL);
g_assert_cmpuint (dbus_message_get_serial (m), ==, 0x12345678u);
g_assert_cmpuint (dbus_message_get_reply_serial (m), ==, 0xabcdef12u);
g_assert_cmpstr (dbus_message_get_signature (m), ==, "u");
/* Implementation detail: appending to the message results in it being
* byteswapped into compiler byte order, which exposed a bug in libdbus,
* fd.o #38120. (If that changes, this test might not exercise that
* particular bug but will still be valid.) */
u = 0xdecafbadu;
ok = dbus_message_append_args (m,
DBUS_TYPE_UINT32, &u,
DBUS_TYPE_INVALID);
g_assert (ok);
dbus_message_marshal (m, &output, &len);
g_assert (output[0] == 'l' || output[0] == 'B');
/* the single-byte fields are unaffected, even if the endianness was
* swapped */
g_assert_cmpint (output[1], ==, blob[1]);
g_assert_cmpint (output[2], ==, blob[2]);
g_assert_cmpint (output[3], ==, blob[3]);
/* the length and serial are in the new endianness, the length has expanded
* to 8, and the serial is correct */
g_assert_cmpuint (get_uint32 (output, OFFSET_BODY_LENGTH, output[0]), ==, 8);
g_assert_cmpuint (get_uint32 (output, OFFSET_SERIAL, output[0]), ==,
0x12345678u);
/* the second "u" in the signature replaced a padding byte, so only
* the length of the body changed */
g_assert_cmpint (len, ==, BLOB_LENGTH + 4);
}
static void
test_needed (Fixture *f,
gconstpointer arg)
{
const gchar *blob = arg;
/* We need at least 16 bytes to know how long the message is - that's just
* a fact of the D-Bus protocol. */
g_assert_cmpint (
dbus_message_demarshal_bytes_needed (blob, 0), ==, 0);
g_assert_cmpint (
dbus_message_demarshal_bytes_needed (blob, 15), ==, 0);
/* This is enough that we should be able to tell how much we need. */
g_assert_cmpint (
dbus_message_demarshal_bytes_needed (blob, 16), ==, BLOB_LENGTH);
/* The header is 32 bytes long (here), so that's another interesting
* boundary. */
g_assert_cmpint (
dbus_message_demarshal_bytes_needed (blob, 31), ==, BLOB_LENGTH);
g_assert_cmpint (
dbus_message_demarshal_bytes_needed (blob, 32), ==, BLOB_LENGTH);
g_assert_cmpint (
dbus_message_demarshal_bytes_needed (blob, 33), ==, BLOB_LENGTH);
g_assert_cmpint (
dbus_message_demarshal_bytes_needed (blob, BLOB_LENGTH - 1), ==,
BLOB_LENGTH);
g_assert_cmpint (
dbus_message_demarshal_bytes_needed (blob, BLOB_LENGTH), ==,
BLOB_LENGTH);
g_assert_cmpint (
dbus_message_demarshal_bytes_needed (blob, sizeof (be_blob)), ==,
BLOB_LENGTH);
}
static void
teardown (Fixture *f,
gconstpointer arg G_GNUC_UNUSED)
{
dbus_error_free (&f->e);
}
int
main (int argc,
char **argv)
{
g_test_init (&argc, &argv, NULL);
g_test_add ("/demarshal/le", Fixture, le_blob, setup, test_endian, teardown);
g_test_add ("/demarshal/be", Fixture, be_blob, setup, test_endian, teardown);
g_test_add ("/demarshal/needed/le", Fixture, le_blob, setup, test_needed,
teardown);
g_test_add ("/demarshal/needed/be", Fixture, be_blob, setup, test_needed,
teardown);
return g_test_run ();
}

318
test/relay.c Normal file
View file

@ -0,0 +1,318 @@
/* Regression test for passing unmodified messages between connections
*
* Author: Simon McVittie <simon.mcvittie@collabora.co.uk>
* Copyright © 2010-2011 Nokia Corporation
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation files
* (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge,
* publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include <config.h>
#include <glib.h>
#include <dbus/dbus.h>
#include <dbus/dbus-glib-lowlevel.h>
/* This is basically a miniature dbus-daemon. We relay messages from the client
* on the left to the client on the right.
*
* left socket left dispatch right socket right
* client ===========> server --------------> server ===========> client
* conn conn conn conn
*
* In the real dbus-daemon, the client connections would be out-of-process,
* but here we're cheating and doing everything in-process.
*/
typedef struct {
DBusError e;
DBusServer *server;
DBusConnection *left_client_conn;
DBusConnection *left_server_conn;
DBusConnection *right_server_conn;
DBusConnection *right_client_conn;
/* queue of DBusMessage received by right_client_conn */
GQueue messages;
} Fixture;
static void
assert_no_error (const DBusError *e)
{
if (G_UNLIKELY (dbus_error_is_set (e)))
g_error ("expected success but got error: %s: %s", e->name, e->message);
}
static DBusHandlerResult
server_message_cb (DBusConnection *server_conn,
DBusMessage *message,
void *data)
{
Fixture *f = data;
g_assert (server_conn == f->left_server_conn);
g_assert (f->right_server_conn != NULL);
dbus_connection_send (f->right_server_conn, message, NULL);
return DBUS_HANDLER_RESULT_HANDLED;
}
static DBusHandlerResult
right_client_message_cb (DBusConnection *client_conn,
DBusMessage *message,
void *data)
{
Fixture *f = data;
g_assert (client_conn == f->right_client_conn);
g_queue_push_tail (&f->messages, dbus_message_ref (message));
return DBUS_HANDLER_RESULT_HANDLED;
}
static void
new_conn_cb (DBusServer *server,
DBusConnection *server_conn,
void *data)
{
Fixture *f = data;
dbus_bool_t have_mem;
if (f->left_server_conn == NULL)
{
f->left_server_conn = dbus_connection_ref (server_conn);
have_mem = dbus_connection_add_filter (server_conn,
server_message_cb, f, NULL);
g_assert (have_mem);
}
else
{
g_assert (f->right_server_conn == NULL);
f->right_server_conn = dbus_connection_ref (server_conn);
}
dbus_connection_setup_with_g_main (server_conn, NULL);
}
static void
setup (Fixture *f,
gconstpointer data G_GNUC_UNUSED)
{
dbus_error_init (&f->e);
g_queue_init (&f->messages);
f->server = dbus_server_listen ("tcp:host=127.0.0.1", &f->e);
assert_no_error (&f->e);
g_assert (f->server != NULL);
dbus_server_set_new_connection_function (f->server,
new_conn_cb, f, NULL);
dbus_server_setup_with_g_main (f->server, NULL);
}
static void
test_connect (Fixture *f,
gconstpointer data G_GNUC_UNUSED)
{
dbus_bool_t have_mem;
g_assert (f->left_server_conn == NULL);
g_assert (f->right_server_conn == NULL);
f->left_client_conn = dbus_connection_open_private (
dbus_server_get_address (f->server), &f->e);
assert_no_error (&f->e);
g_assert (f->left_client_conn != NULL);
dbus_connection_setup_with_g_main (f->left_client_conn, NULL);
while (f->left_server_conn == NULL)
{
g_print (".");
g_main_context_iteration (NULL, TRUE);
}
f->right_client_conn = dbus_connection_open_private (
dbus_server_get_address (f->server), &f->e);
assert_no_error (&f->e);
g_assert (f->right_client_conn != NULL);
dbus_connection_setup_with_g_main (f->right_client_conn, NULL);
while (f->right_server_conn == NULL)
{
g_print (".");
g_main_context_iteration (NULL, TRUE);
}
have_mem = dbus_connection_add_filter (f->right_client_conn,
right_client_message_cb, f, NULL);
g_assert (have_mem);
}
static dbus_uint32_t
send_one (Fixture *f,
const char *member)
{
dbus_bool_t have_mem;
dbus_uint32_t serial;
DBusMessage *outgoing;
outgoing = dbus_message_new_signal ("/com/example/Hello",
"com.example.Hello", member);
g_assert (outgoing != NULL);
have_mem = dbus_connection_send (f->left_client_conn, outgoing, &serial);
g_assert (have_mem);
g_assert (serial != 0);
dbus_message_unref (outgoing);
return serial;
}
static void
test_relay (Fixture *f,
gconstpointer data)
{
DBusMessage *incoming;
test_connect (f, data);
send_one (f, "First");
send_one (f, "Second");
while (g_queue_get_length (&f->messages) < 2)
{
g_print (".");
g_main_context_iteration (NULL, TRUE);
}
g_assert_cmpuint (g_queue_get_length (&f->messages), ==, 2);
incoming = g_queue_pop_head (&f->messages);
g_assert_cmpstr (dbus_message_get_member (incoming), ==, "First");
dbus_message_unref (incoming);
incoming = g_queue_pop_head (&f->messages);
g_assert_cmpstr (dbus_message_get_member (incoming), ==, "Second");
dbus_message_unref (incoming);
}
/* An arbitrary number of messages */
#define MANY 8192
static void
test_limit (Fixture *f,
gconstpointer data)
{
DBusMessage *incoming;
guint i;
test_connect (f, data);
/* This was an attempt to reproduce fd.o #34393. It didn't work. */
g_test_bug ("34393");
dbus_connection_set_max_received_size (f->left_server_conn, 1);
g_main_context_iteration (NULL, TRUE);
for (i = 0; i < MANY; i++)
{
gchar *buf = g_strdup_printf ("Message%u", i);
send_one (f, buf);
g_free (buf);
}
i = 0;
while (i < MANY)
{
while (g_queue_is_empty (&f->messages))
{
g_main_context_iteration (NULL, TRUE);
}
while ((incoming = g_queue_pop_head (&f->messages)) != NULL)
{
i++;
dbus_message_unref (incoming);
}
}
}
static void
teardown (Fixture *f,
gconstpointer data G_GNUC_UNUSED)
{
if (f->left_client_conn != NULL)
{
dbus_connection_close (f->left_client_conn);
dbus_connection_unref (f->left_client_conn);
f->left_client_conn = NULL;
}
if (f->right_client_conn != NULL)
{
dbus_connection_close (f->right_client_conn);
dbus_connection_unref (f->right_client_conn);
f->right_client_conn = NULL;
}
if (f->left_server_conn != NULL)
{
dbus_connection_close (f->left_server_conn);
dbus_connection_unref (f->left_server_conn);
f->left_server_conn = NULL;
}
if (f->right_server_conn != NULL)
{
dbus_connection_close (f->right_server_conn);
dbus_connection_unref (f->right_server_conn);
f->right_server_conn = NULL;
}
if (f->server != NULL)
{
dbus_server_disconnect (f->server);
dbus_server_unref (f->server);
f->server = NULL;
}
}
int
main (int argc,
char **argv)
{
g_test_init (&argc, &argv, NULL);
g_test_bug_base ("https://bugs.freedesktop.org/show_bug.cgi?id=");
g_test_add ("/connect", Fixture, NULL, setup,
test_connect, teardown);
g_test_add ("/relay", Fixture, NULL, setup,
test_relay, teardown);
g_test_add ("/limit", Fixture, NULL, setup,
test_limit, teardown);
return g_test_run ();
}