bustle-0.8.0: c-sources/pcap-reader.c
/*
* 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,
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);
}