forked from osmocom/wireshark
481 lines
11 KiB
C
481 lines
11 KiB
C
/* ftap.c
|
|
*
|
|
* $Id$
|
|
*
|
|
* Filetap Library
|
|
* Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
*
|
|
* This program 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 General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
|
|
#ifdef HAVE_SYS_TYPES_H
|
|
#include <sys/types.h>
|
|
#endif
|
|
|
|
#ifdef HAVE_UNISTD_H
|
|
#include <unistd.h>
|
|
#endif
|
|
|
|
#ifdef HAVE_LIBZ
|
|
#include <zlib.h>
|
|
#endif
|
|
|
|
#include "ftap-int.h"
|
|
|
|
#include "ft_file_wrappers.h"
|
|
#include <wsutil/file_util.h>
|
|
#include "buffer.h"
|
|
|
|
#ifdef HAVE_PLUGINS
|
|
|
|
#include <wsutil/plugins.h>
|
|
|
|
/*
|
|
* List of wiretap plugins.
|
|
*/
|
|
typedef struct {
|
|
void (*register_ftap_module)(void); /* routine to call to register a wiretap module */
|
|
} ftap_plugin;
|
|
|
|
static GSList *ftap_plugins = NULL;
|
|
|
|
/*
|
|
* Callback for each plugin found.
|
|
*/
|
|
static gboolean
|
|
check_for_ftap_plugin(GModule *handle)
|
|
{
|
|
gpointer gp;
|
|
void (*register_ftap_module)(void);
|
|
ftap_plugin *plugin;
|
|
|
|
/*
|
|
* Do we have a register_ftap_module routine?
|
|
*/
|
|
if (!g_module_symbol(handle, "register_ftap_module", &gp)) {
|
|
/* No, so this isn't a filetap module plugin. */
|
|
return FALSE;
|
|
}
|
|
|
|
/*
|
|
* Yes - this plugin includes one or more filetap modules.
|
|
*/
|
|
register_ftap_module = (void (*)(void))gp;
|
|
|
|
/*
|
|
* Add this one to the list of wiretap module plugins.
|
|
*/
|
|
plugin = (ftap_plugin *)g_malloc(sizeof (ftap_plugin));
|
|
plugin->register_ftap_module = register_ftap_module;
|
|
ftap_plugins = g_slist_append(ftap_plugins, plugin);
|
|
return TRUE;
|
|
}
|
|
|
|
void
|
|
ftap_register_plugin_types(void)
|
|
{
|
|
add_plugin_type("file format", check_for_ftap_plugin);
|
|
}
|
|
|
|
static void
|
|
register_ftap_module_plugin(gpointer data, gpointer user_data _U_)
|
|
{
|
|
ftap_plugin *plugin = (ftap_plugin *)data;
|
|
|
|
(plugin->register_ftap_module)();
|
|
}
|
|
|
|
/*
|
|
* For all wiretap module plugins, call their register routines.
|
|
*/
|
|
void
|
|
register_all_filetap_modules(void)
|
|
{
|
|
g_slist_foreach(ftap_plugins, register_ftap_module_plugin, NULL);
|
|
}
|
|
#endif /* HAVE_PLUGINS */
|
|
|
|
/*
|
|
* Return the size of the file, as reported by the OS.
|
|
* (gint64, in case that's 64 bits.)
|
|
*/
|
|
gint64
|
|
ftap_file_size(ftap *fth, int *err)
|
|
{
|
|
ws_statb64 statb;
|
|
|
|
if (file_fstat((fth->fh == NULL) ? fth->random_fh : fth->fh,
|
|
&statb, err) == -1)
|
|
return -1;
|
|
return statb.st_size;
|
|
}
|
|
|
|
/*
|
|
* Do an fstat on the file.
|
|
*/
|
|
int
|
|
ftap_fstat(ftap *fth, ws_statb64 *statb, int *err)
|
|
{
|
|
if (file_fstat((fth->fh == NULL) ? fth->random_fh : fth->fh,
|
|
statb, err) == -1)
|
|
return -1;
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
ftap_file_type_subtype(ftap *fth)
|
|
{
|
|
return fth->file_type_subtype;
|
|
}
|
|
|
|
gboolean
|
|
ftap_iscompressed(ftap *fth)
|
|
{
|
|
return file_iscompressed((fth->fh == NULL) ? fth->random_fh : fth->fh);
|
|
}
|
|
|
|
guint
|
|
ftap_snapshot_length(ftap *fth)
|
|
{
|
|
return fth->snapshot_length;
|
|
}
|
|
|
|
int
|
|
ftap_file_encap(ftap *fth)
|
|
{
|
|
return fth->file_encap;
|
|
}
|
|
|
|
/* Table of the encapsulation types we know about. */
|
|
struct encap_type_info {
|
|
const char *name;
|
|
const char *short_name;
|
|
};
|
|
|
|
static struct encap_type_info encap_table_base[] = {
|
|
/* FTAP_ENCAP_UNKNOWN */
|
|
{ "Unknown", "unknown" },
|
|
};
|
|
|
|
WS_DLL_LOCAL
|
|
gint ftap_num_encap_types = sizeof(encap_table_base) / sizeof(struct encap_type_info);
|
|
static GArray* encap_table_arr = NULL;
|
|
|
|
#define encap_table_entry(encap) \
|
|
g_array_index(encap_table_arr, struct encap_type_info, encap)
|
|
|
|
static void ftap_init_encap_types(void) {
|
|
|
|
if (encap_table_arr) return;
|
|
|
|
encap_table_arr = g_array_new(FALSE,TRUE,sizeof(struct encap_type_info));
|
|
|
|
g_array_append_vals(encap_table_arr,encap_table_base,ftap_num_encap_types);
|
|
}
|
|
|
|
int ftap_get_num_encap_types(void) {
|
|
ftap_init_encap_types();
|
|
return ftap_num_encap_types;
|
|
}
|
|
|
|
|
|
int ftap_register_encap_type(const char* name, const char* short_name) {
|
|
struct encap_type_info e;
|
|
ftap_init_encap_types();
|
|
|
|
e.name = g_strdup(name);
|
|
e.short_name = g_strdup(short_name);
|
|
|
|
g_array_append_val(encap_table_arr,e);
|
|
|
|
return ftap_num_encap_types++;
|
|
}
|
|
|
|
|
|
/* Name that should be somewhat descriptive. */
|
|
const char *
|
|
ftap_encap_string(int encap)
|
|
{
|
|
if (encap < FTAP_ENCAP_PER_RECORD || encap >= FTAP_NUM_ENCAP_TYPES)
|
|
return "Illegal";
|
|
else if (encap == FTAP_ENCAP_PER_RECORD)
|
|
return "Per record";
|
|
else
|
|
return encap_table_entry(encap).name;
|
|
}
|
|
|
|
/* Name to use in, say, a command-line flag specifying the type. */
|
|
const char *
|
|
ftap_encap_short_string(int encap)
|
|
{
|
|
if (encap < FTAP_ENCAP_PER_RECORD || encap >= FTAP_NUM_ENCAP_TYPES)
|
|
return "illegal";
|
|
else if (encap == FTAP_ENCAP_PER_RECORD)
|
|
return "per-record";
|
|
else
|
|
return encap_table_entry(encap).short_name;
|
|
}
|
|
|
|
/* Translate a short name to a capture file type. */
|
|
int
|
|
ftap_short_string_to_encap(const char *short_name)
|
|
{
|
|
int encap;
|
|
|
|
for (encap = 0; encap < FTAP_NUM_ENCAP_TYPES; encap++) {
|
|
if (encap_table_entry(encap).short_name != NULL &&
|
|
strcmp(short_name, encap_table_entry(encap).short_name) == 0)
|
|
return encap;
|
|
}
|
|
return -1; /* no such encapsulation type */
|
|
}
|
|
|
|
static const char *ftap_errlist[] = {
|
|
"The file isn't a plain file or pipe",
|
|
"The file is being opened for random access but is a pipe",
|
|
"The file isn't a capture file in a known format",
|
|
"File contains record data we don't support",
|
|
"That file format cannot be written to a pipe",
|
|
NULL,
|
|
"Files can't be saved in that format",
|
|
"Files from that network type can't be saved in that format",
|
|
"That file format doesn't support per-packet encapsulations",
|
|
NULL,
|
|
NULL,
|
|
"Less data was read than was expected",
|
|
"The file appears to be damaged or corrupt",
|
|
"Less data was written than was requested",
|
|
"Uncompression error: data oddly truncated",
|
|
"Uncompression error: data would overflow buffer",
|
|
"Uncompression error: bad LZ77 offset",
|
|
"The standard input cannot be opened for random access",
|
|
"That file format doesn't support compression",
|
|
NULL,
|
|
NULL,
|
|
"Uncompression error",
|
|
"Internal error"
|
|
};
|
|
#define FTAP_ERRLIST_SIZE (sizeof ftap_errlist / sizeof ftap_errlist[0])
|
|
|
|
const char *
|
|
ftap_strerror(int err)
|
|
{
|
|
static char errbuf[128];
|
|
unsigned int ftap_errlist_index;
|
|
|
|
if (err < 0) {
|
|
ftap_errlist_index = -1 - err;
|
|
if (ftap_errlist_index >= FTAP_ERRLIST_SIZE) {
|
|
g_snprintf(errbuf, 128, "Error %d", err);
|
|
return errbuf;
|
|
}
|
|
if (ftap_errlist[ftap_errlist_index] == NULL)
|
|
return "Unknown reason";
|
|
return ftap_errlist[ftap_errlist_index];
|
|
} else
|
|
return g_strerror(err);
|
|
}
|
|
|
|
/* Close only the sequential side, freeing up memory it uses.
|
|
|
|
Note that we do *not* want to call the subtype's close function,
|
|
as it would free any per-subtype data, and that data may be
|
|
needed by the random-access side.
|
|
|
|
Instead, if the subtype has a "sequential close" function, we call it,
|
|
to free up stuff used only by the sequential side. */
|
|
void
|
|
ftap_sequential_close(ftap *fth)
|
|
{
|
|
if (fth->subtype_sequential_close != NULL)
|
|
(*fth->subtype_sequential_close)(fth);
|
|
|
|
if (fth->fh != NULL) {
|
|
file_close(fth->fh);
|
|
fth->fh = NULL;
|
|
}
|
|
|
|
if (fth->frame_buffer) {
|
|
buffer_free(fth->frame_buffer);
|
|
g_free(fth->frame_buffer);
|
|
fth->frame_buffer = NULL;
|
|
}
|
|
}
|
|
|
|
static void
|
|
g_fast_seek_item_free(gpointer data, gpointer user_data _U_)
|
|
{
|
|
g_free(data);
|
|
}
|
|
|
|
/*
|
|
* Close the file descriptors for the sequential and random streams, but
|
|
* don't discard any information about those streams. Used on Windows if
|
|
* we need to rename a file that we have open or if we need to rename on
|
|
* top of a file we have open.
|
|
*/
|
|
void
|
|
ftap_fdclose(ftap *fth)
|
|
{
|
|
if (fth->fh != NULL)
|
|
file_fdclose(fth->fh);
|
|
if (fth->random_fh != NULL)
|
|
file_fdclose(fth->random_fh);
|
|
}
|
|
|
|
void
|
|
ftap_close(ftap *fth)
|
|
{
|
|
ftap_sequential_close(fth);
|
|
|
|
if (fth->subtype_close != NULL)
|
|
(*fth->subtype_close)(fth);
|
|
|
|
if (fth->random_fh != NULL)
|
|
file_close(fth->random_fh);
|
|
|
|
if (fth->priv != NULL)
|
|
g_free(fth->priv);
|
|
|
|
if (fth->fast_seek != NULL) {
|
|
g_ptr_array_foreach(fth->fast_seek, g_fast_seek_item_free, NULL);
|
|
g_ptr_array_free(fth->fast_seek, TRUE);
|
|
}
|
|
|
|
g_free(fth);
|
|
}
|
|
|
|
void
|
|
ftap_cleareof(ftap *fth) {
|
|
/* Reset EOF */
|
|
file_clearerr(fth->fh);
|
|
}
|
|
|
|
gboolean
|
|
ftap_read(ftap *fth, int *err, gchar **err_info, gint64 *data_offset)
|
|
{
|
|
#if 0
|
|
/*
|
|
* Set the packet encapsulation to the file's encapsulation
|
|
* value; if that's not FTAP_ENCAP_PER_RECORD, it's the
|
|
* right answer (and means that the read routine for this
|
|
* capture file type doesn't have to set it), and if it
|
|
* *is* FTAP_ENCAP_PER_RECORD, the caller needs to set it
|
|
* anyway.
|
|
*/
|
|
wth->phdr.pkt_encap = wth->file_encap;
|
|
#endif
|
|
|
|
if (!fth->subtype_read(fth, err, err_info, data_offset)) {
|
|
/*
|
|
* If we didn't get an error indication, we read
|
|
* the last packet. See if there's any deferred
|
|
* error, as might, for example, occur if we're
|
|
* reading a compressed file, and we got an error
|
|
* reading compressed data from the file, but
|
|
* got enough compressed data to decompress the
|
|
* last packet of the file.
|
|
*/
|
|
if (*err == 0)
|
|
*err = file_error(fth->fh, err_info);
|
|
return FALSE; /* failure */
|
|
}
|
|
|
|
#if 0
|
|
/*
|
|
* It makes no sense for the captured data length to be bigger
|
|
* than the actual data length.
|
|
*/
|
|
if (wth->phdr.caplen > wth->phdr.len)
|
|
wth->phdr.caplen = wth->phdr.len;
|
|
|
|
/*
|
|
* Make sure that it's not FTAP_ENCAP_PER_RECORD, as that
|
|
* probably means the file has that encapsulation type
|
|
* but the read routine didn't set this packet's
|
|
* encapsulation type.
|
|
*/
|
|
g_assert(wth->phdr.pkt_encap != FTAP_ENCAP_PER_RECORD);
|
|
|
|
#endif
|
|
return TRUE; /* success */
|
|
}
|
|
|
|
/*
|
|
* Read packet data into a Buffer, growing the buffer as necessary.
|
|
*
|
|
* This returns an error on a short read, even if the short read hit
|
|
* the EOF immediately. (The assumption is that each packet has a
|
|
* header followed by raw packet data, and that we've already read the
|
|
* header, so if we get an EOF trying to read the packet data, the file
|
|
* has been cut short, even if the read didn't read any data at all.)
|
|
*/
|
|
gboolean
|
|
ftap_read_packet_bytes(FILE_F fh, Buffer *buf, guint length, int *err,
|
|
gchar **err_info)
|
|
{
|
|
int bytes_read;
|
|
|
|
buffer_assure_space(buf, length);
|
|
errno = FTAP_ERR_CANT_READ;
|
|
bytes_read = file_read(buffer_start_ptr(buf), length, fh);
|
|
|
|
if (bytes_read < 0 || (guint)bytes_read != length) {
|
|
*err = file_error(fh, err_info);
|
|
if (*err == 0)
|
|
*err = FTAP_ERR_SHORT_READ;
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* Return an approximation of the amount of data we've read sequentially
|
|
* from the file so far. (gint64, in case that's 64 bits.)
|
|
*/
|
|
gint64
|
|
ftap_read_so_far(ftap *fth)
|
|
{
|
|
return file_tell_raw(fth->fh);
|
|
}
|
|
|
|
#if 0
|
|
struct wtap_pkthdr *
|
|
wtap_phdr(wtap *wth)
|
|
{
|
|
return &wth->phdr;
|
|
}
|
|
|
|
guint8 *
|
|
wtap_buf_ptr(wtap *wth)
|
|
{
|
|
return buffer_start_ptr(wth->frame_buffer);
|
|
}
|
|
#endif
|
|
|
|
gboolean
|
|
ftap_seek_read(ftap *fth, gint64 seek_off,
|
|
Buffer *buf, int len,
|
|
int *err, gchar **err_info)
|
|
{
|
|
return fth->subtype_seek_read(fth, seek_off, buf, len,
|
|
err, err_info);
|
|
}
|