aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--docbook/CMakeLists.txt1
-rw-r--r--docbook/Makefile.common1
-rwxr-xr-xdocbook/make-wsluarm.pl8
-rw-r--r--docbook/user-guide.xml1
-rw-r--r--docbook/wsluarm.xml1
-rw-r--r--epan/wslua/CMakeLists.txt1
-rw-r--r--epan/wslua/Makefile.am2
-rw-r--r--epan/wslua/Makefile.nmake2
-rwxr-xr-xepan/wslua/make-init-lua.pl20
-rw-r--r--epan/wslua/template-init.lua4
-rw-r--r--epan/wslua/wslua.h101
-rw-r--r--epan/wslua/wslua_file.c2134
-rw-r--r--epan/wslua/wslua_util.c21
-rw-r--r--test/captures/sip.pcapngbin0 -> 3696 bytes
-rw-r--r--test/captures/sipmsg.log136
-rw-r--r--test/lua/acme_file.lua1365
-rw-r--r--test/lua/pcap_file.lua548
-rwxr-xr-xtest/suite-wslua.sh98
-rw-r--r--wiretap/file_access.c496
-rw-r--r--wiretap/file_wrappers.c47
-rw-r--r--wiretap/file_wrappers.h5
-rw-r--r--wiretap/wtap-int.h13
-rw-r--r--wiretap/wtap.h40
23 files changed, 4874 insertions, 171 deletions
diff --git a/docbook/CMakeLists.txt b/docbook/CMakeLists.txt
index 0373da5f76..e03076f5aa 100644
--- a/docbook/CMakeLists.txt
+++ b/docbook/CMakeLists.txt
@@ -309,6 +309,7 @@ set(WSLUA_MODULES
${CMAKE_SOURCE_DIR}/epan/wslua/wslua_proto.c
${CMAKE_SOURCE_DIR}/epan/wslua/wslua_tree.c
${CMAKE_SOURCE_DIR}/epan/wslua/wslua_tvb.c
+ ${CMAKE_SOURCE_DIR}/epan/wslua/wslua_file.c
${CMAKE_SOURCE_DIR}/epan/wslua/wslua_util.c
${CMAKE_SOURCE_DIR}/epan/wslua/wslua_struct.c
)
diff --git a/docbook/Makefile.common b/docbook/Makefile.common
index adb4d49d9a..d0056cc3e3 100644
--- a/docbook/Makefile.common
+++ b/docbook/Makefile.common
@@ -254,4 +254,5 @@ WSLUA_MODULES = \
../epan/wslua/wslua_struct.c \
../epan/wslua/wslua_tree.c \
../epan/wslua/wslua_tvb.c \
+ ../epan/wslua/wslua_file.c \
../epan/wslua/wslua_util.c
diff --git a/docbook/make-wsluarm.pl b/docbook/make-wsluarm.pl
index 5c0b8db740..c4a06d21ff 100755
--- a/docbook/make-wsluarm.pl
+++ b/docbook/make-wsluarm.pl
@@ -40,8 +40,16 @@ sub gorolla {
my $s = shift;
$s =~ s/^([\n]|\s)*//ms;
$s =~ s/([\n]|\s)*$//ms;
+ # as far as I can tell, these will only convert the *first* '<'/'>' they find in a line, but
+ # not subsequent ones in that line (because the flag isn't 'msg'?) -hadriel
$s =~ s/\</&lt;/ms;
$s =~ s/\>/&gt;/ms;
+ # this is a horrible horrible hack, but it works
+ # basically we undo the replacements just made above, if it's a '</para>' or '<para>' case
+ # so that comments can include them for prettier output. Really this API generator thing needs
+ # to be rewritten, but I don't understand perl well enough to do it properly -hadriel
+ $s =~ s/&lt;\/para&gt;/<\/para>/ms;
+ $s =~ s/&lt;para&gt;/<para>/ms;
$s;
}
diff --git a/docbook/user-guide.xml b/docbook/user-guide.xml
index dd35a85ed4..5d5e2548c6 100644
--- a/docbook/user-guide.xml
+++ b/docbook/user-guide.xml
@@ -346,6 +346,7 @@ WSLua Reference Manual
<!ENTITY WsLuaProto SYSTEM "wsluarm_src/wslua_proto.xml">
<!ENTITY WsLuaTree SYSTEM "wsluarm_src/wslua_tree.xml">
<!ENTITY WsLuaTvb SYSTEM "wsluarm_src/wslua_tvb.xml">
+ <!ENTITY WsLuaFile SYSTEM "wsluarm_src/wslua_file.xml">
<!ENTITY WsLuaUtility SYSTEM "wsluarm_src/wslua_util.xml">
<!ENTITY WsLuaInt64 SYSTEM "wsluarm_src/wslua_int64.xml">
<!ENTITY WsLuaStruct SYSTEM "wsluarm_src/wslua_struct.xml">
diff --git a/docbook/wsluarm.xml b/docbook/wsluarm.xml
index 1a01b568d4..5c9253da52 100644
--- a/docbook/wsluarm.xml
+++ b/docbook/wsluarm.xml
@@ -176,6 +176,7 @@ end
&WsLuaProto;
&WsLuaTree;
&WsLuaTvb;
+ &WsLuaFile;
&WsLuaUtility;
&WsLuaInt64;
&WsLuaStruct;
diff --git a/epan/wslua/CMakeLists.txt b/epan/wslua/CMakeLists.txt
index 7d29798665..ca5dfa3fb9 100644
--- a/epan/wslua/CMakeLists.txt
+++ b/epan/wslua/CMakeLists.txt
@@ -37,6 +37,7 @@ set(WSLUA_MODULES
${CMAKE_CURRENT_SOURCE_DIR}/wslua/wslua_gui.c
${CMAKE_CURRENT_SOURCE_DIR}/wslua/wslua_util.c
${CMAKE_CURRENT_SOURCE_DIR}/wslua/wslua_field.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/wslua/wslua_file.c
${CMAKE_CURRENT_SOURCE_DIR}/wslua/wslua_struct.c
${CMAKE_CURRENT_SOURCE_DIR}/wslua/wslua_dumper.c
${CMAKE_CURRENT_SOURCE_DIR}/wslua/wslua_internals.c
diff --git a/epan/wslua/Makefile.am b/epan/wslua/Makefile.am
index 0a721bd4fe..96c455e2dc 100644
--- a/epan/wslua/Makefile.am
+++ b/epan/wslua/Makefile.am
@@ -43,6 +43,7 @@ wslua_modules = \
$(srcdir)/wslua_gui.c \
$(srcdir)/wslua_util.c \
$(srcdir)/wslua_field.c \
+ $(srcdir)/wslua_file.c \
$(srcdir)/wslua_struct.c \
$(srcdir)/wslua_dumper.c \
$(srcdir)/wslua_internals.c
@@ -134,6 +135,7 @@ checkapi:
lrexlib_glib_f.c \
wslua_dumper.c \
wslua_field.c \
+ wslua_file.c \
wslua_gui.c \
wslua_int64.c \
wslua_pinfo.c \
diff --git a/epan/wslua/Makefile.nmake b/epan/wslua/Makefile.nmake
index aa3e07a038..dc672efe47 100644
--- a/epan/wslua/Makefile.nmake
+++ b/epan/wslua/Makefile.nmake
@@ -28,6 +28,7 @@ MODULES = \
wslua_gui.c \
wslua_util.c \
wslua_field.c \
+ wslua_file.c \
wslua_struct.c \
wslua_dumper.c \
wslua_internals.c
@@ -49,6 +50,7 @@ OBJECTS= \
wslua_gui.obj \
wslua_util.obj \
wslua_field.obj \
+ wslua_file.obj \
wslua_struct.obj \
wslua_dumper.obj \
wslua_internals.obj
diff --git a/epan/wslua/make-init-lua.pl b/epan/wslua/make-init-lua.pl
index 89cfdb5231..048f8097d2 100755
--- a/epan/wslua/make-init-lua.pl
+++ b/epan/wslua/make-init-lua.pl
@@ -34,7 +34,9 @@ die "'$WSROOT' is not a directory" unless -d $WSROOT;
my $wtap_encaps_table = '';
my $wtap_filetypes_table = '';
+my $wtap_commenttypes_table = '';
my $ft_types_table = '';
+my $wtap_presence_flags_table = '';
my $bases_table = '';
my $encodings = '';
my $expert_pi = '';
@@ -43,7 +45,9 @@ my $menu_groups = '';
my %replacements = %{{
WTAP_ENCAPS => \$wtap_encaps_table,
WTAP_FILETYPES => \$wtap_filetypes_table,
+ WTAP_COMMENTTYPES => \$wtap_commenttypes_table,
FT_TYPES => \$ft_types_table,
+ WTAP_PRESENCE_FLAGS => \$wtap_presence_flags_table,
BASES => \$bases_table,
ENCODINGS => \$encodings,
EXPERT => \$expert_pi,
@@ -66,10 +70,13 @@ close TEMPLATE;
#
# WTAP_FILE_ values
# WTAP_ENCAP_ values
+# WTAP_HAS_ values
#
$wtap_encaps_table = "-- Wiretap encapsulations XXX\nwtap_encaps = {\n";
$wtap_filetypes_table = "-- Wiretap file types\nwtap_filetypes = {\n";
+$wtap_commenttypes_table = "-- Wiretap file comment types\nwtap_comments = {\n";
+$wtap_presence_flags_table = "-- Wiretap presence flags\nwtap_presence_flags = {\n";
open WTAP_H, "< $WSROOT/wiretap/wtap.h" or die "cannot open '$WSROOT/wiretap/wtap.h': $!";
@@ -82,10 +89,23 @@ while(<WTAP_H>) {
if ( /^#define WTAP_FILE_(?:TYPE_SUBTYPE_)?([A-Z0-9_]+)\s+(\d+)/ ) {
$wtap_filetypes_table .= "\t[\"$1\"] = $2,\n";
}
+
+ if ( /^#define WTAP_COMMENT_([A-Z0-9_]+)\s+(0x\d+)/ ) {
+ $wtap_commenttypes_table .= "\t[\"$1\"] = $2,\n";
+ }
+
+ if ( /^#define WTAP_HAS_([A-Z0-9_]+)\s+(0x\d+)\s+\/\*\*<([^\*]+)\*\// ) {
+ my $num = hex($2);
+ $wtap_presence_flags_table .= "\t[\"$1\"] = $num, --$3\n";
+ }
}
$wtap_encaps_table =~ s/,\n$/\n}\nwtap = wtap_encaps -- for bw compatibility\n/msi;
$wtap_filetypes_table =~ s/,\n$/\n}\n/msi;
+$wtap_commenttypes_table =~ s/,\n$/\n}\n/msi;
+# wtap_presence_flags_table has comments at the end (not a comma),
+# but Lua doesn't care about extra commas so leave it in
+$wtap_presence_flags_table =~ s/\n$/\n}\n/msi;
#
# Extract values from epan/ftypes/ftypes.h:
diff --git a/epan/wslua/template-init.lua b/epan/wslua/template-init.lua
index f036c2460a..a80dc74023 100644
--- a/epan/wslua/template-init.lua
+++ b/epan/wslua/template-init.lua
@@ -73,8 +73,12 @@ end
-- %WTAP_FILETYPES%
+-- %WTAP_COMMENTTYPES%
+
-- %FT_TYPES%
+-- %WTAP_PRESENCE_FLAGS%
+
-- %BASES%
-- %ENCODINGS%
diff --git a/epan/wslua/wslua.h b/epan/wslua/wslua.h
index 840c61a28c..4bc16062dc 100644
--- a/epan/wslua/wslua.h
+++ b/epan/wslua/wslua.h
@@ -226,15 +226,59 @@ struct _wslua_tap {
gboolean all_fields;
};
-# define DIRECTORY_T GDir
-# define FILE_T gchar
-# define OPENDIR_OP(name) g_dir_open(name, 0, dir->dummy)
-# define DIRGETNEXT_OP(dir) g_dir_read_name(dir)
-# define GETFNAME_OP(file) (file);
-# define CLOSEDIR_OP(dir) g_dir_close(dir)
+/* a "File" object can be different things under the hood. It can either
+ be a FILE_T from wtap struct, which it is during read operations, or it
+ can be a wtap_dumper struct during write operations. A wtap_dumper struct
+ has a FILE_T member, but we can't only store its pointer here because
+ dump operations need the whole thing to write out with. Ugh. */
+struct _wslua_file {
+ FILE_T file;
+ wtap_dumper *wdh; /* will be NULL during read usage */
+ gboolean expired;
+};
+
+/* a "CaptureInfo" object can also be different things under the hood. */
+struct _wslua_captureinfo {
+ wtap *wth; /* will be NULL during write usage */
+ wtap_dumper *wdh; /* will be NULL during read usage */
+ gboolean expired;
+};
+
+struct _wslua_phdr {
+ struct wtap_pkthdr *phdr; /* this also exists in wtap struct, but is different for seek_read ops */
+ Buffer *buf; /* can't use the one in wtap because it's different for seek_read ops */
+ gboolean expired;
+};
+
+struct _wslua_const_phdr {
+ const struct wtap_pkthdr *phdr;
+ const guint8 *pd;
+ gboolean expired;
+};
+
+struct _wslua_filehandler {
+ struct file_type_subtype_info finfo;
+ gboolean is_reader;
+ gboolean is_writer;
+ gchar* description;
+ gchar* type;
+ gchar* extensions;
+ lua_State* L;
+ int read_open_ref;
+ int read_ref;
+ int seek_read_ref;
+ int read_close_ref;
+ int seq_read_close_ref;
+ int can_write_encap_ref;
+ int write_open_ref;
+ int write_ref;
+ int write_close_ref;
+ int file_type;
+ gboolean registered;
+};
struct _wslua_dir {
- DIRECTORY_T* dir;
+ GDir* dir;
char* ext;
GError** dummy;
};
@@ -272,6 +316,12 @@ typedef struct _wslua_field_info* FieldInfo;
typedef struct _wslua_tap* Listener;
typedef struct _wslua_tw* TextWindow;
typedef struct _wslua_progdlg* ProgDlg;
+typedef struct _wslua_file* File;
+typedef struct _wslua_captureinfo* CaptureInfo;
+typedef struct _wslua_captureinfo* CaptureInfoConst;
+typedef struct _wslua_phdr* FrameInfo;
+typedef struct _wslua_const_phdr* FrameInfoConst;
+typedef struct _wslua_filehandler* FileHandler;
typedef wtap_dumper* Dumper;
typedef struct lua_pseudo_header* PseudoHeader;
typedef tvbparse_t* Parser;
@@ -446,6 +496,9 @@ extern int wslua_set__index(lua_State *L);
C obj = check##C (L,1); \
if (! lua_isfunction(L,-1) ) \
return luaL_error(L, "%s's attribute `%s' must be a function", #C , #field ); \
+ if (obj->field##_ref != LUA_NOREF) \
+ /* there was one registered before, remove it */ \
+ luaL_unref(L, LUA_REGISTRYINDEX, obj->field##_ref); \
obj->field##_ref = luaL_ref(L, LUA_REGISTRYINDEX); \
return 0; \
} \
@@ -491,6 +544,13 @@ extern int wslua_set__index(lua_State *L);
/* silly little trick so we can add a semicolon after this macro */ \
static int C##_set_##name(lua_State*)
+#define WSLUA_ATTRIBUTE_NAMED_BOOLEAN_SETTER(C,name,member) \
+ WSLUA_ATTRIBUTE_SET(C,name, { \
+ if (! lua_isboolean(L,-1) ) \
+ return luaL_error(L, "%s's attribute `%s' must be a boolean", #C , #name ); \
+ obj->member = lua_toboolean(L,-1); \
+ })
+
/* to make this integral-safe, we treat it as int32 and then cast
Note: this will truncate 64-bit integers (but then Lua itself only has doubles */
#define WSLUA_ATTRIBUTE_NAMED_NUMBER_SETTER(C,name,member,cast) \
@@ -503,6 +563,23 @@ extern int wslua_set__index(lua_State *L);
#define WSLUA_ATTRIBUTE_NUMBER_SETTER(C,member,cast) \
WSLUA_ATTRIBUTE_NAMED_NUMBER_SETTER(C,member,member,cast)
+#define WSLUA_ATTRIBUTE_NAMED_STRING_SETTER(C,field,member,need_free) \
+ static int C##_set_##field (lua_State* L) { \
+ C obj = check##C (L,1); \
+ gchar* s = NULL; \
+ if (lua_isstring(L,-1) || lua_isnil(L,-1)) { \
+ s = g_strdup(lua_tostring(L,-1)); \
+ } else { \
+ return luaL_error(L, "%s's attribute `%s' must be a string or nil", #C , #field ); \
+ } \
+ if (obj->member != NULL && need_free) \
+ free((void*) obj->member); \
+ obj->member = s; \
+ return 0; \
+ }
+
+#define WSLUA_ATTRIBUTE_STRING_SETTER(C,field,need_free) \
+ WSLUA_ATTRIBUTE_NAMED_STRING_SETTER(C,field,field,need_free)
#define WSLUA_ERROR(name,error) { luaL_error(L, ep_strdup_printf("%s%s", #name ": " ,error) ); }
#define WSLUA_ARG_ERROR(name,attr,error) { luaL_argerror(L,WSLUA_ARG_ ## name ## _ ## attr, #name ": " error); }
@@ -518,7 +595,17 @@ extern int wslua_set__index(lua_State *L);
/* empty macro arguments trigger ISO C90 warnings, so do this */
#define NOP (void)p
+
#define FAIL_ON_NULL(s) if (! *p) luaL_argerror(L,idx,"null " s)
+
+#define FAIL_ON_NULL_MEMBER_OR_EXPIRED(s,member) if (!*p) { \
+ luaL_argerror(L,idx,"null " s); \
+ } else if ((*p)->member == NULL) { \
+ luaL_argerror(L,idx,"null " s " member " #member); \
+ } else if ((*p)->expired) { \
+ luaL_argerror(L,idx,"expired " s); \
+ }
+
#define FAIL_ON_NULL_OR_EXPIRED(s) if (!*p) { \
luaL_argerror(L,idx,"null " s); \
} else if ((*p)->expired) { \
diff --git a/epan/wslua/wslua_file.c b/epan/wslua/wslua_file.c
new file mode 100644
index 0000000000..cc3ae668d5
--- /dev/null
+++ b/epan/wslua/wslua_file.c
@@ -0,0 +1,2134 @@
+/*
+ * wslua_file.c
+ *
+ * Wireshark's interface to the Lua Programming Language
+ * for custom file format reading/writing.
+ *
+ * (c) 2014, Hadriel Kaplan <hadrielk@yahoo.com>
+ *
+ * $Id: wslua_file.c 47903 2013-02-26 15:10:28Z hadrielk $
+ *
+ * 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 "config.h"
+
+#include "wslua.h"
+#include <errno.h>
+#include <wiretap/wtap-int.h>
+#include <wiretap/file_wrappers.h>
+#include <epan/addr_resolv.h>
+#include <math.h>
+
+#define MAX_LINE_LENGTH 65536
+
+/* WSLUA_MODULE File Custom file format reading/writing */
+
+
+WSLUA_CLASS_DEFINE(File,FAIL_ON_NULL_OR_EXPIRED("File"),NOP);
+/*
+ A File object, passed into Lua as an argument by FileHandler callback
+ functions (e.g., read_open, read, write, etc.). This behaves similarly to the
+ Lua 'io' library's 'file' object, returned when calling io.open(), *except*
+ in this case you cannot call file:close(), file:open(), nor file:setvbuf(),
+ since Wireshark/tshark manages the opening and closing of files.
+ You also cannot use the 'io' library itself on this object, i.e. you cannot
+ do io.read(file, 4). Instead, use this File with the object-oriented style
+ calling its methods, i.e. myfile:read(4).
+
+ </para><para>
+ The purpose of this object is to hide the internal complexity of how Wireshark
+ handles files, and instead provide a Lua interface that is familiar, by mimicking
+ the io library. The reason true/raw io files cannot be used is because Wireshark
+ does many things under the hood, such as compress the file, or write to stdout,
+ or various other things based on configuration/commands.
+
+ </para><para>
+ When a File object is passed in through reading-based callback functions, such as
+ read_open(), read(), and read_close(), then the File object's write() and flush()
+ functions are not usable and will raise an error if used.
+
+ </para><para>
+ When a File object is passed in through writing-based callback functions, such as
+ write_open(), write(), and write_close(), then the File object's read() and lines()
+ functions are not usable and will raise an error if used.
+
+ </para><para>
+ Note: a File object should never be stored/saved beyond the scope of the callback function
+ it is passed in to.
+ */
+
+/* a "File" object can be different things under the hood. It can either
+ be a FILE_T from wtap struct, which it is during read operations, or it
+ can be a wtap_dumper struct during write operations. A wtap_dumper struct
+ has a FILE_T member, but we can't only store its pointer here because
+ dump operations need the whole thing to write out with. Ugh. */
+static File* push_File(lua_State* L, FILE_T ft) {
+ File f = (File) g_malloc(sizeof(struct _wslua_file));
+ f->file = ft;
+ f->wdh = NULL;
+ f->expired = FALSE;
+ return pushFile(L,f);
+}
+
+static File* push_Wdh(lua_State* L, wtap_dumper *wdh) {
+ File f = (File) g_malloc(sizeof(struct _wslua_file));
+ f->file = wdh->fh;
+ f->wdh = wdh;
+ f->expired = FALSE;
+ return pushFile(L,f);
+}
+
+static gboolean file_is_reader(File f) {
+ return (f->wdh == NULL);
+}
+
+/* This internal function reads a number from the file, similar to Lua's io.read("*num").
+ * In Lua this is done with a fscanf(file, "%lf", &double), but we can't use fscanf() since
+ * this may be coming from a zip file and we need to use file_wrappers.c functions.
+ * So we get a character at a time, building a buffer for fscanf.
+ * XXX this isn't perfect - if just "2." exists in file, for example, it consumes it.
+ */
+#define WSLUA_MAXNUMBER2STR 32 /* 16 digits, sign, point, and \0 */
+static int File_read_number (lua_State *L, FILE_T ft) {
+ lua_Number d;
+ gchar buff[WSLUA_MAXNUMBER2STR];
+ int buff_end = 0;
+ int c = -1;
+ int num_digits = 0;
+ gboolean has_decimal = FALSE;
+
+ c = file_peekc(ft);
+ if (c == '+' || c == '-') {
+ buff[buff_end++] = (gchar)c;
+ /* make sure next char is a digit */
+ c = file_peekc(ft);
+ if (c < '0' || c > '9') {
+ lua_pushnil(L); /* "result" to be removed */
+ return 0; /* read fails */
+ }
+ /* eat the +/- */
+ file_getc(ft);
+ }
+
+ while((c = file_peekc(ft)) > 0 && buff_end < (WSLUA_MAXNUMBER2STR-1)) {
+ if (c >= '0' && c <= '9') {
+ buff[buff_end++] = (gchar)c;
+ num_digits++;
+ file_getc(ft);
+ }
+ else if (!has_decimal && c == '.') {
+ has_decimal = TRUE;
+ buff[buff_end++] = (gchar)c;
+ file_getc(ft);
+ }
+ else break;
+ }
+
+ buff[buff_end] = '\0';
+
+ if (buff_end > 0 && num_digits > 0 && sscanf(buff, "%lf", &d) == 1) {
+ lua_pushnumber(L, d);
+ return 1;
+ }
+ else {
+ lua_pushnil(L); /* "result" to be removed */
+ return 0; /* read fails */
+ }
+}
+
+static int File_read_line(lua_State *L, FILE_T ft) {
+ static gchar linebuff[MAX_LINE_LENGTH];
+ gint64 pos_before = file_tell(ft);
+ gint length = 0;
+
+ if (file_gets(linebuff, MAX_LINE_LENGTH, ft) == NULL) {
+ /* No characters found, or error */
+ /* *err = file_error(ft, err_info); */
+ return 0;
+ }
+
+ /* Set length (avoiding strlen()) */
+ length = (gint)(file_tell(ft) - pos_before);
+
+ /* ...but don't want to include newline in line length */
+ if (linebuff[length-1] == '\n') {
+ length--;
+ /* Nor do we want '\r' (as will be written when log is created on windows) */
+ if (linebuff[length-1] == '\r') {
+ length--;
+ }
+ linebuff[length] = '\0';
+ }
+
+ lua_pushlstring(L, linebuff, length);
+ return 1;
+}
+
+/* This internal function reads X number of btyes form the file, same as io.read(num) in Lua.
+ * Since we have to use file_wrappers.c, and an intermediate buffer, we read it in chunks
+ * of 1024 bytes at a time. (or less if called with a smaller number) To do that, we use
+ * Lua's buffer manager to push it into Lua as those chunks, while ending up with one long
+ * Lua string in the end.
+ */
+#define WSLUA_BUFFERSIZE 1024
+
+/* Lua 5.1 used lua_objlen() instead of lua_rawlen() */
+#if LUA_VERSION_NUM == 501
+#define lua_rawlen lua_objlen
+#endif
+
+static int File_read_chars(lua_State *L, FILE_T ft, size_t n) {
+ size_t rlen; /* how much to read */
+ size_t nr; /* number of chars actually read */
+ int nri; /* temp number of chars read, as an int to handle -1 errors */
+ gchar buff[WSLUA_BUFFERSIZE]; /* for file_read to write to, and we push into Lua */
+ luaL_Buffer b;
+
+ rlen = WSLUA_BUFFERSIZE; /* try to read that much each time */
+ luaL_buffinit(L, &b); /* initialize Lua buffer */
+
+ do {
+ if (rlen > n) rlen = n; /* cannot read more than asked */
+ nri = file_read(buff, (unsigned int)rlen, ft);
+ if (nri < 1) break;
+ nr = (size_t) nri;
+ luaL_addlstring(&b, buff, nr);
+ n -= nr; /* still have to read `n' chars */
+ } while (n > 0 && nr == rlen); /* until end of count or eof */
+
+ luaL_pushresult(&b); /* close buffer */
+
+ return (n == 0 || lua_rawlen(L, -1) > 0);
+}
+
+/* returns nil if EOF, else an empty string - this is what Lua does too for this case */
+static int File_test_eof(lua_State *L, FILE_T ft) {
+ if (file_eof(ft)) {
+ lua_pushnil(L);
+ }
+ else {
+ lua_pushlstring(L, "", 0);
+ }
+ return 1;
+}
+
+static int pushresult (lua_State *L, int i, const char *filename) {
+ int en = errno; /* calls to Lua API may change this value, so we save it */
+ if (i) {
+ lua_pushboolean(L, 1);
+ return 1;
+ }
+ else {
+ lua_pushnil(L);
+ if (filename)
+ lua_pushfstring(L, "%s: %s", filename, strerror(en));
+ else
+ lua_pushfstring(L, "%s", strerror(en));
+ lua_pushinteger(L, en);
+ return 3;
+ }
+}
+
+WSLUA_METHOD File_read(lua_State* L) {
+ /* Reads from the File, similar to Lua's file:read(). See Lua 5.x ref manual for file:read(). */
+ File f = shiftFile(L,1);
+ int nargs = lua_gettop(L);
+ int success;
+ int n = 1;
+ FILE_T ft = NULL;
+
+ if (!f || !f->file) {
+ return 0;
+ }
+
+ /* shiftFile() doesn't verify things like expired */
+ if (f->expired) {
+ g_warning("Error in File read: Lua File has expired");
+ return 0;
+ }
+
+ if (!file_is_reader(f)) {
+ g_warning("Error in File read: this File object instance is for writing only");
+ return 0;
+ }
+
+ ft = f->file;
+
+ /* file_clearerr(ft); */
+ if (nargs == 0) { /* no arguments? */
+ success = File_read_line(L, ft);
+ n = 2; /* to return 1 result */
+ }
+ else { /* ensure stack space for all results and Lua */
+ luaL_checkstack(L, nargs+LUA_MINSTACK, "too many arguments");
+ success = 1;
+ for (n = 1; nargs-- && success; n++) {
+ if (lua_type(L, n) == LUA_TNUMBER) {
+ size_t l = (size_t)lua_tointeger(L, n);
+ success = (l == 0) ? File_test_eof(L, ft) : File_read_chars(L, ft, l);
+ }
+ else {
+ const char *p = lua_tostring(L, n);
+ if (!p) return luaL_argerror(L, n, "invalid format argument");
+ luaL_argcheck(L, p[0] == '*', n, "invalid option");
+ switch (p[1]) {
+ case 'n': /* number */
+ success = File_read_number(L, ft);
+ break;
+ case 'l': /* line */
+ success = File_read_line(L, ft);
+ break;
+ case 'a': /* file, read everything */
+ File_read_chars(L, ft, ~((size_t)0)); /* read MAX_SIZE_T chars */
+ success = 1; /* always success */
+ break;
+ default:
+ return luaL_argerror(L, n, "invalid format");
+ }
+ }
+ }
+ }
+ if (file_error(ft, NULL))
+ return pushresult(L, 0, NULL);
+ if (!success) {
+ lua_pop(L, 1); /* remove last result */
+ lua_pushnil(L); /* push nil instead */
+ }
+ return n - 1;
+}
+
+WSLUA_METHOD File_seek(lua_State* L) {
+ /* Seeks in the File, similar to Lua's file:seek(). See Lua 5.x ref manual for file:seek(). */
+ static const int mode[] = { SEEK_SET, SEEK_CUR, SEEK_END };
+ static const char *const modenames[] = {"set", "cur", "end", NULL};
+ File f = checkFile(L,1);
+ int op = luaL_checkoption(L, 2, "cur", modenames);
+ gint64 offset = (gint64) luaL_optlong(L, 3, 0);
+ int err = WTAP_ERR_INTERNAL;
+
+
+ if (file_is_reader(f)) {
+ offset = file_seek(f->file, offset, mode[op], &err);
+
+ if (offset < 0) {
+ lua_pushnil(L); /* error */
+ lua_pushstring(L, wtap_strerror(err));
+ return 2;
+ }
+
+ lua_pushnumber(L, (lua_Number)(file_tell(f->file)));
+ }
+ else {
+ offset = wtap_dump_file_seek(f->wdh, offset, mode[op], &err);
+
+ if (offset < 0) {
+ lua_pushnil(L); /* error */
+ lua_pushstring(L, wtap_strerror(err));
+ return 2;
+ }
+
+ offset = wtap_dump_file_tell(f->wdh, &err);
+
+ if (offset < 0) {
+ lua_pushnil(L); /* error */
+ lua_pushstring(L, wtap_strerror(err));
+ return 2;
+ }
+
+ lua_pushnumber(L, (lua_Number)(offset));
+ }
+
+ WSLUA_RETURN(1); /* The current file cursor position as a number. */
+}
+
+static int File_lines_iterator(lua_State* L) {
+ FILE_T ft = *(FILE_T *)lua_touserdata(L, lua_upvalueindex(1));
+ int success;
+
+ if (ft == NULL)
+ return luaL_error(L, "Error getting File handle for lines iterator");
+
+ success = File_read_line(L, ft);
+
+ /* if (ferror(ft))
+ return luaL_error(L, "%s", strerror(errno));
+ */
+ return success;
+}
+
+WSLUA_METHOD File_lines(lua_State* L) {
+ /* Lua iterator function for retrieving ascii File lines, similar to Lua's file:lines(). See Lua 5.x ref manual for file:lines(). */
+ File f = checkFile(L,1);
+ FILE_T ft = NULL;
+
+ if (!f->file)
+ return luaL_error(L, "Error getting File handle for lines");
+
+ if (!file_is_reader(f)) {
+ g_warning("Error in File read: this File object instance is for writing only");
+ return 0;
+ }
+
+ ft = f->file;
+
+ lua_pushlightuserdata(L, ft);
+ lua_pushcclosure(L, File_lines_iterator, 1);
+
+ return 1;
+}
+
+/* yeah this function is a little weird, but I'm mimicking Lua's actual code for io:write() */
+WSLUA_METHOD File_write(lua_State* L) {
+ /* Writes to the File, similar to Lua's file:write(). See Lua 5.x ref manual for file:write(). */
+ File f = checkFile(L,1);
+ int arg = 2; /* beginning index for arguments */
+ int nargs = lua_gettop(L) - 1;
+ int status = TRUE;
+ int err = 0;
+
+ if (!f->wdh) {
+ g_warning("Error in File read: this File object instance is for reading only");
+ return 0;
+ }
+
+ lua_pushvalue(L, 1); /* push File at the stack top (to be returned) */
+
+ for (; nargs--; arg++) {
+ size_t len;
+ const char *s = luaL_checklstring(L, arg, &len);
+ status = wtap_dump_file_write(f->wdh, s, len, &err);
+ if (!status) break;
+ f->wdh->bytes_dumped += len;
+ }
+
+ if (!status) {
+ lua_pop(L,1); /* pop the extraneous File object */
+ lua_pushnil(L);
+ lua_pushfstring(L, "File write error: %s", strerror(err));
+ lua_pushinteger(L, err);
+ return 3;
+ }
+
+ return 1; /* File object already on stack top */
+}
+
+WSLUA_METAMETHOD File__tostring(lua_State* L) {
+ /* Generates a string of debug info for the File object */
+ File f = toFile(L,1);
+
+ if (!f) {
+ lua_pushstring(L,"File pointer is NULL!");
+ } else {
+ lua_pushfstring(L,"File expired=%s, handle=%s, is %s", f->expired? "true":"false", f->file? "<ptr>":"<NULL>",
+ f->wdh? "writer":"reader");
+ }
+
+ WSLUA_RETURN(1); /* String of debug information. */
+}
+
+/* We free the struct we malloc'ed, but not the FILE_T/dumper in it of course */
+static int File__gc(lua_State* L _U_) {
+ File f = toFile(L,1);
+ if (f)
+ g_free(f);
+ return 0;
+}
+
+/* WSLUA_ATTRIBUTE File_compressed RO Whether the File is compressed or not.
+ See 'wtap_encaps' in init.lua for available types. Set to 'wtap_encaps.PER_PACKET' if packets can
+ have different types, then later set 'FrameInfo.encap' for each packet during read()/seek_read(). */
+static int File_get_compressed(lua_State* L) {
+ File f = checkFile(L,1);
+
+ if (file_is_reader(f)) {
+ lua_pushboolean(L, file_iscompressed(f->file));
+ } else {
+ lua_pushboolean(L, f->wdh->compressed);
+ }
+ return 1;
+}
+
+WSLUA_ATTRIBUTES File_attributes[] = {
+ WSLUA_ATTRIBUTE_ROREG(File,compressed),
+ { NULL, NULL, NULL }
+};
+
+WSLUA_METHODS File_methods[] = {
+ WSLUA_CLASS_FNREG(File,lines),
+ WSLUA_CLASS_FNREG(File,read),
+ WSLUA_CLASS_FNREG(File,seek),
+ WSLUA_CLASS_FNREG(File,write),
+ { NULL, NULL }
+};
+
+WSLUA_META File_meta[] = {
+ WSLUA_CLASS_MTREG(File,tostring),
+ { NULL, NULL }
+};
+
+int File_register(lua_State* L) {
+ WSLUA_REGISTER_CLASS(File);
+ WSLUA_REGISTER_ATTRIBUTES(File);
+ return 0;
+}
+
+
+WSLUA_CLASS_DEFINE(CaptureInfo,FAIL_ON_NULL_MEMBER_OR_EXPIRED("CaptureInfo",wth),NOP);
+/*
+ A CaptureInfo object, passed into Lua as an argument by FileHandler callback
+ function read_open().
+ This object represents capture file data and meta-data (data about the
+ capture file) being read into Wireshark/Tshark.
+
+ </para><para>
+ This object's fields can be written-to by Lua during the read_open function callback.
+ In other words, when the Lua plugin's FileHandler read_open function is invoked, a
+ CaptureInfo object will be passed in as one of the arguments, and its fields
+ should be written to by your Lua code to tell Wireshark about the capture.
+ */
+
+static CaptureInfo* push_CaptureInfo(lua_State* L, wtap *wth) {
+ CaptureInfo f = (CaptureInfo) g_malloc0(sizeof(struct _wslua_captureinfo));
+ f->wth = wth;
+ f->wdh = NULL;
+ /* XXX: need to do this? */
+ wth->file_encap = WTAP_ENCAP_UNKNOWN;
+ wth->tsprecision = WTAP_FILE_TSPREC_SEC;
+ wth->snapshot_length = 0;
+ f->expired = FALSE;
+ return pushCaptureInfo(L,f);
+}
+
+WSLUA_METAMETHOD CaptureInfo__tostring(lua_State* L) {
+ /* Generates a string of debug info for the CaptureInfo */
+ CaptureInfo fi = toCaptureInfo(L,1);
+
+ if (!fi || !fi->wth) {
+ lua_pushstring(L,"CaptureInfo pointer is NULL!");
+ } else {
+ wtap *wth = fi->wth;
+ lua_pushfstring(L, "CaptureInfo: file_type_subtype=%d, snapshot_length=%d, pkt_encap=%d, tsprecision='%s'",
+ wth->file_type_subtype, wth->snapshot_length, wth->phdr.pkt_encap, wth->tsprecision);
+ }
+
+ WSLUA_RETURN(1); /* String of debug information. */
+}
+
+
+static int CaptureInfo__gc(lua_State* L _U_) {
+ FrameInfo fi = toFrameInfo(L,1);
+ if (fi)
+ g_free(fi);
+ return 0;
+}
+
+/* WSLUA_ATTRIBUTE CaptureInfo_encap RW The packet encapsulation type for the whole file.
+ See 'wtap_encaps' in init.lua for available types. Set to 'wtap_encaps.PER_PACKET' if packets can
+ have different types, then later set 'FrameInfo.encap' for each packet during read()/seek_read(). */
+WSLUA_ATTRIBUTE_NAMED_NUMBER_GETTER(CaptureInfo,encap,wth->file_encap);
+WSLUA_ATTRIBUTE_NAMED_NUMBER_SETTER(CaptureInfo,encap,wth->file_encap,int);
+
+/* WSLUA_ATTRIBUTE CaptureInfo_time_precision RW The precision of the packet timestamps in the file.
+ See 'wtap_file_tsprec' in init.lua for available precisions. */
+WSLUA_ATTRIBUTE_NAMED_NUMBER_GETTER(CaptureInfo,time_precision,wth->tsprecision);
+WSLUA_ATTRIBUTE_NAMED_NUMBER_SETTER(CaptureInfo,time_precision,wth->tsprecision,int);
+
+/* WSLUA_ATTRIBUTE CaptureInfo_snapshot_length RW The maximum packet length that could be recorded.
+ Setting it to 0 means unknown. Wireshark cannot handle anything bigger than 65535 bytes. */
+WSLUA_ATTRIBUTE_NAMED_NUMBER_GETTER(CaptureInfo,snapshot_length,wth->snapshot_length);
+WSLUA_ATTRIBUTE_NAMED_NUMBER_SETTER(CaptureInfo,snapshot_length,wth->snapshot_length,guint);
+
+/* WSLUA_ATTRIBUTE CaptureInfo_comment RW A string comment for the whole capture file,
+ or nil if there is no comment. */
+WSLUA_ATTRIBUTE_NAMED_STRING_GETTER(CaptureInfo,comment,wth->shb_hdr.opt_comment);
+WSLUA_ATTRIBUTE_NAMED_STRING_SETTER(CaptureInfo,comment,wth->shb_hdr.opt_comment,TRUE);
+
+/* WSLUA_ATTRIBUTE CaptureInfo_hardware RW A string containing the description of
+ the hardware used to create the capture, or nil if there is no hardware string. */
+WSLUA_ATTRIBUTE_NAMED_STRING_GETTER(CaptureInfo,hardware,wth->shb_hdr.shb_hardware);
+WSLUA_ATTRIBUTE_NAMED_STRING_SETTER(CaptureInfo,hardware,wth->shb_hdr.shb_hardware,TRUE);
+
+/* WSLUA_ATTRIBUTE CaptureInfo_os RW A string containing the name of
+ the operating system used to create the capture, or nil if there is no os string. */
+WSLUA_ATTRIBUTE_NAMED_STRING_GETTER(CaptureInfo,os,wth->shb_hdr.shb_os);
+WSLUA_ATTRIBUTE_NAMED_STRING_SETTER(CaptureInfo,os,wth->shb_hdr.shb_os,TRUE);
+
+/* WSLUA_ATTRIBUTE CaptureInfo_user_app RW A string containing the name of
+ the application used to create the capture, or nil if there is no user_app string. */
+WSLUA_ATTRIBUTE_NAMED_STRING_GETTER(CaptureInfo,user_app,wth->shb_hdr.shb_user_appl);
+WSLUA_ATTRIBUTE_NAMED_STRING_SETTER(CaptureInfo,user_app,wth->shb_hdr.shb_user_appl,TRUE);
+
+/* WSLUA_ATTRIBUTE CaptureInfo_hosts WO Sets resolved ip-to-hostname information.
+ The value set must be a Lua table of two key-ed names: 'ipv4_addresses' and 'ipv6_addresses'.
+ The value of each of these names are themselves array tables, of key-ed tables, such that the inner table has a key
+ 'addr' set to the raw 4-byte or 16-byte IP address Lua string and a 'name' set to the resolved name.
+
+ </para><para>
+ For example, if the capture file identifies one resolved IPv4 address of 1.2.3.4 to 'foo.com', then you must set
+ CaptureInfo.hosts to a table of { ipv4_addresses = { { addr = '\01\02\03\04', name = 'foo.com' } } }.
+
+ </para><para>
+ Note that either the 'ipv4_addresses' or the 'ipv6_addresses' table, or both, may be empty or nil.
+ */
+static int CaptureInfo_set_hosts(lua_State* L) {
+ CaptureInfo fi = checkCaptureInfo(L,1);
+ wtap *wth = fi->wth;
+ const char *addr = NULL;
+ const char *name = NULL;
+ size_t addr_len = 0;
+ size_t name_len = 0;
+ guint32 v4_addr = 0;
+ struct e_in6_addr v6_addr = { {0} };
+
+ if (!wth->add_new_ipv4 || !wth->add_new_ipv6) {
+ return luaL_error(L, "CaptureInfo wtap has no IPv4 or IPv6 name resolution");
+ }
+
+ if (!lua_istable(L,-1)) {
+ return luaL_error(L, "CaptureInfo.host must be set to a table");
+ }
+
+ /* get the ipv4_addresses table */
+ lua_getfield(L, -1, "ipv4_addresses");
+
+ if (lua_istable(L,-1)) {
+ /* now walk the table */
+ lua_pushnil(L); /* first key */
+ while (lua_next(L, -2) != 0) {
+ /* 'key' (at index -2) and 'value' (at index -1) */
+ if (!lua_istable(L,-1)) {
+ lua_pop(L, 3); /* remove whatever it is, the key, and the ipv4_addreses table */
+ return luaL_error(L, "CaptureInfo.host ipv4_addresses table does not contain a table");
+ }
+
+ lua_getfield(L, -1, "addr");
+ if (!lua_isstring(L,-1)) {
+ lua_pop(L, 3); /* remove whatever it is, the key, and the ipv4_addreses table */
+ return luaL_error(L, "CaptureInfo.host ipv4_addresses table's table does not contain an 'addr' field");
+ }
+ addr = luaL_checklstring(L,-1,&addr_len);
+ if (addr_len != 4) {
+ lua_pop(L, 3); /* remove whatever it is, the key, and the ipv4_addreses table */
+ return luaL_error(L, "CaptureInfo.host ipv4_addresses 'addr' value is not 4 bytes long");
+ }
+ memcpy(&v4_addr, addr, 4);
+
+ lua_getfield(L, -1, "name");
+ if (!lua_isstring(L,-1)) {
+ lua_pop(L, 3); /* remove whatever it is, the key, and the ipv4_addreses table */
+ return luaL_error(L, "CaptureInfo.host ipv4_addresses table's table does not contain an 'addr' field");
+ }
+ name = luaL_checklstring(L,-1,&name_len);
+
+ wth->add_new_ipv4(v4_addr, name);
+
+ /* removes 'value'; keeps 'key' for next iteration */
+ lua_pop(L, 1);
+ }
+ }
+
+ /* wasn't a table, or it was and we walked it; either way pop it */
+ lua_pop(L,1);
+
+
+ /* get the ipv6_addresses table */
+ lua_getfield(L, -1, "ip6_addresses");
+
+ if (lua_istable(L,-1)) {
+ /* now walk the table */
+ lua_pushnil(L); /* first key */
+ while (lua_next(L, -2) != 0) {
+ /* 'key' (at index -2) and 'value' (at index -1) */
+ if (!lua_istable(L,-1)) {
+ lua_pop(L, 3); /* remove whatever it is, the key, and the ipv4_addreses table */
+ return luaL_error(L, "CaptureInfo.host ipv6_addresses table does not contain a table");
+ }
+
+ lua_getfield(L, -1, "addr");
+ if (!lua_isstring(L,-1)) {
+ lua_pop(L, 3); /* remove whatever it is, the key, and the ipv4_addreses table */
+ return luaL_error(L, "CaptureInfo.host ipv6_addresses table's table does not contain an 'addr' field");
+ }
+ addr = luaL_checklstring(L,-1,&addr_len);
+ if (addr_len != 16) {
+ lua_pop(L, 3); /* remove whatever it is, the key, and the ipv4_addreses table */
+ return luaL_error(L, "CaptureInfo.host ipv6_addresses 'addr' value is not 16 bytes long");
+ }
+ memcpy(&v6_addr, addr, 16);
+
+ lua_getfield(L, -1, "name");
+ if (!lua_isstring(L,-1)) {
+ lua_pop(L, 3); /* remove whatever it is, the key, and the ipv4_addreses table */
+ return luaL_error(L, "CaptureInfo.host ipv6_addresses table's table does not contain an 'addr' field");
+ }
+ name = luaL_checklstring(L,-1,&name_len);
+
+ wth->add_new_ipv6((const void *)(&v6_addr), name);
+
+ /* removes 'value'; keeps 'key' for next iteration */
+ lua_pop(L, 1);
+ }
+ }
+
+ /* wasn't a table, or it was and we walked it; either way pop it */
+ lua_pop(L,1);
+
+ return 0;
+}
+
+WSLUA_ATTRIBUTES CaptureInfo_attributes[] = {
+ WSLUA_ATTRIBUTE_RWREG(CaptureInfo,encap),
+ WSLUA_ATTRIBUTE_RWREG(CaptureInfo,time_precision),
+ WSLUA_ATTRIBUTE_RWREG(CaptureInfo,snapshot_length),
+ WSLUA_ATTRIBUTE_RWREG(CaptureInfo,comment),
+ WSLUA_ATTRIBUTE_RWREG(CaptureInfo,hardware),
+ WSLUA_ATTRIBUTE_RWREG(CaptureInfo,os),
+ WSLUA_ATTRIBUTE_RWREG(CaptureInfo,user_app),
+ WSLUA_ATTRIBUTE_WOREG(CaptureInfo,hosts),
+ { NULL, NULL, NULL }
+};
+
+WSLUA_META CaptureInfo_meta[] = {
+ {"__tostring", CaptureInfo__tostring},
+ { NULL, NULL }
+};
+
+int CaptureInfo_register(lua_State* L) {
+ WSLUA_REGISTER_META(CaptureInfo);
+ WSLUA_REGISTER_ATTRIBUTES(CaptureInfo);
+ return 0;
+}
+
+
+WSLUA_CLASS_DEFINE(CaptureInfoConst,FAIL_ON_NULL_MEMBER_OR_EXPIRED("CaptureInfoConst",wdh),NOP);
+/*
+ A CaptureInfoConst object, passed into Lua as an argument to the FileHandler callback
+ function write_open().
+ This object represents capture file data and meta-data (data about the
+ capture file) for the current capture in Wireshark/Tshark.
+
+ </para><para>
+ This object's fields are read-from when used by write_open function callback.
+ In other words, when the Lua plugin's FileHandler write_open function is invoked, a
+ CaptureInfoConst object will be passed in as one of the arguments, and its fields
+ should be read from by your Lua code to get data about the capture that needs to be written.
+ */
+
+static CaptureInfoConst* push_CaptureInfoConst(lua_State* L, wtap_dumper *wdh) {
+ CaptureInfoConst f = (CaptureInfoConst) g_malloc0(sizeof(struct _wslua_captureinfo));
+ f->wth = NULL;
+ f->wdh = wdh;
+ f->expired = FALSE;
+ return pushCaptureInfoConst(L,f);
+}
+
+WSLUA_METAMETHOD CaptureInfoConst__tostring(lua_State* L) {
+ /* Generates a string of debug info for the CaptureInfoConst */
+ CaptureInfoConst fi = toCaptureInfoConst(L,1);
+
+ if (!fi || !fi->wdh) {
+ lua_pushstring(L,"CaptureInfoConst pointer is NULL!");
+ } else {
+ wtap_dumper *wdh = fi->wdh;
+ lua_pushfstring(L, "CaptureInfoConst: file_type_subtype=%d, snaplen=%d, encap=%d, compressed=%d, tsprecision='%s'",
+ wdh->file_type_subtype, wdh->snaplen, wdh->encap, wdh->compressed, wdh->tsprecision);
+ }
+
+ WSLUA_RETURN(1); /* String of debug information. */
+}
+
+/* WSLUA_ATTRIBUTE CaptureInfoConst_type RO The file type. */
+WSLUA_ATTRIBUTE_NAMED_NUMBER_GETTER(CaptureInfoConst,type,wdh->file_type_subtype);
+
+/* WSLUA_ATTRIBUTE CaptureInfoConst_snapshot_length RO The maximum packet length that is actually recorded (vs. the original
+ length of any given packet on-the-wire). A value of 0 means the snapshot length is unknown or there is no one
+ such length for the whole file. */
+WSLUA_ATTRIBUTE_NAMED_NUMBER_GETTER(CaptureInfoConst,snapshot_length,wdh->snaplen);
+
+/* WSLUA_ATTRIBUTE CaptureInfoConst_encap RO The packet encapsulation type for the whole file.
+ See 'wtap_encaps' in init.lua for available types. It is set to 'wtap_encaps.PER_PACKET' if packets can
+ have different types, in which case each Frame identifies its type, in 'FrameInfo.packet_encap'. */
+WSLUA_ATTRIBUTE_NAMED_NUMBER_GETTER(CaptureInfoConst,encap,wdh->encap);
+
+/* WSLUA_ATTRIBUTE CaptureInfoConst_comment RW A comment for the whole capture file, if the
+ 'wtap_presence_flags.COMMENTS' was set in the presence flags; nil if there is no comment. */
+WSLUA_ATTRIBUTE_NAMED_STRING_GETTER(CaptureInfoConst,comment,wth->shb_hdr.opt_comment);
+
+/* WSLUA_ATTRIBUTE CaptureInfoConst_hardware RO A string containing the description of
+ the hardware used to create the capture, or nil if there is no hardware string. */
+WSLUA_ATTRIBUTE_NAMED_STRING_GETTER(CaptureInfoConst,hardware,wth->shb_hdr.shb_hardware);
+
+/* WSLUA_ATTRIBUTE CaptureInfoConst_os RO A string containing the name of
+ the operating system used to create the capture, or nil if there is no os string. */
+WSLUA_ATTRIBUTE_NAMED_STRING_GETTER(CaptureInfoConst,os,wth->shb_hdr.shb_os);
+
+/* WSLUA_ATTRIBUTE CaptureInfoConst_user_app RO A string containing the name of
+ the application used to create the capture, or nil if there is no user_app string. */
+WSLUA_ATTRIBUTE_NAMED_STRING_GETTER(CaptureInfoConst,user_app,wth->shb_hdr.shb_user_appl);
+
+/* WSLUA_ATTRIBUTE CaptureInfoConst_hosts RO A ip-to-hostname Lua table of two key-ed names: 'ipv4_addresses' and 'ipv6_addresses'.
+ The value of each of these names are themselves array tables, of key-ed tables, such that the inner table has a key
+ 'addr' set to the raw 4-byte or 16-byte IP address Lua string and a 'name' set to the resolved name.
+
+ </para><para>
+ For example, if the current capture has one resolved IPv4 address of 1.2.3.4 to 'foo.com', then getting
+ CaptureInfoConst.hosts will get a table of:
+ { ipv4_addresses = { { addr = '\01\02\03\04', name = 'foo.com' } }, ipv6_addresses = { } }.
+
+ </para><para>
+ Note that either the 'ipv4_addresses' or the 'ipv6_addresses' table, or both, may be empty, however they will not
+ be nil. */
+static int CaptureInfoConst_get_hosts(lua_State* L) {
+ CaptureInfoConst fi = checkCaptureInfoConst(L,1);
+ wtap_dumper *wdh = fi->wdh;
+
+ /* create the main table to return */
+ lua_newtable(L);
+
+ /* create the ipv4_addresses table */
+ lua_newtable(L);
+
+ if (wdh->addrinfo_lists && wdh->addrinfo_lists->ipv4_addr_list) {
+ hashipv4_t *ipv4_hash_list_entry = (hashipv4_t *)g_list_nth_data(wdh->addrinfo_lists->ipv4_addr_list, 0);
+ int i, j;
+ for (i=1, j=1; ipv4_hash_list_entry != NULL; i++) {
+ if ((ipv4_hash_list_entry->flags & USED_AND_RESOLVED_MASK) == RESOLVED_ADDRESS_USED) {
+ lua_pushnumber(L, j); /* push numeric index key starting at 1, so it will be an array table */
+ /* create the entry table */
+ lua_newtable(L);
+ /* addr is in network order already */
+ lua_pushlstring(L, (char*)(&ipv4_hash_list_entry->ip), 4);
+ lua_setfield(L, -2, "addr");
+ lua_pushstring(L, ipv4_hash_list_entry->name);
+ lua_setfield(L, -2, "name");
+ /* now our ipv4_addresses table is at -3, key number is -2, and entry table at -2, so we're good */
+ lua_settable(L, -3);
+ j++;
+ }
+ ipv4_hash_list_entry = (hashipv4_t *)g_list_nth_data(wdh->addrinfo_lists->ipv4_addr_list, i);
+ }
+ }
+
+ /* set the (possibly empty) ipv4_addresses table into the main table */
+ lua_setfield(L, -2, "ipv4_addresses");
+
+ /* create the ipv6_addresses table */
+ lua_newtable(L);
+
+ if (wdh->addrinfo_lists && wdh->addrinfo_lists->ipv6_addr_list) {
+ hashipv6_t *ipv6_hash_list_entry = (hashipv6_t *)g_list_nth_data(wdh->addrinfo_lists->ipv6_addr_list, 0);
+ int i, j;
+ for (i=1, j=1; ipv6_hash_list_entry != NULL; i++) {
+ if ((ipv6_hash_list_entry->flags & USED_AND_RESOLVED_MASK) == RESOLVED_ADDRESS_USED) {
+ lua_pushnumber(L, j); /* push numeric index key starting at 1, so it will be an array table */
+ /* create the entry table */
+ lua_newtable(L);
+ /* addr is in network order already */
+ lua_pushlstring(L, (char*)(&ipv6_hash_list_entry->addr.bytes[0]), 16);
+ lua_setfield(L, -2, "addr");
+ lua_pushstring(L, ipv6_hash_list_entry->name);
+ lua_setfield(L, -2, "name");
+ /* now our ipv6_addresses table is at -3, key number is -2, and entry table at -2, so we're good */
+ lua_settable(L, -3);
+ j++;
+ }
+ ipv6_hash_list_entry = (hashipv6_t *)g_list_nth_data(wdh->addrinfo_lists->ipv6_addr_list, i);
+ }
+ }
+
+ /* set the (possibly empty) ipv6_addresses table into the main table */
+ lua_setfield(L, -2, "ip6_addresses");
+
+ /* return the main table */
+ return 1;
+}
+
+
+static int CaptureInfoConst__gc(lua_State* L _U_) {
+ FrameInfoConst fi = toFrameInfoConst(L,1);
+ if (fi)
+ g_free(fi);
+ return 0;
+}
+
+WSLUA_ATTRIBUTES CaptureInfoConst_attributes[] = {
+ WSLUA_ATTRIBUTE_ROREG(CaptureInfoConst,encap),
+ WSLUA_ATTRIBUTE_ROREG(CaptureInfoConst,type),
+ WSLUA_ATTRIBUTE_ROREG(CaptureInfoConst,snapshot_length),
+ WSLUA_ATTRIBUTE_ROREG(CaptureInfoConst,comment),
+ WSLUA_ATTRIBUTE_ROREG(CaptureInfoConst,hardware),
+ WSLUA_ATTRIBUTE_ROREG(CaptureInfoConst,os),
+ WSLUA_ATTRIBUTE_ROREG(CaptureInfoConst,user_app),
+ WSLUA_ATTRIBUTE_ROREG(CaptureInfoConst,hosts),
+ { NULL, NULL, NULL }
+};
+
+WSLUA_META CaptureInfoConst_meta[] = {
+ {"__tostring", CaptureInfoConst__tostring},
+ { NULL, NULL }
+};
+
+int CaptureInfoConst_register(lua_State* L) {
+ WSLUA_REGISTER_META(CaptureInfoConst);
+ WSLUA_REGISTER_ATTRIBUTES(CaptureInfoConst);
+ return 0;
+}
+
+
+WSLUA_CLASS_DEFINE(FrameInfo,FAIL_ON_NULL_OR_EXPIRED("FrameInfo"),NOP);
+/*
+ A FrameInfo object, passed into Lua as an argument by FileHandler callback
+ functions (e.g., read, seek_read, etc.).
+
+ </para><para>
+ This object represents frame data and
+ meta-data (data about the frame/packet) for a given read/seek_read/write's frame.
+
+ </para><para>
+ This object's fields are written-to/set when used by read function callbacks, and
+ read-from/get when used by file write function callbacks. In other words, when
+ the Lua plugin's FileHandler read/seek_read/etc. functions are invoked, a
+ FrameInfo object will be passed in as one of the arguments, and its fields
+ should be written-to/set based on the frame information read from the file;
+ whereas when the Lua plugin's FileHandler write function is invoked, the
+ FrameInfo object passed in should have its fields read-from/get, to write that
+ frame information to the file.
+ */
+
+static FrameInfo* push_FrameInfo(lua_State* L, struct wtap_pkthdr *phdr, Buffer* buf) {
+ FrameInfo f = (FrameInfo) g_malloc0(sizeof(struct _wslua_phdr));
+ f->phdr = phdr;
+ f->buf = buf;
+ f->expired = FALSE;
+ return pushFrameInfo(L,f);
+}
+
+WSLUA_METAMETHOD FrameInfo__tostring(lua_State* L) {
+ /* Generates a string of debug info for the FrameInfo */
+ FrameInfo fi = toFrameInfo(L,1);
+
+ if (!fi) {
+ lua_pushstring(L,"FrameInfo pointer is NULL!");
+ } else {
+ if (fi->phdr)
+ lua_pushfstring(L, "FrameInfo: presence_flags=%d, caplen=%d, len=%d, pkt_encap=%d, opt_comment='%s'",
+ fi->phdr->presence_flags, fi->phdr->caplen, fi->phdr->len, fi->phdr->pkt_encap, fi->phdr->opt_comment);
+ else
+ lua_pushstring(L, "FrameInfo phdr pointer is NULL!");
+ }
+
+ WSLUA_RETURN(1); /* String of debug information. */
+}
+
+/* XXX: should this function be a method of File instead? */
+WSLUA_METHOD FrameInfo_read_data(lua_State* L) {
+ /* Tells Wireshark to read directly from given file into frame data buffer, for length bytes. Returns true if succeeded, else false. */
+#define WSLUA_ARG_FrameInfo_read_data_FILE 2 /* The File object userdata, provided by Wireshark previously in a reading-based callback */
+#define WSLUA_ARG_FrameInfo_read_data_LENGTH 3 /* The number of bytes to read from the file at the current cursor position */
+ FrameInfo fi = checkFrameInfo(L,1);
+ File fh = checkFile(L,WSLUA_ARG_FrameInfo_read_data_FILE);
+ guint32 len = wslua_checkguint32(L, WSLUA_ARG_FrameInfo_read_data_LENGTH);
+ int err = 0;
+ gchar *err_info = NULL;
+
+ if (!fi->buf || !fh->file) {
+ luaL_error(L, "FrameInfo read_data() got null buffer or file pointer internally");
+ return 0;
+ }
+
+ if (!wtap_read_packet_bytes(fh->file, fi->buf, len, &err, &err_info)) {
+ lua_pushboolean(L, FALSE);
+ if (err_info) {
+ lua_pushstring(L, err_info);
+ g_free(err_info); /* is this right? */
+ }
+ else lua_pushnil(L);
+ lua_pushnumber(L, err);
+ return 3;
+ }
+
+ lua_pushboolean(L, TRUE);
+
+ WSLUA_RETURN(1); /* True if succeeded, else returns false along with the error number and string error description. */
+}
+
+/* free the struct we created, but not the phdr/buf it points to */
+static int FrameInfo__gc(lua_State* L _U_) {
+ FrameInfo fi = toFrameInfo(L,1);
+ if (fi)
+ g_free(fi);
+ return 0;
+}
+
+/* WSLUA_ATTRIBUTE FrameInfo_time RW The packet timestamp as an NSTime object.
+ Note: Set the 'FileHandler.time_precision' to the appropriate 'wtap_file_tsprec' value as well. */
+static int FrameInfo_set_time (lua_State* L) {
+ FrameInfo fi = checkFrameInfo(L,1);
+ NSTime nstime = checkNSTime(L,2);
+
+ if (!fi->phdr) return 0;
+
+ fi->phdr->ts.secs = nstime->secs;
+ fi->phdr->ts.nsecs = nstime->nsecs;
+
+ return 0;
+}
+
+static int FrameInfo_get_time (lua_State* L) {
+ FrameInfo fi = checkFrameInfo(L,1);
+ NSTime nstime = (NSTime)g_malloc(sizeof(nstime_t));
+
+ if (!nstime) return 0;
+
+ nstime->secs = fi->phdr->ts.secs;
+ nstime->nsecs = fi->phdr->ts.nsecs;
+
+ pushNSTime(L,nstime);
+
+ return 1; /* An NSTime object of the frame's timestamp. */
+}
+
+/* WSLUA_ATTRIBUTE FrameInfo_data RW The data buffer containing the packet. This cannot be cleared once set. */
+static int FrameInfo_set_data (lua_State* L) {
+ FrameInfo fi = checkFrameInfo(L,1);
+
+ if (!fi->phdr) {
+ g_warning("Error in FrameInfo set data: NULL pointer");
+ return 0;
+ }
+
+ if (!fi->buf) {
+ g_warning("Error in FrameInfo set data: NULL frame_buffer pointer");
+ return 0;
+ }
+
+ if (lua_isstring(L,2)) {
+ size_t len = 0;
+ const gchar* s = luaL_checklstring(L,2,&len);
+ if (s) {
+ /* Make sure we have enough room for the packet */
+ buffer_assure_space(fi->buf, len);
+ memcpy(buffer_start_ptr(fi->buf), s, len);
+ fi->phdr->caplen = (guint32) len;
+ fi->phdr->len = (guint32) len;
+ } else {
+ luaL_error(L, "FrameInfo's attribute 'data' did not get a valid Lua string");
+ }
+ }
+ else
+ luaL_error(L, "FrameInfo's attribute 'data' must be a Lua string");
+
+ return 0;
+}
+
+static int FrameInfo_get_data (lua_State* L) {
+ FrameInfo fi = checkFrameInfo(L,1);
+
+ if (!fi->buf) return 0;
+
+ lua_pushlstring(L, buffer_start_ptr(fi->buf), buffer_length(fi->buf));
+
+ WSLUA_RETURN(1); /* A Lua string of the frame buffer's data. */
+}
+
+/* WSLUA_ATTRIBUTE FrameInfo_flags RW The presence flags of the packet frame.
+ See 'wtap_presence_flags' in init.lua for bit values. */
+WSLUA_ATTRIBUTE_NAMED_NUMBER_GETTER(FrameInfo,flags,phdr->presence_flags);
+WSLUA_ATTRIBUTE_NAMED_NUMBER_SETTER(FrameInfo,flags,phdr->presence_flags,guint32);
+
+/* WSLUA_ATTRIBUTE FrameInfo_captured_length RW The captured packet length,
+ and thus the length of the buffer passed to the FrameInfo.data field. */
+WSLUA_ATTRIBUTE_NAMED_NUMBER_GETTER(FrameInfo,captured_length,phdr->caplen);
+WSLUA_ATTRIBUTE_NAMED_NUMBER_SETTER(FrameInfo,captured_length,phdr->caplen,guint32);
+
+/* WSLUA_ATTRIBUTE FrameInfo_original_length RW The on-the-wire packet length,
+ which may be longer than the captured_length. */
+WSLUA_ATTRIBUTE_NAMED_NUMBER_GETTER(FrameInfo,original_length,phdr->len);
+WSLUA_ATTRIBUTE_NAMED_NUMBER_SETTER(FrameInfo,original_length,phdr->len,guint32);
+
+/* WSLUA_ATTRIBUTE FrameInfo_encap RW The packet encapsulation type for the frame/packet,
+ if the file supports per-packet types. See 'wtap_encaps' in init.lua for possible
+ packet encapsulation types to use as the value for this field. */
+WSLUA_ATTRIBUTE_NAMED_NUMBER_GETTER(FrameInfo,encap,phdr->pkt_encap);
+WSLUA_ATTRIBUTE_NAMED_NUMBER_SETTER(FrameInfo,encap,phdr->pkt_encap,int);
+
+/* WSLUA_ATTRIBUTE FrameInfo_comment RW A string comment for the packet, if the
+ 'wtap_presence_flags.COMMENTS' was set in the presence flags; nil if there is no comment. */
+WSLUA_ATTRIBUTE_NAMED_STRING_GETTER(FrameInfo,comment,phdr->opt_comment);
+WSLUA_ATTRIBUTE_NAMED_STRING_SETTER(FrameInfo,comment,phdr->opt_comment,TRUE);
+
+/* This table is ultimately registered as a sub-table of the class' metatable,
+ * and if __index/__newindex is invoked then it calls the appropriate function
+ * from this table for getting/setting the members.
+ */
+WSLUA_ATTRIBUTES FrameInfo_attributes[] = {
+ WSLUA_ATTRIBUTE_RWREG(FrameInfo,flags),
+ WSLUA_ATTRIBUTE_RWREG(FrameInfo,captured_length),
+ WSLUA_ATTRIBUTE_RWREG(FrameInfo,original_length),
+ WSLUA_ATTRIBUTE_RWREG(FrameInfo,comment),
+ WSLUA_ATTRIBUTE_RWREG(FrameInfo,encap),
+ WSLUA_ATTRIBUTE_RWREG(FrameInfo,time),
+ WSLUA_ATTRIBUTE_RWREG(FrameInfo,data),
+ { NULL, NULL, NULL }
+};
+
+WSLUA_METHODS FrameInfo_methods[] = {
+ {"read_data", FrameInfo_read_data},
+ { NULL, NULL }
+};
+
+WSLUA_META FrameInfo_meta[] = {
+ {"__tostring", FrameInfo__tostring},
+ { NULL, NULL }
+};
+
+int FrameInfo_register(lua_State* L) {
+ WSLUA_REGISTER_CLASS(FrameInfo);
+ WSLUA_REGISTER_ATTRIBUTES(FrameInfo);
+ return 0;
+}
+
+WSLUA_CLASS_DEFINE(FrameInfoConst,FAIL_ON_NULL_OR_EXPIRED("FrameInfo"),NOP);
+/*
+ A constant FrameInfo object, passed into Lua as an argument by the FileHandler write
+ callback function. This has similar attributes/properties as FrameInfo, but the fields can
+ only be read from, not written to.
+ */
+
+static FrameInfoConst* push_FrameInfoConst(lua_State* L, const struct wtap_pkthdr *phdr, const guint8 *pd) {
+ FrameInfoConst f = (FrameInfoConst) g_malloc(sizeof(struct _wslua_const_phdr));
+ f->phdr = phdr;
+ f->pd = pd;
+ f->expired = FALSE;
+ return pushFrameInfoConst(L,f);
+}
+
+WSLUA_METAMETHOD FrameInfoConst__tostring(lua_State* L) {
+ /* Generates a string of debug info for the FrameInfo */
+ FrameInfoConst fi = toFrameInfoConst(L,1);
+
+ if (!fi) {
+ lua_pushstring(L,"FrameInfo pointer is NULL!");
+ } else {
+ if (fi->phdr && !fi->expired)
+ lua_pushfstring(L, "FrameInfo: presence_flags=%d, caplen=%d, len=%d, pkt_encap=%d, opt_comment='%s'",
+ fi->phdr->presence_flags, fi->phdr->caplen, fi->phdr->len, fi->phdr->pkt_encap, fi->phdr->opt_comment);
+ else
+ lua_pushfstring(L, "FrameInfo has %s", fi->phdr?"expired":"null phdr pointer");
+ }
+
+ WSLUA_RETURN(1); /* String of debug information. */
+}
+
+/* XXX: should this function be a method of File instead? */
+WSLUA_METHOD FrameInfoConst_write_data(lua_State* L) {
+ /* Tells Wireshark to write directly to given file from the frame data buffer, for length bytes. Returns true if succeeded, else false. */
+#define WSLUA_ARG_FrameInfoConst_write_data_FILE 2 /* The File object userdata, provided by Wireshark previously in a writing-based callback */
+#define WSLUA_OPTARG_FrameInfoConst_write_data_LENGTH 3 /* The number of bytes to write to the file at the current cursor position, or all if not supplied. */
+ FrameInfoConst fi = checkFrameInfoConst(L,1);
+ File fh = checkFile(L,WSLUA_ARG_FrameInfoConst_write_data_FILE);
+ guint32 len = wslua_optguint32(L, WSLUA_OPTARG_FrameInfoConst_write_data_LENGTH, fi->phdr ? fi->phdr->caplen:0);
+ int err = 0;
+
+ if (!fi->pd || !fi->phdr || !fh->wdh) {
+ luaL_error(L, "FrameInfoConst write_data() got null buffer or file pointer internally");
+ return 0;
+ }
+
+ if (len > fi->phdr->caplen)
+ len = fi->phdr->caplen;
+
+ if (!wtap_dump_file_write(fh->wdh, fi->pd, (size_t)(len), &err)) {
+ lua_pushboolean(L, FALSE);
+ lua_pushfstring(L, "FrameInfoConst write_data() error: %s", strerror(err));
+ lua_pushnumber(L, err);
+ return 3;
+ }
+
+ lua_pushboolean(L, TRUE);
+
+ WSLUA_RETURN(1); /* True if succeeded, else returns false along with the error number and string error description. */
+}
+
+/* free the struct we created, but not the wtap_pkthdr it points to */
+static int FrameInfoConst__gc(lua_State* L _U_) {
+ FrameInfoConst fi = toFrameInfoConst(L,1);
+ if (fi)
+ g_free(fi);
+ return 0;
+}
+
+/* WSLUA_ATTRIBUTE FrameInfoConst_time RO The packet timestamp as an NSTime object. */
+static int FrameInfoConst_get_time (lua_State* L) {
+ FrameInfoConst fi = checkFrameInfoConst(L,1);
+ NSTime nstime = (NSTime)g_malloc(sizeof(nstime_t));
+
+ if (!nstime) return 0;
+
+ nstime->secs = fi->phdr->ts.secs;
+ nstime->nsecs = fi->phdr->ts.nsecs;
+
+ pushNSTime(L,nstime);
+
+ return 1; /* An NSTime object of the frame's timestamp. */
+}
+
+/* WSLUA_ATTRIBUTE FrameInfoConst_data RO The data buffer containing the packet. */
+static int FrameInfoConst_get_data (lua_State* L) {
+ FrameInfoConst fi = checkFrameInfoConst(L,1);
+
+ if (!fi->pd || !fi->phdr) return 0;
+
+ lua_pushlstring(L, fi->pd, fi->phdr->caplen);
+
+ return 1;
+}
+
+/* WSLUA_ATTRIBUTE FrameInfoConst_flags RO The presence flags of the packet frame - see 'wtap_presence_flags' in init.lua for bits. */
+WSLUA_ATTRIBUTE_NAMED_NUMBER_GETTER(FrameInfoConst,flags,phdr->presence_flags);
+
+/* WSLUA_ATTRIBUTE FrameInfoConst_captured_length RO The captured packet length, and thus the length of the buffer in the FrameInfoConst.data field. */
+WSLUA_ATTRIBUTE_NAMED_NUMBER_GETTER(FrameInfoConst,captured_length,phdr->caplen);
+
+/* WSLUA_ATTRIBUTE FrameInfoConst_original_length RO The on-the-wire packet length, which may be longer than the captured_length. */
+WSLUA_ATTRIBUTE_NAMED_NUMBER_GETTER(FrameInfoConst,original_length,phdr->len);
+
+/* WSLUA_ATTRIBUTE FrameInfoConst_encap RO The packet encapsulation type, if the file supports per-packet types.
+ See 'wtap_encaps' in init.lua for possible packet encapsulation types to use as the value for this field. */
+WSLUA_ATTRIBUTE_NAMED_NUMBER_GETTER(FrameInfoConst,encap,phdr->pkt_encap);
+
+/* WSLUA_ATTRIBUTE FrameInfoConst_comment RO A comment for the packet; nil if there is none. */
+WSLUA_ATTRIBUTE_NAMED_STRING_GETTER(FrameInfoConst,comment,phdr->opt_comment);
+
+WSLUA_ATTRIBUTES FrameInfoConst_attributes[] = {
+ WSLUA_ATTRIBUTE_ROREG(FrameInfoConst,flags),
+ WSLUA_ATTRIBUTE_ROREG(FrameInfoConst,captured_length),
+ WSLUA_ATTRIBUTE_ROREG(FrameInfoConst,original_length),
+ WSLUA_ATTRIBUTE_ROREG(FrameInfoConst,encap),
+ WSLUA_ATTRIBUTE_ROREG(FrameInfoConst,comment),
+ WSLUA_ATTRIBUTE_ROREG(FrameInfoConst,time),
+ WSLUA_ATTRIBUTE_ROREG(FrameInfoConst,data),
+ { NULL, NULL, NULL }
+};
+
+WSLUA_METHODS FrameInfoConst_methods[] = {
+ {"write_data", FrameInfoConst_write_data},
+ { NULL, NULL }
+};
+
+WSLUA_META FrameInfoConst_meta[] = {
+ {"__tostring", FrameInfoConst__tostring},
+ { NULL, NULL }
+};
+
+int FrameInfoConst_register(lua_State* L) {
+ WSLUA_REGISTER_CLASS(FrameInfoConst);
+ WSLUA_REGISTER_ATTRIBUTES(FrameInfoConst);
+ return 0;
+}
+
+
+WSLUA_CLASS_DEFINE(FileHandler,NOP,NOP);
+/*
+ A FileHandler object, created by a call to FileHandler.new(arg1, arg2, ...).
+ The FileHandler object lets you create a file-format reader, or writer, or
+ both, by setting your own read_open/read or write_open/write functions.
+ */
+
+static int filehandler_cb_error_handler(lua_State* L) {
+ const gchar* error = lua_tostring(L,1);
+ const gchar* functype = luaL_optstring(L, lua_upvalueindex(1), "UNKNOWN");
+ report_failure("Lua: Error During execution of FileHandler %s callback:\n %s",functype,error);
+ lua_pop(L, 1);
+ return 0;
+}
+
+static int push_error_handler(lua_State* L, const gchar* funcname) {
+ lua_pushstring(L, funcname);
+ lua_pushcclosure(L, filehandler_cb_error_handler, 1);
+ return 1;
+}
+
+
+/* During file routines, we cannot allow the FileHandler to get de-registered, since
+ that would change the GArray's in file_access.c and hilarity would ensue. So we
+ set this to true right before pcall(), and back to false afterwards */
+static gboolean in_routine = FALSE;
+
+/* This does the verification and setup common to all open/read/seek_read/close routines */
+#define INIT_FILEHANDLER_ROUTINE(name,retval) \
+ if (!fh) { \
+ g_warning("Error in file %s: no Lua FileHandler object", #name); \
+ return retval; \
+ } \
+ if (!fh->registered) { \
+ g_warning("Error in file %s: Lua FileHandler is not registered", #name); \
+ return retval; \
+ } \
+ if (!fh->L) { \
+ g_warning("Error in file %s: no FileHandler Lua state", #name); \
+ return retval; \
+ } \
+ if (fh->name##_ref == LUA_NOREF) { \
+ g_warning("Error in file %s: no FileHandler %s routine reference", #name, #name); \
+ return retval; \
+ } \
+ L = fh->L; \
+ lua_settop(L,0); \
+ push_error_handler(L, #name " routine"); \
+ lua_rawgeti(L, LUA_REGISTRYINDEX, fh->name##_ref); \
+ if (!lua_isfunction(L, -1)) { \
+ g_warning("Error in file %s: no FileHandler %s routine function in Lua", #name, #name); \
+ return retval; \
+ } \
+ /* now guard against de-registering during pcall() */ \
+ in_routine = TRUE;
+
+#define END_FILEHANDLER_ROUTINE() \
+ /* now allow de-registering again */ \
+ in_routine = TRUE;
+
+
+/* LUA_ERRGCMM is in Lua 5.2 only - making it 9 disables it */
+#ifndef LUA_ERRGCMM
+#define LUA_ERRGCMM 9
+#endif
+
+#define CASE_ERROR(name) \
+ case LUA_ERRRUN: \
+ g_warning("Run-time error while calling FileHandler %s routine", name); \
+ break; \
+ case LUA_ERRMEM: \
+ g_warning("Memory alloc error while calling FileHandler %s routine", name); \
+ break; \
+ case LUA_ERRERR: \
+ g_warning("Error in error handling while calling FileHandler %s routine", name); \
+ break; \
+ case LUA_ERRGCMM: \
+ g_warning("Error in garbage collector while calling FileHandler %s routine", name); \
+ break; \
+ default: \
+ g_assert_not_reached(); \
+ break;
+
+#define CASE_ERROR_ERRINFO(name) \
+ case LUA_ERRRUN: \
+ g_warning("Run-time error while calling FileHandler %s routine", name); \
+ *err_info = g_strdup_printf("Run-time error while calling FileHandler %s routine", name); \
+ break; \
+ case LUA_ERRMEM: \
+ g_warning("Memory alloc error while calling FileHandler %s routine", name); \
+ *err_info = g_strdup_printf("Memory alloc error while calling FileHandler %s routine", name); \
+ break; \
+ case LUA_ERRERR: \
+ g_warning("Error in error handling while calling FileHandler %s routine", name); \
+ *err_info = g_strdup_printf("Error in error handling while calling FileHandler %s routine", name); \
+ break; \
+ case LUA_ERRGCMM: \
+ g_warning("Error in garbage collector while calling FileHandler %s routine", name); \
+ *err_info = g_strdup_printf("Error in garbage collector while calling FileHandler %s routine", name); \
+ break; \
+ default: \
+ g_assert_not_reached(); \
+ break;
+
+
+/* some declarations */
+static gboolean
+wslua_filehandler_read(wtap *wth, int *err, gchar **err_info,
+ gint64 *data_offset);
+static gboolean
+wslua_filehandler_seek_read(wtap *wth, gint64 seek_off,
+ struct wtap_pkthdr *phdr, Buffer *buf,
+ int *err, gchar **err_info);
+static void
+wslua_filehandler_close(wtap *wth);
+static void
+wslua_filehandler_sequential_close(wtap *wth);
+
+
+/* This is our one-and-only open routine for file handling. When called by
+ * file_access.c, the wtap wth argument has a void* wslua_data that holds the specific
+ * FileHandler for the specific registered file format reader. It has this because
+ * we passed it in when we registered the open routine.
+ * 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 wtap" to the type of the file.
+ */
+static int
+wslua_filehandler_open(wtap *wth, int *err _U_, gchar **err_info)
+{
+ FileHandler fh = (FileHandler)(wth->wslua_data);
+ int retval = 0;
+ lua_State* L = NULL;
+ File *fp = NULL;
+ CaptureInfo *fc = NULL;
+
+ INIT_FILEHANDLER_ROUTINE(read_open,0);
+
+ fp = push_File(L, wth->fh);
+ fc = push_CaptureInfo(L, wth);
+
+ errno = WTAP_ERR_CANT_READ;
+ switch ( lua_pcall(L,2,1,1) ) {
+ case 0:
+ retval = wslua_optboolint(L,-1,0);
+ break;
+ CASE_ERROR_ERRINFO("read_open")
+ }
+
+ END_FILEHANDLER_ROUTINE();
+
+ (*fp)->expired = TRUE;
+ (*fc)->expired = TRUE;
+
+ if (retval == 1) {
+ /* this is our file type - set the routines and settings into wtap */
+
+ if (fh->read_ref != LUA_NOREF) {
+ wth->subtype_read = wslua_filehandler_read;
+ }
+ else return 0;
+
+ if (fh->seek_read_ref != LUA_NOREF) {
+ wth->subtype_seek_read = wslua_filehandler_seek_read;
+ }
+ else return 0;
+
+ /* it's ok to not have a close routine */
+ if (fh->read_close_ref != LUA_NOREF)
+ wth->subtype_close = wslua_filehandler_close;
+ else
+ wth->subtype_close = NULL;
+
+ /* it's ok to not have a sequential close routine */
+ if (fh->seq_read_close_ref != LUA_NOREF)
+ wth->subtype_sequential_close = wslua_filehandler_sequential_close;
+ else
+ wth->subtype_sequential_close = NULL;
+
+ wth->file_type_subtype = fh->file_type;
+ }
+
+ lua_settop(L,0);
+ return retval;
+}
+
+/* The classic wtap read routine. This returns TRUE if it found the next packet,
+ * else FALSE.
+ * If it finds a frame/packet, it should set the pseudo-header info (ie, let Lua set it).
+ * Also Lua needs to set data_offset to the beginning of the line we're returning.
+ * This will be the seek_off parameter when this frame is re-read.
+*/
+static gboolean
+wslua_filehandler_read(wtap *wth, int *err, gchar **err_info,
+ gint64 *data_offset)
+{
+ FileHandler fh = (FileHandler)(wth->wslua_data);
+ int retval = -1;
+ lua_State* L = NULL;
+ File *fp = NULL;
+ FrameInfo *fi = NULL;
+
+ INIT_FILEHANDLER_ROUTINE(read,FALSE);
+
+ /* Reset errno */
+ *err = errno = 0;
+
+ wth->phdr.opt_comment = NULL;
+
+ fp = push_File(L, wth->fh);
+ fi = push_FrameInfo(L, &wth->phdr, wth->frame_buffer);
+
+ errno = WTAP_ERR_CANT_READ;
+ switch ( lua_pcall(L,2,1,1) ) {
+ case 0:
+ if (lua_isnumber(L,-1)) {
+ *data_offset = (gint64) lua_tonumber(L, -1);
+ retval = 1;
+ break;
+ }
+ retval = wslua_optboolint(L,-1,0);
+ break;
+ CASE_ERROR_ERRINFO("read")
+ }
+
+ END_FILEHANDLER_ROUTINE();
+
+ (*fp)->expired = TRUE;
+ (*fi)->expired = TRUE;
+ lua_settop(L,0);
+
+ return (retval == 1);
+}
+
+/* Classic wtap seek_read function, called by wtap core. This must return TRUE on
+ * success, FALSE on error.
+ */
+static gboolean
+wslua_filehandler_seek_read(wtap *wth, gint64 seek_off,
+ struct wtap_pkthdr *phdr, Buffer *buf,
+ int *err, gchar **err_info)
+{
+ FileHandler fh = (FileHandler)(wth->wslua_data);
+ int retval = -1;
+ lua_State* L = NULL;
+ File *fp = NULL;
+ FrameInfo *fi = NULL;
+
+ INIT_FILEHANDLER_ROUTINE(seek_read,FALSE);
+
+ /* Reset errno */
+ *err = errno = 0;
+ phdr->opt_comment = NULL;
+
+ fp = push_File(L, wth->random_fh);
+ fi = push_FrameInfo(L, phdr, buf);
+ lua_pushnumber(L, (lua_Number)seek_off);
+
+ *err = WTAP_ERR_CANT_READ;
+ switch ( lua_pcall(L,3,1,1) ) {
+ case 0:
+ if (lua_isstring(L,-1)) {
+ size_t len = 0;
+ const gchar* fd = lua_tolstring(L, -1, &len);
+ if (len < WTAP_MAX_PACKET_SIZE)
+ memcpy(buffer_start_ptr(buf), fd, len);
+ retval = 1;
+ break;
+ }
+ retval = wslua_optboolint(L,-1,0);
+ break;
+ CASE_ERROR_ERRINFO("seek_read")
+ }
+
+ END_FILEHANDLER_ROUTINE();
+
+ (*fp)->expired = TRUE;
+ (*fi)->expired = TRUE;
+ lua_settop(L,0);
+
+ return (retval == 1);
+}
+
+/* Classic wtap close function, called by wtap core.
+ */
+static void
+wslua_filehandler_close(wtap *wth)
+{
+ FileHandler fh = (FileHandler)(wth->wslua_data);
+ lua_State* L = NULL;
+ File *fp = NULL;
+
+ INIT_FILEHANDLER_ROUTINE(read_close,);
+
+ fp = push_File(L, wth->fh);
+
+ switch ( lua_pcall(L,1,1,1) ) {
+ case 0:
+ break;
+ CASE_ERROR("read_close")
+ }
+
+ END_FILEHANDLER_ROUTINE();
+
+ (*fp)->expired = TRUE;
+ lua_settop(L,0);
+
+ return;
+}
+
+/* Classic wtap sequential close function, called by wtap core.
+ */
+static void
+wslua_filehandler_sequential_close(wtap *wth)
+{
+ FileHandler fh = (FileHandler)(wth->wslua_data);
+ lua_State* L = NULL;
+ File *fp = NULL;
+
+ INIT_FILEHANDLER_ROUTINE(seq_read_close,);
+
+ fp = push_File(L, wth->fh);
+
+ switch ( lua_pcall(L,1,1,1) ) {
+ case 0:
+ break;
+ CASE_ERROR("seq_read_close")
+ }
+
+ END_FILEHANDLER_ROUTINE();
+
+ (*fp)->expired = TRUE;
+ lua_settop(L,0);
+
+ return;
+}
+
+
+/* basically a dummy function to use for can_write_encap so that the caller calls
+ * wslua_can_write_encap instead (which will be wslua_filehandler_can_write_encap)
+ */
+static int
+wslua_dummy_can_write_encap(int encap _U_)
+{
+ return WTAP_ERR_CHECK_WSLUA;
+}
+
+/* Similar to the classic wtap can_write_encap function.
+ * This returns 0 if the encap is ok for this file type.
+ */
+static int
+wslua_filehandler_can_write_encap(int encap, void* data)
+{
+ FileHandler fh = (FileHandler)(data);
+ int retval = WTAP_ERR_UNSUPPORTED_ENCAP;
+ lua_State* L = NULL;
+
+ INIT_FILEHANDLER_ROUTINE(can_write_encap,WTAP_ERR_INTERNAL);
+
+ lua_pushnumber(L, encap);
+
+ errno = WTAP_ERR_CANT_READ;
+ switch ( lua_pcall(L,1,1,1) ) {
+ case 0:
+ retval = wslua_optboolint(L,-1,WTAP_ERR_UNSUPPORTED_ENCAP);
+ break;
+ CASE_ERROR("can_write_encap")
+ }
+
+ END_FILEHANDLER_ROUTINE();
+
+ /* the retval we got was either a 1 for true, 0 for false, or WTAP_ERR_UNSUPPORTED_ENCAP;
+ but can_write_encap() expects 0 to be true/yes */
+ if (retval == 1) {
+ retval = 0;
+ } else if (retval == 0) {
+ retval = WTAP_ERR_UNSUPPORTED_ENCAP;
+ }
+
+ return retval;
+}
+
+/* some declarations */
+static gboolean
+wslua_filehandler_dump(wtap_dumper *wdh, const struct wtap_pkthdr *phdr,
+ const guint8 *pd, int *err);
+static gboolean
+wslua_filehandler_dump_close(wtap_dumper *wdh, int *err);
+
+
+/* The classic wtap dump_open function.
+ * This returns 1 (TRUE) on success.
+ */
+static int
+wslua_filehandler_dump_open(wtap_dumper *wdh, int *err)
+{
+ FileHandler fh = (FileHandler)(wdh->wslua_data);
+ int retval = 0;
+ lua_State* L = NULL;
+ File *fp = NULL;
+ CaptureInfoConst *fc = NULL;
+
+ INIT_FILEHANDLER_ROUTINE(write_open,0);
+
+ fp = push_Wdh(L, wdh);
+ fc = push_CaptureInfoConst(L,wdh);
+
+ /* Reset err */
+ *err = 0;
+
+ switch ( lua_pcall(L,2,1,1) ) {
+ case 0:
+ retval = wslua_optboolint(L,-1,0);
+ break;
+ CASE_ERROR("write_open")
+ }
+
+ END_FILEHANDLER_ROUTINE();
+
+ (*fp)->expired = TRUE;
+ (*fc)->expired = TRUE;
+
+ if (retval == 1) {
+ /* this is our file type - set the routines and settings into wtap */
+
+ if (fh->write_ref != LUA_NOREF) {
+ wdh->subtype_write = wslua_filehandler_dump;
+ }
+ else {
+ g_warning("FileHandler was not set with a write function, even though write_open() returned true");
+ return 0;
+ }
+
+ /* it's ok to not have a close routine */
+ if (fh->write_close_ref != LUA_NOREF)
+ wdh->subtype_close = wslua_filehandler_dump_close;
+ else
+ wdh->subtype_close = NULL;
+ }
+
+ return retval;
+}
+
+/* The classic wtap dump routine. This returns TRUE if it writes the current packet,
+ * else FALSE.
+*/
+static gboolean
+wslua_filehandler_dump(wtap_dumper *wdh, const struct wtap_pkthdr *phdr,
+ const guint8 *pd, int *err)
+{
+ FileHandler fh = (FileHandler)(wdh->wslua_data);
+ int retval = -1;
+ lua_State* L = NULL;
+ File *fp = NULL;
+ FrameInfoConst *fi = NULL;
+
+ INIT_FILEHANDLER_ROUTINE(write,FALSE);
+
+ /* Reset errno */
+ *err = errno = 0;
+
+ fp = push_Wdh(L, wdh);
+ fi = push_FrameInfoConst(L, phdr, pd);
+
+ errno = WTAP_ERR_CANT_READ;
+ switch ( lua_pcall(L,2,1,1) ) {
+ case 0:
+ retval = wslua_optboolint(L,-1,0);
+ break;
+ CASE_ERROR("write")
+ }
+
+ END_FILEHANDLER_ROUTINE();
+
+ (*fp)->expired = TRUE;
+ (*fi)->expired = TRUE;
+
+ return (retval == 1);
+}
+
+/* The classic wtap dump_close routine. This returns TRUE if it closes cleanly,
+ * else FALSE.
+*/
+static gboolean
+wslua_filehandler_dump_close(wtap_dumper *wdh, int *err)
+{
+ FileHandler fh = (FileHandler)(wdh->wslua_data);
+ int retval = -1;
+ lua_State* L = NULL;
+ File *fp = NULL;
+
+ INIT_FILEHANDLER_ROUTINE(write_close,FALSE);
+
+ /* Reset errno */
+ *err = errno = 0;
+
+ fp = push_Wdh(L, wdh);
+
+ errno = WTAP_ERR_CANT_READ;
+ switch ( lua_pcall(L,1,1,1) ) {
+ case 0:
+ retval = wslua_optboolint(L,-1,0);
+ break;
+ CASE_ERROR("write_close")
+ }
+
+ END_FILEHANDLER_ROUTINE();
+
+ (*fp)->expired = TRUE;
+
+ return (retval == 1);
+}
+
+
+WSLUA_CONSTRUCTOR FileHandler_new(lua_State* L) {
+ /* Creates a new FileHandler */
+#define WSLUA_ARG_FileHandler_new_NAME 1 /* The name of the file type, for display purposes only. E.g., "Wireshark - pcapng" */
+#define WSLUA_ARG_FileHandler_new_SHORTNAME 2 /* the file type short name, used as a shortcut in various places. E.g., "pcapng". Note: the name cannot already be in use. */
+#define WSLUA_ARG_FileHandler_new_DESCRIPTION 3 /* Descriptive text about this file format, for display purposes only */
+#define WSLUA_ARG_FileHandler_new_TYPE 4 /* The type of FileHandler, "r"/"w"/"rw" for reader/writer/both, include "m" for magic, "s" for strong heuristic */
+
+ const gchar* name = luaL_checkstring(L,WSLUA_ARG_FileHandler_new_NAME);
+ const gchar* short_name = luaL_checkstring(L,WSLUA_ARG_FileHandler_new_SHORTNAME);
+ const gchar* desc = luaL_checkstring(L,WSLUA_ARG_FileHandler_new_DESCRIPTION);
+ const gchar* type = luaL_checkstring(L,WSLUA_ARG_FileHandler_new_TYPE);
+ FileHandler fh = (FileHandler) g_malloc0(sizeof(struct _wslua_filehandler));
+
+ fh->is_reader = (strchr(type,'r') != NULL) ? TRUE : FALSE;
+ fh->is_writer = (strchr(type,'w') != NULL) ? TRUE : FALSE;
+
+ if (fh->is_reader && wtap_has_open_info(short_name)) {
+ return luaL_error(L, "FileHandler.new: '%s' short name already exists for a reader!", short_name);
+ }
+
+ if (fh->is_writer && wtap_short_string_to_file_type_subtype(short_name) > -1) {
+ return luaL_error(L, "FileHandler.new: '%s' short name already exists for a writer!", short_name);
+ }
+
+ fh->type = g_strdup(type);
+ fh->finfo.name = g_strdup(name);
+ fh->finfo.short_name = g_strdup(short_name);
+ fh->finfo.default_file_extension = NULL;
+ fh->finfo.additional_file_extensions = NULL;
+ fh->finfo.writing_must_seek = FALSE;
+ fh->finfo.has_name_resolution = FALSE;
+ fh->finfo.can_write_encap = NULL;
+ fh->finfo.dump_open = NULL;
+ /* this will be set to a new file_type when registered */
+ fh->file_type = WTAP_FILE_TYPE_SUBTYPE_UNKNOWN;
+
+ fh->description = g_strdup(desc);
+ fh->L = L;
+ fh->read_open_ref = LUA_NOREF;
+ fh->read_ref = LUA_NOREF;
+ fh->seek_read_ref = LUA_NOREF;
+ fh->read_close_ref = LUA_NOREF;
+ fh->seq_read_close_ref = LUA_NOREF;
+ fh->write_open_ref = LUA_NOREF;
+ fh->write_ref = LUA_NOREF;
+ fh->write_close_ref = LUA_NOREF;
+ fh->can_write_encap_ref = LUA_NOREF;
+
+ fh->registered = FALSE;
+
+ pushFileHandler(L,fh);
+ WSLUA_RETURN(1); /* The newly created FileHandler object */
+}
+
+WSLUA_METAMETHOD FileHandler__tostring(lua_State* L) {
+ /* Generates a string of debug info for the FileHandler */
+ FileHandler fh = toFileHandler(L,1);
+
+ if (!fh) {
+ lua_pushstring(L,"FileHandler pointer is NULL!");
+ } else {
+ lua_pushfstring(L, "FileHandler(%s): short-name='%s', description='%s', read_open=%d, read=%d, write=%d",
+ fh->finfo.name, fh->finfo.short_name, fh->description, fh->read_open_ref, fh->read_ref, fh->write_ref);
+ }
+
+ WSLUA_RETURN(1); /* String of debug information. */
+}
+
+static int FileHandler__gc(lua_State* L _U_) {
+ /* do NOT free FileHandler, it's never free'd */
+ /* TODO: handle this and other Lua things that should be free'd on exit, in a better way */
+ return 0;
+}
+
+/* A Lua File handler must not be expired, and must be either a reader or writer, and
+ * a *reader* one MUST at least define read_open, read, and seek_read funcs; and
+ * a *writer* one MUST at least define can_write_encap, write_open, and write funcs
+ */
+static gboolean verify_filehandler_complete(FileHandler fh) {
+ return ((fh->is_reader || fh->is_writer) &&
+ (!fh->is_reader ||
+ (fh->is_reader &&
+ fh->read_open_ref != LUA_NOREF &&
+ fh->read_ref != LUA_NOREF &&
+ fh->seek_read_ref != LUA_NOREF)) &&
+ (!fh->is_writer ||
+ (fh->is_writer &&
+ fh->can_write_encap_ref != LUA_NOREF &&
+ fh->write_open_ref != LUA_NOREF &&
+ fh->write_ref != LUA_NOREF)) );
+}
+
+
+WSLUA_FUNCTION wslua_register_filehandler(lua_State* L) {
+ /* Register the FileHandler into Wireshark/tshark, so they can read/write this new format.
+ All functions and settings must be complete before calling this registration function.
+ This function cannot be called inside the reading/writing callback functions. */
+#define WSLUA_ARG_register_filehandler_FILEHANDLER 1 /* the FileHandler object to be registered */
+ FileHandler fh = checkFileHandler(L,WSLUA_ARG_register_filehandler_FILEHANDLER);
+
+ if (in_routine)
+ return luaL_error(L,"a FileHAndler cannot be registered during reading/writing callback functions");
+
+ if (fh->registered)
+ return luaL_error(L,"this FileHandler is already registered");
+
+ if (!verify_filehandler_complete(fh))
+ return luaL_error(L,"this FileHandler is not complete enough to register");
+
+ if (fh->is_writer) {
+ fh->finfo.can_write_encap = wslua_dummy_can_write_encap;
+ fh->finfo.wslua_info = (wtap_wslua_file_info_t*) g_malloc0(sizeof(wtap_wslua_file_info_t));
+ fh->finfo.wslua_info->wslua_can_write_encap = wslua_filehandler_can_write_encap;
+ fh->finfo.wslua_info->wslua_data = (void*)(fh);
+ fh->finfo.dump_open = wslua_filehandler_dump_open;
+ }
+
+ fh->file_type = wtap_register_file_type_subtypes(&(fh->finfo),fh->file_type);
+
+ if (fh->is_reader) {
+ struct open_info oi = { NULL, OPEN_INFO_HEURISTIC, NULL, NULL, NULL };
+ oi.name = fh->finfo.short_name;
+ oi.open_routine = wslua_filehandler_open;
+ oi.extensions = fh->finfo.additional_file_extensions;
+ oi.wslua_data = (void*)(fh);
+ if (strchr(fh->type,'m') != NULL) {
+ oi.type = OPEN_INFO_MAGIC;
+ } else {
+ oi.type = OPEN_INFO_HEURISTIC;
+ }
+ wtap_register_open_info(&oi, (strchr(fh->type,'s') != NULL));
+ }
+
+ fh->registered = TRUE;
+
+ lua_pushnumber(L, fh->file_type);
+
+ WSLUA_RETURN(1); /* the new type number for this file reader/write */
+}
+
+WSLUA_FUNCTION wslua_deregister_filehandler(lua_State* L) {
+ /* De-register the FileHandler from Wireshark/tshark, so it no longer gets used for reading/writing/display.
+ This function cannot be called inside the reading/writing callback functions. */
+#define WSLUA_ARG_register_filehandler_FILEHANDLER 1 /* the FileHandler object to be de-registered */
+ FileHandler fh = checkFileHandler(L,WSLUA_ARG_register_filehandler_FILEHANDLER);
+
+ if (in_routine)
+ return luaL_error(L,"A FileHAndler cannot be de-registered during reading/writing callback functions");
+
+ if (!fh->registered)
+ return 0;
+
+ /* undo writing stuff, even if it wasn't a writer */
+ fh->finfo.can_write_encap = NULL;
+ if (fh->finfo.wslua_info) {
+ fh->finfo.wslua_info->wslua_can_write_encap = NULL;
+ fh->finfo.wslua_info->wslua_data = NULL;
+ g_free(fh->finfo.wslua_info);
+ fh->finfo.wslua_info = NULL;
+ }
+ fh->finfo.dump_open = NULL;
+
+ if (fh->file_type != WTAP_FILE_TYPE_SUBTYPE_UNKNOWN)
+ wtap_deregister_file_type_subtype(fh->file_type);
+
+ if (fh->is_reader && wtap_has_open_info(fh->finfo.short_name)) {
+ wtap_deregister_open_info(fh->finfo.short_name);
+ }
+
+ fh->registered = FALSE;
+
+ return 0;
+}
+
+/* The folllowing macros generate setter functions for Lua, for the following Lua
+ function references in _wslua_filehandler struct:
+ int read_open_ref;
+ int read_ref;
+ int seek_read_ref;
+ int read_close_ref;
+ int seq_read_close_ref;
+ int can_write_encap_ref;
+ int write_open_ref;
+ int write_ref;
+ int write_close_ref;
+*/
+
+/* WSLUA_ATTRIBUTE FileHandler_read_open WO The Lua function to be called when Wireshark opens a file for reading.
+
+ </para><para>
+ When later called by Wireshark, the Lua function will be given (1) a File object, and (2) a CaptureInfo object.
+
+ </para><para>
+ The purpose of the Lua function set to this 'read_open' field is to check if the file Wireshark is opening is of its type,
+ for example by checking for magic numbers or trying to parse records in the file, etc. The more can be verified
+ the better, because Wireshark tries all file readers until it finds one that accepts the file, so accepting an
+ incorrect file prevents other file readers from reading their files.
+
+ </para><para>
+ The called Lua function should return true if the file is its type (it accepts it), false if not. The Lua
+ function must also set the File offset position (using file:seek()) to where it wants it to be for its first
+ read() call, or back to beginning if it returns false.
+ */
+WSLUA_ATTRIBUTE_FUNC_SETTER(FileHandler,read_open);
+
+/* WSLUA_ATTRIBUTE FileHandler_read WO The Lua function to be called when Wireshark wants to read a packet from the file.
+
+ </para><para>
+ When later called by Wireshark, the Lua function will be given (1) a File object, and (2) a FrameInfo object.
+
+ </para><para>
+ The purpose of the Lua function set to this 'read' field is to read the next packet from the file, and setting the parsed/read
+ packet into the frame buffer using 'FrameInfo.data = foo' or 'FrameInfo:read_data()'.
+
+ </para><para>
+ The called Lua function should return the file offset/position number where the packet begins, or false if it hit an
+ error. The file offset will be saved by Wireshark and passed into the set seek_read() Lua function later. */
+WSLUA_ATTRIBUTE_FUNC_SETTER(FileHandler,read);
+
+/* WSLUA_ATTRIBUTE FileHandler_seek_read WO The Lua function to be called when Wireshark wants to read a packet from the file at the given offset.
+
+ </para><para>
+ When later called by Wireshark, the Lua function will be given (1) a File object, and (2) a FrameInfo object,
+ and (3) the file offset number previously set by the read function call. */
+WSLUA_ATTRIBUTE_FUNC_SETTER(FileHandler,seek_read);
+
+/* WSLUA_ATTRIBUTE FileHandler_read_close WO The Lua function to be called when Wireshark wants to close the read file completely.
+
+ </para><para>
+ When later called by Wireshark, the Lua function will be given (1) a File object.
+
+ </para><para>
+ It is not necessary to set this field to a Lua function - FileHandler can be registered without doing so - it
+ is available in case there is memory/state to clear in your script when the file is closed. */
+WSLUA_ATTRIBUTE_FUNC_SETTER(FileHandler,read_close);
+
+/* WSLUA_ATTRIBUTE FileHandler_seq_read_close WO The Lua function to be called when Wireshark wants to close the sequentially-read file.
+
+ </para><para>
+ When later called by Wireshark, the Lua function will be given a File object.
+
+ </para><para>
+ It is not necessary to set this field to a Lua
+ function - FileHandler can be registered without doing so - it is available in case there is memory/state to clear in your script
+ when the file is closed for the sequential reading portion. After this point, there will be no more calls to read(), only seek_read(). */
+WSLUA_ATTRIBUTE_FUNC_SETTER(FileHandler,seq_read_close);
+
+
+/* WSLUA_ATTRIBUTE FileHandler_can_write_encap WO The Lua function to be called when Wireshark wants to write a file,
+ by checking if this file writer can handle the wtap packet encapsulation(s).
+
+ </para><para>
+ When later called by Wireshark, the Lua function will be given a Lua number, which matches one of the encapsulations
+ in the Lua 'wtap_encaps' table. This might be the 'wtap_encap.PER_PACKET' number, meaning the capture contains multiple
+ encapsulation types, and the file reader should only return true if it can handle multiple encap types in one file. The
+ function will then be called again, once for each encap type in the file, to make sure it can write each one.
+
+ </para><para>
+ If the Lua file writer can write the given type of encapsulation into a file, then it returns the boolean true, else false. */
+WSLUA_ATTRIBUTE_FUNC_SETTER(FileHandler,can_write_encap);
+
+/* WSLUA_ATTRIBUTE FileHandler_write_open WO The Lua function to be called when Wireshark opens a file for writing.
+
+ </para><para>
+ When later called by Wireshark, the Lua function will be given (1) a File object, and (2) a CaptureInfoConst object.
+
+ </para><para>
+ The purpose of the Lua function set to this 'write_open' field is similar to the read_open callback function:
+ to initialize things necessary for writing the capture to a file. For example, if the output file format has a
+ file header, then the file header should be written within this write_open function.
+
+ </para><para>
+ The called Lua function should return true on success, or false if it hit an error.
+ Also make sure to set the FileHandler.write (and potentially FileHandler.write_close) functions before
+ returning true from this function. */
+WSLUA_ATTRIBUTE_FUNC_SETTER(FileHandler,write_open);
+
+/* WSLUA_ATTRIBUTE FileHandler_write WO The Lua function to be called when Wireshark wants to write a packet to the file.
+
+ </para><para>
+ When later called by Wireshark, the Lua function will be given (1) a File object, and (2) a FrameInfoConst object
+ of the current frame/packet to be written.
+
+ </para><para>
+ The purpose of the Lua function set to this 'write' field is to write the next packet to the file.
+
+ </para><para>
+ The called Lua function should return true on succcess, or false if it hit an error. */
+WSLUA_ATTRIBUTE_FUNC_SETTER(FileHandler,write);
+
+/* WSLUA_ATTRIBUTE FileHandler_write_close WO The Lua function to be called when Wireshark wants to close the written file.
+
+ </para><para>
+ When later called by Wireshark, the Lua function will be given a File object.
+
+ </para><para>
+ It is not necessary to set this field to a Lua function - FileHandler can be registered without doing so - it is available
+ in case there is memory/state to clear in your script when the file is closed. */
+WSLUA_ATTRIBUTE_FUNC_SETTER(FileHandler,write_close);
+
+/* generate other member accessors setters/getters */
+
+/* WSLUA_ATTRIBUTE FileHandler_type RO The internal file type. This is automatically set with a new
+ number when the FileHandler is registered. */
+WSLUA_ATTRIBUTE_NAMED_NUMBER_GETTER(FileHandler,type,file_type);
+
+/* WSLUA_ATTRIBUTE FileHandler_extensions RW One or more file extensions that this file type usually uses.
+
+ </para><para>
+ For readers using heuristics to determine file type, Wireshark will try the readers of the file's
+ extension first, before trying other readers. But ultimately Wireshark tries all file readers
+ for any file extension, until it finds one that accepts the file. */
+WSLUA_ATTRIBUTE_NAMED_STRING_GETTER(FileHandler,extensions,finfo.additional_file_extensions);
+WSLUA_ATTRIBUTE_NAMED_STRING_SETTER(FileHandler,extensions,finfo.additional_file_extensions,TRUE);
+
+/* WSLUA_ATTRIBUTE FileHandler_writing_must_seek RW true if the ability to seek is required when writing
+ this file format, else false. This will be checked by Wireshark when writing out to compressed
+ file formats, because seeking is not possible with compressed files. Usually a file writer only
+ needs to be able to seek if it needs to go back in the file to change something, such as a block or
+ file length value earlier in the file. */
+WSLUA_ATTRIBUTE_NAMED_BOOLEAN_GETTER(FileHandler,writing_must_seek,finfo.writing_must_seek);
+WSLUA_ATTRIBUTE_NAMED_BOOLEAN_SETTER(FileHandler,writing_must_seek,finfo.writing_must_seek);
+
+/* WSLUA_ATTRIBUTE FileHandler_writes_name_resolution RW true if the file format supports name resolution
+ records, else false. */
+WSLUA_ATTRIBUTE_NAMED_BOOLEAN_GETTER(FileHandler,writes_name_resolution,finfo.has_name_resolution);
+WSLUA_ATTRIBUTE_NAMED_BOOLEAN_SETTER(FileHandler,writes_name_resolution,finfo.has_name_resolution);
+
+/* WSLUA_ATTRIBUTE FileHandler_supported_comment_types RW set to the bit-wise OR'ed number representing
+ the type of comments the file writer supports writing, based on the numbers in the 'wtap_comments' table. */
+WSLUA_ATTRIBUTE_NAMED_NUMBER_GETTER(FileHandler,supported_comment_types,finfo.supported_comment_types);
+WSLUA_ATTRIBUTE_NAMED_NUMBER_SETTER(FileHandler,supported_comment_types,finfo.supported_comment_types,guint32);
+
+/* This table is ultimately registered as a sub-table of the class' metatable,
+ * and if __index/__newindex is invoked then it calls the appropriate function
+ * from this table for getting/setting the members.
+ */
+WSLUA_ATTRIBUTES FileHandler_attributes[] = {
+ WSLUA_ATTRIBUTE_WOREG(FileHandler,read_open),
+ WSLUA_ATTRIBUTE_WOREG(FileHandler,read),
+ WSLUA_ATTRIBUTE_WOREG(FileHandler,seek_read),
+ WSLUA_ATTRIBUTE_WOREG(FileHandler,read_close),
+ WSLUA_ATTRIBUTE_WOREG(FileHandler,seq_read_close),
+ WSLUA_ATTRIBUTE_WOREG(FileHandler,can_write_encap),
+ WSLUA_ATTRIBUTE_WOREG(FileHandler,write_open),
+ WSLUA_ATTRIBUTE_WOREG(FileHandler,write),
+ WSLUA_ATTRIBUTE_WOREG(FileHandler,write_close),
+ WSLUA_ATTRIBUTE_ROREG(FileHandler,type),
+ WSLUA_ATTRIBUTE_RWREG(FileHandler,extensions),
+ WSLUA_ATTRIBUTE_RWREG(FileHandler,writing_must_seek),
+ WSLUA_ATTRIBUTE_RWREG(FileHandler,writes_name_resolution),
+ WSLUA_ATTRIBUTE_RWREG(FileHandler,supported_comment_types),
+ { NULL, NULL, NULL }
+};
+
+WSLUA_METHODS FileHandler_methods[] = {
+ {"new", FileHandler_new},
+ { NULL, NULL }
+};
+
+WSLUA_META FileHandler_meta[] = {
+ {"__tostring", FileHandler__tostring},
+ { NULL, NULL }
+};
+
+int FileHandler_register(lua_State* L) {
+ WSLUA_REGISTER_CLASS(FileHandler);
+ WSLUA_REGISTER_ATTRIBUTES(FileHandler);
+ return 0;
+}
diff --git a/epan/wslua/wslua_util.c b/epan/wslua/wslua_util.c
index d912607fef..f66310bf7c 100644
--- a/epan/wslua/wslua_util.c
+++ b/epan/wslua/wslua_util.c
@@ -287,7 +287,7 @@ WSLUA_CONSTRUCTOR Dir_open(lua_State* L) {
}
dir = (Dir)g_malloc(sizeof(struct _wslua_dir));
- dir->dir = OPENDIR_OP(dirname_clean);
+ dir->dir = g_dir_open(dirname_clean, 0, dir->dummy);
g_free(dirname_clean);
dir->ext = extension ? g_strdup(extension) : NULL;
dir->dummy = (GError **)g_malloc(sizeof(GError *));
@@ -309,7 +309,7 @@ WSLUA_METAMETHOD Dir__call(lua_State* L) {
/* At every invocation will return one file (nil when done) */
Dir dir = checkDir(L,1);
- const FILE_T* file;
+ const gchar* file;
const gchar* filename;
const char* ext;
@@ -317,21 +317,20 @@ WSLUA_METAMETHOD Dir__call(lua_State* L) {
return 0;
}
- if ( ! ( file = DIRGETNEXT_OP(dir->dir ) )) {
- CLOSEDIR_OP(dir->dir);
+ if ( ! ( file = g_dir_read_name(dir->dir ) )) {
+ g_dir_close(dir->dir);
dir->dir = NULL;
return 0;
}
if ( ! dir->ext ) {
- filename = GETFNAME_OP(file);
- lua_pushstring(L,filename);
+ lua_pushstring(L,file);
return 1;
}
do {
- filename = GETFNAME_OP(file);
+ filename = file;
/* XXX strstr returns ptr to first match,
this fails ext=".xxx" filename="aaa.xxxz.xxx" */
@@ -339,9 +338,9 @@ WSLUA_METAMETHOD Dir__call(lua_State* L) {
lua_pushstring(L,filename);
return 1;
}
- } while(( file = DIRGETNEXT_OP(dir->dir) ));
+ } while(( file = g_dir_read_name(dir->dir) ));
- CLOSEDIR_OP(dir->dir);
+ g_dir_close(dir->dir);
dir->dir = NULL;
return 0;
}
@@ -351,7 +350,7 @@ WSLUA_METHOD Dir_close(lua_State* L) {
Dir dir = checkDir(L,1);
if (dir->dir) {
- CLOSEDIR_OP(dir->dir);
+ g_dir_close(dir->dir);
dir->dir = NULL;
}
@@ -365,7 +364,7 @@ static int Dir__gc(lua_State* L) {
if(!dir) return 0;
if (dir->dir) {
- CLOSEDIR_OP(dir->dir);
+ g_dir_close(dir->dir);
}
g_free(dir->dummy);
diff --git a/test/captures/sip.pcapng b/test/captures/sip.pcapng
new file mode 100644
index 0000000000..58373b9aa5
--- /dev/null
+++ b/test/captures/sip.pcapng
Binary files differ
diff --git a/test/captures/sipmsg.log b/test/captures/sipmsg.log
new file mode 100644
index 0000000000..d69c99c072
--- /dev/null
+++ b/test/captures/sipmsg.log
@@ -0,0 +1,136 @@
+File opened.
+Mar 6 13:34:22.599 UDP[3:0]10.102.131.194:5060 OPENED
+Mar 6 13:34:22.616 UDP[6:0]10.102.130.185:5060 OPENED
+Mar 6 13:34:49.416 On [6:0]10.102.130.185:5060 received from 10.102.130.150:5060
+REGISTER sip:csp.noklab.net SIP/2.0
+Via: SIP/2.0/UDP 192.168.1.100:5060;branch=z9hG4bK26b7a48d
+From: sip:34903@csp.noklab.net
+To: sip:34903@csp.noklab.net
+Call-ID: 003094c3-a0160002-23aa7e86-29e5808d@192.168.1.100
+CSeq: 144 REGISTER
+User-Agent: CSCO/7
+Contact: <sip:34903@192.168.1.100:5060>
+Content-Length: 0
+Expires: 3600
+
+
+----------------------------------------
+Mar 6 13:34:49.516 On [6:0]10.102.130.185:5060 sent to 10.102.130.150:5060
+SIP/2.0 200 OK
+Via: SIP/2.0/UDP 192.168.1.100:5060;received=10.102.130.150;branch=z9hG4bK26b7a48d;rport=5060
+From: sip:34903@csp.noklab.net
+To: sip:34903@csp.noklab.net
+Call-ID: 003094c3-a0160002-23aa7e86-29e5808d@192.168.1.100
+CSeq: 144 REGISTER
+Contact: <sip:34903@192.168.1.100:5060>;expires=34
+
+
+----------------------------------------
+Mar 6 13:39:06.100 On 127.0.0.1:5060 received from 127.0.0.1:5070
+INVITE sip:17324201111@135.25.31.10:5060;acme_realm=cpea8500 SIP/2.0
+Via: SIP/2.0/UDP 127.0.0.1:5070;branch=z9hG4bKIWFuqpq6n00c0o1eckfm741;acme_irealm=public;acme_sa=192.168.109.112
+Contact: "B5-2C23-052 Blu"<sip:7323685154@127.0.0.1:5070>
+GenericID: 117318834600008@0008250123d0
+Supported: 100rel
+From: "B5-2C23-052 Blu"<sip:7323685154@127.25.29.135:5060>;tag=0000047b000ce0e0
+To: <sip:17324201111@135.25.31.10:5060>
+Call-ID: 7f00000113ce0000047b000cd140@127.0.0.1
+CSeq: 2 INVITE
+P-Asserted-Identity: "B5-2C23-052 Blu"<sip:7323685154@127.25.29.135:5060>
+Content-Length: 187
+Content-Type: application/sdp
+
+v=0
+o=IWF 10 10 IN IP4 192.168.109.113
+s=H323 Call
+c=IN IP4 192.168.109.113
+t=0 0
+m=audio 29156 RTP/AVP 18 0
+a=rtpmap:18 G729/8000/1
+a=fmtp:18 annexb=yes
+a=rtpmap:0 PCMU/8000/1
+
+----------------------------------------
+Mar 6 13:39:06.104 On 127.0.0.1:5060 sent to 127.0.0.1:5070
+SIP/2.0 100 Trying
+Via: SIP/2.0/UDP 127.0.0.1:5070;branch=z9hG4bKIWFuqpq6n00c0o1eckfm741
+From: "B5-2C23-052 Blu"<sip:7323685154@127.0.0.1:5060>;tag=0000047b000ce0e0
+To: <sip:17324201111@127.0.0.1:5060>
+Call-ID: 7f00000113ce0000047b000cd140@127.0.0.1
+CSeq: 2 INVITE
+
+
+----------------------------------------
+Mar 6 13:39:06.122 On 127.0.0.1:2945 sent to 127.0.0.1:2944
+ 0000: ac 3e fd 01 00 07 89 d9 00 fc 10 00 00 00 02 00 .>..............
+ 0010: 00 00 00 00 f1 21 00 00 00 02 00 6d 30 00 6a 8c .....!.....m0.j.
+ 0020: 00 02 20 01 80 00 06 70 75 62 6c 69 63 83 00 05 .. ....public...
+ 0030: 24 57 45 53 54 84 00 08 63 70 65 61 38 35 30 30 $WEST...cpea8500
+ 0040: 86 00 05 24 45 41 53 54 88 00 01 01 8b 00 01 00 ...$EAST........
+ 0050: 89 00 02 00 02 8a 00 04 00 00 00 00 98 00 04 00 ................
+ 0060: 00 00 00 99 00 04 00 00 00 00 9a 00 04 00 00 00 ................
+ 0070: 00 94 00 04 87 19 1f 0a 96 00 01 01 a7 00 01 00 ................
+ 0080: a8 00 01 00 a9 00 02 00 00 21 00 00 00 02 00 76 .........!.....v
+ 0090: 30 00 73 8c 00 02 10 01 80 00 08 63 70 65 61 38 0.s........cpea8
+ 00a0: 35 30 30 83 00 05 24 45 41 53 54 84 00 06 70 75 500...$EAST...pu
+ 00b0: 62 6c 69 63 86 00 05 24 57 45 53 54 87 00 06 c0 blic...$WEST....
+ 00c0: a8 6d 71 71 e4 88 00 01 01 8b 00 01 01 89 00 02 .mqq............
+ 00d0: 00 02 8a 00 04 00 00 00 00 98 00 04 00 00 00 00 ................
+ 00e0: 99 00 04 00 00 00 00 9a 00 04 00 00 00 00 94 00 ................
+ 00f0: 04 7f 00 00 01 96 00 01 01 a7 00 01 00 a8 00 01 ................
+ 0100: 00 a9 00 02 00 00 ......
+Transaction = 494041 {
+ Context = $ {
+ Add = $ {
+ Flow {
+ index=1E
+ irealm=public
+ idest=$WEST
+ erealm=cpea8500
+ esource=$EAST
+ media=audio
+ trans=UDP
+ mode=off
+ num=2
+ bw=0
+ peakr=0
+ avgr=0
+ mbs=0
+ subscr=135.25.31.10
+ }
+ },
+ Add = $ {
+ Flow {
+ index=1W
+ irealm=cpea8500
+ idest=$EAST
+ erealm=public
+ esource=$WEST
+ edest=192.168.109.113:29156
+ media=audio
+ trans=UDP
+ mode=1way
+ num=2
+ bw=0
+ peakr=0
+ avgr=0
+ mbs=0
+ subscr=127.0.0.1
+ }
+ }
+ }
+}
+----------------------------------------
+Mar 6 13:39:06.127 On 127.0.0.1:5060 sent to 127.0.0.1:5070
+SIP/2.0 181 Call Is Being Forwarded
+Via: SIP/2.0/UDP 127.0.0.1:5070;branch=z9hG4bKIWFuqpq6n00c0o1eckfm741;acme_iwf_2833_preferred=101
+From: "B5-2C23-052 Blu"<sip:7323685154@127.25.29.135:5060>;tag=0000047b000ce0e0
+To: <sip:17324201111@135.25.31.10:5060>
+Call-ID: 7f00000113ce0000047b000cd140@127.0.0.1
+CSeq: 2 INVITE
+
+
+----------------------------------------
+Jun 8 14:35:50.233 UDP[3:0]10.102.131.194:5060 CLOSED
+Jun 8 14:35:50.233 UDP[6:0]10.102.130.185:5060 CLOSED
+File closed.
diff --git a/test/lua/acme_file.lua b/test/lua/acme_file.lua
new file mode 100644
index 0000000000..8eeaea5473
--- /dev/null
+++ b/test/lua/acme_file.lua
@@ -0,0 +1,1365 @@
+------------------------------------------
+-- acme_file_reader.lua
+-- Author: Hadriel Kaplan (hadrielk at yahoo dot com)
+-- version = 1.0
+-- date = 3/3/2014
+------------------------------------------
+--[[
+ This is a Wireshark Lua-based capture file reader.
+ This "capture file" reader reads message logs from Acme Packet (now Oracle) Session Border Controllers,
+ such as sipmsg.log files. There are several variants of the log file format, as well as some changes that
+ can happen based on how the log file is generated and retrieved; for example if it's generated through a
+ 'tail' command, or FTP'ed by a FTP client which adds carriage-returns. This Lua file reader tries to handle
+ such conditions.
+
+ Note: this script wasn't written to be super-efficient, nor clever. When you've been writing Lua for a while
+ you get used to writing in a different, more elegant fashion than this script is; but other people find it
+ hard to read such Lua code, so I've tried to keep this simpler.
+
+ Features:
+ -handles sipmsg type logs, sipdns type logs, algd type logs
+ -handles both IPv4 and IPv6, for both UDP and TCP
+ -reads sipmsg logs from 3800, 4250, 4500, 9200, 6300 SBCs
+ -handles logs with extra carriage-returns and linefeeds, such as from certain FTP'ed cases
+ -handles logs generated/copied from a 'tail' command on the SBC ACLI
+ -handles MBCD messages in logs, and puts their decoded ascii description in comments in Wireshark
+
+ Issues:
+ -for very large logs (many megabytes), it takes a long time (many minutes)
+ -creates fake IP and UDP/TCP headers, which might be misleading
+ -has to guess sometimes, though it hasn't guessed wrong yet as far as I know
+
+ To-do:
+ - make it use Struct.tohex/fromhex now that we have the Struct library in Wireshark
+ - make it use a linux cooked-mode pseudo-header (see http://wiki.wireshark.org/SLL)
+ - make it use preferences, once I write C-code for Wireshark to do that :)
+ - rewrite some of the pattern searches to use real regex/PCRE instead? It's not in Wireshark yet,
+ but it's coming (see https://code.wireshark.org/review/#/c/332/)
+
+Example SIP over UDP message:
+Aug 26 19:25:10.685 On [5:0]2.1.1.1:5060 received from 2.1.2.115:5060
+REGISTER sip:2.1.1.1:5060 SIP/2.0
+Via: SIP/2.0/UDP 2.1.2.115:5060;branch=z9hG4bK6501441021660x81000412
+From: <sip:public_115@2.1.1.1:5060>;tag=520052-7015560x81000412
+To: <sip:public_115@2.1.1.1:5060>
+Call-ID: 680192-4234150x81000412@2.1.2.115
+CSeq: 247 REGISTER
+Contact: <sip:public_115@2.1.2.115:5060;transport=udp>
+Expires: 300
+Max-Forwards: 70
+Authorization: Digest username="public_115",realm="empirix.com",uri="sip:2.1.1.1",response="5d61837cc54dc27018a40f2532e622de",nonce="430f6ff09ecd8c3fdfc5430b6e7e437a4cf77057",algorithm=md5
+Content-Length: 0
+
+
+----------------------------------------
+Another one:
+2007-03-06 13:38:48.037 OPENED
+2007-03-06 13:38:48.037 OPENED
+2007-03-06 13:38:48.037 OPENED
+Mar 6 13:38:54.959 On [1:0]135.25.29.135:5060 received from 192.168.109.138:65471
+OPTIONS sip:135.25.29.135 SIP/2.0
+Accept: application/sdp
+User-Agent: ABS GW v5.1.0
+To: sip:135.25.29.135
+From: sip:192.168.109.138;tag=a2a090ade36bb108da70b0c8f7ba02e9
+Contact: sip:192.168.109.138
+Call-ID: 8c0296da4a0d9f4d97e1389cd28d2352@172.16.6.114
+CSeq: 347517161 OPTIONS
+Via: SIP/2.0/UDP 192.168.109.138;branch=z9hG4bK21feac80fe9a63c1cf2988baa2af0849
+Max-Forwards: 70
+Content-Length: 0
+
+
+----------------------------------------
+Another SIP over UDP (from 9200):
+File opened.
+Jun 8 14:34:22.599 UDP[3:0]10.102.131.194:5060 OPENED
+Jun 8 14:34:22.616 UDP[6:0]10.102.130.185:5060 OPENED
+Jun 8 14:34:49.416 On [6:0]10.102.130.185:5060 received from 10.102.130.150:5060
+REGISTER sip:csp.noklab.net SIP/2.0
+Via: SIP/2.0/UDP 192.168.1.100:5060;branch=z9hG4bK26b7a48d
+From: sip:34903@csp.noklab.net
+To: sip:34903@csp.noklab.net
+Call-ID: 003094c3-a0160002-23aa7e86-29e5808d@192.168.1.100
+CSeq: 144 REGISTER
+User-Agent: CSCO/7
+Contact: <sip:34903@192.168.1.100:5060>
+Content-Length: 0
+Expires: 3600
+
+
+----------------------------------------
+
+Example SIP over TCP message (note it ends in the middle of a header name):
+Jan 12 00:03:54.700 On 172.25.96.200:8194 received from 172.25.32.28:5060
+SIP/2.0 200 OK
+From: Unavailable <sip:Unavailable@172.25.96.200:5060;user=phone>;tag=1200822480
+To: 24001900011 <sip:0011@172.25.32.28:5060;user=phone>;tag=03c86c0b27df1b1254aeccbc000
+Call-ID: 7f6b0887-1d313896-1511da31-b045@144.229.136.111
+CSe
+----------------------------------------
+
+Example SIP Pre and Post-NAT messages:
+Post-NAT from private<realm=e911core> encoded:
+SIP/2.0 302 Moved Temporarily
+Call-ID: SD27o9f04-fcc63aa885c83e22a1be64cfc210b55e-vjvtv00
+CSeq: 2 INVITE
+From: <sip:7866932005@127.1.0.100:5060;user=phone;e911core=TSD5051AEPCORE-dnamt76v6nm04;CKE=BSLD-5cuduig6t52l2;e911vpn=TSD5051AEPVPN-7gdq13vt8fi59>;tag=SD27o9f04-10000000-0-1424021314
+To: <sip:911@127.0.0.100;user=phone;CKE=BSLD-8blt7m3dhnj17>;tag=10280004-0-1239441202
+Via: SIP/2.0/UDP 127.254.254.1:5060;branch=z9hG4bK5i4ue300dgrdras7q281.1
+Server: DC-SIP/1.2
+Content-Length: 0
+Contact: <sip:1111119999@127.0.0.100:5060;e911core=TSD5051AEPCORE-5n86t36uuma01>
+
+
+----------------------------------------
+Pre-NAT to private<realm=e911core> decode:
+ACK sip:911@127.0.0.100;user=phone;CKE=BSLD-8blt7m3dhnj17 SIP/2.0
+Via: SIP/2.0/UDP 127.254.254.1:5060;branch=z9hG4bK5i4ue300dgrdras7q281.1
+Call-ID: SD27o9f04-fcc63aa885c83e22a1be64cfc210b55e-vjvtv00
+CSeq: 2 ACK
+From: <sip:7866932005@127.1.0.100:5060;user=phone;e911core=TSD5051AEPCORE-dnamt76v6nm04;CKE=BSLD-5cuduig6t52l2;e911vpn=TSD5051AEPVPN-7gdq13vt8fi59>;tag=SD27o9f04-10000000-0-1424021314
+To: <sip:911@127.0.0.100;user=phone;CKE=BSLD-8blt7m3dhnj17>;tag=10280004-0-1239441202
+Max-Forwards: 70
+
+
+----------------------------------------
+
+Example DNS message:
+Nov 1 23:03:12.811 On 10.21.232.194:1122 received from 10.21.199.204:53
+DNS Response 3916 flags=8503 q=1 ans=0 auth=1 add=0 net-ttl=0
+ Q:NAPTR 7.6.5.4.3.2.1.0.1.2.e164
+ NS:SOA e164 ttl=0 netnumber01
+ rname=user.netnumber01
+ ser=223 ref=0 retry=0 exp=0 minttl=0
+
+ 0000: 0f 4c 85 03 00 01 00 00 00 01 00 00 01 37 01 36 .L...........7.6
+ 0010: 01 35 01 34 01 33 01 32 01 31 01 30 01 31 01 32 .5.4.3.2.1.0.1.2
+ 0020: 04 65 31 36 34 00 00 23 00 01 04 65 31 36 34 00 .e164..#...e164.
+ 0030: 00 06 00 01 00 00 00 00 00 33 0b 6e 65 74 6e 75 .........3.netnu
+ 0040: 6d 62 65 72 30 31 00 04 75 73 65 72 0b 6e 65 74 mber01..user.net
+ 0050: 6e 75 6d 62 65 72 30 31 00 00 00 00 df 00 00 00 number01........
+ 0060: 00 00 00 00 00 00 00 00 00 00 00 00 00 .............
+
+----------------------------------------
+Example MGCP message (note the IP/UDP headers are in the hex):
+Mar 1 14:37:26.683 On [0:803]172.16.84.141:2427 sent to 172.16.74.100:2427
+Packet:
+ 0000: 00 04 00 00 00 01 00 02 00 00 03 23 0a ad 00 c9 ...........#....
+ 0010: 45 00 00 a8 23 36 00 00 3c 11 63 fd ac 10 54 8d E...#6..<.c...T.
+ 0020: ac 10 4a 64 09 7b 09 7b 00 94 16 c2 32 35 30 20 ..Jd.{.{....250
+
+250 55363 Connection Deleted
+P: PS=6551, OS=1048160, PR=6517, OR=1042720, PL=0, JI=1, LA=5, PC/RPS=6466, PC/ROS=1034560, PC/RPL=0, PC/RJI=0
+
+----------------------------------------
+Example MBCD message:
+Mar 1 14:37:26.672 On 127.0.0.1:2946 sent to 127.0.0.1:2944
+ 0000: ac 3e fd a8 01 01 77 36 9e 00 37 10 0c 34 4c bc .>....w6..7..4L.
+ 0010: 00 30 23 0c 34 4c bc 00 11 33 00 0e 35 00 04 00 .0#.4L...3..5...
+ 0020: 00 00 00 30 00 04 00 00 00 00 23 0c 34 4c bd 00 ...0......#.4L..
+ 0030: 11 33 00 0e 35 00 04 00 00 00 00 30 00 04 00 00 .3..5......0....
+ 0040: 00 00 ..
+Transaction = 24589982 {
+ Context = 204754108 {
+ Subtract = 204754108 {
+ Audit {
+ Stats,
+ Flow
+ }
+ },
+ Subtract = 204754109 {
+ Audit {
+ Stats,
+ Flow
+ }
+ }
+ }
+}
+----------------------------------------
+
+]]----------------------------------------
+
+-- debug printer, set DEBUG to true to enable printing debug info
+-- set DEBUG2 to true to enable really verbose printing
+local DEBUG, DEBUG2 = true, false
+
+local dprint = function() end
+local dprint2 = function() end
+if DEBUG or DEBUG2 then
+ dprint = function(...)
+ print(table.concat({"Lua:", ...}," "))
+ end
+
+ if DEBUG2 then
+ dprint2 = dprint
+ end
+end
+
+local fh = FileHandler.new("Oracle Acme Packet logs", "acme", "A file reader for Oracle Acme Packet message logs such as sipmsg.log","rs")
+
+
+-- there are certain things we have to create fake state/data for, because they don't exist in the log file
+-- for example to create IP headers we have to cerate fake identification field values, and to create
+-- timestamps we have to guess the year (and in some cases month/day as well), and for TCP we have
+-- to create fake conneciton info, such as sequence numbers. We can't simply have a global static variable holding
+-- such things, because Wireshark reads the file sequentially at first, but then calls seek_read for random
+-- packets again and we don't want to re-create the fake info again because it will be wrong. So we need to
+-- create it for each packet and remember what we created for each packet, so that seek_read gets the same values.
+-- We could store the variables in a big table, keyed by the specific header info line for each one; but instead we'll
+-- key it off of the file position number, since read() sets it for Wireshark and seek_read() gets it from Wireshark.
+-- So we'll have a set of global statics used during read(), but the actual per-packet values will be stored in
+-- a table indexed/keyed by the file position number. A separate table holds TCP peer connection info as described later.
+
+-- the following local table holds global (to this file) static variables that need to be reset every new file read
+local statics = { ["ip_ident"] = 0, ["tyear"] = 0, ["tmonth"] = 0, ["tmin"] = 0, ["tmin"] = 0, ["tsec"] = 0, ["tmilli"] = 0, ["nstime"] = NSTime() }
+
+-- the following table holds per-packet info
+-- the key index will be a number - the file position - but it won't be an array type table (too sparse).
+-- Each packets entry is a table holding the "static" variables for that packet; this sub-table will be
+-- an array style instead of hashmap, to reduce size/performance
+-- This table needs to be cleared whenever the file is closed/opened.
+local packets = {}
+-- the indeces for the variable sub-tables
+local IP_IDENT = 1
+local TTIME = 2
+local LOCAL_SEQ = 3
+local REMOTE_SEQ = 4
+
+-- the following local table holds TCP peer "connection" info, which is basically
+-- TCP control block (TCB) type information; this is needed to create and keep track
+-- of fake TCP sockets/headers for messages that went over TCP, for example for fake
+-- sequence number info.
+-- The key index for this is the local+remote ip:port strings concatenated.
+-- The value is a sub-table, array style, holding the most recent sequence numbers.
+-- This whole table needs to be cleared whenever the file is closed/opened.
+local tcb = {}
+-- the indeces for the sub-tables
+local TLOCAL_SEQ = 1
+local TREMOTE_SEQ = 2
+
+local function reset_state()
+ tcb = {}
+ packets = {}
+ for name, v in pairs(statics) do
+ statics[name] = 0
+ end
+ statics.nstime = NSTime()
+end
+
+-- helper functions
+local char = string.char
+local floor = math.floor
+
+-- takes a Lua number and converts it into a 2-byte string binary (network order)
+
+local function dec2bin16(num)
+ return Struct.pack(">I2",num)
+end
+
+-- takes a Lua number and converts it into a 4-byte string binary (network order)
+local function dec2bin32(num)
+ return Struct.pack(">I4",num)
+end
+
+
+-- function to skip log info before/between/after messages
+local delim = "^%-%-%-%-%-%-%-%-%-%-%-%-%-%-%-%-%-%-%-%-%-%-%-%-%-%-%-%-%-%-%-%-%-%-%-%-%-%-%-%-$"
+-- words that must be found to be skipped. "File ..." is found in 9200 logs)
+local skiplist = { " OPENED", " CLOSED", " STARTED", " STOPPED", "^File ", delim }
+-- pre/post NAT entries
+local pre_nat_header_pattern = "^Pre%-NAT to private<realm=([^>]+)> decode:\r?$"
+local post_nat_header_pattern = "^Post%-NAT from private<realm=([^>]+)> encoded:\r?$"
+
+local function skip_ahead(file, line, position)
+ repeat
+ local found = #line == 0 -- will be false unless the line is empty
+ for i, word in ipairs(skiplist) do
+ if line:find(word) then
+ found = true
+ break
+ end
+ end
+ if found then
+ position = file:seek()
+ line = file:read()
+ if not line then return nil end
+ elseif line:find(pre_nat_header_pattern) or line:find(post_nat_header_pattern) then
+ -- skip the whole message
+ found = true
+ repeat
+ line = file:read()
+ until line:find(delim)
+ end
+ until not found
+ return line, position
+end
+
+-- following pattern grabs month, day, hour, min, sec, millisecs
+local header_time_pattern = "^(%u%l%l) ?(%d%d?) (%d%d?):(%d%d):(%d%d)%.(%d%d%d) On "
+-- tail'ed file has no month/day
+local header_tail_time_pattern = "^(%d%d):(%d%d)%.(%d%d%d) On "
+
+-- grabs local and remote IPv4:ports (not phy/vlan), and words in between (i.e., "sent to" or "received from")
+local header_address_pattern = "(%d%d?%d?%.%d%d?%d?%.%d%d?%d?%.%d%d?%d?):(%d+) (%l+ %l+) (%d%d?%d?%.%d%d?%d?%.%d%d?%d?%.%d%d?%d?):(%d+) ?\r?$"
+-- grabs local and remote IPv6:ports (not phy/vlan), and words in between (i.e., "sent to" or "received from")
+local header_v6address_pattern = "%[([:%x]+)%]:(%d+) (%l+ %l+) %[([:%x]+)%]:(%d+) ?\r?$"
+
+-- grabs phy/vlan info
+local header_phy_pattern = "%[(%d+):(%d+)%]"
+
+local SENT = 1
+local RECV = 2
+local function get_direction(phrase)
+ if #phrase == 7 and phrase:find("sent to") then
+ return SENT
+ elseif #phrase == 13 and phrase:find("received from") then
+ return RECV
+ end
+ dprint("direction phrase not found")
+ return nil
+end
+
+-- monthlist table for getting month number value from 3-char name (number is table index)
+local monthlist = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}
+
+local function get_timestamp(line, file_position, seeking)
+ local i, line_pos, month, day, hour, min, sec, milli = line:find(header_time_pattern)
+ if not month then
+ return
+ end
+
+ if seeking then
+ -- we've seen this packet before, just go get the saved timestamp
+ sec = packets[file_position][TTIME]
+ if not sec then
+ dprint("failed to get saved timestamp for packet at position:", file_position)
+ return
+ end
+ return sec, line_pos
+ end
+
+ -- find the month's number
+ for index, name in ipairs(monthlist) do
+ if month == name then
+ month = index
+ break
+ end
+ end
+ if type(month) ~= "number" then return end
+
+ day = tonumber(day)
+ hour = tonumber(hour)
+ min = tonumber(min)
+ sec = tonumber(sec)
+ milli = tonumber(milli)
+
+ if not day or not hour or not min or not sec or not milli then
+ dprint("timestamp could not be determined")
+ return nil
+ end
+
+ -- we don't know what year the log file was created, so we have to guess
+ -- if we guess the current system year, then a log of December loaded in January will appear wrong,
+ -- as will a log file which lasts over new year
+ -- so we're going to check the current system month, and if it's less than the log file's then we'll
+ -- assume the log file started last year; if the system month is larger or equal, then we'll assume the log
+ -- file is of this year. We only do this checking once per file.
+ if statics.tyear == 0 then
+ local curr_year, curr_month = tonumber(os.date("%Y")), tonumber(os.date("%m"))
+ if curr_month < month then
+ -- use last year
+ if curr_year > 0 then
+ curr_year = curr_year - 1
+ end
+ end
+ statics.tyear = curr_year
+ end
+
+ -- if this message's month is less than previous message's, then year wrapped
+ if month < statics.tmonth then
+ statics.tyear = statics.tyear + 1
+ end
+ statics.tmonth = month
+
+ local timet = os.time({ ["year"] = statics.tyear, ["month"] = month, ["day"] = day, ["hour"] = hour, ["min"] = min, ["sec"] = sec })
+ if not timet then
+ dprint("timestamp conversion failed")
+ end
+
+ -- make an NSTime
+ statics.nstime = NSTime(timet, milli * 1000000)
+ packets[file_position][TTIME] = statics.nstime
+
+ timet = timet + (milli/1000)
+ dprint2("found time of ", os.date("%c",timet), " with value=",timet)
+
+ return statics.nstime, line_pos
+end
+
+-- get_tail_time() gets a fictitous timestamp starting from 19:00:00 on Dec 31, 1969, and incrementing based
+-- on the minutes/secs/millisecs seen (i.e., if the minute wrapped then hour increases by 1, etc.).
+-- this is needed for tail'ed log files, since they don't show month/day/hour
+local function get_tail_time(line, file_position, seeking)
+ local i, line_pos, min, sec, milli = line:find(header_tail_time_pattern)
+ if not min then return end
+
+ if seeking then
+ -- we've seen this packet before, just go get the saved timestamp
+ sec = packets[file_position][TTIME]
+ if not sec then
+ dprint("failed to get saved timestamp for packet at position:", file_position)
+ return
+ end
+ return sec, line_pos
+ end
+
+ min = tonumber(min)
+ sec = tonumber(sec)
+ milli = tonumber(milli)
+
+ if not min or not sec or not milli then
+ dprint("timestamp could not be determined")
+ return nil
+ end
+
+ -- get difference in time
+ local tmin, tsec, tmilli, nstime = statics.tmin, statics.tsec, statics.tmilli, statics.nstime
+ local ttime = nstime.secs
+
+ -- min, sec, milli are what the log says this tail'ed packet is
+ -- tmin, tsec, tmilli are what we got from last packet
+ -- nstime is the unix time of that, and ttime is the seconds of that unix time
+
+ -- if minutes wrapped, or they're equal but seconds wrapped, then handle it as if in the next hour
+ if (min < tmin) or (min == tmin and sec < tsec) or (min == tmin and sec == tsec and milli < tmilli) then
+ -- something wrapped, calculate difference as if in next hour
+ ttime = ttime + (((min * 60) + sec + 3600) - ((tmin * 60) + tsec))
+ else
+ ttime = ttime + (((min * 60) + sec) - ((tmin * 60) + tsec))
+ end
+ statics.tmin, statics.tsec, statics.tmilli = min, sec, milli
+ statics.nstime = NSTime(ttime, milli * 1000000)
+ packets[file_position][TTIME] = statics.nstime
+
+ return statics.nstime, line_pos
+end
+
+local hexbin = {
+ ["0"]=0, ["1"]=1, ["2"]=2, ["3"]=3, ["4"]=4, ["5"]=5, ["6"]=6, ["7"]=7, ["8"]=8, ["9"]=9, ["a"]=10, ["b"]=11, ["c"]=12, ["d"]=13, ["e"]=14, ["f"]=15,
+ ["00"]=0, ["01"]=1, ["02"]=2, ["03"]=3, ["04"]=4, ["05"]=5, ["06"]=6, ["07"]=7, ["08"]=8, ["09"]=9, ["0a"]=10, ["0b"]=11, ["0c"]=12, ["0d"]=13, ["0e"]=14, ["0f"]=15,
+ ["10"]=16, ["11"]=17, ["12"]=18, ["13"]=19, ["14"]=20, ["15"]=21, ["16"]=22, ["17"]=23, ["18"]=24, ["19"]=25, ["1a"]=26, ["1b"]=27, ["1c"]=28, ["1d"]=29, ["1e"]=30, ["1f"]=31,
+ ["20"]=32, ["21"]=33, ["22"]=34, ["23"]=35, ["24"]=36, ["25"]=37, ["26"]=38, ["27"]=39, ["28"]=40, ["29"]=41, ["2a"]=42, ["2b"]=43, ["2c"]=44, ["2d"]=45, ["2e"]=46, ["2f"]=47,
+ ["30"]=48, ["31"]=49, ["32"]=50, ["33"]=51, ["34"]=52, ["35"]=53, ["36"]=54, ["37"]=55, ["38"]=56, ["39"]=57, ["3a"]=58, ["3b"]=59, ["3c"]=60, ["3d"]=61, ["3e"]=62, ["3f"]=63,
+ ["40"]=64, ["41"]=65, ["42"]=66, ["43"]=67, ["44"]=68, ["45"]=69, ["46"]=70, ["47"]=71, ["48"]=72, ["49"]=73, ["4a"]=74, ["4b"]=75, ["4c"]=76, ["4d"]=77, ["4e"]=78, ["4f"]=79,
+ ["50"]=80, ["51"]=81, ["52"]=82, ["53"]=83, ["54"]=84, ["55"]=85, ["56"]=86, ["57"]=87, ["58"]=88, ["59"]=89, ["5a"]=90, ["5b"]=91, ["5c"]=92, ["5d"]=93, ["5e"]=94, ["5f"]=95,
+ ["60"]=96, ["61"]=97, ["62"]=98, ["63"]=99, ["64"]=100, ["65"]=101, ["66"]=102, ["67"]=103, ["68"]=104, ["69"]=105, ["6a"]=106, ["6b"]=107, ["6c"]=108, ["6d"]=109, ["6e"]=110, ["6f"]=111,
+ ["70"]=112, ["71"]=113, ["72"]=114, ["73"]=115, ["74"]=116, ["75"]=117, ["76"]=118, ["77"]=119, ["78"]=120, ["79"]=121, ["7a"]=122, ["7b"]=123, ["7c"]=124, ["7d"]=125, ["7e"]=126, ["7f"]=127,
+ ["80"]=128, ["81"]=129, ["82"]=130, ["83"]=131, ["84"]=132, ["85"]=133, ["86"]=134, ["87"]=135, ["88"]=136, ["89"]=137, ["8a"]=138, ["8b"]=139, ["8c"]=140, ["8d"]=141, ["8e"]=142, ["8f"]=143,
+ ["90"]=144, ["91"]=145, ["92"]=146, ["93"]=147, ["94"]=148, ["95"]=149, ["96"]=150, ["97"]=151, ["98"]=152, ["99"]=153, ["9a"]=154, ["9b"]=155, ["9c"]=156, ["9d"]=157, ["9e"]=158, ["9f"]=159,
+ ["a0"]=160, ["a1"]=161, ["a2"]=162, ["a3"]=163, ["a4"]=164, ["a5"]=165, ["a6"]=166, ["a7"]=167, ["a8"]=168, ["a9"]=169, ["aa"]=170, ["ab"]=171, ["ac"]=172, ["ad"]=173, ["ae"]=174, ["af"]=175,
+ ["b0"]=176, ["b1"]=177, ["b2"]=178, ["b3"]=179, ["b4"]=180, ["b5"]=181, ["b6"]=182, ["b7"]=183, ["b8"]=184, ["b9"]=185, ["ba"]=186, ["bb"]=187, ["bc"]=188, ["bd"]=189, ["be"]=190, ["bf"]=191,
+ ["c0"]=192, ["c1"]=193, ["c2"]=194, ["c3"]=195, ["c4"]=196, ["c5"]=197, ["c6"]=198, ["c7"]=199, ["c8"]=200, ["c9"]=201, ["ca"]=202, ["cb"]=203, ["cc"]=204, ["cd"]=205, ["ce"]=206, ["cf"]=207,
+ ["d0"]=208, ["d1"]=209, ["d2"]=210, ["d3"]=211, ["d4"]=212, ["d5"]=213, ["d6"]=214, ["d7"]=215, ["d8"]=216, ["d9"]=217, ["da"]=218, ["db"]=219, ["dc"]=220, ["dd"]=221, ["de"]=222, ["df"]=223,
+ ["e0"]=224, ["e1"]=225, ["e2"]=226, ["e3"]=227, ["e4"]=228, ["e5"]=229, ["e6"]=230, ["e7"]=231, ["e8"]=232, ["e9"]=233, ["ea"]=234, ["eb"]=235, ["ec"]=236, ["ed"]=237, ["ee"]=238, ["ef"]=239,
+ ["f0"]=240, ["f1"]=241, ["f2"]=242, ["f3"]=243, ["f4"]=244, ["f5"]=245, ["f6"]=246, ["f7"]=247, ["f8"]=248, ["f9"]=249, ["fa"]=250, ["fb"]=251, ["fc"]=252, ["fd"]=253, ["fe"]=254, ["ff"]=255
+}
+
+local function iptobytes(ipaddr)
+ local bytes = { ipaddr:match("(%d+)%.(%d+)%.(%d+)%.(%d+)") }
+ if not #bytes == 4 then
+ dprint("failed to get ip address bytes for '", ipaddr, "'")
+ return
+ end
+ local ip = ""
+ for i, byte in ipairs(bytes) do
+ ip = ip .. char(tonumber(byte))
+ end
+ return ip
+end
+
+local function hexword2bin(word)
+ if #word == 4 then
+ return char(hexbin[word:sub(1,2)], hexbin[word:sub(3,4)])
+ elseif #word == 3 then
+ return char(hexbin[word:sub(1,1)], hexbin[word:sub(2,3)])
+ elseif #word < 3 then
+ return char(0, hexbin[word])
+ end
+ return nil -- error
+end
+
+-- convert this 2620:0:60:8ac::102 to its 16-byte binary (=8 of 2-byte words)
+local NUMWORDS = 8
+local function ipv6tobytes(ipaddr)
+ -- start with all 16 bytes being zeroes
+ local words = { "\00\00", "\00\00", "\00\00", "\00\00", "\00\00", "\00\00", "\00\00", "\00\00" }
+ -- now walk from front of ipv6 address string replacing byte numbers above;
+ -- if we hit a "::", then jump to end and do it in reverse
+ local colon_s, colon_e = ipaddr:find("::%x")
+ if colon_s then
+ -- there's a double-colon, so split the string and do the end first, backwards
+ -- get each chunk first
+ local t = {}
+ local index, wordix = 1, NUMWORDS
+ for w in string.gmatch(ipaddr:sub(colon_e - 1), ":(%x+)") do
+ t[index] = hexword2bin(w)
+ index = index + 1
+ end
+ for ix=index-1, 1, -1 do
+ words[wordix] = t[ix]
+ wordix = wordix - 1
+ end
+ ipaddr = ipaddr:sub(1, colon_s)
+ end
+
+ local i = 1
+ for w in string.gmatch(ipaddr, "(%x+):?") do
+ words[i] = hexword2bin(w)
+ i = i + 1
+ end
+
+ if not #words == NUMWORDS then
+ dprint("failed to get IPv6 address bytes for '", ipaddr, "'")
+ return
+ end
+
+ return table.concat(words)
+end
+
+-- calculates checksum as done for IP, TCP, UDP
+local function checksum(chunk)
+ local sum = 0
+ -- take every 2-byte value and add them up
+ for one, two in chunk:gmatch("(.)(.)") do
+ sum = sum + (string.byte(one) * 256) + (string.byte(two))
+ while floor(sum / 65536) > 0 do
+ -- add carry/overflow value
+ sum = (sum % 65536) + (floor(sum / 65536))
+ end
+ end
+
+ -- now get one's complement of that
+ sum = 65535 - sum
+
+ -- and return it as a 2-byte string
+ return dec2bin16(sum)
+end
+
+----------------------------------------
+-- protocol type number
+local PROTO_UDP = "\17"
+local PROTO_TCP = "\06"
+-- enum
+local IPv4 = 1
+local IPv6 = 2
+-- both type enums and header lengths
+local UDP = 8
+local TCP = 20
+
+----------------------------------------
+-- Packet creation/serialization occurs using a Lua class object model
+-- There's a single base class 'Packet' which has data/methods every packet type has
+-- 'RawPacket' and 'DataPacket' both derive from 'Packet'.
+-- 'RawPacket' is for packets which the log file has the raw IP/UDP headers for,
+-- such as ALG log messages (MGCP/NCS). Since the IP headers are in them, we use those.
+-- 'DataPacket' is for packets which the log file only has payload data for, and
+-- we need to create fake IP/UDP or IP/TCP headers for.
+-- 'BinPacket' and'AsciiPacket' both derive from 'DataPacket'.
+-- 'BinPacket' is for binary-style logged packets, such as MBCD or DNS, while
+-- 'AsciiPacket' is for ascii-style ones such as SIP.
+-- 'DnsPacket' derives from 'BinPacket', for DNS-style logs.
+
+-- Each class has a read_data() method, which reads in the packet data, builds the packet,
+-- and sets the Wireshark buffer. Some classes have a get_data() method which read_data()
+-- calls, to get the payload data before building a fake packet.
+
+-- The base Packet class has a get_hex_data() and get_ascii_data() methods, to get the payload
+-- in either form, and those base methods are called by get_data() or read_data() of derived
+-- classes.
+
+-- For performance reasons, packet data is read line-by-line into a table (called bufftbl),
+-- which is concatendated at the end. This avoids Lua building interim strings and garbage
+-- collecting them. But it makes the code uglier. The get_data()/get_hex_data()/get_ascii_data()
+-- methods read into this table they get passed, while the read_data() functions handle managing
+-- the table.
+
+----------------------------------------
+----------------------------------------
+-- The base Packet class, from which others derive
+-- all Packets have a ptype, timestamp, source and dest address:port, and data
+--
+local Packet = {}
+local Packet_mt = { __index = Packet }
+
+function Packet:new(timestamp, direction, source_ip, source_port, dest_ip, dest_port, ptype, ttype, file_position)
+ local new_class = { -- the new instance
+ ["timestamp"] = timestamp,
+ ["direction"] = direction,
+ ["source_ip"] = source_ip,
+ ["source_port"] = source_port,
+ ["dest_ip"] = dest_ip,
+ ["dest_port"] = dest_port,
+ ["ptype"] = ptype,
+ ["ttype"] = ttype,
+ ["file_position"] = file_position
+ }
+ setmetatable( new_class, Packet_mt ) -- all instances share the same metatable
+ return new_class
+end
+
+function Packet:set_comment(comment)
+ self["comment"] = comment
+end
+
+function Packet:set_wslua_fields(frame)
+ frame.time = self.timestamp
+ frame.flags = wtap_presence_flags.TS -- for timestamp
+ if self.comment then
+ frame.comment = self.comment
+ frame.flags = frame.flags + wtap_presence_flags.COMMENTS -- comment flag
+ end
+ return true
+end
+
+local packet_hexline_pattern = "^ %x%x%x0: %x%x"
+function Packet:get_hex_data(file, line, bufftbl, index)
+ local start = index
+
+ dprint2("Packet:get_hex_data() called")
+ repeat
+ for word in line:gmatch("(%x%x) ") do
+ bufftbl[index] = char(hexbin[word])
+ index = index + 1
+ if ((index - start) % 16) == 0 then break end
+ end
+ line = file:read()
+ until not line or not line:find(packet_hexline_pattern)
+
+ return index - start, line
+end
+
+function Packet:get_ascii_data(file, line, bufftbl, index, only_newline)
+ local bufflen = 0 -- keep tally of total length of payload
+ local found_delim = true
+
+ dprint2("Packet:get_ascii_data() called")
+ repeat
+ bufftbl[index] = line
+ bufflen = bufflen + #line
+
+ -- sanity check if line has "\r" at end, and if so only add \n
+ if line:find("\r",-1,true) then
+ bufftbl[index+1] = "\n"
+ bufflen = bufflen + 1
+ dprint2("Found carriage-return at end of line")
+ elseif only_newline then
+ -- only add a newline
+ bufftbl[index+1] = "\n"
+ bufflen = bufflen + 1
+ else
+ bufftbl[index+1] = "\r\n"
+ bufflen = bufflen + 2
+ end
+ index = index + 2
+
+ -- read next line now
+ line = file:read()
+ if not line then
+ -- hit eof?
+ found_delim = false
+ break
+ end
+
+ until line:find(delim)
+
+ -- get rid of last \r\n, if we found a dashed delimeter, as it's not part of packet
+ if found_delim then
+ bufflen = bufflen - bufftbl[index-1]:len()
+ bufftbl[index-1] = nil
+ end
+
+ return bufflen
+end
+
+----------------------------------------
+-- RawPacket class, for packets that the log file contains the whole IP header for, such as algd logs
+--
+local RawPacket = {}
+local RawPacket_mt = { __index = RawPacket }
+setmetatable( RawPacket, Packet_mt ) -- make RawPacket inherit from Packet
+
+function RawPacket:new(...)
+ local new_class = Packet:new(...) -- the new instance
+ setmetatable( new_class, RawPacket_mt ) -- all instances share the same metatable
+ return new_class
+end
+
+function RawPacket:read_data(file, frame, line, seeking)
+ local bufftbl = {} -- table to hold data bytes
+ local index = 1 -- start at first slot in array
+
+ -- need to skip "Packet:" line and first 0000: line, it's internal junk
+ line = file:read()
+ line = file:read()
+
+ dprint2("RawPacket:read_data() getting hex from line='", line, "'")
+ local bufflen, line = self:get_hex_data(file, line, bufftbl, index)
+ if not bufflen or bufflen < 21 then
+ dprint("error getting binary data")
+ return false
+ end
+
+ -- add remainder as more packet data, but first delete overlap
+ -- see if frag bits are set in IP header, to see if UDP/TCP header exists
+ if self.ptype == IPv4 then
+ -- grab byte with frag flags and first byte of offset
+ local flag = string.byte(bufftbl[7]) -- converts binary character to number
+ local frag_offset = flag % 32 -- masks off upper 3 bits
+ frag_offset = (frag_offset * 256) + string.byte(bufftbl[8])
+ flag = floor(flag / 224) -- shift right
+ flag = flag % 2 -- mask upper bits
+ if flag == 1 or frag_offset > 0 then
+ -- we have a fragmented IPv4 packet, so no proto header
+ -- only save first 20 bytes (the IP header)
+ for i=bufflen, 21, -1 do
+ bufftbl[i] = nil
+ end
+ bufflen = 20
+ else
+ -- only save first 20 + proto size bytes
+ local save
+ if bufftbl[10] == PROTO_UDP then
+ save = 28
+ elseif bufftbl[10] == PROTO_TCP then
+ save = 40
+ else
+ dprint("failed to fix raw packet overlap")
+ return
+ end
+ for i=bufflen, save+1, -1 do
+ bufftbl[i] = nil
+ end
+ bufflen = save
+ end
+ end
+ -- TODO: IPv6
+
+ -- now read in rest of message, if any
+ -- first skip extra empty newline
+ if #line == 0 then
+ line = file:read()
+ end
+
+ bufflen = bufflen + self:get_ascii_data(file, line, bufftbl, bufflen+1, true)
+
+ frame.data = table.concat(bufftbl)
+
+ return true
+end
+
+----------------------------------------
+-- DataPacket class, for packets that the log file contains just the payload data for
+--
+local DataPacket = {}
+local DataPacket_mt = { __index = DataPacket }
+setmetatable( DataPacket, Packet_mt ) -- make Dataacket inherit from Packet
+
+function DataPacket:new(...)
+ local new_class = Packet:new(...) -- the new instance
+ setmetatable( new_class, DataPacket_mt ) -- all instances share the same metatable
+ return new_class
+end
+
+function DataPacket:set_tcbkey(key)
+ self["tcbkey"] = key
+ return
+end
+
+function DataPacket:build_ipv4_hdr(bufflen, proto, seeking)
+ local len = bufflen + 20 -- 20 byte IPv4 header size
+
+ -- figure out the ip identification value
+ local ip_ident
+ if seeking then
+ ip_ident = packets[self.file_position][IP_IDENT]
+ else
+ -- increment ident value
+ statics.ip_ident = statics.ip_ident + 1
+ if statics.ip_ident == 65536 then
+ statics.ip_ident = 1
+ end
+ ip_ident = statics.ip_ident
+ -- save it for future seeking
+ packets[self.file_position][IP_IDENT] = ip_ident
+ end
+
+ -- use a table to concatenate as it's slightly faster that way
+ local hdrtbl = {
+ "\69\00", -- 1=ipv4 and 20 byte header length
+ dec2bin16(len), -- 2=packet length bytes
+ dec2bin16(ip_ident), -- 3=ident field bytes
+ "\00\00\64", -- 4=flags/fragment offset, ttl
+ proto, -- 5=proto
+ "\00\00", -- 6=checksum (using zero for now)
+ iptobytes(self.source_ip), -- 7=source ip
+ iptobytes(self.dest_ip) -- 8=dest ip
+ }
+
+ -- calc IPv4 header checksum, and set its value
+ hdrtbl[6] = checksum(table.concat(hdrtbl))
+
+ return table.concat(hdrtbl)
+end
+
+function DataPacket:build_ipv6_hdr(bufflen, proto)
+ -- use a table to concatenate as it's slightly faster that way
+ local hdrtbl = {
+ "\96\00\00\00", -- 1=ipv6 version, class, label
+ dec2bin16(bufflen), -- 2=packet length bytes
+ proto .. "\64", -- 4=proto, ttl
+ ipv6tobytes(self.source_ip), -- 5=source ip
+ ipv6tobytes(self.dest_ip) -- 6=dest ip
+ }
+ return table.concat(hdrtbl)
+end
+
+-- calculates TCP/UDP header checksums with pseudo-header info
+function DataPacket:calc_header_checksum(bufftbl, bufflen, hdrtbl, proto)
+ -- first create pseudo IP header
+ if self.ptype == IPv4 then
+ local iphdrtbl = {
+ iptobytes(self.source_ip), -- 1=source ip
+ iptobytes(self.dest_ip), -- 2=dest ip
+ "\00", -- zeros
+ proto, -- proto
+ dec2bin16(bufflen) -- payload length bytes
+ }
+ bufftbl[1] = table.concat(iphdrtbl)
+ elseif self.ptype == IPv6 then
+ local iphdrtbl = {
+ ipv6tobytes(self.source_ip), -- 1=source ip
+ ipv6tobytes(self.dest_ip), -- 2=dest ip
+ "\00\00", -- zeroes
+ dec2bin16(bufflen), -- payload length bytes
+ "\00\00\00", -- zeros
+ proto -- proto
+ }
+ bufftbl[1] = table.concat(iphdrtbl)
+ end
+
+ -- and pseudo TCP or UDP header
+ bufftbl[2] = table.concat(hdrtbl)
+
+ -- see if payload is odd length
+ local odd = false
+ if bufflen % 2 == 1 then
+ -- odd number of payload bytes, add zero byte at end
+ odd = true -- remember to undo this
+ bufftbl[#bufftbl+1] = "\00"
+ end
+
+ local result = checksum(table.concat(bufftbl))
+
+ -- remove pseudo-headers
+ bufftbl[1] = nil
+ bufftbl[2] = nil
+ if odd then
+ bufftbl[#bufftbl] = nil
+ end
+
+ return result
+end
+
+
+function DataPacket:build_udp_hdr(bufflen, bufftbl)
+ local len = bufflen + 8 -- 8 for size of UDP header
+ local hdrtbl = {
+ dec2bin16(self.source_port), -- 1=source port bytes
+ dec2bin16(self.dest_port), -- 2=dest port bytes
+ dec2bin16(len), -- 3=payload length bytes
+ "\00\00" -- 4=checksum
+ }
+ if bufftbl then
+ -- calc udp checksum (only done for IPv6)
+ hdrtbl[4] = self:calc_header_checksum(bufftbl, len, hdrtbl, PROTO_UDP)
+ end
+ return table.concat(hdrtbl)
+end
+
+
+function DataPacket:build_tcp_hdr(bufflen, bufftbl, seeking)
+ local len = bufflen + 20 -- 20 for size of TCP header
+
+ local local_seq, remote_seq
+ if seeking then
+ local_seq = packets[self.file_position][LOCAL_SEQ]
+ remote_seq = packets[self.file_position][REMOTE_SEQ]
+ else
+ -- find socket/tcb info for this "stream", create if not found
+ if not tcb[self.tcbkey] then
+ -- create them
+ tcb[self.tcbkey] = {}
+ local_seq = 1
+ remote_seq = 1
+ packets[self.file_position][LOCAL_SEQ] = 1
+ packets[self.file_position][REMOTE_SEQ] = 1
+ -- set tcb to next sequence numbers, so that the correct "side"
+ -- acknowledges receiving these bytes
+ if self.direction == SENT then
+ -- this packet is being sent, so local sequence increases next time
+ tcb[self.tcbkey][TLOCAL_SEQ] = bufflen+1
+ tcb[self.tcbkey][TREMOTE_SEQ] = 1
+ else
+ -- this packet is being received, so remote sequence increases next time
+ -- and local side will acknowldge it next time
+ tcb[self.tcbkey][TLOCAL_SEQ] = 1
+ tcb[self.tcbkey][TREMOTE_SEQ] = bufflen+1
+ end
+ else
+ -- stream already exists, so send the current tcb seqs and update for next time
+ if self.direction == SENT then
+ -- this packet is being sent, so local sequence increases next time
+ local_seq = tcb[self.tcbkey][TLOCAL_SEQ]
+ remote_seq = tcb[self.tcbkey][TREMOTE_SEQ]
+ tcb[self.tcbkey][TLOCAL_SEQ] = local_seq + bufflen
+ else
+ -- this packet is being received, so the "local" seq number of the packet is the remote's seq really
+ local_seq = tcb[self.tcbkey][TREMOTE_SEQ]
+ remote_seq = tcb[self.tcbkey][TLOCAL_SEQ]
+ -- and remote seq needs to increase next time (remember local_seq is TREMOTE_SEQ)
+ tcb[self.tcbkey][TREMOTE_SEQ] = local_seq + bufflen
+ end
+ packets[self.file_position][LOCAL_SEQ] = local_seq
+ packets[self.file_position][REMOTE_SEQ] = remote_seq
+ end
+ end
+
+ local hdrtbl = {
+ dec2bin16(self.source_port), -- 1=source port bytes
+ dec2bin16(self.dest_port), -- 2=dest port bytes
+ dec2bin32(local_seq), -- 3=sequence
+ dec2bin32(remote_seq), -- 4=ack number
+ "\80\16\255\255", -- 5=offset, flags, window size
+ "\00\00", -- 6=checksum
+ "\00\00" -- 7=urgent pointer
+ }
+
+ -- calc tcp checksum
+ hdrtbl[6] = self:calc_header_checksum(bufftbl, len, hdrtbl, PROTO_TCP)
+
+ return table.concat(hdrtbl)
+end
+
+function DataPacket:build_packet(bufftbl, bufflen, seeking)
+ dprint2("DataPacket:build_packet() called")
+ if self.ptype == IPv4 then
+ if self.ttype == UDP then
+ bufftbl[2] = self:build_udp_hdr(bufflen)
+ bufftbl[1] = self:build_ipv4_hdr(bufflen + 8, PROTO_UDP, seeking)
+ elseif self.ttype == TCP then
+ bufftbl[2] = self:build_tcp_hdr(bufflen, bufftbl, seeking)
+ bufftbl[1] = self:build_ipv4_hdr(bufflen + 20, PROTO_TCP, seeking)
+ end
+ elseif self.ptype == IPv6 then
+ -- UDP for IPv6 requires checksum calculation, so we can't avoid more work
+ if self.ttype == UDP then
+ bufftbl[2] = self:build_udp_hdr(bufflen, bufftbl)
+ bufftbl[1] = self:build_ipv6_hdr(bufflen + 8, PROTO_UDP)
+ elseif self.ttype == TCP then
+ bufftbl[2] = self:build_tcp_hdr(bufflen, bufftbl, seeking)
+ bufftbl[1] = self:build_ipv6_hdr(bufflen + 20, PROTO_TCP)
+ end
+ else
+ dprint("DataPacket:build_packet: invalid packet type (neither IPv4 nor IPv6)")
+ return nil
+ end
+
+ return table.concat(bufftbl)
+end
+
+-- for performance, we read each line into a table and concatenate it at end
+-- but it makes this code super ugly
+function DataPacket:read_data(file, frame, line, seeking)
+ local bufftbl = { "", "" } -- 2 slots for ip and udp/tcp headers
+ local index = 3 -- start at third slot in array
+ local comment -- for any packet comments
+
+ dprint2("DataPacket: read_data(): calling get_data")
+ local bufflen = self:get_data(file, line, bufftbl, index)
+ if not bufflen then
+ dprint("DataPacket: error getting ascii or binary data")
+ return false
+ end
+
+ local buff = self:build_packet(bufftbl, bufflen, seeking)
+
+ frame.data = buff
+
+ return true
+end
+
+
+----------------------------------------
+-- BinPacket class, for packets that the log file contains binary payload data for, such as MBCD
+--
+local BinPacket = {}
+local BinPacket_mt = { __index = BinPacket }
+setmetatable( BinPacket, DataPacket_mt ) -- make BinPacket inherit from DataPacket
+
+function BinPacket:new(...)
+ local new_class = DataPacket:new(...) -- the new instance
+ setmetatable( new_class, BinPacket_mt ) -- all instances share the same metatable
+ return new_class
+end
+
+function BinPacket:get_comment_data(file, line, stop_pattern)
+ local comments = {}
+
+ while line and not line:find(stop_pattern) do
+ if #line > 0 then
+ comments[#comments+1] = line
+ comments[#comments+1] = "\r\n"
+ end
+ line = file:read()
+ end
+
+ if #comments > 0 then
+ -- get rid of extra "\r\n"
+ comments[#comments] = nil
+ self:set_comment(table.concat(comments))
+ end
+
+ return line
+end
+
+function BinPacket:get_data(file, line, bufftbl, index)
+ local is_alg = false
+
+ local bufflen, line = self:get_hex_data(file, line, bufftbl, index)
+
+ -- now eat rest of message until delimeter or end of file
+ -- we'll put them in comments
+ line = self:get_comment_data(file, line, delim)
+
+ -- return the bufflen, which is the same as number of table entries we made
+ return bufflen
+end
+
+----------------------------------------
+-- DnsPacket class, for DNS packets (which are binary but with commments at top)
+--
+local DnsPacket = {}
+local DnsPacket_mt = { __index = DnsPacket }
+setmetatable( DnsPacket, BinPacket_mt ) -- make DnsPacket inherit from BinPacket
+
+function DnsPacket:new(...)
+ local new_class = BinPacket:new(...) -- the new instance
+ setmetatable( new_class, DnsPacket_mt ) -- all instances share the same metatable
+ return new_class
+end
+
+local binpacket_start_pattern = "^ 0000: %x%x %x%x %x%x %x%x %x%x %x%x %x%x %x%x "
+function DnsPacket:get_data(file, line, bufftbl, index)
+ -- it's UDP regardless of what parse_header() thinks
+ self.ttype = UDP
+
+ -- comments are at top instead of bottom of messsage
+ line = self:get_comment_data(file, line, binpacket_start_pattern)
+
+ local bufflen, line = self:get_hex_data(file, line, bufftbl, index)
+
+ -- now eat rest of message until delimeter or end of file
+ while line and not line:find(delim) do
+ line = file:read()
+ end
+
+ -- return the bufflen, which is the same as number of table entries we made
+ return bufflen
+end
+
+----------------------------------------
+-- AsciiPacket class, for packets that the log file contains ascii payload data for
+--
+local AsciiPacket = {}
+local AsciiPacket_mt = { __index = AsciiPacket }
+setmetatable( AsciiPacket, DataPacket_mt ) -- make AsciiPacket inherit from DataPacket
+
+function AsciiPacket:new(...)
+ local new_class = DataPacket:new(...) -- the new instance
+ setmetatable( new_class, AsciiPacket_mt ) -- all instances share the same metatable
+ return new_class
+end
+
+function AsciiPacket:get_data(file, line, bufftbl, index)
+ return self:get_ascii_data(file, line, bufftbl, index)
+end
+
+
+----------------------------------------
+-- To determine packet type, we peek at the first line of 'data' following the log
+-- message header. Its pattern determines the Packet object type.
+-- The following are the patterns we look for; if it doesn't match one of these,
+-- then it's an AsciiPacket:
+local packet_patterns = {
+ { "^ 0000: %x%x %x%x %x%x %x%x %x%x %x%x %x%x %x%x ", BinPacket },
+ { "^Packet:$", RawPacket },
+ { "^DNS Query %d+ flags=%d+ q=%d+ ans=%d+", DnsPacket },
+ { "^DNS Response %d+ flags=%d+ q=%d+ ans=%d+", DnsPacket }
+}
+-- indeces for above
+local PP_PATTERN = 1
+local PP_CLASS = 2
+
+local function get_packet_class(line)
+ for i, t in ipairs(packet_patterns) do
+ if line:find(t[PP_PATTERN]) then
+ dprint2("got class type=",i)
+ return t[PP_CLASS]
+ end
+ end
+ dprint2("got class type AsciiPacket")
+ return AsciiPacket
+end
+
+----------------------------------------
+-- parses header line
+-- returns nil on failure
+-- the header lines look like this:
+-- Aug 10 14:30:11.134 On [1:544]10.201.145.237:5060 received from 10.210.1.193:5060
+-- this one has no phy/vlan info in brackets:
+-- Mar 6 13:39:06.122 On 127.0.0.1:2945 sent to 127.0.0.1:2944
+-- this one is IPv6:
+-- Aug 10 14:30:11.140 On [3:0][2620:0:60:8ac::102]:5060 sent to [2620:0:60:8ab::12]:5060
+-- this is from a tail'ed log output:
+-- 52:22.434 On [0:0]205.152.56.211:5060 received from 205.152.56.75:5060
+local loopback_pattern = "^127%.0%.0%.%d+$"
+local function parse_header(file, line, file_position, seeking)
+
+ if seeking then
+ -- verify we've seen this packet before
+ if not packets[file_position] then
+ dprint("parse_header: packet at file position ", file_position, " not saved previously")
+ return
+ end
+ else
+ -- first time through, create sub-table for the packet
+ packets[file_position] = {}
+ end
+
+ -- get time info, and line match ending position
+ local timestamp, line_pos = get_timestamp(line, file_position, seeking)
+ if not timestamp then
+ -- see if it's a tail'ed log instead
+ timestamp, line_pos = get_tail_time(line, file_position, seeking)
+ end
+
+ if not timestamp then
+ dprint("parse_header: could not parse time portion")
+ return
+ end
+
+ local ptype, ttype = IPv4, UDP
+
+ -- get phy/vlan if present
+ -- first skip past time portion
+ local phy, vlan, i, j, k
+ line_pos = line_pos + 1
+ i, j, phy, vlan = line:find(header_phy_pattern, line_pos)
+ if i then
+ phy = tonumber(phy)
+ vlan = tonumber(vlan)
+ line_pos = j -- skip past this portion for next match
+ else
+ -- if there's no phy/vlan info, then assume it's TCP (unless it's loopback address we'll check later)
+ ttype = TCP
+ end
+
+ -- get addresses and direction
+ local local_ip, local_port, direction, remote_ip, remote_port = line:match(header_address_pattern, line_pos)
+ if not local_ip then
+ -- try IPv6
+ local_ip, local_port, direction, remote_ip, remote_port = line:match(header_v6address_pattern, line_pos)
+ if not local_ip then
+ dprint("parse_header: could not parse address portion")
+ return nil
+ end
+ ptype = IPv6
+ end
+
+ if local_ip:find(loopback_pattern) and remote_ip:find(loopback_pattern) then
+ -- internal loopback packets never have phy/vlan but are always UDP messages (for all intents)
+ ttype = UDP
+ end
+
+ direction = get_direction(direction)
+ if direction == nil then
+ dprint("parse_header: failed to convert direction")
+ return nil
+ end
+
+ local source_ip, source_port, dest_ip, dest_port = local_ip, local_port, remote_ip, remote_port
+ if direction == RECV then
+ -- swap them
+ source_ip, source_port, dest_ip, dest_port = remote_ip, remote_port, local_ip, local_port
+ end
+ -- convert
+ source_port = tonumber(source_port)
+ dest_port = tonumber(dest_port)
+
+ -- peek at next line to determine packet type
+ local position = file:seek()
+ line = file:read()
+ dprint2("parse_header: peeking at line='", line, "'")
+ packet_class = get_packet_class(line)
+ file:seek("set", position) -- go back
+
+ local packet = packet_class:new(timestamp, direction, source_ip, source_port, dest_ip, dest_port, ptype, ttype, file_position)
+ if not packet then
+ dprint("parse_header: parser failed to create Packet object")
+ end
+
+ if ttype == TCP then
+ -- if the packet is tcp type, then set the key for TCB table lookup
+ packet:set_tcbkey(table.concat({ "[", local_ip, "]:", local_port, "->[", remote_ip, "]:", remote_port }))
+ end
+
+ return packet
+end
+
+
+----------------------------------------
+-- file handling functions for Wireshark to use
+
+-- The read_open is called by Wireshark once per file, to see if the file is this reader's type.
+-- Since there is no exact magic sequence to search for, we have to use heuristics to guess if the file
+-- is our type or not, which we do by parsing a message header.
+-- Since Wireshark uses the file cursor position for future reading of this file, we also have to seek back to the beginning
+-- so that our normal read() function works correctly.
+local function read_open(file, capture)
+ -- save current position to return later
+ local position = file:seek()
+ local line = file:read()
+ if not line then return false end
+ dprint2("read_open: got this line begin:\n'", line, "'")
+
+ line, position = skip_ahead(file, line, position)
+ if not line then return false end
+ dprint2("read_open: got this line after skip:\n'", line, "', with position=", position)
+
+ if parse_header(file, line, position) then
+ file:seek("set",position)
+ capture.time_precision = wtap_filetypes.TSPREC_MSEC -- for millisecond precision
+ capture.encap = wtap.RAW_IP -- whole file is raw IP format
+ capture.snapshot_length = 0 -- unknown snaplen
+ capture.comment = "Oracle Acme Packet SBC message log"
+ capture.os = "VxWorks or Linux"
+ capture.hardware = "Oracle Acme Packet SBC"
+ -- reset static variables
+ reset_state()
+ return true
+ end
+
+ return false
+end
+
+----------------------------------------
+-- this is used by both read() and seek_read()
+local function read_common(funcname, file, frame, position, seeking)
+
+ local line = file:read()
+ if not line then
+ dprint(funcname, "hit end of file")
+ return false
+ end
+ line, position = skip_ahead(file, line, position)
+ if not line then
+ if file:read(0) ~= nil then
+ dprint(funcname, "did not hit end of file after skipping but ending anyway")
+ else
+ dprint2(funcname, "hit end of file after skipping")
+ end
+ return false
+ end
+
+ dprint2(funcname, ": parsing line='", line, "'")
+ local phdr = parse_header(file, line, position, seeking)
+ if not phdr then
+ dprint(funcname, "failed to parse header")
+ return false
+ end
+
+ line = file:read()
+
+ dprint2(funcname,": calling class object's read_data()")
+ phdr:read_data(file, frame, line, seeking)
+
+ if not phdr:set_wslua_fields(frame) then
+ dprint(funcname, "failed to set Wireshark packet header info")
+ return
+ end
+ return position
+end
+
+----------------------------------------
+-- Wireshark/tshark calls read() for each frame/record in the file
+-- It passes in a File object and FrameInfo object to this function
+-- It expects in return the file offset position the record starts at,
+-- or nil/false if there's an error or end-of-file is reached.
+-- The offset position is used later: wireshark remembers it and gives
+-- it to seek_read() at various random times
+local function read(file, frame)
+ local position = file:seek()
+ position = read_common("read", file, frame, position)
+ if not position then
+ if file:read(0) ~= nil then
+ dprint("read failed to call read_common")
+ else
+ dprint2("read: reached end of file")
+ end
+ return false
+ end
+ return position
+end
+
+----------------------------------------
+-- Wireshark/tshark calls seek_read() for each frame/record in the file, at random times
+-- It passes in to this function a File object, FrameInfo object, and the offset position number
+-- It expects in return true for successful parsing, or nil/false if there's an error.
+local function seek_read(file, frame, offset)
+ file:seek("set",offset)
+ if not read_common("seek_read", file, frame, offset, true) then
+ dprint("seek_read failed to call read_common")
+ return false
+ end
+ return true
+end
+
+----------------------------------------
+-- Wireshark/tshark calls read_close() when it's closing the file completely
+-- this is a good opportunity to clean up any state you may have created during
+-- file reading. (in our case there *is* state to reset)
+local function read_close(file)
+ reset_state()
+ return true
+end
+
+----------------------------------------
+-- An often unused function, Wireshark calls this when the sequential walk-through is over
+-- (i.e., no more calls to read(), only to seek_read()). So we'll clear the TCB table
+-- here to free up memory; this is undoubtedly unecessary, but good practice.
+local function seq_read_close(file)
+ tcb = {}
+ return true
+end
+
+-- set above functions to the FileHandler
+fh.read_open = read_open
+fh.read = read
+fh.seek_read = seek_read
+fh.read_close = read_close
+fh.seq_read_close = seq_read_close
+fh.extensions = "log" -- this is just a hint
+
+-- and finally, register the FileHandler!
+register_filehandler(fh)
diff --git a/test/lua/pcap_file.lua b/test/lua/pcap_file.lua
new file mode 100644
index 0000000000..3f3367880d
--- /dev/null
+++ b/test/lua/pcap_file.lua
@@ -0,0 +1,548 @@
+-- pcap_file_reader.lua
+--------------------------------------------------------------------------------
+--[[
+ This is a Wireshark Lua-based pcap capture file reader.
+ Author: Hadriel Kaplan
+
+ This "capture file" reader reads pcap files - the old style ones. Don't expect this to
+ be as good as the real thing; this is a simplistic implementation to show how to
+ create such file readers, and for testing purposes.
+
+ This script requires Wireshark v1.11.3 or newer.
+--]]
+--------------------------------------------------------------------------------
+
+local wireshark_name = "Wireshark"
+if not GUI_ENABLED then
+ wireshark_name = "Tshark"
+end
+
+-- verify Wireshark is new enough
+local major, minor, micro = get_version():match("(%d+)%.(%d+)%.(%d+)")
+if major and tonumber(major) <= 1 and ((tonumber(minor) <= 10) or (tonumber(minor) == 11 and tonumber(micro) < 3)) then
+ error( "Sorry, but your " .. wireshark_name .. " version (" .. get_version() .. ") is too old for this script!\n" ..
+ "This script needs " .. wireshark_name .. "version 1.11.3 or higher.\n" )
+end
+
+-- verify we have the Struct library in wireshark
+-- technically we should be able to do this with 'require', but Struct is a built-in
+assert(Struct.unpack, wireshark_name .. " does not have the Struct library!")
+
+-- debug printer, set DEBUG to true to enable printing debug info
+-- set DEBUG2 to true to enable really verbose printing
+local DEBUG, DEBUG2 = false, false
+
+local dprint = function() end
+local dprint2 = function() end
+if DEBUG or DEBUG2 then
+ dprint = function(...)
+ print(table.concat({"Lua:", ...}," "))
+ end
+
+ if DEBUG2 then
+ dprint2 = dprint
+ end
+end
+
+----------------------------------------
+-- to make it easier to read this file, we'll define some of the functions
+-- later on, but we need them earlier, so we "declare" them here
+local parse_file_header, parse_rec_header, read_common
+
+
+-- these will be set inside of parse_file_header(), but we're declaring them up here
+local VERSION_MAJOR = 2
+local VERSION_MINOR = 4
+local TIMEZONE = 0
+local SIGFIGS = 0
+local SNAPLEN = 0
+local ENCAP_TYPE = wtap.UNKNOWN
+
+--------------------------------------------------------------------------------
+-- file reader handling functions for Wireshark to use
+--------------------------------------------------------------------------------
+
+----------------------------------------
+-- The read_open() is called by Wireshark once per file, to see if the file is this reader's type.
+-- Wireshark passes in a File object to this function
+-- It expects in return either nil or false to mean it's not our file type, or true if it is
+-- In our case what this means is we figure out if the file has the magic header, and get the
+-- endianess of the file, and the encapsulation type of its frames/records
+-- Since Wireshark uses the file cursor position for future reading of this file, we also have to seek back to the beginning
+-- so that our normal read() function works correctly.
+local function read_open(file, capture)
+ dprint2("read_open() called")
+
+ -- save current position to return later
+ local position = file:seek()
+
+ if parse_file_header(file) then
+
+ dprint2("read_open: success, file is for us")
+
+ -- if the file is for us, we MUST set the file position cursor to
+ -- where we want the first call to read() function to get it the next time
+ -- for example if we checked a few records to be sure it's or type
+ -- but in this simple example we only verify the file header (24 bytes)
+ -- and we want the file position to remain after that header for our read()
+ -- call, so we don't change it back
+ --file:seek("set",position)
+
+ -- these we can also set per record later during read operations
+ capture.time_precision = wtap_filetypes.TSPREC_USEC -- for microsecond precision
+ capture.encap = ENCAP_TYPE -- this was updated by parse_file_header()
+ capture.snapshot_length = SNAPLEN -- also updated by parse_file_header()
+
+ return true
+ end
+
+ dprint2("read_open: file not for us")
+
+ -- if it's not for us, wireshark will reset the file position itself
+ -- but we might as well do it too, in case that behavior ever changes
+ file:seek("set",position)
+
+ return false
+end
+
+----------------------------------------
+-- Wireshark/tshark calls read() for each frame/record in the file
+-- It passes in a File object and FrameInfo object to this function
+-- It expects in return the file offset position the record starts at,
+-- or nil/false if there's an error or end-of-file is reached.
+-- The offset position is used later: wireshark remembers it and gives
+-- it to seek_read() at various random times
+local function read(file, frame)
+ dprint2("read() called")
+
+ -- call our common reader function
+ local position = file:seek()
+
+ if not read_common("read", file, frame) then
+ -- this isnt' actually an error, because it might just mean we reached end-of-file
+ -- so let's test for that (read(0) is a special case in Lua, see Lua docs)
+ if file:read(0) ~= nil then
+ dprint("read: failed to call read_common")
+ else
+ dprint2("read: reached end of file")
+ end
+ return false
+ end
+
+ dprint2("read: succeess")
+
+ -- return the position we got to (or nil if we hit EOF/error)
+ return position
+end
+
+----------------------------------------
+-- Wireshark/tshark calls seek_read() for each frame/record in the file, at random times
+-- It passes in to this function a File object, FrameInfo object, and the offset position number
+-- It expects in return true for successful parsing, or nil/false if there's an error.
+local function seek_read(file, frame, offset)
+ dprint2("seek_read() called")
+
+ -- first move to the right position in the file
+ file:seek("set",offset)
+
+ if not read_common("seek_read", file, frame) then
+ dprint("seek_read: failed to call read_common")
+ return false
+ end
+
+ return true
+end
+
+----------------------------------------
+-- Wireshark/tshark calls read_close() when it's closing the file completely
+-- this is a good opportunity to clean up any state you may have created during
+-- file reading. (in our case there's no real state)
+local function read_close(file)
+ dprint2("read_close() called")
+ -- we don't really have to reset these, but just to show what you might do in this function...
+ VERSION_MAJOR = 2
+ VERSION_MINOR = 4
+ TIMEZONE = 0
+ SIGFIGS = 0
+ SNAPLEN = 0
+ ENCAP_TYPE = wtap.UNKNOWN
+ return true
+end
+
+----------------------------------------
+-- An often unused function, Wireshark calls this when the sequential walk-through is over
+-- (i.e., no more calls to read(), only to seek_read()).
+-- This gives you a chance to clean up any state you used during read() calls, but remember
+-- that there will be calls to seek_read() after this (in Wireshark, though not Tshark)
+local function seq_read_close(file)
+ dprint2("First pass of read() calls are over, but there may be seek_read() calls after this")
+ return true
+end
+
+----------------------------------------
+-- ok, so let's create a FileHandler object
+local fh = FileHandler.new("Lua-based PCAP reader", "lua_pcap", "A Lua-based file reader for PCAP-type files","rs")
+
+-- set above functions to the FileHandler
+fh.read_open = read_open
+fh.read = read
+fh.seek_read = seek_read
+fh.read_close = read_close
+fh.seq_read_close = seq_read_close
+fh.extensions = "pcap;cap" -- this is just a hint
+
+-- and finally, register the FileHandler!
+register_filehandler(fh)
+
+dprint2("FileHandler registered")
+
+--------------------------------------------------------------------------------
+-- ok now for the boring stuff that actually does the work
+--------------------------------------------------------------------------------
+
+----------------------------------------
+-- in Lua, we have access to encapsulation types in the 'wtap_encaps' table, but
+-- those numbers don't actually necessarily match the numbers in pcap files
+-- for the encapsulation type, because the namespace got screwed up at some
+-- point in the past (blame LBL NRG, not wireshark for that)
+-- but I'm not going to create the full mapping of these two namespaces
+-- instead we'll just use this smaller table to map them
+-- these are taken from wiretap/pcap-common.c
+local pcap2wtap = {
+ [0] = wtap_encaps.NULL,
+ [1] = wtap_encaps.ETHERNET,
+ [6] = wtap_encaps.TOKEN_RING,
+ [8] = wtap_encaps.SLIP,
+ [9] = wtap_encaps.PPP,
+ [101] = wtap_encaps.RAW_IP,
+ [105] = wtap_encaps.IEEE_802_11,
+ [140] = wtap_encaps.MTP2,
+ [141] = wtap_encaps.MTP3,
+ [143] = wtap_encaps.DOCSIS,
+ [147] = wtap_encaps.USER0,
+ [148] = wtap_encaps.USER1,
+ [149] = wtap_encaps.USER2,
+ [150] = wtap_encaps.USER3,
+ [151] = wtap_encaps.USER4,
+ [152] = wtap_encaps.USER5,
+ [153] = wtap_encaps.USER6,
+ [154] = wtap_encaps.USER7,
+ [155] = wtap_encaps.USER8,
+ [156] = wtap_encaps.USER9,
+ [157] = wtap_encaps.USER10,
+ [158] = wtap_encaps.USER11,
+ [159] = wtap_encaps.USER12,
+ [160] = wtap_encaps.USER13,
+ [161] = wtap_encaps.USER14,
+ [162] = wtap_encaps.USER15,
+ [186] = wtap_encaps.USB,
+ [187] = wtap_encaps.BLUETOOTH_H4,
+ [189] = wtap_encaps.USB_LINUX,
+ [195] = wtap_encaps.IEEE802_15_4,
+}
+
+-- we can use the above to directly map very quickly
+-- but to map it backwards we'll use this, because I'm lazy:
+local function wtap2pcap(encap)
+ for k,v in pairs(pcap2wtap) do
+ if v == encap then
+ return k
+ end
+ end
+ return 0
+end
+
+----------------------------------------
+-- the pcap magic field: 0xA1B2C3D4, of both endianess
+local MAGIC = 0xa1b2c3d4
+local SWAPPED_MAGIC = 0xd4c3b2a1
+
+-- here are the "structs" we're going to parse, of the various records in a pcap file
+-- these pattern string gets used in calls to Struct.unpack()
+--
+-- we will prepend a '<' or '>' later, once we figure out what endian-ess the files are in
+--
+-- a pcap file header struct
+-- this is: magic, version_major, version_minor, timezone, sigfigs, snaplen, encap type
+local FILE_HEADER = "I4 I2 I2 i4 I4 I4 I4"
+local FILE_HDR_LEN = Struct.size(FILE_HEADER)
+
+-- a pcap record header struct
+-- this is: time_sec, time_usec, capture_len, original_len
+local REC_HEADER = "I4 I4 I4 I4"
+local REC_HDR_LEN = Struct.size(REC_HEADER)
+local NUM_REC_FIELDS = 4
+
+-- these will hold the '<'/'>' prepended version of above
+local file_header, rec_header
+
+-- snaplen/caplen can't be bigger than this
+local WTAP_MAX_PACKET_SIZE = 65535
+
+----------------------------------------
+-- internal functions declared previously
+----------------------------------------
+
+----------------------------------------
+-- used by read_open(), this parses the file header
+parse_file_header = function(file)
+ dprint2("parse_file_header() called")
+
+ -- by default, file:read() gets the next "string", meaning ending with a newline \n
+ -- but we want raw byte reads, so tell it how many bytes to read
+ local line = file:read(FILE_HDR_LEN)
+
+ -- it's ok for us to not be able to read it, but we need to tell wireshark the
+ -- file's not for us, so return false
+ if not line then return false end
+
+ dprint2("parse_file_header: got this line:\n'", Struct.tohex(line,false,":"), "'")
+
+ -- let's peek at the magic int32, assuming it's little-endian
+ local magic = Struct.unpack("<I4", line)
+
+ if magic == MAGIC then
+ dprint2("file is little-endian")
+ file_header = "<" .. FILE_HEADER
+ rec_header = "<" .. REC_HEADER
+ elseif magic == SWAPPED_MAGIC then
+ dprint2("file is big-endian")
+ file_header = ">" .. FILE_HEADER
+ rec_header = ">" .. REC_HEADER
+ else
+ dprint("magic was:",magic," so not a pcap file")
+ return false
+ end
+
+ local nettype
+
+ magic, VERSION_MAJOR, VERSION_MINOR, TIMEZONE, SIGFIGS, SNAPLEN, nettype = Struct.unpack(file_header, line)
+
+ if not magic then
+ dprint("parse_file_header: failed to unpack header struct")
+ return false
+ end
+
+ dprint("parse_file_header: got magic=",magic, ", major version=",VERSION_MAJOR, ", minor=",VERSION_MINOR,
+ ", timezone=",TIMEZONE, ", sigfigs=",SIGFIGS, "snaplen=",SNAPLEN, ", nettype =",nettype)
+
+ -- wireshark only supports version 2.0 and later
+ if VERSION_MAJOR < 2 then
+ dprint("got version =",VERSION_MAJOR,"but only version 2 or greater supported")
+ return false
+ end
+
+ -- convert pcap file interface type to wtap number type
+ ENCAP_TYPE = pcap2wtap[nettype]
+ if not ENCAP_TYPE then
+ dprint("file nettype",nettype,"couldn't be mapped to wireshark wtap type")
+ return false
+ end
+
+
+ if SNAPLEN > WTAP_MAX_PACKET_SIZE then
+ SNAPLEN = WTAP_MAX_PACKET_SIZE
+ end
+
+ --ok, it's a pcap file
+ dprint2("parse_file_header: success")
+ return true
+end
+
+----------------------------------------
+-- this is used by both read() and seek_read()
+-- the calling function to this should have already set the file position correctly
+read_common = function(funcname, file, frame)
+ dprint2(funcname,": read_common() called")
+
+ -- first parse the record header, which will set the FrameInfo fields
+ if not parse_rec_header(funcname, file, frame) then
+ dprint2(funcname, ": read_common: hit end of file or error")
+ return false
+ end
+
+ frame.encap = ENCAP_TYPE
+
+ -- now we need to get the packet bytes from the file record into the frame...
+ -- we *could* read them into a string using file:read(numbytes), and then
+ -- set them to frame.data so that wireshark gets it...
+ -- but that would mean the packet's string would be copied into Lua
+ -- and then sent right back into wireshark, which is gonna slow things
+ -- down; instead FrameInfo has a read_data() method, which makes
+ -- wireshark read directly from the file into the frame buffer, so we use that
+ if not frame:read_data(file, frame.captured_length) then
+ dprint(funcname, ": read_common: failed to read data from file into buffer")
+ return false
+ end
+
+ return true
+end
+
+----------------------------------------
+-- the function to parse individual records
+parse_rec_header = function(funcname, file, frame)
+ dprint2(funcname,": parse_rec_header() called")
+
+ local line = file:read(REC_HDR_LEN)
+
+ -- it's ok for us to not be able to read it, if it's end of file
+ if not line then return false end
+
+ -- this is: time_sec, time_usec, capture_len, original_len
+ local fields = { Struct.unpack(rec_header, line) }
+
+ -- sanity check; also note that Struct.unpack() returns the fields plus
+ -- a number of where in the line it stopped reading (ie, the end in this case)
+ -- so we got back number of fields + 1
+ if #fields ~= NUM_REC_FIELDS + 1 then
+ dprint(funcname, ": parse_rec_header: failed to read the record header")
+ return nil
+ end
+
+ -- we could just do this:
+ --frame.time = fields[1] + (fields[2] / 1000000)
+ -- but Lua numbers are doubles, which lose precision in the fractional part
+ -- so we use a NSTime() object instead; remember though that an NSTime takes
+ -- nanoseconds for its second arg, and pcap's are only microseconds, so *1000
+ frame.time = NSTime(fields[1], fields[2]*1000)
+
+ -- sanity check, verify captured length isn't more than original length
+ if fields[3] > fields[4] then
+ dprint("captured length of",fields[3],"is bigger than original length of",fields[4])
+ -- swap them
+ local caplen = fields[3]
+ fields[3] = fields[4]
+ fields[4] = caplen
+ end
+
+ if fields[3] > WTAP_MAX_PACKET_SIZE then
+ dprint("Got a captured_length of",fields[3],"which is too big")
+ return nil
+ end
+
+ frame.captured_length = fields[3]
+ frame.original_length = fields[4]
+
+ frame.flags = wtap_presence_flags.TS + wtap_presence_flags.CAP_LEN -- for timestamp|cap_len
+
+ return true
+end
+
+
+
+--------------------------------------------------------------------------------
+-- file writer handling functions for Wireshark to use
+--------------------------------------------------------------------------------
+
+-- file encaps we can handle writing
+local canwrite = {
+ [ wtap_encaps.NULL ] = true,
+ [ wtap_encaps.ETHERNET ] = true,
+ [ wtap_encaps.PPP ] = true,
+ [ wtap_encaps.RAW_IP ] = true,
+ [ wtap_encaps.IEEE_802_11 ] = true,
+ [ wtap_encaps.MTP2 ] = true,
+ [ wtap_encaps.MTP3 ] = true,
+ -- etc., etc.
+}
+
+-- we can't reuse the variables we used in the reader, because this script might be sued to both
+-- open a file for reading and write it out, at the same time, so we prepend 'W_' for the writer's
+-- versions. Normally I'd put this type of stuff in a class table and just create a new instance,
+-- but I didn't want to confuse people with Lua class models in this script
+local W_VERSION_MAJOR = 2
+local W_VERSION_MINOR = 4
+local W_TIMEZONE = 0
+local W_SIGFIGS = 0
+local W_SNAPLEN = 0
+local W_ENCAP_TYPE = wtap.UNKNOWN
+-- write out things in little-endian order
+local w_file_header = "<" .. FILE_HEADER
+local w_rec_header = "<" .. REC_HEADER
+local TSPRECISION = wtap_filetypes.TSPREC_USEC
+
+----------------------------------------
+-- The can_write_encap() function is called by Wireshark when it wants to write out a file,
+-- and needs to see if this file writer can handle the packet types in the window.
+-- We need to return true if we can handle it, else false
+local function can_write_encap(encap)
+ dprint2("can_write_encap() called with encap=",encap)
+ return canwrite[encap] or false
+end
+
+local function write_open(file, capture)
+ dprint2("write_open() called")
+
+ -- write out file header
+ local hdr = Struct.pack(w_file_header,
+ MAGIC, W_VERSION_MAJOR, W_VERSION_MINOR,
+ W_TIMEZONE, W_SIGFIGS, capture.snapshot_length, wtap2pcap(capture.encap))
+
+ if not hdr then
+ dprint("write_open: error generating file header")
+ return false
+ end
+
+ dprint2("write_open generating:",Struct.tohex(hdr))
+
+ if not file:write(hdr) then
+ dprint("write_open: error writing file header to file")
+ return false
+ end
+
+ return true
+end
+
+local function write(file, frame)
+ dprint2("write() called")
+
+ -- write out record header: time_sec, time_usec, capture_len, original_len
+
+ -- first get times
+ local nstime = frame.time
+
+ -- pcap format is in usecs
+ local nsecs = nstime.nsecs / 1000
+
+ local hdr = Struct.pack(w_rec_header, nstime.secs, nsecs, frame.captured_length, frame.original_length)
+
+ if not hdr then
+ dprint("write_open: error generating record header")
+ return false
+ end
+
+ if not file:write(hdr) then
+ dprint("write_open: error writing record header to file")
+ return false
+ end
+
+ -- we could write the packet data the same way, by getting frame.data and writing it out
+ -- but we can avoid copying those bytes into Lua by using the write_data() function
+ if not frame:write_data(file) then
+ dprint("write_open: error writing record data to file")
+ return false
+ end
+
+ return true
+end
+
+local function write_close(file)
+ dprint2("write_close() called")
+ dprint2("Good night, and good luck")
+ return true
+end
+
+-- ok, so let's create another FileHandler object
+local fh2 = FileHandler.new("Lua-based PCAP writer", "lua_pcap2", "A Lua-based file writer for PCAP-type files","wms")
+
+-- set above functions to the FileHandler
+fh2.can_write_encap = can_write_encap
+fh2.write_open = write_open
+fh2.write = write
+fh2.write_close = write_close
+fh2.extensions = "pcap;cap" -- this is just a hint
+
+-- and finally, register the FileHandler!
+register_filehandler(fh2)
+
+dprint2("Second FileHandler registered")
diff --git a/test/suite-wslua.sh b/test/suite-wslua.sh
index fa682b08d3..0b8fc4e339 100755
--- a/test/suite-wslua.sh
+++ b/test/suite-wslua.sh
@@ -66,6 +66,103 @@ wslua_step_field_test() {
fi
}
+wslua_step_file_test() {
+ if [ $HAVE_LUA -ne 0 ]; then
+ test_step_skipped
+ return
+ fi
+
+ # First run tshark with the pcap_file_reader script.
+ $TSHARK -r $CAPTURE_DIR/dhcp.pcap -X lua_script:$TESTS_DIR/lua/pcap_file.lua > testin.txt 2>&1
+ $TSHARK -r $CAPTURE_DIR/wpa-Induction.pcap.gz -X lua_script:$TESTS_DIR/lua/pcap_file.lua >> testin.txt 2>&1
+ RETURNVALUE=$?
+ if [ ! $RETURNVALUE -eq $EXIT_OK ]; then
+ echo
+ cat ./testin.txt
+ test_step_failed "exit status of $DUT: $RETURNVALUE"
+ return
+ fi
+
+ # then run tshark again without the script
+ $TSHARK -r $CAPTURE_DIR/dhcp.pcap > testout.txt 2>&1
+ $TSHARK -r $CAPTURE_DIR/wpa-Induction.pcap.gz >> testout.txt 2>&1
+ RETURNVALUE=$?
+ if [ ! $RETURNVALUE -eq $EXIT_OK ]; then
+ echo
+ cat ./testout.txt
+ test_step_failed "exit status of $DUT: $RETURNVALUE"
+ return
+ fi
+
+ # now compare the two files - they should be identical
+ if diff -q ./testin.txt ./testout.txt; then
+ rm ./testin.txt
+ else
+ echo
+ cat ./testin.txt
+ cat ./testout.txt
+ test_step_failed "reading the pcap file with Lua did not match internal"
+ fi
+
+ # Now generate a new capture file using the Lua writer.
+ $TSHARK -r $CAPTURE_DIR/dhcp.pcap -X lua_script:$TESTS_DIR/lua/pcap_file.lua -w testin.txt -F lua_pcap2 > testout.txt 2>&1
+ RETURNVALUE=$?
+ if [ ! $RETURNVALUE -eq $EXIT_OK ]; then
+ echo
+ cat ./testout.txt
+ test_step_failed "exit status of $DUT: $RETURNVALUE"
+ return
+ fi
+
+ # now compare the two files - they should be identical
+ if diff -q $CAPTURE_DIR/dhcp.pcap ./testin.txt; then
+ rm ./testin.txt
+ else
+ echo
+ cat ./testout.txt
+ test_step_failed "writing the pcap out as Lua did not match dhcp.cap"
+ fi
+
+ # Now read an acme sipmsg.log using the acme Lua reader, writing it out as pcapng.
+ $TSHARK -r $CAPTURE_DIR/sipmsg.log -X lua_script:$TESTS_DIR/lua/acme_file.lua -w testin.txt -F pcapng > testout.txt 2>&1
+ RETURNVALUE=$?
+ if [ ! $RETURNVALUE -eq $EXIT_OK ]; then
+ echo
+ cat ./testout.txt
+ test_step_failed "exit status of $DUT: $RETURNVALUE"
+ return
+ fi
+
+ # testin.txt is now a pcapng, read it out using -V verbose into testout.txt
+ $TSHARK -r ./testin.txt -V > testout.txt 2>&1
+ RETURNVALUE=$?
+ if [ ! $RETURNVALUE -eq $EXIT_OK ]; then
+ echo
+ cat ./testout.txt
+ test_step_failed "exit status of $DUT: $RETURNVALUE"
+ return
+ fi
+
+ # now readout sip.pcapng into testin.txt using -V verbose
+ $TSHARK -r $CAPTURE_DIR/sip.pcapng -V > testin.txt 2>&1
+ RETURNVALUE=$?
+ if [ ! $RETURNVALUE -eq $EXIT_OK ]; then
+ echo
+ cat ./testin.txt
+ test_step_failed "exit status of $DUT: $RETURNVALUE"
+ return
+ fi
+
+ # now compare testin and testout - they should be identical
+ if diff -q ./testout.txt ./testin.txt; then
+ test_step_ok
+ else
+ echo
+ cat ./testout.txt
+ test_step_failed "writing the acme sipmsg.log out as pcapng did not match sip.pcapng"
+ fi
+}
+
wslua_step_listener_test() {
if [ $HAVE_LUA -ne 0 ]; then
test_step_skipped
@@ -260,6 +357,7 @@ wslua_suite() {
test_step_set_post wslua_cleanup_step
test_step_add "wslua dissector" wslua_step_dissector_test
test_step_add "wslua field/fieldinfo" wslua_step_field_test
+ test_step_add "wslua file" wslua_step_file_test
test_step_add "wslua globals" wslua_step_globals_test
test_step_add "wslua gregex" wslua_step_gregex_test
test_step_add "wslua int64" wslua_step_int64_test
diff --git a/wiretap/file_access.c b/wiretap/file_access.c
index 1be8b35a72..d641aaaea1 100644
--- a/wiretap/file_access.c
+++ b/wiretap/file_access.c
@@ -300,39 +300,39 @@ GSList *wtap_get_all_file_extensions_list(void)
*/
static struct open_info open_info_base[] = {
- { "Pcap", OPEN_INFO_MAGIC, libpcap_open, "pcap" },
- { "PcapNG", OPEN_INFO_MAGIC, pcapng_open, "pcapng"},
- { "NgSniffer", OPEN_INFO_MAGIC, ngsniffer_open, NULL },
- { "Snoop", OPEN_INFO_MAGIC, snoop_open, NULL },
- { "IP Trace", OPEN_INFO_MAGIC, iptrace_open, NULL },
- { "Netmon", OPEN_INFO_MAGIC, netmon_open, NULL },
- { "Netxray", OPEN_INFO_MAGIC, netxray_open, NULL },
- { "Radcom", OPEN_INFO_MAGIC, radcom_open, NULL },
- { "Nettl", OPEN_INFO_MAGIC, nettl_open, NULL },
- { "Visual", OPEN_INFO_MAGIC, visual_open, NULL },
- { "5 Views", OPEN_INFO_MAGIC, _5views_open, NULL },
- { "Network Instruments", OPEN_INFO_MAGIC, network_instruments_open, NULL },
- { "Peek Tagged", OPEN_INFO_MAGIC, peektagged_open, NULL },
- { "DBS Etherwatch", OPEN_INFO_MAGIC, dbs_etherwatch_open, NULL },
- { "K12", OPEN_INFO_MAGIC, k12_open, NULL },
- { "Catapult DCT 2000", OPEN_INFO_MAGIC, catapult_dct2000_open, NULL },
- { "Aethra", OPEN_INFO_MAGIC, aethra_open, NULL },
- { "BTSNOOP", OPEN_INFO_MAGIC, btsnoop_open, "log" },
- { "EYESDN", OPEN_INFO_MAGIC, eyesdn_open, NULL },
- { "TNEF", OPEN_INFO_MAGIC, tnef_open, NULL },
- { "MIME Files with Magic Bytes", OPEN_INFO_MAGIC, mime_file_open, NULL },
- { "Lanalyzer", OPEN_INFO_HEURISTIC, lanalyzer_open, "tr1" },
+ { "Pcap", OPEN_INFO_MAGIC, libpcap_open, "pcap", NULL },
+ { "PcapNG", OPEN_INFO_MAGIC, pcapng_open, "pcapng", NULL },
+ { "NgSniffer", OPEN_INFO_MAGIC, ngsniffer_open, NULL, NULL },
+ { "Snoop", OPEN_INFO_MAGIC, snoop_open, NULL, NULL },
+ { "IP Trace", OPEN_INFO_MAGIC, iptrace_open, NULL, NULL },
+ { "Netmon", OPEN_INFO_MAGIC, netmon_open, NULL, NULL },
+ { "Netxray", OPEN_INFO_MAGIC, netxray_open, NULL, NULL },
+ { "Radcom", OPEN_INFO_MAGIC, radcom_open, NULL, NULL },
+ { "Nettl", OPEN_INFO_MAGIC, nettl_open, NULL, NULL },
+ { "Visual", OPEN_INFO_MAGIC, visual_open, NULL, NULL },
+ { "5 Views", OPEN_INFO_MAGIC, _5views_open, NULL, NULL },
+ { "Network Instruments", OPEN_INFO_MAGIC, network_instruments_open, NULL, NULL },
+ { "Peek Tagged", OPEN_INFO_MAGIC, peektagged_open, NULL, NULL },
+ { "DBS Etherwatch", OPEN_INFO_MAGIC, dbs_etherwatch_open, NULL, NULL },
+ { "K12", OPEN_INFO_MAGIC, k12_open, NULL, NULL },
+ { "Catapult DCT 2000", OPEN_INFO_MAGIC, catapult_dct2000_open, NULL, NULL },
+ { "Aethra", OPEN_INFO_MAGIC, aethra_open, NULL, NULL },
+ { "BTSNOOP", OPEN_INFO_MAGIC, btsnoop_open, "log", NULL },
+ { "EYESDN", OPEN_INFO_MAGIC, eyesdn_open, NULL, NULL },
+ { "TNEF", OPEN_INFO_MAGIC, tnef_open, NULL, NULL },
+ { "MIME Files with Magic Bytes", OPEN_INFO_MAGIC, mime_file_open, NULL, NULL },
+ { "Lanalyzer", OPEN_INFO_HEURISTIC, lanalyzer_open, "tr1", NULL },
/*
* PacketLogger must come before MPEG, because its files
* are sometimes grabbed by mpeg_open.
*/
- { "Packet Logger", OPEN_INFO_HEURISTIC, packetlogger_open, "pklg" },
+ { "Packet Logger", OPEN_INFO_HEURISTIC, packetlogger_open, "pklg", NULL },
/* Some MPEG files have magic numbers, others just have heuristics. */
- { "Mpeg", OPEN_INFO_HEURISTIC, mpeg_open, "mpg;mp3" },
- { "DCT3 Trace", OPEN_INFO_HEURISTIC, dct3trace_open, "xml" },
- { "Daintree SNA", OPEN_INFO_HEURISTIC, daintree_sna_open, "dcf" },
- { "Stanag 4607", OPEN_INFO_HEURISTIC, stanag4607_open, NULL },
- { "BER", OPEN_INFO_HEURISTIC, ber_open, NULL },
+ { "Mpeg", OPEN_INFO_HEURISTIC, mpeg_open, "mpg;mp3", NULL },
+ { "DCT3 Trace", OPEN_INFO_HEURISTIC, dct3trace_open, "xml", NULL },
+ { "Daintree SNA", OPEN_INFO_HEURISTIC, daintree_sna_open, "dcf", NULL },
+ { "Stanag 4607", OPEN_INFO_HEURISTIC, stanag4607_open, NULL, NULL },
+ { "BER", OPEN_INFO_HEURISTIC, ber_open, NULL, NULL },
/* I put NetScreen *before* erf, because there were some
* false positives with my test-files (Sake Blok, July 2007)
*
@@ -345,34 +345,59 @@ static struct open_info open_info_base[] = {
* because there were some cases where files of those types were
* misidentified as vwr files (Guy Harris, December 2013)
*/
- { "Netscreen", OPEN_INFO_HEURISTIC, netscreen_open, "txt" },
- { "ERF", OPEN_INFO_HEURISTIC, erf_open, "erf" },
- { "IPfix", OPEN_INFO_HEURISTIC, ipfix_open, "pfx;ipfix" },
- { "K12 Text", OPEN_INFO_HEURISTIC, k12text_open, "txt" },
- { "Peek Classic", OPEN_INFO_HEURISTIC, peekclassic_open, "pkt;tpc;apc;wpz" },
- { "PPP Dump", OPEN_INFO_HEURISTIC, pppdump_open, NULL },
- { "iSeries", OPEN_INFO_HEURISTIC, iseries_open, "txt" },
- { "i4btrace", OPEN_INFO_HEURISTIC, i4btrace_open, NULL },
- { "Mp2t", OPEN_INFO_HEURISTIC, mp2t_open, "ts;mpg" },
- { "Csids", OPEN_INFO_HEURISTIC, csids_open, NULL },
- { "VMS", OPEN_INFO_HEURISTIC, vms_open, "txt" },
- { "Cosine", OPEN_INFO_HEURISTIC, cosine_open, "txt" },
- { "Hcidump", OPEN_INFO_HEURISTIC, hcidump_open, NULL },
- { "Commview", OPEN_INFO_HEURISTIC, commview_open, "ncf" },
- { "Nstrace", OPEN_INFO_HEURISTIC, nstrace_open, "txt" },
+ { "Netscreen", OPEN_INFO_HEURISTIC, netscreen_open, "txt", NULL },
+ { "ERF", OPEN_INFO_HEURISTIC, erf_open, "erf", NULL },
+ { "IPfix", OPEN_INFO_HEURISTIC, ipfix_open, "pfx;ipfix",NULL },
+ { "K12 Text", OPEN_INFO_HEURISTIC, k12text_open, "txt", NULL },
+ { "Peek Classic", OPEN_INFO_HEURISTIC, peekclassic_open, "pkt;tpc;apc;wpz", NULL },
+ { "PPP Dump", OPEN_INFO_HEURISTIC, pppdump_open, NULL, NULL },
+ { "iSeries", OPEN_INFO_HEURISTIC, iseries_open, "txt", NULL },
+ { "i4btrace", OPEN_INFO_HEURISTIC, i4btrace_open, NULL, NULL },
+ { "Mp2t", OPEN_INFO_HEURISTIC, mp2t_open, "ts;mpg", NULL },
+ { "Csids", OPEN_INFO_HEURISTIC, csids_open, NULL, NULL },
+ { "VMS", OPEN_INFO_HEURISTIC, vms_open, "txt", NULL },
+ { "Cosine", OPEN_INFO_HEURISTIC, cosine_open, "txt", NULL },
+ { "Hcidump", OPEN_INFO_HEURISTIC, hcidump_open, NULL, NULL },
+ { "Commview", OPEN_INFO_HEURISTIC, commview_open, "ncf", NULL },
+ { "Nstrace", OPEN_INFO_HEURISTIC, nstrace_open, "txt", NULL },
/* ASCII trace files from Telnet sessions. */
- { "Ascend", OPEN_INFO_HEURISTIC, ascend_open, "txt" },
- { "Toshiba", OPEN_INFO_HEURISTIC, toshiba_open, "txt" },
+ { "Ascend", OPEN_INFO_HEURISTIC, ascend_open, "txt", NULL },
+ { "Toshiba", OPEN_INFO_HEURISTIC, toshiba_open, "txt", NULL },
/* Extremely weak heuristics - put them at the end. */
- { "VWR", OPEN_INFO_HEURISTIC, vwr_open, "vwr" },
- { "Camins", OPEN_INFO_HEURISTIC, camins_open, "camins" },
- { NULL, 0, NULL, NULL}
+ { "VWR", OPEN_INFO_HEURISTIC, vwr_open, "vwr", NULL },
+ { "Camins", OPEN_INFO_HEURISTIC, camins_open, "camins", NULL },
+ { NULL, 0, NULL, NULL, NULL }
};
+
+/* this is only used to build the dynamic array on load, do NOT use this
+ * for anything else, because the size of the actual array will change if
+ * Lua scripts register a new file reader.
+ */
#define N_OPEN_INFO_ROUTINES ((sizeof open_info_base / sizeof open_info_base[0]))
+static GArray *open_info_arr = NULL;
+
+/* this always points to the top of the created array */
struct open_info *open_routines = NULL;
-static GArray *open_info_arr = NULL;
+/* this points to the first OPEN_INFO_HEURISTIC type in the array */
+static guint heuristic_open_routine_idx = 0;
+
+static void set_heuristic_routine(void) {
+ guint i;
+ g_assert(open_info_arr != NULL);
+
+ for (i = 0; i < open_info_arr->len - 1; i++) {
+ if (open_routines[i].type == OPEN_INFO_HEURISTIC) {
+ heuristic_open_routine_idx = i;
+ break;
+ }
+ /* sanity check */
+ g_assert(open_routines[i].type == OPEN_INFO_MAGIC);
+ }
+
+ g_assert(heuristic_open_routine_idx > 0);
+}
void init_open_routines(void) {
@@ -382,15 +407,87 @@ void init_open_routines(void) {
g_array_append_vals(open_info_arr, open_info_base, N_OPEN_INFO_ROUTINES);
- open_routines = (struct open_info *) open_info_arr->data;
+ open_routines = (struct open_info *)(void*) open_info_arr->data;
+ set_heuristic_routine();
+}
+
+/* Registers a new file reader - currently only called by wslua code for Lua readers.
+ * If first_routine is true, it's added before other readers of its type (magic or heuristic).
+ * Also, it checks for an existing reader of the same name and errors if it finds one; if
+ * you want to handle that condition more gracefully, call wtap_has_open_info() first.
+ */
+void wtap_register_open_info(const struct open_info *oi, const gboolean first_routine) {
+ init_open_routines();
+
+ if (!oi || !oi->name) {
+ g_error("No open_info name given to register");
+ return;
+ }
+
+ /* verify name doesn't already exist */
+ if (wtap_has_open_info(oi->name)) {
+ g_error("Name given to register_open_info already exists");
+ return;
+ }
+
+ /* if it's magic and first, prepend it; if it's heuristic and not first,
+ append it; if it's anything else, stick it in the middle */
+ if (first_routine && oi->type == OPEN_INFO_MAGIC) {
+ g_array_prepend_val(open_info_arr, *oi);
+ } else if (!first_routine && oi->type == OPEN_INFO_HEURISTIC) {
+ g_array_append_val(open_info_arr, *oi);
+ } else {
+ g_array_insert_val(open_info_arr, heuristic_open_routine_idx, *oi);
+ }
+
+ open_routines = (struct open_info *)(void*) open_info_arr->data;
+ set_heuristic_routine();
}
-void wtap_register_open_info(const struct open_info *oi) {
+/* De-registers a file reader by removign it from the GArray based on its name.
+ * This function must NOT be called during wtap_open_offline(), since it changes the array.
+ * Note: this function will error if it doesn't find the given name; if you want to handle
+ * that condition more gracefully, call wtap_has_open_info() first.
+ */
+void wtap_deregister_open_info(const gchar *name) {
+ guint i;
init_open_routines();
- g_array_append_val(open_info_arr, oi);
+ if (!name) {
+ g_error("Missing open_info name to de-register");
+ return;
+ }
+
+ for (i = 0; i < open_info_arr->len - 1; i++) {
+ if (open_routines[i].name && strcmp(open_routines[i].name, name) == 0) {
+ open_info_arr = g_array_remove_index(open_info_arr, i);
+ set_heuristic_routine();
+ return;
+ }
+ }
- open_routines = (struct open_info *) open_info_arr->data;
+ g_error("deregister_open_info: name not found");
+}
+
+/* Determines if a open routine short name already exists
+ */
+gboolean wtap_has_open_info(const gchar *name) {
+ guint i;
+ init_open_routines();
+
+ if (!name) {
+ g_error("No name given to wtap_has_open_info!");
+ return FALSE;
+ }
+
+
+ for (i = 0; i < open_info_arr->len - 1; i++) {
+ if (open_routines[i].name && strcmp(open_routines[i].name, name) == 0) {
+ return TRUE;
+ }
+ }
+
+ return FALSE;
}
/*
@@ -527,6 +624,11 @@ static char *get_file_extension(const char *pathname)
return extensionp;
}
+/* TODO: this is called every time a file is checked for matching heuristic,
+ * which means just clicking on a file in the open dialog may call this thing
+ * twice... for *each* open_routine. That's silly, since this info never changes.
+ * It would be better to create this list in the lookup array on initialization.
+ */
static gboolean heuristic_uses_extension(unsigned int i, const char *extension)
{
gchar **extensions_set, **extensionp;
@@ -686,6 +788,7 @@ wtap* wtap_open_offline(const char *filename, unsigned int type, int *err, char
wth->subtype_close = NULL;
wth->tsprecision = WTAP_FILE_TSPREC_USEC;
wth->priv = NULL;
+ wth->wslua_data = NULL;
if (wth->random_fh) {
wth->fast_seek = g_ptr_array_new();
@@ -694,7 +797,8 @@ wtap* wtap_open_offline(const char *filename, unsigned int type, int *err, char
file_set_random_access(wth->random_fh, TRUE, wth->fast_seek);
}
- if (type != 0 && type <= open_info_arr->len + 1) {
+ /* 'type' is 1 greater than the array index */
+ if (type != 0 && type <= open_info_arr->len) {
int result;
if (file_seek(wth->fh, 0, SEEK_SET, err) == -1) {
@@ -703,6 +807,12 @@ wtap* wtap_open_offline(const char *filename, unsigned int type, int *err, char
return NULL;
}
+ /* Set wth with wslua data if any - this is how we pass the data
+ * to the file reader, kinda like the priv member but not free'd later.
+ * It's ok for this to copy a NULL.
+ */
+ wth->wslua_data = open_routines[type - 1].wslua_data;
+
result = (*open_routines[type - 1].open_routine)(wth, err, err_info);
switch (result) {
@@ -730,14 +840,18 @@ wtap* wtap_open_offline(const char *filename, unsigned int type, int *err, char
to start reading at the beginning.
Initialize the data offset while we're at it. */
- if (open_routines[i].type != OPEN_INFO_MAGIC) continue;
-
if (file_seek(wth->fh, 0, SEEK_SET, err) == -1) {
/* I/O error - give up */
wtap_close(wth);
return NULL;
}
+ /* Set wth with wslua data if any - this is how we pass the data
+ * to the file reader, kinda like the priv member but not free'd later.
+ * It's ok for this to copy a NULL.
+ */
+ wth->wslua_data = open_routines[i].wslua_data;
+
switch ((*open_routines[i].open_routine)(wth, err, err_info)) {
case -1:
@@ -755,14 +869,13 @@ wtap* wtap_open_offline(const char *filename, unsigned int type, int *err, char
}
}
+
/* 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 < open_info_arr->len - 1; i++) {
- if (open_routines[i].type != OPEN_INFO_HEURISTIC) continue;
+ for (i = heuristic_open_routine_idx; i < open_info_arr->len - 1; i++) {
/* Does this type use that extension? */
-
if (heuristic_uses_extension(i, extension)) {
/* Yes. */
if (file_seek(wth->fh, 0, SEEK_SET, err) == -1) {
@@ -772,6 +885,11 @@ wtap* wtap_open_offline(const char *filename, unsigned int type, int *err, char
return NULL;
}
+ /* Set wth with wslua data if any - this is how we pass the data
+ * to the file reader, kind of like priv but not free'd later.
+ */
+ wth->wslua_data = open_routines[i].wslua_data;
+
switch ((*open_routines[i].open_routine)(wth,
err, err_info)) {
@@ -794,8 +912,7 @@ wtap* wtap_open_offline(const char *filename, unsigned int type, int *err, char
}
/* Now try the ones that don't use it. */
- for (i = 0; i < open_info_arr->len - 1; i++) {
- if (open_routines[i].type != OPEN_INFO_HEURISTIC) continue;
+ for (i = heuristic_open_routine_idx; i < open_info_arr->len - 1; i++) {
/* Does this type use that extension? */
if (!heuristic_uses_extension(i, extension)) {
/* No. */
@@ -806,6 +923,11 @@ wtap* wtap_open_offline(const char *filename, unsigned int type, int *err, char
return NULL;
}
+ /* Set wth with wslua data if any - this is how we pass the data
+ * to the file reader, kind of like priv but not free'd later.
+ */
+ wth->wslua_data = open_routines[i].wslua_data;
+
switch ((*open_routines[i].open_routine)(wth,
err, err_info)) {
@@ -829,8 +951,7 @@ wtap* wtap_open_offline(const char *filename, unsigned int type, int *err, char
g_free(extension);
} else {
/* No - try all the heuristics types in order. */
- for (i = 0; i < open_info_arr->len - 1; i++) {
- if (open_routines[i].type != OPEN_INFO_HEURISTIC) continue;
+ for (i = heuristic_open_routine_idx; i < open_info_arr->len - 1; i++) {
if (file_seek(wth->fh, 0, SEEK_SET, err) == -1) {
/* I/O error - give up */
@@ -838,6 +959,11 @@ wtap* wtap_open_offline(const char *filename, unsigned int type, int *err, char
return NULL;
}
+ /* Set wth with wslua data if any - this is how we pass the data
+ * to the file reader, kind of like priv but not free'd later.
+ */
+ wth->wslua_data = open_routines[i].wslua_data;
+
switch ((*open_routines[i].open_routine)(wth, err, err_info)) {
case -1:
@@ -963,338 +1089,338 @@ static const struct file_type_subtype_info dump_open_table_base[] = {
/* WTAP_FILE_TYPE_SUBTYPE_UNKNOWN (only used internally for initialization) */
{ NULL, NULL, NULL, NULL,
FALSE, FALSE, 0,
- NULL, NULL },
+ NULL, NULL, NULL },
/* WTAP_FILE_TYPE_SUBTYPE_PCAP */
/* Gianluca Varenni suggests that we add "deprecated" to the description. */
{ "Wireshark/tcpdump/... - pcap", "pcap", "pcap", "cap;dmp",
FALSE, FALSE, 0,
- libpcap_dump_can_write_encap, libpcap_dump_open },
+ libpcap_dump_can_write_encap, libpcap_dump_open, NULL },
/* WTAP_FILE_TYPE_SUBTYPE_PCAPNG */
{ "Wireshark/... - pcapng", "pcapng", "pcapng", "ntar",
FALSE, TRUE, WTAP_COMMENT_PER_SECTION|WTAP_COMMENT_PER_INTERFACE|WTAP_COMMENT_PER_PACKET,
- pcapng_dump_can_write_encap, pcapng_dump_open },
+ pcapng_dump_can_write_encap, pcapng_dump_open, NULL },
/* WTAP_FILE_TYPE_SUBTYPE_PCAP_NSEC */
{ "Wireshark - nanosecond libpcap", "nseclibpcap", "pcap", "cap;dmp",
FALSE, FALSE, 0,
- libpcap_dump_can_write_encap, libpcap_dump_open },
+ libpcap_dump_can_write_encap, libpcap_dump_open, NULL },
/* WTAP_FILE_TYPE_SUBTYPE_PCAP_AIX */
{ "AIX tcpdump - libpcap", "aixlibpcap", "pcap", "cap;dmp",
FALSE, FALSE, 0,
- NULL, NULL },
+ NULL, NULL, NULL },
/* WTAP_FILE_TYPE_SUBTYPE_PCAP_SS991029 */
{ "Modified tcpdump - libpcap", "modlibpcap", "pcap", "cap;dmp",
FALSE, FALSE, 0,
- libpcap_dump_can_write_encap, libpcap_dump_open },
+ libpcap_dump_can_write_encap, libpcap_dump_open, NULL },
/* WTAP_FILE_TYPE_SUBTYPE_PCAP_NOKIA */
{ "Nokia tcpdump - libpcap ", "nokialibpcap", "pcap", "cap;dmp",
FALSE, FALSE, 0,
- libpcap_dump_can_write_encap, libpcap_dump_open },
+ libpcap_dump_can_write_encap, libpcap_dump_open, NULL },
/* WTAP_FILE_TYPE_SUBTYPE_PCAP_SS990417 */
{ "RedHat 6.1 tcpdump - libpcap", "rh6_1libpcap", "pcap", "cap;dmp",
FALSE, FALSE, 0,
- libpcap_dump_can_write_encap, libpcap_dump_open },
+ libpcap_dump_can_write_encap, libpcap_dump_open, NULL },
/* WTAP_FILE_TYPE_SUBTYPE_PCAP_SS990915 */
{ "SuSE 6.3 tcpdump - libpcap", "suse6_3libpcap", "pcap", "cap;dmp",
FALSE, FALSE, 0,
- libpcap_dump_can_write_encap, libpcap_dump_open },
+ libpcap_dump_can_write_encap, libpcap_dump_open, NULL },
/* WTAP_FILE_TYPE_SUBTYPE_5VIEWS */
{ "InfoVista 5View capture", "5views", "5vw", NULL,
TRUE, FALSE, 0,
- _5views_dump_can_write_encap, _5views_dump_open },
+ _5views_dump_can_write_encap, _5views_dump_open, NULL },
/* WTAP_FILE_TYPE_SUBTYPE_IPTRACE_1_0 */
{ "AIX iptrace 1.0", "iptrace_1", NULL, NULL,
FALSE, FALSE, 0,
- NULL, NULL },
+ NULL, NULL, NULL },
/* WTAP_FILE_TYPE_SUBTYPE_IPTRACE_2_0 */
{ "AIX iptrace 2.0", "iptrace_2", NULL, NULL,
FALSE, FALSE, 0,
- NULL, NULL },
+ NULL, NULL, NULL },
/* WTAP_FILE_TYPE_SUBTYPE_BER */
{ "ASN.1 Basic Encoding Rules", "ber", NULL, NULL,
FALSE, FALSE, 0,
- NULL, NULL },
+ NULL, NULL, NULL },
/* WTAP_FILE_TYPE_SUBTYPE_HCIDUMP */
{ "Bluetooth HCI dump", "hcidump", NULL, NULL,
FALSE, FALSE, 0,
- NULL, NULL },
+ NULL, NULL, NULL },
/* WTAP_FILE_TYPE_SUBTYPE_CATAPULT_DCT2000 */
{ "Catapult DCT2000 trace (.out format)", "dct2000", "out", NULL,
FALSE, FALSE, 0,
- catapult_dct2000_dump_can_write_encap, catapult_dct2000_dump_open },
+ catapult_dct2000_dump_can_write_encap, catapult_dct2000_dump_open, NULL },
/* WTAP_FILE_TYPE_SUBTYPE_NETXRAY_OLD */
{ "Cinco Networks NetXRay 1.x", "netxray1", "cap", NULL,
TRUE, FALSE, 0,
- NULL, NULL },
+ NULL, NULL, NULL },
/* WTAP_FILE_TYPE_SUBTYPE_NETXRAY_1_0 */
{ "Cinco Networks NetXRay 2.0 or later", "netxray2", "cap", NULL,
TRUE, FALSE, 0,
- NULL, NULL },
+ NULL, NULL, NULL },
/* WTAP_FILE_TYPE_SUBTYPE_COSINE */
{ "CoSine IPSX L2 capture", "cosine", "txt", NULL,
FALSE, FALSE, 0,
- NULL, NULL },
+ NULL, NULL, NULL },
/* WTAP_FILE_TYPE_SUBTYPE_CSIDS */
{ "CSIDS IPLog", "csids", NULL, NULL,
FALSE, FALSE, 0,
- NULL, NULL },
+ NULL, NULL, NULL },
/* WTAP_FILE_TYPE_SUBTYPE_DBS_ETHERWATCH */
{ "DBS Etherwatch (VMS)", "etherwatch", "txt", NULL,
FALSE, FALSE, 0,
- NULL, NULL},
+ NULL, NULL, NULL },
/* WTAP_FILE_TYPE_SUBTYPE_ERF */
{ "Endace ERF capture", "erf", "erf", NULL,
FALSE, FALSE, 0,
- erf_dump_can_write_encap, erf_dump_open },
+ erf_dump_can_write_encap, erf_dump_open, NULL },
/* WTAP_FILE_TYPE_SUBTYPE_EYESDN */
{ "EyeSDN USB S0/E1 ISDN trace format", "eyesdn", "trc", NULL,
FALSE, FALSE, 0,
- eyesdn_dump_can_write_encap, eyesdn_dump_open },
+ eyesdn_dump_can_write_encap, eyesdn_dump_open, NULL },
/* WTAP_FILE_TYPE_SUBTYPE_NETTL */
{ "HP-UX nettl trace", "nettl", "trc0", "trc1",
FALSE, FALSE, 0,
- nettl_dump_can_write_encap, nettl_dump_open },
+ nettl_dump_can_write_encap, nettl_dump_open, NULL },
/* WTAP_FILE_TYPE_SUBTYPE_ISERIES */
{ "IBM iSeries comm. trace (ASCII)", "iseries_ascii", "txt", NULL,
FALSE, FALSE, 0,
- NULL, NULL },
+ NULL, NULL, NULL },
/* WTAP_FILE_TYPE_SUBTYPE_ISERIES_UNICODE */
{ "IBM iSeries comm. trace (UNICODE)", "iseries_unicode", "txt", NULL,
FALSE, FALSE, 0,
- NULL, NULL },
+ NULL, NULL, NULL },
/* WTAP_FILE_TYPE_SUBTYPE_I4BTRACE */
{ "I4B ISDN trace", "i4btrace", NULL, NULL,
FALSE, FALSE, 0,
- NULL, NULL },
+ NULL, NULL, NULL },
/* WTAP_FILE_TYPE_SUBTYPE_ASCEND */
{ "Lucent/Ascend access server trace", "ascend", "txt", NULL,
FALSE, FALSE, 0,
- NULL, NULL },
+ NULL, NULL, NULL },
/* WTAP_FILE_TYPE_SUBTYPE_NETMON_1_x */
{ "Microsoft NetMon 1.x", "netmon1", "cap", NULL,
TRUE, FALSE, 0,
- netmon_dump_can_write_encap_1_x, netmon_dump_open },
+ netmon_dump_can_write_encap_1_x, netmon_dump_open, NULL },
/* WTAP_FILE_TYPE_SUBTYPE_NETMON_2_x */
{ "Microsoft NetMon 2.x", "netmon2", "cap", NULL,
TRUE, FALSE, 0,
- netmon_dump_can_write_encap_2_x, netmon_dump_open },
+ netmon_dump_can_write_encap_2_x, netmon_dump_open, NULL },
/* WTAP_FILE_TYPE_SUBTYPE_NGSNIFFER_UNCOMPRESSED */
{ "Sniffer (DOS)", "ngsniffer", "cap", "enc;trc;fdc;syc",
FALSE, FALSE, 0,
- ngsniffer_dump_can_write_encap, ngsniffer_dump_open },
+ ngsniffer_dump_can_write_encap, ngsniffer_dump_open, NULL },
/* WTAP_FILE_TYPE_SUBTYPE_NGSNIFFER_COMPRESSED */
{ "Sniffer (DOS), compressed", "ngsniffer_comp", "cap", "enc;trc;fdc;syc",
FALSE, FALSE, 0,
- NULL, NULL },
+ NULL, NULL, NULL },
/* WTAP_FILE_TYPE_SUBTYPE_NETXRAY_1_1 */
{ "NetXray, Sniffer (Windows) 1.1", "ngwsniffer_1_1", "cap", NULL,
TRUE, FALSE, 0,
- netxray_dump_can_write_encap_1_1, netxray_dump_open_1_1 },
+ netxray_dump_can_write_encap_1_1, netxray_dump_open_1_1, NULL },
/* WTAP_FILE_TYPE_SUBTYPE_NETXRAY_2_00x */
{ "Sniffer (Windows) 2.00x", "ngwsniffer_2_0", "cap", "caz",
TRUE, FALSE, 0,
- netxray_dump_can_write_encap_2_0, netxray_dump_open_2_0 },
+ netxray_dump_can_write_encap_2_0, netxray_dump_open_2_0, NULL },
/* WTAP_FILE_TYPE_SUBTYPE_NETWORK_INSTRUMENTS */
{ "Network Instruments Observer", "niobserver", "bfr", NULL,
FALSE, FALSE, 0,
- network_instruments_dump_can_write_encap, network_instruments_dump_open },
+ network_instruments_dump_can_write_encap, network_instruments_dump_open, NULL },
/* WTAP_FILE_TYPE_SUBTYPE_LANALYZER */
{ "Novell LANalyzer","lanalyzer", "tr1", NULL,
TRUE, FALSE, 0,
- lanalyzer_dump_can_write_encap, lanalyzer_dump_open },
+ lanalyzer_dump_can_write_encap, lanalyzer_dump_open, NULL },
/* WTAP_FILE_TYPE_SUBTYPE_PPPDUMP */
{ "pppd log (pppdump format)", "pppd", NULL, NULL,
FALSE, FALSE, 0,
- NULL, NULL },
+ NULL, NULL, NULL },
/* WTAP_FILE_TYPE_SUBTYPE_RADCOM */
{ "RADCOM WAN/LAN analyzer", "radcom", NULL, NULL,
FALSE, FALSE, 0,
- NULL, NULL },
+ NULL, NULL, NULL },
/* WTAP_FILE_TYPE_SUBTYPE_SNOOP */
{ "Sun snoop", "snoop", "snoop", "cap",
FALSE, FALSE, 0,
- snoop_dump_can_write_encap, snoop_dump_open },
+ snoop_dump_can_write_encap, snoop_dump_open, NULL },
/* WTAP_FILE_TYPE_SUBTYPE_SHOMITI */
{ "Shomiti/Finisar Surveyor", "shomiti", "cap", NULL,
FALSE, FALSE, 0,
- NULL, NULL },
+ NULL, NULL, NULL },
/* WTAP_FILE_TYPE_SUBTYPE_VMS */
{ "TCPIPtrace (VMS)", "tcpiptrace", "txt", NULL,
FALSE, FALSE, 0,
- NULL, NULL},
+ NULL, NULL, NULL },
/* WTAP_FILE_TYPE_SUBTYPE_K12 */
{ "Tektronix K12xx 32-bit .rf5 format", "rf5", "rf5", NULL,
TRUE, FALSE, 0,
- k12_dump_can_write_encap, k12_dump_open },
+ k12_dump_can_write_encap, k12_dump_open, NULL },
/* WTAP_FILE_TYPE_SUBTYPE_TOSHIBA */
{ "Toshiba Compact ISDN Router snoop", "toshiba", "txt", NULL,
FALSE, FALSE, 0,
- NULL, NULL },
+ NULL, NULL, NULL },
/* WTAP_FILE_TYPE_SUBTYPE_VISUAL_NETWORKS */
{ "Visual Networks traffic capture", "visual", NULL, NULL,
TRUE, FALSE, 0,
- visual_dump_can_write_encap, visual_dump_open },
+ visual_dump_can_write_encap, visual_dump_open, NULL },
/* WTAP_FILE_TYPE_SUBTYPE_PEEKCLASSIC_V56 */
{ "WildPackets classic (V5 and V6)", "peekclassic56", "pkt", "tpc;apc;wpz",
FALSE, FALSE, 0,
- NULL, NULL },
+ NULL, NULL, NULL },
/* WTAP_FILE_TYPE_SUBTYPE_PEEKCLASSIC_V7 */
{ "WildPackets classic (V7)", "peekclassic7", "pkt", "tpc;apc;wpz",
FALSE, FALSE, 0,
- NULL, NULL },
+ NULL, NULL, NULL },
/* WTAP_FILE_TYPE_SUBTYPE_PEEKTAGGED */
{ "WildPackets tagged", "peektagged", "pkt", "tpc;apc;wpz",
FALSE, FALSE, 0,
- NULL, NULL },
+ NULL, NULL, NULL },
/* WTAP_FILE_TYPE_SUBTYPE_MPEG */
{ "MPEG", "mpeg", "mpeg", "mpg;mp3",
FALSE, FALSE, 0,
- NULL, NULL },
+ NULL, NULL, NULL },
/* WTAP_FILE_TYPE_SUBTYPE_K12TEXT */
{ "K12 text file", "k12text", "txt", NULL,
FALSE, FALSE, 0,
- k12text_dump_can_write_encap, k12text_dump_open },
+ k12text_dump_can_write_encap, k12text_dump_open, NULL },
/* WTAP_FILE_TYPE_SUBTYPE_NETSCREEN */
{ "NetScreen snoop text file", "netscreen", "txt", NULL,
FALSE, FALSE, 0,
- NULL, NULL },
+ NULL, NULL, NULL },
/* WTAP_FILE_TYPE_SUBTYPE_COMMVIEW */
{ "TamoSoft CommView", "commview", "ncf", NULL,
FALSE, FALSE, 0,
- commview_dump_can_write_encap, commview_dump_open },
+ commview_dump_can_write_encap, commview_dump_open, NULL },
/* WTAP_FILE_TYPE_SUBTYPE_BTSNOOP */
{ "Symbian OS btsnoop", "btsnoop", "log", NULL,
FALSE, FALSE, 0,
- btsnoop_dump_can_write_encap, btsnoop_dump_open_h4 },
+ btsnoop_dump_can_write_encap, btsnoop_dump_open_h4, NULL },
/* WTAP_FILE_TYPE_SUBTYPE_TNEF */
{ "Transport-Neutral Encapsulation Format", "tnef", NULL, NULL,
FALSE, FALSE, 0,
- NULL, NULL },
+ NULL, NULL, NULL },
/* WTAP_FILE_TYPE_SUBTYPE_DCT3TRACE */
{ "Gammu DCT3 trace", "dct3trace", "xml", NULL,
FALSE, FALSE, 0,
- NULL, NULL },
+ NULL, NULL, NULL },
/* WTAP_FILE_TYPE_SUBTYPE_PACKETLOGGER */
{ "PacketLogger", "pklg", "pklg", NULL,
FALSE, FALSE, 0,
- NULL, NULL },
+ NULL, NULL, NULL },
/* WTAP_FILE_TYPE_SUBTYPE_DAINTREE_SNA */
{ "Daintree SNA", "dsna", "dcf", NULL,
FALSE, FALSE, 0,
- NULL, NULL },
+ NULL, NULL, NULL },
/* WTAP_FILE_TYPE_SUBTYPE_NETSCALER_1_0 */
{ "NetScaler Trace (Version 1.0)", "nstrace10", NULL, NULL,
TRUE, FALSE, 0,
- nstrace_10_dump_can_write_encap, nstrace_dump_open },
+ nstrace_10_dump_can_write_encap, nstrace_dump_open, NULL },
/* WTAP_FILE_TYPE_SUBTYPE_NETSCALER_2_0 */
{ "NetScaler Trace (Version 2.0)", "nstrace20", "cap", NULL,
TRUE, FALSE, 0,
- nstrace_20_dump_can_write_encap, nstrace_dump_open },
+ nstrace_20_dump_can_write_encap, nstrace_dump_open, NULL },
/* WTAP_FILE_TYPE_SUBTYPE_JPEG_JFIF */
{ "JPEG/JFIF", "jpeg", "jpg", "jpeg;jfif",
FALSE, FALSE, 0,
- NULL, NULL },
+ NULL, NULL, NULL },
/* WTAP_FILE_TYPE_SUBTYPE_IPFIX */
{ "IPFIX File Format", "ipfix", "pfx", "ipfix",
FALSE, FALSE, 0,
- NULL, NULL },
+ NULL, NULL, NULL },
/* WTAP_ENCAP_MIME */
{ "MIME File Format", "mime", NULL, NULL,
FALSE, FALSE, 0,
- NULL, NULL },
+ NULL, NULL, NULL },
/* WTAP_FILE_TYPE_SUBTYPE_AETHRA */
{ "Aethra .aps file", "aethra", "aps", NULL,
FALSE, FALSE, 0,
- NULL, NULL },
+ NULL, NULL, NULL },
/* WTAP_FILE_TYPE_SUBTYPE_MPEG_2_TS */
{ "MPEG2 transport stream", "mp2t", "mp2t", "ts;mpg",
FALSE, FALSE, 0,
- NULL, NULL },
+ NULL, NULL, NULL },
/* WTAP_FILE_TYPE_SUBTYPE_VWR_80211 */
{ "Ixia IxVeriWave .vwr Raw 802.11 Capture", "vwr80211", "vwr", NULL,
FALSE, FALSE, 0,
- NULL, NULL },
+ NULL, NULL, NULL },
/* WTAP_FILE_TYPE_SUBTYPE_VWR_ETH */
{ "Ixia IxVeriWave .vwr Raw Ethernet Capture", "vwreth", "vwr", NULL,
FALSE, FALSE, 0,
- NULL, NULL },
+ NULL, NULL, NULL },
/* WTAP_FILE_TYPE_SUBTYPE_CAMINS */
{ "CAM Inspector file", "camins", "camins", NULL,
FALSE, FALSE, 0,
- NULL, NULL },
+ NULL, NULL, NULL },
/* WTAP_FILE_TYPE_SUBTYPE_STANAG_4607 */
{ "STANAG 4607 Format", "stanag4607", NULL, NULL,
FALSE, FALSE, 0,
- NULL, NULL },
+ NULL, NULL, NULL },
/* WTAP_FILE_NETSCALER_3_0 */
{ "NetScaler Trace (Version 3.0)", "nstrace30", "cap", NULL,
TRUE, FALSE, 0,
- nstrace_30_dump_can_write_encap, nstrace_dump_open }
+ nstrace_30_dump_can_write_encap, nstrace_dump_open, NULL }
};
@@ -1315,14 +1441,77 @@ static void init_file_types_subtypes(void) {
dump_open_table = (const struct file_type_subtype_info*)(void *)dump_open_table_arr->data;
}
-int wtap_register_file_type_subtypes(const struct file_type_subtype_info* fi) {
+/* if subtype is WTAP_FILE_TYPE_SUBTYPE_UNKNOWN, then create a new subtype as well as register it, else replace the
+ existing entry in that spot */
+int wtap_register_file_type_subtypes(const struct file_type_subtype_info* fi, const int subtype) {
+ struct file_type_subtype_info* finfo = NULL;
init_file_types_subtypes();
- g_array_append_val(dump_open_table_arr,*fi);
+ if (!fi || !fi->name || !fi->short_name || subtype > wtap_num_file_types_subtypes) {
+ g_error("no file type info or invalid file type to register");
+ return subtype;
+ }
- dump_open_table = (const struct file_type_subtype_info*)(void *)dump_open_table_arr->data;
+ /* do we want a new registration? */
+ if (subtype == WTAP_FILE_TYPE_SUBTYPE_UNKNOWN) {
+ /* register a new one; first verify there isn't one named this already */
+ if (wtap_short_string_to_file_type_subtype(fi->short_name) > -1 ) {
+ g_error("file type short name already exists");
+ return subtype;
+ }
+
+ g_array_append_val(dump_open_table_arr,*fi);
+
+ dump_open_table = (const struct file_type_subtype_info*)(void *)dump_open_table_arr->data;
+
+ return wtap_num_file_types_subtypes++;
+ }
- return wtap_num_file_types_subtypes++;
+ /* re-register an existing one - verify the short names do match (sanity check really) */
+ if (!dump_open_table[subtype].short_name || strcmp(dump_open_table[subtype].short_name,fi->short_name) != 0) {
+ g_error("invalid file type name given to register");
+ return subtype;
+ }
+
+ /* yes, we're going to cast to change its const-ness */
+ finfo = (struct file_type_subtype_info*)(&dump_open_table[subtype]);
+ /*finfo->name = fi->name;*/
+ /*finfo->short_name = fi->short_name;*/
+ finfo->default_file_extension = fi->default_file_extension;
+ finfo->additional_file_extensions = fi->additional_file_extensions;
+ finfo->writing_must_seek = fi->writing_must_seek;
+ finfo->has_name_resolution = fi->has_name_resolution;
+ finfo->supported_comment_types = fi->supported_comment_types;
+ finfo->can_write_encap = fi->can_write_encap;
+ finfo->dump_open = fi->dump_open;
+ finfo->wslua_info = fi->wslua_info;
+
+ return subtype;
+}
+
+/* De-registers a file writer - they can never be removed from the GArray, but we can "clear" an entry.
+ */
+void wtap_deregister_file_type_subtype(const int subtype) {
+ struct file_type_subtype_info* finfo = NULL;
+
+ if (subtype < 0 || subtype >= wtap_num_file_types_subtypes) {
+ g_error("invalid file type to de-register");
+ return;
+ }
+
+ /* yes, we're going to cast to change its const-ness */
+ finfo = (struct file_type_subtype_info*)(&dump_open_table[subtype]);
+ /* unfortunately, it's not safe to null-out the name or short_name; bunch of other code doesn't guard aainst that, afaict */
+ /*finfo->name = NULL;*/
+ /*finfo->short_name = NULL;*/
+ finfo->default_file_extension = NULL;
+ finfo->additional_file_extensions = NULL;
+ finfo->writing_must_seek = FALSE;
+ finfo->has_name_resolution = FALSE;
+ finfo->supported_comment_types = 0;
+ finfo->can_write_encap = NULL;
+ finfo->dump_open = NULL;
+ finfo->wslua_info = NULL;
}
int wtap_get_num_file_types_subtypes(void)
@@ -1352,12 +1541,27 @@ wtap_dump_file_encap_type(const GArray *file_encaps)
static gboolean
wtap_dump_can_write_encap(int filetype, int encap)
{
+ int result = 0;
+
if (filetype < 0 || filetype >= wtap_num_file_types_subtypes
|| dump_open_table[filetype].can_write_encap == NULL)
return FALSE;
- if ((*dump_open_table[filetype].can_write_encap)(encap) != 0)
- return FALSE;
+ result = (*dump_open_table[filetype].can_write_encap)(encap);
+
+ if (result != 0) {
+ /* if the err said to check wslua's can_write_encap, try that */
+ if (result == WTAP_ERR_CHECK_WSLUA
+ && dump_open_table[filetype].wslua_info != NULL
+ && dump_open_table[filetype].wslua_info->wslua_can_write_encap != NULL) {
+
+ result = (*dump_open_table[filetype].wslua_info->wslua_can_write_encap)(encap, dump_open_table[filetype].wslua_info->wslua_data);
+
+ }
+
+ if (result != 0)
+ return FALSE;
+ }
return TRUE;
}
@@ -1898,6 +2102,15 @@ static gboolean wtap_dump_open_check(int file_type_subtype, int encap, gboolean
/* OK, we know how to write that type; can we write the specified
encapsulation type? */
*err = (*dump_open_table[file_type_subtype].can_write_encap)(encap);
+ /* if the err said to check wslua's can_write_encap, try that */
+ if (*err == WTAP_ERR_CHECK_WSLUA
+ && dump_open_table[file_type_subtype].wslua_info != NULL
+ && dump_open_table[file_type_subtype].wslua_info->wslua_can_write_encap != NULL) {
+
+ *err = (*dump_open_table[file_type_subtype].wslua_info->wslua_can_write_encap)(encap, dump_open_table[file_type_subtype].wslua_info->wslua_data);
+
+ }
+
if (*err != 0)
return FALSE;
@@ -1907,10 +2120,6 @@ static gboolean wtap_dump_open_check(int file_type_subtype, int encap, gboolean
return FALSE;
}
- *err = (*dump_open_table[file_type_subtype].can_write_encap)(encap);
- if (*err != 0)
- return FALSE;
-
/* All systems go! */
return TRUE;
}
@@ -1930,6 +2139,7 @@ static wtap_dumper* wtap_dump_alloc_wdh(int file_type_subtype, int encap, int sn
wdh->snaplen = snaplen;
wdh->encap = encap;
wdh->compressed = compressed;
+ wdh->wslua_data = NULL;
return wdh;
}
@@ -1959,6 +2169,12 @@ static gboolean wtap_dump_open_finish(wtap_dumper *wdh, int file_type_subtype, g
return FALSE;
}
+ /* Set wdh with wslua data if any - this is how we pass the data
+ * to the file writer.
+ */
+ if (dump_open_table[file_type_subtype].wslua_info)
+ wdh->wslua_data = dump_open_table[file_type_subtype].wslua_info->wslua_data;
+
/* Now try to open the file for writing. */
if (!(*dump_open_table[file_type_subtype].dump_open)(wdh, err)) {
return FALSE;
diff --git a/wiretap/file_wrappers.c b/wiretap/file_wrappers.c
index 1bfccf0570..0f30493ee6 100644
--- a/wiretap/file_wrappers.c
+++ b/wiretap/file_wrappers.c
@@ -1208,6 +1208,53 @@ file_read(void *buf, unsigned int len, FILE_T file)
}
/*
+ * XXX - this *peeks* at next byte, not a character.
+ */
+int
+file_peekc(FILE_T file)
+{
+ int ret = 0;
+
+ /* 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) {
+ return *(file->next);
+ }
+
+ /* process a skip request */
+ if (file->seek_pending) {
+ file->seek_pending = FALSE;
+ if (gz_skip(file, file->skip) == -1)
+ return -1;
+ }
+ /* if we processed a skip request, there may be data in the buffer,
+ * or an error could have occured; likewise if we didn't do seek but
+ * now call fill_out_buffer, the errors can occur. So we do this while
+ * loop to check before and after - this is basically the logic from
+ * file_read() but only for peeking not consuming a byte
+ */
+ while (1) {
+ if (file->have) {
+ return *(file->next);
+ }
+ else if (file->err) {
+ return -1;
+ }
+ else if (file->eof && file->avail_in == 0) {
+ return -1;
+ }
+ else if (fill_out_buffer(file) == -1) {
+ return -1;
+ }
+ }
+ /* it's actually impossible to get here */
+ return ret;
+}
+
+/*
* XXX - this gets a byte, not a character.
*/
int
diff --git a/wiretap/file_wrappers.h b/wiretap/file_wrappers.h
index c32d380180..7f3cdc4205 100644
--- a/wiretap/file_wrappers.h
+++ b/wiretap/file_wrappers.h
@@ -22,7 +22,7 @@
#define __WTAP_FILE_WRAPPERS_H__
#include <glib.h>
-#include <wtap.h>
+#include "wtap.h"
#include <wsutil/file_util.h>
#include "ws_symbol_export.h"
@@ -34,8 +34,9 @@ extern gboolean file_skip(FILE_T file, gint64 delta, int *err);
WS_DLL_PUBLIC gint64 file_tell(FILE_T stream);
extern gint64 file_tell_raw(FILE_T stream);
extern int file_fstat(FILE_T stream, ws_statb64 *statb, int *err);
-extern gboolean file_iscompressed(FILE_T stream);
+WS_DLL_PUBLIC gboolean file_iscompressed(FILE_T stream);
WS_DLL_PUBLIC int file_read(void *buf, unsigned int count, FILE_T file);
+WS_DLL_PUBLIC int file_peekc(FILE_T stream);
WS_DLL_PUBLIC int file_getc(FILE_T stream);
WS_DLL_PUBLIC char *file_gets(char *buf, int len, FILE_T stream);
WS_DLL_PUBLIC int file_eof(FILE_T stream);
diff --git a/wiretap/wtap-int.h b/wiretap/wtap-int.h
index 8c186b40ec..1d47f25423 100644
--- a/wiretap/wtap-int.h
+++ b/wiretap/wtap-int.h
@@ -58,7 +58,8 @@ struct wtap {
guint number_of_interfaces; /**< The number of interfaces a capture was made on, number of IDB:s in a pcapng file or equivalent(?)*/
GArray *interface_data; /**< An array holding the interface data from pcapng IDB:s or equivalent(?)*/
- void *priv;
+ void *priv; /* this one holds per-file state and is free'd automatically by wtap_close() */
+ void *wslua_data; /* this one holds wslua state info and is not free'd */
subtype_read_func subtype_read;
subtype_seek_read_func subtype_seek_read;
@@ -97,7 +98,8 @@ struct wtap_dumper {
gboolean compressed;
gint64 bytes_dumped;
- void *priv;
+ void *priv; /* this one holds per-file state and is free'd automatically by wtap_dump_close() */
+ void *wslua_data; /* this one holds wslua state info and is not free'd */
subtype_write_func subtype_write;
subtype_close_func subtype_close;
@@ -111,10 +113,10 @@ struct wtap_dumper {
GArray *interface_data; /**< An array holding the interface data from pcapng IDB:s or equivalent(?) NULL if not present.*/
};
-gboolean wtap_dump_file_write(wtap_dumper *wdh, const void *buf,
+WS_DLL_PUBLIC gboolean wtap_dump_file_write(wtap_dumper *wdh, const void *buf,
size_t bufsize, int *err);
-gint64 wtap_dump_file_seek(wtap_dumper *wdh, gint64 offset, int whence, int *err);
-gint64 wtap_dump_file_tell(wtap_dumper *wdh, int *err);
+WS_DLL_PUBLIC gint64 wtap_dump_file_seek(wtap_dumper *wdh, gint64 offset, int whence, int *err);
+WS_DLL_PUBLIC gint64 wtap_dump_file_tell(wtap_dumper *wdh, int *err);
extern gint wtap_num_file_types;
@@ -280,6 +282,7 @@ GSList *wtap_get_compressed_file_extensions(void);
* 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.)
*/
+WS_DLL_PUBLIC
gboolean
wtap_read_packet_bytes(FILE_T fh, Buffer *buf, guint length, int *err,
gchar **err_info);
diff --git a/wiretap/wtap.h b/wiretap/wtap.h
index f61b51f111..d0bc4747cb 100644
--- a/wiretap/wtap.h
+++ b/wiretap/wtap.h
@@ -1134,6 +1134,22 @@ typedef struct wtap_dumper wtap_dumper;
typedef struct wtap_reader *FILE_T;
+/* Similar to the wtap_open_routine_info for open routines, the following
+ * wtap_wslua_file_info struct is used by wslua code for Lua-based file writers.
+ *
+ * This concept is necessary because when wslua goes to invoke the
+ * registered dump/write_open routine callback in Lua, it needs the ref number representing
+ * the hooked function inside Lua. This will be stored in the thing pointed to
+ * by the void* data here. This 'data' pointer will be copied into the
+ * wtap_dumper struct's 'void* data' member when calling the dump_open function,
+ * which is how wslua finally retrieves it. Unlike wtap_dumper's 'priv' member, its
+ * 'data' member is not free'd in wtap_dump_close().
+ */
+typedef struct wtap_wslua_file_info {
+ int (*wslua_can_write_encap)(int, void*); /* a can_write_encap func for wslua uses */
+ void* wslua_data; /* holds the wslua data */
+} wtap_wslua_file_info_t;
+
/*
* For registering extensions used for capture file formats.
*
@@ -1218,6 +1234,7 @@ struct open_info {
int type;
wtap_open_routine_t open_routine;
const char *extensions;
+ void* wslua_data; /* should be NULL for C-code file readers */
};
WS_DLL_PUBLIC struct open_info *open_routines;
@@ -1264,6 +1281,10 @@ struct file_type_subtype_info {
/* the function to open the capture file for writing */
/* should be NULL is this file type don't have write support */
int (*dump_open)(wtap_dumper *, int *);
+
+ /* if can_write_encap returned WTAP_ERR_CHECK_WSLUA, then this is used instead */
+ /* this should be NULL for everyone except Lua-based file writers */
+ wtap_wslua_file_info_t *wslua_info;
};
#define WTAP_TYPE_AUTO 0
@@ -1488,18 +1509,21 @@ WS_DLL_PUBLIC
void register_all_wiretap_modules(void);
WS_DLL_PUBLIC
void wtap_register_file_type_extension(const struct file_extension_info *ei);
-#if 0
+
WS_DLL_PUBLIC
-void wtap_register_magic_number_open_routine(wtap_open_routine_t open_routine);
+void wtap_register_open_info(const struct open_info *oi, const gboolean first_routine);
WS_DLL_PUBLIC
-void wtap_register_heuristic_open_info(const struct heuristic_open_info *oi);
-#endif
+gboolean wtap_has_open_info(const gchar *name);
WS_DLL_PUBLIC
-void wtap_register_open_info(const struct open_info *oi);
+void wtap_deregister_open_info(const gchar *name);
+
WS_DLL_PUBLIC
unsigned int open_info_name_to_type(const char *name);
WS_DLL_PUBLIC
-int wtap_register_file_type_subtypes(const struct file_type_subtype_info* fi);
+int wtap_register_file_type_subtypes(const struct file_type_subtype_info* fi, const int subtype);
+WS_DLL_PUBLIC
+void wtap_deregister_file_type_subtype(const int file_type_subtype);
+
WS_DLL_PUBLIC
int wtap_register_encap_type(const char* name, const char* short_name);
@@ -1582,6 +1606,10 @@ int wtap_register_encap_type(const char* name, const char* short_name);
/** Packet being written is larger than we support; do not use when
reading, use WTAP_ERR_BAD_FILE instead */
+#define WTAP_ERR_CHECK_WSLUA -25
+ /** Not really an error: the file type being checked is from a Lua
+ plugin, so that the code will call wslua_can_write_encap() instead if it gets this */
+
#ifdef __cplusplus
}
#endif /* __cplusplus */