diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-10 20:34:10 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-10 20:34:10 +0000 |
commit | e4ba6dbc3f1e76890b22773807ea37fe8fa2b1bc (patch) | |
tree | 68cb5ef9081156392f1dd62a00c6ccc1451b93df /epan/wslua/wslua_listener.c | |
parent | Initial commit. (diff) | |
download | wireshark-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.c | 442 |
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: + */ |