From 6daf9fd15b0b63cef0bbd13cd574888f05d695f6 Mon Sep 17 00:00:00 2001 From: Michael Mann Date: Wed, 1 Jan 2014 13:18:45 +0000 Subject: Filetap library, version 0.001 This is intended as the "tap library" for fileshark. Right now its basically just a very stripped down (and renamed) copy of the wiretap library. The goal is to remove "capture" and "wire" specific functionality out of the file handling to make it easier/simpler to support non-capture type files in the epan architecture. svn path=/trunk/; revision=54531 --- filetap/CMakeLists.txt | 84 +++ filetap/Makefile.am | 84 +++ filetap/Makefile.common | 42 ++ filetap/Makefile.nmake | 59 ++ filetap/buffer.c | 163 +++++ filetap/buffer.h | 74 +++ filetap/file_access.c | 993 ++++++++++++++++++++++++++++ filetap/file_wrappers.c | 1649 +++++++++++++++++++++++++++++++++++++++++++++++ filetap/file_wrappers.h | 61 ++ filetap/ftap-int.h | 252 ++++++++ filetap/ftap.c | 480 ++++++++++++++ filetap/ftap.h | 389 +++++++++++ 12 files changed, 4330 insertions(+) create mode 100644 filetap/CMakeLists.txt create mode 100644 filetap/Makefile.am create mode 100644 filetap/Makefile.common create mode 100644 filetap/Makefile.nmake create mode 100644 filetap/buffer.c create mode 100644 filetap/buffer.h create mode 100644 filetap/file_access.c create mode 100644 filetap/file_wrappers.c create mode 100644 filetap/file_wrappers.h create mode 100644 filetap/ftap-int.h create mode 100644 filetap/ftap.c create mode 100644 filetap/ftap.h (limited to 'filetap') diff --git a/filetap/CMakeLists.txt b/filetap/CMakeLists.txt new file mode 100644 index 0000000000..ebb3357637 --- /dev/null +++ b/filetap/CMakeLists.txt @@ -0,0 +1,84 @@ +# CMakeLists.txt +# +# $Id$ +# +# Wireshark - Network traffic analyzer +# By Gerald Combs +# Copyright 1998 Gerald Combs +# +# 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(UseABICheck) + +set(CLEAN_FILES + buffer.c + file_wrappers.c + ftap.c +) + +set(DIRTY_FILES +) + +set(FILETAP_FILES + ${CLEAN_FILES} + ${DIRTY_FILES} +) + +if (WERROR) + set_source_files_properties( + ${CLEAN_FILES} + PROPERTIES + COMPILE_FLAGS -Werror + ) +endif() + +set(filetap_LIBS + ${GLIB2_LIBRARIES} + ${GMODULE2_LIBRARIES} + ${ZLIB_LIBRARIES} + wsutil +) + +add_library(filetap ${LINK_MODE_LIB} + ${FILETAP_FILES} +) + +set(FULL_SO_VERSION "0.0.0") + +set_target_properties(filetap PROPERTIES COMPILE_DEFINITIONS "WS_BUILD_DLL") +set_target_properties(filetap PROPERTIES LINK_FLAGS "${WS_LINK_FLAGS}") +set_target_properties(filetap PROPERTIES VERSION ${FULL_SO_VERSION} SOVERSION 0) +set_target_properties(filetap PROPERTIES FOLDER "DLLs") + +ABICHECK(libfiletap) + +add_custom_command(OUTPUT libfiletap.abi.tar.gz + COMMAND cp ../config.h ${ABICHECK_TMPDIR}/ + COMMAND ${ABICHECK_COMMAND} + COMMAND cp ${CMAKE_CURRENT_BINARY_DIR}/abi_dumps/libfiletap/libfiletap_* ${CMAKE_CURRENT_BINARY_DIR}/libfiletap.abi.tar.gz + COMMAND rm -rf ${ABICHECK_TMPDIR}/* ${CMAKE_CURRENT_BINARY_DIR}/abi_dumps + DEPENDS ${HEADERS} filetap) + +target_link_libraries(filetap ${filetap_LIBS}) + +if(NOT ${ENABLE_STATIC}) + install(TARGETS filetap + LIBRARY DESTINATION lib + RUNTIME DESTINATION lib + ARCHIVE DESTINATION lib + ) +endif() + diff --git a/filetap/Makefile.am b/filetap/Makefile.am new file mode 100644 index 0000000000..ef93d88aa5 --- /dev/null +++ b/filetap/Makefile.am @@ -0,0 +1,84 @@ +# Makefile.am +# Automake file for Filetap +# +# $Id$ +# +# Wireshark - Network traffic analyzer +# By Gerald Combs +# Copyright 1998 Gerald Combs +# +# 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 ../Makefile.am.inc + +ACLOCAL_AMFLAGS = `../aclocal-flags` + +include Makefile.common + +noinst_LTLIBRARIES = libfiletap_generated.la +lib_LTLIBRARIES = libfiletap.la +# http://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html +libfiletap_la_LDFLAGS = -version-info 0:3:0 @LDFLAGS_SHAREDLIB@ + +AM_NON_GENERATED_CFLAGS =-DWS_BUILD_DLL + +if HAVE_WARNINGS_AS_ERRORS +AM_NON_GENERATED_CFLAGS += -Werror +endif + +AM_CPPFLAGS = -I$(srcdir)/.. + +CLEANFILES = \ + libfiletap.a \ + libfiletap.la \ + libfiletap_generated.a \ + libfiletap_generated.la \ + *~ + +MAINTAINERCLEANFILES = \ + Makefile.in + +libfiletap_la_SOURCES = \ + $(NONGENERATED_C_FILES) \ + $(NONGENERATED_HEADER_FILES) +libfiletap_la_CFLAGS = $(AM_NON_GENERATED_CFLAGS) + +EXTRA_DIST = \ + README.airmagnet \ + README.developer \ + Makefile.common \ + Makefile.nmake \ + libfiletap.vcproj + +libfiletap_la_LIBADD = libfiletap_generated.la ${top_builddir}/wsutil/libwsutil.la $(GLIB_LIBS) +libfiletap_la_DEPENDENCIES = libfiletap_generated.la ${top_builddir}/wsutil/libwsutil.la + +# ABI compliance checker can be obtained from +# http://ispras.linux-foundation.org/index.php/ABI_compliance_checker +# Checked using version 1.21.12 +dumpabi-libfiletap: all abi-descriptor.xml + rm -rf abi-check-headers abi_dumps .libs/*.abi.tar.gz + mkdir abi-check-headers + cp *.h ../ws_symbol_export.h abi-check-headers/ + abi-compliance-checker -l libfiletap -v1 `readlink .libs/libfiletap.so | sed 's/.*\.so\.//'` \ + -relpath $(abs_srcdir) -dump-abi abi-descriptor.xml || \ + cat logs/libfiletap/[0-9]*/log.txt + cp -f abi_dumps/libfiletap/libfiletap_* .libs/ + cd .libs && ln -sf libfiletap_*.abi.tar.gz libfiletap.abi.tar.gz + +checkapi: +## 'abort' checking disabled for now pending resolution of existing use of g_assert & g_error +## $(PERL) ../tools/checkAPIs.pl -g abort -g termoutput $(NONGENERATED_C_FILES) + $(PERL) ../tools/checkAPIs.pl -g termoutput -build $(NONGENERATED_C_FILES) diff --git a/filetap/Makefile.common b/filetap/Makefile.common new file mode 100644 index 0000000000..ce64c72759 --- /dev/null +++ b/filetap/Makefile.common @@ -0,0 +1,42 @@ +# Makefile.common +# Contains the stuff from Makefile.am and Makefile.nmake that is +# a) common to both files and +# b) portable between both files +# +# $Id$ +# +# Wireshark - Network traffic analyzer +# By Gerald Combs +# Copyright 1998 Gerald Combs +# +# 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. + +# C source files that are part of the Filetap source; this includes only +# .c files, not YACC or Lex or... files (as Makefile.nmake maps this list +# into a list of object files by replacing ".c" with ".obj") or files +# generated from YACC or Lex files (as Automake doesn't want them in +# _SOURCES variables). +NONGENERATED_C_FILES = \ + buffer.c \ + file_access.c \ + file_wrappers.c \ + ftap.c + +# Header files that are not generated from other files +NONGENERATED_HEADER_FILES = \ + buffer.h \ + file_wrappers.h \ + ftap.h \ + ftap-int.h diff --git a/filetap/Makefile.nmake b/filetap/Makefile.nmake new file mode 100644 index 0000000000..72d54a3eb3 --- /dev/null +++ b/filetap/Makefile.nmake @@ -0,0 +1,59 @@ +# +# $Id$ +# + +include ..\config.nmake +include +include ..\Makefile.nmake.inc + +include Makefile.common + +# We use GENERATED_CFLAGS to get around flex's non-LLP64-compliant output +GENERATED_CFLAGS=\ + $(STANDARD_CFLAGS) \ + -DYYMALLOC=malloc -DYYFREE=free \ + /I. /I.. $(GLIB_CFLAGS) \ + $(ZLIB_CFLAGS) /I$(PCAP_DIR)/include \ + -DWS_BUILD_DLL + +CFLAGS=$(WARNINGS_ARE_ERRORS) $(GENERATED_CFLAGS) + +.c.obj:: + $(CC) $(CFLAGS) -Fd.\ -c $< + +OBJECTS=$(NONGENERATED_C_FILES:.c=.obj) + +filetap_LIBS = \ + $(GLIB_LIBS) \ + ..\wsutil\libwsutil.lib \ + $(ZLIB_LIBS) + +all: filetap-$(FTAP_VERSION).dll + +filetap-$(FTAP_VERSION).lib: filetap-$(FTAP_VERSION).dll +filetap-$(FTAP_VERSION).exp: filetap-$(FTAP_VERSION).dll + +filetap-$(FTAP_VERSION).dll : $(OBJECTS) ..\image\filetap.res + $(link) $(dlllflags) $(conlibsdll) \ + $(LOCAL_LDFLAGS) $(DLL_LDFLAGS) \ + /OUT:filetap-$(FTAP_VERSION).dll \ + /IMPLIB:filetap-$(FTAP_VERSION).lib \ + ..\image\filetap.res \ + $(OBJECTS) $(filetap_LIBS) + +clean : + rm -f $(OBJECTS) \ + filetap-*.lib \ + filetap-*.exp \ + filetap-*.dll \ + filetap-*.dll.manifest \ + *.pdb *.sbr + +distclean: clean + +maintainer-clean: distclean + +checkapi: +## 'abort' checking disabled for now pending resolution of existing use of g_assert & g_error +## $(PERL) ../tools/checkAPIs.pl -g abort -g termoutput $(NONGENERATED_C_FILES) + $(PERL) ../tools/checkAPIs.pl -g termoutput -build $(NONGENERATED_C_FILES) diff --git a/filetap/buffer.c b/filetap/buffer.c new file mode 100644 index 0000000000..fc12c6a987 --- /dev/null +++ b/filetap/buffer.c @@ -0,0 +1,163 @@ +/* buffer.c + * + * $Id$ + * + * Wiretap Library + * Copyright (c) 1998 by Gilbert Ramirez + * + * 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 +#include +#include +#include + +#include "buffer.h" + +/* Initializes a buffer with a certain amount of allocated space */ +void +buffer_init(Buffer* buffer, gsize space) +{ + buffer->data = (guint8*)g_malloc(space); + buffer->allocated = space; + buffer->start = 0; + buffer->first_free = 0; +} + +/* Frees the memory used by a buffer, and the buffer struct */ +void +buffer_free(Buffer* buffer) +{ + g_free(buffer->data); +} + +/* Assures that there are 'space' bytes at the end of the used space + so that another routine can copy directly into the buffer space. After + doing that, the routine will also want to run + buffer_increase_length(). */ +void +buffer_assure_space(Buffer* buffer, gsize space) +{ + gsize available_at_end = buffer->allocated - buffer->first_free; + gsize space_used; + gboolean space_at_beginning; + + /* If we've got the space already, good! */ + if (space <= available_at_end) { + return; + } + + /* Maybe we don't have the space available at the end, but we would + if we moved the used space back to the beginning of the + allocation. The buffer could have become fragmented through lots + of calls to buffer_remove_start(). I'm using buffer->start as the + same as 'available_at_start' in this comparison. */ + + /* or maybe there's just no more room. */ + + space_at_beginning = buffer->start >= space; + if (space_at_beginning || buffer->start > 0) { + space_used = buffer->first_free - buffer->start; + /* this memory copy better be safe for overlapping memory regions! */ + memmove(buffer->data, buffer->data + buffer->start, space_used); + buffer->start = 0; + buffer->first_free = space_used; + } + /*if (buffer->start >= space) {*/ + if (space_at_beginning) { + return; + } + + /* We'll allocate more space */ + buffer->allocated += space + 1024; + buffer->data = (guint8*)g_realloc(buffer->data, buffer->allocated); +} + +void +buffer_append(Buffer* buffer, guint8 *from, gsize bytes) +{ + buffer_assure_space(buffer, bytes); + memcpy(buffer->data + buffer->first_free, from, bytes); + buffer->first_free += bytes; +} + +void +buffer_remove_start(Buffer* buffer, gsize bytes) +{ + if (buffer->start + bytes > buffer->first_free) { + g_error("buffer_remove_start trying to remove %" G_GINT64_MODIFIER "u bytes. s=%" G_GINT64_MODIFIER "u ff=%" G_GINT64_MODIFIER "u!\n", + (guint64)bytes, (guint64)buffer->start, + (guint64)buffer->first_free); + /** g_error() does an abort() and thus never returns **/ + } + buffer->start += bytes; + + if (buffer->start == buffer->first_free) { + buffer->start = 0; + buffer->first_free = 0; + } +} + + +#ifndef SOME_FUNCTIONS_ARE_DEFINES +void +buffer_clean(Buffer* buffer) +{ + buffer_remove_start(buffer, buffer_length(buffer)); +} +#endif + +#ifndef SOME_FUNCTIONS_ARE_DEFINES +void +buffer_increase_length(Buffer* buffer, gsize bytes) +{ + buffer->first_free += bytes; +} +#endif + +#ifndef SOME_FUNCTIONS_ARE_DEFINES +gsize +buffer_length(Buffer* buffer) +{ + return buffer->first_free - buffer->start; +} +#endif + +#ifndef SOME_FUNCTIONS_ARE_DEFINES +guint8 * +buffer_start_ptr(Buffer* buffer) +{ + return buffer->data + buffer->start; +} +#endif + +#ifndef SOME_FUNCTIONS_ARE_DEFINES +guint8 * +buffer_end_ptr(Buffer* buffer) +{ + return buffer->data + buffer->first_free; +} +#endif + +#ifndef SOME_FUNCTIONS_ARE_DEFINES +void +buffer_append_buffer(Buffer* buffer, Buffer* src_buffer) +{ + buffer_append(buffer, buffer_start_ptr(src_buffer), buffer_length(src_buffer)); +} +#endif diff --git a/filetap/buffer.h b/filetap/buffer.h new file mode 100644 index 0000000000..792ae0fd5c --- /dev/null +++ b/filetap/buffer.h @@ -0,0 +1,74 @@ +/* buffer.h + * + * $Id$ + * + * Wiretap Library + * Copyright (c) 1998 by Gilbert Ramirez + * + * 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. + * + */ + +#ifndef __W_BUFFER_H__ +#define __W_BUFFER_H__ + +#include +#include "ws_symbol_export.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#define SOME_FUNCTIONS_ARE_DEFINES + +typedef struct Buffer { + guint8 *data; + gsize allocated; + gsize start; + gsize first_free; +} Buffer; + +WS_DLL_PUBLIC +void buffer_init(Buffer* buffer, gsize space); +WS_DLL_PUBLIC +void buffer_free(Buffer* buffer); +WS_DLL_PUBLIC +void buffer_assure_space(Buffer* buffer, gsize space); +WS_DLL_PUBLIC +void buffer_append(Buffer* buffer, guint8 *from, gsize bytes); +WS_DLL_PUBLIC +void buffer_remove_start(Buffer* buffer, gsize bytes); + +#ifdef SOME_FUNCTIONS_ARE_DEFINES +# define buffer_clean(buffer) buffer_remove_start((buffer), buffer_length(buffer)) +# define buffer_increase_length(buffer,bytes) (buffer)->first_free += (bytes) +# define buffer_length(buffer) ((buffer)->first_free - (buffer)->start) +# define buffer_start_ptr(buffer) ((buffer)->data + (buffer)->start) +# define buffer_end_ptr(buffer) ((buffer)->data + (buffer)->first_free) +# define buffer_append_buffer(buffer,src_buffer) buffer_append((buffer), buffer_start_ptr(src_buffer), buffer_length(src_buffer)) +#else + void buffer_clean(Buffer* buffer); + void buffer_increase_length(Buffer* buffer, unsigned int bytes); + unsigned int buffer_length(Buffer* buffer); + guint8* buffer_start_ptr(Buffer* buffer); + guint8* buffer_end_ptr(Buffer* buffer); + void buffer_append_buffer(Buffer* buffer, Buffer* src_buffer); +#endif + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif diff --git a/filetap/file_access.c b/filetap/file_access.c new file mode 100644 index 0000000000..0468906406 --- /dev/null +++ b/filetap/file_access.c @@ -0,0 +1,993 @@ +/* file_access.c + * + * $Id$ + * + * Wiretap Library + * Copyright (c) 1998 by Gilbert Ramirez + * + * 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 +#include +#include + +#ifdef HAVE_FCNTL_H +#include +#endif + +#ifdef HAVE_UNISTD_H +#include +#endif + +#include + +#include + +#include "ftap-int.h" +#include "file_wrappers.h" +#include "buffer.h" + +/* + * Add an extension, and all compressed versions thereof, to a GSList + * of extensions. + */ +static GSList *add_extensions(GSList *extensions, const gchar *extension, + GSList *compressed_file_extensions) +{ + GSList *compressed_file_extension; + + /* + * Add the specified extension. + */ + extensions = g_slist_append(extensions, g_strdup(extension)); + + /* + * Now add the extensions for compressed-file versions of + * that extension. + */ + for (compressed_file_extension = compressed_file_extensions; + compressed_file_extension != NULL; + compressed_file_extension = g_slist_next(compressed_file_extension)) { + extensions = g_slist_append(extensions, + g_strdup_printf("%s.%s", extension, + (gchar *)compressed_file_extension->data)); + } + + return extensions; +} + +/* + * File types that can be identified by file extensions. + */ +static const struct filetap_extension_info file_type_extensions_base[] = { + { "Wireshark/tcpdump/... - pcap", "pcap;cap;dmp" }, +}; + +#define N_FILE_TYPE_EXTENSIONS (sizeof file_type_extensions_base / sizeof file_type_extensions_base[0]) + +static const struct filetap_extension_info* file_type_extensions = NULL; + +static GArray* file_type_extensions_arr = NULL; + +/* initialize the extensions array if it has not been initialized yet */ +static void init_file_type_extensions(void) { + + if (file_type_extensions_arr) return; + + file_type_extensions_arr = g_array_new(FALSE,TRUE,sizeof(struct filetap_extension_info)); + + g_array_append_vals(file_type_extensions_arr,file_type_extensions_base,N_FILE_TYPE_EXTENSIONS); + + file_type_extensions = (struct filetap_extension_info*)(void *)file_type_extensions_arr->data; +} + +void ftap_register_file_type_extension(const struct filetap_extension_info *ei) { + init_file_type_extensions(); + + g_array_append_val(file_type_extensions_arr,*ei); + + file_type_extensions = (const struct filetap_extension_info*)(void *)file_type_extensions_arr->data; +} + +int ftap_get_num_file_type_extensions(void) +{ + return file_type_extensions_arr->len; +} + +const char *ftap_get_file_extension_type_name(int extension_type) +{ + return file_type_extensions[extension_type].name; +} + +static GSList *add_extensions_for_file_extensions_type(int extension_type, + GSList *extensions, GSList *compressed_file_extensions) +{ + gchar **extensions_set, **extensionp, *extension; + + /* + * Split the extension-list string into a set of extensions. + */ + extensions_set = g_strsplit(file_type_extensions[extension_type].extensions, + ";", 0); + + /* + * Add each of those extensions to the list. + */ + for (extensionp = extensions_set; *extensionp != NULL; extensionp++) { + extension = *extensionp; + + /* + * Add the extension, and all compressed variants + * of it. + */ + extensions = add_extensions(extensions, extension, + compressed_file_extensions); + } + + g_strfreev(extensions_set); + return extensions; +} + +/* Return a list of file extensions that are used by the specified file + extension type. + + All strings in the list are allocated with g_malloc() and must be freed + with g_free(). */ +GSList *ftap_get_file_extension_type_extensions(guint extension_type) +{ + GSList *compressed_file_extensions; + GSList *extensions; + + if (extension_type >= file_type_extensions_arr->len) + return NULL; /* not a valid extension type */ + + extensions = NULL; /* empty list, to start with */ + + /* + * Get the list of compressed-file extensions. + */ + compressed_file_extensions = ftap_get_compressed_file_extensions(); + + /* + * Add all this file extension type's extensions, with compressed + * variants. + */ + extensions = add_extensions_for_file_extensions_type(extension_type, + extensions, compressed_file_extensions); + + g_slist_free(compressed_file_extensions); + return extensions; +} + +/* Return a list of all extensions that are used by all file types, + including compressed extensions, e.g. not just "pcap" but also + "pcap.gz" if we can read gzipped files. + + All strings in the list are allocated with g_malloc() and must be freed + with g_free(). */ +GSList *ftap_get_all_file_extensions_list(void) +{ + GSList *compressed_file_extensions; + GSList *extensions; + unsigned int i; + + init_file_type_extensions(); + + extensions = NULL; /* empty list, to start with */ + + /* + * Get the list of compressed-file extensions. + */ + compressed_file_extensions = ftap_get_compressed_file_extensions(); + + for (i = 0; i < file_type_extensions_arr->len; i++) { + /* + * Add all this file extension type's extensions, with + * compressed variants. + */ + extensions = add_extensions_for_file_extensions_type(i, + extensions, compressed_file_extensions); + } + + g_slist_free(compressed_file_extensions); + return extensions; +} + +/* The open_file_* routines should return: + * + * -1 on an I/O error; + * + * 1 if the file they're reading is one of the types it handles; + * + * 0 if the file they're reading isn't the type they're checking for. + * + * If the routine handles this type of file, it should set the "file_type" + * field in the "struct ftap" to the type of the file. + * + * Note that the routine does not have to free the private data pointer on + * error. The caller takes care of that by calling ftap_close on error. + * (See https://bugs.wireshark.org/bugzilla/show_bug.cgi?id=8518) + * + * However, the caller does have to free the private data pointer when + * returning 0, since the next file type will be called and will likely + * just overwrite the pointer. + */ + +/* Files that have magic bytes in fixed locations. These + * are easy to identify. Only an open routine is needed. + */ +static const ftap_open_routine_t magic_number_open_routines_base[] = { + NULL/* libpcap_open, */ +}; +#define N_MAGIC_FILE_TYPES (sizeof magic_number_open_routines_base / sizeof magic_number_open_routines_base[0]) + +static ftap_open_routine_t* magic_number_open_routines = NULL; + +static GArray* magic_number_open_routines_arr = NULL; + +/* + * Initialize the magic-number open routines array if it has not been + * initialized yet. + */ +static void init_magic_number_open_routines(void) { + + if (magic_number_open_routines_arr) return; + + magic_number_open_routines_arr = g_array_new(FALSE,TRUE,sizeof(ftap_open_routine_t)); + + g_array_append_vals(magic_number_open_routines_arr,magic_number_open_routines_base,N_MAGIC_FILE_TYPES); + + magic_number_open_routines = (ftap_open_routine_t*)(void *)magic_number_open_routines_arr->data; +} + +void ftap_register_magic_number_open_routine(ftap_open_routine_t open_routine) { + init_magic_number_open_routines(); + + g_array_append_val(magic_number_open_routines_arr,open_routine); + + magic_number_open_routines = (ftap_open_routine_t*)(void *)magic_number_open_routines_arr->data; +} + +/* Files that don't have magic bytes at a fixed location, + * but that instead require a heuristic of some sort to + * identify them. This includes ASCII trace files. + * + * Entries for the ASCII trace files that would be, for example, + * saved copies of a Telnet session to some box are put after + * most of the other entries, as we don't want to treat a capture + * of such a session as a trace file from such a session + * merely because it has the right text in it. They still + * appear before the *really* weak entries, such as the VWR entry. + */ +static const struct ftap_heuristic_open_info heuristic_open_info_base[] = { + { NULL, "(empty)", }, +}; +#define N_HEURISTIC_FILE_TYPES (sizeof heuristic_open_info_base / sizeof heuristic_open_info_base[0]) + +static const struct ftap_heuristic_open_info* heuristic_open_info = NULL; + +static GArray* heuristic_open_info_arr = NULL; + +/* + * Initialize the heuristics array if it has not been initialized yet. + */ +static void init_heuristic_open_info(void) { + + if (heuristic_open_info_arr) return; + + heuristic_open_info_arr = g_array_new(FALSE,TRUE,sizeof(struct ftap_heuristic_open_info)); + + g_array_append_vals(heuristic_open_info_arr,heuristic_open_info_base,N_HEURISTIC_FILE_TYPES); + + heuristic_open_info = (const struct ftap_heuristic_open_info*)(void *)heuristic_open_info_arr->data; +} + +void ftap_register_heuristic_open_info(const struct ftap_heuristic_open_info *hi) { + init_heuristic_open_info(); + + g_array_append_val(heuristic_open_info_arr,*hi); + + heuristic_open_info = (const struct ftap_heuristic_open_info*)(void *)heuristic_open_info_arr->data; +} + +/* + * Visual C++ on Win32 systems doesn't define these. (Old UNIX systems don't + * define them either.) + * + * Visual C++ on Win32 systems doesn't define S_IFIFO, it defines _S_IFIFO. + */ +#ifndef S_ISREG +#define S_ISREG(mode) (((mode) & S_IFMT) == S_IFREG) +#endif +#ifndef S_IFIFO +#define S_IFIFO _S_IFIFO +#endif +#ifndef S_ISFIFO +#define S_ISFIFO(mode) (((mode) & S_IFMT) == S_IFIFO) +#endif +#ifndef S_ISDIR +#define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR) +#endif + +static char *get_file_extension(const char *pathname) +{ + gchar *filename; + gchar **components; + size_t ncomponents; + GSList *compressed_file_extensions, *compressed_file_extension; + gchar *extensionp; + + /* + * Is the pathname empty? + */ + if (strcmp(pathname, "") == 0) + return NULL; /* no extension */ + + /* + * Find the last component of the pathname. + */ + filename = g_path_get_basename(pathname); + + /* + * Does it have an extension? + */ + if (strchr(filename, '.') == NULL) { + g_free(filename); + return NULL; /* no extension whatsoever */ + } + + /* + * Yes. Split it into components separated by ".". + */ + components = g_strsplit(filename, ".", 0); + g_free(filename); + + /* + * Count the components. + */ + for (ncomponents = 0; components[ncomponents] != NULL; ncomponents++) + ; + + if (ncomponents == 0) { + g_strfreev(components); + return NULL; /* no components */ + } + if (ncomponents == 1) { + g_strfreev(components); + return NULL; /* only one component, with no "." */ + } + + /* + * Is the last component one of the extensions used for compressed + * files? + */ + compressed_file_extensions = ftap_get_compressed_file_extensions(); + if (compressed_file_extensions == NULL) { + /* + * We don't support reading compressed files, so just + * return a copy of whatever extension we did find. + */ + extensionp = g_strdup(components[ncomponents - 1]); + g_strfreev(components); + return extensionp; + } + extensionp = components[ncomponents - 1]; + for (compressed_file_extension = compressed_file_extensions; + compressed_file_extension != NULL; + compressed_file_extension = g_slist_next(compressed_file_extension)) { + if (strcmp(extensionp, (char *)compressed_file_extension->data) == 0) { + /* + * Yes, it's one of the compressed-file extensions. + * Is there an extension before that? + */ + if (ncomponents == 2) { + g_strfreev(components); + return NULL; /* no, only two components */ + } + + /* + * Yes, return that extension. + */ + extensionp = g_strdup(components[ncomponents - 2]); + g_strfreev(components); + return extensionp; + } + } + + /* + * The extension isn't one of the compressed-file extensions; + * return it. + */ + extensionp = g_strdup(extensionp); + g_strfreev(components); + return extensionp; +} + +gboolean heuristic_uses_extension(unsigned int i, const char *extension) +{ + gchar **extensions_set, **extensionp; + + /* + * Does this file type *have* any extensions? + */ + if (heuristic_open_info[i].extensions == NULL) + return FALSE; /* no */ + + /* + * Get a list of the extensions used by the specified file type. + */ + extensions_set = g_strsplit(heuristic_open_info[i].extensions, ";", 0); + + /* + * Check each of them against the specified extension. + */ + for (extensionp = extensions_set; *extensionp != NULL; + extensionp++) { + if (strcmp(extension, *extensionp) == 0) { + g_strfreev(extensions_set); + return TRUE; /* it's one of them */ + } + } + g_strfreev(extensions_set); + return FALSE; /* it's not one of them */ +} + +/* Opens a file and prepares a ftap struct. + If "do_random" is TRUE, it opens the file twice; the second open + allows the application to do random-access I/O without moving + the seek offset for sequential I/O, which is used by Wireshark + so that it can do sequential I/O to a capture file that's being + written to as new packets arrive independently of random I/O done + to display protocol trees for packets when they're selected. */ +ftap* ftap_open_offline(const char *filename, int *err, char **err_info, + gboolean do_random) +{ + int fd; + ws_statb64 statb; + ftap *fth; + unsigned int i; + gboolean use_stdin = FALSE; + gchar *extension; + + /* open standard input if filename is '-' */ + if (strcmp(filename, "-") == 0) + use_stdin = TRUE; + + /* First, make sure the file is valid */ + if (use_stdin) { + if (ws_fstat64(0, &statb) < 0) { + *err = errno; + return NULL; + } + } else { + if (ws_stat64(filename, &statb) < 0) { + *err = errno; + return NULL; + } + } + if (S_ISFIFO(statb.st_mode)) { + /* + * Opens of FIFOs are allowed only when not opening + * for random access. + * + * XXX - currently, we do seeking when trying to find + * out the file type, so we don't actually support + * opening FIFOs. However, we may eventually + * do buffering that allows us to do at least some + * file type determination even on pipes, so we + * allow FIFO opens and let things fail later when + * we try to seek. + */ + if (do_random) { + *err = FTAP_ERR_RANDOM_OPEN_PIPE; + return NULL; + } + } else if (S_ISDIR(statb.st_mode)) { + /* + * Return different errors for "this is a directory" + * and "this is some random special file type", so + * the user can get a potentially more helpful error. + */ + *err = EISDIR; + return NULL; + } else if (! S_ISREG(statb.st_mode)) { + *err = FTAP_ERR_NOT_REGULAR_FILE; + return NULL; + } + + /* + * We need two independent descriptors for random access, so + * they have different file positions. If we're opening the + * standard input, we can only dup it to get additional + * descriptors, so we can't have two independent descriptors, + * and thus can't do random access. + */ + if (use_stdin && do_random) { + *err = FTAP_ERR_RANDOM_OPEN_STDIN; + return NULL; + } + + errno = ENOMEM; + fth = (ftap *)g_malloc0(sizeof(ftap)); + + /* Open the file */ + errno = FTAP_ERR_CANT_OPEN; + if (use_stdin) { + /* + * We dup FD 0, so that we don't have to worry about + * a file_close of wth->fh closing the standard + * input of the process. + */ + fd = ws_dup(0); + if (fd < 0) { + *err = errno; + g_free(fth); + return NULL; + } +#ifdef _WIN32 + if (_setmode(fd, O_BINARY) == -1) { + /* "Shouldn't happen" */ + *err = errno; + g_free(fth); + return NULL; + } +#endif + if (!(fth->fh = file_fdopen(fd))) { + *err = errno; + ws_close(fd); + g_free(fth); + return NULL; + } + } else { + if (!(fth->fh = file_open(filename))) { + *err = errno; + g_free(fth); + return NULL; + } + } + + if (do_random) { + if (!(fth->random_fh = file_open(filename))) { + *err = errno; + file_close(fth->fh); + g_free(fth); + return NULL; + } + } else + fth->random_fh = NULL; + + /* initialization */ + fth->file_encap = FTAP_ENCAP_UNKNOWN; + fth->subtype_sequential_close = NULL; + fth->subtype_close = NULL; + fth->priv = NULL; + + init_magic_number_open_routines(); + init_heuristic_open_info(); + if (fth->random_fh) { + fth->fast_seek = g_ptr_array_new(); + + file_set_random_access(fth->fh, FALSE, fth->fast_seek); + file_set_random_access(fth->random_fh, TRUE, fth->fast_seek); + } + + /* Try all file types that support magic numbers */ + for (i = 0; i < magic_number_open_routines_arr->len; i++) { + /* Seek back to the beginning of the file; the open routine + for the previous file type may have left the file + position somewhere other than the beginning, and the + open routine for this file type will probably want + to start reading at the beginning. + + Initialize the data offset while we're at it. */ + if (file_seek(fth->fh, 0, SEEK_SET, err) == -1) { + /* I/O error - give up */ + ftap_close(fth); + return NULL; + } + + switch ((*magic_number_open_routines[i])(fth, err, err_info)) { + + case -1: + /* I/O error - give up */ + ftap_close(fth); + return NULL; + + case 0: + /* No I/O error, but not that type of file */ + break; + + case 1: + /* We found the file type */ + goto success; + } + } + + /* Does this file's name have an extension? */ + extension = get_file_extension(filename); + if (extension != NULL) { + /* Yes - try the heuristic types that use that extension first. */ + for (i = 0; i < heuristic_open_info_arr->len; i++) { + /* Does this type use that extension? */ + if (heuristic_uses_extension(i, extension)) { + /* Yes. */ + if (file_seek(fth->fh, 0, SEEK_SET, err) == -1) { + /* I/O error - give up */ + g_free(extension); + ftap_close(fth); + return NULL; + } + + switch ((*heuristic_open_info[i].open_routine)(fth, + err, err_info)) { + + case -1: + /* I/O error - give up */ + g_free(extension); + ftap_close(fth); + return NULL; + + case 0: + /* No I/O error, but not that type of file */ + break; + + case 1: + /* We found the file type */ + g_free(extension); + goto success; + } + } + } + + /* Now try the ones that don't use it. */ + for (i = 0; i < heuristic_open_info_arr->len; i++) { + /* Does this type use that extension? */ + if (!heuristic_uses_extension(i, extension)) { + /* No. */ + if (file_seek(fth->fh, 0, SEEK_SET, err) == -1) { + /* I/O error - give up */ + g_free(extension); + ftap_close(fth); + return NULL; + } + + switch ((*heuristic_open_info[i].open_routine)(fth, + err, err_info)) { + + case -1: + /* I/O error - give up */ + g_free(extension); + ftap_close(fth); + return NULL; + + case 0: + /* No I/O error, but not that type of file */ + break; + + case 1: + /* We found the file type */ + g_free(extension); + goto success; + } + } + } + g_free(extension); + } else { + /* No - try all the heuristics types in order. */ + for (i = 0; i < heuristic_open_info_arr->len; i++) { + if (file_seek(fth->fh, 0, SEEK_SET, err) == -1) { + /* I/O error - give up */ + ftap_close(fth); + return NULL; + } + + switch ((*heuristic_open_info[i].open_routine)(fth, + err, err_info)) { + + case -1: + /* I/O error - give up */ + ftap_close(fth); + return NULL; + + case 0: + /* No I/O error, but not that type of file */ + break; + + case 1: + /* We found the file type */ + goto success; + } + } + } + + /* Well, it's not one of the types of file we know about. */ + ftap_close(fth); + *err = FTAP_ERR_FILE_UNKNOWN_FORMAT; + return NULL; + +success: + fth->frame_buffer = (struct Buffer *)g_malloc(sizeof(struct Buffer)); + buffer_init(fth->frame_buffer, 1500); + + return fth; +} + +/* + * Given the pathname of the file we just closed with ftap_fdclose(), attempt + * to reopen that file and assign the new file descriptor(s) to the sequential + * stream and, if do_random is TRUE, to the random stream. Used on Windows + * after the rename of a file we had open was done or if the rename of a + * file on top of a file we had open failed. + * + * This is only required by Wireshark, not TShark, and, at the point that + * Wireshark is doing this, the sequential stream is closed, and the + * random stream is open, so this refuses to open pipes, and only + * reopens the random stream. + */ +gboolean +ftap_fdreopen(ftap *fth, const char *filename, int *err) +{ + ws_statb64 statb; + + /* + * We need two independent descriptors for random access, so + * they have different file positions. If we're opening the + * standard input, we can only dup it to get additional + * descriptors, so we can't have two independent descriptors, + * and thus can't do random access. + */ + if (strcmp(filename, "-") == 0) { + *err = FTAP_ERR_RANDOM_OPEN_STDIN; + return FALSE; + } + + /* First, make sure the file is valid */ + if (ws_stat64(filename, &statb) < 0) { + *err = errno; + return FALSE; + } + if (S_ISFIFO(statb.st_mode)) { + /* + * Opens of FIFOs are not allowed; see above. + */ + *err = FTAP_ERR_RANDOM_OPEN_PIPE; + return FALSE; + } else if (S_ISDIR(statb.st_mode)) { + /* + * Return different errors for "this is a directory" + * and "this is some random special file type", so + * the user can get a potentially more helpful error. + */ + *err = EISDIR; + return FALSE; + } else if (! S_ISREG(statb.st_mode)) { + *err = FTAP_ERR_NOT_REGULAR_FILE; + return FALSE; + } + + /* Open the file */ + errno = FTAP_ERR_CANT_OPEN; + if (!file_fdreopen(fth->random_fh, filename)) { + *err = errno; + return FALSE; + } + return TRUE; +} + +/* Table of the file types we know about. + Entries must be sorted by FTAP_FILE_TYPE_SUBTYPE_xxx values in ascending order */ +static const struct ftap_file_type_subtype_info dump_open_table_base[] = { + /* FTAP_FILE_TYPE_SUBTYPE_UNKNOWN (only used internally for initialization) */ + { NULL, NULL, NULL, NULL }, +}; + +gint ftap_num_file_types_subtypes = sizeof(dump_open_table_base) / sizeof(struct ftap_file_type_subtype_info); + +static GArray* dump_open_table_arr = NULL; +static const struct ftap_file_type_subtype_info* dump_open_table = dump_open_table_base; + +/* initialize the file types array if it has not being initialized yet */ +static void init_file_types_subtypes(void) { + + if (dump_open_table_arr) return; + + dump_open_table_arr = g_array_new(FALSE,TRUE,sizeof(struct ftap_file_type_subtype_info)); + + g_array_append_vals(dump_open_table_arr,dump_open_table_base,ftap_num_file_types_subtypes); + + dump_open_table = (const struct ftap_file_type_subtype_info*)(void *)dump_open_table_arr->data; +} + +int ftap_register_file_type_subtypes(const struct ftap_file_type_subtype_info* fi) { + init_file_types_subtypes(); + + g_array_append_val(dump_open_table_arr,*fi); + + dump_open_table = (const struct ftap_file_type_subtype_info*)(void *)dump_open_table_arr->data; + + return ftap_num_file_types_subtypes++; +} + +int ftap_get_num_file_types_subtypes(void) +{ + return ftap_num_file_types_subtypes; +} + +/* + * Given a GArray of FTAP_ENCAP_ types, return the per-file encapsulation + * type that would be needed to write out a file with those types. If + * there's only one type, it's that type, otherwise it's + * FTAP_ENCAP_PER_RECORD. + */ +int +ftap_dump_file_encap_type(const GArray *file_encaps) +{ + int encap; + + encap = FTAP_ENCAP_PER_RECORD; + if (file_encaps->len == 1) { + /* OK, use the one-and-only encapsulation type. */ + encap = g_array_index(file_encaps, gint, 0); + } + return encap; +} + +/* Name that should be somewhat descriptive. */ +const char *ftap_file_type_subtype_string(int file_type_subtype) +{ + if (file_type_subtype < 0 || file_type_subtype >= ftap_num_file_types_subtypes) { + g_error("Unknown capture file type %d", file_type_subtype); + /** g_error() does an abort() and thus never returns **/ + return ""; + } else + return dump_open_table[file_type_subtype].name; +} + +/* Name to use in, say, a command-line flag specifying the type/subtype. */ +const char *ftap_file_type_subtype_short_string(int file_type_subtype) +{ + if (file_type_subtype < 0 || file_type_subtype >= ftap_num_file_types_subtypes) + return NULL; + else + return dump_open_table[file_type_subtype].short_name; +} + +/* Translate a short name to a capture file type/subtype. */ +int ftap_short_string_to_file_type_subtype(const char *short_name) +{ + int file_type_subtype; + + for (file_type_subtype = 0; file_type_subtype < ftap_num_file_types_subtypes; file_type_subtype++) { + if (dump_open_table[file_type_subtype].short_name != NULL && + strcmp(short_name, dump_open_table[file_type_subtype].short_name) == 0) + return file_type_subtype; + } + + return -1; /* no such file type, or we can't write it */ +} + +static GSList * +add_extensions_for_file_type_subtype(int file_type_subtype, GSList *extensions, + GSList *compressed_file_extensions) +{ + gchar **extensions_set, **extensionp; + gchar *extension; + + /* + * Add the default extension, and all compressed variants of + * it. + */ + extensions = add_extensions(extensions, + dump_open_table[file_type_subtype].default_file_extension, + compressed_file_extensions); + + if (dump_open_table[file_type_subtype].additional_file_extensions != NULL) { + /* + * We have additional extensions; add them. + * + * First, split the extension-list string into a set of + * extensions. + */ + extensions_set = g_strsplit(dump_open_table[file_type_subtype].additional_file_extensions, + ";", 0); + + /* + * Add each of those extensions to the list. + */ + for (extensionp = extensions_set; *extensionp != NULL; + extensionp++) { + extension = *extensionp; + + /* + * Add the extension, and all compressed variants + * of it. + */ + extensions = add_extensions(extensions, extension, + compressed_file_extensions); + } + + g_strfreev(extensions_set); + } + return extensions; +} + +/* Return a list of file extensions that are used by the specified file type. + + If include_compressed is TRUE, the list will include compressed + extensions, e.g. not just "pcap" but also "pcap.gz" if we can read + gzipped files. + + All strings in the list are allocated with g_malloc() and must be freed + with g_free(). */ +GSList *ftap_get_file_extensions_list(int file_type_subtype, gboolean include_compressed) +{ + GSList *compressed_file_extensions; + GSList *extensions; + + if (file_type_subtype < 0 || file_type_subtype >= ftap_num_file_types_subtypes) + return NULL; /* not a valid file type */ + + if (dump_open_table[file_type_subtype].default_file_extension == NULL) + return NULL; /* valid, but no extensions known */ + + extensions = NULL; /* empty list, to start with */ + + /* + * If include_compressions is true, get the list of compressed-file + * extensions. + */ + if (include_compressed) + compressed_file_extensions = ftap_get_compressed_file_extensions(); + else + compressed_file_extensions = NULL; + + /* + * Add all this file type's extensions, with compressed + * variants. + */ + extensions = add_extensions_for_file_type_subtype(file_type_subtype, extensions, + compressed_file_extensions); + + g_slist_free(compressed_file_extensions); + return extensions; +} + +/* + * Free a list returned by ftap_get_file_extension_type_extensions(), + * ftap_get_all_file_extensions_list, or ftap_get_file_extensions_list(). + */ +void ftap_free_extensions_list(GSList *extensions) +{ + GSList *extension; + + for (extension = extensions; extension != NULL; + extension = g_slist_next(extension)) { + g_free(extension->data); + } + g_slist_free(extensions); +} + +/* Return the default file extension to use with the specified file type; + that's just the extension, without any ".". */ +const char *ftap_default_file_extension(int file_type_subtype) +{ + if (file_type_subtype < 0 || file_type_subtype >= ftap_num_file_types_subtypes) + return NULL; + else + return dump_open_table[file_type_subtype].default_file_extension; +} diff --git a/filetap/file_wrappers.c b/filetap/file_wrappers.c new file mode 100644 index 0000000000..8541967396 --- /dev/null +++ b/filetap/file_wrappers.c @@ -0,0 +1,1649 @@ +/* file_wrappers.c + * + * $Id$ + * + * Wiretap Library + * Copyright (c) 1998 by Gilbert Ramirez + * + * 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. + */ + +/* file_access interface based heavily on zlib gzread.c and gzlib.c from zlib + * Copyright (C) 1995-2010 Jean-loup Gailly and Mark Adler + * under licence: + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. +*/ + +#include "config.h" + +#ifdef HAVE_UNISTD_H +#include +#endif /* HAVE_UNISTD_H */ + +#include +#include +#ifdef HAVE_FCNTL_H +#include +#endif /* HAVE_FCNTL_H */ +#include +#include "ftap-int.h" +#include "file_wrappers.h" +#include + +#ifdef HAVE_LIBZ +#include +#endif /* HAVE_LIBZ */ + +/* + * See RFC 1952 for a description of the gzip file format. + * + * Some other compressed file formats we might want to support: + * + * XZ format: http://tukaani.org/xz/ + * + * Bzip2 format: http://bzip.org/ + */ + +/* + * List of extensions for compressed files. + * If we add support for more compressed file types, this table + * might be expanded to include routines to handle the various + * compression types. + */ +static const char *compressed_file_extensions[] = { +#ifdef HAVE_LIBZ + "gz", +#endif + NULL +}; + +/* + * Return a GSList of all the compressed file extensions. + * The data pointers all point to items in compressed_file_extensions[], + * so the GSList can just be freed with g_slist_free(). + */ +GSList * +ftap_get_compressed_file_extensions(void) +{ + const char **extension; + GSList *extensions; + + extensions = NULL; + for (extension = &compressed_file_extensions[0]; *extension != NULL; + extension++) + extensions = g_slist_append(extensions, (gpointer)(*extension)); + return extensions; +} + +/* #define GZBUFSIZE 8192 */ +#define GZBUFSIZE 4096 + +/* values for ftap_reader compression */ +typedef enum { + UNKNOWN, /* unknown - look for a gzip header */ + UNCOMPRESSED, /* uncompressed - copy input directly */ +#ifdef HAVE_LIBZ + ZLIB, /* decompress a zlib stream */ + GZIP_AFTER_HEADER +#endif +} compression_t; + +struct ftap_reader { + int fd; /* file descriptor */ + gint64 raw_pos; /* current position in file (just to not call lseek()) */ + gint64 pos; /* current position in uncompressed data */ + guint size; /* buffer size */ + unsigned char *in; /* input buffer */ + unsigned char *out; /* output buffer (double-sized when reading) */ + unsigned char *next; /* next output data to deliver or write */ + + guint have; /* amount of output data unused at next */ + gboolean eof; /* TRUE if end of input file reached */ + gint64 start; /* where the gzip data started, for rewinding */ + gint64 raw; /* where the raw data started, for seeking */ + compression_t compression; /* type of compression, if any */ + gboolean is_compressed; /* FALSE if completely uncompressed, TRUE otherwise */ + /* seek request */ + gint64 skip; /* amount to skip (already rewound if backwards) */ + gboolean seek_pending; /* TRUE if seek request pending */ + /* error information */ + int err; /* error code */ + const char *err_info; /* additional error information string for some errors */ + + guint avail_in; /* number of bytes available at next_in */ + unsigned char *next_in; /* next input byte */ +#ifdef HAVE_LIBZ + /* zlib inflate stream */ + z_stream strm; /* stream structure in-place (not a pointer) */ + gboolean dont_check_crc; /* TRUE if we aren't supposed to check the CRC */ +#endif + /* fast seeking */ + GPtrArray *fast_seek; + void *fast_seek_cur; +}; + +static int /* gz_load */ +raw_read(FILE_F state, unsigned char *buf, unsigned int count, guint *have) +{ + ssize_t ret; + + *have = 0; + do { + ret = read(state->fd, buf + *have, count - *have); + if (ret <= 0) + break; + *have += (unsigned)ret; + state->raw_pos += ret; + } while (*have < count); + if (ret < 0) { + state->err = errno; + state->err_info = NULL; + return -1; + } + if (ret == 0) + state->eof = TRUE; + return 0; +} + +static int /* gz_avail */ +fill_in_buffer(FILE_F state) +{ + if (state->err) + return -1; + if (state->eof == 0) { + if (raw_read(state, state->in, state->size, &(state->avail_in)) == -1) + return -1; + state->next_in = state->in; + } + return 0; +} + +#define ZLIB_WINSIZE 32768 + +struct fast_seek_point { + gint64 out; /* corresponding offset in uncompressed data */ + gint64 in; /* offset in input file of first full byte */ + + compression_t compression; + union { + struct { +#ifdef HAVE_INFLATEPRIME + int bits; /* number of bits (1-7) from byte at in - 1, or 0 */ +#endif + unsigned char window[ZLIB_WINSIZE]; /* preceding 32K of uncompressed data */ + + /* be gentle with Z_STREAM_END, 8 bytes more... Another solution would be to comment checks out */ + guint32 adler; + guint32 total_out; + } zlib; + } data; +}; + +struct zlib_cur_seek_point { + unsigned char window[ZLIB_WINSIZE]; /* preceding 32K of uncompressed data */ + unsigned int pos; + unsigned int have; +}; + +#define SPAN G_GINT64_CONSTANT(1048576) +static struct fast_seek_point * +fast_seek_find(FILE_F file, gint64 pos) +{ + struct fast_seek_point *smallest = NULL; + struct fast_seek_point *item; + guint low, i, max; + + if (!file->fast_seek) + return NULL; + + for (low = 0, max = file->fast_seek->len; low < max; ) { + i = (low + max) / 2; + item = (struct fast_seek_point *)file->fast_seek->pdata[i]; + + if (pos < item->out) + max = i; + else if (pos > item->out) { + smallest = item; + low = i + 1; + } else { + return item; + } + } + return smallest; +} + +static void +fast_seek_header(FILE_F file, gint64 in_pos, gint64 out_pos, + compression_t compression) +{ + struct fast_seek_point *item = NULL; + + if (file->fast_seek->len != 0) + item = (struct fast_seek_point *)file->fast_seek->pdata[file->fast_seek->len - 1]; + + if (!item || item->out < out_pos) { + struct fast_seek_point *val = g_new(struct fast_seek_point,1); + val->in = in_pos; + val->out = out_pos; + val->compression = compression; + + g_ptr_array_add(file->fast_seek, val); + } +} + +static void +fast_seek_reset(FILE_F state _U_) +{ +#ifdef HAVE_LIBZ + if (state->compression == ZLIB && state->fast_seek_cur) { + struct zlib_cur_seek_point *cur = (struct zlib_cur_seek_point *) state->fast_seek_cur; + + cur->have = 0; + } +#endif +} + +#ifdef HAVE_LIBZ + +/* Get next byte from input, or -1 if end or error. + * + * Note: + * + * 1) errors from raw_read(), and thus from fill_in_buffer(), are + * "sticky", and fill_in_buffer() won't do any reading if there's + * an error; + * + * 2) GZ_GETC() returns -1 on an EOF; + * + * so it's safe to make multiple GZ_GETC() calls and only check the + * last one for an error. */ +#define GZ_GETC() ((state->avail_in == 0 && fill_in_buffer(state) == -1) ? -1 : \ + (state->avail_in == 0 ? -1 : \ + (state->avail_in--, *(state->next_in)++))) + +/* Get a one-byte integer and return 0 on success and the value in *ret. + Otherwise -1 is returned, state->err is set, and *ret is not modified. */ +static int +gz_next1(FILE_F state, guint8 *ret) +{ + int ch; + + ch = GZ_GETC(); + if (ch == -1) { + if (state->err == 0) { + /* EOF */ + state->err = FTAP_ERR_SHORT_READ; + state->err_info = NULL; + } + return -1; + } + *ret = ch; + return 0; +} + +/* Get a two-byte little-endian integer and return 0 on success and the value + in *ret. Otherwise -1 is returned, state->err is set, and *ret is not + modified. */ +static int +gz_next2(FILE_F state, guint16 *ret) +{ + guint16 val; + int ch; + + val = GZ_GETC(); + ch = GZ_GETC(); + if (ch == -1) { + if (state->err == 0) { + /* EOF */ + state->err = FTAP_ERR_SHORT_READ; + state->err_info = NULL; + } + return -1; + } + val += (guint16)ch << 8; + *ret = val; + return 0; +} + +/* Get a four-byte little-endian integer and return 0 on success and the value + in *ret. Otherwise -1 is returned, state->err is set, and *ret is not + modified. */ +static int +gz_next4(FILE_F state, guint32 *ret) +{ + guint32 val; + int ch; + + val = GZ_GETC(); + val += (unsigned)GZ_GETC() << 8; + val += (guint32)GZ_GETC() << 16; + ch = GZ_GETC(); + if (ch == -1) { + if (state->err == 0) { + /* EOF */ + state->err = FTAP_ERR_SHORT_READ; + state->err_info = NULL; + } + return -1; + } + val += (guint32)ch << 24; + *ret = val; + return 0; +} + +/* Skip the specified number of bytes and return 0 on success. Otherwise -1 + is returned. */ +static int +gz_skipn(FILE_F state, size_t n) +{ + while (n != 0) { + if (GZ_GETC() == -1) { + if (state->err == 0) { + /* EOF */ + state->err = FTAP_ERR_SHORT_READ; + state->err_info = NULL; + } + return -1; + } + n--; + } + return 0; +} + +/* Skip a null-terminated string and return 0 on success. Otherwise -1 + is returned. */ +static int +gz_skipzstr(FILE_F state) +{ + int ch; + + /* It's null-terminated, so scan until we read a byte with + the value 0 or get an error. */ + while ((ch = GZ_GETC()) > 0) + ; + if (ch == -1) { + if (state->err == 0) { + /* EOF */ + state->err = FTAP_ERR_SHORT_READ; + state->err_info = NULL; + } + return -1; + } + return 0; +} + +static void +zlib_fast_seek_add(FILE_F file, struct zlib_cur_seek_point *point, int bits, gint64 in_pos, gint64 out_pos) +{ + /* it's for sure after gzip header, so file->fast_seek->len != 0 */ + struct fast_seek_point *item = (struct fast_seek_point *)file->fast_seek->pdata[file->fast_seek->len - 1]; + +#ifndef HAVE_INFLATEPRIME + if (bits) + return; +#endif + + /* Glib has got Balanced Binary Trees (GTree) but I couldn't find a way to do quick search for nearest (and smaller) value to seek (It's what fast_seek_find() do) + * Inserting value in middle of sorted array is expensive, so we want to add only in the end. + * It's not big deal, cause first-read don't usually invoke seeking + */ + if (item->out + SPAN < out_pos) { + struct fast_seek_point *val = g_new(struct fast_seek_point,1); + val->in = in_pos; + val->out = out_pos; + val->compression = ZLIB; +#ifdef HAVE_INFLATEPRIME + val->data.zlib.bits = bits; +#endif + if (point->pos != 0) { + unsigned int left = ZLIB_WINSIZE - point->pos; + + memcpy(val->data.zlib.window, point->window + point->pos, left); + memcpy(val->data.zlib.window + left, point->window, point->pos); + } else + memcpy(val->data.zlib.window, point->window, ZLIB_WINSIZE); + + /* + * XXX - strm.adler is a uLong in at least some versions + * of zlib, and uLong is an unsigned long in at least + * some of those versions, which means it's 64-bit + * on LP64 platforms, even though the checksum is + * 32-bit. We assume the actual Adler checksum + * is in the lower 32 bits of strm.adler; as the + * checksum in the file is only 32 bits, we save only + * those lower 32 bits, and cast away any additional + * bits to squelch warnings. + * + * The same applies to strm.total_out. + */ + val->data.zlib.adler = (guint32) file->strm.adler; + val->data.zlib.total_out = (guint32) file->strm.total_out; + g_ptr_array_add(file->fast_seek, val); + } +} + +static void /* gz_decomp */ +zlib_read(FILE_F state, unsigned char *buf, unsigned int count) +{ + int ret = 0; /* XXX */ + guint32 crc, len; + z_streamp strm = &(state->strm); + + unsigned char *buf2 = buf; + unsigned int count2 = count; + + strm->avail_out = count; + strm->next_out = buf; + + /* fill output buffer up to end of deflate stream or error */ + do { + /* get more input for inflate() */ + if (state->avail_in == 0 && fill_in_buffer(state) == -1) + break; + if (state->avail_in == 0) { + /* EOF */ + state->err = FTAP_ERR_SHORT_READ; + state->err_info = NULL; + break; + } + + strm->avail_in = state->avail_in; + strm->next_in = state->next_in; + /* decompress and handle errors */ +#ifdef Z_BLOCK + ret = inflate(strm, Z_BLOCK); +#else + ret = inflate(strm, Z_NO_FLUSH); +#endif + state->avail_in = strm->avail_in; + state->next_in = strm->next_in; + if (ret == Z_STREAM_ERROR) { + state->err = FTAP_ERR_DECOMPRESS; + state->err_info = strm->msg; + break; + } + if (ret == Z_NEED_DICT) { + state->err = FTAP_ERR_DECOMPRESS; + state->err_info = "preset dictionary needed"; + break; + } + if (ret == Z_MEM_ERROR) { + /* This means "not enough memory". */ + state->err = ENOMEM; + state->err_info = NULL; + break; + } + if (ret == Z_DATA_ERROR) { /* deflate stream invalid */ + state->err = FTAP_ERR_DECOMPRESS; + state->err_info = strm->msg; + break; + } + /* + * XXX - Z_BUF_ERROR? + */ + + strm->adler = crc32(strm->adler, buf2, count2 - strm->avail_out); +#ifdef Z_BLOCK + if (state->fast_seek_cur) { + struct zlib_cur_seek_point *cur = (struct zlib_cur_seek_point *) state->fast_seek_cur; + unsigned int ready = count2 - strm->avail_out; + + if (ready < ZLIB_WINSIZE) { + guint left = ZLIB_WINSIZE - cur->pos; + + if (ready >= left) { + memcpy(cur->window + cur->pos, buf2, left); + if (ready != left) + memcpy(cur->window, buf2 + left, ready - left); + + cur->pos = ready - left; + cur->have += ready; + } else { + memcpy(cur->window + cur->pos, buf2, ready); + cur->pos += ready; + cur->have += ready; + } + + if (cur->have >= ZLIB_WINSIZE) + cur->have = ZLIB_WINSIZE; + + } else { + memcpy(cur->window, buf2 + (ready - ZLIB_WINSIZE), ZLIB_WINSIZE); + cur->pos = 0; + cur->have = ZLIB_WINSIZE; + } + + if (cur->have >= ZLIB_WINSIZE && ret != Z_STREAM_END && (strm->data_type & 128) && !(strm->data_type & 64)) + zlib_fast_seek_add(state, cur, (strm->data_type & 7), state->raw_pos - strm->avail_in, state->pos + (count - strm->avail_out)); + } +#endif + buf2 = (buf2 + count2 - strm->avail_out); + count2 = strm->avail_out; + + } while (strm->avail_out && ret != Z_STREAM_END); + + /* update available output and crc check value */ + state->next = buf; + state->have = count - strm->avail_out; + + /* Check gzip trailer if at end of deflate stream. + We don't fail immediately here, we just set an error + indication, so that we try to process what data we + got before the error. The next attempt to read + something past that data will get the error. */ + if (ret == Z_STREAM_END) { + if (gz_next4(state, &crc) != -1 && + gz_next4(state, &len) != -1) { + if (crc != strm->adler && !state->dont_check_crc) { + state->err = FTAP_ERR_DECOMPRESS; + state->err_info = "bad CRC"; + } else if (len != (strm->total_out & 0xffffffffL)) { + state->err = FTAP_ERR_DECOMPRESS; + state->err_info = "length field wrong"; + } + } + state->compression = UNKNOWN; /* ready for next stream, once have is 0 */ + g_free(state->fast_seek_cur); + state->fast_seek_cur = NULL; + } +} +#endif + +static int +gz_head(FILE_F state) +{ + /* get some data in the input buffer */ + if (state->avail_in == 0) { + if (fill_in_buffer(state) == -1) + return -1; + if (state->avail_in == 0) + return 0; + } + + /* look for the gzip magic header bytes 31 and 139 */ +#ifdef HAVE_LIBZ + if (state->next_in[0] == 31) { + state->avail_in--; + state->next_in++; + if (state->avail_in == 0 && fill_in_buffer(state) == -1) + return -1; + if (state->avail_in && state->next_in[0] == 139) { + guint8 cm; + guint8 flags; + guint16 len; + guint16 hcrc; + + /* we have a gzip header, woo hoo! */ + state->avail_in--; + state->next_in++; + + /* read rest of header */ + + /* compression method (CM) */ + if (gz_next1(state, &cm) == -1) + return -1; + if (cm != 8) { + state->err = FTAP_ERR_DECOMPRESS; + state->err_info = "unknown compression method"; + return -1; + } + + /* flags (FLG) */ + if (gz_next1(state, &flags) == -1) + return -1; + if (flags & 0xe0) { /* reserved flag bits */ + state->err = FTAP_ERR_DECOMPRESS; + state->err_info = "reserved flag bits set"; + return -1; + } + + /* modification time (MTIME) */ + if (gz_skipn(state, 4) == -1) + return -1; + + /* extra flags (XFL) */ + if (gz_skipn(state, 1) == -1) + return -1; + + /* operating system (OS) */ + if (gz_skipn(state, 1) == -1) + return -1; + + if (flags & 4) { + /* extra field - get XLEN */ + if (gz_next2(state, &len) == -1) + return -1; + + /* skip the extra field */ + if (gz_skipn(state, len) == -1) + return -1; + } + if (flags & 8) { + /* file name */ + if (gz_skipzstr(state) == -1) + return -1; + } + if (flags & 16) { + /* comment */ + if (gz_skipzstr(state) == -1) + return -1; + } + if (flags & 2) { + /* header crc */ + if (gz_next2(state, &hcrc) == -1) + return -1; + /* XXX - check the CRC? */ + } + + /* set up for decompression */ + inflateReset(&(state->strm)); + state->strm.adler = crc32(0L, Z_NULL, 0); + state->compression = ZLIB; + state->is_compressed = TRUE; +#ifdef Z_BLOCK + if (state->fast_seek) { + struct zlib_cur_seek_point *cur = g_new(struct zlib_cur_seek_point,1); + + cur->pos = cur->have = 0; + g_free(state->fast_seek_cur); + state->fast_seek_cur = cur; + fast_seek_header(state, state->raw_pos - state->avail_in, state->pos, GZIP_AFTER_HEADER); + } +#endif + return 0; + } + else { + /* not a gzip file -- save first byte (31) and fall to raw i/o */ + state->out[0] = 31; + state->have = 1; + } + } +#endif +#ifdef HAVE_LIBXZ + /* { 0xFD, '7', 'z', 'X', 'Z', 0x00 } */ + /* FD 37 7A 58 5A 00 */ +#endif + if (state->fast_seek) + fast_seek_header(state, state->raw_pos - state->avail_in - state->have, state->pos, UNCOMPRESSED); + + /* doing raw i/o, save start of raw data for seeking, copy any leftover + input to output -- this assumes that the output buffer is larger than + the input buffer, which also assures space for gzungetc() */ + state->raw = state->pos; + state->next = state->out; + if (state->avail_in) { + memcpy(state->next + state->have, state->next_in, state->avail_in); + state->have += state->avail_in; + state->avail_in = 0; + } + state->compression = UNCOMPRESSED; + return 0; +} + +static int /* gz_make */ +fill_out_buffer(FILE_F state) +{ + if (state->compression == UNKNOWN) { /* look for gzip header */ + if (gz_head(state) == -1) + return -1; + if (state->have) /* got some data from gz_head() */ + return 0; + } + if (state->compression == UNCOMPRESSED) { /* straight copy */ + if (raw_read(state, state->out, state->size /* << 1 */, &(state->have)) == -1) + return -1; + state->next = state->out; + } +#ifdef HAVE_LIBZ + else if (state->compression == ZLIB) { /* decompress */ + zlib_read(state, state->out, state->size << 1); + } +#endif + return 0; +} + +static int +gz_skip(FILE_F state, gint64 len) +{ + guint n; + + /* skip over len bytes or reach end-of-file, whichever comes first */ + while (len) + if (state->have) { + /* We have stuff in the output buffer; skip over + it. */ + n = (gint64)state->have > len ? (unsigned)len : state->have; + state->have -= n; + state->next += n; + state->pos += n; + len -= n; + } else if (state->err) { + /* We have nothing in the output buffer, and + we have an error that may not have been + reported yet; that means we can't generate + any more data into the output buffer, so + return an error indication. */ + return -1; + } else if (state->eof && state->avail_in == 0) { + /* We have nothing in the output buffer, and + we're at the end of the input; just return. */ + break; + } else { + /* We have nothing in the output buffer, and + we can generate more data; get more output, + looking for header if required. */ + if (fill_out_buffer(state) == -1) + return -1; + } + return 0; +} + +static void +gz_reset(FILE_F state) +{ + state->have = 0; /* no output data available */ + state->eof = FALSE; /* not at end of file */ + state->compression = UNKNOWN; /* look for gzip header */ + + state->seek_pending = FALSE; /* no seek request pending */ + state->err = 0; /* clear error */ + state->err_info = NULL; + state->pos = 0; /* no uncompressed data yet */ + state->avail_in = 0; /* no input data yet */ +} + +FILE_F +file_fdopen(int fd) +{ +#ifdef _STATBUF_ST_BLKSIZE /* XXX, _STATBUF_ST_BLKSIZE portable? */ + struct stat st; +#endif + int want = GZBUFSIZE; + FILE_F state; + + if (fd == -1) + return NULL; + + /* allocate FILE_F structure to return */ + state = (FILE_F)g_try_malloc(sizeof *state); + if (state == NULL) + return NULL; + + state->fast_seek_cur = NULL; + state->fast_seek = NULL; + + /* open the file with the appropriate mode (or just use fd) */ + state->fd = fd; + + /* we don't yet know whether it's compressed */ + state->is_compressed = FALSE; + + /* save the current position for rewinding (only if reading) */ + state->start = ws_lseek64(state->fd, 0, SEEK_CUR); + if (state->start == -1) state->start = 0; + state->raw_pos = state->start; + + /* initialize stream */ + gz_reset(state); + +#ifdef _STATBUF_ST_BLKSIZE + if (fstat(fd, &st) >= 0) { + /* + * Yes, st_blksize can be bigger than an int; apparently, + * it's a long on LP64 Linux, for example. + * + * If the value is too big to fit into an int, just + * use the default. + */ + if (st.st_blksize <= G_MAXINT) + want = (int)st.st_blksize; + /* XXX, verify result? */ + } +#endif + + /* allocate buffers */ + state->in = (unsigned char *)g_try_malloc(want); + state->out = (unsigned char *)g_try_malloc(want << 1); + state->size = want; + if (state->in == NULL || state->out == NULL) { + g_free(state->out); + g_free(state->in); + g_free(state); + errno = ENOMEM; + return NULL; + } + +#ifdef HAVE_LIBZ + /* allocate inflate memory */ + state->strm.zalloc = Z_NULL; + state->strm.zfree = Z_NULL; + state->strm.opaque = Z_NULL; + state->strm.avail_in = 0; + state->strm.next_in = Z_NULL; + if (inflateInit2(&(state->strm), -15) != Z_OK) { /* raw inflate */ + g_free(state->out); + g_free(state->in); + g_free(state); + errno = ENOMEM; + return NULL; + } + + /* for now, assume we should check the crc */ + state->dont_check_crc = FALSE; +#endif + /* return stream */ + return state; +} + +FILE_F +file_open(const char *path) +{ + int fd; + FILE_F ft; +#ifdef HAVE_LIBZ + const char *suffixp; +#endif + + /* open file and do correct filename conversions. + + XXX - do we need O_LARGEFILE? On UN*X, if we need to do + something special to get large file support, the configure + script should have set us up with the appropriate #defines, + so we should be getting a large-file-enabled file descriptor + here. Pre-Large File Summit UN*Xes, and possibly even some + post-LFS UN*Xes, might require O_LARGEFILE here, though. + If so, we should probably handle that in ws_open(). */ + if ((fd = ws_open(path, O_RDONLY|O_BINARY, 0000)) == -1) + return NULL; + + /* open file handle */ + ft = file_fdopen(fd); + if (ft == NULL) { + ws_close(fd); + return NULL; + } + +#ifdef HAVE_LIBZ + /* + * If this file's name ends in ".caz", it's probably a compressed + * Windows Sniffer file. The compression is gzip, but if we + * process the CRC as specified by RFC 1952, the computed CRC + * doesn't match the stored CRC. + * + * Compressed Windows Sniffer files don't all have the same CRC + * value; is it just random crap, or are they running the CRC on + * a different set of data than you're supposed to (e.g., not + * CRCing some of the data), or something such as that? + * + * For now, we just set a flag to ignore CRC errors. + */ + suffixp = strrchr(path, '.'); + if (suffixp != NULL) { + if (g_ascii_strcasecmp(suffixp, ".caz") == 0) + ft->dont_check_crc = TRUE; + } +#endif + + return ft; +} + +void +file_set_random_access(FILE_F stream, gboolean random_flag _U_, GPtrArray *seek) +{ + stream->fast_seek = seek; +} + +gint64 +file_seek(FILE_F file, gint64 offset, int whence, int *err) +{ + struct fast_seek_point *here; + guint n; + + /* can only seek from start or relative to current position */ + if (whence != SEEK_SET && whence != SEEK_CUR) { + g_assert_not_reached(); +/* + *err = EINVAL; + return -1; + */ + } + + /* normalize offset to a SEEK_CUR specification */ + if (whence == SEEK_SET) + offset -= file->pos; + else if (file->seek_pending) + offset += file->skip; + file->seek_pending = FALSE; + + if (offset < 0 && file->next) { + /* + * This is guaranteed to fit in an unsigned int. + * To squelch compiler warnings, we cast the + * result. + */ + guint had = (unsigned)(file->next - file->out); + if (-offset <= had) { + /* + * Offset is negative, so -offset is + * non-negative, and -offset is + * <= an unsigned and thus fits in an + * unsigned. Get that value and + * adjust appropriately. + * + * (Casting offset to unsigned makes + * it positive, which is not what we + * would want, so we cast -offset + * instead.) + */ + guint adjustment = (unsigned)(-offset); + file->have += adjustment; + file->next -= adjustment; + file->pos -= adjustment; + return file->pos; + } + } + + /* XXX, profile */ + if ((here = fast_seek_find(file, file->pos + offset)) && (offset < 0 || offset > SPAN || here->compression == UNCOMPRESSED)) { + gint64 off, off2; + +#ifdef HAVE_LIBZ + if (here->compression == ZLIB) { +#ifdef HAVE_INFLATEPRIME + off = here->in - (here->data.zlib.bits ? 1 : 0); +#else + off = here->in; +#endif + off2 = here->out; + } else if (here->compression == GZIP_AFTER_HEADER) { + off = here->in; + off2 = here->out; + } else +#endif + { + off2 = (file->pos + offset); + off = here->in + (off2 - here->out); + } + + if (ws_lseek64(file->fd, off, SEEK_SET) == -1) { + *err = errno; + return -1; + } + fast_seek_reset(file); + + file->raw_pos = off; + file->have = 0; + file->eof = FALSE; + file->seek_pending = FALSE; + file->err = 0; + file->err_info = NULL; + file->avail_in = 0; + +#ifdef HAVE_LIBZ + if (here->compression == ZLIB) { + z_stream *strm = &file->strm; + + inflateReset(strm); + strm->adler = here->data.zlib.adler; + strm->total_out = here->data.zlib.total_out; +#ifdef HAVE_INFLATEPRIME + if (here->data.zlib.bits) { + FILE_F state = file; + int ret = GZ_GETC(); + + if (ret == -1) { + if (state->err == 0) { + /* EOF */ + *err = FTAP_ERR_SHORT_READ; + } else + *err = state->err; + return -1; + } + (void)inflatePrime(strm, here->data.zlib.bits, ret >> (8 - here->data.zlib.bits)); + } +#endif + (void)inflateSetDictionary(strm, here->data.zlib.window, ZLIB_WINSIZE); + file->compression = ZLIB; + } else if (here->compression == GZIP_AFTER_HEADER) { + z_stream *strm = &file->strm; + + inflateReset(strm); + strm->adler = crc32(0L, Z_NULL, 0); + file->compression = ZLIB; + } else +#endif + file->compression = here->compression; + + offset = (file->pos + offset) - off2; + file->pos = off2; + /* g_print("OK! %ld\n", offset); */ + + if (offset) { + file->seek_pending = TRUE; + file->skip = offset; + } + return file->pos + offset; + } + + /* if within raw area while reading, just go there */ + if (file->compression == UNCOMPRESSED && file->pos + offset >= file->raw + && (offset < 0 || offset >= file->have) /* seek only when we don't have that offset in buffer */) + { + if (ws_lseek64(file->fd, offset - file->have, SEEK_CUR) == -1) { + *err = errno; + return -1; + } + file->raw_pos += (offset - file->have); + file->have = 0; + file->eof = FALSE; + file->seek_pending = FALSE; + file->err = 0; + file->err_info = NULL; + file->avail_in = 0; + file->pos += offset; + return file->pos; + } + + /* calculate skip amount, rewinding if needed for back seek when reading */ + if (offset < 0) { + offset += file->pos; + if (offset < 0) { /* before start of file! */ + *err = EINVAL; + return -1; + } + /* rewind, then skip to offset */ + + /* back up and start over */ + if (ws_lseek64(file->fd, file->start, SEEK_SET) == -1) { + *err = errno; + return -1; + } + fast_seek_reset(file); + file->raw_pos = file->start; + gz_reset(file); + } + + /* skip what's in output buffer (one less gzgetc() check) */ + n = (gint64)file->have > offset ? (unsigned)offset : file->have; + file->have -= n; + file->next += n; + file->pos += n; + offset -= n; + + /* request skip (if not zero) */ + if (offset) { + file->seek_pending = TRUE; + file->skip = offset; + } + return file->pos + offset; +} + +/* + * Skip forward the specified number of bytes in the file. + * Currently implemented as a wrapper around file_seek(), + * but if, for example, we ever add support for reading + * sequentially from a pipe, this could instead just skip + * forward by reading the bytes in question. + */ +gboolean +file_skip(FILE_F file, gint64 delta, int *err) +{ + if (file_seek(file, delta, SEEK_CUR, err) == -1) + return FALSE; + return TRUE; +} + +gint64 +file_tell(FILE_F stream) +{ + /* return position */ + return stream->pos + (stream->seek_pending ? stream->skip : 0); +} + +gint64 +file_tell_raw(FILE_F stream) +{ + return stream->raw_pos; +} + +int +file_fstat(FILE_F stream, ws_statb64 *statb, int *err) +{ + if (ws_fstat64(stream->fd, statb) == -1) { + if (err != NULL) + *err = errno; + return -1; + } + return 0; +} + +gboolean +file_iscompressed(FILE_F stream) +{ + return stream->is_compressed; +} + +int +file_read(void *buf, unsigned int len, FILE_F file) +{ + guint got, n; + + /* if len is zero, avoid unnecessary operations */ + if (len == 0) + return 0; + + /* process a skip request */ + if (file->seek_pending) { + file->seek_pending = FALSE; + if (gz_skip(file, file->skip) == -1) + return -1; + } + + /* get len bytes to buf, or less than len if at the end */ + got = 0; + do { + if (file->have) { + /* We have stuff in the output buffer; copy + what we have. */ + n = file->have > len ? len : file->have; + memcpy(buf, file->next, n); + file->next += n; + file->have -= n; + } else if (file->err) { + /* We have nothing in the output buffer, and + we have an error that may not have been + reported yet; that means we can't generate + any more data into the output buffer, so + return an error indication. */ + return -1; + } else if (file->eof && file->avail_in == 0) { + /* We have nothing in the output buffer, and + we're at the end of the input; just return + with what we've gotten so far. */ + break; + } else { + /* We have nothing in the output buffer, and + we can generate more data; get more output, + looking for header if required, and + keep looping to process the new stuff + in the output buffer. */ + if (fill_out_buffer(file) == -1) + return -1; + continue; /* no progress yet -- go back to memcpy() above */ + } + /* update progress */ + len -= n; + buf = (char *)buf + n; + got += n; + file->pos += n; + } while (len); + + return (int)got; +} + +/* + * XXX - this gets a byte, not a character. + */ +int +file_getc(FILE_F file) +{ + unsigned char buf[1]; + int ret; + + /* check that we're reading and that there's no error */ + if (file->err) + return -1; + + /* try output buffer (no need to check for skip request) */ + if (file->have) { + file->have--; + file->pos++; + return *(file->next)++; + } + + ret = file_read(buf, 1, file); + return ret < 1 ? -1 : buf[0]; +} + +char * +file_gets(char *buf, int len, FILE_F file) +{ + guint left, n; + char *str; + unsigned char *eol; + + /* check parameters */ + if (buf == NULL || len < 1) + return NULL; + + /* check that there's no error */ + if (file->err) + return NULL; + + /* process a skip request */ + if (file->seek_pending) { + file->seek_pending = FALSE; + if (gz_skip(file, file->skip) == -1) + return NULL; + } + + /* copy output bytes up to new line or len - 1, whichever comes first -- + append a terminating zero to the string (we don't check for a zero in + the contents, let the user worry about that) */ + str = buf; + left = (unsigned)len - 1; + if (left) do { + /* assure that something is in the output buffer */ + if (file->have == 0) { + /* We have nothing in the output buffer. */ + if (file->err) { + /* We have an error that may not have + been reported yet; that means we + can't generate any more data into + the output buffer, so return an + error indication. */ + return NULL; + } + if (fill_out_buffer(file) == -1) + return NULL; /* error */ + if (file->have == 0) { /* end of file */ + if (buf == str) /* got bupkus */ + return NULL; + break; /* got something -- return it */ + } + } + + /* look for end-of-line in current output buffer */ + n = file->have > left ? left : file->have; + eol = (unsigned char *)memchr(file->next, '\n', n); + if (eol != NULL) + n = (unsigned)(eol - file->next) + 1; + + /* copy through end-of-line, or remainder if not found */ + memcpy(buf, file->next, n); + file->have -= n; + file->next += n; + file->pos += n; + left -= n; + buf += n; + } while (left && eol == NULL); + + /* found end-of-line or out of space -- terminate string and return it */ + buf[0] = 0; + return str; +} + +int +file_eof(FILE_F file) +{ + /* return end-of-file state */ + return (file->eof && file->avail_in == 0 && file->have == 0); +} + +/* + * Routine to return a Wiretap error code (0 for no error, an errno + * for a file error, or a FTAP_ERR_ code for other errors) for an + * I/O stream. Also returns an error string for some errors. + */ +int +file_error(FILE_F fh, gchar **err_info) +{ + if (fh->err!=0 && err_info) { + /* g_strdup() returns NULL for NULL argument */ + *err_info = g_strdup(fh->err_info); + } + return fh->err; +} + +void +file_clearerr(FILE_F stream) +{ + /* clear error and end-of-file */ + stream->err = 0; + stream->err_info = NULL; + stream->eof = FALSE; +} + +void +file_fdclose(FILE_F file) +{ + ws_close(file->fd); + file->fd = -1; +} + +gboolean +file_fdreopen(FILE_F file, const char *path) +{ + int fd; + + if ((fd = ws_open(path, O_RDONLY|O_BINARY, 0000)) == -1) + return FALSE; + file->fd = fd; + return TRUE; +} + +void +file_close(FILE_F file) +{ + int fd = file->fd; + + /* free memory and close file */ + if (file->size) { +#ifdef HAVE_LIBZ + inflateEnd(&(file->strm)); +#endif + g_free(file->out); + g_free(file->in); + } + g_free(file->fast_seek_cur); + file->err = 0; + file->err_info = NULL; + g_free(file); + /* + * If fd is -1, somebody's done a file_closefd() on us, so + * we don't need to close the FD itself, and shouldn't do + * so. + */ + if (fd != -1) + ws_close(fd); +} + +#ifdef HAVE_LIBZ +/* internal gzip file state data structure for writing */ +struct wtap_writer { + int fd; /* file descriptor */ + gint64 pos; /* current position in uncompressed data */ + guint size; /* buffer size, zero if not allocated yet */ + guint want; /* requested buffer size, default is GZBUFSIZE */ + unsigned char *in; /* input buffer */ + unsigned char *out; /* output buffer (double-sized when reading) */ + unsigned char *next; /* next output data to deliver or write */ + int level; /* compression level */ + int strategy; /* compression strategy */ + int err; /* error code */ + /* zlib deflate stream */ + z_stream strm; /* stream structure in-place (not a pointer) */ +}; + +GZWFILE_T +gzwfile_open(const char *path) +{ + int fd; + GZWFILE_T state; + int save_errno; + + fd = ws_open(path, O_BINARY|O_WRONLY|O_CREAT|O_TRUNC, 0666); + if (fd == -1) + return NULL; + state = gzwfile_fdopen(fd); + if (state == NULL) { + save_errno = errno; + close(fd); + errno = save_errno; + } + return state; +} + +GZWFILE_T +gzwfile_fdopen(int fd) +{ + GZWFILE_T state; + + /* allocate wtap_writer structure to return */ + state = (GZWFILE_T)g_try_malloc(sizeof *state); + if (state == NULL) + return NULL; + state->fd = fd; + state->size = 0; /* no buffers allocated yet */ + state->want = GZBUFSIZE; /* requested buffer size */ + + state->level = Z_DEFAULT_COMPRESSION; + state->strategy = Z_DEFAULT_STRATEGY; + + /* initialize stream */ + state->err = Z_OK; /* clear error */ + state->pos = 0; /* no uncompressed data yet */ + state->strm.avail_in = 0; /* no input data yet */ + + /* return stream */ + return state; +} + +/* Initialize state for writing a gzip file. Mark initialization by setting + state->size to non-zero. Return -1, and set state->err, on failure; + return 0 on success. */ +static int +gz_init(GZWFILE_T state) +{ + int ret; + z_streamp strm = &(state->strm); + + /* allocate input and output buffers */ + state->in = (unsigned char *)g_try_malloc(state->want); + state->out = (unsigned char *)g_try_malloc(state->want); + if (state->in == NULL || state->out == NULL) { + g_free(state->out); + g_free(state->in); + state->err = ENOMEM; + return -1; + } + + /* allocate deflate memory, set up for gzip compression */ + strm->zalloc = Z_NULL; + strm->zfree = Z_NULL; + strm->opaque = Z_NULL; + ret = deflateInit2(strm, state->level, Z_DEFLATED, + 15 + 16, 8, state->strategy); + if (ret != Z_OK) { + g_free(state->out); + g_free(state->in); + if (ret == Z_MEM_ERROR) { + /* This means "not enough memory". */ + state->err = ENOMEM; + } else { + /* This "shouldn't happen". */ + state->err = FTAP_ERR_INTERNAL; + } + return -1; + } + + /* mark state as initialized */ + state->size = state->want; + + /* initialize write buffer */ + strm->avail_out = state->size; + strm->next_out = state->out; + state->next = strm->next_out; + return 0; +} + +/* Compress whatever is at avail_in and next_in and write to the output file. + Return -1, and set state->err, if there is an error writing to the output + file; return 0 on success. + flush is assumed to be a valid deflate() flush value. If flush is Z_FINISH, + then the deflate() state is reset to start a new gzip stream. */ +static int +gz_comp(GZWFILE_T state, int flush) +{ + int ret; + ssize_t got; + ptrdiff_t have; + z_streamp strm = &(state->strm); + + /* allocate memory if this is the first time through */ + if (state->size == 0 && gz_init(state) == -1) + return -1; + + /* run deflate() on provided input until it produces no more output */ + ret = Z_OK; + do { + /* write out current buffer contents if full, or if flushing, but if + doing Z_FINISH then don't write until we get to Z_STREAM_END */ + if (strm->avail_out == 0 || (flush != Z_NO_FLUSH && + (flush != Z_FINISH || ret == Z_STREAM_END))) { + have = strm->next_out - state->next; + if (have) { + got = write(state->fd, state->next, (unsigned int)have); + if (got < 0) { + state->err = errno; + return -1; + } + if ((ptrdiff_t)got != have) { + state->err = FTAP_ERR_SHORT_WRITE; + return -1; + } + } + if (strm->avail_out == 0) { + strm->avail_out = state->size; + strm->next_out = state->out; + } + state->next = strm->next_out; + } + + /* compress */ + have = strm->avail_out; + ret = deflate(strm, flush); + if (ret == Z_STREAM_ERROR) { + /* This "shouldn't happen". */ + state->err = FTAP_ERR_INTERNAL; + return -1; + } + have -= strm->avail_out; + } while (have); + + /* if that completed a deflate stream, allow another to start */ + if (flush == Z_FINISH) + deflateReset(strm); + + /* all done, no errors */ + return 0; +} + +/* Write out len bytes from buf. Return 0, and set state->err, on + failure or on an attempt to write 0 bytes (in which case state->err + is Z_OK); return the number of bytes written on success. */ +unsigned +gzwfile_write(GZWFILE_T state, const void *buf, guint len) +{ + guint put = len; + guint n; + z_streamp strm; + + strm = &(state->strm); + + /* check that there's no error */ + if (state->err != Z_OK) + return 0; + + /* if len is zero, avoid unnecessary operations */ + if (len == 0) + return 0; + + /* allocate memory if this is the first time through */ + if (state->size == 0 && gz_init(state) == -1) + return 0; + + /* for small len, copy to input buffer, otherwise compress directly */ + if (len < state->size) { + /* copy to input buffer, compress when full */ + do { + if (strm->avail_in == 0) + strm->next_in = state->in; + n = state->size - strm->avail_in; + if (n > len) + n = len; + memcpy(strm->next_in + strm->avail_in, buf, n); + strm->avail_in += n; + state->pos += n; + buf = (const char *)buf + n; + len -= n; + if (len && gz_comp(state, Z_NO_FLUSH) == -1) + return 0; + } while (len); + } + else { + /* consume whatever's left in the input buffer */ + if (strm->avail_in && gz_comp(state, Z_NO_FLUSH) == -1) + return 0; + + /* directly compress user buffer to file */ + strm->avail_in = len; + strm->next_in = (Bytef *)buf; + state->pos += len; + if (gz_comp(state, Z_NO_FLUSH) == -1) + return 0; + } + + /* input was all buffered or compressed (put will fit in int) */ + return (int)put; +} + +/* Flush out what we've written so far. Returns -1, and sets state->err, + on failure; returns 0 on success. */ +int +gzwfile_flush(GZWFILE_T state) +{ + /* check that there's no error */ + if (state->err != Z_OK) + return -1; + + /* compress remaining data with Z_SYNC_FLUSH */ + gz_comp(state, Z_SYNC_FLUSH); + if (state->err != Z_OK) + return -1; + return 0; +} + +/* Flush out all data written, and close the file. Returns a Wiretap + error on failure; returns 0 on success. */ +int +gzwfile_close(GZWFILE_T state) +{ + int ret = 0; + + /* flush, free memory, and close file */ + if (gz_comp(state, Z_FINISH) == -1 && ret == 0) + ret = state->err; + (void)deflateEnd(&(state->strm)); + g_free(state->out); + g_free(state->in); + state->err = Z_OK; + if (close(state->fd) == -1 && ret == 0) + ret = errno; + g_free(state); + return ret; +} + +int +gzwfile_geterr(GZWFILE_T state) +{ + return state->err; +} +#endif diff --git a/filetap/file_wrappers.h b/filetap/file_wrappers.h new file mode 100644 index 0000000000..6bad094738 --- /dev/null +++ b/filetap/file_wrappers.h @@ -0,0 +1,61 @@ +/* file_wrappers.h + * + * $Id$ + * + * Wiretap Library + * Copyright (c) 1998 by Gilbert Ramirez + * + * 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. + */ + +#ifndef __FILE_H__ +#define __FILE_H__ + +#include +#include +#include +#include "ws_symbol_export.h" + +extern FILE_F file_open(const char *path); +extern FILE_F file_fdopen(int fildes); +extern void file_set_random_access(FILE_F stream, gboolean random_flag, GPtrArray *seek); +WS_DLL_PUBLIC gint64 file_seek(FILE_F stream, gint64 offset, int whence, int *err); +extern gboolean file_skip(FILE_F file, gint64 delta, int *err); +WS_DLL_PUBLIC gint64 file_tell(FILE_F stream); +extern gint64 file_tell_raw(FILE_F stream); +extern int file_fstat(FILE_F stream, ws_statb64 *statb, int *err); +extern gboolean file_iscompressed(FILE_F stream); +WS_DLL_PUBLIC int file_read(void *buf, unsigned int count, FILE_F file); +WS_DLL_PUBLIC int file_getc(FILE_F stream); +WS_DLL_PUBLIC char *file_gets(char *buf, int len, FILE_F stream); +WS_DLL_PUBLIC int file_eof(FILE_F stream); +WS_DLL_PUBLIC int file_error(FILE_F fh, gchar **err_info); +extern void file_clearerr(FILE_F stream); +extern void file_fdclose(FILE_F file); +extern int file_fdreopen(FILE_F file, const char *path); +extern void file_close(FILE_F file); + +#ifdef HAVE_LIBZ +typedef struct wtap_writer *GZWFILE_T; + +extern GZWFILE_T gzwfile_open(const char *path); +extern GZWFILE_T gzwfile_fdopen(int fd); +extern guint gzwfile_write(GZWFILE_T state, const void *buf, guint len); +extern int gzwfile_flush(GZWFILE_T state); +extern int gzwfile_close(GZWFILE_T state); +extern int gzwfile_geterr(GZWFILE_T state); +#endif /* HAVE_LIBZ */ + +#endif /* __FILE_H__ */ diff --git a/filetap/ftap-int.h b/filetap/ftap-int.h new file mode 100644 index 0000000000..32026bc29f --- /dev/null +++ b/filetap/ftap-int.h @@ -0,0 +1,252 @@ +/* ftap-int.h + * + * $Id$ + * + * Filetap Library + * Copyright (c) 1998 by Gilbert Ramirez + * + * 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. + */ + +#ifndef __FTAP_INT_H__ +#define __FTAP_INT_H__ + +#ifdef HAVE_SYS_TIME_H +#include +#endif + +#include +#include +#include + +#ifdef HAVE_WINSOCK2_H +#include +#endif + +#include + +#include "ftap.h" + +WS_DLL_PUBLIC +int ftap_fstat(ftap *fth, ws_statb64 *statb, int *err); + +typedef gboolean (*subtype_read_func)(struct ftap*, int*, char**, gint64*); +typedef gboolean (*subtype_seek_read_func)(struct ftap*, gint64, + Buffer *buf, + int, int *, char **); +/** + * Struct holding data of the currently read file. + */ +struct ftap { + FILE_F fh; + FILE_F random_fh; /**< Secondary FILE_T for random access */ + int file_type_subtype; + guint snapshot_length; + struct Buffer *frame_buffer; + void *priv; + + subtype_read_func subtype_read; + subtype_seek_read_func subtype_seek_read; + void (*subtype_sequential_close)(struct ftap*); + void (*subtype_close)(struct ftap*); + int file_encap; /* per-file, for those + * file formats that have + * per-file encapsulation + * types + */ + GPtrArray *fast_seek; +}; + +extern gint ftap_num_file_types; + +#include + +/* Macros to byte-swap possibly-unaligned 64-bit, 32-bit and 16-bit quantities; + * they take a pointer to the quantity, and byte-swap it in place. + */ +#define PBSWAP64(p) \ + { \ + guint8 tmp; \ + tmp = (p)[7]; \ + (p)[7] = (p)[0]; \ + (p)[0] = tmp; \ + tmp = (p)[6]; \ + (p)[6] = (p)[1]; \ + (p)[1] = tmp; \ + tmp = (p)[5]; \ + (p)[5] = (p)[2]; \ + (p)[2] = tmp; \ + tmp = (p)[4]; \ + (p)[4] = (p)[3]; \ + (p)[3] = tmp; \ + } +#define PBSWAP32(p) \ + { \ + guint8 tmp; \ + tmp = (p)[3]; \ + (p)[3] = (p)[0]; \ + (p)[0] = tmp; \ + tmp = (p)[2]; \ + (p)[2] = (p)[1]; \ + (p)[1] = tmp; \ + } +#define PBSWAP16(p) \ + { \ + guint8 tmp; \ + tmp = (p)[1]; \ + (p)[1] = (p)[0]; \ + (p)[0] = tmp; \ + } + + +/* Pointer routines to put items out in a particular byte order. + * These will work regardless of the byte alignment of the pointer. + */ + +#ifndef phtons +#define phtons(p, v) \ + { \ + (p)[0] = (guint8)((v) >> 8); \ + (p)[1] = (guint8)((v) >> 0); \ + } +#endif + +#ifndef phton24 +#define phton24(p, v) \ + { \ + (p)[0] = (guint8)((v) >> 16); \ + (p)[1] = (guint8)((v) >> 8); \ + (p)[2] = (guint8)((v) >> 0); \ + } +#endif + +#ifndef phtonl +#define phtonl(p, v) \ + { \ + (p)[0] = (guint8)((v) >> 24); \ + (p)[1] = (guint8)((v) >> 16); \ + (p)[2] = (guint8)((v) >> 8); \ + (p)[3] = (guint8)((v) >> 0); \ + } +#endif + +#ifndef phtonll +#define phtonll(p, v) \ + { \ + (p)[0] = (guint8)((v) >> 56); \ + (p)[1] = (guint8)((v) >> 48); \ + (p)[2] = (guint8)((v) >> 40); \ + (p)[3] = (guint8)((v) >> 32); \ + (p)[4] = (guint8)((v) >> 24); \ + (p)[5] = (guint8)((v) >> 16); \ + (p)[6] = (guint8)((v) >> 8); \ + (p)[7] = (guint8)((v) >> 0); \ + } +#endif + +#ifndef phtoles +#define phtoles(p, v) \ + { \ + (p)[0] = (guint8)((v) >> 0); \ + (p)[1] = (guint8)((v) >> 8); \ + } +#endif + +#ifndef phtolel +#define phtolel(p, v) \ + { \ + (p)[0] = (guint8)((v) >> 0); \ + (p)[1] = (guint8)((v) >> 8); \ + (p)[2] = (guint8)((v) >> 16); \ + (p)[3] = (guint8)((v) >> 24); \ + } +#endif + +#ifndef phtolell +#define phtolell(p, v) \ + { \ + (p)[0] = (guint8)((v) >> 0); \ + (p)[1] = (guint8)((v) >> 8); \ + (p)[2] = (guint8)((v) >> 16); \ + (p)[3] = (guint8)((v) >> 24); \ + (p)[4] = (guint8)((v) >> 32); \ + (p)[5] = (guint8)((v) >> 40); \ + (p)[6] = (guint8)((v) >> 48); \ + (p)[7] = (guint8)((v) >> 56); \ + } +#endif + +#define ftap_file_read_unknown_bytes(target, num_bytes, fh, err, err_info) \ + G_STMT_START \ + { \ + int _bytes_read; \ + _bytes_read = file_read((target), (num_bytes), (fh)); \ + if (_bytes_read != (int) (num_bytes)) { \ + *(err) = file_error((fh), (err_info)); \ + return FALSE; \ + } \ + } \ + G_STMT_END + +#define ftap_file_read_expected_bytes(target, num_bytes, fh, err, err_info) \ + G_STMT_START \ + { \ + int _bytes_read; \ + _bytes_read = file_read((target), (num_bytes), (fh)); \ + if (_bytes_read != (int) (num_bytes)) { \ + *(err) = file_error((fh), (err_info)); \ + if (*(err) == 0 && _bytes_read > 0) { \ + *(err) = FTAP_ERR_SHORT_READ; \ + } \ + return FALSE; \ + } \ + } \ + G_STMT_END + +/* glib doesn't have g_ptr_array_len of all things!*/ +#ifndef g_ptr_array_len +#define g_ptr_array_len(a) ((a)->len) +#endif + +/*** get GSList of all compressed file extensions ***/ +GSList *ftap_get_compressed_file_extensions(void); + +/* + * 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); + +#endif /* __FTAP_INT_H__ */ + +/* + * Editor modelines + * + * Local Variables: + * c-basic-offset: 8 + * tab-width: 8 + * indent-tabs-mode: t + * End: + * + * ex: set shiftwidth=8 tabstop=8 noexpandtab: + * :indentSize=8:tabSize=8:noTabs=false: + */ diff --git a/filetap/ftap.c b/filetap/ftap.c new file mode 100644 index 0000000000..35b319c441 --- /dev/null +++ b/filetap/ftap.c @@ -0,0 +1,480 @@ +/* ftap.c + * + * $Id$ + * + * Filetap Library + * Copyright (c) 1998 by Gilbert Ramirez + * + * 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 +#include + +#ifdef HAVE_SYS_TYPES_H +#include +#endif + +#ifdef HAVE_UNISTD_H +#include +#endif + +#ifdef HAVE_LIBZ +#include +#endif + +#include "ftap-int.h" + +#include "file_wrappers.h" +#include +#include "buffer.h" + +#ifdef HAVE_PLUGINS + +#include + +/* + * 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, /* XXX - phdr*/NULL, buf, len, + err, err_info); +} diff --git a/filetap/ftap.h b/filetap/ftap.h new file mode 100644 index 0000000000..878ff5e823 --- /dev/null +++ b/filetap/ftap.h @@ -0,0 +1,389 @@ +/* ftap.h + * + * $Id$ + * + * Filetap Library + * Copyright (c) 1998 by Gilbert Ramirez + * + * 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. + */ + +#ifndef __FTAP_H__ +#define __FTAP_H__ + +#ifdef HAVE_SYS_TIME_H +#include +#endif + +#include +#include +#include +#include +#include "ws_symbol_export.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* Encapsulation types. Choose names that truly reflect + * what is contained in the packet trace file. + * + * FTAP_ENCAP_PER_RECORD is a value passed to "ftap_dump_open()" or + * "ftap_dump_fd_open()" to indicate that there is no single encapsulation + * type for all records in the file; this may cause those routines to + * fail if the file format being written can't support that. + * It's also returned by "ftap_file_encap()" for capture files that + * don't have a single encapsulation type for all packets in the file. + * + * FTAP_ENCAP_UNKNOWN is returned by "ftap_pcap_encap_to_ftap_encap()" + * if it's handed an unknown encapsulation. + * + */ +#define FTAP_ENCAP_PER_RECORD -1 +#define FTAP_ENCAP_UNKNOWN 0 + + /* After adding new item here, please also add new item to encap_table_base array */ + +#define FTAP_NUM_ENCAP_TYPES ftap_get_num_encap_types() + +/* File types/subtypes that can be read by filetap. */ +#define FTAP_FILE_TYPE_SUBTYPE_UNKNOWN 0 + +#define FTAP_NUM_FILE_TYPES_SUBTYPES ftap_get_num_file_types_subtypes() + +/* + * Maximum record size we'll support. + * 65535 is the largest snapshot length that libpcap supports, so we + * use that. + */ +#define FTAP_MAX_RECORD_SIZE 65535 + +typedef struct ftap ftap; +typedef struct ftap_dumper ftap_dumper; + +typedef struct ftap_reader *FILE_F; + +/* + * For registering extensions used for capture file formats. + * + * These items are used in dialogs for opening files, so that + * the user can ask to see all capture files (as identified + * by file extension) or particular types of capture files. + * + * Each file type has a description and a list of extensions the file + * might have. Some file types aren't real file types, they're + * just generic types, such as "text file" or "XML file", that can + * be used for, among other things, captures we can read, or for + * extensions such as ".cap" that were unimaginatively chosen by + * several different sniffers for their file formats. + */ +struct filetap_extension_info { + /* the file type name */ + const char *name; + + /* a semicolon-separated list of file extensions used for this type */ + const char *extensions; +}; + +/* + * For registering file types that we can open. + * + * Each file type has an open routine and an optional list of extensions + * the file might have. + * + * The open routine should return: + * + * -1 on an I/O error; + * + * 1 if the file it's reading is one of the types it handles; + * + * 0 if the file it's reading isn't the type it handles. + * + * If the routine handles this type of file, it should set the "file_type" + * field in the "struct ftap" to the type of the file. + * + * Note that the routine does not have to free the private data pointer on + * error. The caller takes care of that by calling ftap_close on error. + * (See https://bugs.wireshark.org/bugzilla/show_bug.cgi?id=8518) + * + * However, the caller does have to free the private data pointer when + * returning 0, since the next file type will be called and will likely + * just overwrite the pointer. + */ + +/* + * Some file formats have defined magic numbers at fixed offsets from + * the beginning of the file; those routines should return 1 if and + * only if the file has the magic number at that offset. (pcap-ng + * is a bit of a special case, as it has both the Section Header Block + * type field and its byte-order magic field; it checks for both.) + * Those file formats do not require a file name extension in order + * to recognize them or to avoid recognizing other file types as that + * type, and have no extensions specified for them. + */ +typedef int (*ftap_open_routine_t)(struct ftap*, int *, char **); + +/* + * Some file formats don't have defined magic numbers at fixed offsets, + * so a heuristic is required. If that file format has any file name + * extensions used for it, a list of those extensions should be + * specified, so that, if the name of the file being opened has an + * extension, the file formats that use that extension are tried before + * the ones that don't, to handle the case where a file of one type + * might be recognized by the heuristics for a different file type. + */ +struct ftap_heuristic_open_info { + ftap_open_routine_t open_routine; + const char *extensions; +}; + +struct ftap_file_type_subtype_info { + /* the file type name */ + /* should be NULL for all "pseudo" types that are only internally used and not read/writeable */ + const char *name; + + /* the file type short name, used as a shortcut for the command line tools */ + /* should be NULL for all "pseudo" types that are only internally used and not read/writeable */ + const char *short_name; + + /* the default file extension, used to save this type */ + /* should be NULL if no default extension is known */ + const char *default_file_extension; + + /* a semicolon-separated list of additional file extensions */ + /* used for this type */ + /* should be NULL if no extensions, or no extensions other */ + /* than the default extension, are known */ + const char *additional_file_extensions; +}; + + +/** On failure, "ftap_open_offline()" returns NULL, and puts into the + * "int" pointed to by its second argument: + * + * @param filename Name of the file to open + * @param err a positive "errno" value if the capture file can't be opened; + * a negative number, indicating the type of error, on other failures. + * @param err_info for some errors, a string giving more details of + * the error + * @param do_random TRUE if random access to the file will be done, + * FALSE if not + */ +WS_DLL_PUBLIC +struct ftap* ftap_open_offline(const char *filename, int *err, + gchar **err_info, gboolean do_random); + +/** + * If we were compiled with zlib and we're at EOF, unset EOF so that + * ftap_read/gzread has a chance to succeed. This is necessary if + * we're tailing a file. + */ +WS_DLL_PUBLIC +void ftap_cleareof(ftap *fth); + +/** Returns TRUE if read was successful. FALSE if failure. data_offset is + * set to the offset in the file where the data for the read packet is + * located. */ +WS_DLL_PUBLIC +gboolean ftap_read(ftap *fth, int *err, gchar **err_info, + gint64 *data_offset); + +WS_DLL_PUBLIC +gboolean ftap_seek_read (ftap *fth, gint64 seek_off, + Buffer *buf, int len, + int *err, gchar **err_info); + +/*** get various information snippets about the current file ***/ + +/** Return an approximation of the amount of data we've read sequentially + * from the file so far. */ +WS_DLL_PUBLIC +gint64 ftap_read_so_far(ftap *fth); +WS_DLL_PUBLIC +gint64 ftap_file_size(ftap *fth, int *err); +WS_DLL_PUBLIC +gboolean ftap_iscompressed(ftap *fth); +WS_DLL_PUBLIC +guint ftap_snapshot_length(ftap *fth); /* per file */ +WS_DLL_PUBLIC +int ftap_file_type_subtype(ftap *fth); +WS_DLL_PUBLIC +int ftap_file_encap(ftap *fth); + +/*** close the file descriptors for the current file ***/ +WS_DLL_PUBLIC +void ftap_fdclose(ftap *fth); + +/*** reopen the random file descriptor for the current file ***/ +WS_DLL_PUBLIC +gboolean ftap_fdreopen(ftap *fth, const char *filename, int *err); + +/*** close the current file ***/ +WS_DLL_PUBLIC +void ftap_sequential_close(ftap *fth); +WS_DLL_PUBLIC +void ftap_close(ftap *fth); + +/*** various string converter functions ***/ +WS_DLL_PUBLIC +const char *ftap_file_type_subtype_string(int file_type_subtype); +WS_DLL_PUBLIC +const char *ftap_file_type_subtype_short_string(int file_type_subtype); +WS_DLL_PUBLIC +int ftap_short_string_to_file_type_subtype(const char *short_name); + +/*** various file extension functions ***/ +WS_DLL_PUBLIC +GSList *ftap_get_all_file_extensions_list(void); +WS_DLL_PUBLIC +const char *ftap_default_file_extension(int filetype); +WS_DLL_PUBLIC +GSList *ftap_get_file_extensions_list(int filetype, gboolean include_compressed); +WS_DLL_PUBLIC +void ftap_free_extensions_list(GSList *extensions); + +WS_DLL_PUBLIC +const char *ftap_encap_string(int encap); +WS_DLL_PUBLIC +const char *ftap_encap_short_string(int encap); +WS_DLL_PUBLIC +int ftap_short_string_to_encap(const char *short_name); + +WS_DLL_PUBLIC +const char *ftap_strerror(int err); + +/*** get available number of file types and encapsulations ***/ +WS_DLL_PUBLIC +int ftap_get_num_file_type_extensions(void); +WS_DLL_PUBLIC +int ftap_get_num_encap_types(void); +WS_DLL_PUBLIC +int ftap_get_num_file_types_subtypes(void); + +/*** get information for file type extension ***/ +WS_DLL_PUBLIC +const char *ftap_get_file_extension_type_name(int extension_type); +WS_DLL_PUBLIC +GSList *ftap_get_file_extension_type_extensions(guint extension_type); + +/*** dynamically register new file types and encapsulations ***/ +WS_DLL_PUBLIC +void ftap_register_plugin_types(void); +WS_DLL_PUBLIC +void register_all_filetap_modules(void); +WS_DLL_PUBLIC +void ftap_register_file_type_extension(const struct filetap_extension_info *ei); +WS_DLL_PUBLIC +void ftap_register_magic_number_open_routine(ftap_open_routine_t open_routine); +WS_DLL_PUBLIC +void ftap_register_heuristic_open_info(const struct ftap_heuristic_open_info *oi); +WS_DLL_PUBLIC +int ftap_register_file_type_subtypes(const struct ftap_file_type_subtype_info* fi); +WS_DLL_PUBLIC +int ftap_register_encap_type(const char* name, const char* short_name); + + +/** + * Filetap error codes. + */ +#define FTAP_ERR_NOT_REGULAR_FILE -1 + /** The file being opened for reading isn't a plain file (or pipe) */ + +#define FTAP_ERR_RANDOM_OPEN_PIPE -2 + /** The file is being opened for random access and it's a pipe */ + +#define FTAP_ERR_FILE_UNKNOWN_FORMAT -3 + /** The file being opened is not a capture file in a known format */ + +#define FTAP_ERR_UNSUPPORTED -4 + /** Supported file type, but there's something in the file we + can't support */ + +#define FTAP_ERR_CANT_WRITE_TO_PIPE -5 + /** Filetap can't save to a pipe in the specified format */ + +#define FTAP_ERR_CANT_OPEN -6 + /** The file couldn't be opened, reason unknown */ + +#define FTAP_ERR_UNSUPPORTED_FILE_TYPE -7 + /** Filetap can't save files in the specified format */ + +#define FTAP_ERR_UNSUPPORTED_ENCAP -8 + /** Filetap can't read or save files in the specified format with the + specified encapsulation */ + +#define FTAP_ERR_ENCAP_PER_RECORD_UNSUPPORTED -9 + /** The specified format doesn't support per-packet encapsulations */ + +#define FTAP_ERR_CANT_CLOSE -10 + /** The file couldn't be closed, reason unknown */ + +#define FTAP_ERR_CANT_READ -11 + /** An attempt to read failed, reason unknown */ + +#define FTAP_ERR_SHORT_READ -12 + /** An attempt to read read less data than it should have */ + +#define FTAP_ERR_BAD_FILE -13 + /** The file appears to be damaged or corrupted or otherwise bogus */ + +#define FTAP_ERR_SHORT_WRITE -14 + /** An attempt to write wrote less data than it should have */ + +#define FTAP_ERR_UNC_TRUNCATED -15 + /** Compressed data was oddly truncated */ + +#define FTAP_ERR_UNC_OVERFLOW -16 + /** Uncompressing data would overflow buffer */ + +#define FTAP_ERR_UNC_BAD_OFFSET -17 + /** LZ77 compressed data has bad offset to string */ + +#define FTAP_ERR_RANDOM_OPEN_STDIN -18 + /** We're trying to open the standard input for random access */ + +#define FTAP_ERR_COMPRESSION_NOT_SUPPORTED -19 + /* The filetype doesn't support output compression */ + +#define FTAP_ERR_CANT_SEEK -20 + /** An attempt to seek failed, reason unknown */ + +#define FTAP_ERR_CANT_SEEK_COMPRESSED -21 + /** An attempt to seek on a compressed stream */ + +#define FTAP_ERR_DECOMPRESS -22 + /** Error decompressing */ + +#define FTAP_ERR_INTERNAL -23 + /** "Shouldn't happen" internal errors */ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* __FTAP_H__ */ + +/* + * Editor modelines - http://www.wireshark.org/tools/modelines.html + * + * Local variables: + * c-basic-offset: 4 + * tab-width: 8 + * indent-tabs-mode: nil + * End: + * + * vi: set shiftwidth=4 tabstop=8 expandtab: + * :indentSize=4:tabSize=8:noTabs=true: + */ -- cgit v1.2.3