summaryrefslogtreecommitdiff
path: root/c-sources/pcap-reader.c
diff options
context:
space:
mode:
Diffstat (limited to 'c-sources/pcap-reader.c')
-rw-r--r--c-sources/pcap-reader.c282
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);
+}