summaryrefslogtreecommitdiffstats
path: root/epan/wslua/wslua_listener.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-10 20:34:10 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-10 20:34:10 +0000
commite4ba6dbc3f1e76890b22773807ea37fe8fa2b1bc (patch)
tree68cb5ef9081156392f1dd62a00c6ccc1451b93df /epan/wslua/wslua_listener.c
parentInitial commit. (diff)
downloadwireshark-e4ba6dbc3f1e76890b22773807ea37fe8fa2b1bc.tar.xz
wireshark-e4ba6dbc3f1e76890b22773807ea37fe8fa2b1bc.zip
Adding upstream version 4.2.2.upstream/4.2.2
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'epan/wslua/wslua_listener.c')
-rw-r--r--epan/wslua/wslua_listener.c442
1 files changed, 442 insertions, 0 deletions
diff --git a/epan/wslua/wslua_listener.c b/epan/wslua/wslua_listener.c
new file mode 100644
index 0000000..669a64f
--- /dev/null
+++ b/epan/wslua/wslua_listener.c
@@ -0,0 +1,442 @@
+/*
+ * wslua_listener.c
+ *
+ * Wireshark's interface to the Lua Programming Language
+ *
+ * Implementation of tap Listeners
+ *
+ * (c) 2006, Luis E. Garcia Ontanon <luis@ontanon.org>
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "config.h"
+
+/* WSLUA_MODULE Listener Post-Dissection Packet Analysis */
+
+#include "wslua.h"
+
+WSLUA_CLASS_DEFINE(Listener,FAIL_ON_NULL("Listener"));
+/*
+ A `Listener` is called once for every packet that matches a certain filter or has a certain tap.
+ It can read the tree, the packet's <<lua_class_Tvb,`Tvb`>> buffer as well as the tapped data, but it cannot add elements to the tree.
+ */
+
+static int tap_packet_cb_error_handler(lua_State* L) {
+ const gchar* error = lua_tostring(L,1);
+ static gchar* last_error = NULL;
+ static int repeated = 0;
+ static int next = 2;
+ gchar* where = (lua_pinfo) ?
+ wmem_strdup_printf(NULL, "Lua: on packet %i Error during execution of Listener packet callback",lua_pinfo->num) :
+ wmem_strdup_printf(NULL, "Lua: Error during execution of Listener packet callback") ;
+
+ /* show the error the 1st, 3rd, 5th, 9th, 17th, 33th... time it appears to avoid window flooding */
+ /* XXX the last series of identical errors won't be shown (the user however gets at least one message) */
+
+ if (! last_error) {
+ report_failure("%s:\n%s",where,error);
+ last_error = g_strdup(error);
+ repeated = 0;
+ next = 2;
+ wmem_free(NULL, where);
+ return 0;
+ }
+
+ if (g_str_equal(last_error,error) ) {
+ repeated++;
+ if ( repeated == next ) {
+ report_failure("%s happened %i times:\n %s",where,repeated,error);
+ next *= 2;
+ }
+ } else {
+ report_failure("%s happened %i times:\n %s",where,repeated,last_error);
+ g_free(last_error);
+ last_error = g_strdup(error);
+ repeated = 0;
+ next = 2;
+ report_failure("%s:\n %s",where,error);
+ }
+
+ wmem_free(NULL, where);
+ return 0;
+}
+
+
+static tap_packet_status lua_tap_packet(void *tapdata, packet_info *pinfo, epan_dissect_t *edt, const void *data, tap_flags_t flags _U_) {
+ Listener tap = (Listener)tapdata;
+ tap_packet_status retval = TAP_PACKET_DONT_REDRAW;
+ TreeItem lua_tree_tap;
+
+ if (tap->packet_ref == LUA_NOREF) return TAP_PACKET_DONT_REDRAW; /* XXX - report error and return TAP_PACKET_FAILED? */
+
+ lua_settop(tap->L,0);
+ lua_pushcfunction(tap->L,tap_packet_cb_error_handler);
+ lua_rawgeti(tap->L, LUA_REGISTRYINDEX, tap->packet_ref);
+
+ push_Pinfo(tap->L, pinfo);
+ push_Tvb(tap->L, edt->tvb);
+
+ if (tap->extractor) {
+ tap->extractor(tap->L,data);
+ } else {
+ lua_pushnil(tap->L);
+ }
+
+ lua_pinfo = pinfo;
+ lua_tvb = edt->tvb;
+ lua_tree_tap = create_TreeItem(edt->tree, NULL);
+ lua_tree = lua_tree_tap;
+
+ switch ( lua_pcall(tap->L,3,1,1) ) {
+ case 0:
+ /* XXX - treat 2 as TAP_PACKET_FAILED? */
+ retval = luaL_optinteger(tap->L,-1,1) == 0 ? TAP_PACKET_DONT_REDRAW : TAP_PACKET_REDRAW;
+ break;
+ case LUA_ERRRUN:
+ /* XXX - TAP_PACKET_FAILED? */
+ break;
+ case LUA_ERRMEM:
+ ws_warning("Memory alloc error while calling listener tap callback packet");
+ /* XXX - TAP_PACKET_FAILED? */
+ break;
+ case LUA_ERRERR:
+ ws_warning("Error while running the error handler function for listener tap callback");
+ break;
+ default:
+ ws_assert_not_reached();
+ break;
+ }
+
+ clear_outstanding_Pinfo();
+ clear_outstanding_Tvb();
+
+ lua_pinfo = NULL;
+ lua_tvb = NULL;
+ lua_tree = NULL;
+ g_free(lua_tree_tap);
+
+ return retval;
+}
+
+static int tap_reset_cb_error_handler(lua_State* L) {
+ const gchar* error = lua_tostring(L,1);
+ report_failure("Lua: Error during execution of Listener reset callback:\n %s",error);
+ return 0;
+}
+
+static void lua_tap_reset(void *tapdata) {
+ Listener tap = (Listener)tapdata;
+
+ if (tap->reset_ref == LUA_NOREF) return;
+
+ lua_pushcfunction(tap->L,tap_reset_cb_error_handler);
+ lua_rawgeti(tap->L, LUA_REGISTRYINDEX, tap->reset_ref);
+
+ switch ( lua_pcall(tap->L,0,0,lua_gettop(tap->L)-1) ) {
+ case 0:
+ break;
+ case LUA_ERRRUN:
+ ws_warning("Runtime error while calling a listener's init()");
+ break;
+ case LUA_ERRMEM:
+ ws_warning("Memory alloc error while calling a listener's init()");
+ break;
+ case LUA_ERRERR:
+ ws_warning("Error while running the error handler function for a listener's init()");
+ break;
+ default:
+ ws_assert_not_reached();
+ break;
+ }
+}
+
+static int tap_draw_cb_error_handler(lua_State* L) {
+ const gchar* error = lua_tostring(L,1);
+ report_failure("Lua: Error during execution of Listener draw callback:\n %s",error);
+ return 0;
+}
+
+static void lua_tap_draw(void *tapdata) {
+ Listener tap = (Listener)tapdata;
+ const gchar* error;
+
+ if (tap->draw_ref == LUA_NOREF) return;
+
+ lua_pushcfunction(tap->L,tap_draw_cb_error_handler);
+ lua_rawgeti(tap->L, LUA_REGISTRYINDEX, tap->draw_ref);
+
+ switch ( lua_pcall(tap->L,0,0,lua_gettop(tap->L)-1) ) {
+ case 0:
+ /* OK */
+ break;
+ case LUA_ERRRUN:
+ error = lua_tostring(tap->L,-1);
+ ws_warning("Runtime error while calling a listener's draw(): %s",error);
+ break;
+ case LUA_ERRMEM:
+ ws_warning("Memory alloc error while calling a listener's draw()");
+ break;
+ case LUA_ERRERR:
+ ws_warning("Error while running the error handler function for a listener's draw()");
+ break;
+ default:
+ ws_assert_not_reached();
+ break;
+ }
+}
+
+/* TODO: we should probably use a Lua table here */
+static GPtrArray *listeners = NULL;
+
+static void deregister_Listener (lua_State* L _U_, Listener tap) {
+ if (tap->all_fields) {
+ epan_set_always_visible(FALSE);
+ tap->all_fields = FALSE;
+ }
+
+ remove_tap_listener(tap);
+
+ g_free(tap->filter);
+ g_free(tap->name);
+ g_free(tap);
+}
+
+WSLUA_CONSTRUCTOR Listener_new(lua_State* L) {
+ /* Creates a new `Listener` tap object. */
+#define WSLUA_OPTARG_Listener_new_TAP 1 /* The name of this tap. See <<lua_fn_Listener_list__,`Listener.list()`>> for a way to print valid listener names. */
+#define WSLUA_OPTARG_Listener_new_FILTER 2 /*
+ A display filter to apply to the tap.
+ The `tap.packet` function will be called for each matching packet.
+ The default is `nil`, which matches every packet.
+ Example: "m2tp".
+ */
+#define WSLUA_OPTARG_Listener_new_ALLFIELDS 3 /*
+ Whether to generate all fields.
+ The default is `false`.
+ Note: This impacts performance. */
+
+ const gchar* tap_type = luaL_optstring(L,WSLUA_OPTARG_Listener_new_TAP,"frame");
+ const gchar* filter = luaL_optstring(L,WSLUA_OPTARG_Listener_new_FILTER,NULL);
+ const gboolean all_fields = wslua_optbool(L, WSLUA_OPTARG_Listener_new_ALLFIELDS, FALSE);
+ Listener tap;
+ GString* error;
+
+ tap = (Listener)g_malloc(sizeof(struct _wslua_tap));
+
+ tap->name = g_strdup(tap_type);
+ tap->filter = g_strdup(filter);
+ tap->extractor = wslua_get_tap_extractor(tap_type);
+ tap->L = L;
+ tap->packet_ref = LUA_NOREF;
+ tap->draw_ref = LUA_NOREF;
+ tap->reset_ref = LUA_NOREF;
+ tap->all_fields = all_fields;
+
+ /*
+ * XXX - do all Lua taps require the protocol tree? If not, it might
+ * be useful to have a way to indicate whether any do.
+ *
+ * XXX - do any Lua taps require the columns? If so, we either need
+ * to request them for this tap, or do so if any Lua taps require them.
+ */
+ error = register_tap_listener(tap_type, tap, tap->filter, TL_REQUIRES_PROTO_TREE, lua_tap_reset, lua_tap_packet, lua_tap_draw, NULL);
+
+ if (error) {
+ g_free(tap->filter);
+ g_free(tap->name);
+ g_free(tap);
+ /* WSLUA_ERROR(new_tap,"tap registration error"); */
+ lua_pushfstring(L,"Error while registering tap:\n%s",error->str);
+ g_string_free(error,TRUE);
+ return luaL_error(L,lua_tostring(L,-1));
+ }
+
+ if (all_fields) {
+ epan_set_always_visible(TRUE);
+ }
+
+ g_ptr_array_add(listeners, tap);
+
+ pushListener(L,tap);
+ WSLUA_RETURN(1); /* The newly created Listener listener object */
+}
+
+/* Allow dissector key names to be sorted alphabetically */
+static gint
+compare_dissector_key_name(gconstpointer dissector_a, gconstpointer dissector_b)
+{
+ return strcmp((const char*)dissector_a, (const char*)dissector_b);
+}
+
+WSLUA_CONSTRUCTOR Listener_list (lua_State *L) { /*
+ Gets a Lua array table of all registered `Listener` tap names.
+
+ Note: This is an expensive operation, and should only be used for troubleshooting.
+
+ @since 1.11.3
+
+ ===== Example
+
+ [source,lua]
+ ----
+ -- Print a list of tap listeners to stdout.
+ for _,tap_name in pairs(Listener.list()) do
+ print(tap_name)
+ end
+ ----
+ */
+ GList* list = get_tap_names();
+ GList* elist = NULL;
+ int i = 1;
+
+ if (!list) return luaL_error(L,"Cannot retrieve tap name list");
+
+ list = g_list_sort(list, (GCompareFunc)compare_dissector_key_name);
+ elist = g_list_first(list);
+
+ lua_newtable(L);
+ for (i=1; elist; i++, elist = g_list_next(elist)) {
+ lua_pushstring(L,(const char *) elist->data);
+ lua_rawseti(L,-2,i);
+ }
+
+ g_list_free(list);
+ WSLUA_RETURN(1); /* The array table of registered tap names */
+}
+
+WSLUA_METHOD Listener_remove(lua_State* L) {
+ /* Removes a tap `Listener`. */
+ Listener tap = checkListener(L,1);
+
+ if (listeners && g_ptr_array_remove(listeners, tap)) {
+ deregister_Listener(L, tap);
+ }
+
+ return 0;
+}
+
+WSLUA_METAMETHOD Listener__tostring(lua_State* L) {
+ /* Generates a string of debug info for the tap `Listener`. */
+ Listener tap = checkListener(L,1);
+
+ lua_pushfstring(L,"Listener(%s) filter: %s tapinfo: %s",tap->name, tap->filter ? tap->filter : "NONE", tap->extractor ? "YES": "NO");
+
+ return 1;
+}
+
+
+/* WSLUA_ATTRIBUTE Listener_packet WO A function that will be called once every packet matches the
+ `Listener` listener filter.
+
+ When later called by Wireshark, the `packet` function will be given:
+ 1. A `Pinfo` object
+ 2. A `Tvb` object
+ 3. A `tapinfo` table
+
+ [source,lua]
+ ----
+ function tap.packet(pinfo,tvb,tapinfo) ... end
+ ----
+
+ [NOTE]
+ ====
+ `tapinfo` is a table of info based on the `Listener` type, or nil.
+
+ See _epan/wslua/taps_ for `tapinfo` structure definitions.
+ ====
+*/
+WSLUA_ATTRIBUTE_FUNC_SETTER(Listener,packet);
+
+
+/* WSLUA_ATTRIBUTE Listener_draw WO A function that will be called once every few seconds to redraw the GUI objects;
+ in TShark this funtion is called only at the very end of the capture file.
+
+ When later called by Wireshark, the `draw` function will not be given any arguments.
+
+ [source,lua]
+ ----
+ function tap.draw() ... end
+ ----
+*/
+WSLUA_ATTRIBUTE_FUNC_SETTER(Listener,draw);
+
+/* WSLUA_ATTRIBUTE Listener_reset WO A function that will be called at the end of the capture run.
+
+ When later called by Wireshark, the `reset` function will not be given any arguments.
+
+ [source,lua]
+ ----
+ function tap.reset() ... end
+ ----
+*/
+WSLUA_ATTRIBUTE_FUNC_SETTER(Listener,reset);
+
+
+static int Listener__gc(lua_State* L _U_) {
+ /* do NOT free Listener here, only in deregister_Listener */
+ return 0;
+}
+
+/* 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 Listener_attributes[] = {
+ WSLUA_ATTRIBUTE_WOREG(Listener,packet),
+ WSLUA_ATTRIBUTE_WOREG(Listener,draw),
+ WSLUA_ATTRIBUTE_WOREG(Listener,reset),
+ { NULL, NULL, NULL }
+};
+
+WSLUA_METHODS Listener_methods[] = {
+ WSLUA_CLASS_FNREG(Listener,new),
+ WSLUA_CLASS_FNREG(Listener,remove),
+ WSLUA_CLASS_FNREG(Listener,list),
+ { NULL, NULL }
+};
+
+WSLUA_META Listener_meta[] = {
+ WSLUA_CLASS_MTREG(Listener,tostring),
+ { NULL, NULL }
+};
+
+int Listener_register(lua_State* L) {
+ wslua_set_tap_enums(L);
+
+ listeners = g_ptr_array_new();
+
+ WSLUA_REGISTER_CLASS_WITH_ATTRS(Listener);
+ return 0;
+}
+
+static void deregister_tap_listener (gpointer data, gpointer userdata) {
+ lua_State *L = (lua_State *) userdata;
+ Listener tap = (Listener) data;
+ deregister_Listener(L, tap);
+}
+
+int wslua_deregister_listeners(lua_State* L) {
+ g_ptr_array_foreach(listeners, deregister_tap_listener, L);
+ g_ptr_array_free(listeners, TRUE);
+ listeners = NULL;
+
+ return 0;
+}
+
+/*
+ * Editor modelines - https://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 4
+ * tab-width: 8
+ * indent-tabs-mode: nil
+ * End:
+ *
+ * vi: set shiftwidth=4 tabstop=8 expandtab:
+ * :indentSize=4:tabSize=8:noTabs=true:
+ */