summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--filetap/CMakeLists.txt84
-rw-r--r--filetap/Makefile.am84
-rw-r--r--filetap/Makefile.common42
-rw-r--r--filetap/Makefile.nmake59
-rw-r--r--filetap/buffer.c163
-rw-r--r--filetap/buffer.h74
-rw-r--r--filetap/file_access.c993
-rw-r--r--filetap/file_wrappers.c1649
-rw-r--r--filetap/file_wrappers.h61
-rw-r--r--filetap/ftap-int.h252
-rw-r--r--filetap/ftap.c480
-rw-r--r--filetap/ftap.h389
-rw-r--r--image/filetap.rc.in34
13 files changed, 4364 insertions, 0 deletions
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 <gerald@wireshark.org>
+# 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 <gerald@wireshark.org>
+# 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 <gerald@wireshark.org>
+# 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 <win32.mak>
+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 <gram@alumni.rice.edu>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <glib.h>
+
+#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 <gram@alumni.rice.edu>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#ifndef __W_BUFFER_H__
+#define __W_BUFFER_H__
+
+#include <glib.h>
+#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 <gram@alumni.rice.edu>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include <errno.h>
+
+#include <wsutil/file_util.h>
+
+#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 <gram@alumni.rice.edu>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+/* 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 <unistd.h>
+#endif /* HAVE_UNISTD_H */
+
+#include <errno.h>
+#include <stdio.h>
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif /* HAVE_FCNTL_H */
+#include <string.h>
+#include "ftap-int.h"
+#include "file_wrappers.h"
+#include <wsutil/file_util.h>
+
+#ifdef HAVE_LIBZ
+#include <zlib.h>
+#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 <gram@alumni.rice.edu>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef __FILE_H__
+#define __FILE_H__
+
+#include <glib.h>
+#include <ftap.h>
+#include <wsutil/file_util.h>
+#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 <gram@alumni.rice.edu>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef __FTAP_INT_H__
+#define __FTAP_INT_H__
+
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+
+#include <glib.h>
+#include <stdio.h>
+#include <time.h>
+
+#ifdef HAVE_WINSOCK2_H
+#include <winsock2.h>
+#endif
+
+#include <wsutil/file_util.h>
+
+#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 <wsutil/pint.h>
+
+/* 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 <gram@alumni.rice.edu>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "config.h"
+
+#include <string.h>
+#include <errno.h>
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#ifdef HAVE_LIBZ
+#include <zlib.h>
+#endif
+
+#include "ftap-int.h"
+
+#include "file_wrappers.h"
+#include <wsutil/file_util.h>
+#include "buffer.h"
+
+#ifdef HAVE_PLUGINS
+
+#include <wsutil/plugins.h>
+
+/*
+ * List of wiretap plugins.
+ */
+typedef struct {
+ void (*register_ftap_module)(void); /* routine to call to register a wiretap module */
+} ftap_plugin;
+
+static GSList *ftap_plugins = NULL;
+
+/*
+ * Callback for each plugin found.
+ */
+static gboolean
+check_for_ftap_plugin(GModule *handle)
+{
+ gpointer gp;
+ void (*register_ftap_module)(void);
+ ftap_plugin *plugin;
+
+ /*
+ * Do we have a register_ftap_module routine?
+ */
+ if (!g_module_symbol(handle, "register_ftap_module", &gp)) {
+ /* No, so this isn't a filetap module plugin. */
+ return FALSE;
+ }
+
+ /*
+ * Yes - this plugin includes one or more filetap modules.
+ */
+ register_ftap_module = (void (*)(void))gp;
+
+ /*
+ * Add this one to the list of wiretap module plugins.
+ */
+ plugin = (ftap_plugin *)g_malloc(sizeof (ftap_plugin));
+ plugin->register_ftap_module = register_ftap_module;
+ ftap_plugins = g_slist_append(ftap_plugins, plugin);
+ return TRUE;
+}
+
+void
+ftap_register_plugin_types(void)
+{
+ add_plugin_type("file format", check_for_ftap_plugin);
+}
+
+static void
+register_ftap_module_plugin(gpointer data, gpointer user_data _U_)
+{
+ ftap_plugin *plugin = (ftap_plugin *)data;
+
+ (plugin->register_ftap_module)();
+}
+
+/*
+ * For all wiretap module plugins, call their register routines.
+ */
+void
+register_all_filetap_modules(void)
+{
+ g_slist_foreach(ftap_plugins, register_ftap_module_plugin, NULL);
+}
+#endif /* HAVE_PLUGINS */
+
+/*
+ * Return the size of the file, as reported by the OS.
+ * (gint64, in case that's 64 bits.)
+ */
+gint64
+ftap_file_size(ftap *fth, int *err)
+{
+ ws_statb64 statb;
+
+ if (file_fstat((fth->fh == NULL) ? fth->random_fh : fth->fh,
+ &statb, err) == -1)
+ return -1;
+ return statb.st_size;
+}
+
+/*
+ * Do an fstat on the file.
+ */
+int
+ftap_fstat(ftap *fth, ws_statb64 *statb, int *err)
+{
+ if (file_fstat((fth->fh == NULL) ? fth->random_fh : fth->fh,
+ statb, err) == -1)
+ return -1;
+ return 0;
+}
+
+int
+ftap_file_type_subtype(ftap *fth)
+{
+ return fth->file_type_subtype;
+}
+
+gboolean
+ftap_iscompressed(ftap *fth)
+{
+ return file_iscompressed((fth->fh == NULL) ? fth->random_fh : fth->fh);
+}
+
+guint
+ftap_snapshot_length(ftap *fth)
+{
+ return fth->snapshot_length;
+}
+
+int
+ftap_file_encap(ftap *fth)
+{
+ return fth->file_encap;
+}
+
+/* Table of the encapsulation types we know about. */
+struct encap_type_info {
+ const char *name;
+ const char *short_name;
+};
+
+static struct encap_type_info encap_table_base[] = {
+ /* FTAP_ENCAP_UNKNOWN */
+ { "Unknown", "unknown" },
+};
+
+WS_DLL_LOCAL
+gint ftap_num_encap_types = sizeof(encap_table_base) / sizeof(struct encap_type_info);
+static GArray* encap_table_arr = NULL;
+
+#define encap_table_entry(encap) \
+ g_array_index(encap_table_arr, struct encap_type_info, encap)
+
+static void ftap_init_encap_types(void) {
+
+ if (encap_table_arr) return;
+
+ encap_table_arr = g_array_new(FALSE,TRUE,sizeof(struct encap_type_info));
+
+ g_array_append_vals(encap_table_arr,encap_table_base,ftap_num_encap_types);
+}
+
+int ftap_get_num_encap_types(void) {
+ ftap_init_encap_types();
+ return ftap_num_encap_types;
+}
+
+
+int ftap_register_encap_type(const char* name, const char* short_name) {
+ struct encap_type_info e;
+ ftap_init_encap_types();
+
+ e.name = g_strdup(name);
+ e.short_name = g_strdup(short_name);
+
+ g_array_append_val(encap_table_arr,e);
+
+ return ftap_num_encap_types++;
+}
+
+
+/* Name that should be somewhat descriptive. */
+const char *
+ftap_encap_string(int encap)
+{
+ if (encap < FTAP_ENCAP_PER_RECORD || encap >= FTAP_NUM_ENCAP_TYPES)
+ return "Illegal";
+ else if (encap == FTAP_ENCAP_PER_RECORD)
+ return "Per record";
+ else
+ return encap_table_entry(encap).name;
+}
+
+/* Name to use in, say, a command-line flag specifying the type. */
+const char *
+ftap_encap_short_string(int encap)
+{
+ if (encap < FTAP_ENCAP_PER_RECORD || encap >= FTAP_NUM_ENCAP_TYPES)
+ return "illegal";
+ else if (encap == FTAP_ENCAP_PER_RECORD)
+ return "per-record";
+ else
+ return encap_table_entry(encap).short_name;
+}
+
+/* Translate a short name to a capture file type. */
+int
+ftap_short_string_to_encap(const char *short_name)
+{
+ int encap;
+
+ for (encap = 0; encap < FTAP_NUM_ENCAP_TYPES; encap++) {
+ if (encap_table_entry(encap).short_name != NULL &&
+ strcmp(short_name, encap_table_entry(encap).short_name) == 0)
+ return encap;
+ }
+ return -1; /* no such encapsulation type */
+}
+
+static const char *ftap_errlist[] = {
+ "The file isn't a plain file or pipe",
+ "The file is being opened for random access but is a pipe",
+ "The file isn't a capture file in a known format",
+ "File contains record data we don't support",
+ "That file format cannot be written to a pipe",
+ NULL,
+ "Files can't be saved in that format",
+ "Files from that network type can't be saved in that format",
+ "That file format doesn't support per-packet encapsulations",
+ NULL,
+ NULL,
+ "Less data was read than was expected",
+ "The file appears to be damaged or corrupt",
+ "Less data was written than was requested",
+ "Uncompression error: data oddly truncated",
+ "Uncompression error: data would overflow buffer",
+ "Uncompression error: bad LZ77 offset",
+ "The standard input cannot be opened for random access",
+ "That file format doesn't support compression",
+ NULL,
+ NULL,
+ "Uncompression error",
+ "Internal error"
+};
+#define FTAP_ERRLIST_SIZE (sizeof ftap_errlist / sizeof ftap_errlist[0])
+
+const char *
+ftap_strerror(int err)
+{
+ static char errbuf[128];
+ unsigned int ftap_errlist_index;
+
+ if (err < 0) {
+ ftap_errlist_index = -1 - err;
+ if (ftap_errlist_index >= FTAP_ERRLIST_SIZE) {
+ g_snprintf(errbuf, 128, "Error %d", err);
+ return errbuf;
+ }
+ if (ftap_errlist[ftap_errlist_index] == NULL)
+ return "Unknown reason";
+ return ftap_errlist[ftap_errlist_index];
+ } else
+ return g_strerror(err);
+}
+
+/* Close only the sequential side, freeing up memory it uses.
+
+ Note that we do *not* want to call the subtype's close function,
+ as it would free any per-subtype data, and that data may be
+ needed by the random-access side.
+
+ Instead, if the subtype has a "sequential close" function, we call it,
+ to free up stuff used only by the sequential side. */
+void
+ftap_sequential_close(ftap *fth)
+{
+ if (fth->subtype_sequential_close != NULL)
+ (*fth->subtype_sequential_close)(fth);
+
+ if (fth->fh != NULL) {
+ file_close(fth->fh);
+ fth->fh = NULL;
+ }
+
+ if (fth->frame_buffer) {
+ buffer_free(fth->frame_buffer);
+ g_free(fth->frame_buffer);
+ fth->frame_buffer = NULL;
+ }
+}
+
+static void
+g_fast_seek_item_free(gpointer data, gpointer user_data _U_)
+{
+ g_free(data);
+}
+
+/*
+ * Close the file descriptors for the sequential and random streams, but
+ * don't discard any information about those streams. Used on Windows if
+ * we need to rename a file that we have open or if we need to rename on
+ * top of a file we have open.
+ */
+void
+ftap_fdclose(ftap *fth)
+{
+ if (fth->fh != NULL)
+ file_fdclose(fth->fh);
+ if (fth->random_fh != NULL)
+ file_fdclose(fth->random_fh);
+}
+
+void
+ftap_close(ftap *fth)
+{
+ ftap_sequential_close(fth);
+
+ if (fth->subtype_close != NULL)
+ (*fth->subtype_close)(fth);
+
+ if (fth->random_fh != NULL)
+ file_close(fth->random_fh);
+
+ if (fth->priv != NULL)
+ g_free(fth->priv);
+
+ if (fth->fast_seek != NULL) {
+ g_ptr_array_foreach(fth->fast_seek, g_fast_seek_item_free, NULL);
+ g_ptr_array_free(fth->fast_seek, TRUE);
+ }
+
+ g_free(fth);
+}
+
+void
+ftap_cleareof(ftap *fth) {
+ /* Reset EOF */
+ file_clearerr(fth->fh);
+}
+
+gboolean
+ftap_read(ftap *fth, int *err, gchar **err_info, gint64 *data_offset)
+{
+#if 0
+ /*
+ * Set the packet encapsulation to the file's encapsulation
+ * value; if that's not FTAP_ENCAP_PER_RECORD, it's the
+ * right answer (and means that the read routine for this
+ * capture file type doesn't have to set it), and if it
+ * *is* FTAP_ENCAP_PER_RECORD, the caller needs to set it
+ * anyway.
+ */
+ wth->phdr.pkt_encap = wth->file_encap;
+#endif
+
+ if (!fth->subtype_read(fth, err, err_info, data_offset)) {
+ /*
+ * If we didn't get an error indication, we read
+ * the last packet. See if there's any deferred
+ * error, as might, for example, occur if we're
+ * reading a compressed file, and we got an error
+ * reading compressed data from the file, but
+ * got enough compressed data to decompress the
+ * last packet of the file.
+ */
+ if (*err == 0)
+ *err = file_error(fth->fh, err_info);
+ return FALSE; /* failure */
+ }
+
+#if 0
+ /*
+ * It makes no sense for the captured data length to be bigger
+ * than the actual data length.
+ */
+ if (wth->phdr.caplen > wth->phdr.len)
+ wth->phdr.caplen = wth->phdr.len;
+
+ /*
+ * Make sure that it's not FTAP_ENCAP_PER_RECORD, as that
+ * probably means the file has that encapsulation type
+ * but the read routine didn't set this packet's
+ * encapsulation type.
+ */
+ g_assert(wth->phdr.pkt_encap != FTAP_ENCAP_PER_RECORD);
+
+#endif
+ return TRUE; /* success */
+}
+
+/*
+ * Read packet data into a Buffer, growing the buffer as necessary.
+ *
+ * This returns an error on a short read, even if the short read hit
+ * the EOF immediately. (The assumption is that each packet has a
+ * header followed by raw packet data, and that we've already read the
+ * header, so if we get an EOF trying to read the packet data, the file
+ * has been cut short, even if the read didn't read any data at all.)
+ */
+gboolean
+ftap_read_packet_bytes(FILE_F fh, Buffer *buf, guint length, int *err,
+ gchar **err_info)
+{
+ int bytes_read;
+
+ buffer_assure_space(buf, length);
+ errno = FTAP_ERR_CANT_READ;
+ bytes_read = file_read(buffer_start_ptr(buf), length, fh);
+
+ if (bytes_read < 0 || (guint)bytes_read != length) {
+ *err = file_error(fh, err_info);
+ if (*err == 0)
+ *err = FTAP_ERR_SHORT_READ;
+ return FALSE;
+ }
+ return TRUE;
+}
+
+/*
+ * Return an approximation of the amount of data we've read sequentially
+ * from the file so far. (gint64, in case that's 64 bits.)
+ */
+gint64
+ftap_read_so_far(ftap *fth)
+{
+ return file_tell_raw(fth->fh);
+}
+
+#if 0
+struct wtap_pkthdr *
+wtap_phdr(wtap *wth)
+{
+ return &wth->phdr;
+}
+
+guint8 *
+wtap_buf_ptr(wtap *wth)
+{
+ return buffer_start_ptr(wth->frame_buffer);
+}
+#endif
+
+gboolean
+ftap_seek_read(ftap *fth, gint64 seek_off,
+ Buffer *buf, int len,
+ int *err, gchar **err_info)
+{
+ return fth->subtype_seek_read(fth, seek_off, /* 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 <gram@alumni.rice.edu>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef __FTAP_H__
+#define __FTAP_H__
+
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+
+#include <glib.h>
+#include <time.h>
+#include <filetap/buffer.h>
+#include <wsutil/nstime.h>
+#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:
+ */
diff --git a/image/filetap.rc.in b/image/filetap.rc.in
new file mode 100644
index 0000000000..2a80f5fdfb
--- /dev/null
+++ b/image/filetap.rc.in
@@ -0,0 +1,34 @@
+#include "winver.h"
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION @RC_VERSION@
+ PRODUCTVERSION @RC_VERSION@
+ FILEFLAGSMASK 0x0L
+#ifdef _DEBUG
+ FILEFLAGS VS_FF_DEBUG
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS VOS_NT_WINDOWS32
+ FILETYPE VFT_DLL
+ FILESUBTYPE 0x0L
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904b0"
+ BEGIN
+ VALUE "CompanyName", "The Wireshark developer community, http://www.wireshark.org/\0"
+ VALUE "FileDescription", "Wireshark filetap library\0"
+ VALUE "FileVersion", "@VERSION@\0"
+ VALUE "InternalName", "filetap @VERSION@\0"
+ VALUE "LegalCopyright", "Copyright © 2000 Gerald Combs <gerald@wireshark.org>, Gilbert Ramirez <gram@alumni.rice.edu> and others\0"
+ VALUE "OriginalFilename", "filetap-@VERSION@.dll\0"
+ VALUE "ProductName", "Wireshark\0"
+ VALUE "ProductVersion", "@VERSION@\0"
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x409, 1200
+ END
+END