From e5c5ef3ad24f6569b616359d72b7b8f825e9e144 Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Fri, 23 Jan 2015 19:32:31 +0000 Subject: [PATCH 09/10] dbus-monitor: add options to log binary data with or without pcap framing --- doc/dbus-monitor.1.xml.in | 19 ++++-- tools/dbus-monitor.c | 155 ++++++++++++++++++++++++++++++++++++++++++++-- tools/tool-common.c | 38 ++++++++++++ tools/tool-common.h | 1 + 4 files changed, 205 insertions(+), 8 deletions(-) diff --git a/doc/dbus-monitor.1.xml.in b/doc/dbus-monitor.1.xml.in index af05e3a..2f807d2 100644 --- a/doc/dbus-monitor.1.xml.in +++ b/doc/dbus-monitor.1.xml.in @@ -22,7 +22,7 @@ dbus-monitor --system --session --address ADDRESS - --profile --monitor + --profile --monitor --pcap --binary watchexpressions @@ -44,11 +44,22 @@ monitor the system or session buses respectively. If neither is specified, dbus-monitor monitors the session bus. -dbus-monitor has two different output modes, the 'classic'-style -monitoring mode and profiling mode. The profiling format is a compact +dbus-monitor has two different text output +modes: the 'classic'-style +monitoring mode, and profiling mode. The profiling format is a compact format with a single line per message and microsecond-resolution timing information. The --profile and --monitor options select the profiling -and monitoring output format respectively. If neither is specified, +and monitoring output format respectively. + +dbus-monitor also has two binary output modes. + The binary mode, selected by --binary, outputs the + entire binary message stream (without the initial authentication handshake). + The PCAP mode, selected by --pcap, adds a + PCAP file header to the beginning of the output, and prepends a PCAP + message header to each message; this produces a binary file that can + be read by, for instance, Wireshark. + +If no mode is specified, dbus-monitor uses the monitoring output format. diff --git a/tools/dbus-monitor.c b/tools/dbus-monitor.c index 23e0c17..4068129 100644 --- a/tools/dbus-monitor.c +++ b/tools/dbus-monitor.c @@ -20,6 +20,9 @@ */ #include + +#include "dbus/dbus-internals.h" /* just for the macros */ + #include #include #include @@ -38,6 +41,15 @@ #define EAVESDROPPING_RULE "eavesdrop=true" +#ifndef STDOUT_FILENO +#define STDOUT_FILENO 1 +#endif + +/* http://www.tcpdump.org/linktypes.html */ +#define LINKTYPE_DBUS 231 +/* what Bustle uses */ +#define LINKTYPE_NULL 0 + #ifdef DBUS_WIN /* gettimeofday is not defined on windows */ @@ -213,10 +225,87 @@ profile_filter_func (DBusConnection *connection, return DBUS_HANDLER_RESULT_HANDLED; } +typedef enum { + BINARY_MODE_NOT, + BINARY_MODE_RAW, + BINARY_MODE_PCAP, + BINARY_MODE_BUSTLE +} BinaryMode; + +static DBusHandlerResult +binary_filter_func (DBusConnection *connection, + DBusMessage *message, + void *user_data) +{ + BinaryMode mode = _DBUS_POINTER_TO_INT (user_data); + char *blob; + int len; + + /* It would be nice if we could do a zero-copy "peek" one day, but libdbus + * is so copy-happy that this isn't really a big deal. + */ + if (!dbus_message_marshal (message, &blob, &len)) + tool_oom ("retrieving message"); + + switch (mode) + { + case BINARY_MODE_PCAP: + case BINARY_MODE_BUSTLE: + { + struct timeval t = { 0, 0 }; + /* seconds, microseconds, bytes captured (possibly truncated), + * original length. + * http://wiki.wireshark.org/Development/LibpcapFileFormat + */ + dbus_uint32_t header[4] = { 0, 0, len, len }; + + /* If this gets padded then we'd need to write it out in pieces */ + _DBUS_STATIC_ASSERT (sizeof (header) == 16); + + if (_DBUS_UNLIKELY (gettimeofday (&t, NULL) < 0)) + { + /* I'm fairly sure this can't actually happen */ + perror ("dbus-monitor: gettimeofday"); + exit (1); + } + + header[0] = t.tv_sec; + header[1] = t.tv_usec; + + if (!tool_write_all (STDOUT_FILENO, header, sizeof (header))) + { + perror ("dbus-monitor: write"); + exit (1); + } + } + break; + + case BINARY_MODE_RAW: + default: + /* nothing special, just the raw message stream */ + break; + } + + if (!tool_write_all (STDOUT_FILENO, blob, len)) + { + perror ("dbus-monitor: write"); + exit (1); + } + + dbus_free (blob); + + if (dbus_message_is_signal (message, + DBUS_INTERFACE_LOCAL, + "Disconnected")) + exit (0); + + return DBUS_HANDLER_RESULT_HANDLED; +} + static void usage (char *name, int ecode) { - fprintf (stderr, "Usage: %s [--system | --session | --address ADDRESS] [--monitor | --profile ] [watch expressions]\n", name); + fprintf (stderr, "Usage: %s [--system | --session | --address ADDRESS] [--monitor | --profile | --pcap | --binary ] [watch expressions]\n", name); exit (ecode); } @@ -304,7 +393,7 @@ main (int argc, char *argv[]) DBusHandleMessageFunction filter_func = monitor_filter_func; char *address = NULL; dbus_bool_t seen_bus_type = FALSE; - + BinaryMode binary_mode = BINARY_MODE_NOT; int i = 0, j = 0, numFilters = 0; char **filters = NULL; @@ -348,9 +437,30 @@ main (int argc, char *argv[]) else if (!strcmp (arg, "--help")) usage (argv[0], 0); else if (!strcmp (arg, "--monitor")) - filter_func = monitor_filter_func; + { + filter_func = monitor_filter_func; + binary_mode = BINARY_MODE_NOT; + } else if (!strcmp (arg, "--profile")) - filter_func = profile_filter_func; + { + filter_func = profile_filter_func; + binary_mode = BINARY_MODE_NOT; + } + else if (!strcmp (arg, "--binary")) + { + filter_func = binary_filter_func; + binary_mode = BINARY_MODE_RAW; + } + else if (!strcmp (arg, "--pcap")) + { + filter_func = binary_filter_func; + binary_mode = BINARY_MODE_PCAP; + } + else if (!strcmp (arg, "--bustle")) + { + filter_func = binary_filter_func; + binary_mode = BINARY_MODE_BUSTLE; + } else if (!strcmp (arg, "--")) continue; else if (arg[0] == '-') @@ -471,6 +581,43 @@ main (int argc, char *argv[]) } } + switch (binary_mode) + { + case BINARY_MODE_NOT: + case BINARY_MODE_RAW: + break; + + case BINARY_MODE_PCAP: + case BINARY_MODE_BUSTLE: + { + struct { + dbus_uint32_t magic; + dbus_uint16_t major_version; + dbus_uint16_t minor_version; + dbus_int32_t timezone; + dbus_uint32_t precision; + dbus_uint32_t max_length; + dbus_uint32_t link_type; + } header = { + 0xA1B2C3D4U, + 2, 4, + 0, /* capture in GMT */ + 0, /* no opinion on timestamp precision */ + (1 << 27), /* D-Bus spec says so */ + LINKTYPE_DBUS + }; + + if (binary_mode == BINARY_MODE_BUSTLE) + header.link_type = LINKTYPE_NULL; + + if (!tool_write_all (STDOUT_FILENO, &header, sizeof (header))) + { + perror ("dbus-monitor: write"); + exit (1); + } + } + break; + } while (dbus_connection_read_write_dispatch(connection, -1)) ; diff --git a/tools/tool-common.c b/tools/tool-common.c index b6af629..ee5099d 100644 --- a/tools/tool-common.c +++ b/tools/tool-common.c @@ -24,13 +24,17 @@ #include #include "tool-common.h" +#include #include #include #include #include #ifdef DBUS_WIN +#include #include +#else +#include #endif /* a hack to avoid having to depend on the static -util version of libdbus; @@ -58,3 +62,37 @@ tool_oom (const char *doing) fprintf (stderr, "OOM while %s\n", doing); exit (1); } + +#ifdef DBUS_WIN +typedef int WriteResult; +#define write(fd, buf, len) _write(fd, buf, len) +#else +typedef ssize_t WriteResult; +#endif + +dbus_bool_t +tool_write_all (int fd, + const void *buf, + size_t size) +{ + const char *p = buf; + size_t bytes_written = 0; + + while (size > bytes_written) + { + WriteResult this_time = write (fd, p, size - bytes_written); + + if (this_time < 0) + { + if (errno == EINTR) + continue; + else + return FALSE; + } + + p += this_time; + bytes_written += this_time; + } + + return TRUE; +} diff --git a/tools/tool-common.h b/tools/tool-common.h index f31076f..d56abf8 100644 --- a/tools/tool-common.h +++ b/tools/tool-common.h @@ -34,5 +34,6 @@ void tool_millisleep (int ms); void tool_oom (const char *doing); +dbus_bool_t tool_write_all (int fd, const void *buf, size_t size); #endif -- 2.1.4