diff options
Diffstat (limited to 'c-sources/pcap-reader.c')
-rw-r--r-- | c-sources/pcap-reader.c | 282 |
1 files changed, 282 insertions, 0 deletions
diff --git a/c-sources/pcap-reader.c b/c-sources/pcap-reader.c new file mode 100644 index 0000000..7d3dd5f --- /dev/null +++ b/c-sources/pcap-reader.c @@ -0,0 +1,282 @@ +/* + * pcap-reader.c - reads DBus messages from a pcap stream + * Copyright © 2011–2012 Collabora Ltd. + * Copyright © 2018–2020 Will Thompson + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#define _GNU_SOURCE + +#include "config.h" +#include "pcap-reader.h" + +#include <errno.h> +#include <fcntl.h> +#include <signal.h> +#include <string.h> +#include <sys/ioctl.h> +#include <sys/stat.h> +#include <sys/types.h> + +#include <pcap/pcap.h> + +typedef struct _BustlePcapReader { + GObject parent; + + gchar *filename; + FILE *filep; + + pcap_t *pcap_in; +} BustlePcapReader; + +typedef enum { + PROP_FILENAME = 1, + PROP_FILEP +} BustlePcapReaderProp; + +static void initable_iface_init ( + gpointer g_class, + gpointer unused); + +G_DEFINE_TYPE_WITH_CODE (BustlePcapReader, bustle_pcap_reader, G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, initable_iface_init); + ) + +/* A sad echo of the functions in libglnx. */ +static inline void * +throw_errno (GError **error, + const gchar *prefix) +{ + int errsv = errno; + g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errsv), + "%s: %s", prefix, g_strerror (errsv)); + return NULL; +} + +static void +bustle_pcap_reader_init (BustlePcapReader *self) +{ +} + +static void +bustle_pcap_reader_set_property ( + GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + BustlePcapReader *self = BUSTLE_PCAP_READER (object); + + switch ((BustlePcapReaderProp) property_id) + { + case PROP_FILENAME: + self->filename = g_value_dup_string (value); + break; + case PROP_FILEP: + self->filep = g_value_get_pointer (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + } +} + +static void +bustle_pcap_reader_finalize (GObject *object) +{ + BustlePcapReader *self = BUSTLE_PCAP_READER (object); + GObjectClass *parent_class = bustle_pcap_reader_parent_class; + + g_clear_pointer (&self->filename, g_free); + g_clear_pointer (&self->filep, fclose); + g_clear_pointer (&self->pcap_in, pcap_close); + + if (parent_class->finalize != NULL) + parent_class->finalize (object); +} + +static void +bustle_pcap_reader_class_init (BustlePcapReaderClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GParamSpec *param_spec; + + object_class->set_property = bustle_pcap_reader_set_property; + object_class->finalize = bustle_pcap_reader_finalize; + + param_spec = g_param_spec_string ("filename", "Filename", + "Path to pcap file to read", + NULL, + G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS); + g_object_class_install_property (object_class, PROP_FILENAME, param_spec); + + param_spec = g_param_spec_pointer ("filep", "FILE *", + "FILE * to read pcap stream from", + G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS); + g_object_class_install_property (object_class, PROP_FILEP, param_spec); +} + +/** + * bustle_pcap_reader_read_one: + * @self: + * @hdr: (out) (transfer none): location to store pcap header (or %NULL on EOF) + * @blob: (out) (transfer none): location to store raw message (or %NULL on EOF) + * @message: (out) (transfer full): location to store parsed message (or %NULL on EOF) + * @error: + * + * Returns: %FALSE on error; %TRUE on success or end-of-file. + */ +gboolean +bustle_pcap_reader_read_one (BustlePcapReader *self, + struct pcap_pkthdr **hdr, + const guchar **blob, + GDBusMessage **message, + GError **error) +{ + int ret; + + g_return_val_if_fail (BUSTLE_IS_PCAP_READER (self), FALSE); + g_return_val_if_fail (hdr != NULL, FALSE); + g_return_val_if_fail (blob != NULL, FALSE); + g_return_val_if_fail (message != NULL && *message == NULL, FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + if (self->pcap_in == NULL) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_CLOSED, "Already closed"); + return FALSE; + } + + ret = pcap_next_ex (self->pcap_in, hdr, blob); + switch (ret) + { + case 1: + *message = g_dbus_message_new_from_blob ((guchar *) *blob, (*hdr)->caplen, G_DBUS_CAPABILITY_FLAGS_UNIX_FD_PASSING, error); + if (*message == NULL) + { + g_prefix_error (error, "Error while parsing message from dbus-monitor: "); + return FALSE; + } + + return TRUE; + + case -2: + /* EOF */ + *hdr = NULL; + *blob = NULL; + *message = NULL; + return TRUE; + + default: + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "Error %i reading dbus-monitor stream: %s", + ret, pcap_geterr (self->pcap_in)); + return FALSE; + } +} + + +/** + * bustle_pcap_reader_close: + * @self: a #BustlePcapReader + * + * Closes the underlying file or stream. + * + * If @self is reading from a pipe to `dbus-monitor`, this will cause + * `dbus-monitor` to quit in due course when it next tries to write to the + * pipe. + */ +void +bustle_pcap_reader_close (BustlePcapReader *self) +{ + g_return_if_fail (BUSTLE_IS_PCAP_READER (self)); + + g_clear_pointer (&self->pcap_in, pcap_close); + g_clear_pointer (&self->filep, fclose); +} + +static gboolean +initable_init ( + GInitable *initable, + GCancellable *cancellable, + GError **error) +{ + BustlePcapReader *self = BUSTLE_PCAP_READER (initable); + char errbuf[PCAP_ERRBUF_SIZE] = {0}; + + g_return_val_if_fail ((self->filename == NULL) ^ (self->filep == NULL), + FALSE); + + if (self->filename != NULL) + { + self->pcap_in = pcap_open_offline (self->filename, errbuf); + } + else /* self->filep != NULL */ + { + self->pcap_in = pcap_fopen_offline (self->filep, errbuf); + } + + if (self->pcap_in == NULL) + { + g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, errbuf); + return FALSE; + } + + /* Now owned by pcap_in */ + self->filep = NULL; + return TRUE; +} + +static void +initable_iface_init ( + gpointer g_class, + gpointer unused) +{ + GInitableIface *iface = g_class; + + iface->init = initable_init; +} + +BustlePcapReader * +bustle_pcap_reader_open (const gchar *filename, + GError **error) +{ + g_return_val_if_fail (filename != NULL, NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + return g_initable_new ( + BUSTLE_TYPE_PCAP_READER, NULL, error, + "filename", filename, + NULL); +} + +/** + * bustle_pcap_reader_fopen: + * @filep: (transfer full): + * + * Returns: a reader, or %NULL on error + */ +BustlePcapReader * +bustle_pcap_reader_fopen (FILE *filep, + GError **error) +{ + g_return_val_if_fail (filep != NULL, NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + return g_initable_new ( + BUSTLE_TYPE_PCAP_READER, NULL, error, + "filep", filep, + NULL); +} |