/* * 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 #include #include #include #include #include #include #include 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, glong *sec, glong *usec, const guchar **blob, guint *length, GDBusMessage **message, GError **error) { struct pcap_pkthdr *hdr; int ret; g_return_val_if_fail (BUSTLE_IS_PCAP_READER (self), FALSE); g_return_val_if_fail (sec != NULL, FALSE); g_return_val_if_fail (usec != NULL, FALSE); g_return_val_if_fail (blob != NULL, FALSE); g_return_val_if_fail (length != 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: if (message != NULL) { *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; } } *sec = hdr->ts.tv_sec; *usec = hdr->ts.tv_usec; *length = hdr->caplen; return TRUE; case -2: /* EOF */ *sec = 0; *usec = 0; *blob = NULL; *length = 0; if (message != 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; int dlt = pcap_datalink (self->pcap_in); if (dlt != DLT_DBUS) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, "Unexpected link type %s", pcap_datalink_val_to_name (dlt)); bustle_pcap_reader_close (self); return FALSE; } 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); }