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 | |
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')
46 files changed, 24562 insertions, 0 deletions
diff --git a/epan/wslua/.editorconfig b/epan/wslua/.editorconfig new file mode 100644 index 00000000..d9f4951a --- /dev/null +++ b/epan/wslua/.editorconfig @@ -0,0 +1,8 @@ +# +# Editor configuration +# +# https://editorconfig.org/ +# + +[wslua_struct.[ch]] +indent_size = 2 diff --git a/epan/wslua/CMakeLists.txt b/epan/wslua/CMakeLists.txt new file mode 100644 index 00000000..c7d16e1c --- /dev/null +++ b/epan/wslua/CMakeLists.txt @@ -0,0 +1,152 @@ +# CMakeLists.txt +# +# Wireshark - Network traffic analyzer +# By Gerald Combs <gerald@wireshark.org> +# Copyright 1998 Gerald Combs +# +# SPDX-License-Identifier: GPL-2.0-or-later +# + +add_subdirectory(lrexlib) + +set(WSLUA_MODULES + ${CMAKE_CURRENT_SOURCE_DIR}/lua_bitop.c + ${CMAKE_CURRENT_SOURCE_DIR}/wslua_address.c + ${CMAKE_CURRENT_SOURCE_DIR}/wslua_byte_array.c + ${CMAKE_CURRENT_SOURCE_DIR}/wslua_capture_info.c + ${CMAKE_CURRENT_SOURCE_DIR}/wslua_column.c + ${CMAKE_CURRENT_SOURCE_DIR}/wslua_dir.c + ${CMAKE_CURRENT_SOURCE_DIR}/wslua_dissector.c + ${CMAKE_CURRENT_SOURCE_DIR}/wslua_dumper.c + ${CMAKE_CURRENT_SOURCE_DIR}/wslua_field.c + ${CMAKE_CURRENT_SOURCE_DIR}/wslua_file.c + ${CMAKE_CURRENT_SOURCE_DIR}/wslua_file_common.c + ${CMAKE_CURRENT_SOURCE_DIR}/wslua_file_handler.c + ${CMAKE_CURRENT_SOURCE_DIR}/wslua_frame_info.c + ${CMAKE_CURRENT_SOURCE_DIR}/wslua_gui.c + ${CMAKE_CURRENT_SOURCE_DIR}/wslua_int64.c + ${CMAKE_CURRENT_SOURCE_DIR}/wslua_internals.c + ${CMAKE_CURRENT_SOURCE_DIR}/wslua_listener.c + ${CMAKE_CURRENT_SOURCE_DIR}/wslua_nstime.c + ${CMAKE_CURRENT_SOURCE_DIR}/wslua_pinfo.c + ${CMAKE_CURRENT_SOURCE_DIR}/wslua_pref.c + ${CMAKE_CURRENT_SOURCE_DIR}/wslua_proto.c + ${CMAKE_CURRENT_SOURCE_DIR}/wslua_proto_expert.c + ${CMAKE_CURRENT_SOURCE_DIR}/wslua_proto_field.c + ${CMAKE_CURRENT_SOURCE_DIR}/wslua_struct.c + ${CMAKE_CURRENT_SOURCE_DIR}/wslua_tree.c + ${CMAKE_CURRENT_SOURCE_DIR}/wslua_tvb.c + ${CMAKE_CURRENT_SOURCE_DIR}/wslua_utility.c + ${CMAKE_CURRENT_SOURCE_DIR}/wslua_wtap.c +) + +set(WSLUA_FILES + ${WSLUA_MODULES} + ${CMAKE_CURRENT_BINARY_DIR}/taps_wslua.c + ${CMAKE_CURRENT_BINARY_DIR}/register_wslua.c + ${CMAKE_CURRENT_SOURCE_DIR}/init_wslua.c +) +source_group(wslua FILES ${WSLUA_FILES}) + +set_source_files_properties( + ${WSLUA_FILES} + PROPERTIES + COMPILE_FLAGS "${WERROR_COMMON_FLAGS}" +) + +# make-taps.py depends on the current working directory +# to find the dissector files (contents of taps file +# depend on this actually, so just changing the paths +# in these lists won't help). + +# Used with untypical CWD +set(WSLUA_TAPS_USED + ${CMAKE_SOURCE_DIR}/epan/dissectors/packet-http.h + ${CMAKE_SOURCE_DIR}/epan/dissectors/packet-ip.h + ${CMAKE_SOURCE_DIR}/epan/dissectors/packet-udp.h + ${CMAKE_SOURCE_DIR}/epan/dissectors/packet-h225.h + ${CMAKE_SOURCE_DIR}/epan/dissectors/packet-ieee80211.h +) + +add_custom_command( + OUTPUT + ${CMAKE_BINARY_DIR}/epan/wslua/taps_wslua.c + # XXX taps.txt doesn't appear to be used anywhere. + ${CMAKE_BINARY_DIR}/epan/wslua/taps.txt + COMMAND + ${Python3_EXECUTABLE} + ${CMAKE_SOURCE_DIR}/epan/wslua/make-taps.py + ${CMAKE_BINARY_DIR}/epan/wslua/taps_wslua.c + ${CMAKE_BINARY_DIR}/epan/wslua/taps.txt + DEPENDS + ${CMAKE_SOURCE_DIR}/epan/wslua/taps.ini + ${CMAKE_SOURCE_DIR}/epan/wslua/make-taps.py + # Only here to add dependencies for the taps "source" files + ${WSLUA_TAPS_USED} +) + + +add_custom_command( + OUTPUT + declare_wslua.h + register_wslua.c + COMMAND + ${Python3_EXECUTABLE} + ${CMAKE_SOURCE_DIR}/epan/wslua/make-reg.py + ${WSLUA_MODULES} + DEPENDS + ${CMAKE_SOURCE_DIR}/epan/wslua/make-reg.py + ${WSLUA_MODULES} + # ${CMAKE_CURRENT_BINARY_DIR}/taps_wslua.c +) + +add_custom_target( + register_wslua ALL + DEPENDS + declare_wslua.h + register_wslua.c +) + +set_target_properties(register_wslua PROPERTIES FOLDER "Libs/epan/wslua") + +add_library(wslua OBJECT + ${WSLUA_FILES} +) + +if(FETCH_lua) + add_dependencies(wslua lua52) +endif() + +target_include_directories(wslua + SYSTEM PRIVATE + ${LUA_INCLUDE_DIRS} + PRIVATE + ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_CURRENT_SOURCE_DIR} +) + +set_target_properties(wslua PROPERTIES + FOLDER "Libs/epan/wslua" + COMPILE_DEFINITIONS "WS_BUILD_DLL" +) + +CHECKAPI( + NAME + wslua + SWITCHES + SOURCES + ${WSLUA_FILES} +) + +# +# Editor modelines - https://www.wireshark.org/tools/modelines.html +# +# Local variables: +# c-basic-offset: 8 +# tab-width: 8 +# indent-tabs-mode: t +# End: +# +# vi: set shiftwidth=8 tabstop=8 noexpandtab: +# :indentSize=8:tabSize=8:noTabs=false: +# diff --git a/epan/wslua/init_wslua.c b/epan/wslua/init_wslua.c new file mode 100644 index 00000000..40093265 --- /dev/null +++ b/epan/wslua/init_wslua.c @@ -0,0 +1,1760 @@ +/* + * init_wslua.c + * + * Wireshark's interface to the Lua Programming Language + * + * (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" +#define WS_LOG_DOMAIN LOG_DOMAIN_WSLUA + +#include "wslua.h" +#include "init_wslua.h" + +#include <epan/dissectors/packet-frame.h> +#include <errno.h> +#include <math.h> +#include <stdio.h> +#include <epan/expert.h> +#include <epan/ex-opt.h> +#include <epan/introspection.h> +#include <wiretap/introspection.h> +#include <wsutil/privileges.h> +#include <wsutil/file_util.h> +#include <wsutil/wslog.h> + +/* linked list of Lua plugins */ +typedef struct _wslua_plugin { + gchar *name; /**< plugin name */ + gchar *version; /**< plugin version */ + gchar *filename; /**< plugin filename */ + struct _wslua_plugin *next; +} wslua_plugin; + +static wslua_plugin *wslua_plugin_list = NULL; + +static lua_State* L = NULL; + +static void (*wslua_gui_print_func_ptr)(const char *, void *) = NULL; +static void *wslua_gui_print_data_ptr = NULL; +static int wslua_lua_print_func_ref = LUA_NOREF; + +/* XXX: global variables? Really?? Yuck. These could be done differently, + using the Lua registry */ +packet_info* lua_pinfo; +struct _wslua_treeitem* lua_tree; +tvbuff_t* lua_tvb; +int lua_dissectors_table_ref = LUA_NOREF; +int lua_heur_dissectors_table_ref = LUA_NOREF; + +static int proto_lua = -1; + +static int hf_wslua_fake = -1; +static int hf_wslua_text = -1; + +static expert_field ei_lua_error = EI_INIT; + +static expert_field ei_lua_proto_checksum_comment = EI_INIT; +static expert_field ei_lua_proto_checksum_chat = EI_INIT; +static expert_field ei_lua_proto_checksum_note = EI_INIT; +static expert_field ei_lua_proto_checksum_warn = EI_INIT; +static expert_field ei_lua_proto_checksum_error = EI_INIT; + +static expert_field ei_lua_proto_sequence_comment = EI_INIT; +static expert_field ei_lua_proto_sequence_chat = EI_INIT; +static expert_field ei_lua_proto_sequence_note = EI_INIT; +static expert_field ei_lua_proto_sequence_warn = EI_INIT; +static expert_field ei_lua_proto_sequence_error = EI_INIT; + +static expert_field ei_lua_proto_response_comment = EI_INIT; +static expert_field ei_lua_proto_response_chat = EI_INIT; +static expert_field ei_lua_proto_response_note = EI_INIT; +static expert_field ei_lua_proto_response_warn = EI_INIT; +static expert_field ei_lua_proto_response_error = EI_INIT; + +static expert_field ei_lua_proto_request_comment = EI_INIT; +static expert_field ei_lua_proto_request_chat = EI_INIT; +static expert_field ei_lua_proto_request_note = EI_INIT; +static expert_field ei_lua_proto_request_warn = EI_INIT; +static expert_field ei_lua_proto_request_error = EI_INIT; + +static expert_field ei_lua_proto_undecoded_comment = EI_INIT; +static expert_field ei_lua_proto_undecoded_chat = EI_INIT; +static expert_field ei_lua_proto_undecoded_note = EI_INIT; +static expert_field ei_lua_proto_undecoded_warn = EI_INIT; +static expert_field ei_lua_proto_undecoded_error = EI_INIT; + +static expert_field ei_lua_proto_reassemble_comment = EI_INIT; +static expert_field ei_lua_proto_reassemble_chat = EI_INIT; +static expert_field ei_lua_proto_reassemble_note = EI_INIT; +static expert_field ei_lua_proto_reassemble_warn = EI_INIT; +static expert_field ei_lua_proto_reassemble_error = EI_INIT; + +static expert_field ei_lua_proto_malformed_comment = EI_INIT; +static expert_field ei_lua_proto_malformed_chat = EI_INIT; +static expert_field ei_lua_proto_malformed_note = EI_INIT; +static expert_field ei_lua_proto_malformed_warn = EI_INIT; +static expert_field ei_lua_proto_malformed_error = EI_INIT; + +static expert_field ei_lua_proto_debug_comment = EI_INIT; +static expert_field ei_lua_proto_debug_chat = EI_INIT; +static expert_field ei_lua_proto_debug_note = EI_INIT; +static expert_field ei_lua_proto_debug_warn = EI_INIT; +static expert_field ei_lua_proto_debug_error = EI_INIT; + +static expert_field ei_lua_proto_protocol_comment = EI_INIT; +static expert_field ei_lua_proto_protocol_chat = EI_INIT; +static expert_field ei_lua_proto_protocol_note = EI_INIT; +static expert_field ei_lua_proto_protocol_warn = EI_INIT; +static expert_field ei_lua_proto_protocol_error = EI_INIT; + +static expert_field ei_lua_proto_security_comment = EI_INIT; +static expert_field ei_lua_proto_security_chat = EI_INIT; +static expert_field ei_lua_proto_security_note = EI_INIT; +static expert_field ei_lua_proto_security_warn = EI_INIT; +static expert_field ei_lua_proto_security_error = EI_INIT; + +static expert_field ei_lua_proto_comments_comment = EI_INIT; +static expert_field ei_lua_proto_comments_chat = EI_INIT; +static expert_field ei_lua_proto_comments_note = EI_INIT; +static expert_field ei_lua_proto_comments_warn = EI_INIT; +static expert_field ei_lua_proto_comments_error = EI_INIT; + +static expert_field ei_lua_proto_decryption_comment = EI_INIT; +static expert_field ei_lua_proto_decryption_chat = EI_INIT; +static expert_field ei_lua_proto_decryption_note = EI_INIT; +static expert_field ei_lua_proto_decryption_warn = EI_INIT; +static expert_field ei_lua_proto_decryption_error = EI_INIT; + +static expert_field ei_lua_proto_assumption_comment = EI_INIT; +static expert_field ei_lua_proto_assumption_chat = EI_INIT; +static expert_field ei_lua_proto_assumption_note = EI_INIT; +static expert_field ei_lua_proto_assumption_warn = EI_INIT; +static expert_field ei_lua_proto_assumption_error = EI_INIT; + +static expert_field ei_lua_proto_deprecated_comment = EI_INIT; +static expert_field ei_lua_proto_deprecated_chat = EI_INIT; +static expert_field ei_lua_proto_deprecated_note = EI_INIT; +static expert_field ei_lua_proto_deprecated_warn = EI_INIT; +static expert_field ei_lua_proto_deprecated_error = EI_INIT; + +static gint ett_wslua_traceback = -1; + +static bool +lua_pinfo_end(wmem_allocator_t *allocator _U_, wmem_cb_event_t event _U_, + void *user_data _U_) +{ + clear_outstanding_Tvb(); + clear_outstanding_TvbRange(); + clear_outstanding_Pinfo(); + clear_outstanding_Column(); + clear_outstanding_Columns(); + clear_outstanding_PrivateTable(); + clear_outstanding_TreeItem(); + clear_outstanding_FieldInfo(); + clear_outstanding_FuncSavers(); + + /* keep invoking this callback later? */ + return FALSE; +} + +static int wslua_not_register_menu(lua_State* LS) { + luaL_error(LS,"too late to register a menu"); + return 0; +} + +/* a getter for wslua_tree.c's TreeItem_add_item_any() to use */ +int get_hf_wslua_text(void) { + return hf_wslua_text; +} + +#if LUA_VERSION_NUM >= 502 +// Attach the lua traceback to the proto_tree +static int dissector_error_handler(lua_State *LS) { + // Entering, stack: [ error_handler, dissector, errmsg ] + + proto_item *tb_item; + proto_tree *tb_tree; + + // Add the expert info Lua error message + proto_tree_add_expert_format(lua_tree->tree, lua_pinfo, &ei_lua_error, lua_tvb, 0, 0, + "Lua Error: %s", lua_tostring(LS,-1)); + + // Create a new proto sub_tree for the traceback + tb_item = proto_tree_add_text_internal(lua_tree->tree, lua_tvb, 0, 0, "Lua Traceback"); + tb_tree = proto_item_add_subtree(tb_item, ett_wslua_traceback); + + // Push the traceback onto the stack + // After call, stack: [ error_handler, dissector, errmsg, tb_string ] + luaL_traceback(LS, LS, NULL, 1); + + // Get the string length of the traceback. Note that the string + // has a terminating NUL, but string_length doesn't include it. + // The lua docs say the string can have NULs in it too, but we + // ignore that because the traceback string shouldn't have them. + // This function does not own the string; it's still owned by lua. + size_t string_length; + const char *orig_tb_string = lua_tolstring(LS, -1, &string_length); + + // We make the copy so we can modify the string. Don't forget the + // extra byte for the terminating NUL! + char *tb_string = (char*) g_memdup2(orig_tb_string, string_length+1); + + // The string has tabs and new lines in it + // We will add proto_items for each new-line-delimited sub-string. + // We also convert tabs to spaces, because the Wireshark GUI + // shows tabs literally as "\t". + + // 'beginning' is the beginning of the sub-string + char *beginning = tb_string; + + // 'p' is the pointer to the byte as we iterate over the string + char *p = tb_string; + + size_t i; + bool skip_initial_tabs = true; + size_t last_eol_i = 0; + for (i = 0 ; i < string_length ; i++) { + // At the beginning of a sub-string, we will convert tabs to spaces + if (skip_initial_tabs) { + if (*p == '\t') { + *p = ' '; + } else { + // Once we hit the first non-tab character in a substring, + // we won't convert tabs (until the next substring) + skip_initial_tabs = false; + } + } + // If we see a newline, we add the substring to the proto tree + if (*p == '\n') { + // Terminate the string. + *p = '\0'; + proto_tree_add_text_internal(tb_tree, lua_tvb, 0, 0, "%s", beginning); + beginning = ++p; + skip_initial_tabs = true; + last_eol_i = i; + } else { + ++p; + } + } + + // The last portion of the string doesn't have a newline, so add it here + // after the loop. But to be sure, check that we didn't just add it, in + // case lua decides to change it in the future. + if ( last_eol_i < i-1 ) { + proto_tree_add_text_internal(tb_tree, lua_tvb, 0, 0, "%s", beginning); + } + + // Cleanup + g_free(tb_string); + + // Return the same original error message + return -2; +} + +#else + +static int dissector_error_handler(lua_State *LS) { + // Entering, stack: [ error_handler, dissector, errmsg ] + proto_tree_add_expert_format(lua_tree->tree, lua_pinfo, &ei_lua_error, lua_tvb, 0, 0, + "Lua Error: %s", lua_tostring(LS,-1)); + + // Return the same error message + return -1; +} + +#endif + +int dissect_lua(tvbuff_t* tvb, packet_info* pinfo, proto_tree* tree, void* data _U_) { + int consumed_bytes = tvb_captured_length(tvb); + tvbuff_t *saved_lua_tvb = lua_tvb; + packet_info *saved_lua_pinfo = lua_pinfo; + struct _wslua_treeitem *saved_lua_tree = lua_tree; + lua_pinfo = pinfo; + lua_tvb = tvb; + + /* + * almost equivalent to Lua: + * dissectors[current_proto](tvb,pinfo,tree) + */ + + // set the stack top be index 0 + lua_settop(L,0); + + // After call, stack: [ error_handler_func ] + lua_pushcfunction(L, dissector_error_handler); + + // Push the dissectors table onto the the stack + // After call, stack: [ error_handler_func, dissectors_table ] + lua_rawgeti(L, LUA_REGISTRYINDEX, lua_dissectors_table_ref); + + // Push a copy of the current_proto string onto the stack + // After call, stack: [ error_handler_func, dissectors_table, current_proto ] + lua_pushstring(L, pinfo->current_proto); + + // dissectors_table[current_proto], a dissector, goes into the stack + // The key (current_proto) is popped off the stack. + // After call, stack: [ error_handler_func, dissectors_table, dissector ] + lua_gettable(L, -2); + + // We don't need the dissectors_table in the stack + // After call, stack: [ error_handler_func, dissector ] + lua_remove(L,2); + + // Is the dissector a function? + if (lua_isfunction(L,2)) { + + // After call, stack: [ error_handler_func, dissector, tvb ] + push_Tvb(L,tvb); + // After call, stack: [ error_handler_func, dissector, tvb, pinfo ] + push_Pinfo(L,pinfo); + // After call, stack: [ error_handler_func, dissector, tvb, pinfo, TreeItem ] + lua_tree = push_TreeItem(L, tree, proto_tree_add_item(tree, hf_wslua_fake, tvb, 0, 0, ENC_NA)); + proto_item_set_hidden(lua_tree->item); + + if ( lua_pcall(L, /*num_args=*/3, /*num_results=*/1, /*error_handler_func_stack_position=*/1) ) { + // do nothing; the traceback error message handler function does everything + } else { + + /* if the Lua dissector reported the consumed bytes, pass it to our caller */ + if (lua_isnumber(L, -1)) { + /* we got the consumed bytes or the missing bytes as a negative number */ + consumed_bytes = wslua_togint(L, -1); + lua_pop(L, 1); + } + } + + } else { + proto_tree_add_expert_format(tree, pinfo, &ei_lua_error, tvb, 0, 0, + "Lua Error: did not find the %s dissector in the dissectors table", pinfo->current_proto); + } + + wmem_register_callback(pinfo->pool, lua_pinfo_end, NULL); + + lua_pinfo = saved_lua_pinfo; + lua_tree = saved_lua_tree; + lua_tvb = saved_lua_tvb; + + return consumed_bytes; + +} + +/** Type of a heuristic dissector, used in heur_dissector_add(). + * + * @param tvb the tvbuff with the (remaining) packet data + * @param pinfo the packet info of this packet (additional info) + * @param tree the protocol tree to be build or NULL + * @return TRUE if the packet was recognized by the sub-dissector (stop dissection here) + */ +gboolean heur_dissect_lua(tvbuff_t* tvb, packet_info* pinfo, proto_tree* tree, void* data _U_) { + gboolean result = FALSE; + tvbuff_t *saved_lua_tvb = lua_tvb; + packet_info *saved_lua_pinfo = lua_pinfo; + struct _wslua_treeitem *saved_lua_tree = lua_tree; + lua_tvb = tvb; + lua_pinfo = pinfo; + + ws_assert(tvb && pinfo); + + if (!pinfo->heur_list_name || !pinfo->current_proto) { + proto_tree_add_expert_format(tree, pinfo, &ei_lua_error, tvb, 0, 0, + "internal error in heur_dissect_lua: NULL list name or current proto"); + return FALSE; + } + + /* heuristic functions are stored in a table in the registry; the registry has a + * table at reference lua_heur_dissectors_table_ref, and that table has keys for + * the heuristic listname (e.g., "udp", "tcp", etc.), and that key's value is a + * table of keys of the Proto->name, and their value is the function. + * So it's like registry[table_ref][heur_list_name][proto_name] = func + */ + + lua_settop(L,0); + + /* get the table of all lua heuristic dissector lists */ + lua_rawgeti(L, LUA_REGISTRYINDEX, lua_heur_dissectors_table_ref); + + /* get the table inside that, for the lua heuristic dissectors of the requested heur list */ + if (!wslua_get_table(L, -1, pinfo->heur_list_name)) { + /* this shouldn't happen */ + lua_settop(L,0); + proto_tree_add_expert_format(tree, pinfo, &ei_lua_error, tvb, 0, 0, + "internal error in heur_dissect_lua: no %s heur list table", pinfo->heur_list_name); + return FALSE; + } + + /* get the table inside that, for the specific lua heuristic dissector */ + if (!wslua_get_field(L,-1,pinfo->current_proto)) { + /* this shouldn't happen */ + lua_settop(L,0); + proto_tree_add_expert_format(tree, pinfo, &ei_lua_error, tvb, 0, 0, + "internal error in heur_dissect_lua: no %s heuristic dissector for list %s", + pinfo->current_proto, pinfo->heur_list_name); + return FALSE; + } + + /* remove the table of all lists (the one in the registry) */ + lua_remove(L,1); + /* remove the heur_list_name heur list table */ + lua_remove(L,1); + + if (!lua_isfunction(L,-1)) { + /* this shouldn't happen */ + lua_settop(L,0); + proto_tree_add_expert_format(tree, pinfo, &ei_lua_error, tvb, 0, 0, + "internal error in heur_dissect_lua: %s heuristic dissector is not a function", pinfo->current_proto); + return FALSE; + } + + push_Tvb(L,tvb); + push_Pinfo(L,pinfo); + lua_tree = push_TreeItem(L, tree, proto_tree_add_item(tree, hf_wslua_fake, tvb, 0, 0, ENC_NA)); + proto_item_set_hidden(lua_tree->item); + + if ( lua_pcall(L,3,1,0) ) { + proto_tree_add_expert_format(tree, pinfo, &ei_lua_error, tvb, 0, 0, + "Lua Error: error calling %s heuristic dissector: %s", pinfo->current_proto, lua_tostring(L,-1)); + lua_settop(L,0); + } else { + if (lua_isboolean(L, -1) || lua_isnil(L, -1)) { + result = lua_toboolean(L, -1); + } else if (lua_type(L, -1) == LUA_TNUMBER) { + result = lua_tointeger(L,-1) != 0 ? TRUE : FALSE; + } else { + proto_tree_add_expert_format(tree, pinfo, &ei_lua_error, tvb, 0, 0, + "Lua Error: invalid return value from Lua %s heuristic dissector", pinfo->current_proto); + } + lua_pop(L, 1); + } + + wmem_register_callback(pinfo->pool, lua_pinfo_end, NULL); + + lua_pinfo = saved_lua_pinfo; + lua_tree = saved_lua_tree; + lua_tvb = saved_lua_tvb; + + return result; +} + +static void iter_table_and_call(lua_State* LS, const gchar* table_name, lua_CFunction error_handler) { + lua_settop(LS,0); + + lua_pushcfunction(LS,error_handler); + lua_getglobal(LS, table_name); + + if (!lua_istable(LS, 2)) { + report_failure("Lua: either `%s' does not exist or it is not a table!\n",table_name); + lua_close(LS); + L = NULL; + return; + } + + lua_pushnil(LS); + + while (lua_next(LS, 2)) { + const gchar* name = lua_tostring(L,-2); + + if (lua_isfunction(LS,-1)) { + + if ( lua_pcall(LS,0,0,1) ) { + lua_pop(LS,1); + } + + } else { + report_failure("Lua: Something not a function got its way into the %s.%s",table_name,name); + lua_close(LS); + L = NULL; + return; + } + } + + lua_settop(LS,0); +} + + +static int init_error_handler(lua_State* LS) { + const gchar* error = lua_tostring(LS,1); + report_failure("Lua: Error during execution of initialization:\n %s",error); + return 0; +} + + +static gboolean init_routine_initialized = FALSE; +static void wslua_init_routine(void) { + + if ( ! init_routine_initialized ) { + /* + * This must be done only once during the entire life of + * tshark/wireshark, because it must be done only once per the life of + * the Lua state/engine, so we guard this with the boolean above; + * otherwise it would occur every time a file is opened (every time + * epan_new() is called). + * + * If we ever allow the Lua state to be restarted, or to have multiple + * Lua states, we'll need to change this. + */ + lua_prime_all_fields(NULL); + init_routine_initialized = TRUE; + } + + if (L) { + iter_table_and_call(L, WSLUA_INIT_ROUTINES,init_error_handler); + } + +} + +static void wslua_cleanup_routine(void) { + if (L) { + iter_table_and_call(L, WSLUA_INIT_ROUTINES,init_error_handler); + } +} + +static int prefs_changed_error_handler(lua_State* LS) { + const gchar* error = lua_tostring(LS,1); + report_failure("Lua: Error during execution of prefs apply callback:\n %s",error); + return 0; +} + +void wslua_prefs_changed(void) { + if (L) { + iter_table_and_call(L, WSLUA_PREFS_CHANGED,prefs_changed_error_handler); + } +} + +static const char *getF(lua_State *LS _U_, void *ud, size_t *size) +{ + FILE *f=(FILE *)ud; + static char buff[512]; + if (feof(f)) return NULL; + *size=fread(buff,1,sizeof(buff),f); + return (*size>0) ? buff : NULL; +} + +static int error_handler_with_callback(lua_State *LS) { +#if LUA_VERSION_NUM >= 502 + const char *msg = lua_tostring(LS, 1); + luaL_traceback(LS, LS, msg, 1); /* push message with traceback. */ + lua_remove(LS, -2); /* remove original msg */ +#else + /* Return error message, unmodified */ + (void)LS; +#endif + return 1; +} + +static void wslua_add_plugin(const gchar *name, const gchar *version, const gchar *filename) +{ + wslua_plugin *new_plug, *lua_plug; + + lua_plug = wslua_plugin_list; + new_plug = g_new(wslua_plugin, 1); + + if (!lua_plug) { /* the list is empty */ + wslua_plugin_list = new_plug; + } else { + while (lua_plug->next != NULL) { + lua_plug = lua_plug->next; + } + lua_plug->next = new_plug; + } + + new_plug->name = g_strdup(name); + new_plug->version = g_strdup(version); + new_plug->filename = g_strdup(filename); + new_plug->next = NULL; +} + +static void wslua_clear_plugin_list(void) +{ + wslua_plugin *lua_plug; + + while (wslua_plugin_list) { + lua_plug = wslua_plugin_list; + wslua_plugin_list = wslua_plugin_list->next; + g_free (lua_plug->name); + g_free (lua_plug->version); + g_free (lua_plug->filename); + g_free (lua_plug); + } +} + +static int lua_script_push_args(const int script_num) { + gchar* argname = ws_strdup_printf("lua_script%d", script_num); + const gchar* argvalue = NULL; + int i, count = ex_opt_count(argname); + + for (i = 0; i < count; i++) { + argvalue = ex_opt_get_nth(argname, i); + lua_pushstring(L,argvalue); + } + + g_free(argname); + return count; +} + +#define FILE_NAME_KEY "__FILE__" +#define DIR_NAME_KEY "__DIR__" +#define DIR_SEP_NAME_KEY "__DIR_SEPARATOR__" +/* assumes a loaded chunk's function is on top of stack */ +static void set_file_environment(const gchar* filename, const gchar* dirname) { + const char* path; + + lua_newtable(L); /* environment for script (index 3) */ + + lua_pushstring(L, filename); /* tell the script about its filename */ + lua_setfield(L, -2, FILE_NAME_KEY); /* make it accessible at __FILE__ */ + + lua_pushstring(L, dirname); /* tell the script about its dirname */ + lua_setfield(L, -2, DIR_NAME_KEY); /* make it accessible at __DIR__ */ + + lua_pushstring(L, G_DIR_SEPARATOR_S); /* tell the script the directory separator */ + lua_setfield(L, -2, DIR_SEP_NAME_KEY); /* make it accessible at __DIR__ */ + + lua_newtable(L); /* new metatable */ + +#if LUA_VERSION_NUM >= 502 + lua_pushglobaltable(L); +#else + lua_pushvalue(L, LUA_GLOBALSINDEX); +#endif + /* prepend the directory name to _G.package.path */ + lua_getfield(L, -1, "package"); /* get the package table from the global table */ + lua_getfield(L, -1, "path"); /* get the path field from the package table */ + path = luaL_checkstring(L, -1); /* get the path string */ + lua_pop(L, 1); /* pop the path string */ + /* prepend the various paths */ + lua_pushfstring(L, "%s" G_DIR_SEPARATOR_S "?.lua;%s" G_DIR_SEPARATOR_S "?.lua;%s" G_DIR_SEPARATOR_S "?.lua;%s", + dirname, get_plugins_pers_dir(), get_plugins_dir(), path); + lua_setfield(L, -2, "path"); /* set the new string to be the path field of the package table */ + lua_setfield(L, -2, "package"); /* set the package table to be the package field of the global */ + + lua_setfield(L, -2, "__index"); /* make metatable's __index point to global table */ + + lua_setmetatable(L, -2); /* pop metatable, set it as metatable of environment */ + +#if LUA_VERSION_NUM >= 502 + lua_setupvalue(L, -2, 1); /* pop environment and assign it to upvalue 1 */ +#else + lua_setfenv(L, -2); /* pop environment and set it as the func's environment */ +#endif +} + + +/* If file_count > 0 then it's a command-line-added user script, and the count + * represents which user script it is (first=1, second=2, etc.). + * If dirname != NULL, then it's a user script and the dirname will get put in a file environment + * If dirname == NULL then it's a wireshark script and no file environment is created + */ +static gboolean lua_load_script(const gchar* filename, const gchar* dirname, const int file_count) { + FILE* file; + int error; + int numargs = 0; + + if (! ( file = ws_fopen(filename,"r")) ) { + report_open_failure(filename,errno,FALSE); + return FALSE; + } + + lua_settop(L,0); + + lua_pushcfunction(L, error_handler_with_callback); + /* The source argument should start with '@' to indicate a file. */ + lua_pushfstring(L, "@%s", filename); + +#if LUA_VERSION_NUM >= 502 + error = lua_load(L, getF, file, lua_tostring(L, -1), NULL); +#else + error = lua_load(L, getF, file, lua_tostring(L, -1)); +#endif + + switch (error) { + case 0: /* LUA_OK */ + if (dirname) { + set_file_environment(filename, dirname); + } + if (file_count > 0) { + numargs = lua_script_push_args(file_count); + } + error = lua_pcall(L, numargs, 0, 1); + if (error) { + switch (error) { + case LUA_ERRRUN: + report_failure("Lua: Error during loading:\n%s", lua_tostring(L, -1)); + break; + case LUA_ERRMEM: + report_failure("Lua: Error during loading: out of memory"); + break; + case LUA_ERRERR: + report_failure("Lua: Error during loading: error while retrieving error message"); + break; + default: + report_failure("Lua: Error during loading: unknown error %d", error); + break; + } + } + break; + + case LUA_ERRSYNTAX: + report_failure("Lua: syntax error: %s", lua_tostring(L, -1)); + break; + + case LUA_ERRMEM: + report_failure("Lua: memory allocation error during precompilation of %s", filename); + break; + + default: + report_failure("Lua: unknown error during precompilation of %s: %d", filename, error); + break; + } + fclose(file); + lua_pop(L, 2); /* pop the filename and error handler */ + return error == 0; +} + +/* This one is used to load the init.lua scripts, or anything else + * that shouldn't really be considered a real plugin. + */ +static gboolean lua_load_internal_script(const gchar* filename) { + return lua_load_script(filename, NULL, 0); +} + +/* This one is used to load plugins: either from the plugin directories, + * or from the command line. + */ +static gboolean lua_load_plugin_script(const gchar* name, + const gchar* filename, + const gchar* dirname, + const int file_count) +{ + ws_debug("Loading lua script: %s", filename); + if (lua_load_script(filename, dirname, file_count)) { + wslua_add_plugin(name, get_current_plugin_version(), filename); + clear_current_plugin_version(); + return TRUE; + } + return FALSE; +} + +static int wslua_panic(lua_State* LS) { + ws_error("LUA PANIC: %s",lua_tostring(LS,-1)); + /** ws_error() does an abort() and thus never returns **/ + return 0; /* keep gcc happy */ +} + +static gint string_compare(gconstpointer a, gconstpointer b) { + return strcmp((const char*)a, (const char*)b); +} + +static int lua_load_plugins(const char *dirname, register_cb cb, gpointer client_data, + gboolean count_only, const gboolean is_user, GHashTable *loaded_files, + int depth) +{ + WS_DIR *dir; /* scanned directory */ + WS_DIRENT *file; /* current file */ + gchar *filename, *dot; + const gchar *name; + int plugins_counter = 0; + GList *sorted_dirnames = NULL; + GList *sorted_filenames = NULL; + GList *l = NULL; + + if ((dir = ws_dir_open(dirname, 0, NULL)) != NULL) { + while ((file = ws_dir_read_name(dir)) != NULL) { + name = ws_dir_get_name(file); + + if (strcmp(name, ".") == 0 || strcmp(name, "..") == 0) { + /* skip "." and ".." */ + continue; + } + if (depth == 0 && strcmp(name, "init.lua") == 0) { + /* If we are in the root directory skip the special "init.lua" + * file that was already loaded before every other user script. + * (If we are below the root script directory we just treat it like any other + * lua script.) */ + continue; + } + + filename = ws_strdup_printf("%s" G_DIR_SEPARATOR_S "%s", dirname, name); + if (test_for_directory(filename) == EISDIR) { + sorted_dirnames = g_list_prepend(sorted_dirnames, (gpointer)filename); + continue; + } + + /* skip files starting wih . */ + if (name[0] == '.') { + g_free(filename); + continue; + } + + /* skip anything but files with .lua suffix */ + dot = strrchr(name, '.'); + if (dot == NULL || g_ascii_strcasecmp(dot+1, "lua") != 0) { + g_free(filename); + continue; + } + + if (file_exists(filename)) { + sorted_filenames = g_list_prepend(sorted_filenames, (gpointer)filename); + } + else { + g_free(filename); + } + } + ws_dir_close(dir); + } + + /* Depth first; ie, process subdirectories (in ASCIIbetical order) before files */ + if (sorted_dirnames != NULL) { + sorted_dirnames = g_list_sort(sorted_dirnames, string_compare); + for (l = sorted_dirnames; l != NULL; l = l->next) { + plugins_counter += lua_load_plugins((const char *)l->data, cb, client_data, count_only, is_user, loaded_files, depth + 1); + } + g_list_free_full(sorted_dirnames, g_free); + } + + /* Process files in ASCIIbetical order */ + if (sorted_filenames != NULL) { + sorted_filenames = g_list_sort(sorted_filenames, string_compare); + for (l = sorted_filenames; l != NULL; l = l->next) { + filename = (gchar *)l->data; + name = strrchr(filename, G_DIR_SEPARATOR) + 1; + + /* Check if we have already loaded this file name, if provided with a set */ + if (loaded_files && g_hash_table_lookup_extended(loaded_files, name, NULL, NULL)) { + continue; + } + + if (!count_only) { + if (cb) + (*cb)(RA_LUA_PLUGINS, name, client_data); + lua_load_plugin_script(name, filename, is_user ? dirname : NULL, 0); + + if (loaded_files) { + g_hash_table_insert(loaded_files, g_strdup(name), NULL); + } + } + plugins_counter++; + } + g_list_free_full(sorted_filenames, g_free); + } + + return plugins_counter; +} + +static int lua_load_global_plugins(register_cb cb, gpointer client_data, + gboolean count_only) +{ + return lua_load_plugins(get_plugins_dir(), cb, client_data, count_only, FALSE, NULL, 0); +} + +static int lua_load_pers_plugins(register_cb cb, gpointer client_data, + gboolean count_only) +{ + int plugins_counter = 0; + + /* aux table (set) to make sure we only load each file once (by name) */ + GHashTable *loaded_user_scripts = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL); + + /* load user scripts */ + plugins_counter += lua_load_plugins(get_plugins_pers_dir(), cb, client_data, count_only, TRUE, loaded_user_scripts, 0); + + /* for backward compatibility check old plugin directory */ + char *old_path = get_persconffile_path("plugins", FALSE); + if (strcmp(get_plugins_pers_dir(), old_path) != 0) { + plugins_counter += lua_load_plugins(old_path, cb, client_data, count_only, TRUE, loaded_user_scripts, 0); + } + g_free(old_path); + + g_hash_table_destroy(loaded_user_scripts); + + return plugins_counter; +} + +int wslua_count_plugins(void) { + int plugins_counter; + + /* count global scripts */ + plugins_counter = lua_load_global_plugins(NULL, NULL, TRUE); + + /* count user scripts */ + plugins_counter += lua_load_pers_plugins(NULL, NULL, TRUE); + + /* count scripts from command line */ + plugins_counter += ex_opt_count("lua_script"); + + return plugins_counter; +} + +void wslua_plugins_get_descriptions(wslua_plugin_description_callback callback, void *user_data) { + wslua_plugin *lua_plug; + + for (lua_plug = wslua_plugin_list; lua_plug != NULL; lua_plug = lua_plug->next) + { + callback(lua_plug->name, lua_plug->version, wslua_plugin_type_name(), + lua_plug->filename, user_data); + } +} + +static void +print_wslua_plugin_description(const char *name, const char *version, + const char *description, const char *filename, + void *user_data _U_) +{ + printf("%s\t%s\t%s\t%s\n", name, version, description, filename); +} + +void +wslua_plugins_dump_all(void) +{ + wslua_plugins_get_descriptions(print_wslua_plugin_description, NULL); +} + +const char *wslua_plugin_type_name(void) { + return "lua script"; +} + +static ei_register_info* ws_lua_ei = NULL; +static int ws_lua_ei_len = 0; + +expert_field* +wslua_get_expert_field(const int group, const int severity) +{ + int i; + const ei_register_info *ei = ws_lua_ei; + + ws_assert(ei); + + for (i=0; i < ws_lua_ei_len; i++, ei++) { + if (ei->eiinfo.group == group && ei->eiinfo.severity == severity) + return ei->ids; + } + + return &ei_lua_error; +} + +static void * +wslua_allocf(void *ud _U_, void *ptr, size_t osize _U_, size_t nsize) +{ + /* g_realloc frees ptr if nsize==0 and returns NULL (as desired). + * Furthermore it simplifies error handling by aborting on OOM */ + return g_realloc(ptr, nsize); +} + +#define WSLUA_EPAN_ENUMS_TABLE "_EPAN" +#define WSLUA_WTAP_ENUMS_TABLE "_WTAP" + +#define WSLUA_BASE_TABLE "base" +#define WSLUA_FTYPE_TABLE "ftypes" +#define WSLUA_FRAMETYPE_TABLE "frametype" +#define WSLUA_EXPERT_TABLE "expert" +#define WSLUA_EXPERT_GROUP_TABLE "group" +#define WSLUA_EXPERT_SEVERITY_TABLE "severity" +#define WSLUA_WTAP_ENCAPS_TABLE "wtap_encaps" +#define WSLUA_WTAP_TSPREC_TABLE "wtap_tsprecs" +#define WSLUA_WTAP_COMMENTS_TABLE "wtap_comments" +#define WSLUA_WTAP_RECTYPES_TABLE "wtap_rec_types" +#define WSLUA_WTAP_PRESENCE_FLAGS_TABLE "wtap_presence_flags" + +static void +add_table_symbol(const char *table, const char *name, int value) +{ + /* Get table from the global environment. */ + lua_getglobal(L, table); + /* Set symbol in table. */ + lua_pushstring(L, name); + lua_pushnumber(L, value); + lua_settable(L, -3); + /* Pop table from stack. */ + lua_pop(L, 1); +} + +static void +add_global_symbol(const char *name, int value) +{ + /* Set symbol in global environment. */ + lua_pushnumber(L, value); + lua_setglobal(L, name); +} + +static void +add_pi_severity_symbol(const char *name, int value) +{ + lua_getglobal(L, WSLUA_EXPERT_TABLE); + lua_getfield(L, -1, WSLUA_EXPERT_SEVERITY_TABLE); + lua_pushnumber(L, value); + lua_setfield(L, -2, name); + lua_pop(L, 2); +} + +static void +add_pi_group_symbol(const char *name, int value) +{ + lua_getglobal(L, WSLUA_EXPERT_TABLE); + lua_getfield(L, -1, WSLUA_EXPERT_GROUP_TABLE); + lua_pushnumber(L, value); + lua_setfield(L, -2, name); + lua_pop(L, 2); +} + +static void +add_menu_group_symbol(const char *name, int value) +{ + /* Set symbol in global environment. */ + lua_pushnumber(L, value); + char *str = g_strdup(name); + char *s = strstr(str, "_GROUP_"); + if (s == NULL) + return; + *s = '\0'; + s += strlen("_GROUP_"); + char *str2 = ws_strdup_printf("MENU_%s_%s", str, s); + lua_setglobal(L, str2); + g_free(str); + g_free(str2); +} + +/* + * Read introspection constants and add them according to the historical + * (sometimes arbitrary) rules of make-init-lua.py. For efficiency reasons + * we only loop the enums array once. + */ +static void +wslua_add_introspection(void) +{ + const ws_enum_t *ep; + + /* Add empty tables to be populated. */ + lua_newtable(L); + lua_setglobal(L, WSLUA_BASE_TABLE); + lua_newtable(L); + lua_setglobal(L, WSLUA_FTYPE_TABLE); + lua_newtable(L); + lua_setglobal(L, WSLUA_FRAMETYPE_TABLE); + lua_newtable(L); + lua_pushstring(L, WSLUA_EXPERT_GROUP_TABLE); + lua_newtable(L); + lua_settable(L, -3); + lua_pushstring(L, WSLUA_EXPERT_SEVERITY_TABLE); + lua_newtable(L); + lua_settable(L, -3); + lua_setglobal(L, WSLUA_EXPERT_TABLE); + /* Add catch-all _EPAN table. */ + lua_newtable(L); + lua_setglobal(L, WSLUA_EPAN_ENUMS_TABLE); + + for (ep = epan_inspect_enums(); ep->symbol != NULL; ep++) { + + if (g_str_has_prefix(ep->symbol, "BASE_")) { + add_table_symbol(WSLUA_BASE_TABLE, ep->symbol + strlen("BASE_"), ep->value); + } + else if (g_str_has_prefix(ep->symbol, "SEP_")) { + add_table_symbol(WSLUA_BASE_TABLE, ep->symbol + strlen("SEP_"), ep->value); + } + else if (g_str_has_prefix(ep->symbol, "ABSOLUTE_TIME_")) { + add_table_symbol(WSLUA_BASE_TABLE, ep->symbol + strlen("ABSOLUTE_TIME_"), ep->value); + } + else if (g_str_has_prefix(ep->symbol, "ENC_")) { + add_global_symbol(ep->symbol, ep->value); + } + else if (g_str_has_prefix(ep->symbol, "FT_FRAMENUM_")) { + add_table_symbol(WSLUA_FRAMETYPE_TABLE, ep->symbol + strlen("FT_FRAMENUM_"), ep->value); + } + else if (g_str_has_prefix(ep->symbol, "FT_")) { + add_table_symbol(WSLUA_FTYPE_TABLE, ep->symbol + strlen("FT_"), ep->value); + } + else if (g_str_has_prefix(ep->symbol, "PI_")) { + if (ep->value & PI_SEVERITY_MASK) { + add_pi_severity_symbol(ep->symbol + strlen("PI_"), ep->value); + } + else { + add_pi_group_symbol(ep->symbol + strlen("PI_"), ep->value); + } + /* For backward compatibility. */ + add_global_symbol(ep->symbol, ep->value); + } + else if (g_str_has_prefix(ep->symbol, "REGISTER_")) { + add_menu_group_symbol(ep->symbol + strlen("REGISTER_"), ep->value); + } + add_table_symbol(WSLUA_EPAN_ENUMS_TABLE, ep->symbol, ep->value); + } + + /* Add empty tables to be populated. */ + lua_newtable(L); + lua_setglobal(L, WSLUA_WTAP_ENCAPS_TABLE); + lua_newtable(L); + lua_setglobal(L, WSLUA_WTAP_TSPREC_TABLE); + lua_newtable(L); + lua_setglobal(L, WSLUA_WTAP_COMMENTS_TABLE); + lua_newtable(L); + lua_setglobal(L, WSLUA_WTAP_RECTYPES_TABLE); + lua_newtable(L); + lua_setglobal(L, WSLUA_WTAP_PRESENCE_FLAGS_TABLE); + /* Add catch-all _WTAP table. */ + lua_newtable(L); + lua_setglobal(L, WSLUA_WTAP_ENUMS_TABLE); + + for (ep = wtap_inspect_enums(); ep->symbol != NULL; ep++) { + + if (g_str_has_prefix(ep->symbol, "WTAP_ENCAP_")) { + add_table_symbol(WSLUA_WTAP_ENCAPS_TABLE, ep->symbol + strlen("WTAP_ENCAP_"), ep->value); + } + else if (g_str_has_prefix(ep->symbol, "WTAP_TSPREC_")) { + add_table_symbol(WSLUA_WTAP_TSPREC_TABLE, ep->symbol + strlen("WTAP_TSPREC_"), ep->value); + } + else if (g_str_has_prefix(ep->symbol, "WTAP_COMMENT_")) { + add_table_symbol(WSLUA_WTAP_COMMENTS_TABLE, ep->symbol + strlen("WTAP_COMMENT_"), ep->value); + } + else if (g_str_has_prefix(ep->symbol, "REC_TYPE_")) { + add_table_symbol(WSLUA_WTAP_RECTYPES_TABLE, ep->symbol + strlen("REC_TYPE_"), ep->value); + } + else if (g_str_has_prefix(ep->symbol, "WTAP_HAS_")) { + add_table_symbol(WSLUA_WTAP_PRESENCE_FLAGS_TABLE, ep->symbol + strlen("WTAP_HAS_"), ep->value); + } + add_table_symbol(WSLUA_WTAP_ENUMS_TABLE, ep->symbol, ep->value); + } +} + +static void wslua_add_deprecated(void) +{ + /* For backward compatibility. */ + lua_getglobal(L, "wtap_encaps"); + lua_setglobal(L, "wtap"); + + /* + * Generate the wtap_filetypes items for file types, for backwards + * compatibility. + * We no longer have WTAP_FILE_TYPE_SUBTYPE_ #defines; + * built-in file types are registered the same way that + * plugin file types are registered. + * + * New code should use wtap_name_to_file_type_subtype to + * look up file types by name. + */ + wslua_init_wtap_filetypes(L); + + /* Old / deprecated menu groups. These shoudn't be used in new code. */ + lua_getglobal(L, "MENU_PACKET_ANALYZE_UNSORTED"); + lua_setglobal(L, "MENU_ANALYZE_UNSORTED"); + lua_getglobal(L, "MENU_ANALYZE_CONVERSATION_FILTER"); + lua_setglobal(L, "MENU_ANALYZE_CONVERSATION"); + lua_getglobal(L, "MENU_STAT_CONVERSATION_LIST"); + lua_setglobal(L, "MENU_STAT_CONVERSATION"); + lua_getglobal(L, "MENU_STAT_ENDPOINT_LIST"); + lua_setglobal(L, "MENU_STAT_ENDPOINT"); + lua_getglobal(L, "MENU_STAT_RESPONSE_TIME"); + lua_setglobal(L, "MENU_STAT_RESPONSE"); + lua_getglobal(L, "MENU_PACKET_STAT_UNSORTED"); + lua_setglobal(L, "MENU_STAT_UNSORTED"); + + /* deprecated function names */ + lua_getglobal(L, "Dir"); + lua_getfield(L, -1, "global_config_path"); + lua_setglobal(L, "datafile_path"); + lua_getfield(L, -1, "personal_config_path"); + lua_setglobal(L, "persconffile_path"); + lua_pop(L, 1); +} + +static int wslua_console_print(lua_State *_L); + +static const char *lua_error_msg(int code) +{ + switch (code) { + case LUA_ERRSYNTAX: return "syntax error during precompilation"; + case LUA_ERRMEM: return "memory allocation error"; +#if LUA_VERSION_NUM == 502 + case LUA_ERRGCMM: return "error while running a __gc metamethod"; +#endif + case LUA_ERRRUN: return "runtime error"; + case LUA_ERRERR: return "error while running the message handler"; + default: break; /* Should not happen. */ + } + return "unknown error"; +} + +#if LUA_VERSION_NUM == 501 +#define LUA_OK 0 +#endif + +static int lua_funnel_console_eval(const char *console_input, + char **error_ptr, + char **error_hint, + void *callback_data _U_) +{ + int lcode; + + const int curr_top = lua_gettop(L); + + // If it starts with an equals sign replace it with "return" + char *codestr; + while (g_ascii_isspace(*console_input)) + console_input++; + if (*console_input == '=') + codestr = ws_strdup_printf("return %s", console_input+1); + else + codestr = (char *)console_input; /* Violate const safety to avoid a strdup() */ + + ws_noisy("Console input: %s", codestr); + lcode = luaL_loadstring(L, codestr); + /* Free only if we called strdup(). */ + if (codestr != console_input) + g_free(codestr); + codestr = NULL; + + if (lcode != LUA_OK) { + ws_debug("luaL_loadstring(): %s (%d)", lua_error_msg(lcode), lcode); + if (error_hint) { + *error_hint = g_strdup(lua_error_msg(lcode)); + } + /* If we have an error message return it. */ + if (error_ptr && !lua_isnil(L, -1)) { + *error_ptr = g_strdup(lua_tostring(L, -1)); + } + return -1; + } + + lcode = lua_pcall(L, 0, LUA_MULTRET, 0); + if (lcode != LUA_OK) { + ws_debug("lua_pcall(): %s (%d)", lua_error_msg(lcode), lcode); + if (error_hint) { + *error_hint = g_strdup(lua_error_msg(lcode)); + } + /* If we have an error message return it. */ + if (error_ptr && !lua_isnil(L, -1)) { + *error_ptr = g_strdup(lua_tostring(L, -1)); + } + return 1; + } + + // If we have values returned print them all + if (lua_gettop(L) > curr_top) { /* any arguments? */ + lua_pushcfunction(L, wslua_console_print); + lua_insert(L, curr_top+1); + lcode = lua_pcall(L, lua_gettop(L)-curr_top-1, 0, 0); + if (lcode != LUA_OK) { + /* Error printing result */ + if (error_hint) + *error_hint = ws_strdup_printf("error printing return values: %s", lua_error_msg(lcode)); + return 1; + } + } + + // Maintain stack discipline + if (lua_gettop(L) != curr_top) { + ws_critical("Expected stack top == %d, have %d", curr_top, lua_gettop(L)); + } + + ws_noisy("Success"); + return 0; +} + +#if LUA_VERSION_NUM == 501 +static const char *luaL_tolstring (lua_State *_L, int idx, size_t *len) { + if (!luaL_callmeta(_L, idx, "__tostring")) { /* no metafield? */ + switch (lua_type(_L, idx)) { + case LUA_TNUMBER: + case LUA_TSTRING: + lua_pushvalue(_L, idx); + break; + case LUA_TBOOLEAN: + lua_pushstring(_L, (lua_toboolean(_L, idx) ? "true" : "false")); + break; + case LUA_TNIL: + lua_pushliteral(_L, "nil"); + break; + default: + lua_pushfstring(_L, "%s: %p", luaL_typename(_L, idx), + lua_topointer(_L, idx)); + break; + } + } + return lua_tolstring(_L, -1, len); +} +#endif /* LUA_VERSION_NUM == 501 */ + +/* Receives C print function pointer as first upvalue. */ +/* Receives C print function data pointer as second upvalue. */ +static int wslua_console_print(lua_State *_L) +{ + GString *gstr = g_string_new(NULL); + const char *repr; + + /* Print arguments. */ + for (int i = 1; i <= lua_gettop(_L); i++) { + repr = luaL_tolstring(_L, i, NULL); + if (i > 1) + g_string_append_c(gstr, '\t'); + g_string_append(gstr, repr); + lua_pop(_L, 1); + } + g_string_append_c(gstr, '\n'); + + if (wslua_gui_print_func_ptr == NULL) { + ws_critical("GUI print function not registered; Trying to print: %s", gstr->str); + } + else { + wslua_gui_print_func_ptr(gstr->str, wslua_gui_print_data_ptr); + } + g_string_free(gstr, TRUE); + return 0; +} + +// Replace lua print function with a custom print function. +// We will place the original function in the Lua registry and return the reference. +static void lua_funnel_console_open(void (*print_func_ptr)(const char *, void *), + void *print_data_ptr, + void *callback_data _U_) +{ + /* Store original print value in the registry (even if it is nil). */ + lua_getglobal(L, "print"); + wslua_lua_print_func_ref = luaL_ref(L, LUA_REGISTRYINDEX); + + /* Set new "print" function (to output to the GUI) */ + lua_pushcfunction(L, wslua_console_print); + lua_setglobal(L, "print"); + + /* Save the globals */ + ws_assert(print_func_ptr); + wslua_gui_print_func_ptr = print_func_ptr; + wslua_gui_print_data_ptr = print_data_ptr; +} + +// Restore original Lua print function. Clean state. +static void lua_funnel_console_close(void *callback_data _U_) +{ + /* Restore the original print function. */ + int ref = (int)wslua_lua_print_func_ref; + /* push original function into stack */ + lua_rawgeti(L, LUA_REGISTRYINDEX, ref); + lua_setglobal(L, "print"); + /* Release reference */ + luaL_unref(L, LUA_REGISTRYINDEX, ref); + + /* Clear the globals. */ + wslua_gui_print_func_ptr = NULL; + wslua_gui_print_data_ptr = NULL; + wslua_lua_print_func_ref = LUA_NOREF; +} + +static int wslua_file_exists(lua_State *_L) +{ + const char *path = luaL_checkstring(_L, 1); + lua_pushboolean(_L, g_file_test(path, G_FILE_TEST_EXISTS)); + return 1; +} + +static int wslua_lua_typeof(lua_State *_L) +{ + const char *classname = wslua_typeof(_L, 1); + lua_pushstring(_L, classname); + return 1; +} + +/* Other useful constants */ +void wslua_add_useful_constants(void) +{ + const funnel_ops_t *ops = funnel_get_funnel_ops(); + char *path; + + WSLUA_REG_GLOBAL_BOOL(L,"GUI_ENABLED",ops && ops->new_dialog); + + /* DATA_DIR has a trailing directory separator. */ + path = get_datafile_path(""); + lua_pushfstring(L, "%s"G_DIR_SEPARATOR_S, path); + g_free(path); + lua_setglobal(L, "DATA_DIR"); + + /* USER_DIR has a trailing directory separator. */ + path = get_persconffile_path("", FALSE); + lua_pushfstring(L, "%s"G_DIR_SEPARATOR_S, path); + g_free(path); + lua_setglobal(L, "USER_DIR"); + + lua_pushcfunction(L, wslua_file_exists); + lua_setglobal(L, "file_exists"); + + lua_pushcfunction(L, wslua_lua_typeof); + lua_setglobal(L, "typeof"); +} + +void wslua_init(register_cb cb, gpointer client_data) { + gchar* filename; + gboolean enable_lua = TRUE; + gboolean run_anyway = FALSE; + expert_module_t* expert_lua; + int file_count = 1; + static gboolean first_time = TRUE; + int i; + + static hf_register_info hf[] = { + { &hf_wslua_fake, + { "Wireshark Lua fake item", "_ws.lua.fake", + FT_NONE, BASE_NONE, NULL, 0x0, + "Fake internal item for Wireshark Lua", HFILL }}, + { &hf_wslua_text, + { "Wireshark Lua text", "_ws.lua.text", + FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL }}, + }; + static gint *ett[] = { + &ett_wslua_traceback, + }; + + static ei_register_info ei[] = { + /* the following are created so we can continue to support the TreeItem_add_expert_info() + function to Lua scripts. That function doesn't know what registered protocol to use, + so it uses the "_ws.lua" one. */ + /* XXX: it seems to me we should not be offering PI_GROUP_MASK nor PI_SEVERITY_MASK since + they are not real settings, so I'm not adding them below (should they also not be exported + into Lua? they are right now.) */ + /* NOTE: do not add expert entries at the top of this array - only at the bottom. This array + is not only used by expert.c, but also by wslua_get_expert_field() to find the appropriate + "dummy" entry. So this array's ordering matters. */ + { &ei_lua_proto_checksum_comment, { "_ws.lua.proto.comment", PI_CHECKSUM, PI_COMMENT ,"Protocol Comment", EXPFILL }}, + { &ei_lua_proto_checksum_chat, { "_ws.lua.proto.chat", PI_CHECKSUM, PI_CHAT ,"Protocol Chat", EXPFILL }}, + { &ei_lua_proto_checksum_note, { "_ws.lua.proto.note", PI_CHECKSUM, PI_NOTE ,"Protocol Note", EXPFILL }}, + { &ei_lua_proto_checksum_warn, { "_ws.lua.proto.warning", PI_CHECKSUM, PI_WARN ,"Protocol Warning", EXPFILL }}, + { &ei_lua_proto_checksum_error, { "_ws.lua.proto.error", PI_CHECKSUM, PI_ERROR ,"Protocol Error", EXPFILL }}, + + { &ei_lua_proto_sequence_comment, { "_ws.lua.proto.comment", PI_SEQUENCE, PI_COMMENT ,"Protocol Comment", EXPFILL }}, + { &ei_lua_proto_sequence_chat, { "_ws.lua.proto.chat", PI_SEQUENCE, PI_CHAT ,"Protocol Chat", EXPFILL }}, + { &ei_lua_proto_sequence_note, { "_ws.lua.proto.note", PI_SEQUENCE, PI_NOTE ,"Protocol Note", EXPFILL }}, + { &ei_lua_proto_sequence_warn, { "_ws.lua.proto.warning", PI_SEQUENCE, PI_WARN ,"Protocol Warning", EXPFILL }}, + { &ei_lua_proto_sequence_error, { "_ws.lua.proto.error", PI_SEQUENCE, PI_ERROR ,"Protocol Error", EXPFILL }}, + + { &ei_lua_proto_response_comment, { "_ws.lua.proto.comment", PI_RESPONSE_CODE, PI_COMMENT ,"Protocol Comment", EXPFILL }}, + { &ei_lua_proto_response_chat, { "_ws.lua.proto.chat", PI_RESPONSE_CODE, PI_CHAT ,"Protocol Chat", EXPFILL }}, + { &ei_lua_proto_response_note, { "_ws.lua.proto.note", PI_RESPONSE_CODE, PI_NOTE ,"Protocol Note", EXPFILL }}, + { &ei_lua_proto_response_warn, { "_ws.lua.proto.warning", PI_RESPONSE_CODE, PI_WARN ,"Protocol Warning", EXPFILL }}, + { &ei_lua_proto_response_error, { "_ws.lua.proto.error", PI_RESPONSE_CODE, PI_ERROR ,"Protocol Error", EXPFILL }}, + + { &ei_lua_proto_request_comment, { "_ws.lua.proto.comment", PI_REQUEST_CODE, PI_COMMENT ,"Protocol Comment", EXPFILL }}, + { &ei_lua_proto_request_chat, { "_ws.lua.proto.chat", PI_REQUEST_CODE, PI_CHAT ,"Protocol Chat", EXPFILL }}, + { &ei_lua_proto_request_note, { "_ws.lua.proto.note", PI_REQUEST_CODE, PI_NOTE ,"Protocol Note", EXPFILL }}, + { &ei_lua_proto_request_warn, { "_ws.lua.proto.warning", PI_REQUEST_CODE, PI_WARN ,"Protocol Warning", EXPFILL }}, + { &ei_lua_proto_request_error, { "_ws.lua.proto.error", PI_REQUEST_CODE, PI_ERROR ,"Protocol Error", EXPFILL }}, + + { &ei_lua_proto_undecoded_comment, { "_ws.lua.proto.comment", PI_UNDECODED, PI_COMMENT ,"Protocol Comment", EXPFILL }}, + { &ei_lua_proto_undecoded_chat, { "_ws.lua.proto.chat", PI_UNDECODED, PI_CHAT ,"Protocol Chat", EXPFILL }}, + { &ei_lua_proto_undecoded_note, { "_ws.lua.proto.note", PI_UNDECODED, PI_NOTE ,"Protocol Note", EXPFILL }}, + { &ei_lua_proto_undecoded_warn, { "_ws.lua.proto.warning", PI_UNDECODED, PI_WARN ,"Protocol Warning", EXPFILL }}, + { &ei_lua_proto_undecoded_error, { "_ws.lua.proto.error", PI_UNDECODED, PI_ERROR ,"Protocol Error", EXPFILL }}, + + { &ei_lua_proto_reassemble_comment, { "_ws.lua.proto.comment", PI_REASSEMBLE, PI_COMMENT ,"Protocol Comment", EXPFILL }}, + { &ei_lua_proto_reassemble_chat, { "_ws.lua.proto.chat", PI_REASSEMBLE, PI_CHAT ,"Protocol Chat", EXPFILL }}, + { &ei_lua_proto_reassemble_note, { "_ws.lua.proto.note", PI_REASSEMBLE, PI_NOTE ,"Protocol Note", EXPFILL }}, + { &ei_lua_proto_reassemble_warn, { "_ws.lua.proto.warning", PI_REASSEMBLE, PI_WARN ,"Protocol Warning", EXPFILL }}, + { &ei_lua_proto_reassemble_error, { "_ws.lua.proto.error", PI_REASSEMBLE, PI_ERROR ,"Protocol Error", EXPFILL }}, + + { &ei_lua_proto_malformed_comment, { "_ws.lua.proto.comment", PI_MALFORMED, PI_COMMENT ,"Protocol Comment", EXPFILL }}, + { &ei_lua_proto_malformed_chat, { "_ws.lua.proto.chat", PI_MALFORMED, PI_CHAT ,"Protocol Chat", EXPFILL }}, + { &ei_lua_proto_malformed_note, { "_ws.lua.proto.note", PI_MALFORMED, PI_NOTE ,"Protocol Note", EXPFILL }}, + { &ei_lua_proto_malformed_warn, { "_ws.lua.proto.warning", PI_MALFORMED, PI_WARN ,"Protocol Warning", EXPFILL }}, + { &ei_lua_proto_malformed_error, { "_ws.lua.proto.error", PI_MALFORMED, PI_ERROR ,"Protocol Error", EXPFILL }}, + + { &ei_lua_proto_debug_comment, { "_ws.lua.proto.comment", PI_DEBUG, PI_COMMENT ,"Protocol Comment", EXPFILL }}, + { &ei_lua_proto_debug_chat, { "_ws.lua.proto.chat", PI_DEBUG, PI_CHAT ,"Protocol Chat", EXPFILL }}, + { &ei_lua_proto_debug_note, { "_ws.lua.proto.note", PI_DEBUG, PI_NOTE ,"Protocol Note", EXPFILL }}, + { &ei_lua_proto_debug_warn, { "_ws.lua.proto.warning", PI_DEBUG, PI_WARN ,"Protocol Warning", EXPFILL }}, + { &ei_lua_proto_debug_error, { "_ws.lua.proto.error", PI_DEBUG, PI_ERROR ,"Protocol Error", EXPFILL }}, + + { &ei_lua_proto_protocol_comment, { "_ws.lua.proto.comment", PI_PROTOCOL, PI_COMMENT ,"Protocol Comment", EXPFILL }}, + { &ei_lua_proto_protocol_chat, { "_ws.lua.proto.chat", PI_PROTOCOL, PI_CHAT ,"Protocol Chat", EXPFILL }}, + { &ei_lua_proto_protocol_note, { "_ws.lua.proto.note", PI_PROTOCOL, PI_NOTE ,"Protocol Note", EXPFILL }}, + { &ei_lua_proto_protocol_warn, { "_ws.lua.proto.warning", PI_PROTOCOL, PI_WARN ,"Protocol Warning", EXPFILL }}, + { &ei_lua_proto_protocol_error, { "_ws.lua.proto.error", PI_PROTOCOL, PI_ERROR ,"Protocol Error", EXPFILL }}, + + { &ei_lua_proto_security_comment, { "_ws.lua.proto.comment", PI_SECURITY, PI_COMMENT ,"Protocol Comment", EXPFILL }}, + { &ei_lua_proto_security_chat, { "_ws.lua.proto.chat", PI_SECURITY, PI_CHAT ,"Protocol Chat", EXPFILL }}, + { &ei_lua_proto_security_note, { "_ws.lua.proto.note", PI_SECURITY, PI_NOTE ,"Protocol Note", EXPFILL }}, + { &ei_lua_proto_security_warn, { "_ws.lua.proto.warning", PI_SECURITY, PI_WARN ,"Protocol Warning", EXPFILL }}, + { &ei_lua_proto_security_error, { "_ws.lua.proto.error", PI_SECURITY, PI_ERROR ,"Protocol Error", EXPFILL }}, + + { &ei_lua_proto_comments_comment, { "_ws.lua.proto.comment", PI_COMMENTS_GROUP, PI_COMMENT ,"Protocol Comment", EXPFILL }}, + { &ei_lua_proto_comments_chat, { "_ws.lua.proto.chat", PI_COMMENTS_GROUP, PI_CHAT ,"Protocol Chat", EXPFILL }}, + { &ei_lua_proto_comments_note, { "_ws.lua.proto.note", PI_COMMENTS_GROUP, PI_NOTE ,"Protocol Note", EXPFILL }}, + { &ei_lua_proto_comments_warn, { "_ws.lua.proto.warning", PI_COMMENTS_GROUP, PI_WARN ,"Protocol Warning", EXPFILL }}, + { &ei_lua_proto_comments_error, { "_ws.lua.proto.error", PI_COMMENTS_GROUP, PI_ERROR ,"Protocol Error", EXPFILL }}, + + { &ei_lua_proto_decryption_comment, { "_ws.lua.proto.comment", PI_DECRYPTION, PI_COMMENT ,"Protocol Comment", EXPFILL }}, + { &ei_lua_proto_decryption_chat, { "_ws.lua.proto.chat", PI_DECRYPTION, PI_CHAT ,"Protocol Chat", EXPFILL }}, + { &ei_lua_proto_decryption_note, { "_ws.lua.proto.note", PI_DECRYPTION, PI_NOTE ,"Protocol Note", EXPFILL }}, + { &ei_lua_proto_decryption_warn, { "_ws.lua.proto.warning", PI_DECRYPTION, PI_WARN ,"Protocol Warning", EXPFILL }}, + { &ei_lua_proto_decryption_error, { "_ws.lua.proto.error", PI_DECRYPTION, PI_ERROR ,"Protocol Error", EXPFILL }}, + + { &ei_lua_proto_assumption_comment, { "_ws.lua.proto.comment", PI_ASSUMPTION, PI_COMMENT ,"Protocol Comment", EXPFILL }}, + { &ei_lua_proto_assumption_chat, { "_ws.lua.proto.chat", PI_ASSUMPTION, PI_CHAT ,"Protocol Chat", EXPFILL }}, + { &ei_lua_proto_assumption_note, { "_ws.lua.proto.note", PI_ASSUMPTION, PI_NOTE ,"Protocol Note", EXPFILL }}, + { &ei_lua_proto_assumption_warn, { "_ws.lua.proto.warning", PI_ASSUMPTION, PI_WARN ,"Protocol Warning", EXPFILL }}, + { &ei_lua_proto_assumption_error, { "_ws.lua.proto.error", PI_ASSUMPTION, PI_ERROR ,"Protocol Error", EXPFILL }}, + + { &ei_lua_proto_deprecated_comment, { "_ws.lua.proto.comment", PI_DEPRECATED, PI_COMMENT ,"Protocol Comment", EXPFILL }}, + { &ei_lua_proto_deprecated_chat, { "_ws.lua.proto.chat", PI_DEPRECATED, PI_CHAT ,"Protocol Chat", EXPFILL }}, + { &ei_lua_proto_deprecated_note, { "_ws.lua.proto.note", PI_DEPRECATED, PI_NOTE ,"Protocol Note", EXPFILL }}, + { &ei_lua_proto_deprecated_warn, { "_ws.lua.proto.warning", PI_DEPRECATED, PI_WARN ,"Protocol Warning", EXPFILL }}, + { &ei_lua_proto_deprecated_error, { "_ws.lua.proto.error", PI_DEPRECATED, PI_ERROR ,"Protocol Error", EXPFILL }}, + + /* this one is for reporting errors executing Lua code */ + { &ei_lua_error, { "_ws.lua.error", PI_UNDECODED, PI_ERROR ,"Lua Error", EXPFILL }}, + }; + + if (first_time) { + ws_lua_ei = ei; + ws_lua_ei_len = array_length(ei); + } + + if (!L) { + L = lua_newstate(wslua_allocf, NULL); + } + + WSLUA_INIT(L); + +#if LUA_VERSION_NUM == 501 + /* table.unpack was introduced with Lua 5.2, alias it to unpack. */ + lua_getglobal(L, "table"); + lua_getglobal(L, "unpack"); + lua_setfield(L, -2, "unpack"); + lua_pop(L, 1); +#endif + + if (first_time) { + proto_lua = proto_register_protocol("Lua Dissection", "Lua Dissection", "_ws.lua"); + proto_register_field_array(proto_lua, hf, array_length(hf)); + proto_register_subtree_array(ett, array_length(ett)); + expert_lua = expert_register_protocol(proto_lua); + expert_register_field_array(expert_lua, ei, array_length(ei)); + } + + lua_atpanic(L,wslua_panic); + + /* + * The init_routines table (accessible by the user). + * + * For a table a, a.init is syntactic sugar for a["init"], and + * + * function t.a.b.c.f () body end + * + * is syntactic sugar for + * + * t.a.b.c.f = function () body end + * + * so + * + * function proto.init () body end + * + * means + * + * proto["init"] = function () body end + * + * and the Proto class has an "init" method, with Proto_set_init() + * being the setter for that method; that routine adds the Lua + * function passed to it as a Lua argument to the WSLUA_INIT_ROUTINES + * table - i.e., "init_routines". + */ + lua_newtable (L); + lua_setglobal(L, WSLUA_INIT_ROUTINES); + + /* the dissectors table goes in the registry (not accessible) */ + lua_newtable (L); + lua_dissectors_table_ref = luaL_ref(L, LUA_REGISTRYINDEX); + lua_newtable (L); + lua_heur_dissectors_table_ref = luaL_ref(L, LUA_REGISTRYINDEX); + + /* the preferences apply_cb table (accessible by the user) */ + lua_newtable (L); + lua_setglobal(L, WSLUA_PREFS_CHANGED); + + /* set running_superuser variable to its proper value */ + WSLUA_REG_GLOBAL_BOOL(L,"running_superuser",started_with_special_privs()); + + /* special constant used by PDU reassembly handling */ + /* see dissect_lua() for notes */ + WSLUA_REG_GLOBAL_NUMBER(L,"DESEGMENT_ONE_MORE_SEGMENT",DESEGMENT_ONE_MORE_SEGMENT); + + /* the possible values for Pinfo's p2p_dir attribute */ + WSLUA_REG_GLOBAL_NUMBER(L,"P2P_DIR_UNKNOWN",-1); + WSLUA_REG_GLOBAL_NUMBER(L,"P2P_DIR_SENT",0); + WSLUA_REG_GLOBAL_NUMBER(L,"P2P_DIR_RECV",1); + + wslua_add_introspection(); + + wslua_add_useful_constants(); + + wslua_add_deprecated(); + + // Register Lua's console menu (in the GUI) + if (first_time) { + funnel_register_console_menu("Lua", + lua_funnel_console_eval, + lua_funnel_console_open, + lua_funnel_console_close, + NULL, NULL); + } + else if (wslua_gui_print_func_ptr) { + // If we we have an open GUI console dialog re-register the global "print to console" function + lua_funnel_console_open(wslua_gui_print_func_ptr, wslua_gui_print_data_ptr, NULL); + } + + /* load system's init.lua */ + filename = g_build_filename(get_plugins_dir(), "init.lua", (char *)NULL); + if (file_exists(filename)) { + ws_debug("Loading init.lua file: %s", filename); + lua_load_internal_script(filename); + } + g_free(filename); + + /* load user's init.lua */ + /* if we are indeed superuser run user scripts only if told to do so */ + if (!started_with_special_privs() || run_anyway) { + filename = g_build_filename(get_plugins_pers_dir(), "init.lua", (char *)NULL); + if (file_exists(filename)) { + ws_debug("Loading init.lua file: %s", filename); + lua_load_internal_script(filename); + } + g_free(filename); + + /* For backward compatibility also load it from the configuration directory. */ + filename = get_persconffile_path("init.lua", FALSE); + if (file_exists(filename)) { + ws_message("Loading init.lua file from deprecated path: %s", filename); + lua_load_internal_script(filename); + } + g_free(filename); + } + + filename = NULL; + + /* check if lua is to be disabled */ + lua_getglobal(L, "disable_lua"); // 2.6 and earlier, deprecated + if (lua_isboolean(L,-1)) { + enable_lua = ! lua_toboolean(L,-1); + } + lua_pop(L,1); /* pop the getglobal result */ + + lua_getglobal(L, "enable_lua"); // 3.0 and later + if (lua_isboolean(L,-1)) { + enable_lua = lua_toboolean(L,-1); + } + lua_pop(L,1); /* pop the getglobal result */ + + if (!enable_lua) { + /* disable lua */ + lua_close(L); + L = NULL; + first_time = FALSE; + return; + } + + /* load global scripts */ + lua_load_global_plugins(cb, client_data, FALSE); + + /* check whether we should run other scripts even if running superuser */ + lua_getglobal(L,"run_user_scripts_when_superuser"); + + if (lua_isboolean(L,-1) && lua_toboolean(L,-1)) { + run_anyway = TRUE; + } + lua_pop(L,1); /* pop the getglobal result */ + + /* if we are indeed superuser run user scripts only if told to do so */ + if (!started_with_special_privs() || run_anyway) { + + /* load user scripts */ + lua_load_pers_plugins(cb, client_data, FALSE); + + /* load scripts from command line */ + for (i = 0; i < ex_opt_count("lua_script"); i++) { + const gchar *script_filename = ex_opt_get_nth("lua_script", i); + char* dirname = g_strdup(script_filename); + char* dname = get_dirname(dirname); + + if (cb) + (*cb)(RA_LUA_PLUGINS, get_basename(script_filename), client_data); + + lua_load_plugin_script(ws_dir_get_name(script_filename), + script_filename, + dname ? dname : "", + file_count); + file_count++; + g_free(dirname); + } + } + + if (first_time) { + /* at this point we're set up so register the init and cleanup routines */ + register_init_routine(wslua_init_routine); + register_cleanup_routine(wslua_cleanup_routine); + } + + /* + * after this point it is too late to register a menu + * disable the function to avoid weirdness + */ + lua_pushcfunction(L, wslua_not_register_menu); + lua_setglobal(L, "register_menu"); + + /* set up some essential globals */ + lua_pinfo = NULL; + lua_tree = NULL; + lua_tvb = NULL; + + Proto_commit(L); + + first_time = FALSE; +} + +void wslua_early_cleanup(void) { + wslua_deregister_protocols(L); +} + +void wslua_reload_plugins (register_cb cb, gpointer client_data) { + const funnel_ops_t* ops = funnel_get_funnel_ops(); + + if (cb) + (*cb)(RA_LUA_DEREGISTER, NULL, client_data); + + if (ops->close_dialogs) + ops->close_dialogs(); + + wslua_deregister_heur_dissectors(L); + wslua_deregister_protocols(L); + wslua_deregister_dissector_tables(L); + wslua_deregister_listeners(L); + wslua_deregister_fields(L); + wslua_deregister_filehandlers(L); + wslua_deregister_menus(); + wslua_clear_plugin_list(); + + wslua_cleanup(); + wslua_init(cb, client_data); /* reinitialize */ +} + +void wslua_cleanup(void) { + /* cleanup lua */ + if (L) { + lua_close(L); + L = NULL; + } + init_routine_initialized = FALSE; +} + +lua_State* wslua_state(void) { return L; } + +/* + * 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: + */ diff --git a/epan/wslua/init_wslua.h b/epan/wslua/init_wslua.h new file mode 100644 index 00000000..5063076d --- /dev/null +++ b/epan/wslua/init_wslua.h @@ -0,0 +1,33 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs <gerald@wireshark.org> + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef __INIT_WSLUA_H__ +#define __INIT_WSLUA_H__ + +#include "ws_symbol_export.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +WS_DLL_PUBLIC int wslua_count_plugins(void); +WS_DLL_PUBLIC void wslua_reload_plugins (register_cb cb, gpointer client_data); + +typedef void (*wslua_plugin_description_callback)(const char *, const char *, + const char *, const char *, + void *); +WS_DLL_PUBLIC void wslua_plugins_get_descriptions(wslua_plugin_description_callback callback, void *user_data); +WS_DLL_PUBLIC void wslua_plugins_dump_all(void); +WS_DLL_PUBLIC const char *wslua_plugin_type_name(void); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* __INIT_WSLUA_H__ */ diff --git a/epan/wslua/lrexlib/CMakeLists.txt b/epan/wslua/lrexlib/CMakeLists.txt new file mode 100644 index 00000000..2f7231ef --- /dev/null +++ b/epan/wslua/lrexlib/CMakeLists.txt @@ -0,0 +1,30 @@ + +set(REX_SRC + common.c + pcre2/lpcre2.c + pcre2/lpcre2_f.c +) + +add_library(lrexlib STATIC ${REX_SRC}) + +target_link_libraries(lrexlib PRIVATE + ${LUA_LIBRARIES} + $<IF:$<CONFIG:Debug>,${PCRE2_DEBUG_LIBRARIES},${PCRE2_LIBRARIES}> +) + +target_include_directories(lrexlib SYSTEM PRIVATE + ${CMAKE_SOURCE_DIR}/epan + ${LUA_INCLUDE_DIRS} + ${PCRE2_INCLUDE_DIRS} +) + +add_dependencies(lrexlib register_wslua) + +if(FETCH_lua) + add_dependencies(lrexlib lua52) +endif() + +add_compile_definitions( + VERSION=\"2.9.1\" + PCRE2_CODE_UNIT_WIDTH=8 +) diff --git a/epan/wslua/lrexlib/README.rst b/epan/wslua/lrexlib/README.rst new file mode 100644 index 00000000..b9679d35 --- /dev/null +++ b/epan/wslua/lrexlib/README.rst @@ -0,0 +1,56 @@ +Lrexlib +======= + +| by Reuben Thomas (rrt@sc3d.org) +| and Shmuel Zeigerman (shmuz@013net.net) + +**Lrexlib** provides bindings of five regular expression library APIs +(POSIX_, PCRE_, PCRE2_, GNU_, TRE_ and Oniguruma_) to Lua_ >= 5.1. +The bindings for TRE and Oniguruma are not currently complete. + +**Lrexlib** is copyright Reuben Thomas 2000-2020 and copyright Shmuel +Zeigerman 2004-2020, and is released under the same license as Lua, +the MIT_ license (otherwise known as the revised BSD license). There +is no warranty. + +.. _POSIX: http://www.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap09.html +.. _PCRE: http://www.pcre.org/pcre.txt +.. _PCRE2: http://www.pcre.org/pcre2.txt +.. _GNU: ftp://ftp.gnu.org/old-gnu/regex/ +.. _Oniguruma: https://github.com/kkos/oniguruma +.. _TRE: http://laurikari.net/tre/documentation/ +.. _Lua: http://www.lua.org +.. _MIT: http://www.opensource.org/licenses/mit-license.php + +Please report bugs and make suggestions to the maintainer, or use the +LuaForge trackers and mailing lists. + +Thanks to Thatcher Ulrich for bug and warning fixes, and to Nick +Gammon for adding support for PCRE named subpatterns. + +----------------------------------------------------------- + +Installation +------------ + +Lrexlib is installed with LuaRocks_, using the command:: + + luarocks install lrexlib-FLAVOUR + +where **FLAVOUR** is one of PCRE, PCRE2, POSIX, oniguruma, TRE, GNU + +.. _LuaRocks: http://www.luarocks.org + + +Links +----- + +- License_ +- `Reference Manual`_ +- `LuaForge Project Page`_ +- Download_ + +.. _License: http://rrthomas.github.com/lrexlib/license.html +.. _Reference Manual: http://rrthomas.github.com/lrexlib/manual.html +.. _LuaForge Project Page: http://luaforge.net/projects/lrexlib/ +.. _Download: https://github.com/rrthomas/lrexlib/downloads diff --git a/epan/wslua/lrexlib/algo.h b/epan/wslua/lrexlib/algo.h new file mode 100644 index 00000000..2e7a38ab --- /dev/null +++ b/epan/wslua/lrexlib/algo.h @@ -0,0 +1,779 @@ +/* algo.h */ +/* + * Copyright (C) Reuben Thomas 2000-2020 + * Copyright (C) Shmuel Zeigerman 2004-2020 + + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the + * Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, + * sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the + * Software. + + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY + * KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR + * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS + * OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "common.h" + +#define REX_VERSION "Lrexlib " VERSION + +/* Forward declarations */ +static void gmatch_pushsubject (lua_State *L, TArgExec *argE); +static int findmatch_exec (TUserdata *ud, TArgExec *argE); +static int split_exec (TUserdata *ud, TArgExec *argE, int offset); +static int gsub_exec (TUserdata *ud, TArgExec *argE, int offset); +static int gmatch_exec (TUserdata *ud, TArgExec *argE); +static int compile_regex (lua_State *L, const TArgComp *argC, TUserdata **pud); +static int generate_error (lua_State *L, const TUserdata *ud, int errcode); + +#if LUA_VERSION_NUM == 501 +# define ALG_ENVIRONINDEX LUA_ENVIRONINDEX +#else +# define ALG_ENVIRONINDEX lua_upvalueindex(1) +#endif + +#ifndef ALG_CHARSIZE +# define ALG_CHARSIZE 1 +#endif + +#ifndef BUFFERZ_PUTREPSTRING +# define BUFFERZ_PUTREPSTRING bufferZ_putrepstring +#endif + +#ifndef ALG_GETCARGS +# define ALG_GETCARGS(a,b,c) +#endif + +#ifndef DO_NAMED_SUBPATTERNS +#define DO_NAMED_SUBPATTERNS(a,b,c) +#endif + +#define METHOD_FIND 0 +#define METHOD_MATCH 1 +#define METHOD_EXEC 2 +#define METHOD_TFIND 3 + + +static int OptLimit (lua_State *L, int pos) { + if (lua_isnoneornil (L, pos)) + return GSUB_UNLIMITED; + if (lua_isfunction (L, pos)) + return GSUB_CONDITIONAL; + if (lua_isnumber (L, pos)) { + int a = lua_tointeger (L, pos); + return a < 0 ? 0 : a; + } + return luaL_typerror (L, pos, "number or function"); +} + + +static int get_startoffset(lua_State *L, int stackpos, size_t len) { + int startoffset = (int)luaL_optinteger(L, stackpos, 1); + if(startoffset > 0) + startoffset--; + else if(startoffset < 0) { + startoffset += len/ALG_CHARSIZE; + if(startoffset < 0) + startoffset = 0; + } + return startoffset*ALG_CHARSIZE; +} + + +static TUserdata* test_ud (lua_State *L, int pos) +{ + TUserdata *ud; + if (lua_getmetatable(L, pos) && + lua_rawequal(L, -1, ALG_ENVIRONINDEX) && + (ud = (TUserdata *)lua_touserdata(L, pos)) != NULL) { + lua_pop(L, 1); + return ud; + } + return NULL; +} + + +static TUserdata* check_ud (lua_State *L) +{ + TUserdata *ud = test_ud(L, 1); + if (ud == NULL) luaL_typerror(L, 1, REX_TYPENAME); + return ud; +} + + +static void check_subject (lua_State *L, int pos, TArgExec *argE) +{ + int stype; + argE->text = lua_tolstring (L, pos, &argE->textlen); + stype = lua_type (L, pos); + if (stype != LUA_TSTRING && stype != LUA_TTABLE && stype != LUA_TUSERDATA) { + luaL_typerror (L, pos, "string, table or userdata"); + } else if (argE->text == NULL) { + int type; + lua_getfield (L, pos, "topointer"); + if (lua_type (L, -1) != LUA_TFUNCTION) + luaL_error (L, "subject has no topointer method"); + lua_pushvalue (L, pos); + lua_call (L, 1, 1); + type = lua_type (L, -1); + if (type != LUA_TLIGHTUSERDATA) + luaL_error (L, "subject's topointer method returned %s (expected lightuserdata)", + lua_typename (L, type)); + argE->text = (const char*) lua_touserdata (L, -1); + lua_pop (L, 1); +#if LUA_VERSION_NUM == 501 + if (luaL_callmeta (L, pos, "__len")) { + if (lua_type (L, -1) != LUA_TNUMBER) + luaL_argerror (L, pos, "subject's length is not a number"); + argE->textlen = lua_tointeger (L, -1); + lua_pop (L, 1); + } + else + argE->textlen = lua_objlen (L, pos); +#else + argE->textlen = luaL_len (L, pos); +#endif + } +} + +static void check_pattern (lua_State *L, int pos, TArgComp *argC) +{ + if (lua_isstring (L, pos)) { + argC->pattern = lua_tolstring (L, pos, &argC->patlen); + argC->ud = NULL; + } + else if ((argC->ud = test_ud (L, pos)) == NULL) + luaL_typerror(L, pos, "string or " REX_TYPENAME); +} + +static void checkarg_new (lua_State *L, TArgComp *argC) { + argC->pattern = luaL_checklstring (L, 1, &argC->patlen); + argC->cflags = ALG_GETCFLAGS (L, 2); + ALG_GETCARGS (L, 3, argC); +} + + +/* function gsub (s, patt, f, [n], [cf], [ef], [larg...]) */ +static void checkarg_gsub (lua_State *L, TArgComp *argC, TArgExec *argE) { + check_subject (L, 1, argE); + check_pattern (L, 2, argC); + lua_tostring (L, 3); /* converts number (if any) to string */ + argE->reptype = lua_type (L, 3); + if (argE->reptype != LUA_TSTRING && argE->reptype != LUA_TTABLE && + argE->reptype != LUA_TFUNCTION) { + luaL_typerror (L, 3, "string, table or function"); + } + argE->funcpos = 3; + argE->funcpos2 = 4; + argE->maxmatch = OptLimit (L, 4); + argC->cflags = ALG_GETCFLAGS (L, 5); + argE->eflags = (int)luaL_optinteger (L, 6, ALG_EFLAGS_DFLT); + ALG_GETCARGS (L, 7, argC); +} + + +/* function count (s, patt, [cf], [ef], [larg...]) */ +static void checkarg_count (lua_State *L, TArgComp *argC, TArgExec *argE) { + check_subject (L, 1, argE); + check_pattern (L, 2, argC); + argC->cflags = ALG_GETCFLAGS (L, 3); + argE->eflags = (int)luaL_optinteger (L, 4, ALG_EFLAGS_DFLT); + ALG_GETCARGS (L, 5, argC); +} + + +/* function find (s, patt, [st], [cf], [ef], [larg...]) */ +/* function match (s, patt, [st], [cf], [ef], [larg...]) */ +static void checkarg_find_func (lua_State *L, TArgComp *argC, TArgExec *argE) { + check_subject (L, 1, argE); + check_pattern (L, 2, argC); + argE->startoffset = get_startoffset (L, 3, argE->textlen); + argC->cflags = ALG_GETCFLAGS (L, 4); + argE->eflags = (int)luaL_optinteger (L, 5, ALG_EFLAGS_DFLT); + ALG_GETCARGS (L, 6, argC); +} + + +/* function gmatch (s, patt, [cf], [ef], [larg...]) */ +/* function split (s, patt, [cf], [ef], [larg...]) */ +static void checkarg_gmatch_split (lua_State *L, TArgComp *argC, TArgExec *argE) { + check_subject (L, 1, argE); + check_pattern (L, 2, argC); + argC->cflags = ALG_GETCFLAGS (L, 3); + argE->eflags = (int)luaL_optinteger (L, 4, ALG_EFLAGS_DFLT); + ALG_GETCARGS (L, 5, argC); +} + + +/* method r:tfind (s, [st], [ef]) */ +/* method r:exec (s, [st], [ef]) */ +/* method r:find (s, [st], [ef]) */ +/* method r:match (s, [st], [ef]) */ +static void checkarg_find_method (lua_State *L, TArgExec *argE, TUserdata **ud) { + *ud = check_ud (L); + check_subject (L, 2, argE); + argE->startoffset = get_startoffset (L, 3, argE->textlen); + argE->eflags = (int)luaL_optinteger (L, 4, ALG_EFLAGS_DFLT); +} + + +static int algf_new (lua_State *L) { + TArgComp argC; + checkarg_new (L, &argC); + return compile_regex (L, &argC, NULL); +} + +static void push_substrings (lua_State *L, TUserdata *ud, const char *text, + TFreeList *freelist) { + int i; + if (lua_checkstack (L, ALG_NSUB(ud)) == 0) { + if (freelist) + freelist_free (freelist); + luaL_error (L, "cannot add %d stack slots", ALG_NSUB(ud)); + } + for (i = 1; i <= ALG_NSUB(ud); i++) { + ALG_PUSHSUB_OR_FALSE (L, ud, text, i); + } +} + +static int algf_gsub (lua_State *L) { + TUserdata *ud; + TArgComp argC; + TArgExec argE; + int n_match = 0, n_subst = 0, st = 0, last_to = -1; + TBuffer BufOut, BufRep, BufTemp, *pBuf = &BufOut; + TFreeList freelist; + /*------------------------------------------------------------------*/ + checkarg_gsub (L, &argC, &argE); + if (argC.ud) { + ud = (TUserdata*) argC.ud; + lua_pushvalue (L, 2); + } + else compile_regex (L, &argC, &ud); + freelist_init (&freelist); + /*------------------------------------------------------------------*/ + if (argE.reptype == LUA_TSTRING) { + buffer_init (&BufRep, 256, L, &freelist); + BUFFERZ_PUTREPSTRING (&BufRep, argE.funcpos, ALG_NSUB(ud)); + } + /*------------------------------------------------------------------*/ + if (argE.maxmatch == GSUB_CONDITIONAL) { + buffer_init (&BufTemp, 1024, L, &freelist); + pBuf = &BufTemp; + } + /*------------------------------------------------------------------*/ + buffer_init (&BufOut, 1024, L, &freelist); + while ((argE.maxmatch < 0 || n_match < argE.maxmatch) && st <= (int)argE.textlen) { + int from, to, res; + int curr_subst = 0; + res = gsub_exec (ud, &argE, st); + if (ALG_NOMATCH (res)) { + break; + } + else if (!ALG_ISMATCH (res)) { + freelist_free (&freelist); + return generate_error (L, ud, res); + } + from = ALG_BASE(st) + ALG_SUBBEG(ud,0); + to = ALG_BASE(st) + ALG_SUBEND(ud,0); + if (to == last_to) { /* discard an empty match adjacent to the previous match */ + if (st < (int)argE.textlen) { /* advance by 1 char (not replaced) */ + buffer_addlstring (&BufOut, argE.text + st, ALG_CHARSIZE); + st += ALG_CHARSIZE; + continue; + } + break; + } + last_to = to; + ++n_match; + if (st < from) { + buffer_addlstring (&BufOut, argE.text + st, from - st); +#ifdef ALG_PULL + st = from; +#endif + } + /*----------------------------------------------------------------*/ + if (argE.reptype == LUA_TSTRING) { + size_t iter = 0, num; + const char *str; + while (bufferZ_next (&BufRep, &iter, &num, &str)) { + if (str) + buffer_addlstring (pBuf, str, num); + else if (num == 0 || ALG_SUBVALID (ud,num)) + buffer_addlstring (pBuf, argE.text + ALG_BASE(st) + ALG_SUBBEG(ud,num), ALG_SUBLEN(ud,num)); + } + curr_subst = 1; + } + /*----------------------------------------------------------------*/ + else if (argE.reptype == LUA_TTABLE) { + if (ALG_NSUB(ud) > 0) + ALG_PUSHSUB_OR_FALSE (L, ud, argE.text + ALG_BASE(st), 1); + else + lua_pushlstring (L, argE.text + from, to - from); + lua_gettable (L, argE.funcpos); + } + /*----------------------------------------------------------------*/ + else if (argE.reptype == LUA_TFUNCTION) { + int narg; + lua_pushvalue (L, argE.funcpos); + if (ALG_NSUB(ud) > 0) { + push_substrings (L, ud, argE.text + ALG_BASE(st), &freelist); + narg = ALG_NSUB(ud); + } + else { + lua_pushlstring (L, argE.text + from, to - from); + narg = 1; + } + if (0 != lua_pcall (L, narg, 1, 0)) { + freelist_free (&freelist); + return lua_error (L); /* re-raise the error */ + } + } + /*----------------------------------------------------------------*/ + if (argE.reptype == LUA_TTABLE || argE.reptype == LUA_TFUNCTION) { + if (lua_tostring (L, -1)) { + buffer_addvalue (pBuf, -1); + curr_subst = 1; + } + else if (!lua_toboolean (L, -1)) + buffer_addlstring (pBuf, argE.text + from, to - from); + else { + freelist_free (&freelist); + luaL_error (L, "invalid replacement value (a %s)", luaL_typename (L, -1)); + } + if (argE.maxmatch != GSUB_CONDITIONAL) + lua_pop (L, 1); + } + /*----------------------------------------------------------------*/ + if (argE.maxmatch == GSUB_CONDITIONAL) { + /* Call the function */ + lua_pushvalue (L, argE.funcpos2); + lua_pushinteger (L, from/ALG_CHARSIZE + 1); + lua_pushinteger (L, to/ALG_CHARSIZE); + if (argE.reptype == LUA_TSTRING) + buffer_pushresult (&BufTemp); + else { + lua_pushvalue (L, -4); + lua_remove (L, -5); + } + if (0 != lua_pcall (L, 3, 2, 0)) { + freelist_free (&freelist); + lua_error (L); /* re-raise the error */ + } + /* Handle the 1-st return value */ + if (lua_isstring (L, -2)) { /* coercion is allowed here */ + buffer_addvalue (&BufOut, -2); /* rep2 */ + curr_subst = 1; + } + else if (lua_toboolean (L, -2)) + buffer_addbuffer (&BufOut, &BufTemp); /* rep1 */ + else { + buffer_addlstring (&BufOut, argE.text + from, to - from); /* "no" */ + curr_subst = 0; + } + /* Handle the 2-nd return value */ + if (lua_type (L, -1) == LUA_TNUMBER) { /* no coercion is allowed here */ + int n = lua_tointeger (L, -1); + if (n < 0) /* n */ + n = 0; + argE.maxmatch = n_match + n; + } + else if (lua_toboolean (L, -1)) /* "yes to all" */ + argE.maxmatch = GSUB_UNLIMITED; + else + buffer_clear (&BufTemp); + + lua_pop (L, 2); + if (argE.maxmatch != GSUB_CONDITIONAL) + pBuf = &BufOut; + } + /*----------------------------------------------------------------*/ + n_subst += curr_subst; + if (st < to) { + st = to; + } + else if (st < (int)argE.textlen) { + /* advance by 1 char (not replaced) */ + buffer_addlstring (&BufOut, argE.text + st, ALG_CHARSIZE); + st += ALG_CHARSIZE; + } + else break; + } + /*------------------------------------------------------------------*/ + buffer_addlstring (&BufOut, argE.text + st, argE.textlen - st); + buffer_pushresult (&BufOut); + lua_pushinteger (L, n_match); + lua_pushinteger (L, n_subst); + freelist_free (&freelist); + return 3; +} + + +static int algf_count (lua_State *L) { + TUserdata *ud; + TArgComp argC; + TArgExec argE; + int n_match = 0, st = 0, last_to = -1; + /*------------------------------------------------------------------*/ + checkarg_count (L, &argC, &argE); + if (argC.ud) { + ud = (TUserdata*) argC.ud; + lua_pushvalue (L, 2); + } + else compile_regex (L, &argC, &ud); + /*------------------------------------------------------------------*/ + while (st <= (int)argE.textlen) { + int to, res; + res = gsub_exec (ud, &argE, st); + if (ALG_NOMATCH (res)) { + break; + } + else if (!ALG_ISMATCH (res)) { + return generate_error (L, ud, res); + } + to = ALG_BASE(st) + ALG_SUBEND(ud,0); + if (to == last_to) { /* discard an empty match adjacent to the previous match */ + if (st < (int)argE.textlen) { /* advance by 1 char */ + st += ALG_CHARSIZE; + continue; + } + break; + } + last_to = to; + ++n_match; +#ifdef ALG_PULL + { + int from = ALG_BASE(st) + ALG_SUBBEG(ud,0); + if (st < from) + st = from; + } +#endif + /*----------------------------------------------------------------*/ + if (st < to) { + st = to; + } + else if (st < (int)argE.textlen) { + /* advance by 1 char (not replaced) */ + st += ALG_CHARSIZE; + } + else break; + } + /*------------------------------------------------------------------*/ + lua_pushinteger (L, n_match); + return 1; +} + + +static int finish_generic_find (lua_State *L, TUserdata *ud, TArgExec *argE, + int method, int res) +{ + if (ALG_ISMATCH (res)) { + if (method == METHOD_FIND) + ALG_PUSHOFFSETS (L, ud, ALG_BASE(argE->startoffset), 0); + if (ALG_NSUB(ud)) /* push captures */ + push_substrings (L, ud, argE->text, NULL); + else if (method != METHOD_FIND) { + ALG_PUSHSUB (L, ud, argE->text, 0); + return 1; + } + return (method == METHOD_FIND) ? ALG_NSUB(ud) + 2 : ALG_NSUB(ud); + } + else if (ALG_NOMATCH (res)) + return lua_pushnil (L), 1; + else + return generate_error (L, ud, res); +} + + +static int generic_find_func (lua_State *L, int method) { + TUserdata *ud; + TArgComp argC; + TArgExec argE; + int res; + + checkarg_find_func (L, &argC, &argE); + if (argE.startoffset > (int)argE.textlen) + return lua_pushnil (L), 1; + + if (argC.ud) { + ud = (TUserdata*) argC.ud; + lua_pushvalue (L, 2); + } + else compile_regex (L, &argC, &ud); + res = findmatch_exec (ud, &argE); + return finish_generic_find (L, ud, &argE, method, res); +} + + +static int algf_find (lua_State *L) { + return generic_find_func (L, METHOD_FIND); +} + + +static int algf_match (lua_State *L) { + return generic_find_func (L, METHOD_MATCH); +} + + +static int gmatch_iter (lua_State *L) { + int last_end, res; + TArgExec argE; + TUserdata *ud = (TUserdata*) lua_touserdata (L, lua_upvalueindex (1)); + argE.text = lua_tolstring (L, lua_upvalueindex (2), &argE.textlen); + argE.eflags = lua_tointeger (L, lua_upvalueindex (3)); + argE.startoffset = lua_tointeger (L, lua_upvalueindex (4)); + last_end = lua_tointeger (L, lua_upvalueindex (5)); + + while (1) { + if (argE.startoffset > (int)argE.textlen) + return 0; + res = gmatch_exec (ud, &argE); + if (ALG_ISMATCH (res)) { + int incr = 0; + if (!ALG_SUBLEN(ud,0)) { /* no progress: prevent endless loop */ + if (last_end == ALG_BASE(argE.startoffset) + ALG_SUBEND(ud,0)) { + argE.startoffset += ALG_CHARSIZE; + continue; + } + incr = ALG_CHARSIZE; + } + last_end = ALG_BASE(argE.startoffset) + ALG_SUBEND(ud,0); + lua_pushinteger(L, last_end + incr); /* update start offset */ + lua_replace (L, lua_upvalueindex (4)); + lua_pushinteger(L, last_end); /* update last end of match */ + lua_replace (L, lua_upvalueindex (5)); + /* push either captures or entire match */ + if (ALG_NSUB(ud)) { + push_substrings (L, ud, argE.text, NULL); + return ALG_NSUB(ud); + } + else { + ALG_PUSHSUB (L, ud, argE.text, 0); + return 1; + } + } + else if (ALG_NOMATCH (res)) + return 0; + else + return generate_error (L, ud, res); + } +} + + +static int split_iter (lua_State *L) { + int incr, last_end, newoffset, res; + TArgExec argE; + TUserdata *ud = (TUserdata*) lua_touserdata (L, lua_upvalueindex (1)); + argE.text = lua_tolstring (L, lua_upvalueindex (2), &argE.textlen); + argE.eflags = lua_tointeger (L, lua_upvalueindex (3)); + argE.startoffset = lua_tointeger (L, lua_upvalueindex (4)); + incr = lua_tointeger (L, lua_upvalueindex (5)); + last_end = lua_tointeger (L, lua_upvalueindex (6)); + + if (incr < 0) + return 0; + + while (1) { + if ((newoffset = argE.startoffset + incr) > (int)argE.textlen) + break; + res = split_exec (ud, &argE, newoffset); + if (ALG_ISMATCH (res)) { + if (!ALG_SUBLEN(ud,0)) { /* no progress: prevent endless loop */ + if (last_end == ALG_BASE(argE.startoffset) + ALG_SUBEND(ud,0)) { + incr += ALG_CHARSIZE; + continue; + } + } + lua_pushinteger(L, ALG_BASE(newoffset) + ALG_SUBEND(ud,0)); /* update start offset and last_end */ + lua_pushvalue (L, -1); + lua_replace (L, lua_upvalueindex (4)); + lua_replace (L, lua_upvalueindex (6)); + lua_pushinteger (L, ALG_SUBLEN(ud,0) ? 0 : ALG_CHARSIZE); /* update incr */ + lua_replace (L, lua_upvalueindex (5)); + /* push text preceding the match */ + lua_pushlstring (L, argE.text + argE.startoffset, + ALG_SUBBEG(ud,0) + ALG_BASE(newoffset) - argE.startoffset); + /* push either captures or entire match */ + if (ALG_NSUB(ud)) { + push_substrings (L, ud, argE.text + ALG_BASE(newoffset), NULL); + return 1 + ALG_NSUB(ud); + } + else { + ALG_PUSHSUB (L, ud, argE.text + ALG_BASE(newoffset), 0); + return 2; + } + } + else if (ALG_NOMATCH (res)) + break; + else + return generate_error (L, ud, res); + } + lua_pushinteger (L, -1); /* mark as last iteration */ + lua_replace (L, lua_upvalueindex (5)); /* incr = -1 */ + lua_pushlstring (L, argE.text+argE.startoffset, argE.textlen-argE.startoffset); + return 1; +} + + +static int algf_gmatch (lua_State *L) +{ + TArgComp argC; + TArgExec argE; + checkarg_gmatch_split (L, &argC, &argE); + if (argC.ud) + lua_pushvalue (L, 2); + else + compile_regex (L, &argC, NULL); /* 1-st upvalue: ud */ + gmatch_pushsubject (L, &argE); /* 2-nd upvalue: s */ + lua_pushinteger (L, argE.eflags); /* 3-rd upvalue: ef */ + lua_pushinteger (L, 0); /* 4-th upvalue: startoffset */ + lua_pushinteger (L, -1); /* 5-th upvalue: last end of match */ + lua_pushcclosure (L, gmatch_iter, 5); + return 1; +} + +static int algf_split (lua_State *L) +{ + TArgComp argC; + TArgExec argE; + checkarg_gmatch_split (L, &argC, &argE); + if (argC.ud) + lua_pushvalue (L, 2); + else + compile_regex (L, &argC, NULL); /* 1-st upvalue: ud */ + gmatch_pushsubject (L, &argE); /* 2-nd upvalue: s */ + lua_pushinteger (L, argE.eflags); /* 3-rd upvalue: ef */ + lua_pushinteger (L, 0); /* 4-th upvalue: startoffset */ + lua_pushinteger (L, 0); /* 5-th upvalue: incr */ + lua_pushinteger (L, -1); /* 6-th upvalue: last_end */ + lua_pushcclosure (L, split_iter, 6); + return 1; +} + + +static void push_substring_table (lua_State *L, TUserdata *ud, const char *text) { + int i; + lua_newtable (L); + for (i = 1; i <= ALG_NSUB(ud); i++) { + ALG_PUSHSUB_OR_FALSE (L, ud, text, i); + lua_rawseti (L, -2, i); + } +} + + +static void push_offset_table (lua_State *L, TUserdata *ud, int startoffset) { + int i, j; + lua_newtable (L); + for (i=1, j=1; i <= ALG_NSUB(ud); i++) { + if (ALG_SUBVALID (ud,i)) { + ALG_PUSHSTART (L, ud, startoffset, i); + lua_rawseti (L, -2, j++); + ALG_PUSHEND (L, ud, startoffset, i); + lua_rawseti (L, -2, j++); + } + else { + lua_pushboolean (L, 0); + lua_rawseti (L, -2, j++); + lua_pushboolean (L, 0); + lua_rawseti (L, -2, j++); + } + } +} + + +static int generic_find_method (lua_State *L, int method) { + TUserdata *ud; + TArgExec argE; + int res; + + checkarg_find_method (L, &argE, &ud); + if (argE.startoffset > (int)argE.textlen) + return lua_pushnil(L), 1; + + res = findmatch_exec (ud, &argE); + if (ALG_ISMATCH (res)) { + switch (method) { + case METHOD_EXEC: + ALG_PUSHOFFSETS (L, ud, ALG_BASE(argE.startoffset), 0); + push_offset_table (L, ud, ALG_BASE(argE.startoffset)); + DO_NAMED_SUBPATTERNS (L, ud, argE.text); + return 3; + case METHOD_TFIND: + ALG_PUSHOFFSETS (L, ud, ALG_BASE(argE.startoffset), 0); + push_substring_table (L, ud, argE.text); + DO_NAMED_SUBPATTERNS (L, ud, argE.text); + return 3; + case METHOD_MATCH: + case METHOD_FIND: + return finish_generic_find (L, ud, &argE, method, res); + } + return 0; + } + else if (ALG_NOMATCH (res)) + return lua_pushnil (L), 1; + else + return generate_error(L, ud, res); +} + + +static int algm_find (lua_State *L) { + return generic_find_method (L, METHOD_FIND); +} +static int algm_match (lua_State *L) { + return generic_find_method (L, METHOD_MATCH); +} +static int algm_tfind (lua_State *L) { + return generic_find_method (L, METHOD_TFIND); +} +static int algm_exec (lua_State *L) { + return generic_find_method (L, METHOD_EXEC); +} + +static void alg_register (lua_State *L, const luaL_Reg *r_methods, + const luaL_Reg *r_functions, const char *name) { + /* Create a new function environment to serve as a metatable for methods. */ +#if LUA_VERSION_NUM == 501 + lua_newtable (L); + lua_pushvalue (L, -1); + lua_replace (L, LUA_ENVIRONINDEX); + luaL_register (L, NULL, r_methods); +#else + luaL_newmetatable(L, REX_TYPENAME); + lua_pushvalue(L, -1); + luaL_setfuncs (L, r_methods, 1); +#endif + lua_pushvalue(L, -1); /* mt.__index = mt */ + lua_setfield(L, -2, "__index"); + + /* Register functions. */ + lua_createtable(L, 0, 8); +#if LUA_VERSION_NUM == 501 + luaL_register (L, NULL, r_functions); +#else + lua_pushvalue(L, -2); + luaL_setfuncs (L, r_functions, 1); +#endif +#ifdef REX_CREATEGLOBALVAR + lua_pushvalue(L, -1); + lua_setglobal(L, REX_LIBNAME); +#endif + lua_pushfstring (L, REX_VERSION" (for %s)", name); + lua_setfield (L, -2, "_VERSION"); +#ifndef REX_NOEMBEDDEDTEST + lua_pushcfunction (L, newmembuffer); + lua_setfield (L, -2, "_newmembuffer"); +#endif +} diff --git a/epan/wslua/lrexlib/common.c b/epan/wslua/lrexlib/common.c new file mode 100644 index 00000000..4d42de9c --- /dev/null +++ b/epan/wslua/lrexlib/common.c @@ -0,0 +1,331 @@ +/* common.c */ +/* + * Copyright (C) Reuben Thomas 2000-2020 + * Copyright (C) Shmuel Zeigerman 2004-2020 + + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the + * Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, + * sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the + * Software. + + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY + * KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR + * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS + * OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include <wireshark.h> +DIAG_OFF_CLANG(shorten-64-to-32) +#ifdef _MSC_VER +/* disable: " warning C4244: '=': conversion from 'lua _Integer' to 'int', + * possible loss of data" */ +#pragma warning(disable:4244) +/* warning C4267: '+=': conversion from 'size_t' to 'int', + * possible loss of data */ +#pragma warning(disable:4267) +#endif + +#include <stdlib.h> +#include <ctype.h> +#include <string.h> +#include "lua.h" +#include "lauxlib.h" +#include "common.h" + +#define N_ALIGN sizeof(int) + +/* the table must be on Lua stack top */ +int get_int_field (lua_State *L, const char* field) +{ + int val; + lua_getfield (L, -1, field); + val = lua_tointeger (L, -1); + lua_pop (L, 1); + return val; +} + +/* the table must be on Lua stack top */ +void set_int_field (lua_State *L, const char* field, int val) +{ + lua_pushinteger (L, val); + lua_setfield (L, -2, field); +} + +void *Lmalloc(lua_State *L, size_t size) { + void *ud; + lua_Alloc lalloc = lua_getallocf(L, &ud); + return lalloc(ud, NULL, 0, size); +} + +void *Lrealloc(lua_State *L, void *p, size_t osize, size_t nsize) { + void *ud; + lua_Alloc lalloc = lua_getallocf(L, &ud); + return lalloc(ud, p, osize, nsize); +} + +void Lfree(lua_State *L, void *p, size_t osize) { + void *ud; + lua_Alloc lalloc = lua_getallocf(L, &ud); + lalloc(ud, p, osize, 0); +} + +/* This function fills a table with string-number pairs. + The table can be passed as the 1-st lua-function parameter, + otherwise it is created. The return value is the filled table. +*/ +int get_flags (lua_State *L, const flag_pair **arrs) { + const flag_pair *p; + const flag_pair **pp; + int nparams = lua_gettop(L); + + if(nparams == 0) + lua_newtable(L); + else { + if(!lua_istable(L, 1)) + luaL_argerror(L, 1, "not a table"); + if(nparams > 1) + lua_pushvalue(L, 1); + } + + for(pp=arrs; *pp; ++pp) { + for(p=*pp; p->key; ++p) { + lua_pushstring(L, p->key); + lua_pushinteger(L, p->val); + lua_rawset(L, -3); + } + } + return 1; +} + +const char *get_flag_key (const flag_pair *fp, int val) { + for (; fp->key; ++fp) { + if (fp->val == val) + return fp->key; + } + return NULL; +} + +/* Classes */ + +/* + * class TFreeList + * *************** + * Simple array of pointers to TBuffer's. + * The array has fixed capacity (not expanded automatically). + */ + +void freelist_init (TFreeList *fl) { + fl->top = 0; +} + +void freelist_add (TFreeList *fl, TBuffer *buf) { + fl->list[fl->top++] = buf; +} + +void freelist_free (TFreeList *fl) { + while (fl->top > 0) + buffer_free (fl->list[--fl->top]); +} + +/* + * class TBuffer + * ************* + * Auto-extensible array of characters for building long strings incrementally. + * * Differs from luaL_Buffer in that: + * * its operations do not change Lua stack top position + * * buffer_addvalue does not extract the value from Lua stack + * * buffer_pushresult does not have to be the last operation + * * Uses TFreeList class: + * * for inserting itself into a TFreeList instance for future clean-up + * * calls freelist_free prior to calling luaL_error. + * * Has specialized "Z-operations" for maintaining mixed string/integer + * array: bufferZ_addlstring, bufferZ_addnum and bufferZ_next. + * * if the array is intended to be "mixed", then the methods + * buffer_addlstring and buffer_addvalue must not be used + * (the application will crash on bufferZ_next). + * * conversely, if the array is not intended to be "mixed", + * then the method bufferZ_next must not be used. + */ + +enum { ID_NUMBER, ID_STRING }; + +void buffer_init (TBuffer *buf, size_t sz, lua_State *L, TFreeList *fl) { + buf->arr = (char*) Lmalloc(L, sz); + if (!buf->arr) { + freelist_free (fl); + luaL_error (L, "malloc failed"); + } + buf->size = sz; + buf->top = 0; + buf->L = L; + buf->freelist = fl; + freelist_add (fl, buf); +} + +void buffer_free (TBuffer *buf) { + Lfree(buf->L, buf->arr, buf->size); +} + +void buffer_clear (TBuffer *buf) { + buf->top = 0; +} + +void buffer_pushresult (TBuffer *buf) { + lua_pushlstring (buf->L, buf->arr, buf->top); +} + +void buffer_addbuffer (TBuffer *trg, TBuffer *src) { + buffer_addlstring (trg, src->arr, src->top); +} + +void buffer_addlstring (TBuffer *buf, const void *src, size_t sz) { + size_t newtop = buf->top + sz; + if (newtop > buf->size) { + char *p = (char*) Lrealloc (buf->L, buf->arr, buf->size, 2 * newtop); /* 2x expansion */ + if (!p) { + freelist_free (buf->freelist); + luaL_error (buf->L, "realloc failed"); + } + buf->arr = p; + buf->size = 2 * newtop; + } + if (src) + memcpy (buf->arr + buf->top, src, sz); + buf->top = newtop; +} + +void buffer_addvalue (TBuffer *buf, int stackpos) { + size_t len; + const char *p = lua_tolstring (buf->L, stackpos, &len); + buffer_addlstring (buf, p, len); +} + +void bufferZ_addlstring (TBuffer *buf, const void *src, size_t len) { + int n; + size_t header[2] = { ID_STRING }; + header[1] = len; + buffer_addlstring (buf, header, sizeof (header)); + buffer_addlstring (buf, src, len); + n = len % N_ALIGN; + if (n) buffer_addlstring (buf, NULL, N_ALIGN - n); +} + +void bufferZ_addnum (TBuffer *buf, size_t num) { + size_t header[2] = { ID_NUMBER }; + header[1] = num; + buffer_addlstring (buf, header, sizeof (header)); +} + +/* 1. When called repeatedly on the same TBuffer, its existing data + is discarded and overwritten by the new data. + 2. The TBuffer's array is never shrunk by this function. +*/ +void bufferZ_putrepstring (TBuffer *BufRep, int reppos, int nsub) { + char dbuf[] = { 0, 0 }; + size_t replen; + const char *p = lua_tolstring (BufRep->L, reppos, &replen); + const char *end = p + replen; + BufRep->top = 0; + while (p < end) { + const char *q; + for (q = p; q < end && *q != '%'; ++q) + {} + if (q != p) + bufferZ_addlstring (BufRep, p, q - p); + if (q < end) { + if (++q < end) { /* skip % */ + if (g_ascii_isdigit (*q)) { + int num; + *dbuf = *q; + num = strtol (dbuf, NULL, 10); + if (num == 1 && nsub == 0) + num = 0; + else if (num > nsub) { + freelist_free (BufRep->freelist); + luaL_error (BufRep->L, "invalid capture index"); + } + bufferZ_addnum (BufRep, num); + } + else bufferZ_addlstring (BufRep, q, 1); + } + p = q + 1; + } + else break; + } +} + +/****************************************************************************** + The intended use of this function is as follows: + size_t iter = 0; + while (bufferZ_next (buf, &iter, &num, &str)) { + if (str) do_something_with_string (str, num); + else do_something_with_number (num); + } +******************************************************************************* +*/ +int bufferZ_next (TBuffer *buf, size_t *iter, size_t *num, const char **str) { + if (*iter < buf->top) { + size_t *ptr_header = (size_t*)(buf->arr + *iter); + *num = ptr_header[1]; + *iter += 2 * sizeof (size_t); + *str = NULL; + if (*ptr_header == ID_STRING) { + int n; + *str = buf->arr + *iter; + *iter += *num; + n = *iter % N_ALIGN; + if (n) *iter += (N_ALIGN - n); + } + return 1; + } + return 0; +} + +#if LUA_VERSION_NUM > 501 +int luaL_typerror (lua_State *L, int narg, const char *tname) { + const char *msg = lua_pushfstring(L, "%s expected, got %s", + tname, luaL_typename(L, narg)); + return luaL_argerror(L, narg, msg); +} +#endif + +#ifndef REX_NOEMBEDDEDTEST +static int ud_topointer (lua_State *L) { + lua_pushlightuserdata (L, lua_touserdata (L, 1)); + return 1; +} + +static int ud_len (lua_State *L) { + lua_pushinteger (L, lua_objlen (L, 1)); + return 1; +} + +/* for testing purposes only */ +int newmembuffer (lua_State *L) { + size_t len; + const char* s = luaL_checklstring (L, 1, &len); + void *ud = lua_newuserdata (L, len); + memcpy (ud, s, len); + lua_newtable (L); /* metatable */ + lua_pushvalue (L, -1); + lua_setfield (L, -2, "__index"); /* metatable.__index = metatable */ + lua_pushcfunction (L, ud_topointer); + lua_setfield (L, -2, "topointer"); + lua_pushcfunction (L, ud_len); + lua_setfield (L, -2, "__len"); + lua_setmetatable (L, -2); + return 1; +} +#endif /* #ifndef REX_NOEMBEDDEDTEST */ diff --git a/epan/wslua/lrexlib/common.h b/epan/wslua/lrexlib/common.h new file mode 100644 index 00000000..121e54c7 --- /dev/null +++ b/epan/wslua/lrexlib/common.h @@ -0,0 +1,128 @@ +/* common.h */ +/* + * Copyright (C) Reuben Thomas 2000-2020 + * Copyright (C) Shmuel Zeigerman 2004-2020 + + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the + * Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, + * sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the + * Software. + + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY + * KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR + * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS + * OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef COMMON_H +#define COMMON_H + +#include "lua.h" + +#if LUA_VERSION_NUM > 501 +# define lua_objlen lua_rawlen + int luaL_typerror (lua_State *L, int narg, const char *tname); +#endif + +/* REX_API can be overridden from the command line or Makefile */ +#ifndef REX_API +# define REX_API LUALIB_API +#endif + +/* Special values for maxmatch in gsub. They all must be negative. */ +#define GSUB_UNLIMITED -1 +#define GSUB_CONDITIONAL -2 + +/* Common structs and functions */ + +typedef struct { + const char* key; + int val; +} flag_pair; + +typedef struct { /* compile arguments */ + const char * pattern; + size_t patlen; + void * ud; + int cflags; + const char * locale; /* PCRE, Oniguruma */ + const unsigned char * tables; /* PCRE */ + int tablespos; /* PCRE */ + void * syntax; /* Oniguruma */ + const unsigned char * translate; /* GNU */ + int gnusyn; /* GNU */ +} TArgComp; + +typedef struct { /* exec arguments */ + const char * text; + size_t textlen; + int startoffset; + int eflags; + int funcpos; + int maxmatch; + int funcpos2; /* used with gsub */ + int reptype; /* used with gsub */ + size_t ovecsize; /* PCRE: dfa_exec */ + size_t wscount; /* PCRE: dfa_exec */ +} TArgExec; + +struct tagFreeList; /* forward declaration */ + +struct tagBuffer { + size_t size; + size_t top; + char * arr; + lua_State * L; + struct tagFreeList * freelist; +}; + +struct tagFreeList { + struct tagBuffer * list[16]; + int top; +}; + +typedef struct tagBuffer TBuffer; +typedef struct tagFreeList TFreeList; + +void freelist_init (TFreeList *fl); +void freelist_add (TFreeList *fl, TBuffer *buf); +void freelist_free (TFreeList *fl); + +void buffer_init (TBuffer *buf, size_t sz, lua_State *L, TFreeList *fl); +void buffer_free (TBuffer *buf); +void buffer_clear (TBuffer *buf); +void buffer_addbuffer (TBuffer *trg, TBuffer *src); +void buffer_addlstring (TBuffer *buf, const void *src, size_t sz); +void buffer_addvalue (TBuffer *buf, int stackpos); +void buffer_pushresult (TBuffer *buf); + +void bufferZ_putrepstring (TBuffer *buf, int reppos, int nsub); +int bufferZ_next (TBuffer *buf, size_t *iter, size_t *len, const char **str); +void bufferZ_addlstring (TBuffer *buf, const void *src, size_t len); +void bufferZ_addnum (TBuffer *buf, size_t num); + +int get_int_field (lua_State *L, const char* field); +void set_int_field (lua_State *L, const char* field, int val); +int get_flags (lua_State *L, const flag_pair **arr); +const char *get_flag_key (const flag_pair *fp, int val); +void *Lmalloc (lua_State *L, size_t size); +void *Lrealloc (lua_State *L, void *p, size_t osize, size_t nsize); +void Lfree (lua_State *L, void *p, size_t size); + +#ifndef REX_NOEMBEDDEDTEST +int newmembuffer (lua_State *L); +#endif + +#endif diff --git a/epan/wslua/lrexlib/pcre2/lpcre2.c b/epan/wslua/lrexlib/pcre2/lpcre2.c new file mode 100644 index 00000000..f24cbec8 --- /dev/null +++ b/epan/wslua/lrexlib/pcre2/lpcre2.c @@ -0,0 +1,544 @@ +/* lpcre2.c - Lua binding of PCRE2 library */ +/* + * Copyright (C) Reuben Thomas 2000-2020 + * Copyright (C) Shmuel Zeigerman 2004-2020 + + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the + * Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, + * sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the + * Software. + + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY + * KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR + * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS + * OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include <wireshark.h> +DIAG_OFF_CLANG(shorten-64-to-32) +DIAG_OFF_CLANG(comma) +#ifdef _MSC_VER +/* disable: " warning C4244: '=': conversion from 'lua _Integer' to 'int', + * possible loss of data" */ +#pragma warning(disable:4244) +/* warning C4267: '+=': conversion from 'size_t' to 'int', + * possible loss of data */ +#pragma warning(disable:4267) +#endif + +#define malloc_free free +#define rex_atoi atoi + +#include <stdlib.h> +#include <string.h> +#include <locale.h> +#include <ctype.h> +#include <stdint.h> +#include <pcre2.h> + +#include "lua.h" +#include "lauxlib.h" +#include "../common.h" + +#include <wslua/wslua.h> + +extern int Lpcre2_get_flags (lua_State *L); +extern int Lpcre2_config (lua_State *L); +extern flag_pair pcre2_error_flags[]; + +/* These 2 settings may be redefined from the command-line or the makefile. + * They should be kept in sync between themselves and with the target name. + */ +#ifndef REX_LIBNAME +# define REX_LIBNAME "rex_pcre2" +#endif +#ifndef REX_OPENLIB +# define REX_OPENLIB luaopen_rex_pcre2 +#endif + +#define REX_TYPENAME REX_LIBNAME"_regex" + +#define ALG_CFLAGS_DFLT 0 +#define ALG_EFLAGS_DFLT 0 + +static int getcflags (lua_State *L, int pos); +#define ALG_GETCFLAGS(L,pos) getcflags(L, pos) + +static void checkarg_compile (lua_State *L, int pos, TArgComp *argC); +#define ALG_GETCARGS(a,b,c) checkarg_compile(a,b,c) + +#define ALG_NOMATCH(res) ((res) == PCRE2_ERROR_NOMATCH) +#define ALG_ISMATCH(res) ((res) >= 0) +#define ALG_SUBBEG(ud,n) ((int)(ud)->ovector[(n)+(n)]) +#define ALG_SUBEND(ud,n) ((int)(ud)->ovector[(n)+(n)+1]) +#define ALG_SUBLEN(ud,n) (ALG_SUBEND((ud),(n)) - ALG_SUBBEG((ud),(n))) +#define ALG_SUBVALID(ud,n) (0 == pcre2_substring_length_bynumber((ud)->match_data, (n), NULL)) +#define ALG_NSUB(ud) ((int)(ud)->ncapt) + +#define ALG_PUSHSUB(L,ud,text,n) \ + lua_pushlstring (L, (text) + ALG_SUBBEG((ud),(n)), ALG_SUBLEN((ud),(n))) + +#define ALG_PUSHSUB_OR_FALSE(L,ud,text,n) \ + (ALG_SUBVALID(ud,n) ? (void) ALG_PUSHSUB (L,ud,text,n) : lua_pushboolean (L,0)) + +#define ALG_PUSHSTART(L,ud,offs,n) lua_pushinteger(L, (offs) + ALG_SUBBEG(ud,n) + 1) +#define ALG_PUSHEND(L,ud,offs,n) lua_pushinteger(L, (offs) + ALG_SUBEND(ud,n)) +#define ALG_PUSHOFFSETS(L,ud,offs,n) \ + (ALG_PUSHSTART(L,ud,offs,n), ALG_PUSHEND(L,ud,offs,n)) + +#define ALG_BASE(st) 0 +#define ALG_PULL + +typedef struct { + pcre2_code *pr; + pcre2_compile_context *ccontext; + pcre2_match_data *match_data; + PCRE2_SIZE *ovector; + int ncapt; + const unsigned char *tables; + int freed; +} TPcre2; + +#define TUserdata TPcre2 + +static void do_named_subpatterns (lua_State *L, TPcre2 *ud, const char *text); +# define DO_NAMED_SUBPATTERNS do_named_subpatterns + +#include "../algo.h" + +/* Locations of the 2 permanent tables in the function environment */ +#define INDEX_CHARTABLES_META 1 /* chartables type's metatable */ +#define INDEX_CHARTABLES_LINK 2 /* link chartables to compiled regex */ + +const char chartables_typename[] = "chartables"; + +/* Functions + ****************************************************************************** + */ + +static int push_error_message (lua_State *L, int errorcode) //### is this function needed? +{ + PCRE2_UCHAR buf[256]; + if (pcre2_get_error_message(errorcode, buf, 256) > 0) + { + lua_pushstring(L, (const char*)buf); + return 1; + } + return 0; +} + +static int getcflags (lua_State *L, int pos) { + switch (lua_type (L, pos)) { + case LUA_TNONE: + case LUA_TNIL: + return ALG_CFLAGS_DFLT; + case LUA_TNUMBER: + return lua_tointeger (L, pos); + case LUA_TSTRING: { + const char *s = lua_tostring (L, pos); + int res = 0, ch; + while ((ch = *s++) != '\0') { + if (ch == 'i') res |= PCRE2_CASELESS; + else if (ch == 'm') res |= PCRE2_MULTILINE; + else if (ch == 's') res |= PCRE2_DOTALL; + else if (ch == 'x') res |= PCRE2_EXTENDED; + else if (ch == 'U') res |= PCRE2_UNGREEDY; + //else if (ch == 'X') res |= PCRE2_EXTRA; //### does not exist in PCRE2 -> reflect in manual + } + return res; + } + default: + return luaL_typerror (L, pos, "number or string"); + } +} + +static int generate_error (lua_State *L, const TPcre2 *ud, int errcode) { + const char *key = get_flag_key (pcre2_error_flags, errcode); + (void) ud; + if (key) + return luaL_error (L, "error PCRE2_%s", key); + else + return luaL_error (L, "PCRE2 error code %d", errcode); +} + +/* method r:dfa_exec (s, [st], [ef], [ovecsize], [wscount]) */ +static void checkarg_dfa_exec (lua_State *L, TArgExec *argE, TPcre2 **ud) { + *ud = check_ud (L); + argE->text = luaL_checklstring (L, 2, &argE->textlen); + argE->startoffset = get_startoffset (L, 3, argE->textlen); + argE->eflags = (int)luaL_optinteger (L, 4, ALG_EFLAGS_DFLT); + argE->ovecsize = (size_t)luaL_optinteger (L, 5, 100); + argE->wscount = (size_t)luaL_optinteger (L, 6, 50); +} + +static void push_chartables_meta (lua_State *L) { + lua_pushinteger (L, INDEX_CHARTABLES_META); + lua_rawget (L, ALG_ENVIRONINDEX); +} + +static int Lpcre2_maketables (lua_State *L) { + *(const void**)lua_newuserdata (L, sizeof(void*)) = pcre2_maketables(NULL); //### argument NULL + push_chartables_meta (L); + lua_setmetatable (L, -2); + return 1; +} + +static void **check_chartables (lua_State *L, int pos) { + void **q; + /* Compare the metatable against the C function environment. */ + if (lua_getmetatable(L, pos)) { + push_chartables_meta (L); + if (lua_rawequal(L, -1, -2) && + (q = (void **)lua_touserdata(L, pos)) != NULL) { + lua_pop(L, 2); + return q; + } + } + luaL_argerror(L, pos, lua_pushfstring (L, "not a %s", chartables_typename)); + return NULL; +} + +static int chartables_gc (lua_State *L) { + void **ud = check_chartables (L, 1); + if (*ud) { + malloc_free (*ud); //### free() should be called only if pcre2_maketables was called with NULL argument + *ud = NULL; + } + return 0; +} + +static int chartables_tostring (lua_State *L) { + void **ud = check_chartables (L, 1); + lua_pushfstring (L, "%s (%p)", chartables_typename, ud); + return 1; +} + +static void checkarg_compile (lua_State *L, int pos, TArgComp *argC) { + argC->locale = NULL; + argC->tables = NULL; + if (!lua_isnoneornil (L, pos)) { + if (lua_isstring (L, pos)) + argC->locale = lua_tostring (L, pos); + else { + argC->tablespos = pos; + argC->tables = (const unsigned char*) *check_chartables (L, pos); + } + } +} + +static int compile_regex (lua_State *L, const TArgComp *argC, TPcre2 **pud) { + int errcode; + PCRE2_SIZE erroffset; + TPcre2 *ud; + + ud = (TPcre2*)lua_newuserdata (L, sizeof (TPcre2)); + memset (ud, 0, sizeof (TPcre2)); /* initialize all members to 0 */ + lua_pushvalue (L, ALG_ENVIRONINDEX); + lua_setmetatable (L, -2); + + ud->ccontext = pcre2_compile_context_create(NULL); + if (ud->ccontext == NULL) + return luaL_error (L, "malloc failed"); + + if (argC->locale) { + char old_locale[256]; + g_strlcpy (old_locale, setlocale (LC_CTYPE, NULL), sizeof(old_locale)); /* store the locale */ + if (NULL == setlocale (LC_CTYPE, argC->locale)) /* set new locale */ + return luaL_error (L, "cannot set locale"); + ud->tables = pcre2_maketables (NULL); /* make tables with new locale */ //### argument NULL + pcre2_set_character_tables(ud->ccontext, ud->tables); + setlocale (LC_CTYPE, old_locale); /* restore the old locale */ + } + else if (argC->tables) { + pcre2_set_character_tables(ud->ccontext, argC->tables); + lua_pushinteger (L, INDEX_CHARTABLES_LINK); + lua_rawget (L, ALG_ENVIRONINDEX); + lua_pushvalue (L, -2); + lua_pushvalue (L, argC->tablespos); + lua_rawset (L, -3); + lua_pop (L, 1); + } + + ud->pr = pcre2_compile ((PCRE2_SPTR)argC->pattern, argC->patlen, argC->cflags, &errcode, + &erroffset, ud->ccontext); //### DOUBLE-CHECK ALL ARGUMENTS + if (!ud->pr) { + if (push_error_message(L, errcode)) + return luaL_error (L, "%s (pattern offset: %d)", lua_tostring(L,-1), erroffset + 1); + else + return luaL_error (L, "%s (pattern offset: %d)", "pattern compile error", erroffset + 1); + } + + if (0 != pcre2_pattern_info (ud->pr, PCRE2_INFO_CAPTURECOUNT, &ud->ncapt)) //### + return luaL_error (L, "could not get pattern info"); + + /* need (2 ints per capture, plus one for substring match) * 3/2 */ + ud->match_data = pcre2_match_data_create(ud->ncapt+1, NULL); //### CHECK ALL + if (!ud->match_data) + return luaL_error (L, "malloc failed"); + + ud->ovector = pcre2_get_ovector_pointer(ud->match_data); + + if (pud) *pud = ud; + return 1; +} + +/* the target table must be on lua stack top */ +static void do_named_subpatterns (lua_State *L, TPcre2 *ud, const char *text) { + int i, namecount, name_entry_size; + unsigned char *name_table; + PCRE2_SPTR tabptr; + + /* do named subpatterns - NJG */ + pcre2_pattern_info (ud->pr, PCRE2_INFO_NAMECOUNT, &namecount); + if (namecount <= 0) + return; + pcre2_pattern_info (ud->pr, PCRE2_INFO_NAMETABLE, &name_table); + pcre2_pattern_info (ud->pr, PCRE2_INFO_NAMEENTRYSIZE, &name_entry_size); + tabptr = name_table; + for (i = 0; i < namecount; i++) { + int n = (tabptr[0] << 8) | tabptr[1]; /* number of the capturing parenthesis */ + if (n > 0 && n <= ALG_NSUB(ud)) { /* check range */ + lua_pushstring (L, (char *)tabptr + 2); /* name of the capture, zero terminated */ + ALG_PUSHSUB_OR_FALSE (L, ud, text, n); + lua_rawset (L, -3); + } + tabptr += name_entry_size; + } +} + +static int Lpcre2_dfa_exec (lua_State *L) +{ + TArgExec argE; + TPcre2 *ud; + int res; + int *wspace; + size_t wsize; + + checkarg_dfa_exec (L, &argE, &ud); + wsize = argE.wscount * sizeof(int); + wspace = (int*) Lmalloc (L, wsize); + if (!wspace) + luaL_error (L, "malloc failed"); + + ud->match_data = pcre2_match_data_create(argE.ovecsize/2, NULL); //### CHECK ALL + if (!ud->match_data) + return luaL_error (L, "malloc failed"); + + res = pcre2_dfa_match (ud->pr, (PCRE2_SPTR)argE.text, argE.textlen, argE.startoffset, + argE.eflags, ud->match_data, NULL, wspace, argE.wscount); //### CHECK ALL + + if (ALG_ISMATCH (res) || res == PCRE2_ERROR_PARTIAL) { + int i; + int max = (res>0) ? res : (res==0) ? (int)argE.ovecsize/2 : 1; + PCRE2_SIZE* ovector = pcre2_get_ovector_pointer(ud->match_data); + + lua_pushinteger (L, ovector[0] + 1); /* 1-st return value */ + lua_newtable (L); /* 2-nd return value */ + for (i=0; i<max; i++) { + lua_pushinteger (L, ovector[i+i+1]); + lua_rawseti (L, -2, i+1); + } + lua_pushinteger (L, res); /* 3-rd return value */ + Lfree (L, wspace, wsize); + return 3; + } + else { + Lfree (L, wspace, wsize); + if (ALG_NOMATCH (res)) + return lua_pushnil (L), 1; + else + return generate_error (L, ud, res); + } +} + +static int gmatch_exec (TUserdata *ud, TArgExec *argE) { + return pcre2_match (ud->pr, (PCRE2_SPTR)argE->text, argE->textlen, + argE->startoffset, argE->eflags, ud->match_data, NULL); //### +} + +static void gmatch_pushsubject (lua_State *L, TArgExec *argE) { + lua_pushlstring (L, argE->text, argE->textlen); +} + +static int findmatch_exec (TPcre2 *ud, TArgExec *argE) { + return pcre2_match (ud->pr, (PCRE2_SPTR)argE->text, argE->textlen, + argE->startoffset, argE->eflags, ud->match_data, NULL); //### +} + +static int gsub_exec (TPcre2 *ud, TArgExec *argE, int st) { + return pcre2_match (ud->pr, (PCRE2_SPTR)argE->text, argE->textlen, + st, argE->eflags, ud->match_data, NULL); //### +} + +static int split_exec (TPcre2 *ud, TArgExec *argE, int offset) { + return pcre2_match (ud->pr, (PCRE2_SPTR)argE->text, argE->textlen, + offset, argE->eflags, ud->match_data, NULL); //### +} + +static int Lpcre2_gc (lua_State *L) { + TPcre2 *ud = check_ud (L); + if (ud->freed == 0) { /* precaution against "manual" __gc calling */ + ud->freed = 1; + if (ud->pr) pcre2_code_free (ud->pr); + //if (ud->tables) pcre_free ((void *)ud->tables); //### + if (ud->ccontext) pcre2_compile_context_free (ud->ccontext); + if (ud->match_data) pcre2_match_data_free (ud->match_data); + } + return 0; +} + +static int Lpcre2_tostring (lua_State *L) { + TPcre2 *ud = check_ud (L); + if (ud->freed == 0) + lua_pushfstring (L, "%s (%p)", REX_TYPENAME, (void*)ud); + else + lua_pushfstring (L, "%s (deleted)", REX_TYPENAME); + return 1; +} + +static int Lpcre2_version (lua_State *L) { + char buf[64]; + pcre2_config(PCRE2_CONFIG_VERSION, buf); + lua_pushstring (L, buf); + return 1; +} + +//### TODO: document this method. +//### TODO: write tests for this method. +static int Lpcre2_jit_compile (lua_State *L) { + TPcre2 *ud = check_ud (L); + uint32_t options = (uint32_t) luaL_optinteger (L, 2, PCRE2_JIT_COMPLETE); + int errcode = pcre2_jit_compile (ud->pr, options); + if (errcode == 0) { + lua_pushboolean(L, 1); + return 1; + } + lua_pushboolean(L, 0); + return 1 + push_error_message(L, errcode); +} + +#define SET_INFO_FIELD(L,ud,what,name,valtype) { \ + valtype val; \ + if (0 == pcre2_pattern_info (ud->pr, what, &val)) { \ + lua_pushnumber (L, val); \ + lua_setfield (L, -2, name); \ + } \ +} + +static int Lpcre2_pattern_info (lua_State *L) { + TPcre2 *ud = check_ud (L); + lua_newtable(L); + + SET_INFO_FIELD (L, ud, PCRE2_INFO_ALLOPTIONS, "ALLOPTIONS", uint32_t) + SET_INFO_FIELD (L, ud, PCRE2_INFO_ARGOPTIONS, "ARGOPTIONS", uint32_t) + SET_INFO_FIELD (L, ud, PCRE2_INFO_BACKREFMAX, "BACKREFMAX", uint32_t) + SET_INFO_FIELD (L, ud, PCRE2_INFO_BSR, "BSR", uint32_t) + SET_INFO_FIELD (L, ud, PCRE2_INFO_CAPTURECOUNT, "CAPTURECOUNT", uint32_t) + //### SET_INFO_FIELD (L, ud, PCRE2_INFO_FIRSTBITMAP, "FIRSTBITMAP", ???) + SET_INFO_FIELD (L, ud, PCRE2_INFO_FIRSTCODETYPE, "FIRSTCODETYPE", uint32_t) + SET_INFO_FIELD (L, ud, PCRE2_INFO_FIRSTCODEUNIT, "FIRSTCODEUNIT", uint32_t) + SET_INFO_FIELD (L, ud, PCRE2_INFO_HASBACKSLASHC, "HASBACKSLASHC", uint32_t) + SET_INFO_FIELD (L, ud, PCRE2_INFO_HASCRORLF, "HASCRORLF", uint32_t) + SET_INFO_FIELD (L, ud, PCRE2_INFO_JCHANGED, "JCHANGED", uint32_t) + SET_INFO_FIELD (L, ud, PCRE2_INFO_JITSIZE, "JITSIZE", size_t) + SET_INFO_FIELD (L, ud, PCRE2_INFO_LASTCODETYPE, "LASTCODETYPE", uint32_t) + SET_INFO_FIELD (L, ud, PCRE2_INFO_LASTCODEUNIT, "LASTCODEUNIT", uint32_t) + SET_INFO_FIELD (L, ud, PCRE2_INFO_MATCHEMPTY, "MATCHEMPTY", uint32_t) + SET_INFO_FIELD (L, ud, PCRE2_INFO_MATCHLIMIT, "MATCHLIMIT", uint32_t) + SET_INFO_FIELD (L, ud, PCRE2_INFO_MAXLOOKBEHIND, "MAXLOOKBEHIND", uint32_t) + SET_INFO_FIELD (L, ud, PCRE2_INFO_MINLENGTH, "MINLENGTH", uint32_t) + SET_INFO_FIELD (L, ud, PCRE2_INFO_NAMECOUNT, "NAMECOUNT", uint32_t) + SET_INFO_FIELD (L, ud, PCRE2_INFO_NAMEENTRYSIZE, "NAMEENTRYSIZE", uint32_t) + //### SET_INFO_FIELD (L, ud, PCRE2_INFO_NAMETABLE, "NAMETABLE", ???) + SET_INFO_FIELD (L, ud, PCRE2_INFO_NEWLINE, "NEWLINE", uint32_t) + SET_INFO_FIELD (L, ud, PCRE2_INFO_RECURSIONLIMIT, "RECURSIONLIMIT", uint32_t) + SET_INFO_FIELD (L, ud, PCRE2_INFO_SIZE, "SIZE", size_t) + + return 1; +} + +static const luaL_Reg chartables_meta[] = { + { "__gc", chartables_gc }, + { "__tostring", chartables_tostring }, + { NULL, NULL } +}; + +static const luaL_Reg r_methods[] = { + { "exec", algm_exec }, + { "tfind", algm_tfind }, /* old name: match */ + { "find", algm_find }, + { "match", algm_match }, + { "dfa_exec", Lpcre2_dfa_exec }, + { "patterninfo", Lpcre2_pattern_info }, //### document name change: fullinfo -> patterninfo + { "fullinfo", Lpcre2_pattern_info }, //### compatibility name + { "jit_compile", Lpcre2_jit_compile }, + { "__gc", Lpcre2_gc }, + { "__tostring", Lpcre2_tostring }, + { NULL, NULL } +}; + +static const luaL_Reg r_functions[] = { + { "match", algf_match }, + { "find", algf_find }, + { "gmatch", algf_gmatch }, + { "gsub", algf_gsub }, + { "count", algf_count }, + { "split", algf_split }, + { "new", algf_new }, + { "flags", Lpcre2_get_flags }, + { "version", Lpcre2_version }, + { "maketables", Lpcre2_maketables }, + { "config", Lpcre2_config }, + { NULL, NULL } +}; + +/* Open the library */ +REX_API int REX_OPENLIB (lua_State *L) { + char buf_ver[64]; + pcre2_config(PCRE2_CONFIG_VERSION, buf_ver); + if (PCRE2_MAJOR > rex_atoi (buf_ver)) { + return luaL_error (L, "%s requires at least version %d of PCRE2 library", + REX_LIBNAME, (int)PCRE2_MAJOR); + } + + alg_register(L, r_methods, r_functions, "PCRE2"); + + /* create a table and register it as a metatable for "chartables" userdata */ + lua_newtable (L); + lua_pushliteral (L, "access denied"); + lua_setfield (L, -2, "__metatable"); +#if LUA_VERSION_NUM == 501 + luaL_register (L, NULL, chartables_meta); + lua_rawseti (L, LUA_ENVIRONINDEX, INDEX_CHARTABLES_META); +#else + lua_pushvalue(L, -3); + luaL_setfuncs (L, chartables_meta, 1); + lua_rawseti (L, -3, INDEX_CHARTABLES_META); +#endif + + /* create a table for connecting "chartables" userdata to "regex" userdata */ + lua_newtable (L); + lua_pushliteral (L, "k"); /* weak keys */ + lua_setfield (L, -2, "__mode"); + lua_pushvalue (L, -1); /* setmetatable (tb, tb) */ + lua_setmetatable (L, -2); +#if LUA_VERSION_NUM == 501 + lua_rawseti (L, LUA_ENVIRONINDEX, INDEX_CHARTABLES_LINK); +#else + lua_rawseti (L, -3, INDEX_CHARTABLES_LINK); +#endif + + return 1; +} diff --git a/epan/wslua/lrexlib/pcre2/lpcre2_f.c b/epan/wslua/lrexlib/pcre2/lpcre2_f.c new file mode 100644 index 00000000..659c8ac7 --- /dev/null +++ b/epan/wslua/lrexlib/pcre2/lpcre2_f.c @@ -0,0 +1,240 @@ +/* lpcre2_f.c - Lua binding of PCRE2 library */ +/* + * Copyright (C) Reuben Thomas 2000-2020 + * Copyright (C) Shmuel Zeigerman 2004-2020 + + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the + * Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, + * sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the + * Software. + + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY + * KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR + * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS + * OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include <pcre2.h> +#include "lua.h" +#include "lauxlib.h" +#include "../common.h" + +#define VERSION_PCRE2 (PCRE2_MAJOR*100 + PCRE2_MINOR) + +extern int Lpcre2_get_flags (lua_State *L); +extern int Lpcre2_config (lua_State *L); + +static flag_pair pcre2_flags[] = { + { "MAJOR", PCRE2_MAJOR }, + { "MINOR", PCRE2_MINOR }, +/*---------------------------------------------------------------------------*/ + { "ANCHORED", PCRE2_ANCHORED }, + { "NO_UTF_CHECK", PCRE2_NO_UTF_CHECK }, + { "ALLOW_EMPTY_CLASS", PCRE2_ALLOW_EMPTY_CLASS }, + { "ALT_BSUX", PCRE2_ALT_BSUX }, + { "AUTO_CALLOUT", PCRE2_AUTO_CALLOUT }, + { "CASELESS", PCRE2_CASELESS }, + { "DOLLAR_ENDONLY", PCRE2_DOLLAR_ENDONLY }, + { "DOTALL", PCRE2_DOTALL }, + { "DUPNAMES", PCRE2_DUPNAMES }, + { "EXTENDED", PCRE2_EXTENDED }, + { "FIRSTLINE", PCRE2_FIRSTLINE }, + { "MATCH_UNSET_BACKREF", PCRE2_MATCH_UNSET_BACKREF }, + { "MULTILINE", PCRE2_MULTILINE }, + { "NEVER_UCP", PCRE2_NEVER_UCP }, + { "NEVER_UTF", PCRE2_NEVER_UTF }, + { "NO_AUTO_CAPTURE", PCRE2_NO_AUTO_CAPTURE }, + { "NO_AUTO_POSSESS", PCRE2_NO_AUTO_POSSESS }, + { "NO_DOTSTAR_ANCHOR", PCRE2_NO_DOTSTAR_ANCHOR }, + { "NO_START_OPTIMIZE", PCRE2_NO_START_OPTIMIZE }, + { "UCP", PCRE2_UCP }, + { "UNGREEDY", PCRE2_UNGREEDY }, + { "UTF", PCRE2_UTF }, + { "NEVER_BACKSLASH_C", PCRE2_NEVER_BACKSLASH_C }, + { "ALT_CIRCUMFLEX", PCRE2_ALT_CIRCUMFLEX }, + { "ALT_VERBNAMES", PCRE2_ALT_VERBNAMES }, + { "USE_OFFSET_LIMIT", PCRE2_USE_OFFSET_LIMIT }, + { "JIT_COMPLETE", PCRE2_JIT_COMPLETE }, + { "JIT_PARTIAL_SOFT", PCRE2_JIT_PARTIAL_SOFT }, + { "JIT_PARTIAL_HARD", PCRE2_JIT_PARTIAL_HARD }, + { "NOTBOL", PCRE2_NOTBOL }, + { "NOTEOL", PCRE2_NOTEOL }, + { "NOTEMPTY", PCRE2_NOTEMPTY }, + { "NOTEMPTY_ATSTART", PCRE2_NOTEMPTY_ATSTART }, + { "PARTIAL_SOFT", PCRE2_PARTIAL_SOFT }, + { "PARTIAL_HARD", PCRE2_PARTIAL_HARD }, + { "DFA_RESTART", PCRE2_DFA_RESTART }, + { "DFA_SHORTEST", PCRE2_DFA_SHORTEST }, + { "SUBSTITUTE_GLOBAL", PCRE2_SUBSTITUTE_GLOBAL }, + { "SUBSTITUTE_EXTENDED", PCRE2_SUBSTITUTE_EXTENDED }, + { "SUBSTITUTE_UNSET_EMPTY", PCRE2_SUBSTITUTE_UNSET_EMPTY }, + { "SUBSTITUTE_UNKNOWN_UNSET", PCRE2_SUBSTITUTE_UNKNOWN_UNSET }, + { "SUBSTITUTE_OVERFLOW_LENGTH", PCRE2_SUBSTITUTE_OVERFLOW_LENGTH }, +#ifdef PCRE2_NO_JIT + { "NO_JIT", PCRE2_NO_JIT }, +#endif + { "NEWLINE_CR", PCRE2_NEWLINE_CR }, + { "NEWLINE_LF", PCRE2_NEWLINE_LF }, + { "NEWLINE_CRLF", PCRE2_NEWLINE_CRLF }, + { "NEWLINE_ANY", PCRE2_NEWLINE_ANY }, + { "NEWLINE_ANYCRLF", PCRE2_NEWLINE_ANYCRLF }, + { "BSR_UNICODE", PCRE2_BSR_UNICODE }, + { "BSR_ANYCRLF", PCRE2_BSR_ANYCRLF }, +/*---------------------------------------------------------------------------*/ + { "INFO_ALLOPTIONS", PCRE2_INFO_ALLOPTIONS }, + { "INFO_ARGOPTIONS", PCRE2_INFO_ARGOPTIONS }, + { "INFO_BACKREFMAX", PCRE2_INFO_BACKREFMAX }, + { "INFO_BSR", PCRE2_INFO_BSR }, + { "INFO_CAPTURECOUNT", PCRE2_INFO_CAPTURECOUNT }, + { "INFO_FIRSTCODEUNIT", PCRE2_INFO_FIRSTCODEUNIT }, + { "INFO_FIRSTCODETYPE", PCRE2_INFO_FIRSTCODETYPE }, + { "INFO_FIRSTBITMAP", PCRE2_INFO_FIRSTBITMAP }, + { "INFO_HASCRORLF", PCRE2_INFO_HASCRORLF }, + { "INFO_JCHANGED", PCRE2_INFO_JCHANGED }, + { "INFO_JITSIZE", PCRE2_INFO_JITSIZE }, + { "INFO_LASTCODEUNIT", PCRE2_INFO_LASTCODEUNIT }, + { "INFO_LASTCODETYPE", PCRE2_INFO_LASTCODETYPE }, + { "INFO_MATCHEMPTY", PCRE2_INFO_MATCHEMPTY }, + { "INFO_MATCHLIMIT", PCRE2_INFO_MATCHLIMIT }, + { "INFO_MAXLOOKBEHIND", PCRE2_INFO_MAXLOOKBEHIND }, + { "INFO_MINLENGTH", PCRE2_INFO_MINLENGTH }, + { "INFO_NAMECOUNT", PCRE2_INFO_NAMECOUNT }, + { "INFO_NAMEENTRYSIZE", PCRE2_INFO_NAMEENTRYSIZE }, + { "INFO_NAMETABLE", PCRE2_INFO_NAMETABLE }, + { "INFO_NEWLINE", PCRE2_INFO_NEWLINE }, + { "INFO_RECURSIONLIMIT", PCRE2_INFO_RECURSIONLIMIT }, + { "INFO_SIZE", PCRE2_INFO_SIZE }, + { "INFO_HASBACKSLASHC", PCRE2_INFO_HASBACKSLASHC }, +/*---------------------------------------------------------------------------*/ + { NULL, 0 } +}; + +flag_pair pcre2_error_flags[] = { + { "ERROR_NOMATCH", PCRE2_ERROR_NOMATCH }, + { "ERROR_PARTIAL", PCRE2_ERROR_PARTIAL }, + { "ERROR_UTF8_ERR1", PCRE2_ERROR_UTF8_ERR1 }, + { "ERROR_UTF8_ERR2", PCRE2_ERROR_UTF8_ERR2 }, + { "ERROR_UTF8_ERR3", PCRE2_ERROR_UTF8_ERR3 }, + { "ERROR_UTF8_ERR4", PCRE2_ERROR_UTF8_ERR4 }, + { "ERROR_UTF8_ERR5", PCRE2_ERROR_UTF8_ERR5 }, + { "ERROR_UTF8_ERR6", PCRE2_ERROR_UTF8_ERR6 }, + { "ERROR_UTF8_ERR7", PCRE2_ERROR_UTF8_ERR7 }, + { "ERROR_UTF8_ERR8", PCRE2_ERROR_UTF8_ERR8 }, + { "ERROR_UTF8_ERR9", PCRE2_ERROR_UTF8_ERR9 }, + { "ERROR_UTF8_ERR10", PCRE2_ERROR_UTF8_ERR10 }, + { "ERROR_UTF8_ERR11", PCRE2_ERROR_UTF8_ERR11 }, + { "ERROR_UTF8_ERR12", PCRE2_ERROR_UTF8_ERR12 }, + { "ERROR_UTF8_ERR13", PCRE2_ERROR_UTF8_ERR13 }, + { "ERROR_UTF8_ERR14", PCRE2_ERROR_UTF8_ERR14 }, + { "ERROR_UTF8_ERR15", PCRE2_ERROR_UTF8_ERR15 }, + { "ERROR_UTF8_ERR16", PCRE2_ERROR_UTF8_ERR16 }, + { "ERROR_UTF8_ERR17", PCRE2_ERROR_UTF8_ERR17 }, + { "ERROR_UTF8_ERR18", PCRE2_ERROR_UTF8_ERR18 }, + { "ERROR_UTF8_ERR19", PCRE2_ERROR_UTF8_ERR19 }, + { "ERROR_UTF8_ERR20", PCRE2_ERROR_UTF8_ERR20 }, + { "ERROR_UTF8_ERR21", PCRE2_ERROR_UTF8_ERR21 }, + { "ERROR_UTF16_ERR1", PCRE2_ERROR_UTF16_ERR1 }, + { "ERROR_UTF16_ERR2", PCRE2_ERROR_UTF16_ERR2 }, + { "ERROR_UTF16_ERR3", PCRE2_ERROR_UTF16_ERR3 }, + { "ERROR_UTF32_ERR1", PCRE2_ERROR_UTF32_ERR1 }, + { "ERROR_UTF32_ERR2", PCRE2_ERROR_UTF32_ERR2 }, + { "ERROR_BADDATA", PCRE2_ERROR_BADDATA }, + { "ERROR_MIXEDTABLES", PCRE2_ERROR_MIXEDTABLES }, + { "ERROR_BADMAGIC", PCRE2_ERROR_BADMAGIC }, + { "ERROR_BADMODE", PCRE2_ERROR_BADMODE }, + { "ERROR_BADOFFSET", PCRE2_ERROR_BADOFFSET }, + { "ERROR_BADOPTION", PCRE2_ERROR_BADOPTION }, + { "ERROR_BADREPLACEMENT", PCRE2_ERROR_BADREPLACEMENT }, + { "ERROR_BADUTFOFFSET", PCRE2_ERROR_BADUTFOFFSET }, + { "ERROR_CALLOUT", PCRE2_ERROR_CALLOUT }, + { "ERROR_DFA_BADRESTART", PCRE2_ERROR_DFA_BADRESTART }, + { "ERROR_DFA_RECURSE", PCRE2_ERROR_DFA_RECURSE }, + { "ERROR_DFA_UCOND", PCRE2_ERROR_DFA_UCOND }, + { "ERROR_DFA_UFUNC", PCRE2_ERROR_DFA_UFUNC }, + { "ERROR_DFA_UITEM", PCRE2_ERROR_DFA_UITEM }, + { "ERROR_DFA_WSSIZE", PCRE2_ERROR_DFA_WSSIZE }, + { "ERROR_INTERNAL", PCRE2_ERROR_INTERNAL }, + { "ERROR_JIT_BADOPTION", PCRE2_ERROR_JIT_BADOPTION }, + { "ERROR_JIT_STACKLIMIT", PCRE2_ERROR_JIT_STACKLIMIT }, + { "ERROR_MATCHLIMIT", PCRE2_ERROR_MATCHLIMIT }, + { "ERROR_NOMEMORY", PCRE2_ERROR_NOMEMORY }, + { "ERROR_NOSUBSTRING", PCRE2_ERROR_NOSUBSTRING }, + { "ERROR_NOUNIQUESUBSTRING", PCRE2_ERROR_NOUNIQUESUBSTRING }, + { "ERROR_NULL", PCRE2_ERROR_NULL }, + { "ERROR_RECURSELOOP", PCRE2_ERROR_RECURSELOOP }, + { "ERROR_RECURSIONLIMIT", PCRE2_ERROR_RECURSIONLIMIT }, + { "ERROR_UNAVAILABLE", PCRE2_ERROR_UNAVAILABLE }, + { "ERROR_UNSET", PCRE2_ERROR_UNSET }, + { "ERROR_BADOFFSETLIMIT", PCRE2_ERROR_BADOFFSETLIMIT }, + { "ERROR_BADREPESCAPE", PCRE2_ERROR_BADREPESCAPE }, + { "ERROR_REPMISSINGBRACE", PCRE2_ERROR_REPMISSINGBRACE }, + { "ERROR_BADSUBSTITUTION", PCRE2_ERROR_BADSUBSTITUTION }, + { "ERROR_BADSUBSPATTERN", PCRE2_ERROR_BADSUBSPATTERN }, + { "ERROR_TOOMANYREPLACE", PCRE2_ERROR_TOOMANYREPLACE }, +#ifdef PCRE2_ERROR_BADSERIALIZEDDATA + { "ERROR_BADSERIALIZEDDATA", PCRE2_ERROR_BADSERIALIZEDDATA }, +#endif +/*---------------------------------------------------------------------------*/ + { NULL, 0 } +}; + +static flag_pair pcre2_config_flags[] = { + { "PCRE2_CONFIG_BSR", PCRE2_CONFIG_BSR }, + { "PCRE2_CONFIG_JIT", PCRE2_CONFIG_JIT }, + { "PCRE2_CONFIG_JITTARGET", PCRE2_CONFIG_JITTARGET }, + { "PCRE2_CONFIG_LINKSIZE", PCRE2_CONFIG_LINKSIZE }, + { "PCRE2_CONFIG_MATCHLIMIT", PCRE2_CONFIG_MATCHLIMIT }, + { "PCRE2_CONFIG_NEWLINE", PCRE2_CONFIG_NEWLINE }, + { "PCRE2_CONFIG_PARENSLIMIT", PCRE2_CONFIG_PARENSLIMIT }, + { "PCRE2_CONFIG_RECURSIONLIMIT", PCRE2_CONFIG_RECURSIONLIMIT }, + { "PCRE2_CONFIG_STACKRECURSE", PCRE2_CONFIG_STACKRECURSE }, + { "PCRE2_CONFIG_UNICODE", PCRE2_CONFIG_UNICODE }, + { "PCRE2_CONFIG_UNICODE_VERSION", PCRE2_CONFIG_UNICODE_VERSION }, + { "PCRE2_CONFIG_VERSION", PCRE2_CONFIG_VERSION }, +/*---------------------------------------------------------------------------*/ + { NULL, 0 } +}; + +extern int Lpcre2_config (lua_State *L) { + flag_pair *fp; + if (lua_istable (L, 1)) + lua_settop (L, 1); + else + lua_newtable (L); + for (fp = pcre2_config_flags; fp->key; ++fp) { + if (fp->val == PCRE2_CONFIG_JITTARGET) { +#if PCRE2_CODE_UNIT_WIDTH == 8 + char buf[64]; + if (PCRE2_ERROR_BADOPTION != pcre2_config (fp->val, buf)) { + lua_pushstring (L, buf); + lua_setfield (L, -2, fp->key); + } +#endif + } + else { + int val; + if (0 == pcre2_config (fp->val, &val)) { + lua_pushinteger (L, val); + lua_setfield (L, -2, fp->key); + } + } + } + return 1; +} + +extern int Lpcre2_get_flags (lua_State *L) { + const flag_pair* fps[] = { pcre2_flags, pcre2_error_flags, NULL }; + return get_flags (L, fps); +} diff --git a/epan/wslua/lua_bitop.c b/epan/wslua/lua_bitop.c new file mode 100644 index 00000000..2f3c3eeb --- /dev/null +++ b/epan/wslua/lua_bitop.c @@ -0,0 +1,171 @@ +/* +** Lua BitOp -- a bit operations library for Lua 5.1/5.2. +** http://bitop.luajit.org/ +** +** Copyright (C) 2008-2012 Mike Pall. All rights reserved. +** +** SPDX-License-Identifier: MIT +** +*/ + +#define LUA_BITOP_VERSION "1.0.2" + +#define LUA_LIB +#include <lua.h> +#include <lauxlib.h> + +#include "lua_bitop.h" +#include "ws_diag_control.h" + +#include <stdint.h> + +typedef int32_t SBits; +typedef uint32_t UBits; + +typedef union { + lua_Number n; +#ifdef LUA_NUMBER_DOUBLE + uint64_t b; +#else + UBits b; +#endif +} BitNum; + +/* Convert argument to bit type. */ +static UBits barg(lua_State *L, int idx) +{ + BitNum bn; + UBits b; +#if LUA_VERSION_NUM < 502 + bn.n = lua_tonumber(L, idx); +#else + bn.n = luaL_checknumber(L, idx); +#endif +#if defined(LUA_NUMBER_DOUBLE) + bn.n += 6755399441055744.0; /* 2^52+2^51 */ +#ifdef SWAPPED_DOUBLE + b = (UBits)(bn.b >> 32); +#else + b = (UBits)bn.b; +#endif +#elif defined(LUA_NUMBER_INT) || defined(LUA_NUMBER_LONG) || \ + defined(LUA_NUMBER_LONGLONG) || defined(LUA_NUMBER_LONG_LONG) || \ + defined(LUA_NUMBER_LLONG) + if (sizeof(UBits) == sizeof(lua_Number)) + b = bn.b; + else + b = (UBits)(SBits)bn.n; +#elif defined(LUA_NUMBER_FLOAT) +#error "A 'float' lua_Number type is incompatible with this library" +#else +#error "Unknown number type, check LUA_NUMBER_* in luaconf.h" +#endif +#if LUA_VERSION_NUM < 502 + if (b == 0 && !lua_isnumber(L, idx)) { + luaL_typerror(L, idx, "number"); + } +#endif + return b; +} + +/* Return bit type. */ +#define BRET(b) lua_pushnumber(L, (lua_Number)(SBits)(b)); return 1; + +static int bit_tobit(lua_State *L) { BRET(barg(L, 1)) } +static int bit_bnot(lua_State *L) { BRET(~barg(L, 1)) } + +#define BIT_OP(func, opr) \ + static int func(lua_State *L) { int i; UBits b = barg(L, 1); \ + for (i = lua_gettop(L); i > 1; i--) { b opr barg(L, i); } BRET(b) } +BIT_OP(bit_band, &=) +BIT_OP(bit_bor, |=) +BIT_OP(bit_bxor, ^=) + +#define bshl(b, n) (b << n) +#define bshr(b, n) (b >> n) +#define bsar(b, n) ((SBits)b >> n) +#define brol(b, n) ((b << n) | (b >> (32-n))) +#define bror(b, n) ((b << (32-n)) | (b >> n)) +#define BIT_SH(func, fn) \ + static int func(lua_State *L) { \ + UBits b = barg(L, 1); UBits n = barg(L, 2) & 31; BRET(fn(b, n)) } +BIT_SH(bit_lshift, bshl) +BIT_SH(bit_rshift, bshr) +BIT_SH(bit_arshift, bsar) +BIT_SH(bit_rol, brol) +BIT_SH(bit_ror, bror) + +static int bit_bswap(lua_State *L) +{ + UBits b = barg(L, 1); + b = (b >> 24) | ((b >> 8) & 0xff00) | ((b & 0xff00) << 8) | (b << 24); + BRET(b) +} + +static int bit_tohex(lua_State *L) +{ + UBits b = barg(L, 1); + SBits n = lua_isnone(L, 2) ? 8 : (SBits)barg(L, 2); + const char *hexdigits = "0123456789abcdef"; + char buf[8]; + int i; + if (n < 0) { n = -n; hexdigits = "0123456789ABCDEF"; } + if (n > 8) n = 8; + for (i = (int)n; --i >= 0; ) { buf[i] = hexdigits[b & 15]; b >>= 4; } + lua_pushlstring(L, buf, (size_t)n); + return 1; +} + +static const struct luaL_Reg bit_funcs[] = { + { "tobit", bit_tobit }, + { "bnot", bit_bnot }, + { "band", bit_band }, + { "bor", bit_bor }, + { "bxor", bit_bxor }, + { "lshift", bit_lshift }, + { "rshift", bit_rshift }, + { "arshift", bit_arshift }, + { "rol", bit_rol }, + { "ror", bit_ror }, + { "bswap", bit_bswap }, + { "tohex", bit_tohex }, + { NULL, NULL } +}; + +/* Signed right-shifts are implementation-defined per C89/C99. +** But the de facto standard are arithmetic right-shifts on two's +** complement CPUs. This behaviour is required here, so test for it. +*/ +#define BAD_SAR (bsar(-8, 2) != (SBits)-2) + +LUALIB_API int luaopen_bit(lua_State *L) +{ + UBits b; + lua_pushnumber(L, (lua_Number)1437217655L); + b = barg(L, -1); + if (b != (UBits)1437217655L || BAD_SAR) { /* Perform a simple self-test. */ + const char *msg = "compiled with incompatible luaconf.h"; +#ifdef LUA_NUMBER_DOUBLE +#ifdef _WIN32 + if (b == (UBits)1610612736L) + msg = "use D3DCREATE_FPU_PRESERVE with DirectX"; +#endif + if (b == (UBits)1127743488L) + msg = "not compiled with SWAPPED_DOUBLE"; +#endif +DIAG_OFF(unreachable-code) + if (BAD_SAR) + msg = "arithmetic right-shift broken"; +DIAG_ON(unreachable-code) + luaL_error(L, "bit library self-test failed (%s)", msg); + } +#if LUA_VERSION_NUM < 502 + luaL_register(L, "bit", bit_funcs); + return 1; +#else + luaL_newlib(L, bit_funcs); + lua_setglobal(L, "bit"); /* added for wireshark */ + return 0; /* changed from 1 to 0 for wireshark, since lua_setglobal now pops the table */ +#endif +} + diff --git a/epan/wslua/lua_bitop.h b/epan/wslua/lua_bitop.h new file mode 100644 index 00000000..17c242db --- /dev/null +++ b/epan/wslua/lua_bitop.h @@ -0,0 +1,16 @@ +/** @file +** Lua BitOp -- a bit operations library for Lua 5.1/5.2. +** http://bitop.luajit.org/ +** +** Copyright (C) 2008-2012 Mike Pall. All rights reserved. +** +** SPDX-License-Identifier: MIT +** +*/ + +#ifndef _LUA_BITOP_H +#define _LUA_BITOP_H + +extern int luaopen_bit(lua_State *L); + +#endif /* _LUA_BITOP_H */ diff --git a/epan/wslua/make-reg.py b/epan/wslua/make-reg.py new file mode 100755 index 00000000..b8ae77b3 --- /dev/null +++ b/epan/wslua/make-reg.py @@ -0,0 +1,112 @@ +#!/usr/bin/env python3 +# +# make-reg.py +# Registration Macros Generator +# +# Copyright 2022 by Moshe Kaplan +# Based on make-reg.pl by Luis E. Garcia Onatnon <luis.ontanon@gmail.com> +# +# Wireshark - Network traffic analyzer +# By Gerald Combs <gerald@wireshark.org> +# Copyright 1998 Gerald Combs +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import argparse +import re + + +def parse_file(file_content): + # extract classes, functions, and internal functions + # and return them as a tuple + CLASS_PATTERN = r'WSLUA_CLASS_DEFINE(?:_BASE)?\050\s*([A-Za-z0-9]+)' + FUNCTION_PATTERN = r'WSLUA_FUNCTION\s+wslua_([a-z_0-9]+)' + INTERNAL_FUNCTION_PATTERN = r'WSLUA_INTERNAL_FUNCTION\s+wslua_([a-z_0-9]+)' + + class_matches = re.findall(CLASS_PATTERN, file_content) + function_matches = re.findall(FUNCTION_PATTERN, file_content) + internal_function_matches = re.findall(INTERNAL_FUNCTION_PATTERN, file_content) + return class_matches, function_matches, internal_function_matches + + +def generate_declare_wslua_h(classes, functions, internal_functions): + output_lines = [] + output_lines += ["/* This file is automatically generated by make-reg.py; do not edit! */\n"] + output_lines += ["#define WSLUA_DECLARE_CLASSES() \\"] + for lua_class in classes: + output_lines += ["\tWSLUA_CLASS_DECLARE({lua_class});\\".format(lua_class=lua_class)] + + output_lines += ["\n"] + output_lines += ["#define WSLUA_DECLARE_FUNCTIONS() \\"] + for lua_function in functions: + output_lines += ["\tWSLUA_FUNCTION wslua_{lua_function}(lua_State* L);\\".format(lua_function=lua_function)] + + for lua_internal_function in internal_functions: + output_lines += ["\tWSLUA_INTERNAL_FUNCTION wslua_{lua_internal_function}(lua_State* L);\\".format(lua_internal_function=lua_internal_function)] + + output_lines += ["\n"] + output_lines += ["extern void wslua_register_classes(lua_State* L);"] + output_lines += ["extern void wslua_register_functions(lua_State* L);"] + output_lines += ["\n\n"] + return "\n".join(output_lines) + + +def generate_register_wslua_c(classes, functions, internal_functions): + output_lines = [] + output_lines += ["/* This file is automatically generated by make-reg.py; do not edit! */\n"] + output_lines += ['#include "config.h"'] + output_lines += ['#include "wslua.h"\n'] + output_lines += ['#include "lua_bitop.h"\n'] + output_lines += ["static void wslua_reg_module(lua_State* L, const char *name _U_, lua_CFunction func) {"] + output_lines += ["\tlua_pushcfunction(L, func);"] + output_lines += ["\tlua_call(L, 0, 0);"] + output_lines += ["}\n"] + output_lines += ["void wslua_register_classes(lua_State* L) {"] + for lua_class in classes: + output_lines += ["\twslua_reg_module(L, \"{lua_class}\", {lua_class}_register);".format(lua_class=lua_class)] + + output_lines += ["\twslua_reg_module(L, \"bit\", luaopen_bit);"] + output_lines += ["\tlua_pushcfunction(L, luaopen_rex_pcre2);"] + output_lines += ["\tlua_call(L, 0, 1);"] + output_lines += ["\tlua_setglobal(L, \"rex_pcre2\");"] + output_lines += ["}\n"] + + output_lines += ["void wslua_register_functions(lua_State* L) {"] + for lua_function in functions: + output_lines += ["\tWSLUA_REGISTER_FUNCTION({lua_function});".format(lua_function=lua_function)] + + for lua_internal_function in internal_functions: + output_lines += ["\tWSLUA_REGISTER_FUNCTION({lua_internal_function});".format(lua_internal_function=lua_internal_function)] + + output_lines += ["}\n"] + + return "\n".join(output_lines) + + +def main(): + parser = argparse.ArgumentParser(description="Generate the registration macros for Lua code.") + parser.add_argument("files", metavar='files', nargs='+', help="paths to Lua C code") + parsed_args = parser.parse_args() + + lua_classes = [] + lua_functions = [] + lua_internal_functions = [] + for filename in parsed_args.files: + with open(filename, encoding='utf-8') as fh: + class_matches, function_matches, internal_function_matches = parse_file(fh.read()) + lua_classes += class_matches + lua_functions += function_matches + lua_internal_functions += internal_function_matches + + declare_wslua_h_content = generate_declare_wslua_h(lua_classes, lua_functions, lua_internal_functions) + register_wslua_c_content = generate_register_wslua_c(lua_classes, lua_functions, lua_internal_functions) + + with open('register_wslua.c', mode='w', encoding='utf-8') as fh: + fh.write(register_wslua_c_content) + + with open('declare_wslua.h', mode='w', encoding='utf-8') as fh: + fh.write(declare_wslua_h_content) + + +if __name__ == '__main__': + main() diff --git a/epan/wslua/make-taps.py b/epan/wslua/make-taps.py new file mode 100755 index 00000000..14c5a397 --- /dev/null +++ b/epan/wslua/make-taps.py @@ -0,0 +1,215 @@ +#!/usr/bin/env python3 +# +# make-taps.py +# +# By Gerald Combs <gerald@wireshark.org> +# Based on make-taps.pl by Luis E. Garcia Onatnon <luis.ontanon@gmail.com> +# +# Wireshark - Network traffic analyzer +# By Gerald Combs <gerald@wireshark.org> +# Copyright 1998 Gerald Combs +# +# SPDX-License-Identifier: GPL-2.0-or-later +'''\ +Extract structs from C headers to generate a function that pushes a lua table +into the stack containing the elements of the struct. +''' + +import argparse +import configparser +import os +import re +import sys + + +this_dir = os.path.dirname(__file__) + + +def get_tap_info(tap_name, header_file, struct_name, enum_types): + code = f'#include "{header_file}"\n' + doc = f'Tap: {tap_name}\n' + enums = {} + buf = '' + + types = { + 'gchar[]': 'lua_pushstring(L,(const char*)v->STR);', + 'gchar*': 'lua_pushstring(L,(const char*)v->STR);', + 'guint': 'lua_pushnumber(L,(lua_Number)v->STR);', + 'guint8': 'lua_pushnumber(L,(lua_Number)v->STR);', + 'guint16': 'lua_pushnumber(L,(lua_Number)v->STR);', + 'guint32': 'lua_pushnumber(L,(lua_Number)v->STR);', + 'gint': 'lua_pushnumber(L,(lua_Number)v->STR);', + 'gint8': 'lua_pushnumber(L,(lua_Number)v->STR);', + 'gint16': 'lua_pushnumber(L,(lua_Number)v->STR);', + 'gint32': 'lua_pushnumber(L,(lua_Number)v->STR);', + 'gboolean': 'lua_pushboolean(L,(int)v->STR);', + 'address': '{ Address a = (Address)g_malloc(sizeof(address)); copy_address(a, &(v->STR)); pushAddress(L,a); }', + 'address*': '{ Address a = (Address)g_malloc(sizeof(address)); copy_address(a, v->STR); pushAddress(L,a); }', + 'int': 'lua_pushnumber(L,(lua_Number)v->STR);', + 'nstime_t': 'lua_pushnumber(L,(lua_Number)nstime_to_sec(&(v->STR)));', + 'nstime_t*': 'lua_pushnumber(L,(lua_Number)nstime_to_sec(v->STR));', + } + + comments = { + 'gchar[]': 'string', + 'gchar*': 'string', + 'guint': 'number', + 'guint8': 'number', + 'guint16': 'number', + 'guint32': 'number', + 'gint': 'number', + 'gint8': 'number', + 'gint16': 'number', + 'gint32': 'number', + 'gboolean': 'boolean', + 'address': 'Address', + 'address*': 'Address', + 'int': 'number', + 'nstime_t': 'number (seconds, since 1-1-1970 if absolute)', + 'nstime_t*': 'number (seconds, since 1-1-1970 if absolute)', + } + + with open(os.path.join(this_dir, header_file), encoding='utf-8') as header_f: + for line in header_f: + # Remove comments + line = re.sub(r'\/\*.*?\*/', '', line) + line = re.sub(r'//.*', '', line) + buf += line + + for enum in enum_types: + m = re.search(fr'typedef\s+enum[^{{]*{{([^}}]*)}}[\s\n]*{enum}[\s\n]*;', buf, flags=re.DOTALL) + if m: + types[enum] = f'lua_pushnumber(L,(lua_Number)v->STR); /* {enum} */' + econsts = m.group(1).splitlines() + econsts = [re.sub(r'\s+', '', item) for item in econsts] + econsts = [re.sub(',', '', item) for item in econsts] + econsts = [item for item in econsts if item] + enums[enum] = econsts + ebody = '|'.join(econsts) + comments[enum] = f'{enum}: {{ {ebody} }}' + + m = re.search(fr'typedef\s+struct.*?{{([^}}]*)}}[\s\n]*({struct_name})[\s\n]*;', buf, flags=re.DOTALL) + if not m: + sys.stderr.write(f'could not find typedef {struct_name} in {header_file}') + sys.exit(1) + + body = m.group(1) + + elems = {} + + for line in body.splitlines(): + k = None + v = None + + m = re.search(r'\s*(.*?)([\w\d_]+)\s*\[\s*\d+\s*\]\s*;', line) + if m: + k = m.group(2) + v = m.group(1) + v += '[]' + + m = re.search(r'\s*(.*?)([\w\d_]+)\s*;', line) + if m: + k = m.group(2) + v = m.group(1) + + if v and k: + v = re.sub(r'const ', '', v) + v = re.sub(r'\s+', '', v) + elems[k] = v + + code += f'static void wslua_{tap_name}_to_table(lua_State* L, const void* p) {{\n\tconst {struct_name}* v;\n\n\tv = (const {struct_name}*)p;\n\tlua_newtable(L);\n\n' + + for el in sorted(elems): + try: + fmt = types[elems[el]] + code += f'\tlua_pushstring(L,\"{el}\");\n\t' + lua_type = re.sub(r'\bSTR\b', el, fmt) + code += lua_type + code += '\n\tlua_settable(L,-3);\n' + doc += f'\t{el}: {comments[elems[el]]}\n' + except KeyError: + pass + + code += "}\n\n" + doc += "\n" + + return (code, doc, enums) + + +def main(): + parser = argparse.ArgumentParser(description="Generate bindings required for Lua taps.") + parser.add_argument("out_c", metavar='C file', help="output C file") + parser.add_argument("out_doc", metavar='documentation file', help="output text file") + args = parser.parse_args() + + tap_config = configparser.ConfigParser() + tap_config.read(os.path.join(this_dir, 'taps.ini')) + + enums = {} + c_body = '''\ +/* This file is autogenerated from ./taps by ./make-taps.py */ +/* DO NOT EDIT! */ + +#include "config.h" + +#include "wslua.h" + +#include <wsutil/nstime.h> + +''' + doc_body = '\n' + + for tap_name in tap_config.sections(): + tap_d = tap_config[tap_name] + enum_types = [] + if 'enum_types' in tap_d.keys(): + enum_types = tap_d['enum_types'].split(' ') + (code, doc, file_enums) = get_tap_info(tap_name, tap_d['header_file'], tap_d['struct_name'], enum_types) + c_body += code + doc_body += doc + enums.update(file_enums) + + c_body += 'static tappable_t tappables[] = {\n' + for tap_name in sorted(tap_config.sections()): + c_body += f'\t{{"{tap_name}", wslua_{tap_name}_to_table }},\n' + c_body += '''\ + {"frame",NULL}, + {NULL,NULL} +}; +''' + + c_body += '\nint wslua_set_tap_enums(lua_State* L) {\n' + for enum in sorted(enums): + c_body += f'\n\t/*\n\t * {enum}\n\t */\n\tlua_newtable(L);\n' + for econst in enums[enum]: + c_body += f'''\ + lua_pushnumber(L,(lua_Number){econst}); + lua_setglobal(L,"{econst}"); + lua_pushnumber(L,(lua_Number){econst}); + lua_pushstring(L,"{econst}"); + lua_settable(L,-3); +''' + c_body += f'\tlua_setglobal(L,\"{enum}\");\n' + c_body += '\treturn 0;\n}\n' + + c_body += '''\ + + +tap_extractor_t wslua_get_tap_extractor(const gchar* name) { + tappable_t* t; + for(t = tappables; t->name; t++ ) { + if (g_str_equal(t->name,name)) return t->extractor; + } + + return NULL; +} +''' + + with open(args.out_c, mode='w', encoding='utf-8') as out_c_f: + out_c_f.write(c_body) + + with open(args.out_doc, mode='w', encoding='utf-8') as out_doc_f: + out_doc_f.write(doc_body) + +if __name__ == '__main__': + main() diff --git a/epan/wslua/taps.ini b/epan/wslua/taps.ini new file mode 100644 index 00000000..c7ae8958 --- /dev/null +++ b/epan/wslua/taps.ini @@ -0,0 +1,209 @@ +# taps.ini +# Instructions for make-taps.py to generate the taps.c file. +# Based on the "taps" configuration file for make-taps.pl +# +# (c) 2006 Luis E. Garcia Ontanon <luis@ontanon.org> +# +# Wireshark - Network traffic analyzer +# By Gerald Combs <gerald@wireshark.org> +# Copyright 2006 Gerald Combs +# +# SPDX-License-Identifier: GPL-2.0-or-later + +# Each section is a tap type (the data structure passed by dissectors). +# Sections have the following format: +# [tapname] +# header_file: <path to dissector header> +# struct_name: <name of struct typedef> +# enum_types: <space-separated list of enum types, optional> + +# [frame] +# header_file: ../dissectors/packet_frame.h +# struct_name: void + +[ip] +header_file: ../dissectors/packet-ip.h +struct_name: ws_ip4 + +[udp] +header_file: ../dissectors/packet-udp.h +struct_name: e_udphdr + +[http] +header_file: ../dissectors/packet-http.h +struct_name: http_info_value_t + +# BACnet statistics +[bacapp] +header_file: ../dissectors/packet-bacapp.h +struct_name: bacapp_info_value_t + +[h225] +header_file: ../dissectors/packet-h225.h +struct_name: h225_packet_info +enum_types: h225_msg_type h225_cs_type + +[actrace] +header_file: ../dissectors/packet-actrace.h +struct_name: actrace_info_t + +# [afp] +# header_file: ../dissectors/packet-afp.h +# struct_name: + +[ansi_a] +header_file: ../dissectors/packet-ansi_a.h +struct_name: ansi_a_tap_rec_t + +[ansi_map] +header_file: ../dissectors/packet-ansi_map.h +struct_name: ansi_map_tap_rec_t + +# [bootp] +# header_file: ../dissectors/packet-bootp.h +# struct_name: bootp_info_t + +# [dcerpc] +# header_file: ../dissectors/packet-dcerpc.h +# struct_name: dcerpc_info_t + +# [dccp] +# header_file: ../dissectors/packet-dccp.h +# struct_name: dccp_info_t + +# [dtls] +# header_file: ../dissectors/packet-dtls.h +# struct_name: dtls_info_t + +# [epl] +# header_file: ../dissectors/packet-epl.h +# struct_name: epl_info_t + +[eth] +header_file: ../dissectors/packet-eth.h +struct_name: eth_hdr + +# [fc] +# header_file: ../dissectors/packet-fc.h +# struct_name: fc_hdr + +# [gsm_a] +# header_file: ../dissectors/packet-gsm_a.h +# struct_name: gsm_a_info_t + +# [gsm_map] +# header_file: ../dissectors/packet-gsm_map.h +# struct_name: gsm_map_info_t + +# [h245] +# header_file: ../dissectors/packet-h245.h +# struct_name: h245_info_t + +# [h245dg] +# header_file: ../dissectors/packet-h245dg.h +# struct_name: h245dg_info_t + +# [ipx] +# header_file: ../dissectors/packet-ipx.h +# struct_name: ipx_info_t + +# [isup] +# header_file: ../dissectors/packet-isup.h +# struct_name: isup_info_t + +# [jxta] +# header_file: ../dissectors/packet-jxta.h +# struct_name: jxta_info_t + +[ldap] +header_file: ../dissectors/packet-ldap.h +struct_name: ldap_call_response_t + +# [mtp3] +# header_file: ../dissectors/packet-mtp3.h +# struct_name: mtp3_info_t + +# [ncp_srt] +# header_file: ../dissectors/packet-ncp_srt.h +# struct_name: ncp_srt_info_t + +# [ncp_hdr] +# header_file: ../dissectors/packet-ncp_hdr.h +# struct_name: ncp_hdr_info_t + +# [ntlmssp] +# header_file: ../dissectors/packet-ntlmssp.h +# struct_name: ntlmssp_info_t + +# [q931] +# header_file: ../dissectors/packet-q931.h +# struct_name: q931_info_t + +# [rpc] +# header_file: ../dissectors/packet-rpc.h +# struct_name: rpc_info_t + +# [rsvp] +# header_file: ../dissectors/packet-rsvp.h +# struct_name: rsvp_info_t + +# [rtpevent] +# header_file: ../dissectors/packet-rtpevent.h +# struct_name: rtpevent_info_t + +# [rtp] +# header_file: ../dissectors/packet-rtp.h +# struct_name: rtp_info_t + +# [scsi] +# header_file: ../dissectors/packet-scsi.h +# struct_name: scsi_info_t + +# [sctp] +# header_file: ../dissectors/packet-sctp.h +# struct_name: sctp_info_t + +# [sdp] +# header_file: ../dissectors/packet-sdp.h +# struct_name: sdp_info_t + +# [sip] +# header_file: ../dissectors/packet-sip.h +# struct_name: sip_info_t + +[smb] +header_file: ../dissectors/packet-smb.h +struct_name: smb_info_t + +[smb2] +header_file: ../dissectors/packet-smb2.h +struct_name: smb2_info_t + +# [t38] +# header_file: ../dissectors/packet-t38.h +# struct_name: t38_info_t + +[tcp] +header_file: ../dissectors/packet-tcp.h +struct_name: tcp_info_t + +# [teredo] +# header_file: ../dissectors/packet-teredo.h +# struct_name: teredo_info_t + +# [tls] +# header_file: ../dissectors/packet-tls.h +# struct_name: ssl_info_t + +# [tr] +# header_file: ../dissectors/packet-tr.h +# struct_name: tr_info_t + +[wlan] +header_file: ../dissectors/packet-ieee80211.h +struct_name: wlan_hdr_t + +# [wsp] +# header_file: ../dissectors/packet-wsp.h +# struct_name: wsp_info_t + diff --git a/epan/wslua/wslua.h b/epan/wslua/wslua.h new file mode 100644 index 00000000..0f6a146e --- /dev/null +++ b/epan/wslua/wslua.h @@ -0,0 +1,834 @@ +/* + * wslua.h + * + * Wireshark's interface to the Lua Programming Language + * + * (c) 2006, Luis E. Garcia Ontanon <luis@ontanon.org> + * (c) 2007, Tamas Regos <tamas.regos@ericsson.com> + * (c) 2008, Balint Reczey <balint.reczey@ericsson.com> + * + * Wireshark - Network traffic analyzer + * By Gerald Combs <gerald@wireshark.org> + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef _PACKET_LUA_H +#define _PACKET_LUA_H + +#include <glib.h> +#include <stdlib.h> +#include <string.h> +#include <math.h> +#include <lua.h> +#include <lualib.h> +#include <lauxlib.h> + +#include <ws_log_defs.h> + +#include <wiretap/wtap.h> + +#include <wsutil/report_message.h> +#include <wsutil/nstime.h> +#include <wsutil/ws_assert.h> +#include <wsutil/wslog.h> + +#include <epan/packet.h> +#include <epan/strutil.h> +#include <epan/to_str.h> +#include <epan/prefs.h> +#include <epan/proto.h> +#include <epan/epan_dissect.h> +#include <epan/tap.h> +#include <epan/column-utils.h> +#include <wsutil/filesystem.h> +#include <epan/funnel.h> +#include <epan/tvbparse.h> +#include <epan/epan.h> +#include <epan/expert.h> + +#include <epan/wslua/declare_wslua.h> + +/** @file + * @ingroup wslua_group + */ + +#define WSLUA_INIT_ROUTINES "init_routines" +#define WSLUA_PREFS_CHANGED "prefs_changed" + +/* type conversion macros - lua_Number is a double, so casting isn't kosher; and + using Lua's already-available lua_tointeger() and luaL_checkinteger() might be different + on different machines; so use these instead please! */ +#define wslua_togint(L,i) (gint) ( lua_tointeger(L,i) ) +#define wslua_togint32(L,i) (gint32) ( lua_tonumber(L,i) ) +#define wslua_togint64(L,i) (gint64) ( lua_tonumber(L,i) ) +#define wslua_toguint(L,i) (guint) ( lua_tointeger(L,i) ) +#define wslua_toguint32(L,i) (guint32) ( lua_tonumber(L,i) ) +#define wslua_toguint64(L,i) (guint64) ( lua_tonumber(L,i) ) + +#define wslua_checkgint(L,i) (gint) ( luaL_checkinteger(L,i) ) +#define wslua_checkgint32(L,i) (gint32) ( luaL_checknumber(L,i) ) +#define wslua_checkgint64(L,i) (gint64) ( luaL_checknumber(L,i) ) +#define wslua_checkguint(L,i) (guint) ( luaL_checkinteger(L,i) ) +#define wslua_checkguint32(L,i) (guint32) ( luaL_checknumber(L,i) ) +#define wslua_checkguint64(L,i) (guint64) ( luaL_checknumber(L,i) ) + +#define wslua_optgint(L,i,d) (gint) ( luaL_optinteger(L,i,d) ) +#define wslua_optgint32(L,i,d) (gint32) ( luaL_optnumber(L,i,d) ) +#define wslua_optgint64(L,i,d) (gint64) ( luaL_optnumber(L,i,d) ) +#define wslua_optguint(L,i,d) (guint) ( luaL_optinteger(L,i,d) ) +#define wslua_optguint32(L,i,d) (guint32) ( luaL_optnumber(L,i,d) ) +#define wslua_optguint64(L,i,d) (guint64) ( luaL_optnumber(L,i,d) ) + + +struct _wslua_tvb { + tvbuff_t* ws_tvb; + gboolean expired; + gboolean need_free; +}; + +struct _wslua_pinfo { + packet_info* ws_pinfo; + gboolean expired; +}; + +struct _wslua_tvbrange { + struct _wslua_tvb* tvb; + int offset; + int len; +}; + +struct _wslua_tw { + funnel_text_window_t* ws_tw; + gboolean expired; + void* close_cb_data; +}; + +typedef struct _wslua_field_t { + int hfid; + int ett; + char* name; + char* abbrev; + char* blob; + enum ftenum type; + unsigned base; + const void* vs; + guint64 mask; +} wslua_field_t; + +typedef struct _wslua_expert_field_t { + expert_field ids; + const gchar *abbrev; + const gchar *text; + int group; + int severity; +} wslua_expert_field_t; + +/** + * PREF_OBSOLETE is used for preferences that a module used to support + * but no longer supports; we give different error messages for them. + */ +typedef enum { + PREF_UINT, + PREF_BOOL, + PREF_ENUM, + PREF_STRING, + PREF_RANGE, + PREF_STATIC_TEXT, + PREF_OBSOLETE +} pref_type_t; + +typedef struct _wslua_pref_t { + gchar* name; + gchar* label; + gchar* desc; + pref_type_t type; + union { + gboolean b; + guint u; + gchar* s; + gint e; + range_t *r; + void* p; + } value; + union { + guint32 max_value; /**< maximum value of a range */ + struct { + const enum_val_t *enumvals; /**< list of name & values */ + gboolean radio_buttons; /**< TRUE if it should be shown as + radio buttons rather than as an + option menu or combo box in + the preferences tab */ + } enum_info; /**< for PREF_ENUM */ + gchar* default_s; /**< default value for value.s */ + } info; /**< display/text file information */ + + struct _wslua_pref_t* next; + struct _wslua_proto_t* proto; + int ref; /* Reference to enable Proto to deregister prefs. */ +} wslua_pref_t; + +typedef struct _wslua_proto_t { + gchar* name; + gchar* loname; + gchar* desc; + int hfid; + int ett; + wslua_pref_t prefs; + int fields; + int expert_info_table_ref; + expert_module_t *expert_module; + module_t *prefs_module; + dissector_handle_t handle; + GArray *hfa; + GArray *etta; + GArray *eia; + gboolean is_postdissector; + gboolean expired; +} wslua_proto_t; + +struct _wslua_distbl_t { + dissector_table_t table; + const gchar* name; + const gchar* ui_name; + gboolean created; + gboolean expired; +}; + +struct _wslua_col_info { + column_info* cinfo; + gint col; + gboolean expired; +}; + +struct _wslua_cols { + column_info* cinfo; + gboolean expired; +}; + +struct _wslua_private_table { + GHashTable *table; + gboolean is_allocated; + gboolean expired; +}; + +struct _wslua_treeitem { + proto_item* item; + proto_tree* tree; + gboolean expired; +}; + +// Internal structure for wslua_field.c to track info about registered fields. +struct _wslua_header_field_info { + char *name; + header_field_info *hfi; +}; + +struct _wslua_field_info { + field_info *ws_fi; + gboolean expired; +}; + +typedef void (*tap_extractor_t)(lua_State*,const void*); + +struct _wslua_tap { + gchar* name; + gchar* filter; + tap_extractor_t extractor; + lua_State* L; + int packet_ref; + int draw_ref; + int reset_ref; + gboolean all_fields; +}; + +/* 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 { + wtap_rec *rec; /* 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 wtap_rec *rec; + const guint8 *pd; + gboolean expired; +}; + +struct _wslua_filehandler { + struct file_type_subtype_info finfo; + gboolean is_reader; + gboolean is_writer; + gchar* internal_description; /* XXX - this is redundant; finfo.description should suffice */ + 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; + gboolean removed; /* This is set during reload Lua plugins */ +}; + +struct _wslua_dir { + GDir* dir; + char* ext; +}; + +struct _wslua_progdlg { + struct progdlg* pw; + char* title; + char* task; + gboolean stopped; +}; + +typedef struct { const char* name; tap_extractor_t extractor; } tappable_t; + +typedef struct {const gchar* str; enum ftenum id; } wslua_ft_types_t; + +typedef wslua_pref_t* Pref; +typedef wslua_pref_t* Prefs; +typedef struct _wslua_field_t* ProtoField; +typedef struct _wslua_expert_field_t* ProtoExpert; +typedef struct _wslua_proto_t* Proto; +typedef struct _wslua_distbl_t* DissectorTable; +typedef dissector_handle_t Dissector; +typedef GByteArray* ByteArray; +typedef struct _wslua_tvb* Tvb; +typedef struct _wslua_tvbrange* TvbRange; +typedef struct _wslua_col_info* Column; +typedef struct _wslua_cols* Columns; +typedef struct _wslua_pinfo* Pinfo; +typedef struct _wslua_treeitem* TreeItem; +typedef address* Address; +typedef nstime_t* NSTime; +typedef gint64 Int64; +typedef guint64 UInt64; +typedef struct _wslua_header_field_info* Field; +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; +typedef tvbparse_wanted_t* Rule; +typedef tvbparse_elem_t* Node; +typedef tvbparse_action_t* Shortcut; +typedef struct _wslua_dir* Dir; +typedef struct _wslua_private_table* PrivateTable; +typedef gchar* Struct; + +/* + * toXxx(L,idx) gets a Xxx from an index (Lua Error if fails) + * checkXxx(L,idx) gets a Xxx from an index after calling check_code (No Lua Error if it fails) + * pushXxx(L,xxx) pushes an Xxx into the stack + * isXxx(L,idx) tests whether we have an Xxx at idx + * shiftXxx(L,idx) removes and returns an Xxx from idx only if it has a type of Xxx, returns NULL otherwise + * WSLUA_CLASS_DEFINE must be used with a trailing ';' + * (a dummy typedef is used to be syntactically correct) + */ +#define WSLUA_CLASS_DEFINE(C,check_code) \ + WSLUA_CLASS_DEFINE_BASE(C,check_code,NULL) + +#define WSLUA_CLASS_DEFINE_BASE(C,check_code,retval) \ +C to##C(lua_State* L, int idx) { \ + C* v = (C*)lua_touserdata (L, idx); \ + if (!v) luaL_error(L, "bad argument %d (%s expected, got %s)", idx, #C, lua_typename(L, lua_type(L, idx))); \ + return v ? *v : retval; \ +} \ +C check##C(lua_State* L, int idx) { \ + C* p; \ + luaL_checktype(L,idx,LUA_TUSERDATA); \ + p = (C*)luaL_checkudata(L, idx, #C); \ + check_code; \ + return p ? *p : retval; \ +} \ +C* push##C(lua_State* L, C v) { \ + C* p; \ + luaL_checkstack(L,2,"Unable to grow stack\n"); \ + p = (C*)lua_newuserdata(L,sizeof(C)); *p = v; \ + luaL_getmetatable(L, #C); lua_setmetatable(L, -2); \ + return p; \ +}\ +gboolean is##C(lua_State* L,int i) { \ + void *p; \ + if(!lua_isuserdata(L,i)) return FALSE; \ + p = lua_touserdata(L, i); \ + lua_getfield(L, LUA_REGISTRYINDEX, #C); \ + if (p == NULL || !lua_getmetatable(L, i) || !lua_rawequal(L, -1, -2)) p=NULL; \ + lua_pop(L, 2); \ + return p ? TRUE : FALSE; \ +} \ +C shift##C(lua_State* L,int i) { \ + C* p; \ + if(!lua_isuserdata(L,i)) return retval; \ + p = (C*)lua_touserdata(L, i); \ + lua_getfield(L, LUA_REGISTRYINDEX, #C); \ + if (p == NULL || !lua_getmetatable(L, i) || !lua_rawequal(L, -1, -2)) p=NULL; \ + lua_pop(L, 2); \ + if (p) { lua_remove(L,i); return *p; }\ + else return retval;\ +} \ +typedef int dummy##C + +typedef struct _wslua_attribute_table { + const gchar *fieldname; + lua_CFunction getfunc; + lua_CFunction setfunc; +} wslua_attribute_table; +extern int wslua_reg_attributes(lua_State *L, const wslua_attribute_table *t, gboolean is_getter); + +#define WSLUA_TYPEOF_FIELD "__typeof" + +#ifdef HAVE_LUA + +/* temporary transition macro to reduce duplication in WSLUA_REGISTER_xxx. */ +#define WSLUA_REGISTER_GC(C) \ + luaL_getmetatable(L, #C); \ + /* add the '__gc' metamethod with a C-function named Class__gc */ \ + /* this will force ALL wslua classes to have a Class__gc function defined, which is good */ \ + lua_pushcfunction(L, C ## __gc); \ + lua_setfield(L, -2, "__gc"); \ + /* pop the metatable */ \ + lua_pop(L, 1) + +#define __WSLUA_REGISTER_META(C, ATTRS) { \ + const wslua_class C ## _class = { \ + .name = #C, \ + .instance_meta = C ## _meta, \ + .attrs = ATTRS \ + }; \ + wslua_register_classinstance_meta(L, &C ## _class); \ + WSLUA_REGISTER_GC(C); \ +} + +#define WSLUA_REGISTER_META(C) __WSLUA_REGISTER_META(C, NULL) +#define WSLUA_REGISTER_META_WITH_ATTRS(C) \ + __WSLUA_REGISTER_META(C, C ## _attributes) + +#define __WSLUA_REGISTER_CLASS(C, ATTRS) { \ + const wslua_class C ## _class = { \ + .name = #C, \ + .class_methods = C ## _methods, \ + .class_meta = C ## _meta, \ + .instance_methods = C ## _methods, \ + .instance_meta = C ## _meta, \ + .attrs = ATTRS \ + }; \ + wslua_register_class(L, &C ## _class); \ + WSLUA_REGISTER_GC(C); \ +} + +#define WSLUA_REGISTER_CLASS(C) __WSLUA_REGISTER_CLASS(C, NULL) +#define WSLUA_REGISTER_CLASS_WITH_ATTRS(C) \ + __WSLUA_REGISTER_CLASS(C, C ## _attributes) + +#define WSLUA_INIT(L) \ + luaL_openlibs(L); \ + wslua_register_classes(L); \ + wslua_register_functions(L); + +#endif + +#define WSLUA_FUNCTION extern int +/* This is for functions intended only to be used in init.lua */ +#define WSLUA_INTERNAL_FUNCTION extern int + +#define WSLUA_REGISTER_FUNCTION(name) { lua_pushcfunction(L, wslua_## name); lua_setglobal(L, #name); } + +#define WSLUA_REGISTER extern int + +#define WSLUA_METHOD static int +#define WSLUA_CONSTRUCTOR static int +#define WSLUA_ATTR_SET static int +#define WSLUA_ATTR_GET static int +#define WSLUA_METAMETHOD static int + +#define WSLUA_METHODS static const luaL_Reg +#define WSLUA_META static const luaL_Reg +#define WSLUA_CLASS_FNREG(class,name) { #name, class##_##name } +#define WSLUA_CLASS_FNREG_ALIAS(class,aliasname,name) { #aliasname, class##_##name } +#define WSLUA_CLASS_MTREG(class,name) { "__" #name, class##__##name } + +#define WSLUA_ATTRIBUTES static const wslua_attribute_table +/* following are useful macros for the rows in the array created by above */ +#define WSLUA_ATTRIBUTE_RWREG(class,name) { #name, class##_get_##name, class##_set_##name } +#define WSLUA_ATTRIBUTE_ROREG(class,name) { #name, class##_get_##name, NULL } +#define WSLUA_ATTRIBUTE_WOREG(class,name) { #name, NULL, class##_set_##name } + +#define WSLUA_ATTRIBUTE_FUNC_SETTER(C,field) \ + static int C##_set_##field (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; \ + } \ + /* silly little trick so we can add a semicolon after this macro */ \ + typedef void __dummy##C##_set_##field + +#define WSLUA_ATTRIBUTE_GET(C,name,block) \ + static int C##_get_##name (lua_State* L) { \ + C obj = check##C (L,1); \ + block \ + return 1; \ + } \ + /* silly little trick so we can add a semicolon after this macro */ \ + typedef void __dummy##C##_get_##name + +#define WSLUA_ATTRIBUTE_NAMED_BOOLEAN_GETTER(C,name,member) \ + WSLUA_ATTRIBUTE_GET(C,name,{lua_pushboolean(L, obj->member );}) + +#define WSLUA_ATTRIBUTE_NAMED_NUMBER_GETTER(C,name,member) \ + WSLUA_ATTRIBUTE_GET(C,name,{lua_pushnumber(L,(lua_Number)(obj->member));}) + +#define WSLUA_ATTRIBUTE_NUMBER_GETTER(C,member) \ + WSLUA_ATTRIBUTE_NAMED_NUMBER_GETTER(C,member,member) + +#define WSLUA_ATTRIBUTE_BLOCK_NUMBER_GETTER(C,name,block) \ + WSLUA_ATTRIBUTE_GET(C,name,{lua_pushnumber(L,(lua_Number)(block));}) + +#define WSLUA_ATTRIBUTE_NAMED_STRING_GETTER(C,name,member) \ + WSLUA_ATTRIBUTE_GET(C,name, { \ + lua_pushstring(L,obj->member); /* this pushes nil if obj->member is null */ \ + }) + +#define WSLUA_ATTRIBUTE_STRING_GETTER(C,member) \ + WSLUA_ATTRIBUTE_NAMED_STRING_GETTER(C,member,member) + +#define WSLUA_ATTRIBUTE_NAMED_OPT_BLOCK_STRING_GETTER(C,name,member,option) \ + WSLUA_ATTRIBUTE_GET(C,name, { \ + char* str; \ + if ((obj->member) && (obj->member->len > 0)) { \ + if (wtap_block_get_string_option_value(g_array_index(obj->member, wtap_block_t, 0), option, &str) == WTAP_OPTTYPE_SUCCESS) { \ + lua_pushstring(L,str); \ + } \ + } \ + }) + +/* + * XXX - we need to support Lua programs getting instances of a "multiple + * allowed" option other than the first option. + */ +#define WSLUA_ATTRIBUTE_NAMED_OPT_BLOCK_NTH_STRING_GETTER(C,name,member,option) \ + WSLUA_ATTRIBUTE_GET(C,name, { \ + char* str; \ + if ((obj->member) && (obj->member->len > 0)) { \ + if (wtap_block_get_nth_string_option_value(g_array_index(obj->member, wtap_block_t, 0), option, 0, &str) == WTAP_OPTTYPE_SUCCESS) { \ + lua_pushstring(L,str); \ + } \ + } \ + }) + +#define WSLUA_ATTRIBUTE_SET(C,name,block) \ + static int C##_set_##name (lua_State* L) { \ + C obj = check##C (L,1); \ + block; \ + return 0; \ + } \ + /* silly little trick so we can add a semicolon after this macro */ \ + typedef void __dummy##C##_set_##name + +#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) \ + WSLUA_ATTRIBUTE_SET(C,name, { \ + if (! lua_isnumber(L,-1) ) \ + return luaL_error(L, "%s's attribute `%s' must be a number", #C , #name ); \ + obj->member = (cast) wslua_togint32(L,-1); \ + }) + +#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) \ + g_free((void*) obj->member); \ + obj->member = s; \ + return 0; \ + } \ + /* silly little trick so we can add a semicolon after this macro */ \ + typedef void __dummy##C##_set_##field + +#define WSLUA_ATTRIBUTE_STRING_SETTER(C,field,need_free) \ + WSLUA_ATTRIBUTE_NAMED_STRING_SETTER(C,field,field,need_free) + +#define WSLUA_ATTRIBUTE_NAMED_OPT_BLOCK_STRING_SETTER(C,field,member,option) \ + 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) && (obj->member->len > 0)) { \ + wtap_block_set_string_option_value(g_array_index(obj->member, wtap_block_t, 0), option, s, strlen(s)); \ + } \ + g_free(s); \ + return 0; \ + } \ + /* silly little trick so we can add a semicolon after this macro */ \ + typedef void __dummy##C##_set_##field + +#define WSLUA_ATTRIBUTE_NAMED_OPT_BLOCK_NTH_STRING_SETTER(C,field,member,option) \ + 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) && (obj->member->len > 0)) { \ + wtap_block_set_nth_string_option_value(g_array_index(obj->member, wtap_block_t, 0), option, 0, s, strlen(s)); \ + } \ + g_free(s); \ + return 0; \ + } \ + /* silly little trick so we can add a semicolon after this macro */ \ + typedef void __dummy##C##_set_##field + +#define WSLUA_ERROR(name,error) { luaL_error(L, "%s%s", #name ": " ,error); } +#define WSLUA_ARG_ERROR(name,attr,error) { luaL_argerror(L,WSLUA_ARG_ ## name ## _ ## attr, #name ": " error); } +#define WSLUA_OPTARG_ERROR(name,attr,error) { luaL_argerror(L,WSLUA_OPTARG_##name##_ ##attr, #name ": " error); } + +#define WSLUA_REG_GLOBAL_BOOL(L,n,v) { lua_pushboolean(L,v); lua_setglobal(L,n); } +#define WSLUA_REG_GLOBAL_STRING(L,n,v) { lua_pushstring(L,v); lua_setglobal(L,n); } +#define WSLUA_REG_GLOBAL_NUMBER(L,n,v) { lua_pushnumber(L,v); lua_setglobal(L,n); } + +#define WSLUA_RETURN(i) return (i) + +#define WSLUA_API extern + +/* 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_OR_EXPIRED(s) if (!*p) { \ + luaL_argerror(L,idx,"null " s); \ + } else if ((*p)->expired) { \ + luaL_argerror(L,idx,"expired " s); \ + } + +/* Clears or marks references that connects Lua to Wireshark structures */ +#define CLEAR_OUTSTANDING(C, marker, marker_val) void clear_outstanding_##C(void) { \ + while (outstanding_##C->len) { \ + C p = (C)g_ptr_array_remove_index_fast(outstanding_##C,0); \ + if (p) { \ + if (p->marker != marker_val) \ + p->marker = marker_val; \ + else \ + g_free(p); \ + } \ + } \ +} + +#define WSLUA_CLASS_DECLARE(C) \ +extern C to##C(lua_State* L, int idx); \ +extern C check##C(lua_State* L, int idx); \ +extern C* push##C(lua_State* L, C v); \ +extern int C##_register(lua_State* L); \ +extern gboolean is##C(lua_State* L,int i); \ +extern C shift##C(lua_State* L,int i) + + +/* Throws a Wireshark exception, catchable via normal exceptions.h routines. */ +#define THROW_LUA_ERROR(...) \ + THROW_FORMATTED(DissectorError, __VA_ARGS__) + +/* Catches any Wireshark exceptions in code and convert it into a Lua error. + * Normal restrictions for TRY/CATCH apply, in particular, do not return! */ +#define WRAP_NON_LUA_EXCEPTIONS(code) \ +{ \ + volatile gboolean has_error = FALSE; \ + TRY { \ + code \ + } CATCH_ALL { \ + lua_pushstring(L, GET_MESSAGE); \ + has_error = TRUE; \ + } ENDTRY; \ + if (has_error) { lua_error(L); } \ +} + + +extern packet_info* lua_pinfo; +extern TreeItem lua_tree; +extern tvbuff_t* lua_tvb; +extern gboolean lua_initialized; +extern int lua_dissectors_table_ref; +extern int lua_heur_dissectors_table_ref; + +WSLUA_DECLARE_CLASSES() +WSLUA_DECLARE_FUNCTIONS() + +extern lua_State* wslua_state(void); + + +/* wslua_internals.c */ +/** + * @brief Type for defining new classes. + * + * A new class is defined as a Lua table type. Instances of this class are + * created through pushXxx which sets the appropriate metatable. + */ +typedef struct _wslua_class { + const char *name; /**< Class name that is exposed to Lua code. */ + const luaL_Reg *class_methods; /**< Methods for the static class (optional) */ + const luaL_Reg *class_meta; /**< Metatable for the static class (optional) */ + const luaL_Reg *instance_methods; /**< Methods for class instances. (optional) */ + const luaL_Reg *instance_meta; /**< Metatable for class instances (optional) */ + const wslua_attribute_table *attrs; /**< Table of getters/setters for attributes on class instances (optional). */ +} wslua_class; +void wslua_register_classinstance_meta(lua_State *L, const wslua_class *cls_def); +void wslua_register_class(lua_State *L, const wslua_class *cls_def); + +extern int wslua__concat(lua_State* L); +extern gboolean wslua_toboolean(lua_State* L, int n); +extern gboolean wslua_checkboolean(lua_State* L, int n); +extern gboolean wslua_optbool(lua_State* L, int n, gboolean def); +extern lua_Integer wslua_tointeger(lua_State* L, int n); +extern int wslua_optboolint(lua_State* L, int n, int def); +extern const char* wslua_checklstring_only(lua_State* L, int n, size_t *l); +extern const char* wslua_checkstring_only(lua_State* L, int n); +extern void wslua_setfuncs(lua_State *L, const luaL_Reg *l, int nup); +extern const gchar* wslua_typeof_unknown; +extern const gchar* wslua_typeof(lua_State *L, int idx); +extern gboolean wslua_get_table(lua_State *L, int idx, const gchar *name); +extern gboolean wslua_get_field(lua_State *L, int idx, const gchar *name); +extern int dissect_lua(tvbuff_t* tvb, packet_info* pinfo, proto_tree* tree, void* data); +extern int heur_dissect_lua(tvbuff_t* tvb, packet_info* pinfo, proto_tree* tree, void* data); +extern expert_field* wslua_get_expert_field(const int group, const int severity); +extern void wslua_prefs_changed(void); +extern void proto_register_lua(void); +extern GString* lua_register_all_taps(void); +extern void wslua_prime_dfilter(epan_dissect_t *edt); +extern gboolean wslua_has_field_extractors(void); +extern void lua_prime_all_fields(proto_tree* tree); + +extern int Proto_commit(lua_State* L); + +extern TreeItem create_TreeItem(proto_tree* tree, proto_item* item); + +extern void clear_outstanding_FuncSavers(void); + +extern void Int64_pack(lua_State* L, luaL_Buffer *b, gint idx, gboolean asLittleEndian); +extern int Int64_unpack(lua_State* L, const gchar *buff, gboolean asLittleEndian); +extern void UInt64_pack(lua_State* L, luaL_Buffer *b, gint idx, gboolean asLittleEndian); +extern int UInt64_unpack(lua_State* L, const gchar *buff, gboolean asLittleEndian); +extern guint64 getUInt64(lua_State *L, int i); + +extern Tvb* push_Tvb(lua_State* L, tvbuff_t* tvb); +extern int push_wsluaTvb(lua_State* L, Tvb t); +extern gboolean push_TvbRange(lua_State* L, tvbuff_t* tvb, int offset, int len); +extern void clear_outstanding_Tvb(void); +extern void clear_outstanding_TvbRange(void); + +extern Pinfo* push_Pinfo(lua_State* L, packet_info* p); +extern void clear_outstanding_Pinfo(void); +extern void clear_outstanding_Column(void); +extern void clear_outstanding_Columns(void); +extern void clear_outstanding_PrivateTable(void); + +extern int get_hf_wslua_text(void); +extern TreeItem push_TreeItem(lua_State *L, proto_tree *tree, proto_item *item); +extern void clear_outstanding_TreeItem(void); + +extern FieldInfo* push_FieldInfo(lua_State *L, field_info* f); +extern void clear_outstanding_FieldInfo(void); + +extern void wslua_print_stack(char* s, lua_State* L); + +extern void wslua_init(register_cb cb, gpointer client_data); +extern void wslua_early_cleanup(void); +extern void wslua_cleanup(void); + +extern tap_extractor_t wslua_get_tap_extractor(const gchar* name); +extern int wslua_set_tap_enums(lua_State* L); + +extern ProtoField wslua_is_field_available(lua_State* L, const char* field_abbr); + +extern char* wslua_get_actual_filename(const char* fname); + +extern int wslua_bin2hex(lua_State* L, const guint8* data, const guint len, const gboolean lowercase, const gchar* sep); +extern int wslua_hex2bin(lua_State* L, const char* data, const guint len, const gchar* sep); +extern int luaopen_rex_pcre2(lua_State *L); + +extern const gchar* get_current_plugin_version(void); +extern void clear_current_plugin_version(void); + +extern int wslua_deregister_heur_dissectors(lua_State* L); +extern int wslua_deregister_protocols(lua_State* L); +extern int wslua_deregister_dissector_tables(lua_State* L); +extern int wslua_deregister_listeners(lua_State* L); +extern int wslua_deregister_fields(lua_State* L); +extern int wslua_deregister_filehandlers(lua_State* L); +extern void wslua_deregister_menus(void); + +extern void wslua_init_wtap_filetypes(lua_State* L); + +#endif + +/* + * 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: + */ diff --git a/epan/wslua/wslua_address.c b/epan/wslua/wslua_address.c new file mode 100644 index 00000000..ea89af3a --- /dev/null +++ b/epan/wslua/wslua_address.c @@ -0,0 +1,280 @@ +/* + * wslua_address.c + * + * Wireshark's interface to the Lua Programming Language + * + * (c) 2006, Luis E. Garcia Ontanon <luis@ontanon.org> + * (c) 2008, Balint Reczey <balint.reczey@ericsson.com> + * (c) 2011, Stig Bjorlykke <stig@bjorlykke.org> + * (c) 2014, Hadriel Kaplan <hadrielk@yahoo.com> + * + * 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" + +#include "wslua.h" + +#include <epan/addr_resolv.h> + +/* WSLUA_CONTINUE_MODULE Pinfo */ + + +WSLUA_CLASS_DEFINE(Address,FAIL_ON_NULL("Address")); /* Represents an address. */ + +WSLUA_CONSTRUCTOR Address_ip(lua_State* L) { + /* Creates an Address Object representing an IPv4 address. */ + +#define WSLUA_ARG_Address_ip_HOSTNAME 1 /* The address or name of the IP host. */ + Address addr = (Address)g_malloc(sizeof(address)); + guint32 ip_addr; + const gchar* name = luaL_checkstring(L,WSLUA_ARG_Address_ip_HOSTNAME); + + if (! get_host_ipaddr(name, &ip_addr)) { + ip_addr = 0; + } + + alloc_address_wmem(NULL, addr, AT_IPv4, 4, &ip_addr); + pushAddress(L,addr); + WSLUA_RETURN(1); /* The Address object. */ +} + +WSLUA_CONSTRUCTOR Address_ipv6(lua_State* L) { + /* Creates an Address Object representing an IPv6 address. */ + +#define WSLUA_ARG_Address_ipv6_HOSTNAME 1 /* The address or name of the IP host. */ + Address addr = (Address)g_malloc(sizeof(address)); + ws_in6_addr ip_addr; + const gchar* name = luaL_checkstring(L,WSLUA_ARG_Address_ipv6_HOSTNAME); + + if (!get_host_ipaddr6(name, &ip_addr)) { + memset(&ip_addr, 0, sizeof(ip_addr)); + } + + alloc_address_wmem(NULL, addr, AT_IPv6, sizeof(ip_addr.bytes), &ip_addr.bytes); + pushAddress(L,addr); + WSLUA_RETURN(1); /* The Address object */ +} + +WSLUA_CONSTRUCTOR Address_ether(lua_State *L) { + /* Creates an Address Object representing an Ethernet address. */ + +#define WSLUA_ARG_Address_ether_ETH 1 /* The Ethernet address. */ + Address addr = (Address)g_malloc(sizeof(address)); + const gchar *name = luaL_checkstring(L, WSLUA_ARG_Address_ether_ETH); + guint8 eth_buf[6]; + + if(!str_to_eth(name, eth_buf)) + memset(eth_buf, 0, sizeof(eth_buf)); + + alloc_address_wmem(NULL, addr, AT_ETHER, sizeof(eth_buf), eth_buf); + pushAddress(L, addr); + WSLUA_RETURN(1); /* The Address object. */ +} + +#if 0 +/* TODO */ +static int Address_ss7(lua_State* L) { + Address addr = g_malloc(sizeof(address)); + + /* alloc_address() */ + + pushAddress(L,addr); + return 1; +} +static int Address_sna(lua_State* L) { + Address addr = g_malloc(sizeof(address)); + + /* alloc_address() */ + + pushAddress(L,addr); + return 1; +} +static int Address_atalk(lua_State* L) { + Address addr = g_malloc(sizeof(address)); + + /* alloc_address() */ + + pushAddress(L,addr); + return 1; +} +static int Address_vines(lua_State* L) { + Address addr = g_malloc(sizeof(address)); + + /* alloc_address() */ + + pushAddress(L,addr); + return 1; +} +static int Address_osi(lua_State* L) { + Address addr = g_malloc(sizeof(address)); + + /* alloc_address() */ + + pushAddress(L,addr); + return 1; +} +static int Address_arcnet(lua_State* L) { + Address addr = g_malloc(sizeof(address)); + + /* alloc_address() */ + + pushAddress(L,addr); + return 1; +} +static int Address_fc(lua_State* L) { + Address addr = g_malloc(sizeof(address)); + + /* alloc_address() */ + + pushAddress(L,addr); + return 1; +} +static int Address_string(lua_State* L) { + Address addr = g_malloc(sizeof(address)); + + /* alloc_address() */ + + pushAddress(L,addr); + return 1; +} +static int Address_eui64(lua_State* L) { + Address addr = g_malloc(sizeof(address)); + + /* alloc_address() */ + + pushAddress(L,addr); + return 1; +} +static int Address_uri(lua_State* L) { + Address addr = g_malloc(sizeof(address)); + + /* alloc_address() */ + + pushAddress(L,addr); + return 1; +} +static int Address_tipc(lua_State* L) { + Address addr = g_malloc(sizeof(address)); + + /* alloc_address() */ + + pushAddress(L,addr); + return 1; +} +#endif + +WSLUA_METHODS Address_methods[] = { + WSLUA_CLASS_FNREG(Address,ip), + WSLUA_CLASS_FNREG_ALIAS(Address,ipv4,ip), + WSLUA_CLASS_FNREG(Address,ipv6), + WSLUA_CLASS_FNREG(Address,ether), +#if 0 + WSLUA_CLASS_FNREG_ALIAS(Address,ss7pc,ss7), + WSLUA_CLASS_FNREG(Address,sna}, + WSLUA_CLASS_FNREG(Address,atalk), + WSLUA_CLASS_FNREG(Address,vines), + WSLUA_CLASS_FNREG(Address,osi), + WSLUA_CLASS_FNREG(Address,arcnet), + WSLUA_CLASS_FNREG(Address,fc), + WSLUA_CLASS_FNREG(Address,string), + WSLUA_CLASS_FNREG(Address,eui64), + WSLUA_CLASS_FNREG(Address,uri), + WSLUA_CLASS_FNREG(Address,tipc), +#endif + { NULL, NULL } +}; + +WSLUA_METAMETHOD Address__tostring(lua_State* L) { + Address addr = checkAddress(L,1); + gchar *str = address_to_display(NULL, addr); + + lua_pushstring(L, str); + + wmem_free(NULL, str); + + WSLUA_RETURN(1); /* The string representing the address. */ +} + +/* Gets registered as metamethod automatically by WSLUA_REGISTER_CLASS/META */ +static int Address__gc(lua_State* L) { + Address addr = toAddress(L,1); + + if (addr) { + free_address(addr); + g_free(addr); + } + + return 0; +} + +WSLUA_METAMETHOD Address__eq(lua_State* L) { /* Compares two Addresses. */ + Address addr1 = checkAddress(L,1); + Address addr2 = checkAddress(L,2); + gboolean result = FALSE; + + if (addresses_equal(addr1, addr2)) + result = TRUE; + + lua_pushboolean(L,result); + + return 1; +} + +WSLUA_METAMETHOD Address__le(lua_State* L) { /* Compares two Addresses. */ + Address addr1 = checkAddress(L,1); + Address addr2 = checkAddress(L,2); + gboolean result = FALSE; + + if (cmp_address(addr1, addr2) <= 0) + result = TRUE; + + lua_pushboolean(L,result); + + return 1; +} + +WSLUA_METAMETHOD Address__lt(lua_State* L) { /* Compares two Addresses. */ + Address addr1 = checkAddress(L,1); + Address addr2 = checkAddress(L,2); + gboolean result = FALSE; + + if (cmp_address(addr1, addr2) < 0) + result = TRUE; + + lua_pushboolean(L,result); + + return 1; +} + +WSLUA_META Address_meta[] = { + WSLUA_CLASS_MTREG(Address,tostring), + WSLUA_CLASS_MTREG(Address,eq), + WSLUA_CLASS_MTREG(Address,le), + WSLUA_CLASS_MTREG(Address,lt), + { NULL, NULL } +}; + + +int Address_register(lua_State *L) { + WSLUA_REGISTER_CLASS(Address); + 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: + */ diff --git a/epan/wslua/wslua_byte_array.c b/epan/wslua/wslua_byte_array.c new file mode 100644 index 00000000..6e6ae304 --- /dev/null +++ b/epan/wslua/wslua_byte_array.c @@ -0,0 +1,765 @@ +/* + * wslua_byte_array.c + * + * Wireshark's interface to the Lua Programming Language + * + * (c) 2006, Luis E. Garcia Ontanon <luis@ontanon.org> + * (c) 2008, Balint Reczey <balint.reczey@ericsson.com> + * (c) 2011, Stig Bjorlykke <stig@bjorlykke.org> + * (c) 2014, Hadriel Kaplan <hadrielk@yahoo.com> + * + * 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" + +#include "wslua.h" + +/* WSLUA_CONTINUE_MODULE Tvb */ + + +WSLUA_CLASS_DEFINE(ByteArray,FAIL_ON_NULL("ByteArray")); + +WSLUA_CONSTRUCTOR ByteArray_new(lua_State* L) { + /* + Creates a new <<lua_class_ByteArray,`ByteArray`>> object. + + Starting in version 1.11.3, if the second argument is a boolean `true`, + then the first argument is treated as a raw Lua string of bytes to use, + instead of a hexadecimal string. + + ===== Example + + [source,lua] + ---- + local empty = ByteArray.new() + local b1 = ByteArray.new("a1 b2 c3 d4") + local b2 = ByteArray.new("112233") + ---- + */ +#define WSLUA_OPTARG_ByteArray_new_HEXBYTES 1 /* A string consisting of hexadecimal bytes like "00 B1 A2" or "1a2b3c4d". */ +#define WSLUA_OPTARG_ByteArray_new_SEPARATOR 2 /* A string separator between hex bytes/words (default=" "), + or if the boolean value `true` is used, then the first argument + is treated as raw binary data */ + GByteArray* ba = g_byte_array_new(); + const gchar* s; + size_t len = 0; + const gchar* sep = " "; + gboolean ishex = TRUE; + + if (lua_gettop(L) >= 1) { + s = luaL_checklstring(L,WSLUA_OPTARG_ByteArray_new_HEXBYTES,&len); + + if (lua_gettop(L) >= 2) { + if (lua_type(L,2) == LUA_TBOOLEAN && lua_toboolean(L,2)) { + ishex = FALSE; + } else { + sep = luaL_optstring(L,WSLUA_OPTARG_ByteArray_new_SEPARATOR," "); + } + } + + if (ishex) { + wslua_hex2bin(L, s, (guint)len, sep); /* this pushes a new string on top of stack */ + s = luaL_checklstring(L, -1, &len); /* get the new binary string */ + g_byte_array_append(ba,s,(guint)len); /* copy it into ByteArray */ + lua_pop(L,1); /* pop the newly created string */ + } else { + g_byte_array_append(ba,s,(guint)len); + } + } + + pushByteArray(L,ba); + + WSLUA_RETURN(1); /* The new ByteArray object. */ +} + +/* Gets registered as metamethod automatically by WSLUA_REGISTER_CLASS/META */ +static int ByteArray__gc(lua_State* L) { + ByteArray ba = toByteArray(L,1); + + if (!ba) return 0; + + g_byte_array_free(ba,TRUE); + return 0; +} + +WSLUA_METAMETHOD ByteArray__concat(lua_State* L) { + /* Concatenate two <<lua_class_ByteArray,`ByteArray`>>s. */ +#define WSLUA_ARG_ByteArray__concat_FIRST 1 /* First array. */ +#define WSLUA_ARG_ByteArray__concat_SECOND 2 /* Second array. */ + + ByteArray ba1 = checkByteArray(L,WSLUA_ARG_ByteArray__concat_FIRST); + ByteArray ba2 = checkByteArray(L,WSLUA_ARG_ByteArray__concat_SECOND); + ByteArray ba; + + ba = g_byte_array_new(); + g_byte_array_append(ba,ba1->data,ba1->len); + g_byte_array_append(ba,ba2->data,ba2->len); + + pushByteArray(L,ba); + WSLUA_RETURN(1); /* The new composite <<lua_class_ByteArray,`ByteArray`>>. */ +} + +WSLUA_METAMETHOD ByteArray__eq(lua_State* L) { + /* Compares two ByteArray values. + + @since 1.11.4 + */ +#define WSLUA_ARG_ByteArray__eq_FIRST 1 /* First array. */ +#define WSLUA_ARG_ByteArray__eq_SECOND 2 /* Second array. */ + ByteArray ba1 = checkByteArray(L,WSLUA_ARG_ByteArray__eq_FIRST); + ByteArray ba2 = checkByteArray(L,WSLUA_ARG_ByteArray__eq_SECOND); + gboolean result = FALSE; + + if (ba1->len == ba2->len) { + if (memcmp(ba1->data, ba2->data, ba1->len) == 0) + result = TRUE; + } + + lua_pushboolean(L,result); + + return 1; +} + +WSLUA_METHOD ByteArray_prepend(lua_State* L) { + /* Prepend a <<lua_class_ByteArray,`ByteArray`>> to this <<lua_class_ByteArray,`ByteArray`>>. */ +#define WSLUA_ARG_ByteArray_prepend_PREPENDED 2 /* <<lua_class_ByteArray,`ByteArray`>> to be prepended. */ + ByteArray ba = checkByteArray(L,1); + ByteArray ba2 = checkByteArray(L,WSLUA_ARG_ByteArray_prepend_PREPENDED); + + g_byte_array_prepend(ba,ba2->data,ba2->len); + + return 0; +} + +WSLUA_METHOD ByteArray_append(lua_State* L) { + /* Append a <<lua_class_ByteArray,`ByteArray`>> to this <<lua_class_ByteArray,`ByteArray`>>. */ +#define WSLUA_ARG_ByteArray_append_APPENDED 2 /* <<lua_class_ByteArray,`ByteArray`>> to be appended. */ + ByteArray ba = checkByteArray(L,1); + ByteArray ba2 = checkByteArray(L,WSLUA_ARG_ByteArray_append_APPENDED); + + g_byte_array_append(ba,ba2->data,ba2->len); + + return 0; +} + +WSLUA_METHOD ByteArray_set_size(lua_State* L) { + /* Sets the size of a <<lua_class_ByteArray,`ByteArray`>>, either truncating it or filling it with zeros. */ +#define WSLUA_ARG_ByteArray_set_size_SIZE 2 /* New size of the array. */ + + ByteArray ba = checkByteArray(L,1); + int siz = (int)luaL_checkinteger(L,WSLUA_ARG_ByteArray_set_size_SIZE); + guint8* padding; + + if (siz < 0) { + WSLUA_ERROR(ByteArray_set_size,"ByteArray size must be non-negative"); + return 0; + } + + if (ba->len >= (guint)siz) { /* truncate */ + g_byte_array_set_size(ba,siz); + } else { /* fill */ + padding = (guint8 *)g_malloc0(sizeof(guint8)*(siz - ba->len)); + g_byte_array_append(ba,padding,siz - ba->len); + g_free(padding); + } + return 0; +} + +WSLUA_METHOD ByteArray_set_index(lua_State* L) { + /* Sets the value of an index of a <<lua_class_ByteArray,`ByteArray`>>. */ +#define WSLUA_ARG_ByteArray_set_index_INDEX 2 /* The position of the byte to be set. */ +#define WSLUA_ARG_ByteArray_set_index_VALUE 3 /* The char value to set [0-255]. */ + ByteArray ba = checkByteArray(L,1); + int idx = (int)luaL_checkinteger(L,WSLUA_ARG_ByteArray_set_index_INDEX); + int v = (int)luaL_checkinteger(L,WSLUA_ARG_ByteArray_set_index_VALUE); + + if (idx == 0 && ! g_str_equal(luaL_optstring(L,2,""),"0") ) { + luaL_argerror(L,2,"bad index"); + return 0; + } + + if (idx < 0 || (guint)idx >= ba->len) { + luaL_argerror(L,2,"index out of range"); + return 0; + } + + if (v < 0 || v > 255) { + luaL_argerror(L,3,"Byte out of range"); + return 0; + } + + ba->data[idx] = (guint8)v; + + return 0; +} + + +WSLUA_METHOD ByteArray_get_index(lua_State* L) { + /* Get the value of a byte in a <<lua_class_ByteArray,`ByteArray`>>. */ +#define WSLUA_ARG_ByteArray_get_index_INDEX 2 /* The position of the byte to get. */ + ByteArray ba = checkByteArray(L,1); + int idx = (int)luaL_checkinteger(L,WSLUA_ARG_ByteArray_get_index_INDEX); + + if (idx == 0 && ! g_str_equal(luaL_optstring(L,2,""),"0") ) { + luaL_argerror(L,2,"bad index"); + return 0; + } + + if (idx < 0 || (guint)idx >= ba->len) { + luaL_argerror(L,2,"index out of range"); + return 0; + } + lua_pushnumber(L,ba->data[idx]); + + WSLUA_RETURN(1); /* The value [0-255] of the byte. */ +} + +WSLUA_METHOD ByteArray_le_int(lua_State* L) { + /* Read a little endian encoded signed integer in a <<lua_class_ByteArray,`ByteArray`>> beginning at given offset with given length. + + @since 4.1.0 + */ +#define WSLUA_OPTARG_ByteArray_le_int_OFFSET 2 /* The position of the first byte. Default is 0, or the first byte. */ +#define WSLUA_OPTARG_ByteArray_le_int_LENGTH 3 /* The length of the integer. Default is -1, or the remaining bytes in the <<lua_class_ByteArray,`ByteArray`>>. */ + ByteArray ba = checkByteArray(L, 1); + int offset = (int)luaL_optinteger(L, WSLUA_OPTARG_ByteArray_le_int_OFFSET, 0); + int len = (int)luaL_optinteger(L, WSLUA_OPTARG_ByteArray_le_int_LENGTH, -1); + + if (offset < 0 || (guint)offset >= ba->len) { + luaL_argerror(L, WSLUA_OPTARG_ByteArray_le_int_OFFSET, "offset out of bounds"); + return 0; + } + + if (len == -1) { + len = ba->len - offset; /* Use remaining bytes. */ + } + + if (len < 1 || len > 4) { + luaL_argerror(L, WSLUA_OPTARG_ByteArray_le_int_LENGTH, "bad length"); + return 0; + } + + if ((guint)(offset + len) > ba->len) { + luaL_error(L, "range out of bounds");; + return 0; + } + + gint32 value = (gint8)ba->data[offset + len - 1]; + for (int i = len - 2; i >= 0; i--) { + value <<= 8; + value |= (guint8)ba->data[offset + i]; + } + + lua_pushnumber(L, value); + + WSLUA_RETURN(1); /* The value of the little endian encoded signed integer beginning at given offset with given length. */ +} + +WSLUA_METHOD ByteArray_le_int64(lua_State* L) { + /* Read a little endian encoded 64 bit signed integer in a <<lua_class_ByteArray,`ByteArray`>> beginning at given offset with given length. + + @since 4.1.0 + */ +#define WSLUA_OPTARG_ByteArray_le_int64_OFFSET 2 /* The position of the first byte. Default is 0, or the first byte. */ +#define WSLUA_OPTARG_ByteArray_le_int64_LENGTH 3 /* The length of the integer. Default is -1, or the remaining bytes in the <<lua_class_ByteArray,`ByteArray`>>. */ + ByteArray ba = checkByteArray(L, 1); + int offset = (int)luaL_optinteger(L, WSLUA_OPTARG_ByteArray_le_int64_OFFSET, 0); + int len = (int)luaL_optinteger(L, WSLUA_OPTARG_ByteArray_le_int64_LENGTH, -1); + + if (offset < 0 || (guint)offset >= ba->len) { + luaL_argerror(L, WSLUA_OPTARG_ByteArray_le_int64_OFFSET, "offset out of bounds"); + return 0; + } + + if (len == -1) { + len = ba->len - offset; /* Use remaining bytes. */ + } + + if (len < 1 || len > 8) { + luaL_argerror(L, WSLUA_OPTARG_ByteArray_le_int64_LENGTH, "bad length"); + return 0; + } + + if ((guint)(offset + len) > ba->len) { + luaL_error(L, "range out of bounds");; + return 0; + } + + gint64 value = (gint8)ba->data[offset + len - 1]; + for (int i = len - 2; i >= 0; i--) { + value <<= 8; + value |= (guint8)ba->data[offset + i]; + } + + pushInt64(L, value); + + WSLUA_RETURN(1); /* The value of the little endian encoded 64 bit signed integer as a <<lua_class_Int64,`Int64`>> object beginning at given offset with given length. */ +} + +WSLUA_METHOD ByteArray_le_uint(lua_State* L) { + /* Read a little endian encoded unsigned integer in a <<lua_class_ByteArray,`ByteArray`>> beginning at given offset with given length. + + @since 4.1.0 + */ +#define WSLUA_OPTARG_ByteArray_le_uint_OFFSET 2 /* The position of the first byte. Default is 0, or the first byte. */ +#define WSLUA_OPTARG_ByteArray_le_uint_LENGTH 3 /* The length of the integer. Default is -1, or the remaining bytes in the <<lua_class_ByteArray,`ByteArray`>>. */ + ByteArray ba = checkByteArray(L, 1); + int offset = (int)luaL_optinteger(L, WSLUA_OPTARG_ByteArray_le_uint_OFFSET, 0); + int len = (int)luaL_optinteger(L, WSLUA_OPTARG_ByteArray_le_uint_LENGTH, -1); + + if (offset < 0 || (guint)offset >= ba->len) { + luaL_argerror(L, WSLUA_OPTARG_ByteArray_le_uint_OFFSET, "offset out of bounds"); + return 0; + } + + if (len == -1) { + len = ba->len - offset; /* Use remaining bytes. */ + } + + if (len < 1 || len > 4) { + luaL_argerror(L, WSLUA_OPTARG_ByteArray_le_uint_LENGTH, "bad length"); + return 0; + } + + if ((guint)(offset + len) > ba->len) { + luaL_error(L, "range out of bounds");; + return 0; + } + + guint32 value = (guint8)ba->data[offset + len - 1]; + for (int i = len - 2; i >= 0; i--) { + value <<= 8; + value |= (guint8)ba->data[offset + i]; + } + + lua_pushnumber(L, value); + + WSLUA_RETURN(1); /* The value of the little endian encoded unsigned integer beginning at given offset with given length. */ +} + +WSLUA_METHOD ByteArray_le_uint64(lua_State* L) { + /* Read a little endian encoded 64 bit unsigned integer in a <<lua_class_ByteArray,`ByteArray`>> beginning at given offset with given length. + + @since 4.1.0 + */ +#define WSLUA_OPTARG_ByteArray_le_uint64_OFFSET 2 /* The position of the first byte. Default is 0, or the first byte. */ +#define WSLUA_OPTARG_ByteArray_le_uint64_LENGTH 3 /* The length of the integer. Default is -1, or the remaining bytes in the <<lua_class_ByteArray,`ByteArray`>>. */ + ByteArray ba = checkByteArray(L, 1); + int offset = (int)luaL_optinteger(L, WSLUA_OPTARG_ByteArray_le_uint64_OFFSET, 0); + int len = (int)luaL_optinteger(L, WSLUA_OPTARG_ByteArray_le_uint64_LENGTH, -1); + + if (offset < 0 || (guint)offset >= ba->len) { + luaL_argerror(L, WSLUA_OPTARG_ByteArray_le_uint64_OFFSET, "offset out of bounds"); + return 0; + } + + if (len == -1) { + len = ba->len - offset; /* Use remaining bytes. */ + } + + if (len < 1 || len > 8) { + luaL_argerror(L, WSLUA_OPTARG_ByteArray_le_uint64_LENGTH, "bad length"); + return 0; + } + + if ((guint)(offset + len) > ba->len) { + luaL_error(L, "range out of bounds");; + return 0; + } + + guint64 value = (guint8)ba->data[offset + len - 1]; + for (int i = len - 2; i >= 0; i--) { + value <<= 8; + value |= (guint8)ba->data[offset + i]; + } + + pushUInt64(L, value); + + WSLUA_RETURN(1); /* The value of the little endian encoded 64 bit unsigned integer as a <<lua_class_UInt64,`UInt64`>> object beginning at given offset with given length. */ +} + +WSLUA_METHOD ByteArray_int(lua_State* L) { + /* Read a big endian encoded signed integer in a <<lua_class_ByteArray,`ByteArray`>> beginning at given offset with given length. + + @since 4.1.0 + */ +#define WSLUA_OPTARG_ByteArray_int_OFFSET 2 /* The position of the first byte. Default is 0, or the first byte. */ +#define WSLUA_OPTARG_ByteArray_int_LENGTH 3 /* The length of the integer. Default is -1, or the remaining bytes in the <<lua_class_ByteArray,`ByteArray`>>. */ + ByteArray ba = checkByteArray(L, 1); + int offset = (int)luaL_optinteger(L, WSLUA_OPTARG_ByteArray_int_OFFSET, 0); + int len = (int)luaL_optinteger(L, WSLUA_OPTARG_ByteArray_int_LENGTH, -1); + + if (offset < 0 || (guint)offset >= ba->len) { + luaL_argerror(L, WSLUA_OPTARG_ByteArray_int_OFFSET, "offset out of bounds"); + return 0; + } + + if (len == -1) { + len = ba->len - offset; /* Use remaining bytes. */ + } + + if (len < 1 || len > 4) { + luaL_argerror(L, WSLUA_OPTARG_ByteArray_int_LENGTH, "bad length"); + return 0; + } + + if ((guint)(offset + len) > ba->len) { + luaL_error(L, "range out of bounds");; + return 0; + } + + gint32 value = (gint8)ba->data[offset]; + for (int i = 1; i < len; i++) { + value <<= 8; + value |= (guint8)ba->data[offset + i]; + } + + lua_pushnumber(L, value); + + WSLUA_RETURN(1); /* The value of the big endian encoded 32 bit signed integer beginning at given offset with given length. */ +} + +WSLUA_METHOD ByteArray_int64(lua_State* L) { + /* Read a big endian encoded 64 bit signed integer in a <<lua_class_ByteArray,`ByteArray`>> beginning at given offset with given length. + + @since 4.1.0 + */ +#define WSLUA_OPTARG_ByteArray_int64_OFFSET 2 /* The position of the first byte. Default is 0, or the first byte. */ +#define WSLUA_OPTARG_ByteArray_int64_LENGTH 3 /* The length of the integer. Default is -1, or the remaining bytes in the <<lua_class_ByteArray,`ByteArray`>>. */ + ByteArray ba = checkByteArray(L, 1); + int offset = (int)luaL_optinteger(L, WSLUA_OPTARG_ByteArray_int64_OFFSET, 0); + int len = (int)luaL_optinteger(L, WSLUA_OPTARG_ByteArray_int64_LENGTH, -1); + + if (offset < 0 || (guint)offset >= ba->len) { + luaL_argerror(L, WSLUA_OPTARG_ByteArray_int64_OFFSET, "offset out of bounds"); + return 0; + } + + if (len == -1) { + len = ba->len - offset; /* Use remaining bytes. */ + } + + if (len < 1 || len > 8) { + luaL_argerror(L, WSLUA_OPTARG_ByteArray_int64_LENGTH, "bad length"); + return 0; + } + + if ((guint)(offset + len) > ba->len) { + luaL_error(L, "range out of bounds");; + return 0; + } + + gint64 value = (gint8)ba->data[offset]; + for (int i = 1; i < len; i++) { + value <<= 8; + value |= (guint8)ba->data[offset + i]; + } + + pushInt64(L, value); + + WSLUA_RETURN(1); /* The value of the big endian encoded 64 bit signed integer as a <<lua_class_Int64,`Int64`>> object beginning at given offset and given length. */ +} + +WSLUA_METHOD ByteArray_uint(lua_State* L) { + /* Read a big endian encoded unsigned integer in a <<lua_class_ByteArray,`ByteArray`>> beginning at given offset with given length. + + @since 4.1.0 + */ +#define WSLUA_OPTARG_ByteArray_uint_OFFSET 2 /* The position of the first byte. Default is 0, or the first byte. */ +#define WSLUA_OPTARG_ByteArray_uint_LENGTH 3 /* The length of the integer. Default is -1, or the remaining bytes in the <<lua_class_ByteArray,`ByteArray`>>. */ + ByteArray ba = checkByteArray(L, 1); + int offset = (int)luaL_optinteger(L, WSLUA_OPTARG_ByteArray_uint_OFFSET, 0); + int len = (int)luaL_optinteger(L, WSLUA_OPTARG_ByteArray_uint_LENGTH, -1); + + if (offset < 0 || (guint)offset >= ba->len) { + luaL_argerror(L, WSLUA_OPTARG_ByteArray_uint_OFFSET, "offset out of bounds"); + return 0; + } + + if (len == -1) { + len = ba->len - offset; /* Use remaining bytes. */ + } + + if (len < 1 || len > 4) { + luaL_argerror(L, WSLUA_OPTARG_ByteArray_uint_LENGTH, "bad length"); + return 0; + } + + if ((guint)(offset + len) > ba->len) { + luaL_error(L, "range out of bounds");; + return 0; + } + + guint32 value = (guint8)ba->data[offset]; + for (int i = 1; i < len; i++) { + value <<= 8; + value |= (guint8)ba->data[offset + i]; + } + + lua_pushnumber(L, value); + + WSLUA_RETURN(1); /* The value of the big endian encoded 32 bit unsigned integer beginning at given offset with given length. */ +} + +WSLUA_METHOD ByteArray_uint64(lua_State* L) { + /* Read a big endian encoded 64 bit unsigned integer in a <<lua_class_ByteArray,`ByteArray`>> beginning at given offset with given length. + + @since 4.1.0 + */ +#define WSLUA_OPTARG_ByteArray_uint64_OFFSET 2 /* The position of the first byte. Default is 0, or the first byte. */ +#define WSLUA_OPTARG_ByteArray_uint64_LENGTH 3 /* The length of the integer. Default is -1, or the remaining bytes in the <<lua_class_ByteArray,`ByteArray`>>. */ + ByteArray ba = checkByteArray(L, 1); + int offset = (int)luaL_optinteger(L, WSLUA_OPTARG_ByteArray_uint64_OFFSET, 0); + int len = (int)luaL_optinteger(L, WSLUA_OPTARG_ByteArray_uint64_LENGTH, -1); + + if (offset < 0 || (guint)offset >= ba->len) { + luaL_argerror(L, WSLUA_OPTARG_ByteArray_uint64_OFFSET, "offset out of bounds"); + return 0; + } + + if (len == -1) { + len = ba->len - offset; /* Use remaining bytes. */ + } + + if (len < 1 || len > 8) { + luaL_argerror(L, WSLUA_OPTARG_ByteArray_uint64_LENGTH, "bad length"); + return 0; + } + + if ((guint)(offset + len) > ba->len) { + luaL_error(L, "range out of bounds");; + return 0; + } + + guint64 value = (guint8)ba->data[offset]; + for (int i = 1; i < len; i++) { + value <<= 8; + value |= (guint8)ba->data[offset + i]; + } + + pushUInt64(L, value); + + WSLUA_RETURN(1); /* The value of the big endian encoded 64 bit unsigned integer as a <<lua_class_UInt64,`UInt64`>> object beginning at given offset with given length. */ +} + +WSLUA_METHOD ByteArray_len(lua_State* L) { + /* Obtain the length of a <<lua_class_ByteArray,`ByteArray`>>. */ + ByteArray ba = checkByteArray(L,1); + + lua_pushnumber(L,(lua_Number)ba->len); + + WSLUA_RETURN(1); /* The length of the <<lua_class_ByteArray,`ByteArray`>>. */ +} + +WSLUA_METHOD ByteArray_subset(lua_State* L) { + /* Obtain a segment of a <<lua_class_ByteArray,`ByteArray`>>, as a new <<lua_class_ByteArray,`ByteArray`>>. */ +#define WSLUA_ARG_ByteArray_subset_OFFSET 2 /* The position of the first byte (0=first). */ +#define WSLUA_ARG_ByteArray_subset_LENGTH 3 /* The length of the segment. */ + ByteArray ba = checkByteArray(L,1); + int offset = (int)luaL_checkinteger(L,WSLUA_ARG_ByteArray_subset_OFFSET); + int len = (int)luaL_checkinteger(L,WSLUA_ARG_ByteArray_subset_LENGTH); + ByteArray sub; + + if ((offset + len) > (int)ba->len || offset < 0 || len < 1) { + luaL_error(L,"Out Of Bounds"); + return 0; + } + + sub = g_byte_array_new(); + g_byte_array_append(sub,ba->data + offset,len); + + pushByteArray(L,sub); + + WSLUA_RETURN(1); /* A <<lua_class_ByteArray,`ByteArray`>> containing the requested segment. */ +} + +WSLUA_METHOD ByteArray_base64_decode(lua_State* L) { + /* Obtain a Base64 decoded <<lua_class_ByteArray,`ByteArray`>>. + + @since 1.11.3 + */ + ByteArray ba = checkByteArray(L,1); + ByteArray ba2; + gchar *data; + gsize len = ba->len; + + if ((len % 4) != 0) { + len += 4 - (len % 4); + } + + ba2 = g_byte_array_new(); + if (ba->len > 1) { + data = (gchar*)g_malloc(len + 1); + memcpy(data, ba->data, ba->len); + if (len > ba->len) { + memcpy(data + ba->len, "====", len - ba->len); + } + data[len] = '\0'; + + g_base64_decode_inplace(data, &len); + g_byte_array_append(ba2, data, (int)len); + g_free(data); + } + + pushByteArray(L,ba2); + WSLUA_RETURN(1); /* The created <<lua_class_ByteArray,`ByteArray`>>. */ +} + +WSLUA_METHOD ByteArray_raw(lua_State* L) { + /* Obtain a Lua string of the binary bytes in a <<lua_class_ByteArray,`ByteArray`>>. + + @since 1.11.3 + */ +#define WSLUA_OPTARG_ByteArray_raw_OFFSET 2 /* The position of the first byte (default=0/first). */ +#define WSLUA_OPTARG_ByteArray_raw_LENGTH 3 /* The length of the segment to get (default=all). */ + ByteArray ba = checkByteArray(L,1); + guint offset = (guint) luaL_optinteger(L,WSLUA_OPTARG_ByteArray_raw_OFFSET,0); + int len; + + if (!ba) return 0; + if (offset > ba->len) { + WSLUA_OPTARG_ERROR(ByteArray_raw,OFFSET,"offset beyond end of byte array"); + return 0; + } + + len = (int) luaL_optinteger(L,WSLUA_OPTARG_ByteArray_raw_LENGTH, ba->len - offset); + if ((len < 0) || ((guint)len > (ba->len - offset))) + len = ba->len - offset; + + lua_pushlstring(L, &(ba->data[offset]), len); + + WSLUA_RETURN(1); /* A Lua string of the binary bytes in the ByteArray. */ +} + +WSLUA_METHOD ByteArray_tohex(lua_State* L) { + /* Obtain a Lua string of the bytes in a <<lua_class_ByteArray,`ByteArray`>> as hex-ascii, with given separator + + @since 1.11.3 + */ +#define WSLUA_OPTARG_ByteArray_tohex_LOWERCASE 2 /* True to use lower-case hex characters (default=false). */ +#define WSLUA_OPTARG_ByteArray_tohex_SEPARATOR 3 /* A string separator to insert between hex bytes (default=nil). */ + ByteArray ba = checkByteArray(L,1); + gboolean lowercase = FALSE; + const gchar* sep = NULL; + + if (!ba) return 0; + + lowercase = wslua_optbool(L,WSLUA_OPTARG_ByteArray_tohex_LOWERCASE,FALSE); + sep = luaL_optstring(L,WSLUA_OPTARG_ByteArray_tohex_SEPARATOR,NULL); + + wslua_bin2hex(L, ba->data, ba->len, lowercase, sep); + + WSLUA_RETURN(1); /* A hex-ascii string representation of the <<lua_class_ByteArray,`ByteArray`>>. */ +} + +WSLUA_METAMETHOD ByteArray__tostring(lua_State* L) { + /* Obtain a Lua string containing the bytes in a <<lua_class_ByteArray,`ByteArray`>> so that it can be used in + display filters (e.g. "01FE456789AB"). */ + ByteArray ba = checkByteArray(L,1); + + if (!ba) return 0; + + wslua_bin2hex(L, ba->data, ba->len, FALSE, NULL); + + WSLUA_RETURN(1); /* A hex-ascii string representation of the <<lua_class_ByteArray,`ByteArray`>>. */ +} + +WSLUA_METHOD ByteArray_tvb (lua_State *L) { + /* + Creates a new <<lua_class_Tvb,`Tvb`>> from a <<lua_class_ByteArray,`ByteArray`>>. + The <<lua_class_Tvb,`Tvb`>> will be added to the current frame. + + ===== Example + + [source,lua] + ---- + function proto_foo.dissector(buf, pinfo, tree) + -- Create a new tab named "My Tvb" and add some data to it + local b = ByteArray.new("11223344") + local tvb = ByteArray.tvb(b, "My Tvb") + + -- Create a tree item that, when clicked, automatically shows the tab we just created + tree:add( tvb(1,2), "Foo" ) + end + ---- + */ +#define WSLUA_ARG_ByteArray_tvb_NAME 2 /* The name to be given to the new data source. */ + ByteArray ba = checkByteArray(L,1); + const gchar* name = luaL_optstring(L,WSLUA_ARG_ByteArray_tvb_NAME,"Unnamed") ; + guint8* data; + Tvb tvb; + + if (!lua_tvb) { + luaL_error(L,"Tvbs can only be created and used in dissectors"); + return 0; + } + + data = (guint8 *)g_memdup2(ba->data, ba->len); + + tvb = (Tvb)g_malloc(sizeof(struct _wslua_tvb)); + tvb->ws_tvb = tvb_new_child_real_data(lua_tvb, data, ba->len,ba->len); + tvb->expired = FALSE; + tvb->need_free = FALSE; + tvb_set_free_cb(tvb->ws_tvb, g_free); + + add_new_data_source(lua_pinfo, tvb->ws_tvb, name); + push_wsluaTvb(L,tvb); + WSLUA_RETURN(1); /* The created <<lua_class_Tvb,`Tvb`>>. */ +} + + +WSLUA_METHODS ByteArray_methods[] = { + WSLUA_CLASS_FNREG(ByteArray,new), + WSLUA_CLASS_FNREG(ByteArray,le_int), + WSLUA_CLASS_FNREG(ByteArray,le_int64), + WSLUA_CLASS_FNREG(ByteArray,le_uint), + WSLUA_CLASS_FNREG(ByteArray,le_uint64), + WSLUA_CLASS_FNREG(ByteArray,int), + WSLUA_CLASS_FNREG(ByteArray,int64), + WSLUA_CLASS_FNREG(ByteArray,uint), + WSLUA_CLASS_FNREG(ByteArray,uint64), + WSLUA_CLASS_FNREG(ByteArray,len), + WSLUA_CLASS_FNREG(ByteArray,prepend), + WSLUA_CLASS_FNREG(ByteArray,append), + WSLUA_CLASS_FNREG(ByteArray,subset), + WSLUA_CLASS_FNREG(ByteArray,set_size), + WSLUA_CLASS_FNREG(ByteArray,tvb), + WSLUA_CLASS_FNREG(ByteArray,base64_decode), + WSLUA_CLASS_FNREG(ByteArray,get_index), + WSLUA_CLASS_FNREG(ByteArray,set_index), + WSLUA_CLASS_FNREG(ByteArray,tohex), + WSLUA_CLASS_FNREG(ByteArray,raw), + { NULL, NULL } +}; + +WSLUA_META ByteArray_meta[] = { + WSLUA_CLASS_MTREG(ByteArray,tostring), + WSLUA_CLASS_MTREG(ByteArray,concat), + WSLUA_CLASS_MTREG(ByteArray,eq), + {"__call",ByteArray_subset}, + { NULL, NULL } +}; + +int ByteArray_register(lua_State* L) { + WSLUA_REGISTER_CLASS(ByteArray); + 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: + */ diff --git a/epan/wslua/wslua_capture_info.c b/epan/wslua/wslua_capture_info.c new file mode 100644 index 00000000..06bf847d --- /dev/null +++ b/epan/wslua/wslua_capture_info.c @@ -0,0 +1,520 @@ +/* + * wslua_capture_info.c + * + * Wireshark's interface to the Lua Programming Language + * for capture file data and meta-data. + * + * (c) 2014, Hadriel Kaplan <hadrielk@yahoo.com> + * + * 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" + +#include "wslua_file_common.h" + +#include <epan/addr_resolv.h> + + +/* WSLUA_CONTINUE_MODULE File */ + + +WSLUA_CLASS_DEFINE(CaptureInfo,FAIL_ON_NULL_OR_EXPIRED("CaptureInfo")); +/* + A `CaptureInfo` object, passed into Lua as an argument by `FileHandler` callback + function `read_open()`, `read()`, `seek_read()`, `seq_read_close()`, and `read_close()`. + This object represents capture file data and meta-data (data about the + capture file) being read into Wireshark/TShark. + + This object's fields can be written-to by Lua during the read-based function callbacks. + 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. + + @since 1.11.3 + */ + +CaptureInfo* push_CaptureInfo(lua_State* L, wtap *wth, const gboolean first_time) { + CaptureInfo f; + + if (!wth) { + luaL_error(L, "Internal error: wth is NULL!"); + return NULL; + } + + f = (CaptureInfo) g_malloc0(sizeof(struct _wslua_captureinfo)); + f->wth = wth; + f->wdh = NULL; + f->expired = FALSE; + + if (first_time) { + /* XXX: need to do this? */ + wth->file_encap = WTAP_ENCAP_UNKNOWN; + wth->file_tsprec = WTAP_TSPREC_UNKNOWN; + wth->snapshot_length = 0; + } + + 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, file_encap=%d, file_tsprec='%s'", + wth->file_type_subtype, wth->snapshot_length, wth->file_encap, wth->file_tsprec); + } + + WSLUA_RETURN(1); /* String of debug information. */ +} + + +static int CaptureInfo__gc(lua_State* L) { + CaptureInfo fc = toCaptureInfo(L,1); + g_free(fc); + return 0; +} + +/* WSLUA_ATTRIBUTE CaptureInfo_encap RW The packet encapsulation type for the whole file. + + See `wtap_encaps` 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` for available precisions. + */ +WSLUA_ATTRIBUTE_NAMED_NUMBER_GETTER(CaptureInfo,time_precision,wth->file_tsprec); +WSLUA_ATTRIBUTE_NAMED_NUMBER_SETTER(CaptureInfo,time_precision,wth->file_tsprec,int); + +/* WSLUA_ATTRIBUTE CaptureInfo_snapshot_length RW The maximum packet length that could be recorded. + + Setting it to `0` means unknown. + */ +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_OPT_BLOCK_NTH_STRING_GETTER(CaptureInfo,comment,wth->shb_hdrs,OPT_COMMENT); +WSLUA_ATTRIBUTE_NAMED_OPT_BLOCK_NTH_STRING_SETTER(CaptureInfo,comment,wth->shb_hdrs,OPT_COMMENT); + +/* 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_OPT_BLOCK_STRING_GETTER(CaptureInfo,hardware,wth->shb_hdrs,OPT_SHB_HARDWARE); +WSLUA_ATTRIBUTE_NAMED_OPT_BLOCK_STRING_SETTER(CaptureInfo,hardware,wth->shb_hdrs,OPT_SHB_HARDWARE); + +/* 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_OPT_BLOCK_STRING_GETTER(CaptureInfo,os,wth->shb_hdrs,OPT_SHB_OS); +WSLUA_ATTRIBUTE_NAMED_OPT_BLOCK_STRING_SETTER(CaptureInfo,os,wth->shb_hdrs,OPT_SHB_OS); + +/* 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_OPT_BLOCK_STRING_GETTER(CaptureInfo,user_app,wth->shb_hdrs,OPT_SHB_USERAPPL); +WSLUA_ATTRIBUTE_NAMED_OPT_BLOCK_STRING_SETTER(CaptureInfo,user_app,wth->shb_hdrs,OPT_SHB_USERAPPL); + +/* 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. + + 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: + + [source,lua] + ---- + { ipv4_addresses = { { addr = "\01\02\03\04", name = "foo.com" } } } + ---- + + 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; + ws_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, FALSE); + + /* 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, FALSE); + + /* 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_ATTRIBUTE CaptureInfo_private_table RW A private Lua value unique to this file. + + The `private_table` is a field you set/get with your own Lua table. + This is provided so that a Lua script can save per-file reading/writing + state, because multiple files can be opened and read at the same time. + + For example, if the user issued a reload-file command, or Lua called the + `reload()` function, then the current capture file is still open while a new one + is being opened, and thus Wireshark will invoke `read_open()` while the previous + capture file has not caused `read_close()` to be called; and if the `read_open()` + succeeds then `read_close()` will be called right after that for the previous + file, rather than the one just opened. Thus the Lua script can use this + `private_table` to store a table of values specific to each file, by setting + this `private_table` in the `read_open()` function, which it can then later get back + inside its `read()`, `seek_read()`, and `read_close()` functions. +*/ +static int CaptureInfo_get_private_table(lua_State* L) { + CaptureInfo fi = checkCaptureInfo(L,1); + return get_wth_priv_table_ref(L, fi->wth); +} + +static int CaptureInfo_set_private_table(lua_State* L) { + CaptureInfo fi = checkCaptureInfo(L,1); + return set_wth_priv_table_ref(L, fi->wth); +} + +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), + WSLUA_ATTRIBUTE_RWREG(CaptureInfo,private_table), + { NULL, NULL, NULL } +}; + +WSLUA_META CaptureInfo_meta[] = { + WSLUA_CLASS_MTREG(CaptureInfo,tostring), + { NULL, NULL } +}; + +int CaptureInfo_register(lua_State* L) { + WSLUA_REGISTER_META_WITH_ATTRS(CaptureInfo); + return 0; +} + + +WSLUA_CLASS_DEFINE(CaptureInfoConst,FAIL_ON_NULL_OR_EXPIRED("CaptureInfoConst")); +/* + 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. + + 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. + + @since 1.11.3 + */ + +CaptureInfoConst* push_CaptureInfoConst(lua_State* L, wtap_dumper *wdh) { + CaptureInfoConst f; + + if (!wdh) { + luaL_error(L, "Internal error: wdh is NULL!"); + return NULL; + } + + 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, compression_type=%d", + wdh->file_type_subtype, wdh->snaplen, wdh->file_encap, wdh->compression_type); + } + + 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` 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->file_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_OPT_BLOCK_STRING_GETTER(CaptureInfoConst,comment,wth->shb_hdrs,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_OPT_BLOCK_STRING_GETTER(CaptureInfoConst,hardware,wth->shb_hdrs,OPT_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_OPT_BLOCK_STRING_GETTER(CaptureInfoConst,os,wth->shb_hdrs,OPT_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_OPT_BLOCK_STRING_GETTER(CaptureInfoConst,user_app,wth->shb_hdrs,OPT_SHB_USERAPPL); + +/* 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. + + 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: + + [source,lua] + ---- + { ipv4_addresses = { { addr = "\01\02\03\04", name = "foo.com" } }, ipv6_addresses = { } } + ---- + + 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[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; +} + +/* WSLUA_ATTRIBUTE CaptureInfoConst_private_table RW A private Lua value unique to this file. + + The `private_table` is a field you set/get with your own Lua table. + This is provided so that a Lua script can save per-file reading/writing + state, because multiple files can be opened and read at the same time. + + For example, if two Lua scripts issue a `Dumper:new_for_current()` call and the + current file happens to use your script's writer, then the Wireshark will invoke + `write_open()` while the previous capture file has not had `write_close()` called. + Thus the Lua script can use this `private_table` to store a table of values + specific to each file, by setting this `private_table` in the write_open() + function, which it can then later get back inside its `write()`, and `write_close()` + functions. +*/ +static int CaptureInfoConst_get_private_table(lua_State* L) { + CaptureInfoConst fi = checkCaptureInfoConst(L,1); + return get_wdh_priv_table_ref(L, fi->wdh); +} + +static int CaptureInfoConst_set_private_table(lua_State* L) { + CaptureInfoConst fi = checkCaptureInfoConst(L,1); + return set_wdh_priv_table_ref(L, fi->wdh); +} + +static int CaptureInfoConst__gc(lua_State* L) { + CaptureInfoConst fi = toCaptureInfoConst(L,1); + 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), + WSLUA_ATTRIBUTE_RWREG(CaptureInfoConst,private_table), + { NULL, NULL, NULL } +}; + +WSLUA_META CaptureInfoConst_meta[] = { + WSLUA_CLASS_MTREG(CaptureInfoConst,tostring), + { NULL, NULL } +}; + +int CaptureInfoConst_register(lua_State* L) { + WSLUA_REGISTER_META_WITH_ATTRS(CaptureInfoConst); + 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: + */ diff --git a/epan/wslua/wslua_column.c b/epan/wslua/wslua_column.c new file mode 100644 index 00000000..5ae0c4cc --- /dev/null +++ b/epan/wslua/wslua_column.c @@ -0,0 +1,441 @@ +/* + * wslua_column.c + * + * Wireshark's interface to the Lua Programming Language + * + * (c) 2006, Luis E. Garcia Ontanon <luis@ontanon.org> + * (c) 2008, Balint Reczey <balint.reczey@ericsson.com> + * (c) 2011, Stig Bjorlykke <stig@bjorlykke.org> + * (c) 2014, Hadriel Kaplan <hadrielk@yahoo.com> + * + * 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" + +#include "wslua_pinfo_common.h" + + +/* WSLUA_CONTINUE_MODULE Pinfo */ + + +static GPtrArray* outstanding_Column = NULL; +static GPtrArray* outstanding_Columns = NULL; + +CLEAR_OUTSTANDING(Column,expired, TRUE) +CLEAR_OUTSTANDING(Columns,expired, TRUE) + +#define PUSH_COLUMN(L,c) {g_ptr_array_add(outstanding_Column,c);pushColumn(L,c);} + +void Push_Columns(lua_State *L, Columns c) +{ + g_ptr_array_add(outstanding_Columns, c); + pushColumns(L, c); +} + + +WSLUA_CLASS_DEFINE(Column,FAIL_ON_NULL("Column")); /* A Column in the packet list. */ + +struct col_names_t { + const gchar* name; + int id; +}; + +// Duplicated belown in Columns__newindex. +static const struct col_names_t colnames[] = { + {"number",COL_NUMBER}, + {"abs_time",COL_ABS_TIME}, + {"utc_time",COL_UTC_TIME}, + {"cls_time",COL_CLS_TIME}, + {"rel_time",COL_REL_TIME}, + {"date",COL_ABS_YMD_TIME}, + {"date_doy",COL_ABS_YDOY_TIME}, + {"utc_date",COL_UTC_YMD_TIME}, + {"utc_date_doy",COL_UTC_YDOY_TIME}, + {"delta_time",COL_DELTA_TIME}, + {"delta_time_displayed",COL_DELTA_TIME_DIS}, + {"src",COL_DEF_SRC}, + {"src_res",COL_RES_SRC}, + {"src_unres",COL_UNRES_SRC}, + {"dl_src",COL_DEF_DL_SRC}, + {"dl_src_res",COL_RES_DL_SRC}, + {"dl_src_unres",COL_UNRES_DL_SRC}, + {"net_src",COL_DEF_NET_SRC}, + {"net_src_res",COL_RES_NET_SRC}, + {"net_src_unres",COL_UNRES_NET_SRC}, + {"dst",COL_DEF_DST}, + {"dst_res",COL_RES_DST}, + {"dst_unres",COL_UNRES_DST}, + {"dl_dst",COL_DEF_DL_DST}, + {"dl_dst_res",COL_RES_DL_DST}, + {"dl_dst_unres",COL_UNRES_DL_DST}, + {"net_dst",COL_DEF_NET_DST}, + {"net_dst_res",COL_RES_NET_DST}, + {"net_dst_unres",COL_UNRES_NET_DST}, + {"src_port",COL_DEF_SRC_PORT}, + {"src_port_res",COL_RES_SRC_PORT}, + {"src_port_unres",COL_UNRES_SRC_PORT}, + {"dst_port",COL_DEF_DST_PORT}, + {"dst_port_res",COL_RES_DST_PORT}, + {"dst_port_unres",COL_UNRES_DST_PORT}, + {"protocol",COL_PROTOCOL}, + {"info",COL_INFO}, + {"packet_len",COL_PACKET_LENGTH}, + {"cumulative_bytes",COL_CUMULATIVE_BYTES}, + {"direction",COL_IF_DIR}, + {"tx_rate",COL_TX_RATE}, + {"rssi",COL_RSSI}, + {NULL,0} +}; + +static gint col_name_to_id(const gchar* name) { + const struct col_names_t* cn; + for(cn = colnames; cn->name; cn++) { + if (g_str_equal(cn->name,name)) { + return cn->id; + } + } + + return 0; +} + +static const gchar* col_id_to_name(gint id) { + const struct col_names_t* cn; + for(cn = colnames; cn->name; cn++) { + if ( cn->id == id ) { + return cn->name; + } + } + return NULL; +} + + +WSLUA_METAMETHOD Column__tostring(lua_State *L) { + Column c = checkColumn(L,1); + const gchar* text; + + if (!c->cinfo) { + text = col_id_to_name(c->col); + lua_pushfstring(L, "(%s)", text ? text : "unknown"); + } + else { + text = col_get_text(c->cinfo, c->col); + lua_pushstring(L, text ? text : "(nil)"); + } + + WSLUA_RETURN(1); /* The column's string text (in parenthesis if not available). */ +} + +/* Gets registered as metamethod automatically by WSLUA_REGISTER_CLASS */ +static int Column__gc(lua_State* L) { + Column col = toColumn(L,1); + + if (!col) return 0; + + if (!col->expired) + col->expired = TRUE; + else + g_free(col); + + return 0; + +} + +WSLUA_METHOD Column_clear(lua_State *L) { + /* Clears a Column. */ + Column c = checkColumn(L,1); + + if (!(c->cinfo)) return 0; + + col_clear(c->cinfo, c->col); + + return 0; +} + +WSLUA_METHOD Column_set(lua_State *L) { + /* Sets the text of a Column. */ +#define WSLUA_ARG_Column_set_TEXT 2 /* The text to which to set the Column. */ + Column c = checkColumn(L,1); + const gchar* s = luaL_checkstring(L,WSLUA_ARG_Column_set_TEXT); + + if (!(c->cinfo)) + return 0; + + col_add_str(c->cinfo, c->col, s); + + return 0; +} + +WSLUA_METHOD Column_append(lua_State *L) { + /* Appends text to a Column. */ +#define WSLUA_ARG_Column_append_TEXT 2 /* The text to append to the Column. */ + Column c = checkColumn(L,1); + const gchar* s = luaL_checkstring(L,WSLUA_ARG_Column_append_TEXT); + + if (!(c->cinfo)) + return 0; + + col_append_str(c->cinfo, c->col, s); + + return 0; +} + +WSLUA_METHOD Column_prepend(lua_State *L) { + /* Prepends text to a Column. */ +#define WSLUA_ARG_Column_prepend_TEXT 2 /* The text to prepend to the Column. */ + Column c = checkColumn(L,1); + const gchar* s = luaL_checkstring(L,WSLUA_ARG_Column_prepend_TEXT); + + if (!(c->cinfo)) + return 0; + + col_prepend_fstr(c->cinfo, c->col, "%s",s); + + return 0; +} + +WSLUA_METHOD Column_fence(lua_State *L) { + /* Sets Column text fence, to prevent overwriting. + + @since 1.10.6 + */ + Column c = checkColumn(L,1); + + if (c->cinfo) + col_set_fence(c->cinfo, c->col); + + return 0; +} + +WSLUA_METHOD Column_clear_fence(lua_State *L) { + /* Clear Column text fence. + + @since 1.11.3 + */ + Column c = checkColumn(L,1); + + if (c->cinfo) + col_clear_fence(c->cinfo, c->col); + + return 0; +} + + +WSLUA_METHODS Column_methods[] = { + WSLUA_CLASS_FNREG(Column,clear), + WSLUA_CLASS_FNREG(Column,set), + WSLUA_CLASS_FNREG(Column,append), + WSLUA_CLASS_FNREG(Column,prepend), + WSLUA_CLASS_FNREG_ALIAS(Column,preppend,prepend), + WSLUA_CLASS_FNREG(Column,fence), + WSLUA_CLASS_FNREG(Column,clear_fence), + { NULL, NULL } +}; + + +WSLUA_META Column_meta[] = { + WSLUA_CLASS_MTREG(Column,tostring), + { NULL, NULL } +}; + + +int Column_register(lua_State *L) { + WSLUA_REGISTER_CLASS(Column); + outstanding_Column = g_ptr_array_new(); + return 0; +} + + +WSLUA_CLASS_DEFINE(Columns,NOP); /* The <<lua_class_Column,``Column``>>s of the packet list. */ + +WSLUA_METAMETHOD Columns__tostring(lua_State *L) { + lua_pushstring(L,"Columns"); + WSLUA_RETURN(1); + /* The string "Columns". This has no real use aside from debugging. */ +} + +/* + * To document this is very odd - it won't make sense to a person reading the + * API docs to see this metamethod as a method, but oh well. + */ +WSLUA_METAMETHOD Columns__newindex(lua_State *L) { /* + Sets the text of a specific column. + Some columns cannot be modified, and no error is raised if attempted. + The columns that are known to allow modification are "info" and "protocol". + */ +#define WSLUA_ARG_Columns__newindex_COLUMN 2 /* + The name of the column to set. + Valid values are: + + [options="header"] + |=== + |Name |Description + |number |Frame number + |abs_time |Absolute timestamp + |utc_time |UTC timestamp + |cls_time |CLS timestamp + |rel_time |Relative timestamp + |date |Absolute date and time + |date_doy |Absolute year, day of year, and time + |utc_date |UTC date and time + |utc_date_doy |UTC year, day of year, and time + |delta_time |Delta time from previous packet + |delta_time_displayed |Delta time from previous displayed packet + |src |Source address + |src_res |Resolved source address + |src_unres |Numeric source address + |dl_src |Source data link address + |dl_src_res |Resolved source data link address + |dl_src_unres |Numeric source data link address + |net_src |Source network address + |net_src_res |Resolved source network address + |net_src_unres |Numeric source network address + |dst |Destination address + |dst_res |Resolve destination address + |dst_unres |Numeric destination address + |dl_dst |Destination data link address + |dl_dst_res |Resolved destination data link address + |dl_dst_unres |Numeric destination data link address + |net_dst |Destination network address + |net_dst_res |Resolved destination network address + |net_dst_unres |Numeric destination network address + |src_port |Source port + |src_port_res |Resolved source port + |src_port_unres |Numeric source port + |dst_port |Destination port + |dst_port_res |Resolved destination port + |dst_port_unres |Numeric destination port + |protocol |Protocol name + |info |General packet information + |packet_len |Packet length + |cumulative_bytes |Cumulative bytes in the capture + |direction |Packet direction + |vsan |Virtual SAN + |tx_rate |Transmit rate + |rssi |RSSI value + |dce_call |DCE call + |=== + + ===== Example + pinfo.cols['info'] = 'foo bar' + + -- syntactic sugar (equivalent to above) + pinfo.cols.info = 'foo bar' + */ +#define WSLUA_ARG_Columns__newindex_TEXT 3 /* The text for the column. */ + Columns cols = checkColumns(L,1); + const struct col_names_t* cn; + const char* colname; + const char* text; + + if (!cols) return 0; + if (cols->expired) { + luaL_error(L,"expired column"); + return 0; + } + + colname = luaL_checkstring(L,WSLUA_ARG_Columns__newindex_COLUMN); + text = luaL_checkstring(L,WSLUA_ARG_Columns__newindex_TEXT); + + for(cn = colnames; cn->name; cn++) { + if( g_str_equal(cn->name,colname) ) { + col_add_str(cols->cinfo, cn->id, text); + return 0; + } + } + + WSLUA_ARG_ERROR(Columns__newindex,COLUMN,"the column name must be a valid column"); + return 0; +} + +WSLUA_METAMETHOD Columns__index(lua_State *L) { + /* Get a specific <<lua_class_Column,``Column``>>. */ + Columns cols = checkColumns(L,1); + const struct col_names_t* cn; + const char* colname = luaL_checkstring(L,2); + + if (!cols) { + Column c = (Column)g_malloc(sizeof(struct _wslua_col_info)); + c->cinfo = NULL; + c->col = col_name_to_id(colname); + c->expired = FALSE; + + PUSH_COLUMN(L,c); + return 1; + } + + + if (cols->expired) { + luaL_error(L,"expired column"); + return 0; + } + + for(cn = colnames; cn->name; cn++) { + if( g_str_equal(cn->name,colname) ) { + Column c = (Column)g_malloc(sizeof(struct _wslua_col_info)); + c->cinfo = cols->cinfo; + c->col = col_name_to_id(colname); + c->expired = FALSE; + + PUSH_COLUMN(L,c); + return 1; + } + } + + return 0; +} + +/* for internal use - used by Pinfo */ +int get_Columns_index(lua_State *L) +{ + return Columns__index(L); +} + + +/* Gets registered as metamethod automatically by WSLUA_REGISTER_META */ +static int Columns__gc(lua_State* L) { + Columns cols = toColumns(L,1); + + if (!cols) return 0; + + if (!cols->expired) + cols->expired = TRUE; + else + g_free(cols); + + return 0; + +} + + +WSLUA_META Columns_meta[] = { + WSLUA_CLASS_MTREG(Columns,tostring), + WSLUA_CLASS_MTREG(Columns,newindex), + WSLUA_CLASS_MTREG(Columns,index), + { NULL, NULL } +}; + + +int Columns_register(lua_State *L) { + WSLUA_REGISTER_META(Columns); + outstanding_Columns = g_ptr_array_new(); + 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: + */ diff --git a/epan/wslua/wslua_dir.c b/epan/wslua/wslua_dir.c new file mode 100644 index 00000000..542ce0a6 --- /dev/null +++ b/epan/wslua/wslua_dir.c @@ -0,0 +1,385 @@ +/* + * wslua_dir.c + * + * (c) 2014, Hadriel Kaplan <hadrielk at yahoo dot com> + * + * 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 Dir Directory Handling Functions */ + +#include "wslua.h" +#include <errno.h> +#include <wsutil/file_util.h> + +WSLUA_CLASS_DEFINE(Dir,FAIL_ON_NULL("Dir")); /* A Directory object, as well as associated functions. */ + +WSLUA_CONSTRUCTOR Dir_make(lua_State* L) { + /* Creates a directory. + + The created directory is set for permission mode 0755 (octal), meaning it is + read+write+execute by owner, but only read+execute by group members and others. + + If the directory was created successfully, a boolean `true` is returned. + If the directory cannot be made because it already exists, `false` is returned. + If the directory cannot be made because an error occurred, `nil` is returned. + + @since 1.11.3 + */ +#define WSLUA_ARG_Dir_make_NAME 1 /* The name of the directory, possibly including path. */ + + const char *dir_path = luaL_checkstring(L, WSLUA_ARG_Dir_make_NAME); + ws_statb64 s_buf; + int ret; + + if (ws_stat64(dir_path, &s_buf) != 0 && errno == ENOENT) { + ret = ws_mkdir(dir_path, 0755); + if (ret == -1) { + lua_pushnil(L); + } else { + lua_pushboolean(L, 1); + } + } else { + lua_pushboolean(L, 0); + } + + WSLUA_RETURN(1); /* Boolean `true` on success, `false` if the directory already exists, `nil` on error. */ +} + +WSLUA_CONSTRUCTOR Dir_exists(lua_State* L) { + /* Returns true if the given directory name exists. + + If the directory exists, a boolean `true` is returned. + If the path is a file instead, `false` is returned. + If the path does not exist or an error occurred, `nil` is returned. + + @since 1.11.3 + */ +#define WSLUA_ARG_Dir_exists_NAME 1 /* The name of the directory, possibly including path. */ + + const char *dir_path = luaL_checkstring(L, WSLUA_ARG_Dir_exists_NAME); + int ret; + + if ((ret = test_for_directory (dir_path)) == EISDIR) { + lua_pushboolean(L, 1); + } else { + if (ret == 0) { + lua_pushboolean(L, 0); + } else { + lua_pushnil(L); + } + } + + WSLUA_RETURN(1); /* Boolean `true` if the directory exists, `false` if it's a file, `nil` on error or not-exist. */ +} + +WSLUA_CONSTRUCTOR Dir_remove(lua_State* L) { + /* Removes an empty directory. + + If the directory was removed successfully, a boolean `true` is returned. + If the directory cannot be removed because it does not exist, `false` is returned. + If the directory cannot be removed because an error occurred, `nil` is returned. + + This function only removes empty directories. To remove a directory regardless, + use `Dir.remove_all()`. + + @since 1.11.3 + */ +#define WSLUA_ARG_Dir_remove_NAME 1 /* The name of the directory, possibly including path. */ + + const char *dir_path = luaL_checkstring(L, WSLUA_ARG_Dir_remove_NAME); + int ret; + + if (test_for_directory (dir_path) == EISDIR) { + ret = ws_remove(dir_path); + if (ret != 0) { + lua_pushnil(L); + } else { + lua_pushboolean(L, 1); + } + } else { + lua_pushboolean(L, 0); + } + + WSLUA_RETURN(1); /* Boolean `true` on success, `false` if does not exist, `nil` on error. */ +} + +static int delete_directory(const char *directory) { + WS_DIR *dir; + WS_DIRENT *file; + gchar *filename; + int ret = 0; + + /* delete all contents of directory */ + if ((dir = ws_dir_open(directory, 0, NULL)) != NULL) { + while ((file = ws_dir_read_name(dir)) != NULL) { + filename = g_build_filename(directory, ws_dir_get_name(file), NULL); + if (test_for_directory(filename) != EISDIR) { + ret = ws_remove(filename); + } else { + /* recurse */ + ret = delete_directory (filename); + } + g_free(filename); + if (ret != 0) { + break; + } + } + ws_dir_close(dir); + } + + if (ret == 0) { + ret = ws_remove(directory); + } + + return ret; +} + + +WSLUA_CONSTRUCTOR Dir_remove_all(lua_State* L) { + /* Removes an empty or non-empty directory. + + If the directory was removed successfully, a boolean `true` is returned. + If the directory cannot be removed because it does not exist, `false` is returned. + If the directory cannot be removed because an error occurred, `nil` is returned. + + @since 1.11.3 + */ +#define WSLUA_ARG_Dir_remove_all_NAME 1 /* The name of the directory, possibly including path. */ + + const char *dir_path = luaL_checkstring(L, WSLUA_ARG_Dir_remove_all_NAME); + int ret; + + if (test_for_directory (dir_path) == EISDIR) { + ret = delete_directory(dir_path); + if (ret != 0) { + lua_pushnil(L); + } else { + lua_pushboolean(L, 1); + } + } else { + lua_pushboolean(L, 0); + } + + WSLUA_RETURN(1); /* Boolean `true` on success, `false` if does not exist, `nil` on error. */ +} + +WSLUA_CONSTRUCTOR Dir_open(lua_State* L) { + /* Opens a directory and returns a <<lua_class_Dir,`Dir`>> object representing the files in the directory. + + ==== Example + + [source,lua] + ---- + -- Print the contents of a directory + for filename in Dir.open('/path/to/dir') do + print(filename) + end + ---- + */ +#define WSLUA_ARG_Dir_open_PATHNAME 1 /* The pathname of the directory. */ +#define WSLUA_OPTARG_Dir_open_EXTENSION 2 /* If given, only files with this extension will be returned. */ + + const char* dirname = luaL_checkstring(L,WSLUA_ARG_Dir_open_PATHNAME); + const char* extension = luaL_optstring(L,WSLUA_OPTARG_Dir_open_EXTENSION,NULL); + Dir dir; + char* dirname_clean; + + dirname_clean = wslua_get_actual_filename(dirname); + if (!dirname_clean) { + WSLUA_ARG_ERROR(Dir_open,PATHNAME,"directory does not exist"); + return 0; + } + + if (!test_for_directory(dirname_clean)) { + g_free(dirname_clean); + WSLUA_ARG_ERROR(Dir_open,PATHNAME, "must be a directory"); + return 0; + } + + dir = (Dir)g_malloc(sizeof(struct _wslua_dir)); + dir->dir = g_dir_open(dirname_clean, 0, NULL); + g_free(dirname_clean); + + if (dir->dir == NULL) { + g_free(dir); + + WSLUA_ARG_ERROR(Dir_open,PATHNAME,"could not open directory"); + return 0; + } + + dir->ext = g_strdup(extension); + + pushDir(L,dir); + WSLUA_RETURN(1); /* The <<lua_class_Dir,`Dir`>> object. */ +} + +WSLUA_METAMETHOD Dir__call(lua_State* L) { + /* + Gets the next file or subdirectory within the directory, or `nil` when done. + + ==== Example + + [source,lua] + ---- + -- Open a directory and print the name of the first file or subdirectory + local dir = Dir.open('/path/to/dir') + local first = dir() + print(tostring(file)) + ---- + */ + + Dir dir = checkDir(L,1); + const gchar* file; + const gchar* filename; + const char* ext; + + if (!dir->dir) { + return 0; + } + + if ( ! ( file = g_dir_read_name(dir->dir ) )) { + g_dir_close(dir->dir); + dir->dir = NULL; + return 0; + } + + + if ( ! dir->ext ) { + lua_pushstring(L,file); + return 1; + } + + do { + filename = file; + + /* XXX strstr returns ptr to first match, + this fails ext=".xxx" filename="aaa.xxxz.xxx" */ + if ( ( ext = strstr(filename,dir->ext)) && g_str_equal(ext,dir->ext) ) { + lua_pushstring(L,filename); + return 1; + } + } while(( file = g_dir_read_name(dir->dir) )); + + g_dir_close(dir->dir); + dir->dir = NULL; + return 0; +} + +WSLUA_METHOD Dir_close(lua_State* L) { + /* Closes the directory. Called automatically during garbage collection of a <<lua_class_Dir,`Dir`>> object. */ + Dir dir = checkDir(L,1); + + if (dir->dir) { + g_dir_close(dir->dir); + dir->dir = NULL; + } + + return 0; +} + +WSLUA_CONSTRUCTOR Dir_personal_config_path(lua_State* L) { + /* Gets the https://www.wireshark.org/docs/wsug_html_chunked/ChAppFilesConfigurationSection.html[personal configuration] directory path, with filename if supplied. + + @since 1.11.3 + */ +#define WSLUA_OPTARG_Dir_personal_config_path_FILENAME 1 /* A filename. */ + const char *fname = luaL_optstring(L, WSLUA_OPTARG_Dir_personal_config_path_FILENAME,""); + char* filename = get_persconffile_path(fname,FALSE); + + lua_pushstring(L,filename); + g_free(filename); + WSLUA_RETURN(1); /* The full pathname for a file in the personal configuration directory. */ +} + +WSLUA_CONSTRUCTOR Dir_global_config_path(lua_State* L) { + /* Gets the https://www.wireshark.org/docs/wsug_html_chunked/ChAppFilesConfigurationSection.html[global configuration] directory path, with filename if supplied. + + @since 1.11.3 + */ +#define WSLUA_OPTARG_Dir_global_config_path_FILENAME 1 /* A filename */ + const char *fname = luaL_optstring(L, WSLUA_OPTARG_Dir_global_config_path_FILENAME,""); + char* filename; + + filename = get_datafile_path(fname); + lua_pushstring(L,filename); + g_free(filename); + WSLUA_RETURN(1); /* The full pathname for a file in Wireshark's configuration directory. */ +} + +WSLUA_CONSTRUCTOR Dir_personal_plugins_path(lua_State* L) { + /* Gets the personal plugins directory path. + + @since 1.11.3 + */ + lua_pushstring(L, get_plugins_pers_dir()); + WSLUA_RETURN(1); /* The pathname of the https://www.wireshark.org/docs/wsug_html_chunked/ChPluginFolders.html[personal plugins] directory. */ +} + +WSLUA_CONSTRUCTOR Dir_global_plugins_path(lua_State* L) { + /* Gets the global plugins directory path. + + @since 1.11.3 + */ + lua_pushstring(L, get_plugins_dir()); + WSLUA_RETURN(1); /* The pathname of the https://www.wireshark.org/docs/wsug_html_chunked/ChPluginFolders.html[global plugins] directory. */ +} + +/* Gets registered as metamethod automatically by WSLUA_REGISTER_CLASS/META */ +static int Dir__gc(lua_State* L) { + Dir dir = toDir(L,1); + + if(!dir) return 0; + + if (dir->dir) { + g_dir_close(dir->dir); + } + + g_free(dir->ext); + g_free(dir); + + return 0; +} + +WSLUA_METHODS Dir_methods[] = { + WSLUA_CLASS_FNREG(Dir,make), + WSLUA_CLASS_FNREG(Dir,exists), + WSLUA_CLASS_FNREG(Dir,remove), + WSLUA_CLASS_FNREG(Dir,remove_all), + WSLUA_CLASS_FNREG(Dir,open), + WSLUA_CLASS_FNREG(Dir,close), + WSLUA_CLASS_FNREG(Dir,personal_config_path), + WSLUA_CLASS_FNREG(Dir,global_config_path), + WSLUA_CLASS_FNREG(Dir,personal_plugins_path), + WSLUA_CLASS_FNREG(Dir,global_plugins_path), + { NULL, NULL } +}; + +WSLUA_META Dir_meta[] = { + WSLUA_CLASS_MTREG(Dir,call), + { NULL, NULL } +}; + +int Dir_register(lua_State* L) { + WSLUA_REGISTER_CLASS(Dir); + 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: + */ diff --git a/epan/wslua/wslua_dissector.c b/epan/wslua/wslua_dissector.c new file mode 100644 index 00000000..5b189308 --- /dev/null +++ b/epan/wslua/wslua_dissector.c @@ -0,0 +1,793 @@ +/* + * wslua_dissector.c + * + * Wireshark's interface to the Lua Programming Language + * + * (c) 2006, Luis E. Garcia Ontanon <luis@ontanon.org> + * (c) 2008, Balint Reczey <balint.reczey@ericsson.com> + * (c) 2011, Stig Bjorlykke <stig@bjorlykke.org> + * (c) 2014, Hadriel Kaplan <hadrielk@yahoo.com> + * + * 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" + +#include "wslua.h" + +#include <epan/decode_as.h> +#include <epan/exceptions.h> +#include <epan/show_exception.h> + + +/* WSLUA_CONTINUE_MODULE Proto */ + + +WSLUA_CLASS_DEFINE(Dissector,NOP); +/* + A refererence to a dissector, used to call a dissector against a packet or a part of it. + */ + +WSLUA_CONSTRUCTOR Dissector_get (lua_State *L) { + /* Obtains a dissector reference by name. */ +#define WSLUA_ARG_Dissector_get_NAME 1 /* The name of the dissector. */ + const gchar* name = luaL_checkstring(L,WSLUA_ARG_Dissector_get_NAME); + Dissector d; + + if ((d = find_dissector(name))) { + pushDissector(L, d); + } else { + lua_pushnil(L); + } + + WSLUA_RETURN(1); /* The <<lua_class_Dissector,`Dissector`>> reference if found, otherwise `nil`. */ +} + +/* 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 Dissector_list (lua_State *L) { + /* Gets a Lua array table of all registered Dissector names. + + Note: This is an expensive operation, and should only be used for troubleshooting. + + @since 1.11.3 + */ + GList* list = get_dissector_names(); + GList* elist = NULL; + int i = 1; + + if (!list) return luaL_error(L,"Cannot retrieve Dissector 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 dissector names. */ +} + +WSLUA_METHOD Dissector_call(lua_State* L) { + /* Calls a dissector against a given packet (or part of it). */ +#define WSLUA_ARG_Dissector_call_TVB 2 /* The buffer to dissect. */ +#define WSLUA_ARG_Dissector_call_PINFO 3 /* The packet info. */ +#define WSLUA_ARG_Dissector_call_TREE 4 /* The tree on which to add the protocol items. */ + + Dissector volatile d = checkDissector(L,1); + Tvb tvb = checkTvb(L,WSLUA_ARG_Dissector_call_TVB); + Pinfo pinfo = checkPinfo(L,WSLUA_ARG_Dissector_call_PINFO); + TreeItem ti = checkTreeItem(L,WSLUA_ARG_Dissector_call_TREE); + const char *volatile error = NULL; + int len = 0; + + if (! ( d && tvb && pinfo) ) return 0; + + TRY { + len = call_dissector(d, tvb->ws_tvb, pinfo->ws_pinfo, ti->tree); + /* XXX Are we sure about this??? is this the right/only thing to catch */ + } CATCH_BOUNDS_AND_DISSECTOR_ERRORS { + show_exception(tvb->ws_tvb, pinfo->ws_pinfo, ti->tree, EXCEPT_CODE, GET_MESSAGE); + error = GET_MESSAGE ? GET_MESSAGE : "Malformed frame"; + } ENDTRY; + + /* XXX: Some exceptions, like FragmentBoundsError and ScsiBoundsError, + are normal conditions and possibly don't need the Lua traceback. */ + if (error) { WSLUA_ERROR(Dissector_call,error); } + + lua_pushnumber(L,(lua_Number)len); + WSLUA_RETURN(1); /* Number of bytes dissected. Note that some dissectors always return number of bytes in incoming buffer, so be aware. */ +} + +WSLUA_METAMETHOD Dissector__call(lua_State* L) { + /* Calls a dissector against a given packet (or part of it). */ +#define WSLUA_ARG_Dissector__call_TVB 2 /* The buffer to dissect. */ +#define WSLUA_ARG_Dissector__call_PINFO 3 /* The packet info. */ +#define WSLUA_ARG_Dissector__call_TREE 4 /* The tree on which to add the protocol items. */ + return Dissector_call(L); +} + +WSLUA_METAMETHOD Dissector__tostring(lua_State* L) { + /* Gets the Dissector's description. */ + Dissector d = checkDissector(L,1); + if (!d) return 0; + lua_pushstring(L,dissector_handle_get_description(d)); + WSLUA_RETURN(1); /* A string of the Dissector's description. */ +} + +/* Gets registered as metamethod automatically by WSLUA_REGISTER_CLASS/META */ +static int Dissector__gc(lua_State* L _U_) { + /* do NOT free Dissector */ + return 0; +} + +WSLUA_METHODS Dissector_methods[] = { + WSLUA_CLASS_FNREG(Dissector,get), + WSLUA_CLASS_FNREG(Dissector,call), + WSLUA_CLASS_FNREG(Dissector,list), + { NULL, NULL } +}; + +WSLUA_META Dissector_meta[] = { + WSLUA_CLASS_MTREG(Dissector,tostring), + WSLUA_CLASS_MTREG(Dissector,call), + { NULL, NULL } +}; + +int Dissector_register(lua_State* L) { + WSLUA_REGISTER_CLASS(Dissector); + return 0; +} + +WSLUA_CLASS_DEFINE(DissectorTable,NOP); +/* + A table of subdissectors of a particular protocol (e.g. TCP subdissectors like http, smtp, sip are added to table "tcp.port"). + + Useful to add more dissectors to a table so that they appear in the “Decode As...” dialog. + */ + +static int dissectortable_table_ref = LUA_NOREF; + +WSLUA_CONSTRUCTOR DissectorTable_new (lua_State *L) { + /* Creates a new `DissectorTable` for your dissector's use. */ +#define WSLUA_ARG_DissectorTable_new_TABLENAME 1 /* The short name of the table. Use lower-case alphanumeric, dot, and/or underscores (e.g., "ansi_map.tele_id" or "udp.port"). */ +#define WSLUA_OPTARG_DissectorTable_new_UINAME 2 /* The name of the table in the user interface. + Defaults to the name given in `tablename`, but can be any string. */ +#define WSLUA_OPTARG_DissectorTable_new_TYPE 3 /* One of `ftypes.UINT8`, `ftypes.UINT16`, + `ftypes.UINT24`, `ftypes.UINT32`, or + `ftypes.STRING`. + Defaults to `ftypes.UINT32`. */ +#define WSLUA_OPTARG_DissectorTable_new_BASE 4 /* One of `base.NONE`, `base.DEC`, `base.HEX`, + `base.OCT`, `base.DEC_HEX` or `base.HEX_DEC`. + Defaults to `base.DEC`. */ +#define WSLUA_OPTARG_DissectorTable_new_PROTO 5 /* The <<lua_class_Proto,`Proto`>> object that uses this dissector table. */ + const gchar* name = (const gchar*)luaL_checkstring(L,WSLUA_ARG_DissectorTable_new_TABLENAME); + const gchar* ui_name = (const gchar*)luaL_optstring(L,WSLUA_OPTARG_DissectorTable_new_UINAME,name); + enum ftenum type = (enum ftenum)luaL_optinteger(L,WSLUA_OPTARG_DissectorTable_new_TYPE,FT_UINT32); + unsigned base = (unsigned)luaL_optinteger(L,WSLUA_OPTARG_DissectorTable_new_BASE,BASE_DEC); + DissectorTable dt; + int proto_id = -1; + + switch(type) { + case FT_STRING: + base = BASE_NONE; + break; + + case FT_NONE: + break; + + case FT_UINT8: + case FT_UINT16: + case FT_UINT24: + case FT_UINT32: + break; + + default: + /* Calling WSLUA_OPTARG_ERROR raises a Lua error and + returns from this function. */ + WSLUA_OPTARG_ERROR( + DissectorTable_new, TYPE, + "must be ftypes.UINT{8,16,24,32}, ftypes.STRING or ftypes.NONE"); + break; + } + + dt = (DissectorTable)g_malloc(sizeof(struct _wslua_distbl_t)); + + if (isProto(L, WSLUA_OPTARG_DissectorTable_new_PROTO)) { + Proto proto = checkProto(L, WSLUA_OPTARG_DissectorTable_new_PROTO); + proto_id = proto_get_id_by_short_name(proto->name); + } + + dt->table = (type == FT_NONE) ? + register_decode_as_next_proto(proto_id, name, ui_name, NULL) : + register_dissector_table(name, ui_name, proto_id, type, base); + dt->name = g_strdup(name); + dt->ui_name = g_strdup(ui_name); + dt->created = TRUE; + dt->expired = FALSE; + + lua_rawgeti(L, LUA_REGISTRYINDEX, dissectortable_table_ref); + lua_pushstring(L, name); + pushDissectorTable(L, dt); + lua_settable(L, -3); + + pushDissectorTable(L, dt); + WSLUA_RETURN(1); /* The newly created DissectorTable. */ +} + +WSLUA_CONSTRUCTOR DissectorTable_heuristic_new(lua_State *L) { + /* Creates a new heuristic `DissectorTable` for your dissector's use. Returns true iff table was created successfully. */ +#define WSLUA_ARG_DissectorTable_heuristic_new_TABLENAME 1 /* The short name of the table. Use lower-case alphanumeric, dot, and/or underscores. */ +#define WSLUA_ARG_DissectorTable_heuristic_new_PROTO 2 /* The <<lua_class_Proto,`Proto`>> object that uses this dissector table. */ + const gchar* name = (const gchar*)luaL_checkstring(L,WSLUA_ARG_DissectorTable_heuristic_new_TABLENAME); + Proto proto = checkProto(L, WSLUA_ARG_DissectorTable_heuristic_new_PROTO); + heur_dissector_list_t list; + int proto_id = proto_get_id_by_short_name(proto->name); + + list = find_heur_dissector_list(name); + if (list) { + luaL_error(L, "Heuristic list '%s' already exists", name); + return 0; + } + + register_heur_dissector_list(name, proto_id); + return 0; +} + +/* this struct is used for passing ourselves user_data through dissector_all_tables_foreach_table(). */ +typedef struct dissector_tables_foreach_table_info { + int num; + lua_State *L; +} dissector_tables_foreach_table_info_t; + +/* this is the DATFunc_table function used for dissector_all_tables_foreach_table() + so we can get all dissector_table names. This pushes the name into a table at stack index 1 */ +static void +dissector_tables_list_func(const gchar *table_name, const gchar *ui_name _U_, gpointer user_data) { + dissector_tables_foreach_table_info_t *data = (dissector_tables_foreach_table_info_t*) user_data; + lua_pushstring(data->L, table_name); + lua_rawseti(data->L, 1, data->num); + data->num = data->num + 1; +} + +WSLUA_CONSTRUCTOR DissectorTable_list (lua_State *L) { + /* Gets a Lua array table of all DissectorTable names - i.e., the string names you can + use for the first argument to DissectorTable.get(). + + Note: This is an expensive operation, and should only be used for troubleshooting. + + @since 1.11.3 + */ + dissector_tables_foreach_table_info_t data = { 1, L }; + + lua_newtable(L); + + dissector_all_tables_foreach_table(dissector_tables_list_func, (gpointer)&data, + (GCompareFunc)compare_dissector_key_name); + + WSLUA_RETURN(1); /* The array table of registered DissectorTable names. */ +} + +/* this is the DATFunc_heur_table function used for dissector_all_heur_tables_foreach_table() + so we can get all heuristic dissector list names. This pushes the name into a table at stack index 1 */ +static void +heur_dissector_tables_list_func(const gchar *table_name, struct heur_dissector_list *table _U_, gpointer user_data) { + dissector_tables_foreach_table_info_t *data = (dissector_tables_foreach_table_info_t*) user_data; + lua_pushstring(data->L, table_name); + lua_rawseti(data->L, 1, data->num); + data->num = data->num + 1; +} + +WSLUA_CONSTRUCTOR DissectorTable_heuristic_list (lua_State *L) { + /* Gets a Lua array table of all heuristic list names - i.e., the string names you can + use for the first argument in Proto:register_heuristic(). + + Note: This is an expensive operation, and should only be used for troubleshooting. + + @since 1.11.3 + */ + dissector_tables_foreach_table_info_t data = { 1, L }; + + lua_newtable(L); + + dissector_all_heur_tables_foreach_table(heur_dissector_tables_list_func, (gpointer)&data, NULL); + + WSLUA_RETURN(1); /* The array table of registered heuristic list names */ +} + +WSLUA_CONSTRUCTOR DissectorTable_try_heuristics (lua_State *L) { + /* + Try all the dissectors in a given heuristic dissector table. + */ +#define WSLUA_ARG_DissectorTable_try_heuristics_LISTNAME 1 /* The name of the heuristic dissector. */ +#define WSLUA_ARG_DissectorTable_try_heuristics_TVB 2 /* The buffer to dissect. */ +#define WSLUA_ARG_DissectorTable_try_heuristics_PINFO 3 /* The packet info. */ +#define WSLUA_ARG_DissectorTable_try_heuristics_TREE 4 /* The tree on which to add the protocol items. */ + + const gchar* name = luaL_checkstring(L,WSLUA_ARG_DissectorTable_try_heuristics_LISTNAME); + Tvb tvb = checkTvb(L,WSLUA_ARG_DissectorTable_try_heuristics_TVB); + Pinfo pinfo = checkPinfo(L,WSLUA_ARG_DissectorTable_try_heuristics_PINFO); + TreeItem tree = checkTreeItem(L,WSLUA_ARG_DissectorTable_try_heuristics_TREE); + heur_dissector_list_t list; + heur_dtbl_entry_t *entry; + + if (!(name && tvb && pinfo && tree)) return 0; + + list = find_heur_dissector_list(name); + if (!list) { + luaL_error(L, "Heuristic list '%s' does not exist", name); + return 0; + } + + lua_pushboolean(L, dissector_try_heuristic(list, tvb->ws_tvb, pinfo->ws_pinfo, tree->tree, &entry, NULL)); + + WSLUA_RETURN(1); /* True if the packet was recognized by the sub-dissector (stop dissection here). */ +} + +WSLUA_CONSTRUCTOR DissectorTable_get (lua_State *L) { + /* + Obtain a reference to an existing dissector table. + */ +#define WSLUA_ARG_DissectorTable_get_TABLENAME 1 /* The short name of the table. */ + const gchar* name = luaL_checkstring(L,WSLUA_ARG_DissectorTable_get_TABLENAME); + dissector_table_t table = find_dissector_table(name); + + if (table) { + DissectorTable dt = (DissectorTable)g_malloc(sizeof(struct _wslua_distbl_t)); + dt->table = table; + dt->name = g_strdup(name); + dt->ui_name = NULL; + dt->created = FALSE; + dt->expired = FALSE; + + pushDissectorTable(L, dt); + } else { + lua_pushnil(L); + } + + WSLUA_RETURN(1); /* The <<lua_class_DissectorTable,`DissectorTable`>> reference if found, otherwise `nil`. */ +} + +WSLUA_METHOD DissectorTable_add (lua_State *L) { + /* + Add a <<lua_class_Proto,`Proto`>> with a dissector function or a <<lua_class_Dissector,`Dissector`>> object to the dissector table. + */ +#define WSLUA_ARG_DissectorTable_add_PATTERN 2 /* The pattern to match (either an integer, a + integer range or a string depending on the table's type). */ +#define WSLUA_ARG_DissectorTable_add_DISSECTOR 3 /* The dissector to add (either a <<lua_class_Proto,`Proto`>> or a <<lua_class_Dissector,`Dissector`>>). */ + + DissectorTable dt = checkDissectorTable(L,1); + ftenum_t type; + Dissector handle; + + if (!dt) return 0; + + if( isProto(L,WSLUA_ARG_DissectorTable_add_DISSECTOR) ) { + Proto p; + p = checkProto(L,WSLUA_ARG_DissectorTable_add_DISSECTOR); + handle = p->handle; + + if (! handle) { + WSLUA_ARG_ERROR(DissectorTable_add,DISSECTOR,"a Protocol that does not have a dissector cannot be added to a table"); + return 0; + } + + } else if ( isDissector(L,WSLUA_ARG_DissectorTable_add_DISSECTOR) ) { + handle = toDissector(L,WSLUA_ARG_DissectorTable_add_DISSECTOR); + } else { + WSLUA_ARG_ERROR(DissectorTable_add,DISSECTOR,"must be either Proto or Dissector"); + return 0; + } + + type = get_dissector_table_selector_type(dt->name); + + if (type == FT_STRING) { + gchar* pattern = g_strdup(luaL_checkstring(L,WSLUA_ARG_DissectorTable_add_PATTERN)); + dissector_add_string(dt->name, pattern,handle); + g_free (pattern); + } else if ( type == FT_UINT32 || type == FT_UINT16 || type == FT_UINT8 || type == FT_UINT24 ) { + if (lua_isnumber(L, WSLUA_ARG_DissectorTable_add_PATTERN)) { + int port = (int)luaL_checkinteger(L, WSLUA_ARG_DissectorTable_add_PATTERN); + dissector_add_uint(dt->name, port, handle); + } else { + /* Not a number, try as range */ + const gchar* pattern = luaL_checkstring(L,WSLUA_ARG_DissectorTable_add_PATTERN); + range_t *range = NULL; + if (range_convert_str(NULL, &range, pattern, G_MAXUINT32) == CVT_NO_ERROR) { + dissector_add_uint_range(dt->name, range, handle); + } else { + wmem_free (NULL, range); + WSLUA_ARG_ERROR(DissectorTable_add,PATTERN,"invalid integer or range"); + return 0; + } + wmem_free (NULL, range); + } + } else { + luaL_error(L,"Strange type %d for a DissectorTable",type); + } + + return 0; +} + +WSLUA_METHOD DissectorTable_set (lua_State *L) { + /* + Clear all existing dissectors from a table and add a new dissector or a range of new dissectors. + + @since 1.11.3 + */ +#define WSLUA_ARG_DissectorTable_set_PATTERN 2 /* The pattern to match (either an integer, a integer range or a string depending on the table's type). */ +#define WSLUA_ARG_DissectorTable_set_DISSECTOR 3 /* The dissector to add (either a <<lua_class_Proto,`Proto`>> or a <<lua_class_Dissector,`Dissector`>>). */ + + DissectorTable dt = checkDissectorTable(L,1); + ftenum_t type; + Dissector handle; + + if (!dt) return 0; + + if( isProto(L,WSLUA_ARG_DissectorTable_set_DISSECTOR) ) { + Proto p; + p = checkProto(L,WSLUA_ARG_DissectorTable_set_DISSECTOR); + handle = p->handle; + + if (! handle) { + WSLUA_ARG_ERROR(DissectorTable_set,DISSECTOR,"a Protocol that does not have a dissector cannot be set to a table"); + return 0; + } + + } else if ( isDissector(L,WSLUA_ARG_DissectorTable_set_DISSECTOR) ) { + handle = toDissector(L,WSLUA_ARG_DissectorTable_set_DISSECTOR); + } else { + WSLUA_ARG_ERROR(DissectorTable_set,DISSECTOR,"must be either Proto or Dissector"); + return 0; + } + + type = get_dissector_table_selector_type(dt->name); + + if (type == FT_STRING) { + const gchar* pattern = luaL_checkstring(L,WSLUA_ARG_DissectorTable_set_PATTERN); + dissector_delete_all(dt->name, handle); + dissector_add_string(dt->name, pattern,handle); + } else if ( type == FT_UINT32 || type == FT_UINT16 || type == FT_UINT8 || type == FT_UINT24 ) { + if (lua_isnumber(L, WSLUA_ARG_DissectorTable_set_PATTERN)) { + int port = (int)luaL_checkinteger(L, WSLUA_ARG_DissectorTable_set_PATTERN); + dissector_delete_all(dt->name, handle); + dissector_add_uint(dt->name, port, handle); + } else { + /* Not a number, try as range */ + const gchar* pattern = luaL_checkstring(L,WSLUA_ARG_DissectorTable_set_PATTERN); + range_t *range = NULL; + if (range_convert_str(NULL, &range, pattern, G_MAXUINT32) == CVT_NO_ERROR) { + dissector_delete_all(dt->name, handle); + dissector_add_uint_range(dt->name, range, handle); + } else { + wmem_free (NULL, range); + WSLUA_ARG_ERROR(DissectorTable_set,PATTERN,"invalid integer or range"); + return 0; + } + wmem_free (NULL, range); + } + } else { + luaL_error(L,"Strange type %d for a DissectorTable",type); + } + + return 0; +} + +WSLUA_METHOD DissectorTable_remove (lua_State *L) { + /* + Remove a dissector or a range of dissectors from a table. + */ +#define WSLUA_ARG_DissectorTable_remove_PATTERN 2 /* The pattern to match (either an integer, a integer range or a string depending on the table's type). */ +#define WSLUA_ARG_DissectorTable_remove_DISSECTOR 3 /* The dissector to remove (either a <<lua_class_Proto,`Proto`>> or a <<lua_class_Dissector,`Dissector`>>). */ + DissectorTable dt = checkDissectorTable(L,1); + ftenum_t type; + Dissector handle; + + if (!dt) return 0; + + if( isProto(L,WSLUA_ARG_DissectorTable_remove_DISSECTOR) ) { + Proto p; + p = checkProto(L,WSLUA_ARG_DissectorTable_remove_DISSECTOR); + handle = p->handle; + + } else if ( isDissector(L,WSLUA_ARG_DissectorTable_remove_DISSECTOR) ) { + handle = toDissector(L,WSLUA_ARG_DissectorTable_remove_DISSECTOR); + } else { + WSLUA_ARG_ERROR(DissectorTable_remove,DISSECTOR,"must be either Proto or Dissector"); + return 0; + } + + type = get_dissector_table_selector_type(dt->name); + + if (type == FT_STRING) { + gchar* pattern = g_strdup(luaL_checkstring(L,WSLUA_ARG_DissectorTable_remove_PATTERN)); + dissector_delete_string(dt->name, pattern,handle); + g_free (pattern); + } else if ( type == FT_UINT32 || type == FT_UINT16 || type == FT_UINT8 || type == FT_UINT24 ) { + if (lua_isnumber(L, WSLUA_ARG_DissectorTable_remove_PATTERN)) { + int port = (int)luaL_checkinteger(L, WSLUA_ARG_DissectorTable_remove_PATTERN); + dissector_delete_uint(dt->name, port, handle); + } else { + /* Not a number, try as range */ + const gchar* pattern = luaL_checkstring(L,WSLUA_ARG_DissectorTable_remove_PATTERN); + range_t *range = NULL; + if (range_convert_str(NULL, &range, pattern, G_MAXUINT32) == CVT_NO_ERROR) + dissector_delete_uint_range(dt->name, range, handle); + else { + wmem_free (NULL, range); + WSLUA_ARG_ERROR(DissectorTable_remove,PATTERN,"invalid integer or range"); + return 0; + } + wmem_free (NULL, range); + } + } + + return 0; +} + +WSLUA_METHOD DissectorTable_remove_all (lua_State *L) { + /* + Remove all dissectors from a table. + + @since 1.11.3 + */ +#define WSLUA_ARG_DissectorTable_remove_all_DISSECTOR 2 /* The dissector to remove (either a <<lua_class_Proto,`Proto`>> or a <<lua_class_Dissector,`Dissector`>>). */ + DissectorTable dt = checkDissectorTable(L,1); + Dissector handle; + + if (!dt) return 0; + + if( isProto(L,WSLUA_ARG_DissectorTable_remove_all_DISSECTOR) ) { + Proto p; + p = checkProto(L,WSLUA_ARG_DissectorTable_remove_all_DISSECTOR); + handle = p->handle; + + } else if ( isDissector(L,WSLUA_ARG_DissectorTable_remove_all_DISSECTOR) ) { + handle = toDissector(L,WSLUA_ARG_DissectorTable_remove_all_DISSECTOR); + } else { + WSLUA_ARG_ERROR(DissectorTable_remove_all,DISSECTOR,"must be either Proto or Dissector"); + return 0; + } + + dissector_delete_all (dt->name, handle); + + return 0; +} + +WSLUA_METHOD DissectorTable_try (lua_State *L) { + /* + Try to call a dissector from a table. + */ +#define WSLUA_ARG_DissectorTable_try_PATTERN 2 /* The pattern to be matched (either an integer or a string depending on the table's type). */ +#define WSLUA_ARG_DissectorTable_try_TVB 3 /* The <<lua_class_Tvb,`Tvb`>> to dissect. */ +#define WSLUA_ARG_DissectorTable_try_PINFO 4 /* The packet's <<lua_class_Pinfo,`Pinfo`>>. */ +#define WSLUA_ARG_DissectorTable_try_TREE 5 /* The <<lua_class_TreeItem,`TreeItem`>> on which to add the protocol items. */ + DissectorTable volatile dt = checkDissectorTable(L,1); + Tvb tvb = checkTvb(L,WSLUA_ARG_DissectorTable_try_TVB); + Pinfo pinfo = checkPinfo(L,WSLUA_ARG_DissectorTable_try_PINFO); + TreeItem ti = checkTreeItem(L,WSLUA_ARG_DissectorTable_try_TREE); + ftenum_t type; + gboolean handled = FALSE; + const gchar *volatile error = NULL; + int len = 0; + + if (! (dt && tvb && tvb->ws_tvb && pinfo && ti) ) return 0; + + type = get_dissector_table_selector_type(dt->name); + + TRY { + + if (type == FT_STRING) { + const gchar* pattern = luaL_checkstring(L,WSLUA_ARG_DissectorTable_try_PATTERN); + + len = dissector_try_string(dt->table,pattern,tvb->ws_tvb,pinfo->ws_pinfo,ti->tree, NULL); + if (len > 0) { + handled = TRUE; + } + } else if ( type == FT_UINT32 || type == FT_UINT16 || type == FT_UINT8 || type == FT_UINT24 ) { + int port = (int)luaL_checkinteger(L, WSLUA_ARG_DissectorTable_try_PATTERN); + + len = dissector_try_uint(dt->table,port,tvb->ws_tvb,pinfo->ws_pinfo,ti->tree); + if (len > 0) { + handled = TRUE; + } + } else { + error = "No such type of dissector table"; + } + + if (!handled) { + len = call_data_dissector(tvb->ws_tvb, pinfo->ws_pinfo, ti->tree); + } + /* XXX Are we sure about this??? is this the right/only thing to catch */ + } CATCH_NONFATAL_ERRORS { + show_exception(tvb->ws_tvb, pinfo->ws_pinfo, ti->tree, EXCEPT_CODE, GET_MESSAGE); + error = "Malformed frame"; + } ENDTRY; + + if (error) { WSLUA_ERROR(DissectorTable_try,error); } + + lua_pushnumber(L,(lua_Number)len); + WSLUA_RETURN(1); /* Number of bytes dissected. Note that some dissectors always return number of bytes in incoming buffer, so be aware. */ +} + +WSLUA_METHOD DissectorTable_get_dissector (lua_State *L) { + /* + Try to obtain a dissector from a table. + */ +#define WSLUA_ARG_DissectorTable_get_dissector_PATTERN 2 /* The pattern to be matched (either an integer or a string depending on the table's type). */ + + DissectorTable dt = checkDissectorTable(L,1); + ftenum_t type; + dissector_handle_t handle = NULL; + + if (!dt) return 0; + + type = get_dissector_table_selector_type(dt->name); + + if (type == FT_STRING) { + const gchar* pattern = luaL_checkstring(L,WSLUA_ARG_DissectorTable_get_dissector_PATTERN); + handle = dissector_get_string_handle(dt->table,pattern); + } else if ( type == FT_UINT32 || type == FT_UINT16 || type == FT_UINT8 || type == FT_UINT24 ) { + int port = (int)luaL_checkinteger(L, WSLUA_ARG_DissectorTable_get_dissector_PATTERN); + handle = dissector_get_uint_handle(dt->table,port); + } + + if (handle) { + pushDissector(L,handle); + } else { + lua_pushnil(L); + } + + WSLUA_RETURN(1); /* The <<lua_class_Dissector,`Dissector`>> handle if found, otherwise `nil` */ +} + +WSLUA_METHOD DissectorTable_add_for_decode_as (lua_State *L) { + /* + Add the given <<lua_class_Proto,`Proto`>> to the “Decode as...” list for this DissectorTable. + The passed-in <<lua_class_Proto,`Proto`>> object's `dissector()` function is used for dissecting. + + @since 1.99.1 + */ +#define WSLUA_ARG_DissectorTable_add_for_decode_as_PROTO 2 /* The <<lua_class_Proto,`Proto`>> to add. */ + DissectorTable dt = checkDissectorTable(L,1); + Proto proto = checkProto(L, WSLUA_ARG_DissectorTable_add_for_decode_as_PROTO); + dissector_handle_t handle = NULL; + + if (! proto->handle) { + proto->handle = register_dissector(proto->loname, dissect_lua, proto->hfid); + } + + handle = proto->handle; + + dissector_add_for_decode_as(dt->name, handle); + + return 0; +} + +/* XXX It would be nice to iterate and print which dissectors it has */ +WSLUA_METAMETHOD DissectorTable__tostring(lua_State* L) { + /* Gets some debug information about the <<lua_class_DissectorTable,`DissectorTable`>>. */ + DissectorTable dt = checkDissectorTable(L,1); + GString* s; + ftenum_t type; + + if (!dt) return 0; + + type = get_dissector_table_selector_type(dt->name); + s = g_string_new("DissectorTable "); + + switch(type) { + case FT_STRING: + { + g_string_append_printf(s,"%s String:\n",dt->name); + break; + } + case FT_UINT8: + case FT_UINT16: + case FT_UINT24: + case FT_UINT32: + { + int base = get_dissector_table_param(dt->name); + g_string_append_printf(s,"%s Integer(%i):\n",dt->name,base); + break; + } + case FT_NONE: + { + g_string_append_printf(s,"%s only for Decode As:\n",dt->name); + break; + } + default: + luaL_error(L,"Strange table type"); + } + + lua_pushstring(L,s->str); + g_string_free(s,TRUE); + WSLUA_RETURN(1); /* A string of debug information about the <<lua_class_DissectorTable,`DissectorTable`>>. */ +} + +/* Gets registered as metamethod automatically by WSLUA_REGISTER_CLASS/META */ +static int DissectorTable__gc(lua_State* L) { + DissectorTable dt = toDissectorTable(L,1); + + if (dt->created && !dt->expired) { + /* Created DissectorTable will pass GC two times */ + dt->expired = TRUE; + } else { + g_free((char *)dt->name); + g_free((char *)dt->ui_name); + g_free(dt); + } + + return 0; +} + +WSLUA_METHODS DissectorTable_methods[] = { + WSLUA_CLASS_FNREG(DissectorTable,new), + WSLUA_CLASS_FNREG(DissectorTable,heuristic_new), + WSLUA_CLASS_FNREG(DissectorTable,get), + WSLUA_CLASS_FNREG(DissectorTable,list), + WSLUA_CLASS_FNREG(DissectorTable,heuristic_list), + WSLUA_CLASS_FNREG(DissectorTable,try_heuristics), + WSLUA_CLASS_FNREG(DissectorTable,add), + WSLUA_CLASS_FNREG(DissectorTable,set), + WSLUA_CLASS_FNREG(DissectorTable,remove), + WSLUA_CLASS_FNREG(DissectorTable,remove_all), + WSLUA_CLASS_FNREG(DissectorTable,try), + WSLUA_CLASS_FNREG(DissectorTable,get_dissector), + WSLUA_CLASS_FNREG(DissectorTable,add_for_decode_as), + { NULL, NULL } +}; + +WSLUA_META DissectorTable_meta[] = { + WSLUA_CLASS_MTREG(DissectorTable,tostring), + { NULL, NULL } +}; + +int DissectorTable_register(lua_State* L) { + WSLUA_REGISTER_CLASS(DissectorTable); + + lua_newtable (L); + dissectortable_table_ref = luaL_ref(L, LUA_REGISTRYINDEX); + + return 0; +} + +int wslua_deregister_dissector_tables(lua_State* L) { + /* for each registered DissectorTable do... */ + lua_rawgeti(L, LUA_REGISTRYINDEX, dissectortable_table_ref); + for (lua_pushnil(L); lua_next(L, -2); lua_pop(L, 1)) { + DissectorTable dt = checkDissectorTable(L, -1); + if (dt->created) { + deregister_dissector_table(dt->name); + } + } + + lua_pop(L, 1); /* dissector_table_ref */ + + 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: + */ diff --git a/epan/wslua/wslua_dumper.c b/epan/wslua/wslua_dumper.c new file mode 100644 index 00000000..de6b2aac --- /dev/null +++ b/epan/wslua/wslua_dumper.c @@ -0,0 +1,634 @@ +/* + * wslua_dumper.c + * + * Wireshark's interface to the Lua Programming Language + * + * (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" + +#include <wiretap/wtap_opttypes.h> +#include <epan/wmem_scopes.h> + +/* WSLUA_MODULE Dumper Saving Capture Files + + The classes/functions defined in this module are for using a `Dumper` object to + make Wireshark save a capture file to disk. `Dumper` represents Wireshark's built-in + file format writers (see the `wtap_name_to_file_type_subtype` function). + + (The `wtap_filetypes` table is deprecated, and should + only be used in code that must run on Wireshark 3.4.3 and earlier 3.4 + releases or in Wireshark 3.2.11 and earlier 3.2.x releases.) + + To have a Lua script create its own file format writer, see the chapter titled + "Custom file format reading/writing". +*/ + +#include "wslua.h" +#include <math.h> + +WSLUA_CLASS_DEFINE(PseudoHeader,NOP); +/* + A pseudoheader to be used to save captured frames. + */ + +enum lua_pseudoheader_type { + PHDR_NONE, + PHDR_ETH, + PHDR_X25, + PHDR_ISDN, + PHDR_ATM, + PHDR_ASCEND, + PHDR_P2P, + PHDR_WIFI, + PHDR_COSINE, + PHDR_IRDA, + PHDR_NETTL, + PHDR_MTP2, + PHDR_K12 +}; + +struct lua_pseudo_header { + enum lua_pseudoheader_type type; + union wtap_pseudo_header* wph; +}; + +WSLUA_CONSTRUCTOR PseudoHeader_none(lua_State* L) { + /* + Creates a "no" pseudoheader. + + */ + PseudoHeader ph = (PseudoHeader)g_malloc(sizeof(struct lua_pseudo_header)); + ph->type = PHDR_NONE; + ph->wph = NULL; + + pushPseudoHeader(L,ph); + + WSLUA_RETURN(1); + /* A null pseudoheader */ +} + +WSLUA_CONSTRUCTOR PseudoHeader_eth(lua_State* L) { + /* + Creates an ethernet pseudoheader. + */ + +#define WSLUA_OPTARG_PseudoHeader_eth_FCSLEN 1 /* The fcs length */ + + PseudoHeader ph = (PseudoHeader)g_malloc(sizeof(struct lua_pseudo_header)); + ph->type = PHDR_ETH; + ph->wph = (union wtap_pseudo_header *)g_malloc(sizeof(union wtap_pseudo_header)); + ph->wph->eth.fcs_len = (gint)luaL_optinteger(L,WSLUA_OPTARG_PseudoHeader_eth_FCSLEN,-1); + + pushPseudoHeader(L,ph); + + WSLUA_RETURN(1); /* The ethernet pseudoheader */ +} + +WSLUA_CONSTRUCTOR PseudoHeader_atm(lua_State* L) { + /* + Creates an ATM pseudoheader. + */ +#define WSLUA_OPTARG_PseudoHeader_atm_AAL 1 /* AAL number */ +#define WSLUA_OPTARG_PseudoHeader_atm_VPI 2 /* VPI */ +#define WSLUA_OPTARG_PseudoHeader_atm_VCI 3 /* VCI */ +#define WSLUA_OPTARG_PseudoHeader_atm_CHANNEL 4 /* Channel */ +#define WSLUA_OPTARG_PseudoHeader_atm_CELLS 5 /* Number of cells in the PDU */ +#define WSLUA_OPTARG_PseudoHeader_atm_AAL5U2U 6 /* AAL5 User to User indicator */ +#define WSLUA_OPTARG_PseudoHeader_atm_AAL5LEN 7 /* AAL5 Len */ + + PseudoHeader ph = (PseudoHeader)g_malloc(sizeof(struct lua_pseudo_header)); + ph->type = PHDR_ATM; + ph->wph = (union wtap_pseudo_header *)g_malloc(sizeof(union wtap_pseudo_header)); + ph->wph->atm.aal = (guint8)luaL_optinteger(L,WSLUA_OPTARG_PseudoHeader_atm_AAL,5); + ph->wph->atm.vpi = (guint16)luaL_optinteger(L,WSLUA_OPTARG_PseudoHeader_atm_VPI,1); + ph->wph->atm.vci = (guint16)luaL_optinteger(L,WSLUA_OPTARG_PseudoHeader_atm_VCI,1); + ph->wph->atm.channel = (guint16)luaL_optinteger(L,WSLUA_OPTARG_PseudoHeader_atm_CHANNEL,0); + ph->wph->atm.cells = (guint16)luaL_optinteger(L,WSLUA_OPTARG_PseudoHeader_atm_CELLS,1); + ph->wph->atm.aal5t_u2u = (guint16)luaL_optinteger(L,WSLUA_OPTARG_PseudoHeader_atm_AAL5U2U,1); + ph->wph->atm.aal5t_len = (guint16)luaL_optinteger(L,WSLUA_OPTARG_PseudoHeader_atm_AAL5LEN,0); + + pushPseudoHeader(L,ph); + WSLUA_RETURN(1); + /* The ATM pseudoheader */ +} + +WSLUA_CONSTRUCTOR PseudoHeader_mtp2(lua_State* L) { + /* Creates an MTP2 PseudoHeader. */ +#define WSLUA_OPTARG_PseudoHeader_mtp2_SENT 1 /* True if the packet is sent, False if received. */ +#define WSLUA_OPTARG_PseudoHeader_mtp2_ANNEXA 2 /* True if annex A is used. */ +#define WSLUA_OPTARG_PseudoHeader_mtp2_LINKNUM 3 /* Link Number. */ + PseudoHeader ph = (PseudoHeader)g_malloc(sizeof(struct lua_pseudo_header)); + ph->type = PHDR_MTP2; + ph->wph = (union wtap_pseudo_header *)g_malloc(sizeof(union wtap_pseudo_header)); + ph->wph->mtp2.sent = (guint8)luaL_optinteger(L,WSLUA_OPTARG_PseudoHeader_mtp2_SENT,0); + ph->wph->mtp2.annex_a_used = (guint8)luaL_optinteger(L,WSLUA_OPTARG_PseudoHeader_mtp2_ANNEXA,0); + ph->wph->mtp2.link_number = (guint16)luaL_optinteger(L,WSLUA_OPTARG_PseudoHeader_mtp2_LINKNUM,0); + + pushPseudoHeader(L,ph); + WSLUA_RETURN(1); /* The MTP2 pseudoheader */ +} + +#if 0 +static int PseudoHeader_x25(lua_State* L) { luaL_error(L,"not implemented"); return 0; } +static int PseudoHeader_isdn(lua_State* L) { luaL_error(L,"not implemented"); return 0; } +static int PseudoHeader_ascend(lua_State* L) { luaL_error(L,"not implemented"); return 0; } +static int PseudoHeader_wifi(lua_State* L) { luaL_error(L,"not implemented"); return 0; } +static int PseudoHeader_cosine(lua_State* L) { luaL_error(L,"not implemented"); return 0; } +static int PseudoHeader_irda(lua_State* L) { luaL_error(L,"not implemented"); return 0; } +static int PseudoHeader_nettl(lua_State* L) { luaL_error(L,"not implemented"); return 0; } +static int PseudoHeader_k12(lua_State* L) { luaL_error(L,"not implemented"); return 0; } +#endif + +/* Gets registered as metamethod automatically by WSLUA_REGISTER_CLASS/META */ +static int PseudoHeader__gc(lua_State* L _U_) { + /* do NOT free PseudoHeader */ + return 0; +} + +WSLUA_METHODS PseudoHeader_methods[] = { + WSLUA_CLASS_FNREG(PseudoHeader,mtp2), + WSLUA_CLASS_FNREG(PseudoHeader,atm), + WSLUA_CLASS_FNREG(PseudoHeader,eth), + WSLUA_CLASS_FNREG(PseudoHeader,none), + {0,0} +}; + +WSLUA_META PseudoHeader_meta[] = { + {0,0} +}; + +int PseudoHeader_register(lua_State* L) { + WSLUA_REGISTER_CLASS(PseudoHeader) + return 0; +} + + +WSLUA_CLASS_DEFINE(Dumper,FAIL_ON_NULL("Dumper already closed")); + +static GHashTable* dumper_encaps = NULL; +#define DUMPER_ENCAP(d) GPOINTER_TO_INT(g_hash_table_lookup(dumper_encaps,d)) + +static const char* cross_plat_fname(const char* fname) { + static char fname_clean[256]; + char* f; + + (void) g_strlcpy(fname_clean,fname,255); + fname_clean[255] = '\0'; + + for(f = fname_clean; *f; f++) { + switch(*f) { + case '/': case '\\': + *f = *(G_DIR_SEPARATOR_S); + break; + default: + break; + } + } + + return fname_clean; +} + +WSLUA_CONSTRUCTOR Dumper_new(lua_State* L) { + /* + Creates a file to write packets. + `Dumper:new_for_current()` will probably be a better choice. + */ +#define WSLUA_ARG_Dumper_new_FILENAME 1 /* The name of the capture file to be created. */ +#define WSLUA_OPTARG_Dumper_new_FILETYPE 2 /* The type of the file to be created - a number returned by `wtap_name_to_file_type_subtype()`. + (The `wtap_filetypes` table + is deprecated, and should only be used + in code that must run on Wireshark 3.4.3 and earlier 3.4 releases + or in Wireshark 3.2.11 and earlier + 3.2.x releases.) */ +#define WSLUA_OPTARG_Dumper_new_ENCAP 3 /* The encapsulation to be used in the file to be created - a number entry from the `wtap_encaps` table. */ + Dumper d; + const char* fname = luaL_checkstring(L,WSLUA_ARG_Dumper_new_FILENAME); + int filetype = (int)luaL_optinteger(L,WSLUA_OPTARG_Dumper_new_FILETYPE,wtap_pcap_file_type_subtype()); + int encap = (int)luaL_optinteger(L,WSLUA_OPTARG_Dumper_new_ENCAP,WTAP_ENCAP_ETHERNET); + int err = 0; + gchar *err_info = NULL; + const char* filename = cross_plat_fname(fname); + wtap_dump_params params = WTAP_DUMP_PARAMS_INIT; + + params.encap = encap; + d = wtap_dump_open(filename, filetype, WTAP_UNCOMPRESSED, ¶ms, &err, + &err_info); + + if (! d ) { + /* WSLUA_ERROR("Error while opening file for writing"); */ + switch (err) { + case WTAP_ERR_NOT_REGULAR_FILE: + luaL_error(L,"The file \"%s\" is a \"special file\" or socket or other non-regular file", + filename); + break; + + case WTAP_ERR_CANT_WRITE_TO_PIPE: + luaL_error(L,"The file \"%s\" is a pipe, and %s capture files can't be written to a pipe", + filename, wtap_file_type_subtype_description(filetype)); + break; + + case WTAP_ERR_UNWRITABLE_FILE_TYPE: + luaL_error(L,"Files of file type %s cannot be written", + wtap_file_type_subtype_description(filetype)); + break; + + case WTAP_ERR_UNWRITABLE_ENCAP: + luaL_error(L,"Files of file type %s don't support encapsulation %s", + wtap_file_type_subtype_description(filetype), + wtap_encap_name(encap)); + break; + + case WTAP_ERR_ENCAP_PER_PACKET_UNSUPPORTED: + luaL_error(L,"Files of file type %s don't support per-packet encapsulation", + wtap_file_type_subtype_description(filetype)); + break; + + case WTAP_ERR_CANT_OPEN: + luaL_error(L,"The file \"%s\" could not be created for some unknown reason", + filename); + break; + + case WTAP_ERR_SHORT_WRITE: + luaL_error(L,"A full header couldn't be written to the file \"%s\".", + filename); + break; + + case WTAP_ERR_COMPRESSION_NOT_SUPPORTED: + luaL_error(L,"Files of file type %s cannot be written as a compressed file", + wtap_file_type_subtype_description(filetype)); + break; + + case WTAP_ERR_INTERNAL: + luaL_error(L,"An internal error occurred creating the file \"%s\" (%s)", + filename, + err_info != NULL ? err_info : "no information supplied"); + g_free(err_info); + break; + + default: + luaL_error(L,"error while opening \"%s\": %s", + filename, + wtap_strerror(err)); + break; + } + return 0; + } + + g_hash_table_insert(dumper_encaps,d,GINT_TO_POINTER(encap)); + + pushDumper(L,d); + WSLUA_RETURN(1); + /* The newly created Dumper object */ +} + +WSLUA_METHOD Dumper_close(lua_State* L) { + /* Closes a dumper. */ + Dumper* dp = (Dumper*)luaL_checkudata(L, 1, "Dumper"); + int err; + gchar *err_info; + + if (! *dp) { + WSLUA_ERROR(Dumper_close,"Cannot operate on a closed dumper"); + return 0; + } + + g_hash_table_remove(dumper_encaps,*dp); + + if (!wtap_dump_close(*dp, NULL, &err, &err_info)) { + if (err_info != NULL) { + luaL_error(L,"error closing: %s (%s)", + wtap_strerror(err), err_info); + g_free(err_info); + } else { + luaL_error(L,"error closing: %s", + wtap_strerror(err)); + } + } + + /* this way if we close a dumper any attempt to use it (for everything but GC) will yield an error */ + *dp = NULL; + + return 0; +} + +WSLUA_METHOD Dumper_flush(lua_State* L) { + /* + Writes all unsaved data of a dumper to the disk. + */ + Dumper d = checkDumper(L,1); + int err; + + if (!d) return 0; + + if (!wtap_dump_flush(d, &err)) { + luaL_error(L,"error while dumping: %s", + wtap_strerror(err)); + } + + return 0; +} + +WSLUA_METHOD Dumper_dump(lua_State* L) { + /* + Dumps an arbitrary packet. + Note: Dumper:dump_current() will fit best in most cases. + */ +#define WSLUA_ARG_Dumper_dump_TIMESTAMP 2 /* The absolute timestamp the packet will have. */ +#define WSLUA_ARG_Dumper_dump_PSEUDOHEADER 3 /* The `PseudoHeader` to use. */ +#define WSLUA_ARG_Dumper_dump_BYTEARRAY 4 /* The data to be saved */ + + Dumper d = checkDumper(L,1); + PseudoHeader ph; + ByteArray ba; + wtap_rec rec; + double ts; + int err; + gchar *err_info; + + if (!d) return 0; + + ts = luaL_checknumber(L,WSLUA_ARG_Dumper_dump_TIMESTAMP); + ph = checkPseudoHeader(L,WSLUA_ARG_Dumper_dump_PSEUDOHEADER); + + if (!ph) { + WSLUA_ARG_ERROR(Dumper_dump,PSEUDOHEADER,"need a PseudoHeader"); + return 0; + } + + ba = checkByteArray(L,WSLUA_ARG_Dumper_dump_BYTEARRAY); + + if (! ba) { + WSLUA_ARG_ERROR(Dumper_dump,BYTEARRAY,"must be a ByteArray"); + return 0; + } + + memset(&rec, 0, sizeof rec); + + rec.rec_type = REC_TYPE_PACKET; + + rec.presence_flags = WTAP_HAS_TS; + rec.ts.secs = (unsigned int)(floor(ts)); + rec.ts.nsecs = (unsigned int)(floor((ts - (double)rec.ts.secs) * 1000000000)); + + rec.rec_header.packet_header.len = ba->len; + rec.rec_header.packet_header.caplen = ba->len; + rec.rec_header.packet_header.pkt_encap = DUMPER_ENCAP(d); + if (ph->wph) { + rec.rec_header.packet_header.pseudo_header = *ph->wph; + } + + /* TODO: Can we get access to pinfo->rec->block here somehow? We + * should be copying it to pkthdr.pkt_block if we can. */ + + if (! wtap_dump(d, &rec, ba->data, &err, &err_info)) { + switch (err) { + + case WTAP_ERR_UNWRITABLE_REC_DATA: + luaL_error(L,"error while dumping: %s (%s)", + wtap_strerror(err), err_info); + g_free(err_info); + break; + + default: + luaL_error(L,"error while dumping: %s", + wtap_strerror(err)); + break; + } + } + + return 0; + +} + +WSLUA_METHOD Dumper_new_for_current(lua_State* L) { + /* + Creates a capture file using the same encapsulation as the one of the current packet. + */ +#define WSLUA_OPTARG_Dumper_new_for_current_FILETYPE 2 /* The file type. Defaults to pcap. */ + Dumper d; + const char* fname = luaL_checkstring(L,1); + int filetype = (int)luaL_optinteger(L,WSLUA_OPTARG_Dumper_new_for_current_FILETYPE,wtap_pcap_file_type_subtype()); + int encap; + int err = 0; + gchar *err_info = NULL; + const char* filename = cross_plat_fname(fname); + wtap_dump_params params = WTAP_DUMP_PARAMS_INIT; + + if (! lua_pinfo ) { + WSLUA_ERROR(Dumper_new_for_current,"Cannot be used outside a tap or a dissector"); + return 0; + } + + if (lua_pinfo->rec->rec_type != REC_TYPE_PACKET) { + return 0; + } + + encap = lua_pinfo->rec->rec_header.packet_header.pkt_encap; + params.encap = encap; + d = wtap_dump_open(filename, filetype, WTAP_UNCOMPRESSED, ¶ms, &err, + &err_info); + + if (! d ) { + switch (err) { + case WTAP_ERR_NOT_REGULAR_FILE: + luaL_error(L,"The file \"%s\" is a \"special file\" or socket or other non-regular file", + filename); + break; + + case WTAP_ERR_CANT_WRITE_TO_PIPE: + luaL_error(L,"The file \"%s\" is a pipe, and %s capture files can't be written to a pipe", + filename, wtap_file_type_subtype_description(filetype)); + break; + + case WTAP_ERR_UNWRITABLE_FILE_TYPE: + luaL_error(L,"Files of file type %s cannot be written", + wtap_file_type_subtype_description(filetype)); + break; + + case WTAP_ERR_UNWRITABLE_ENCAP: + luaL_error(L,"Files of file type %s don't support encapsulation %s", + wtap_file_type_subtype_description(filetype), + wtap_encap_name(encap)); + break; + + case WTAP_ERR_ENCAP_PER_PACKET_UNSUPPORTED: + luaL_error(L,"Files of file type %s don't support per-packet encapsulation", + wtap_file_type_subtype_description(filetype)); + break; + + case WTAP_ERR_CANT_OPEN: + luaL_error(L,"The file \"%s\" could not be created for some unknown reason", + filename); + break; + + case WTAP_ERR_SHORT_WRITE: + luaL_error(L,"A full header couldn't be written to the file \"%s\".", + filename); + break; + + case WTAP_ERR_COMPRESSION_NOT_SUPPORTED: + luaL_error(L,"Files of file type %s cannot be written as a compressed file", + wtap_file_type_subtype_description(filetype)); + break; + + case WTAP_ERR_INTERNAL: + luaL_error(L,"An internal error occurred creating the file \"%s\" (%s)", + filename, + err_info != NULL ? err_info : "no information supplied"); + g_free(err_info); + break; + + default: + luaL_error(L,"error while opening \"%s\": %s", + filename, + wtap_strerror(err)); + break; + } + return 0; + } + + pushDumper(L,d); + WSLUA_RETURN(1); /* The newly created Dumper Object */ + +} + +WSLUA_METHOD Dumper_dump_current(lua_State* L) { + /* + Dumps the current packet as it is. + */ + Dumper d = checkDumper(L,1); + wtap_rec rec; + const guchar* data; + tvbuff_t* tvb; + struct data_source *data_src; + int err = 0; + gchar *err_info; + + if (!d) return 0; + + if (! lua_pinfo ) { + WSLUA_ERROR(Dumper_new_for_current,"Cannot be used outside a tap or a dissector"); + return 0; + } + + if (lua_pinfo->rec->rec_type != REC_TYPE_PACKET) { + return 0; + } + + data_src = (struct data_source*) (lua_pinfo->data_src->data); + if (!data_src) + return 0; + + tvb = get_data_source_tvb(data_src); + + memset(&rec, 0, sizeof rec); + + rec.rec_type = REC_TYPE_PACKET; + rec.presence_flags = WTAP_HAS_TS|WTAP_HAS_CAP_LEN; + rec.ts = lua_pinfo->abs_ts; + rec.rec_header.packet_header.len = tvb_reported_length(tvb); + rec.rec_header.packet_header.caplen = tvb_captured_length(tvb); + rec.rec_header.packet_header.pkt_encap = lua_pinfo->rec->rec_header.packet_header.pkt_encap; + rec.rec_header.packet_header.pseudo_header = *lua_pinfo->pseudo_header; + + /* + * wtap_dump does not modify rec.block, so it should be possible to + * pass epan_get_modified_block() or lua_pinfo->rec->block directly. + * Temporarily duplicating the memory should not hurt though. + */ + if (lua_pinfo->fd->has_modified_block) { + rec.block = epan_get_modified_block(lua_pinfo->epan, lua_pinfo->fd); + rec.block_was_modified = TRUE; + } else { + rec.block = lua_pinfo->rec->block; + } + + data = (const guchar *)tvb_memdup(lua_pinfo->pool,tvb,0,rec.rec_header.packet_header.caplen); + + if (! wtap_dump(d, &rec, data, &err, &err_info)) { + switch (err) { + + case WTAP_ERR_UNWRITABLE_REC_DATA: + luaL_error(L,"error while dumping: %s (%s)", + wtap_strerror(err), err_info); + g_free(err_info); + break; + + default: + luaL_error(L,"error while dumping: %s", + wtap_strerror(err)); + break; + } + } + + return 0; +} + +/* Gets registered as metamethod automatically by WSLUA_REGISTER_CLASS/META */ +static int Dumper__gc(lua_State* L) { + Dumper* dp = (Dumper*)luaL_checkudata(L, 1, "Dumper"); + int err; + gchar *err_info; + + /* If we are Garbage Collected it means the Dumper is no longer usable. Close it */ + + if (! *dp) + return 0; /* already closed, nothing to do! */ + + g_hash_table_remove(dumper_encaps,*dp); + + if (!wtap_dump_close(*dp, NULL, &err, &err_info)) { + if (err_info != NULL) { + luaL_error(L,"error closing: %s (%s)", + wtap_strerror(err), err_info); + g_free(err_info); + } else { + luaL_error(L,"error closing: %s", + wtap_strerror(err)); + } + } + + return 0; +} + + +WSLUA_METHODS Dumper_methods[] = { + WSLUA_CLASS_FNREG(Dumper,new), + WSLUA_CLASS_FNREG(Dumper,new_for_current), + WSLUA_CLASS_FNREG(Dumper,close), + WSLUA_CLASS_FNREG(Dumper,flush), + WSLUA_CLASS_FNREG(Dumper,dump), + WSLUA_CLASS_FNREG(Dumper,dump_current), + { NULL, NULL } +}; + +WSLUA_META Dumper_meta[] = { + { NULL, NULL } +}; + +int Dumper_register(lua_State* L) { + dumper_encaps = g_hash_table_new(g_direct_hash,g_direct_equal); + WSLUA_REGISTER_CLASS(Dumper); + 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: + */ diff --git a/epan/wslua/wslua_field.c b/epan/wslua/wslua_field.c new file mode 100644 index 00000000..17db1974 --- /dev/null +++ b/epan/wslua/wslua_field.c @@ -0,0 +1,832 @@ +/* + * wslua_field.c + * + * Wireshark's interface to the Lua Programming Language + * + * (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" + +#include <epan/dfilter/dfilter.h> +#include <epan/ftypes/ftypes.h> + +/* WSLUA_MODULE Field Obtaining Dissection Data */ + +#include "wslua.h" + +/* any call to checkFieldInfo() will now error on null or expired, so no need to check again */ +WSLUA_CLASS_DEFINE(FieldInfo,FAIL_ON_NULL_OR_EXPIRED("FieldInfo")); +/* + An extracted Field from dissected packet data. A `FieldInfo` object can only be used within + the callback functions of dissectors, post-dissectors, heuristic-dissectors, and taps. + + A `FieldInfo` can be called on either existing Wireshark fields by using either `Field.new()` + or `Field()` before-hand, or it can be called on new fields created by Lua from a `ProtoField`. + */ + +static GPtrArray* outstanding_FieldInfo = NULL; + +FieldInfo* push_FieldInfo(lua_State* L, field_info* f) { + FieldInfo fi = (FieldInfo) g_malloc(sizeof(struct _wslua_field_info)); + fi->ws_fi = f; + fi->expired = FALSE; + g_ptr_array_add(outstanding_FieldInfo,fi); + return pushFieldInfo(L,fi); +} + +CLEAR_OUTSTANDING(FieldInfo,expired,TRUE) + +/* WSLUA_ATTRIBUTE FieldInfo_len RO The length of this field. */ +WSLUA_METAMETHOD FieldInfo__len(lua_State* L) { + /* + Obtain the Length of the field + */ + FieldInfo fi = checkFieldInfo(L,1); + + lua_pushnumber(L,fi->ws_fi->length); + return 1; +} + +/* WSLUA_ATTRIBUTE FieldInfo_offset RO The offset of this field. */ +WSLUA_METAMETHOD FieldInfo__unm(lua_State* L) { + /* + Obtain the Offset of the field + */ + FieldInfo fi = checkFieldInfo(L,1); + + lua_pushnumber(L,fi->ws_fi->start); + return 1; +} + +/* WSLUA_ATTRIBUTE FieldInfo_value RO The value of this field. */ +WSLUA_METAMETHOD FieldInfo__call(lua_State* L) { + /* + Obtain the Value of the field. + + Previous to 1.11.4, this function retrieved the value for most field types, + but for `ftypes.UINT_BYTES` it retrieved the `ByteArray` of the field's entire `TvbRange`. + In other words, it returned a `ByteArray` that included the leading length byte(s), + instead of just the *value* bytes. That was a bug, and has been changed in 1.11.4. + Furthermore, it retrieved an `ftypes.GUID` as a `ByteArray`, which is also incorrect. + + If you wish to still get a `ByteArray` of the `TvbRange`, use `fieldinfo.range` + to get the `TvbRange`, and then use `tvbrange:bytes()` to convert it to a `ByteArray`. + */ + FieldInfo fi = checkFieldInfo(L,1); + + switch(fi->ws_fi->hfinfo->type) { + case FT_BOOLEAN: + lua_pushboolean(L,(int)fvalue_get_uinteger64(fi->ws_fi->value)); + return 1; + case FT_CHAR: + case FT_UINT8: + case FT_UINT16: + case FT_UINT24: + case FT_UINT32: + case FT_FRAMENUM: + lua_pushnumber(L,(lua_Number)(fvalue_get_uinteger(fi->ws_fi->value))); + return 1; + case FT_INT8: + case FT_INT16: + case FT_INT24: + case FT_INT32: + lua_pushnumber(L,(lua_Number)(fvalue_get_sinteger(fi->ws_fi->value))); + return 1; + case FT_FLOAT: + case FT_DOUBLE: + lua_pushnumber(L,(lua_Number)(fvalue_get_floating(fi->ws_fi->value))); + return 1; + case FT_INT64: { + pushInt64(L,(Int64)(fvalue_get_sinteger64(fi->ws_fi->value))); + return 1; + } + case FT_UINT64: { + pushUInt64(L,fvalue_get_uinteger64(fi->ws_fi->value)); + return 1; + } + case FT_ETHER: { + Address eth = (Address)g_malloc(sizeof(address)); + alloc_address_tvb(NULL,eth,AT_ETHER,fi->ws_fi->length,fi->ws_fi->ds_tvb,fi->ws_fi->start); + pushAddress(L,eth); + return 1; + } + case FT_IPv4:{ + Address ipv4 = (Address)g_malloc(sizeof(address)); + alloc_address_tvb(NULL,ipv4,AT_IPv4,fi->ws_fi->length,fi->ws_fi->ds_tvb,fi->ws_fi->start); + pushAddress(L,ipv4); + return 1; + } + case FT_IPv6: { + Address ipv6 = (Address)g_malloc(sizeof(address)); + alloc_address_tvb(NULL,ipv6,AT_IPv6,fi->ws_fi->length,fi->ws_fi->ds_tvb,fi->ws_fi->start); + pushAddress(L,ipv6); + return 1; + } + case FT_FCWWN: { + Address fcwwn = (Address)g_malloc(sizeof(address)); + alloc_address_tvb(NULL,fcwwn,AT_FCWWN,fi->ws_fi->length,fi->ws_fi->ds_tvb,fi->ws_fi->start); + pushAddress(L,fcwwn); + return 1; + } + case FT_IPXNET:{ + Address ipx = (Address)g_malloc(sizeof(address)); + alloc_address_tvb(NULL,ipx,AT_IPX,fi->ws_fi->length,fi->ws_fi->ds_tvb,fi->ws_fi->start); + pushAddress(L,ipx); + return 1; + } + case FT_ABSOLUTE_TIME: + case FT_RELATIVE_TIME: { + NSTime nstime = (NSTime)g_malloc(sizeof(nstime_t)); + *nstime = *fvalue_get_time(fi->ws_fi->value); + pushNSTime(L,nstime); + return 1; + } + case FT_STRING: + case FT_STRINGZ: + case FT_STRINGZPAD: { + gchar* repr = fvalue_to_string_repr(NULL, fi->ws_fi->value, FTREPR_DISPLAY, BASE_NONE); + if (repr) + { + lua_pushstring(L, repr); + wmem_free(NULL, repr); + } + else + { + luaL_error(L,"field cannot be represented as string because it may contain invalid characters"); + } + return 1; + } + case FT_NONE: + if (fi->ws_fi->length > 0 && fi->ws_fi->rep) { + /* it has a length, but calling fvalue_get() on an FT_NONE asserts, + so get the label instead (it's a FT_NONE, so a label is what it basically is) */ + lua_pushstring(L, fi->ws_fi->rep->representation); + return 1; + } + return 0; + case FT_BYTES: + case FT_UINT_BYTES: + case FT_REL_OID: + case FT_SYSTEM_ID: + case FT_OID: + { + ByteArray ba = g_byte_array_new(); + g_byte_array_append(ba, fvalue_get_bytes_data(fi->ws_fi->value), + (guint)fvalue_length2(fi->ws_fi->value)); + pushByteArray(L,ba); + return 1; + } + case FT_PROTOCOL: + { + ByteArray ba = g_byte_array_new(); + tvbuff_t* tvb = fvalue_get_protocol(fi->ws_fi->value); + guint8* raw; + if (tvb != NULL) { + raw = (guint8 *)tvb_memdup(NULL, tvb, 0, tvb_captured_length(tvb)); + g_byte_array_append(ba, raw, tvb_captured_length(tvb)); + wmem_free(NULL, raw); + } + + pushByteArray(L,ba); + return 1; + } + + case FT_GUID: + default: + luaL_error(L,"FT_ not yet supported"); + return 1; + } +} + +/* WSLUA_ATTRIBUTE FieldInfo_label RO The string representing this field. */ +WSLUA_METAMETHOD FieldInfo__tostring(lua_State* L) { + /* The string representation of the field. */ + FieldInfo fi = checkFieldInfo(L,1); + + gchar* repr = NULL; + + if (fi->ws_fi->hfinfo->type == FT_PROTOCOL) { + repr = fvalue_to_string_repr(NULL, fi->ws_fi->value,FTREPR_DFILTER,BASE_NONE); + } + else { + repr = fvalue_to_string_repr(NULL, fi->ws_fi->value,FTREPR_DISPLAY,fi->ws_fi->hfinfo->display); + } + + if (repr) { + lua_pushstring(L,repr); + /* fvalue_to_string_repr() wmem_alloc's the string's buffer */ + wmem_free(NULL, repr); + } + else { + lua_pushstring(L,"(unknown)"); + } + + return 1; +} + +/* WSLUA_ATTRIBUTE FieldInfo_display RO The string display of this field as seen in GUI. */ +static int FieldInfo_get_display(lua_State* L) { + /* The display string of this field as seen in GUI. */ + FieldInfo fi = checkFieldInfo(L,1); + gchar label_str[ITEM_LABEL_LENGTH+1]; + gchar *label_ptr; + gchar *value_ptr; + + if (!fi->ws_fi->rep) { + label_ptr = label_str; + proto_item_fill_label(fi->ws_fi, label_str); + } else + label_ptr = fi->ws_fi->rep->representation; + + if (!label_ptr) return 0; + + value_ptr = strstr(label_ptr, ": "); + if (!value_ptr) { + /* just use whatever's there */ + lua_pushstring(L, label_ptr); + } else { + value_ptr += 2; /* get past the ': ' */ + lua_pushstring(L, value_ptr); + } + + return 1; +} + +/* WSLUA_ATTRIBUTE FieldInfo_type RO The internal field type, a number which + matches one of the `ftype` values. + + @since 1.99.8 + */ +static int FieldInfo_get_type(lua_State* L) { + FieldInfo fi = checkFieldInfo(L,1); + + if (fi->ws_fi->hfinfo) { + lua_pushnumber(L, fi->ws_fi->hfinfo->type); + } + else { + lua_pushnil(L); + } + + return 1; +} + +/* WSLUA_ATTRIBUTE FieldInfo_source RO The source `Tvb` object the `FieldInfo` is derived + from, or nil if there is none. + + @since 1.99.8 + */ +static int FieldInfo_get_source(lua_State* L) { + FieldInfo fi = checkFieldInfo(L,1); + + if (fi->ws_fi->ds_tvb) { + push_Tvb(L, fi->ws_fi->ds_tvb); + } + else { + lua_pushnil(L); + } + + return 1; +} + +/* WSLUA_ATTRIBUTE FieldInfo_range RO The `TvbRange` covering the bytes of this field in a Tvb or nil if there is none. */ +static int FieldInfo_get_range(lua_State* L) { + FieldInfo fi = checkFieldInfo(L,1); + + if (!fi->ws_fi->ds_tvb) { + lua_pushnil(L); + return 1; + } + + if (push_TvbRange (L, fi->ws_fi->ds_tvb, fi->ws_fi->start, fi->ws_fi->length)) { + return 1; + } + + return 0; +} + +/* WSLUA_ATTRIBUTE FieldInfo_generated RO Whether this field was marked as generated (boolean). */ +static int FieldInfo_get_generated(lua_State* L) { + /* Whether this field was marked as generated. */ + FieldInfo fi = checkFieldInfo(L,1); + + lua_pushboolean(L,FI_GET_FLAG(fi->ws_fi, FI_GENERATED)); + return 1; +} + +/* WSLUA_ATTRIBUTE FieldInfo_hidden RO Whether this field was marked as hidden (boolean). + + @since 1.99.8 + */ +static int FieldInfo_get_hidden(lua_State* L) { + FieldInfo fi = checkFieldInfo(L,1); + + lua_pushboolean(L,FI_GET_FLAG(fi->ws_fi, FI_HIDDEN)); + return 1; +} + +/* WSLUA_ATTRIBUTE FieldInfo_is_url RO Whether this field was marked as being a URL (boolean). + + @since 1.99.8 + */ +static int FieldInfo_get_is_url(lua_State* L) { + FieldInfo fi = checkFieldInfo(L,1); + + lua_pushboolean(L,FI_GET_FLAG(fi->ws_fi, FI_URL)); + return 1; +} + +/* WSLUA_ATTRIBUTE FieldInfo_little_endian RO Whether this field is little-endian encoded (boolean). + + @since 1.99.8 + */ +static int FieldInfo_get_little_endian(lua_State* L) { + FieldInfo fi = checkFieldInfo(L,1); + + lua_pushboolean(L,FI_GET_FLAG(fi->ws_fi, FI_LITTLE_ENDIAN)); + return 1; +} + +/* WSLUA_ATTRIBUTE FieldInfo_big_endian RO Whether this field is big-endian encoded (boolean). + + @since 1.99.8 + */ +static int FieldInfo_get_big_endian(lua_State* L) { + FieldInfo fi = checkFieldInfo(L,1); + + lua_pushboolean(L,FI_GET_FLAG(fi->ws_fi, FI_BIG_ENDIAN)); + return 1; +} + +/* WSLUA_ATTRIBUTE FieldInfo_name RO The filter name of this field. + + @since 1.99.8 + */ +static int FieldInfo_get_name(lua_State* L) { + /* The filter name of this field. */ + FieldInfo fi = checkFieldInfo(L,1); + + lua_pushstring(L,fi->ws_fi->hfinfo->abbrev); + return 1; +} + +WSLUA_METAMETHOD FieldInfo__eq(lua_State* L) { + /* Checks whether lhs is within rhs. */ + FieldInfo l = checkFieldInfo(L,1); + FieldInfo r = checkFieldInfo(L,2); + + /* it is not an error if their ds_tvb are different... they're just not equal */ + if (l->ws_fi->ds_tvb == r->ws_fi->ds_tvb && + l->ws_fi->start == r->ws_fi->start && + r->ws_fi->length == l->ws_fi->length) { + lua_pushboolean(L,1); + } else { + lua_pushboolean(L,0); + } + return 1; +} + +WSLUA_METAMETHOD FieldInfo__le(lua_State* L) { + /* Checks whether the end byte of lhs is before the end of rhs. */ + FieldInfo l = checkFieldInfo(L,1); + FieldInfo r = checkFieldInfo(L,2); + + if (l->ws_fi->ds_tvb != r->ws_fi->ds_tvb) + WSLUA_ERROR(FieldInfo__le,"Data source must be the same for both fields"); + + if (l->ws_fi->start + l->ws_fi->length <= r->ws_fi->start + r->ws_fi->length) { + lua_pushboolean(L,1); + } else { + lua_pushboolean(L,0); + } + return 1; +} + +WSLUA_METAMETHOD FieldInfo__lt(lua_State* L) { + /* Checks whether the end byte of lhs is before the beginning of rhs. */ + FieldInfo l = checkFieldInfo(L,1); + FieldInfo r = checkFieldInfo(L,2); + + if (l->ws_fi->ds_tvb != r->ws_fi->ds_tvb) { + WSLUA_ERROR(FieldInfo__lt,"Data source must be the same for both fields"); + return 0; + } + + if (l->ws_fi->start + l->ws_fi->length <= r->ws_fi->start) { + lua_pushboolean(L,1); + } else { + lua_pushboolean(L,0); + } + return 1; +} + +/* Gets registered as metamethod automatically by WSLUA_REGISTER_META */ +static int FieldInfo__gc(lua_State* L) { + FieldInfo fi = toFieldInfo(L,1); + + if (!fi) return 0; + + if (!fi->expired) + fi->expired = TRUE; + else + /* do NOT free fi->ws_fi */ + g_free(fi); + + 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 FieldInfo_attributes[] = { + WSLUA_ATTRIBUTE_ROREG(FieldInfo,range), + WSLUA_ATTRIBUTE_ROREG(FieldInfo,generated), + WSLUA_ATTRIBUTE_ROREG(FieldInfo,hidden), + WSLUA_ATTRIBUTE_ROREG(FieldInfo,is_url), + WSLUA_ATTRIBUTE_ROREG(FieldInfo,little_endian), + WSLUA_ATTRIBUTE_ROREG(FieldInfo,big_endian), + WSLUA_ATTRIBUTE_ROREG(FieldInfo,name), + WSLUA_ATTRIBUTE_ROREG(FieldInfo,display), + WSLUA_ATTRIBUTE_ROREG(FieldInfo,type), + WSLUA_ATTRIBUTE_ROREG(FieldInfo,source), + { "label", FieldInfo__tostring, NULL }, + { "value", FieldInfo__call, NULL }, + { "tvb", FieldInfo_get_range, NULL }, + { "len", FieldInfo__len, NULL }, + { "offset", FieldInfo__unm, NULL }, + { NULL, NULL, NULL } +}; + +WSLUA_META FieldInfo_meta[] = { + WSLUA_CLASS_MTREG(FieldInfo,tostring), + WSLUA_CLASS_MTREG(FieldInfo,call), + WSLUA_CLASS_MTREG(FieldInfo,len), + WSLUA_CLASS_MTREG(FieldInfo,unm), + WSLUA_CLASS_MTREG(FieldInfo,eq), + WSLUA_CLASS_MTREG(FieldInfo,le), + WSLUA_CLASS_MTREG(FieldInfo,lt), + { NULL, NULL } +}; + +int FieldInfo_register(lua_State* L) { + WSLUA_REGISTER_META_WITH_ATTRS(FieldInfo); + return 0; +} + + +WSLUA_FUNCTION wslua_all_field_infos(lua_State* L) { + /* + Obtain all fields from the current tree. Note this only gets whatever fields the underlying + dissectors have filled in for this packet at this time - there may be fields applicable to + the packet that simply aren't being filled in because at this time they're not needed for anything. + This function only gets what the C-side code has currently populated, not the full list. + */ + GPtrArray* found; + int items_found = 0; + guint i; + + if (! lua_tree || ! lua_tree->tree ) { + WSLUA_ERROR(wslua_all_field_infos,"Cannot be called outside a listener or dissector"); + return 0; + } + + found = proto_all_finfos(lua_tree->tree); + + if (found) { + for (i=0; i<found->len; i++) { + push_FieldInfo(L, (field_info *)g_ptr_array_index(found,i)); + items_found++; + } + + g_ptr_array_free(found,TRUE); + } + + return items_found; +} + +WSLUA_CLASS_DEFINE(Field,FAIL_ON_NULL("Field")); +/* + A Field extractor to obtain field values. A `Field` object can only be created *outside* of + the callback functions of dissectors, post-dissectors, heuristic-dissectors, and taps. + + Once created, it is used *inside* the callback functions, to generate a `FieldInfo` object. + */ + +/* Array of Field (struct _wslua_header_field_info*) pointers.*/ +static GPtrArray* wanted_fields = NULL; +static dfilter_t* wslua_dfilter = NULL; + +/* We use a fake dfilter for Lua field extractors, so that + * epan_dissect_run() will populate the fields. This won't happen + * if the passed-in edt->tree is NULL, which it will be if the + * proto_tree isn't created by epan_dissect_init(). But that's by + * design - if shark doesn't pass in a proto_tree, it's probably for + * a good reason and we shouldn't override that. (right?) + */ +void wslua_prime_dfilter(epan_dissect_t *edt) { + if (wslua_dfilter && edt && edt->tree) { + dfilter_prime_proto_tree(wslua_dfilter, edt->tree); + } +} + +/* Check if we have any registered field extractors. */ +gboolean wslua_has_field_extractors(void) { + return (wslua_dfilter && dfilter_has_interesting_fields(wslua_dfilter)); +} + +/* + * field extractor registration is tricky, In order to allow + * the user to define them in the body of the script we will + * populate the Field value with a pointer of the abbrev of it + * to later replace it with the hfi. + * + * This will be added to the wanted_fields array that will + * exists only while they can be defined, and be cleared right + * after the fields are primed. + */ + +static gboolean fake_tap = FALSE; +void lua_prime_all_fields(proto_tree* tree _U_) { + GString* fake_tap_filter = g_string_new("frame"); + guint i; + df_error_t *df_err; + + for(i=0; i < wanted_fields->len; i++) { + Field f = (Field)g_ptr_array_index(wanted_fields,i); + + f->hfi = proto_registrar_get_byname(f->name); + if (!f->hfi) { + report_failure("Could not find field `%s'", f->name); + continue; + } + + g_string_append_printf(fake_tap_filter, " || %s", f->hfi->abbrev); + fake_tap = TRUE; + } + + g_ptr_array_free(wanted_fields,TRUE); + wanted_fields = NULL; + + if (fake_tap && fake_tap_filter->len > strlen("frame")) { + /* a boring tap :-) */ + GString* error = register_tap_listener("frame", + &fake_tap, + fake_tap_filter->str, + 0, /* XXX - do we need the protocol tree or columns? */ + NULL, NULL, NULL, NULL); + + if (error) { + report_failure("while registering lua_fake_tap:\n%s",error->str); + g_string_free(error,TRUE); + } else if (!dfilter_compile(fake_tap_filter->str, &wslua_dfilter, &df_err)) { + report_failure("while compiling dfilter \"%s\" for wslua: %s", fake_tap_filter->str, df_err->msg); + df_error_free(&df_err); + } + } + g_string_free(fake_tap_filter, TRUE); +} + +WSLUA_CONSTRUCTOR Field_new(lua_State *L) { + /* + Create a Field extractor. + */ +#define WSLUA_ARG_Field_new_FIELDNAME 1 /* The filter name of the field (e.g. ip.addr) */ + const gchar* name = luaL_checkstring(L,WSLUA_ARG_Field_new_FIELDNAME); + Field f; + + if (!proto_registrar_get_byname(name) && !wslua_is_field_available(L, name)) { + WSLUA_ARG_ERROR(Field_new,FIELDNAME,"a field with this name must exist"); + return 0; + } + + if (!wanted_fields) { + WSLUA_ERROR(Field_new,"A Field extractor must be defined before Taps or Dissectors get called"); + return 0; + } + + f = (Field)g_new0(struct _wslua_header_field_info, 1); + f->name = g_strdup(name); + + g_ptr_array_add(wanted_fields, f); + + pushField(L,f); + WSLUA_RETURN(1); /* The field extractor */ +} + +WSLUA_CONSTRUCTOR Field_list(lua_State *L) { + /* Gets a Lua array table of all registered field filter names. + + NOTE: This is an expensive operation, and should only be used for troubleshooting. + + @since 1.11.3 + */ + void *cookie, *cookie2; + int i = -1; + int count = 0; + header_field_info *hfinfo = NULL; + + lua_newtable(L); + + for (i = proto_get_first_protocol(&cookie); i != -1; + i = proto_get_next_protocol(&cookie)) { + + for (hfinfo = proto_get_first_protocol_field(i, &cookie2); hfinfo != NULL; + hfinfo = proto_get_next_protocol_field(i, &cookie2)) { + + if (hfinfo->same_name_prev_id != -1) /* ignore duplicate names */ + continue; + + count++; + lua_pushstring(L,hfinfo->abbrev); + lua_rawseti(L,-2,count); + } + } + + WSLUA_RETURN(1); /* The array table of field filter names */ +} + +/* the following is used in Field_get_xxx functions later. If called early + * (wanted_fields is not NULL), it will try to retrieve information directly. + * Otherwise it uses a cached field that was loaded in lua_prime_all_fields. */ +#define GET_HFINFO_MEMBER(luafunc, member) \ + if (wanted_fields) { \ + hfinfo = proto_registrar_get_byname(f->name); \ + if (!hfinfo) { \ + /* could be a Lua-created field */ \ + ProtoField pf = wslua_is_field_available(L, f->name); \ + if (pf) { \ + luafunc(L, pf->member); \ + return 1; \ + } \ + } \ + } else { \ + hfinfo = f->hfi; \ + } \ + \ + if (hfinfo) { \ + luafunc(L,hfinfo->member); \ + } else \ + lua_pushnil(L) + + +/* WSLUA_ATTRIBUTE Field_name RO The filter name of this field, or nil. + + @since 1.99.8 + */ +static int Field_get_name(lua_State* L) { + Field f = checkField(L,1); + header_field_info* hfinfo = NULL; + + GET_HFINFO_MEMBER(lua_pushstring, abbrev); + + return 1; +} + +/* WSLUA_ATTRIBUTE Field_display RO The full display name of this field, or nil. + + @since 1.99.8 + */ +static int Field_get_display(lua_State* L) { + Field f = checkField(L,1); + header_field_info* hfinfo = NULL; + + GET_HFINFO_MEMBER(lua_pushstring, name); + + return 1; +} + +/* WSLUA_ATTRIBUTE Field_type RO The `ftype` of this field, or nil. + + @since 1.99.8 + */ +static int Field_get_type(lua_State* L) { + Field f = checkField(L,1); + header_field_info* hfinfo = NULL; + + GET_HFINFO_MEMBER(lua_pushnumber, type); + + return 1; +} + +WSLUA_METAMETHOD Field__call (lua_State* L) { + /* Obtain all values (see `FieldInfo`) for this field. */ + Field f = checkField(L,1); + header_field_info* in = f->hfi; + int items_found = 0; + + if (! in) { + luaL_error(L,"invalid field"); + return 0; + } + + if (! lua_pinfo ) { + WSLUA_ERROR(Field__call,"Fields cannot be used outside dissectors or taps"); + return 0; + } + + while (in) { + GPtrArray* found = proto_get_finfo_ptr_array(lua_tree->tree, in->id); + guint i; + if (found) { + for (i=0; i<found->len; i++) { + push_FieldInfo(L, (field_info *) g_ptr_array_index(found,i)); + items_found++; + } + } + in = (in->same_name_prev_id != -1) ? proto_registrar_get_nth(in->same_name_prev_id) : NULL; + } + + WSLUA_RETURN(items_found); /* All the values of this field */ +} + +WSLUA_METAMETHOD Field__tostring(lua_State* L) { + /* Obtain a string with the field filter name. */ + Field f = checkField(L,1); + + if (f->hfi) { + /* If a field was found, return the actual field info. */ + lua_pushstring(L, f->hfi->abbrev); + } else { + lua_pushstring(L, f->name); + } + + return 1; +} + +static int Field__gc(lua_State* L) { + Field f = toField(L,1); + if (!f) return 0; + + // If out of scope before lua_prime_all_fields is even called, be sure to + // remove the pointer to avoid a use-after-free. + if (wanted_fields) { + g_ptr_array_remove_fast(wanted_fields, f); + } + + g_free(f->name); + g_free(f); + return 0; +} + +WSLUA_ATTRIBUTES Field_attributes[] = { + WSLUA_ATTRIBUTE_ROREG(Field,name), + WSLUA_ATTRIBUTE_ROREG(Field,display), + WSLUA_ATTRIBUTE_ROREG(Field,type), + { NULL, NULL, NULL } +}; + +WSLUA_METHODS Field_methods[] = { + WSLUA_CLASS_FNREG(Field,new), + WSLUA_CLASS_FNREG(Field,list), + { NULL, NULL } +}; + +WSLUA_META Field_meta[] = { + WSLUA_CLASS_MTREG(Field,tostring), + WSLUA_CLASS_MTREG(Field,call), + { NULL, NULL } +}; + +int Field_register(lua_State* L) { + + wanted_fields = g_ptr_array_new(); + + WSLUA_REGISTER_CLASS_WITH_ATTRS(Field); + outstanding_FieldInfo = g_ptr_array_new(); + + return 0; +} + +int wslua_deregister_fields(lua_State* L _U_) { + if (wslua_dfilter) { + dfilter_free(wslua_dfilter); + wslua_dfilter = NULL; + } + + if (fake_tap) { + remove_tap_listener(&fake_tap); + fake_tap = FALSE; + } + + 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: + */ diff --git a/epan/wslua/wslua_file.c b/epan/wslua/wslua_file.c new file mode 100644 index 00000000..2621a997 --- /dev/null +++ b/epan/wslua/wslua_file.c @@ -0,0 +1,521 @@ +/* + * wslua_file.c + * + * Wireshark's interface to the Lua Programming Language + * for custom file format reading/writing. + * + * (c) 2014, Hadriel Kaplan <hadrielk@yahoo.com> + * + * 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" + +#include "wslua_file_common.h" + +#include <errno.h> +#include <wiretap/file_wrappers.h> + +#define MAX_LINE_LENGTH 65536 + +/* WSLUA_MODULE File Custom File Format Reading And Writing + + The classes/functions defined in this section allow you to create your own + custom Lua-based "capture" file reader, or writer, or both. + + @since 1.11.3 + */ + + +WSLUA_CLASS_DEFINE(File,FAIL_ON_NULL_OR_EXPIRED("File")); +/* + 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)`. (see later example) + + 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. + + 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. + + 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. + + Note: A `File` object should never be stored/saved beyond the scope of the callback function + it is passed in to. + + For example: + + [source,lua] + ---- + function myfilehandler.read_open(file, capture) + local position = file:seek() + + -- read 24 bytes + local line = file:read(24) + + -- do stuff + + -- it's not our file type, seek back (unnecessary but just to show it...) + file:seek("set",position) + + -- return false because it's not our file type + return false + end + ---- + + @since 1.11.3 + */ + + +/* 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 WFILE_T member, but we can't only store its pointer here because + dump operations need the whole thing to write out with. Ugh. */ +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); +} + +File* push_Wdh(lua_State* L, wtap_dumper *wdh) { + File f = (File) g_malloc(sizeof(struct _wslua_file)); + f->file = (FILE_T)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 */ + } +} + +/** + * Attempts to read one line from the file. The actual data read is pushed on + * the stack (or nil on EOF). + */ +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); */ + /* io.lines() and file:read() requires nil on EOF */ + lua_pushnil(L); + return 0; + } + + /* Set length (avoiding strlen()) */ + length = (gint)(file_tell(ft) - pos_before); + + /* ...but don't want to include newline in line length */ + if (length > 0 && linebuff[length-1] == '\n') { + length--; + /* Nor do we want '\r' (as will be written when log is created on windows) */ + if (length > 0 && linebuff[length - 1] == '\r') { + length--; + } + linebuff[length] = '\0'; + } + + lua_pushlstring(L, linebuff, length); + return 1; +} + +/* This internal function reads X number of bytes from 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 + +/** + * Reads some data and returns the number of bytes read. + * The actual data (possibly an empty string) is pushed on the Lua stack. + */ +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, g_strerror(en)); + else + lua_pushfstring(L, "%s", g_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) { + ws_warning("Error in File read: Lua File has expired"); + return 0; + } + + if (!file_is_reader(f)) { + ws_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; + + + 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", g_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)) { + ws_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) { + ws_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", g_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) { + File f = toFile(L,1); + g_free(f); + return 0; +} + +/* WSLUA_ATTRIBUTE File_compressed RO Whether the File is compressed or not. + + See `wtap_encaps` 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->compression_type != WTAP_UNCOMPRESSED); + } + 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_WITH_ATTRS(File); + 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: + */ diff --git a/epan/wslua/wslua_file_common.c b/epan/wslua/wslua_file_common.c new file mode 100644 index 00000000..ab0f2a65 --- /dev/null +++ b/epan/wslua/wslua_file_common.c @@ -0,0 +1,196 @@ +/* + * wslua_file_common.c + * + * Wireshark's interface to the Lua Programming Language + * for file handling related source file internal functions. + * + * (c) 2014, Hadriel Kaplan <hadrielk@yahoo.com> + * + * Wireshark - Network traffic analyzer + * By Gerald Combs <gerald@wireshark.org> + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +/************ + * The following is for handling private data for the duration of the file + * read_open/read/close cycle, or write_open/write/write_close cycle. + * In other words it handles the "priv" member of wtap and wtap_dumper, + * but for the Lua script's use. A Lua script can set a Lua table + * to CaptureInfo/CaptureInfoConst and have it saved and retrievable this way. + * We need to offer that, because there needs to be a way for Lua scripts + * to save state for a given file's operations cycle. Since there can be + * two files opened at the same time for the same Lua script (due to reload + * and other such events), the script can't just have one file state. + */ +#include "config.h" + +#include "wslua_file_common.h" + + +/* create and set the wtap->priv private data for the file instance */ +void create_wth_priv(lua_State* L, wtap *wth) { + file_priv_t *priv = g_new(file_priv_t, 1); + + if (wth->priv != NULL) { + g_free(priv); + luaL_error(L, "Cannot create wtap private data because there already is private data"); + return; + } + priv->table_ref = LUA_NOREF; + wth->priv = (void*) priv; +} + +/* gets the private data table from wtap */ +int get_wth_priv_table_ref(lua_State* L, wtap *wth) { + file_priv_t *priv = (file_priv_t*) wth->priv; + + if (!priv) { + /* shouldn't be possible */ + luaL_error(L, "Cannot get wtap private data: it is null"); + return LUA_NOREF; + } + + /* the following might push a nil, but that's ok */ + lua_rawgeti(L, LUA_REGISTRYINDEX, priv->table_ref); + + return 1; +} + +/* sets the private data to wtap - the table is presumed on top of stack */ +int set_wth_priv_table_ref(lua_State* L, wtap *wth) { + file_priv_t *priv = (file_priv_t*) wth->priv; + + if (!priv) { + /* shouldn't be possible */ + luaL_error(L, "Cannot get wtap private data: it is null"); + return 0; + } + + if (lua_isnil(L, -1)){ + /* user is setting it nil - ok, de-ref any previous one */ + luaL_unref(L, LUA_REGISTRYINDEX, priv->table_ref); + priv->table_ref = LUA_NOREF; + return 0; + } + + if (!lua_istable(L, -1)) { + luaL_error(L, "The private_table member can only be set to a table or nil"); + return 0; + } + + /* if we had a table already referenced, de-ref it first */ + if (priv->table_ref != LUA_NOREF) { + luaL_unref(L, LUA_REGISTRYINDEX, priv->table_ref); + } + + priv->table_ref = luaL_ref(L, LUA_REGISTRYINDEX); + + return 0; +} + +/* remove, deref, and free the wtap->priv data */ +void remove_wth_priv(lua_State* L, wtap *wth) { + file_priv_t *priv = (file_priv_t*) wth->priv; + + if (!priv) { + /* shouldn't be possible */ + luaL_error(L, "Cannot remove wtap private data: it is null"); + return; + } + + luaL_unref(L, LUA_REGISTRYINDEX, priv->table_ref); + + g_free(wth->priv); + wth->priv = NULL; +} + +/* create and set the wtap_dumper->priv private data for the file instance */ +void create_wdh_priv(lua_State* L, wtap_dumper *wdh) { + file_priv_t *priv = g_new(file_priv_t, 1); + + if (wdh->priv != NULL) { + g_free(priv); + luaL_error(L, "Cannot create wtap_dumper private data because there already is private data"); + return; + } + priv->table_ref = LUA_NOREF; + wdh->priv = (void*) priv; +} + +/* get the private data from wtap_dumper */ +int get_wdh_priv_table_ref(lua_State* L, wtap_dumper *wdh) { + file_priv_t *priv = (file_priv_t*) wdh->priv; + + if (!priv) { + /* shouldn't be possible */ + luaL_error(L, "Cannot get wtap_dumper private data: it is null"); + return LUA_NOREF; + } + + /* the following might push a nil, but that's ok */ + lua_rawgeti(L, LUA_REGISTRYINDEX, priv->table_ref); + + return 1; +} + +/* sets the private data to wtap - the table is presumed on top of stack */ +int set_wdh_priv_table_ref(lua_State* L, wtap_dumper *wdh) { + file_priv_t *priv = (file_priv_t*) wdh->priv; + + if (!priv) { + /* shouldn't be possible */ + luaL_error(L, "Cannot get wtap private data: it is null"); + return 0; + } + + if (lua_isnil(L, -1)){ + /* user is setting it nil - ok, de-ref any previous one */ + luaL_unref(L, LUA_REGISTRYINDEX, priv->table_ref); + priv->table_ref = LUA_NOREF; + return 0; + } + + if (!lua_istable(L, -1)) { + luaL_error(L, "The private_table member can only be set to a table or nil"); + return 0; + } + + /* if we had a table already referenced, de-ref it first */ + if (priv->table_ref != LUA_NOREF) { + luaL_unref(L, LUA_REGISTRYINDEX, priv->table_ref); + } + + priv->table_ref = luaL_ref(L, LUA_REGISTRYINDEX); + + return 0; +} + +/* remove and deref the wtap_dumper->priv data */ +void remove_wdh_priv(lua_State* L, wtap_dumper *wdh) { + file_priv_t *priv = (file_priv_t*) wdh->priv; + + if (!priv) { + /* shouldn't be possible */ + luaL_error(L, "Cannot remove wtap_dumper private data: it is null"); + return; + } + + luaL_unref(L, LUA_REGISTRYINDEX, priv->table_ref); + /* we do NOT free wtap_dumper's priv member - wtap_dump_close() free's it */ +} + + +/* + * 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: + */ diff --git a/epan/wslua/wslua_file_common.h b/epan/wslua/wslua_file_common.h new file mode 100644 index 00000000..6ff98b65 --- /dev/null +++ b/epan/wslua/wslua_file_common.h @@ -0,0 +1,72 @@ +/** @file + * + * Wireshark's interface to the Lua Programming Language + * for file handling related source files. + * + * (c) 2014, Hadriel Kaplan <hadrielk@yahoo.com> + * + * Wireshark - Network traffic analyzer + * By Gerald Combs <gerald@wireshark.org> + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + + +/* See wslua_file_common.c for details */ + +#include "wslua.h" +#include <wiretap/wtap_opttypes.h> +#include <wiretap/wtap-int.h> + +/* this is way overkill for this one member, but in case we need to add + more in the future, the plumbing will be here */ +typedef struct _file_priv_t { + int table_ref; +} file_priv_t; + +/* create and set the wtap->priv private data for the file instance */ +extern void create_wth_priv(lua_State* L, wtap *wth); + +/* gets the private data table from wtap */ +extern int get_wth_priv_table_ref(lua_State* L, wtap *wth); + +/* sets the private data to wtap - the table is presumed on top of stack */ +extern int set_wth_priv_table_ref(lua_State* L, wtap *wth); + +/* remove, deref, and free the wtap->priv data */ +extern void remove_wth_priv(lua_State* L, wtap *wth); + +/* create and set the wtap_dumper->priv private data for the file instance */ +extern void create_wdh_priv(lua_State* L, wtap_dumper *wdh); + +/* get the private data from wtap_dumper */ +extern int get_wdh_priv_table_ref(lua_State* L, wtap_dumper *wdh); + +/* sets the private data to wtap - the table is presumed on top of stack */ +extern int set_wdh_priv_table_ref(lua_State* L, wtap_dumper *wdh); + +/* remove and deref the wtap_dumper->priv data */ +extern void remove_wdh_priv(lua_State* L, wtap_dumper *wdh); + +/* implemented in other c files than wslua_file_common.c */ +extern CaptureInfo* push_CaptureInfo(lua_State* L, wtap *wth, const gboolean first_time); +extern CaptureInfoConst* push_CaptureInfoConst(lua_State* L, wtap_dumper *wdh); +extern File* push_File(lua_State* L, FILE_T ft); +extern File* push_Wdh(lua_State* L, wtap_dumper *wdh); +extern FrameInfo* push_FrameInfo(lua_State* L, wtap_rec *rec, Buffer* buf); +extern FrameInfoConst* push_FrameInfoConst(lua_State* L, const wtap_rec *rec, const guint8 *pd); + + +/* + * 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: + */ diff --git a/epan/wslua/wslua_file_handler.c b/epan/wslua/wslua_file_handler.c new file mode 100644 index 00000000..f0c9ad83 --- /dev/null +++ b/epan/wslua/wslua_file_handler.c @@ -0,0 +1,1367 @@ +/* + * wslua_file_handler.c + * + * Wireshark's interface to the Lua Programming Language + * for custom file reading/writing. + * + * (c) 2014, Hadriel Kaplan <hadrielk@yahoo.com> + * + * 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" + +#include "wslua_file_common.h" + +#include <errno.h> +#include <wiretap/file_wrappers.h> + +/* WSLUA_CONTINUE_MODULE File */ + + +WSLUA_CLASS_DEFINE(FileHandler,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. + + @since 1.11.3 + */ + +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; +} + + +/* Keep track of registered FileHandlers such that reloading plugins works. */ +static GSList *registered_file_handlers; + +/* During file routines, we cannot allow the FileHandler to get deregistered, 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; + +static void +report_error(int *err, gchar **err_info, const char *fmt, ...) +{ + va_list ap; + gchar *msg; + + va_start(ap, fmt); + msg = ws_strdup_vprintf(fmt, ap); + va_end(ap); + if (err != NULL) { + *err = WTAP_ERR_INTERNAL; + *err_info = msg; + } else { + ws_warning("%s", msg); + g_free(msg); + } +} + +/* This does the verification and setup common to all open/read/seek_read/close routines */ +#define INIT_FILEHANDLER_ROUTINE(name,retval,err,err_info) \ + if (!fh) { \ + report_error(err, err_info, "Error in file %s: no Lua FileHandler object", #name); \ + return retval; \ + } \ + if (fh->removed) { \ + return retval; \ + } \ + if (!fh->registered) { \ + report_error(err, err_info, "Error in file %s: Lua FileHandler is not registered", #name); \ + return retval; \ + } \ + if (!fh->L) { \ + report_error(err, err_info, "Error in file %s: no FileHandler Lua state", #name); \ + return retval; \ + } \ + if (fh->name##_ref == LUA_NOREF) { \ + report_error(err, err_info, "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)) { \ + report_error(err, err_info, "Error in file %s: no FileHandler %s routine function in Lua", #name, #name); \ + return retval; \ + } \ + /* now guard against deregistering during pcall() */ \ + in_routine = TRUE + +#define END_FILEHANDLER_ROUTINE() \ + /* now allow deregistering again */ \ + in_routine = FALSE + + +/* 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,err,err_info) \ + case LUA_ERRRUN: \ + report_error(err, err_info, "Run-time error while calling FileHandler %s routine", name); \ + break; \ + case LUA_ERRMEM: \ + report_error(err, err_info, "Memory alloc error while calling FileHandler %s routine", name); \ + break; \ + case LUA_ERRERR: \ + report_error(err, err_info, "Error in error handling while calling FileHandler %s routine", name); \ + break; \ + case LUA_ERRGCMM: \ + report_error(err, err_info, "Error in garbage collector while calling FileHandler %s routine", name); \ + break; \ + default: \ + ws_assert_not_reached(); \ + break; + +/* some declarations */ +static gboolean +wslua_filehandler_read(wtap *wth, wtap_rec *rec, Buffer *buf, + int *err, gchar **err_info, gint64 *offset); +static gboolean +wslua_filehandler_seek_read(wtap *wth, gint64 seek_off, wtap_rec *rec, 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 wtap_open_return_val +wslua_filehandler_open(wtap *wth, int *err, gchar **err_info) +{ + FileHandler fh = (FileHandler)(wth->wslua_data); + wtap_open_return_val retval = WTAP_OPEN_NOT_MINE; + lua_State* L = NULL; + File *fp = NULL; + CaptureInfo *fc = NULL; + + INIT_FILEHANDLER_ROUTINE(read_open,WTAP_OPEN_ERROR,err,err_info); + + create_wth_priv(L, wth); + + fp = push_File(L, wth->fh); + fc = push_CaptureInfo(L, wth, TRUE); + + errno = WTAP_ERR_CANT_OPEN; + switch ( lua_pcall(L,2,1,1) ) { + case 0: + retval = (wtap_open_return_val)wslua_optboolint(L,-1,0); + break; + CASE_ERROR("read_open",err,err_info) + } + + END_FILEHANDLER_ROUTINE(); + + (*fp)->expired = TRUE; + (*fc)->expired = TRUE; + + if (retval == WTAP_OPEN_MINE) { + /* 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 { + ws_warning("Lua file format module lacks a read routine"); + return WTAP_OPEN_NOT_MINE; + } + + /* when not having a seek_read routine a default will be used */ + wth->subtype_seek_read = wslua_filehandler_seek_read; + + /* 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; + } + else if (retval == WTAP_OPEN_ERROR) { + /* open error - we *must* return an error code! */ + if (err) { + *err = WTAP_ERR_CANT_OPEN; + } + } + else if (retval == WTAP_OPEN_NOT_MINE) { + /* not our file type */ + remove_wth_priv(L, wth); + } + else { + /* not a valid return type */ + if (err) { + *err = WTAP_ERR_INTERNAL; + *err_info = ws_strdup_printf("FileHandler read_open routine returned %d", retval); + } + retval = WTAP_OPEN_ERROR; + } + + lua_settop(L,0); + return retval; +} + +static gboolean +wslua_filehandler_read_packet(wtap *wth, FILE_T wth_fh, wtap_rec *rec, Buffer *buf, + int *err, gchar **err_info, gint64 *offset) +{ + FileHandler fh = (FileHandler)(wth->wslua_data); + int retval = -1; + lua_State* L = NULL; + File *fp = NULL; + CaptureInfo *fc = NULL; + FrameInfo *fi = NULL; + + INIT_FILEHANDLER_ROUTINE(read,FALSE,err,err_info); + + /* Reset errno */ + if (err) { + *err = errno = 0; + } + + wtap_block_unref(rec->block); + rec->block = NULL; + + fp = push_File(L, wth_fh); + fc = push_CaptureInfo(L, wth, FALSE); + fi = push_FrameInfo(L, rec, buf); + + switch ( lua_pcall(L,3,1,1) ) { + case 0: + /* + * Return values for FileHandler:read(): + * Integer is the number of read bytes. + * Boolean false indicates an error. + * XXX handling of boolean true is not documented. Currently it will + * succeed without advancing data offset. Should it fail instead? + */ + if (lua_type(L, -1) == LUA_TNUMBER) { + *offset = wslua_togint64(L, -1); + retval = 1; + break; + } + retval = wslua_optboolint(L,-1,0); + break; + CASE_ERROR("read",err,err_info) + } + + END_FILEHANDLER_ROUTINE(); + + (*fp)->expired = TRUE; + (*fc)->expired = TRUE; + (*fi)->expired = TRUE; + lua_settop(L,0); + + return (retval == 1); +} + +/* 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, wtap_rec *rec, Buffer *buf, + int *err, gchar **err_info, gint64 *offset) +{ + return wslua_filehandler_read_packet(wth, wth->fh, rec, buf, err, err_info, offset); +} + +static gboolean +wslua_filehandler_seek_read_packet(wtap *wth, gint64 seek_off, wtap_rec *rec, Buffer *buf, + int *err, gchar **err_info) +{ + FileHandler fh = (FileHandler)(wth->wslua_data); + int retval = -1; + lua_State* L = NULL; + File *fp = NULL; + CaptureInfo *fc = NULL; + FrameInfo *fi = NULL; + + INIT_FILEHANDLER_ROUTINE(seek_read,FALSE,err,err_info); + + /* Reset errno */ + if (err) { + *err = errno = 0; + } + + wtap_block_unref(rec->block); + rec->block = NULL; + + fp = push_File(L, wth->random_fh); + fc = push_CaptureInfo(L, wth, FALSE); + fi = push_FrameInfo(L, rec, buf); + lua_pushnumber(L, (lua_Number)seek_off); + + switch ( lua_pcall(L,4,1,1) ) { + case 0: + /* + * Return values for FileHandler:seek_read(): + * Boolean true for successful parsing, false/nil on error. + * Numbers (including zero) are interpreted as success for + * compatibility to match FileHandler:seek semantics. + * (Other values are unspecified/undocumented, but happen to be + * treated as success.) + */ + retval = lua_toboolean(L, -1); + break; + CASE_ERROR("seek_read",err,err_info) + } + + END_FILEHANDLER_ROUTINE(); + + (*fp)->expired = TRUE; + (*fc)->expired = TRUE; + (*fi)->expired = TRUE; + lua_settop(L,0); + + return (retval == 1); +} + +/* Default FileHandler:seek_read() implementation. + * Do a standard file_seek() and then call FileHandler:read(). + */ +static gboolean +wslua_filehandler_seek_read_default(wtap *wth, gint64 seek_off, wtap_rec *rec, Buffer *buf, + int *err, gchar **err_info) +{ + gint64 offset = file_seek(wth->random_fh, seek_off, SEEK_SET, err); + + if (offset < 0) { + return FALSE; + } + + return wslua_filehandler_read_packet(wth, wth->random_fh, rec, buf, err, err_info, &offset); +} + +/* 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, wtap_rec *rec, Buffer *buf, + int *err, gchar **err_info) +{ + FileHandler fh = (FileHandler)(wth->wslua_data); + + if (fh->removed) { + /* Return success when removed during reloading Lua plugins */ + return TRUE; + } + + if (fh->seek_read_ref != LUA_NOREF) { + return wslua_filehandler_seek_read_packet(wth, seek_off, rec, buf, err, err_info); + } else { + return wslua_filehandler_seek_read_default(wth, seek_off, rec, buf, err, err_info); + } +} + +/* 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; + CaptureInfo *fc = NULL; + + INIT_FILEHANDLER_ROUTINE(read_close,,NULL,NULL); + + fp = push_File(L, wth->fh); + fc = push_CaptureInfo(L, wth, FALSE); + + switch ( lua_pcall(L,2,1,1) ) { + case 0: + break; + CASE_ERROR("read_close",NULL,NULL) + } + + END_FILEHANDLER_ROUTINE(); + + remove_wth_priv(L, wth); + + (*fp)->expired = TRUE; + (*fc)->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; + CaptureInfo *fc = NULL; + + INIT_FILEHANDLER_ROUTINE(seq_read_close,,NULL,NULL); + + fp = push_File(L, wth->fh); + fc = push_CaptureInfo(L, wth, FALSE); + + switch ( lua_pcall(L,2,1,1) ) { + case 0: + break; + CASE_ERROR("seq_read_close",NULL,NULL) + } + + END_FILEHANDLER_ROUTINE(); + + (*fp)->expired = TRUE; + (*fc)->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_UNWRITABLE_ENCAP; + lua_State* L = NULL; + + INIT_FILEHANDLER_ROUTINE(can_write_encap,WTAP_ERR_UNWRITABLE_ENCAP,NULL,NULL); + + lua_pushnumber(L, encap); + + switch ( lua_pcall(L,1,1,1) ) { + case 0: + retval = wslua_optboolint(L,-1,WTAP_ERR_UNWRITABLE_ENCAP); + break; + CASE_ERROR("can_write_encap",NULL,NULL) + } + + END_FILEHANDLER_ROUTINE(); + + /* the retval we got was either a 1 for true, 0 for false, or WTAP_ERR_UNWRITABLE_ENCAP; + but can_write_encap() expects 0 to be true/yes */ + if (retval == 1) { + retval = 0; + } else if (retval == 0) { + retval = WTAP_ERR_UNWRITABLE_ENCAP; + } + + return retval; +} + +/* some declarations */ +static gboolean +wslua_filehandler_dump(wtap_dumper *wdh, const wtap_rec *rec, + const guint8 *pd, int *err, gchar **err_info); +static gboolean +wslua_filehandler_dump_finish(wtap_dumper *wdh, int *err, gchar **err_info); + + +/* The classic wtap dump_open function. + * This returns 1 (TRUE) on success. + */ +static int +wslua_filehandler_dump_open(wtap_dumper *wdh, int *err, gchar **err_info) +{ + 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,err,err_info); + + create_wdh_priv(L, wdh); + + fp = push_Wdh(L, wdh); + fc = push_CaptureInfoConst(L,wdh); + + /* Reset err */ + if (err) { + *err = 0; + } + + switch ( lua_pcall(L,2,1,1) ) { + case 0: + retval = wslua_optboolint(L,-1,0); + break; + CASE_ERROR("write_open",err,err_info) + } + + 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 { + ws_warning("FileHandler was not set with a write function, even though write_open() returned true"); + return 0; + } + + /* it's ok to not have a finish routine */ + if (fh->write_close_ref != LUA_NOREF) + wdh->subtype_finish = wslua_filehandler_dump_finish; + else + wdh->subtype_finish = NULL; + } + else { + /* not our file type? */ + remove_wdh_priv(L, wdh); + } + + 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 wtap_rec *rec, + const guint8 *pd, int *err, gchar **err_info) +{ + FileHandler fh = (FileHandler)(wdh->wslua_data); + int retval = -1; + lua_State* L = NULL; + File *fp = NULL; + CaptureInfoConst *fc = NULL; + FrameInfoConst *fi = NULL; + + INIT_FILEHANDLER_ROUTINE(write,FALSE,err,err_info); + + /* Reset errno */ + if (err) { + *err = errno = 0; + } + + fp = push_Wdh(L, wdh); + fc = push_CaptureInfoConst(L,wdh); + fi = push_FrameInfoConst(L, rec, pd); + + errno = WTAP_ERR_CANT_WRITE; + switch ( lua_pcall(L,3,1,1) ) { + case 0: + retval = wslua_optboolint(L,-1,0); + break; + CASE_ERROR("write",err,err_info) + } + + END_FILEHANDLER_ROUTINE(); + + (*fp)->expired = TRUE; + (*fc)->expired = TRUE; + (*fi)->expired = TRUE; + + return (retval == 1); +} + +/* The classic wtap dump_finish routine. This returns TRUE if it + * writes out the last information cleanly, else FALSE. +*/ +static gboolean +wslua_filehandler_dump_finish(wtap_dumper *wdh, int *err, gchar **err_info) +{ + FileHandler fh = (FileHandler)(wdh->wslua_data); + int retval = -1; + lua_State* L = NULL; + File *fp = NULL; + CaptureInfoConst *fc = NULL; + + INIT_FILEHANDLER_ROUTINE(write_close,FALSE,err,err_info); + + /* Reset errno */ + if (err) { + *err = errno = 0; + } + + fp = push_Wdh(L, wdh); + fc = push_CaptureInfoConst(L,wdh); + + errno = WTAP_ERR_CANT_CLOSE; + switch ( lua_pcall(L,2,1,1) ) { + case 0: + retval = wslua_optboolint(L,-1,0); + break; + CASE_ERROR("write_close",err,err_info) + } + + END_FILEHANDLER_ROUTINE(); + + remove_wdh_priv(L, wdh); + + (*fp)->expired = TRUE; + (*fc)->expired = TRUE; + + return (retval == 1); +} + +/* + * Prototype table of option support. + * We start out saying we don't support comments, and we don't mention + * other options. + */ +static const struct supported_option_type option_type_proto[] = { + { OPT_COMMENT, OPTION_NOT_SUPPORTED } +}; + +/* + * Prototype table of block type support. + * We start out saying we only support packets. + */ +static const struct supported_block_type block_type_proto[] = { + { WTAP_BLOCK_SECTION, BLOCK_NOT_SUPPORTED, 0, NULL }, + { WTAP_BLOCK_IF_ID_AND_INFO, BLOCK_NOT_SUPPORTED, 0, NULL }, + { WTAP_BLOCK_NAME_RESOLUTION, BLOCK_NOT_SUPPORTED, 0, NULL }, + { WTAP_BLOCK_IF_STATISTICS, BLOCK_NOT_SUPPORTED, 0, NULL }, + { WTAP_BLOCK_DECRYPTION_SECRETS, BLOCK_NOT_SUPPORTED, 0, NULL }, + { WTAP_BLOCK_PACKET, MULTIPLE_BLOCKS_SUPPORTED, 0, NULL }, + { WTAP_BLOCK_FT_SPECIFIC_REPORT, BLOCK_NOT_SUPPORTED, 0, NULL }, + { WTAP_BLOCK_FT_SPECIFIC_EVENT, BLOCK_NOT_SUPPORTED, 0, NULL } +}; + +#define NUM_LISTED_BLOCK_TYPES (sizeof block_type_proto / sizeof block_type_proto[0]) + +WSLUA_CONSTRUCTOR FileHandler_new(lua_State* L) { + /* Creates a new FileHandler */ +#define WSLUA_ARG_FileHandler_new_DESCRIPTION 1 /* A description of the file type, for display purposes only. E.g., "Wireshark - pcapng" */ +#define WSLUA_ARG_FileHandler_new_NAME 2 /* The file type name, used to look up the file type in various places. E.g., "pcapng". Note: The name cannot already be in use. */ +#define WSLUA_ARG_FileHandler_new_INTERNAL_DESCRIPTION 3 /* Descriptive text about this file format, for internal 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* description = luaL_checkstring(L,WSLUA_ARG_FileHandler_new_DESCRIPTION); + const gchar* name = luaL_checkstring(L,WSLUA_ARG_FileHandler_new_NAME); + const gchar* internal_description = luaL_checkstring(L,WSLUA_ARG_FileHandler_new_INTERNAL_DESCRIPTION); + const gchar* type = luaL_checkstring(L,WSLUA_ARG_FileHandler_new_TYPE); + FileHandler fh = (FileHandler) g_malloc0(sizeof(struct _wslua_filehandler)); + struct supported_block_type *supported_blocks; + + 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(name)) { + g_free(fh); + return luaL_error(L, "FileHandler.new: '%s' name already exists for a reader!", name); + } + + if (fh->is_writer && wtap_name_to_file_type_subtype(name) > -1) { + g_free(fh); + return luaL_error(L, "FileHandler.new: '%s' name already exists for a writer!", name); + } + + fh->type = g_strdup(type); + fh->extensions = NULL; + fh->finfo.description = g_strdup(description); + fh->finfo.name = g_strdup(name); + fh->finfo.default_file_extension = NULL; + fh->finfo.additional_file_extensions = NULL; + fh->finfo.writing_must_seek = FALSE; + supported_blocks = (struct supported_block_type *)g_memdup2(&block_type_proto, sizeof block_type_proto); + /* + * Add a list of options to the seciton block, interface block, and + * packet block, so the file handler can indicate comment support. + */ + for (size_t i = 0; i < NUM_LISTED_BLOCK_TYPES; i++) { + switch (supported_blocks[i].type) { + + case WTAP_BLOCK_SECTION: + case WTAP_BLOCK_IF_ID_AND_INFO: + case WTAP_BLOCK_PACKET: + supported_blocks[i].num_supported_options = OPTION_TYPES_SUPPORTED(option_type_proto); + supported_blocks[i].supported_options = (struct supported_option_type *)g_memdup2(&option_type_proto, sizeof option_type_proto); + break; + + default: + break; + } + } + fh->finfo.num_supported_blocks = NUM_LISTED_BLOCK_TYPES; + fh->finfo.supported_blocks = supported_blocks; + 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->internal_description = g_strdup(internal_description); + 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): description='%s', internal description='%s', read_open=%d, read=%d, write=%d", + fh->finfo.name, fh->finfo.description, fh->internal_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->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) { + if (fh->extensions && fh->extensions[0]) { + char *extension = g_strdup(fh->extensions); + char *extra_extensions = strchr(extension, ';'); + if (extra_extensions) { + /* Split "cap;pcap" -> "cap" and "pcap" */ + *extra_extensions++ = '\0'; + } + fh->finfo.default_file_extension = extension; + fh->finfo.additional_file_extensions = extra_extensions; + } + fh->finfo.can_write_encap = wslua_dummy_can_write_encap; + fh->finfo.wslua_info = g_new0(wtap_wslua_file_info_t, 1); + 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_subtype(&(fh->finfo)); + + if (fh->is_reader) { + struct open_info oi = { NULL, OPEN_INFO_HEURISTIC, NULL, NULL, NULL, NULL }; + oi.name = fh->finfo.name; + oi.open_routine = wslua_filehandler_open; + oi.extensions = fh->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; + registered_file_handlers = g_slist_prepend(registered_file_handlers, fh); + + lua_pushnumber(L, fh->file_type); + + WSLUA_RETURN(1); /* the new type number for this file reader/write */ +} + +static void +wslua_deregister_filehandler_work(FileHandler fh) +{ + /* 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; + } + g_free((char *)fh->finfo.default_file_extension); + fh->finfo.default_file_extension = NULL; + fh->finfo.additional_file_extensions = 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.name)) { + wtap_deregister_open_info(fh->finfo.name); + } + + fh->registered = FALSE; +} + +WSLUA_FUNCTION wslua_deregister_filehandler(lua_State* L) { + /* Deregister 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_deregister_filehandler_FILEHANDLER 1 /* The FileHandler object to be deregistered */ + FileHandler fh = checkFileHandler(L,WSLUA_ARG_deregister_filehandler_FILEHANDLER); + + if (in_routine) + return luaL_error(L,"A FileHandler cannot be deregistered during reading/writing callback functions"); + + if (!fh->registered) + return 0; + + wslua_deregister_filehandler_work(fh); + registered_file_handlers = g_slist_remove(registered_file_handlers, fh); + + return 0; +} + +/* The following 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. + + When later called by Wireshark, the Lua function will be given: + 1. A `File` object + 2. A `CaptureInfo` object + + 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. + + 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. + */ +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. + + When later called by Wireshark, the Lua function will be given: + 1. A `File` object + 2. A `CaptureInfo` object + 3. A `FrameInfo` object + + 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(file, frame.captured_length)`. + + 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. + + When later called by Wireshark, the Lua function will be given: + 1. A `File` object + 2. A `CaptureInfo` object + 3. A `FrameInfo` object + 4. The file offset number previously set by the `read()` function call + + The called Lua function should return true if the read was successful, or false if it hit an error. + Since 2.4.0, a number is also acceptable to signal success, this allows for reuse of `FileHandler:read`: + + [source,lua] + ---- + local function fh_read(file, capture, frame) ... end + myfilehandler.read = fh_read + + function myfilehandler.seek_read(file, capture, frame, offset) + if not file:seek("set", offset) then + -- Seeking failed, return failure + return false + end + + -- Now try to read one frame + return fh_read(file, capture, frame) + end + ---- + + Since 3.6.0, it's possible to omit the `FileHandler:seek_read()` function to get a default seek_read implementation. + */ +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. + + When later called by Wireshark, the Lua function will be given: + 1. A `File` object + 2. A `CaptureInfo` object + + 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. + + When later called by Wireshark, the Lua function will be given: + 1. A `File` object + 2. A `CaptureInfo` object + + 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). + + 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. + + 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. + + When later called by Wireshark, the Lua function will be given: + 1. A `File` object + 2. A `CaptureInfoConst` object + + 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. + + 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_finish`) 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. + + When later called by Wireshark, the Lua function will be given: + 1. A `File` object + 2. A `CaptureInfoConst` object + 3. A `FrameInfoConst` object of the current frame/packet to be written + + The purpose of the Lua function set to this `write` field is to write the next packet to the file. + + The called Lua function should return true on success, or false if it hit an error. */ +WSLUA_ATTRIBUTE_FUNC_SETTER(FileHandler,write); + +/* WSLUA_ATTRIBUTE FileHandler_write_finish WO The Lua function to be called when Wireshark wants to close the written file. + + When later called by Wireshark, the Lua function will be given: + 1. A `File` object + 2. A `CaptureInfoConst` object + + 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 semicolon-separated file extensions that this file type usually uses. + + 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. + + (Since 2.6) For writers, the first extension is used to suggest the default file extension. */ +WSLUA_ATTRIBUTE_STRING_GETTER(FileHandler,extensions); +WSLUA_ATTRIBUTE_STRING_SETTER(FileHandler,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. */ +static inline struct supported_block_type * +safe_cast_away_block_type_const(const struct supported_block_type *arg) +{ + /* + * Cast away constness without a warning; we know we can do this + * because, for Lua file handlers, the table of supported block + * types is in allocated memory, so that we *can* modify it. + * + * The pointer in the file_type_subtype_info structure is a + * pointer to const because compiled file handlers will + * normally set it to point to a static const structure. + */ +DIAG_OFF_CAST_AWAY_CONST + return (struct supported_block_type *)arg; +DIAG_ON_CAST_AWAY_CONST +} + +WSLUA_ATTRIBUTE_GET(FileHandler,writes_name_resolution,{ \ + gboolean supports_name_resolution = FALSE; \ + for (size_t i = 0; i < obj->finfo.num_supported_blocks; i++) { \ + /* \ + * If WTAP_BLOCK_NAME_RESOLUTION is supported, name \ + * resolution is supported. \ + */ \ + if (obj->finfo.supported_blocks[i].type == WTAP_BLOCK_NAME_RESOLUTION) { \ + supports_name_resolution = (obj->finfo.supported_blocks[i].support != BLOCK_NOT_SUPPORTED); \ + break; \ + } \ + } \ + lua_pushboolean(L, supports_name_resolution); \ +}); +WSLUA_ATTRIBUTE_SET(FileHandler,writes_name_resolution, { \ + gboolean supports_name_resolution; \ + if (!lua_isboolean(L,-1) ) \ + return luaL_error(L, "FileHandler's attribute`writes_name_resolution' must be a boolean"); \ + supports_name_resolution = lua_toboolean(L,-1); \ + /* \ + * Update support for WTAP_BLOCK_NAME_RESOLUTION; the entry for \ + * it should be there. \ + */ \ + for (size_t i = 0; i < obj->finfo.num_supported_blocks; i++) { \ + if (obj->finfo.supported_blocks[i].type == WTAP_BLOCK_NAME_RESOLUTION) { \ + struct supported_block_type *supported_blocks; + supported_blocks = safe_cast_away_block_type_const(obj->finfo.supported_blocks); \ + + supported_blocks[i].support = supports_name_resolution ? ONE_BLOCK_SUPPORTED : BLOCK_NOT_SUPPORTED; \ + break; \ + } \ + } \ +}); + +/* 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. */ +static inline struct supported_option_type * +safe_cast_away_option_type_const(const struct supported_option_type *arg) +{ + /* + * Cast away constness without a warning; we know we can do this + * because, for Lua file handlers, the table of supported option + * types is in allocated memory, so that we *can* modify it. + * + * The pointer in the file_type_subtype_info structure is a + * pointer to const because compiled file handlers will + * normally set it to point to a static const structure. + */ +DIAG_OFF_CAST_AWAY_CONST + return (struct supported_option_type *)arg; +DIAG_ON_CAST_AWAY_CONST +} + +WSLUA_ATTRIBUTE_GET(FileHandler,supported_comment_types,{ \ + guint supported_comment_types = 0; \ + for (size_t i = 0; i < obj->finfo.num_supported_blocks; i++) { \ + size_t num_supported_options; \ + const struct supported_option_type *supported_options; +\ + /* \ + * Is this block type supported? \ + */ \ + if (obj->finfo.supported_blocks[i].support == BLOCK_NOT_SUPPORTED) { \ + /* \ + * No - skip it. \ + */ \ + continue; \ + } \ +\ + /* \ + * Yes - what type of block is it? \ + */ \ + switch (obj->finfo.supported_blocks[i].type) { \ +\ + case WTAP_BLOCK_SECTION: \ + /* \ + * Section block - does this block type support comments? \ + */ \ + num_supported_options = obj->finfo.supported_blocks[i].num_supported_options; \ + supported_options = obj->finfo.supported_blocks[i].supported_options; \ + for (size_t j = 0; j < num_supported_options; i++) { \ + if (supported_options[i].opt == OPT_COMMENT) { \ + if (supported_options[i].support != OPTION_NOT_SUPPORTED) \ + supported_comment_types |= WTAP_COMMENT_PER_SECTION; \ + break; \ + } \ + } \ + break; \ +\ + case WTAP_BLOCK_IF_ID_AND_INFO: \ + /* \ + * Interface block - does this block type support comments? \ + */ \ + num_supported_options = obj->finfo.supported_blocks[i].num_supported_options; \ + supported_options = obj->finfo.supported_blocks[i].supported_options; \ + for (size_t j = 0; j < num_supported_options; i++) { \ + if (supported_options[i].opt == OPT_COMMENT) { \ + if (supported_options[i].support != OPTION_NOT_SUPPORTED) \ + supported_comment_types |= WTAP_COMMENT_PER_INTERFACE; \ + break; \ + } \ + } \ + break; \ +\ + case WTAP_BLOCK_PACKET: \ + /* \ + * Packet block - does this block type support comments? \ + */ \ + num_supported_options = obj->finfo.supported_blocks[i].num_supported_options; \ + supported_options = obj->finfo.supported_blocks[i].supported_options; \ + for (size_t j = 0; j < num_supported_options; i++) { \ + if (supported_options[i].opt == OPT_COMMENT) { \ + if (supported_options[i].support != OPTION_NOT_SUPPORTED) \ + supported_comment_types |= WTAP_COMMENT_PER_PACKET; \ + break; \ + } \ + } \ + break; \ +\ + default: \ + break;\ + } \ + } \ + lua_pushnumber(L, (lua_Number)supported_comment_types); \ +}); +WSLUA_ATTRIBUTE_SET(FileHandler,supported_comment_types, { \ + guint supported_comment_types; \ + size_t num_supported_options; \ + struct supported_option_type *supported_options; \ + if (!lua_isnumber(L,-1) ) \ + return luaL_error(L, "FileHandler's attribute`supported_comment_types' must be a number"); \ + supported_comment_types = wslua_toguint(L,-1); \ + /* \ + * Update support for comments in the relevant block types; the entries \ + * for comments in those types should be there. \ + */ \ + for (size_t i = 0; i < obj->finfo.num_supported_blocks; i++) { \ +\ + /* \ + * Is this block type supported? \ + */ \ + if (obj->finfo.supported_blocks[i].support == BLOCK_NOT_SUPPORTED) { \ + /* \ + * No - skip it. \ + */ \ + continue; \ + } \ +\ + /* \ + * Yes - what type of block is it? \ + */ \ + switch (obj->finfo.supported_blocks[i].type) { \ +\ + case WTAP_BLOCK_SECTION: \ + /* \ + * Section block - update the comment support. \ + */ \ + num_supported_options = obj->finfo.supported_blocks[i].num_supported_options; \ + supported_options = safe_cast_away_option_type_const(obj->finfo.supported_blocks[i].supported_options); \ + for (size_t j = 0; j < num_supported_options; i++) { \ + if (supported_options[i].opt == OPT_COMMENT) { \ + supported_options[i].support = \ + (supported_comment_types &= WTAP_COMMENT_PER_SECTION) ? \ + ONE_OPTION_SUPPORTED : OPTION_NOT_SUPPORTED ; \ + break; \ + } \ + } \ + break; \ +\ + case WTAP_BLOCK_IF_ID_AND_INFO: \ + /* \ + * Interface block - does this block type support comments? \ + */ \ + num_supported_options = obj->finfo.supported_blocks[i].num_supported_options; \ + supported_options = safe_cast_away_option_type_const(obj->finfo.supported_blocks[i].supported_options); \ + for (size_t j = 0; j < num_supported_options; i++) { \ + if (supported_options[i].opt == OPT_COMMENT) { \ + supported_options[i].support = \ + (supported_comment_types &= WTAP_COMMENT_PER_INTERFACE) ? \ + ONE_OPTION_SUPPORTED : OPTION_NOT_SUPPORTED ; \ + break; \ + } \ + } \ + break; \ +\ + case WTAP_BLOCK_PACKET: \ + /* \ + * Packet block - does this block type support comments? \ + */ \ + num_supported_options = obj->finfo.supported_blocks[i].num_supported_options; \ + supported_options = safe_cast_away_option_type_const(obj->finfo.supported_blocks[i].supported_options); \ + for (size_t j = 0; j < num_supported_options; i++) { \ + if (supported_options[i].opt == OPT_COMMENT) { \ + supported_options[i].support = \ + (supported_comment_types &= WTAP_COMMENT_PER_PACKET) ? \ + ONE_OPTION_SUPPORTED : OPTION_NOT_SUPPORTED ; \ + break; \ + } \ + } \ + break; \ +\ + default: \ + break;\ + } \ + } \ +}); + +/* 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[] = { + WSLUA_CLASS_FNREG(FileHandler,new), + { NULL, NULL } +}; + +WSLUA_META FileHandler_meta[] = { + WSLUA_CLASS_MTREG(FileHandler,tostring), + { NULL, NULL } +}; + +int FileHandler_register(lua_State* L) { + WSLUA_REGISTER_CLASS_WITH_ATTRS(FileHandler); + return 0; +} + +int wslua_deregister_filehandlers(lua_State* L _U_) { + for (GSList *it = registered_file_handlers; it; it = it->next) { + FileHandler fh = (FileHandler)it->data; + wslua_deregister_filehandler_work(fh); + + for (size_t i = 0; i < fh->finfo.num_supported_blocks; i++) { + g_free((struct supported_option_type *)fh->finfo.supported_blocks[i].supported_options); + } + g_free((struct supported_block_type *)fh->finfo.supported_blocks); + g_free((char *)fh->extensions); + g_free((char *)fh->internal_description); + g_free((char *)fh->finfo.description); + g_free((char *)fh->finfo.name); + g_free(fh->type); + + memset(fh, 0, sizeof(*fh)); + fh->removed = TRUE; + proto_add_deregistered_data(fh); + } + g_slist_free(registered_file_handlers); + registered_file_handlers = 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: + */ diff --git a/epan/wslua/wslua_frame_info.c b/epan/wslua/wslua_frame_info.c new file mode 100644 index 00000000..401abd73 --- /dev/null +++ b/epan/wslua/wslua_frame_info.c @@ -0,0 +1,502 @@ +/* + * wslua_frame_info.c + * + * Wireshark's interface to the Lua Programming Language + * for frame data and meta-data from a capture file. + * + * (c) 2014, Hadriel Kaplan <hadrielk@yahoo.com> + * + * 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" + +#include "wslua_file_common.h" +#include <lua.h> + + +/* WSLUA_CONTINUE_MODULE File */ + + +WSLUA_CLASS_DEFINE(FrameInfo,FAIL_ON_NULL_OR_EXPIRED("FrameInfo")); +/* + A FrameInfo object, passed into Lua as an argument by FileHandler callback + functions (e.g., `read`, `seek_read`, etc.). + + This object represents frame data and meta-data (data about the frame/packet) + for a given `read`/`seek_read`/`write`++'++s frame. + + 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. + + @since 1.11.3 + */ + +FrameInfo* push_FrameInfo(lua_State* L, wtap_rec *rec, Buffer* buf) { + FrameInfo f = (FrameInfo) g_malloc0(sizeof(struct _wslua_phdr)); + f->rec = rec; + 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->rec) + lua_pushfstring(L, "FrameInfo: rec_type=%u, presence_flags=%d, caplen=%d, len=%d, pkt_encap=%d, block='%p'", + fi->rec->rec_type, fi->rec->presence_flags, fi->rec->rec_header.packet_header.caplen, fi->rec->rec_header.packet_header.len, fi->rec->rec_header.packet_header.pkt_encap, fi->rec->block); + else + lua_pushstring(L, "FrameInfo rec 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 rec/buf it points to */ +static int FrameInfo__gc(lua_State* L) { + FrameInfo fi = toFrameInfo(L,1); + g_free(fi); + return 0; +} + +/* WSLUA_ATTRIBUTE FrameInfo_comment RW Table of comments in this frame. */ +static int FrameInfo_get_comment (lua_State* L) { + FrameInfo fi = checkFrameInfo(L,1); +#define FRAMEINFO_COMMENTS_TABLE 2 + gchar *comment = NULL; + wtap_block_t block = NULL; + guint i = 0; + guint n_comments = 0; + + block = fi->rec->block; + // XXX - how to get the user-edited block, if any? + n_comments = wtap_block_count_option(block, OPT_COMMENT); + lua_createtable(L, n_comments, 0); + for (i = 0; i < n_comments; i++) { + comment = NULL; + lua_pushnumber(L, i+1); + if (WTAP_OPTTYPE_SUCCESS == + wtap_block_get_nth_string_option_value(block, OPT_COMMENT, i, &comment)) { + lua_pushstring(L, comment); + } + else { + lua_pushnil(L); + } + lua_settable(L, FRAMEINFO_COMMENTS_TABLE); + } + + return 1; +} + +static int FrameInfo_set_comment (lua_State* L) { + FrameInfo fi = checkFrameInfo(L,1); +#define FRAMEINFO_COMMENTS_NEWTABLE 2 +#define FRAMEINFO_COMMENTS_NEWCOMMENT 2 + size_t len = 0; + gchar *comment = NULL; + wtap_block_t block = NULL; + guint i = 0; + guint n_comments = 0; + + if(fi->rec->block != NULL) { + block = fi->rec->block; + } + else { + block = wtap_block_create(WTAP_BLOCK_PACKET); + fi->rec->block = block; + } + + /* Strip off old comments */ + n_comments = wtap_block_count_option(block, OPT_COMMENT); + for (i = 0; i < n_comments; i++) { + wtap_block_remove_nth_option_instance(block, OPT_COMMENT, 0); + } + + /* Add new comment(s) */ + if (lua_istable(L, FRAMEINFO_COMMENTS_NEWTABLE)) { + for (lua_pushnil(L); lua_next(L, FRAMEINFO_COMMENTS_NEWTABLE); ) { + if (lua_isstring(L,-1)) { + comment = (gchar *)luaL_checklstring(L,-1,&len); + wtap_block_add_string_option(block, OPT_COMMENT, comment, len); + } else if (! lua_isnil(L,-1) ) { + return luaL_error(L,"only strings should be in the table"); + } + lua_pop(L, 1); + } + } + else if (lua_isstring(L, FRAMEINFO_COMMENTS_NEWCOMMENT)) { + comment = (gchar *)luaL_checklstring(L,FRAMEINFO_COMMENTS_NEWCOMMENT,&len); + wtap_block_add_string_option(block, OPT_COMMENT, comment, len); + } + else { + return luaL_error(L,"comment must be either a string or an array of strings"); + } + + 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->rec) return 0; + + fi->rec->ts.secs = nstime->secs; + fi->rec->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->rec->ts.secs; + nstime->nsecs = fi->rec->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. + + [NOTE] + ==== + This cannot be cleared once set. + ==== + */ +static int FrameInfo_set_data (lua_State* L) { + FrameInfo fi = checkFrameInfo(L,1); + + if (!fi->rec) { + ws_warning("Error in FrameInfo set data: NULL pointer"); + return 0; + } + + if (!fi->buf) { + ws_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); + + /* Make sure we have enough room for the packet */ + ws_buffer_assure_space(fi->buf, len); + memcpy(ws_buffer_start_ptr(fi->buf), s, len); + fi->rec->rec_header.packet_header.caplen = (guint32) len; + fi->rec->rec_header.packet_header.len = (guint32) len; + } + 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, ws_buffer_start_ptr(fi->buf), ws_buffer_length(fi->buf)); + + WSLUA_RETURN(1); /* A Lua string of the frame buffer's data. */ +} + +/* WSLUA_ATTRIBUTE FrameInfo_rec_type RW The record type of the packet frame + + See `wtap_rec_types` for values. */ +WSLUA_ATTRIBUTE_NAMED_NUMBER_GETTER(FrameInfo,rec_type,rec->rec_type); +WSLUA_ATTRIBUTE_NAMED_NUMBER_SETTER(FrameInfo,rec_type,rec->rec_type,guint); + +/* WSLUA_ATTRIBUTE FrameInfo_flags RW The presence flags of the packet frame. + + See `wtap_presence_flags` for bit values. */ +WSLUA_ATTRIBUTE_NAMED_NUMBER_GETTER(FrameInfo,flags,rec->presence_flags); +WSLUA_ATTRIBUTE_NAMED_NUMBER_SETTER(FrameInfo,flags,rec->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,rec->rec_header.packet_header.caplen); +WSLUA_ATTRIBUTE_NAMED_NUMBER_SETTER(FrameInfo,captured_length,rec->rec_header.packet_header.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,rec->rec_header.packet_header.len); +WSLUA_ATTRIBUTE_NAMED_NUMBER_SETTER(FrameInfo,original_length,rec->rec_header.packet_header.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` for possible + packet encapsulation types to use as the value for this field. */ +WSLUA_ATTRIBUTE_NAMED_NUMBER_GETTER(FrameInfo,encap,rec->rec_header.packet_header.pkt_encap); +WSLUA_ATTRIBUTE_NAMED_NUMBER_SETTER(FrameInfo,encap,rec->rec_header.packet_header.pkt_encap,int); + +/* 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,rec_type), + 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[] = { + WSLUA_CLASS_FNREG(FrameInfo,read_data), + { NULL, NULL } +}; + +WSLUA_META FrameInfo_meta[] = { + WSLUA_CLASS_MTREG(FrameInfo,tostring), + { NULL, NULL } +}; + +int FrameInfo_register(lua_State* L) { + WSLUA_REGISTER_CLASS_WITH_ATTRS(FrameInfo); + return 0; +} + +WSLUA_CLASS_DEFINE(FrameInfoConst,FAIL_ON_NULL_OR_EXPIRED("FrameInfo")); +/* + 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. + + @since 1.11.3 + */ + +FrameInfoConst* push_FrameInfoConst(lua_State* L, const wtap_rec *rec, const guint8 *pd) { + FrameInfoConst f = (FrameInfoConst) g_malloc(sizeof(struct _wslua_const_phdr)); + f->rec = rec; + 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->rec && !fi->expired) + lua_pushfstring(L, "FrameInfo: rec_type=%u, presence_flags=%d, caplen=%d, len=%d, pkt_encap=%d, block='%p'", + fi->rec->rec_type, fi->rec->presence_flags, fi->rec->rec_header.packet_header.caplen, fi->rec->rec_header.packet_header.len, fi->rec->rec_header.packet_header.pkt_encap, fi->rec->block); + else + lua_pushfstring(L, "FrameInfo has %s", fi->rec?"expired":"null rec 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->rec ? fi->rec->rec_header.packet_header.caplen:0); + int err = 0; + + if (!fi->pd || !fi->rec || !fh->wdh) { + luaL_error(L, "FrameInfoConst write_data() got null buffer or file pointer internally"); + return 0; + } + + if (len > fi->rec->rec_header.packet_header.caplen) + len = fi->rec->rec_header.packet_header.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", g_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_rec it points to */ +static int FrameInfoConst__gc(lua_State* L) { + FrameInfoConst fi = toFrameInfoConst(L,1); + g_free(fi); + return 0; +} + +/* WSLUA_ATTRIBUTE FrameInfoConst_comment RO The first string comment for the packet, if any; + nil if there is no comment. */ +static int FrameInfoConst_get_comment (lua_State* L) { + FrameInfoConst fi = checkFrameInfoConst(L,1); +#define FRAMEINFOCONST_COMMENTS_TABLE 2 + gchar *comment = NULL; + wtap_block_t block = NULL; + guint i = 0; + guint n_comments = 0; + + block = fi->rec->block; + // XXX - how to get the user-edited block, if any? + n_comments = wtap_block_count_option(block, OPT_COMMENT); + lua_createtable(L, n_comments, 0); + for (i = 0; i < n_comments; i++) { + comment = NULL; + lua_pushnumber(L, i+1); + if (WTAP_OPTTYPE_SUCCESS == + wtap_block_get_nth_string_option_value(block, OPT_COMMENT, i, &comment)) { + lua_pushstring(L, comment); + } + else { + lua_pushnil(L); + } + lua_settable(L, FRAMEINFOCONST_COMMENTS_TABLE); + } + + return 1; +} + +/* 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->rec->ts.secs; + nstime->nsecs = fi->rec->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->rec) return 0; + + lua_pushlstring(L, fi->pd, fi->rec->rec_header.packet_header.caplen); + + return 1; +} + +/* WSLUA_ATTRIBUTE FrameInfoConst_rec_type RO The record type of the packet frame - see `wtap_presence_flags` for values. */ +WSLUA_ATTRIBUTE_NAMED_NUMBER_GETTER(FrameInfoConst,rec_type,rec->rec_type); + +/* WSLUA_ATTRIBUTE FrameInfoConst_flags RO The presence flags of the packet frame - see `wtap_presence_flags` for bits. */ +WSLUA_ATTRIBUTE_NAMED_NUMBER_GETTER(FrameInfoConst,flags,rec->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,rec->rec_header.packet_header.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,rec->rec_header.packet_header.len); + +/* WSLUA_ATTRIBUTE FrameInfoConst_encap RO The packet encapsulation type, if the file supports per-packet types. + + See `wtap_encaps` for possible packet encapsulation types to use as the value for this field. */ +WSLUA_ATTRIBUTE_NAMED_NUMBER_GETTER(FrameInfoConst,encap,rec->rec_header.packet_header.pkt_encap); + +WSLUA_ATTRIBUTES FrameInfoConst_attributes[] = { + WSLUA_ATTRIBUTE_ROREG(FrameInfoConst,rec_type), + 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[] = { + WSLUA_CLASS_FNREG(FrameInfoConst,write_data), + { NULL, NULL } +}; + +WSLUA_META FrameInfoConst_meta[] = { + WSLUA_CLASS_MTREG(FrameInfoConst,tostring), + { NULL, NULL } +}; + +int FrameInfoConst_register(lua_State* L) { + WSLUA_REGISTER_CLASS_WITH_ATTRS(FrameInfoConst); + 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: + */ diff --git a/epan/wslua/wslua_gui.c b/epan/wslua/wslua_gui.c new file mode 100644 index 00000000..fe6ab5cf --- /dev/null +++ b/epan/wslua/wslua_gui.c @@ -0,0 +1,1331 @@ +/* + * wslua_gui.c + * + * (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" + +#include <epan/wmem_scopes.h> + +#include "wslua.h" + +/* WSLUA_MODULE Gui GUI Support */ + +static const funnel_ops_t* ops = NULL; + +struct _lua_menu_data { + lua_State* L; + int cb_ref; +}; + +static int menu_cb_error_handler(lua_State* L) { + const gchar* error = lua_tostring(L,1); + report_failure("Lua: Error during execution of Menu callback:\n %s",error); + return 0; +} + +WSLUA_FUNCTION wslua_gui_enabled(lua_State* L) { /* Checks if we're running inside a GUI (i.e. Wireshark) or not. */ + lua_pushboolean(L,GPOINTER_TO_INT(ops && ops->add_button)); + WSLUA_RETURN(1); /* Boolean `true` if a GUI is available, `false` if it isn't. */ +} + +static void lua_menu_callback(gpointer data) { + struct _lua_menu_data* md = (struct _lua_menu_data *)data; + lua_State* L = md->L; + + lua_settop(L,0); + lua_pushcfunction(L,menu_cb_error_handler); + lua_rawgeti(L, LUA_REGISTRYINDEX, md->cb_ref); + + switch ( lua_pcall(L,0,0,1) ) { + case 0: + break; + case LUA_ERRRUN: + ws_warning("Runtime error while calling menu callback"); + break; + case LUA_ERRMEM: + ws_warning("Memory alloc error while calling menu callback"); + break; + case LUA_ERRERR: + ws_warning("Error while running the error handler function for menu callback"); + break; + default: + ws_assert_not_reached(); + break; + } + + return; +} + +WSLUA_FUNCTION wslua_register_menu(lua_State* L) { /* Register a menu item in one of the main menus. Requires a GUI. */ +#define WSLUA_ARG_register_menu_NAME 1 /* The name of the menu item. Use slashes to separate submenus. (e.g. menu:Lua Scripts[My Fancy Statistics]). (string) */ +#define WSLUA_ARG_register_menu_ACTION 2 /* The function to be called when the menu item is invoked. The function must take no arguments and return nothing. */ +#define WSLUA_OPTARG_register_menu_GROUP 3 /* + Where to place the item in the menu hierarchy. + If omitted, defaults to MENU_STAT_GENERIC. + Valid packet (Wireshark) items are: + * MENU_PACKET_ANALYZE_UNSORTED: menu:Analyze[] + * MENU_PACKET_STAT_UNSORTED: menu:Statistics[] + * MENU_STAT_GENERIC: menu:Statistics[], first section + * MENU_STAT_CONVERSATION_LIST: menu:Statistics[Conversation List] + * MENU_STAT_ENDPOINT_LIST: menu:Statistics[Endpoint List] + * MENU_STAT_RESPONSE_TIME: menu:Statistics[Service Response Time] + * MENU_STAT_RSERPOOL = menu:Statistics[Reliable Server Pooling (RSerPool)] + * MENU_STAT_TELEPHONY: menu:Telephony[] + * MENU_STAT_TELEPHONY_ANSI: menu:Telephony[ANSI] + * MENU_STAT_TELEPHONY_GSM: menu:Telephony[GSM] + * MENU_STAT_TELEPHONY_LTE: menu:Telephony[LTE] + * MENU_STAT_TELEPHONY_MTP3: menu:Telephony[MTP3] + * MENU_STAT_TELEPHONY_SCTP: menu:Telephony[SCTP] + * MENU_ANALYZE: menu:Analyze[] + * MENU_ANALYZE_CONVERSATION: menu:Analyze[Conversation Filter] + * MENU_TOOLS_UNSORTED: menu:Tools[] + + Valid log (Logray) items are: + * MENU_LOG_ANALYZE_UNSORTED: menu:Analyze[] + * MENU_LOG_STAT_UNSORTED = 16 + + The following are deprecated and shouldn't be used in new code: + * MENU_ANALYZE_UNSORTED, superseded by MENU_PACKET_ANALYZE_UNSORTED + * MENU_ANALYZE_CONVERSATION, superseded by MENU_ANALYZE_CONVERSATION_FILTER + * MENU_STAT_CONVERSATION, superseded by MENU_STAT_CONVERSATION_LIST + * MENU_STAT_ENDPOINT, superseded by MENU_STAT_ENDPOINT_LIST + * MENU_STAT_RESPONSE, superseded by MENU_STAT_RESPONSE_TIME + * MENU_STAT_UNSORTED, superseded by MENU_PACKET_STAT_UNSORTED + */ + + const gchar* name = luaL_checkstring(L,WSLUA_ARG_register_menu_NAME); + struct _lua_menu_data* md; + gboolean retap = FALSE; + register_stat_group_t group = (register_stat_group_t)wslua_optguint(L,WSLUA_OPTARG_register_menu_GROUP,REGISTER_STAT_GROUP_GENERIC); + + if ( group > REGISTER_TOOLS_GROUP_UNSORTED) { + WSLUA_OPTARG_ERROR(register_menu,GROUP,"Must be a defined MENU_*"); + return 0; + } + + if (!lua_isfunction(L,WSLUA_ARG_register_menu_ACTION)) { + WSLUA_ARG_ERROR(register_menu,ACTION,"Must be a function"); + return 0; + } + + md = g_new(struct _lua_menu_data, 1); + md->L = L; + + lua_pushvalue(L, 2); + md->cb_ref = luaL_ref(L, LUA_REGISTRYINDEX); + lua_remove(L,2); + + funnel_register_menu(name, + group, + lua_menu_callback, + md, + g_free, + retap); + + WSLUA_RETURN(0); +} + +void wslua_deregister_menus(void) { + funnel_deregister_menus(lua_menu_callback); +} + +/** + * Error handler used by lua_custom_packet_menu_callback when calling the user-supplied callback + * + * @param L State of the Lua interpreter + * @return Always returns 0 + */ +static int packet_menu_cb_error_handler(lua_State* L) { + const gchar* error = lua_tostring(L,1); + report_failure("Lua: Error During execution of Packet Menu Callback:\n %s",error); + return 0; +} + +/** + * Wrapper used to call the user-supplied Lua callback when a custom packet + * context menu is clicked. + * + * @param data Lua menu data + * @param finfo_array packet data + */ +static void lua_custom_packet_menu_callback(gpointer data, GPtrArray *finfo_array) { + // _lua_menu_data is State + the integer index of a callback. + struct _lua_menu_data* md = (struct _lua_menu_data *)data; + lua_State* L = md->L; + + lua_settop(L,0); + lua_pushcfunction(L,packet_menu_cb_error_handler); + lua_rawgeti(L, LUA_REGISTRYINDEX, md->cb_ref); + + // Push the packet data as arguments to the Lua callback: + int items_found = 0; + for (guint i = 0; i < finfo_array->len; i ++) { + field_info *fi = (field_info *)g_ptr_array_index (finfo_array, i); + push_FieldInfo(L, fi); + items_found++; + } + + switch ( lua_pcall(L,items_found,0,1) ) { + case 0: + break; + case LUA_ERRRUN: + g_warning("Runtime error while calling custom_packet_menu callback"); + break; + case LUA_ERRMEM: + g_warning("Memory alloc error while calling custom_packet_menu callback"); + break; + default: + g_assert_not_reached(); + break; + } + + return; +} + +/** + * Lua function exposed to users: register_packet_menu + */ +WSLUA_FUNCTION wslua_register_packet_menu(lua_State* L) { /* Register a menu item in the packet list. */ +#define WSLUA_ARG_register_packet_menu_NAME 1 /* The name of the menu item. Use slashes to separate submenus. (e.g. level1/level2/name). (string) */ +#define WSLUA_ARG_register_packet_menu_ACTION 2 /* The function to be called when the menu item is invoked. The function must take a variable number of arguments and return nothing. The arguments will be FieldInfo objects, one for each field present in the selected packet. */ +#define WSLUA_OPTARG_register_packet_menu_REQUIRED_FIELDS 3 /* A comma-separated list of packet fields (e.g., http.host,dns.qry.name) which all must be present for the menu to be displayed. If omitted, the packet menu will be displayed for all packets. */ + + const gchar* name = luaL_checkstring(L,WSLUA_ARG_register_packet_menu_NAME); + const gchar* required_fields = luaL_optstring(L,WSLUA_OPTARG_register_packet_menu_REQUIRED_FIELDS,""); + + struct _lua_menu_data* md; + gboolean retap = FALSE; + + if (!lua_isfunction(L,WSLUA_ARG_register_packet_menu_ACTION)) { + WSLUA_ARG_ERROR(register_packet_menu,ACTION,"Must be a function"); + return 0; + } + + md = g_new0(struct _lua_menu_data, 1); + md->L = L; + + lua_pushvalue(L, 2); + md->cb_ref = luaL_ref(L, LUA_REGISTRYINDEX); + lua_remove(L,2); + + funnel_register_packet_menu(name, + required_fields, + lua_custom_packet_menu_callback, + md, + retap); + WSLUA_RETURN(0); +} + +struct _dlg_cb_data { + lua_State* L; + int func_ref; +}; + +static int dlg_cb_error_handler(lua_State* L) { + const gchar* error = lua_tostring(L,1); + report_failure("Lua: Error during execution of Dialog callback:\n %s",error); + return 0; +} + +static void lua_dialog_cb(gchar** user_input, void* data) { + struct _dlg_cb_data* dcbd = (struct _dlg_cb_data *)data; + int i = 0; + gchar* input; + lua_State* L = dcbd->L; + + lua_settop(L,0); + lua_pushcfunction(L,dlg_cb_error_handler); + lua_rawgeti(L, LUA_REGISTRYINDEX, dcbd->func_ref); + + for (i = 0; (input = user_input[i]) ; i++) { + lua_pushstring(L,input); + g_free(input); + } + + g_free(user_input); + + switch ( lua_pcall(L,i,0,1) ) { + case 0: + break; + case LUA_ERRRUN: + ws_warning("Runtime error while calling dialog callback"); + break; + case LUA_ERRMEM: + ws_warning("Memory alloc error while calling dialog callback"); + break; + case LUA_ERRERR: + ws_warning("Error while running the error handler function for dialog callback"); + break; + default: + ws_assert_not_reached(); + break; + } + +} + +struct _close_cb_data { + lua_State* L; + int func_ref; + TextWindow wslua_tw; +}; + + +static int text_win_close_cb_error_handler(lua_State* L) { + const gchar* error = lua_tostring(L,1); + report_failure("Lua: Error during execution of TextWindow close callback:\n %s",error); + return 0; +} + +static void text_win_close_cb(void* data) { + struct _close_cb_data* cbd = (struct _close_cb_data *)data; + lua_State* L = cbd->L; + + if (cbd->L) { /* close function is set */ + + lua_settop(L,0); + lua_pushcfunction(L,text_win_close_cb_error_handler); + lua_rawgeti(L, LUA_REGISTRYINDEX, cbd->func_ref); + + switch ( lua_pcall(L,0,0,1) ) { + case 0: + break; + case LUA_ERRRUN: + ws_warning("Runtime error during execution of TextWindow close callback"); + break; + case LUA_ERRMEM: + ws_warning("Memory alloc error during execution of TextWindow close callback"); + break; + case LUA_ERRERR: + ws_warning("Error while running the error handler function for TextWindow close callback"); + break; + default: + break; + } + } + + if (cbd->wslua_tw->expired) { + g_free(cbd->wslua_tw); + g_free(cbd); + } else { + cbd->wslua_tw->expired = TRUE; + } + +} + +WSLUA_FUNCTION wslua_new_dialog(lua_State* L) { /* + Displays a dialog, prompting for input. The dialog includes an btn:[OK] button and btn:[Cancel] button. Requires a GUI. + + .An input dialog in action + image::images/wslua-new-dialog.png[{small-screenshot-attrs}] + + ===== Example + + [source,lua] + ---- + if not gui_enabled() then return end + + -- Prompt for IP and port and then print them to stdout + local label_ip = "IP address" + local label_port = "Port" + local function print_ip(ip, port) + print(label_ip, ip) + print(label_port, port) + end + new_dialog("Enter IP address", print_ip, label_ip, label_port) + + -- Prompt for 4 numbers and then print their product to stdout + new_dialog( + "Enter 4 numbers", + function (a, b, c, d) print(a * b * c * d) end, + "a", "b", "c", "d" + ) + ---- + */ +#define WSLUA_ARG_new_dialog_TITLE 1 /* The title of the dialog. */ +#define WSLUA_ARG_new_dialog_ACTION 2 /* Action to be performed when the user presses btn:[OK]. */ +/* WSLUA_MOREARGS new_dialog Strings to be used as labels of the dialog's fields. Each string creates a new labeled field. The first field is required. +Instead of a string it is possible to provide tables with fields 'name' and 'value' of type string. Then the created dialog's field will be labeled with the content of name and prefilled with the content of value.*/ + + const gchar* title; + int top = lua_gettop(L); + int i; + GPtrArray* field_names; + GPtrArray* field_values; + struct _dlg_cb_data* dcbd; + + if (! ops) { + luaL_error(L,"the GUI facility has to be enabled"); + return 0; + } + + if (!ops->new_dialog) { + WSLUA_ERROR(new_dialog,"GUI not available"); + return 0; + } + + title = luaL_checkstring(L,WSLUA_ARG_new_dialog_TITLE); + + if (! lua_isfunction(L,WSLUA_ARG_new_dialog_ACTION)) { + WSLUA_ARG_ERROR(new_dialog,ACTION,"Must be a function"); + return 0; + } + + if (top < 3) { + WSLUA_ERROR(new_dialog,"At least one field required"); + return 0; + } + + + dcbd = g_new(struct _dlg_cb_data, 1); + dcbd->L = L; + + lua_remove(L,1); + + lua_pushvalue(L, 1); + dcbd->func_ref = luaL_ref(L, LUA_REGISTRYINDEX); + lua_remove(L,1); + + field_names = g_ptr_array_new_with_free_func(g_free); + field_values = g_ptr_array_new_with_free_func(g_free); + + top -= 2; + + for (i = 1; i <= top; i++) + { + if (lua_isstring(L, i)) + { + gchar* field_name = g_strdup(luaL_checkstring(L, i)); + gchar* field_value = g_strdup(""); + g_ptr_array_add(field_names, (gpointer)field_name); + g_ptr_array_add(field_values, (gpointer)field_value); + } + else if (lua_istable(L, i)) + { + lua_getfield(L, i, "name"); + lua_getfield(L, i, "value"); + + if (!lua_isstring(L, -2)) + { + lua_pop(L, 2); + + g_ptr_array_free(field_names, TRUE); + g_ptr_array_free(field_values, TRUE); + g_free(dcbd); + WSLUA_ERROR(new_dialog, "All fields must be strings or a table with a string field 'name'."); + return 0; + } + + gchar* field_name = g_strdup(luaL_checkstring(L, -2)); + gchar* field_value = lua_isstring(L, -1) ? + g_strdup(luaL_checkstring(L, -1)) : + g_strdup(""); + + g_ptr_array_add(field_names, (gpointer)field_name); + g_ptr_array_add(field_values, (gpointer)field_value); + + lua_pop(L, 2); + } + else + { + g_ptr_array_free(field_names, TRUE); + g_ptr_array_free(field_values, TRUE); + g_free(dcbd); + WSLUA_ERROR(new_dialog, "All fields must be strings or a table with a string field 'name'."); + return 0; + } + } + + g_ptr_array_add(field_names, NULL); + g_ptr_array_add(field_values, NULL); + + ops->new_dialog(ops->ops_id, title, (const gchar**)(field_names->pdata), (const gchar**)(field_values->pdata), lua_dialog_cb, dcbd, g_free); + + g_ptr_array_free(field_names, TRUE); + g_ptr_array_free(field_values, TRUE); + + WSLUA_RETURN(0); +} + +WSLUA_CLASS_DEFINE(ProgDlg,FAIL_ON_NULL("ProgDlg")); +/* + Creates and manages a modal progress bar. + This is intended to be used with + http://lua-users.org/wiki/CoroutinesTutorial[coroutines], + where a main UI thread controls the progress bar dialog while a background coroutine (worker thread) yields to the main thread between steps. + The main thread checks the status of the btn:[Cancel] button and if it's not set, returns control to the coroutine. + + .A progress bar in action + image::images/wslua-progdlg.png[{medium-screenshot-attrs}] + + The legacy (GTK+) user interface displayed this as a separate dialog, hence the “Dlg” suffix. + The Qt user interface shows a progress bar inside the main status bar. +*/ + +WSLUA_CONSTRUCTOR ProgDlg_new(lua_State* L) { /* + Creates and displays a new `ProgDlg` progress bar with a btn:[Cancel] button and optional title. + It is highly recommended that you wrap code that uses a `ProgDlg` instance because it does not automatically close itself upon encountering an error. + Requires a GUI. + + ===== Example + + [source,lua] + ---- + if not gui_enabled() then return end + + local p = ProgDlg.new("Constructing", "tacos") + + -- We have to wrap the ProgDlg code in a pcall in case some unexpected + -- error occurs. + local ok, errmsg = pcall(function() + local co = coroutine.create( + function() + local limit = 100000 + for i=1,limit do + print("co", i) + coroutine.yield(i/limit, "step "..i.." of "..limit) + end + end + ) + + -- Whenever coroutine yields, check the status of the cancel button to determine + -- when to break. Wait up to 20 sec for coroutine to finish. + local start_time = os.time() + while coroutine.status(co) ~= 'dead' do + local elapsed = os.time() - start_time + + -- Quit if cancel button pressed or 20 seconds elapsed + if p:stopped() or elapsed > 20 then + break + end + + local res, val, val2 = coroutine.resume(co) + if not res or res == false then + if val then + debug(val) + end + print('coroutine error') + break + end + + -- show progress in progress dialog + p:update(val, val2) + end + end) + + p:close() + + if not ok and errmsg then + report_failure(errmsg) + end + ---- +*/ +#define WSLUA_OPTARG_ProgDlg_new_TITLE 1 /* Title of the progress bar. Defaults to "Progress". */ +#define WSLUA_OPTARG_ProgDlg_new_TASK 2 /* Optional task name, which will be appended to the title. Defaults to the empty string (""). */ + ProgDlg pd = (ProgDlg)g_malloc(sizeof(struct _wslua_progdlg)); + pd->title = g_strdup(luaL_optstring(L,WSLUA_OPTARG_ProgDlg_new_TITLE,"Progress")); + pd->task = g_strdup(luaL_optstring(L,WSLUA_OPTARG_ProgDlg_new_TASK,"")); + pd->stopped = FALSE; + + if (ops->new_progress_window) { + pd->pw = ops->new_progress_window(ops->ops_id, pd->title, pd->task, TRUE, &(pd->stopped)); + } else { + g_free (pd); + WSLUA_ERROR(ProgDlg_new, "GUI not available"); + return 0; + } + + pushProgDlg(L,pd); + + WSLUA_RETURN(1); /* The newly created `ProgDlg` object. */ +} + +WSLUA_METHOD ProgDlg_update(lua_State* L) { /* Sets the progress dialog's progress bar position based on percentage done. */ +#define WSLUA_ARG_ProgDlg_update_PROGRESS 2 /* Progress value, e.g. 0.75. Value must be between 0.0 and 1.0 inclusive. */ +#define WSLUA_OPTARG_ProgDlg_update_TASK 3 /* Task name. Currently ignored. Defaults to empty string (""). */ + ProgDlg pd = checkProgDlg(L,1); + double pr = lua_tonumber(L,WSLUA_ARG_ProgDlg_update_PROGRESS); + const gchar* task = luaL_optstring(L,WSLUA_OPTARG_ProgDlg_update_TASK,""); + + if (!ops->update_progress) { + WSLUA_ERROR(ProgDlg_update,"GUI not available"); + return 0; + } + + g_free(pd->task); + pd->task = g_strdup(task); + + /* XXX, dead code: pd already dereferenced. should it be: !pd->task? + if (!pd) { + WSLUA_ERROR(ProgDlg_update,"Cannot be called for something not a ProgDlg"); + } */ + + if (pr >= 0.0 && pr <= 1.0) { + ops->update_progress(pd->pw, (float) pr, task); + } else { + WSLUA_ERROR(ProgDlg_update,"Progress value out of range (must be between 0.0 and 1.0)"); + return 0; + } + + return 0; +} + +WSLUA_METHOD ProgDlg_stopped(lua_State* L) { /* Checks whether the user has pressed the btn:[Cancel] button. */ + ProgDlg pd = checkProgDlg(L,1); + + lua_pushboolean(L,pd->stopped); + + WSLUA_RETURN(1); /* Boolean `true` if the user has asked to stop the operation, `false` otherwise. */ +} + + + +WSLUA_METHOD ProgDlg_close(lua_State* L) { /* Hides the progress bar. */ + ProgDlg pd = checkProgDlg(L,1); + + if (!ops->destroy_progress_window) { + WSLUA_ERROR(ProgDlg_close,"GUI not available"); + return 0; + } + + if (pd->pw) { + ops->destroy_progress_window(pd->pw); + pd->pw = NULL; + } + return 0; +} + + +static int ProgDlg__tostring(lua_State* L) { + ProgDlg pd = checkProgDlg(L,1); + + lua_pushfstring(L, "%sstopped",pd->stopped?"":"not "); + + WSLUA_RETURN(1); /* A string specifying whether the Progress Dialog has stopped or not. */ +} + +/* Gets registered as metamethod automatically by WSLUA_REGISTER_CLASS/META */ +static int ProgDlg__gc(lua_State* L) { + ProgDlg pd = toProgDlg(L,1); + + if (pd) { + if (pd->pw && ops->destroy_progress_window) { + ops->destroy_progress_window(pd->pw); + } + + g_free(pd); + } else { + luaL_error(L, "ProgDlg__gc has being passed something else!"); + } + + return 0; +} + + +WSLUA_METHODS ProgDlg_methods[] = { + WSLUA_CLASS_FNREG(ProgDlg,new), + WSLUA_CLASS_FNREG(ProgDlg,update), + WSLUA_CLASS_FNREG(ProgDlg,stopped), + WSLUA_CLASS_FNREG(ProgDlg,close), + { NULL, NULL } +}; + +WSLUA_META ProgDlg_meta[] = { + WSLUA_CLASS_MTREG(ProgDlg,tostring), + { NULL, NULL } +}; + +int ProgDlg_register(lua_State* L) { + + ops = funnel_get_funnel_ops(); + + WSLUA_REGISTER_CLASS(ProgDlg); + + return 0; +} + + + +WSLUA_CLASS_DEFINE(TextWindow,FAIL_ON_NULL_OR_EXPIRED("TextWindow")); /* + + Creates and manages a text window. + The text can be read-only or editable, and buttons can be added below the text. + + .A text window in action + image::images/wslua-textwindow.png[{medium-screenshot-attrs}] +*/ + +/* XXX: button and close callback data is being leaked */ +/* XXX: lua callback function and TextWindow are not garbage collected because + they stay in LUA_REGISTRYINDEX forever */ + +WSLUA_CONSTRUCTOR TextWindow_new(lua_State* L) { /* + Creates a new `TextWindow` text window and displays it. + Requires a GUI. + + ===== Example + + [source,lua] + ---- + if not gui_enabled() then return end + + -- create new text window and initialize its text + local win = TextWindow.new("Log") + win:set("Hello world!") + + -- add buttons to clear text window and to enable editing + win:add_button("Clear", function() win:clear() end) + win:add_button("Enable edit", function() win:set_editable(true) end) + + -- add button to change text to uppercase + win:add_button("Uppercase", function() + local text = win:get_text() + if text ~= "" then + win:set(string.upper(text)) + end + end) + + -- print "closing" to stdout when the user closes the text window + win:set_atclose(function() print("closing") end) + ---- + +*/ +#define WSLUA_OPTARG_TextWindow_new_TITLE 1 /* Title of the new window. Optional. Defaults to "Untitled Window". */ + + const gchar* title; + TextWindow tw = NULL; + struct _close_cb_data* default_cbd; + + if (!ops->new_text_window || !ops->set_close_cb) { + WSLUA_ERROR(TextWindow_new,"GUI not available"); + return 0; + } + + title = luaL_optstring(L,WSLUA_OPTARG_TextWindow_new_TITLE, "Untitled Window"); + tw = g_new(struct _wslua_tw, 1); + tw->expired = FALSE; + tw->ws_tw = ops->new_text_window(ops->ops_id, title); + + default_cbd = g_new(struct _close_cb_data, 1); + + default_cbd->L = NULL; + default_cbd->func_ref = 0; + default_cbd->wslua_tw = tw; + + tw->close_cb_data = (void *)default_cbd; + + ops->set_close_cb(tw->ws_tw,text_win_close_cb,default_cbd); + + pushTextWindow(L,tw); + + WSLUA_RETURN(1); /* The newly created `TextWindow` object. */ +} + +WSLUA_METHOD TextWindow_set_atclose(lua_State* L) { /* Set the function that will be called when the text window closes. */ +#define WSLUA_ARG_TextWindow_set_atclose_ACTION 2 /* A Lua function to be executed when the user closes the text window. */ + + TextWindow tw = checkTextWindow(L,1); + struct _close_cb_data* cbd; + + if (!ops->set_close_cb) { + WSLUA_ERROR(TextWindow_set_atclose,"GUI not available"); + return 0; + } + + lua_settop(L,2); + + if (! lua_isfunction(L,2)) { + WSLUA_ARG_ERROR(TextWindow_set_atclose,ACTION,"Must be a function"); + return 0; + } + + cbd = g_new(struct _close_cb_data, 1); + + cbd->L = L; + cbd->func_ref = luaL_ref(L, LUA_REGISTRYINDEX); + cbd->wslua_tw = tw; + + g_free(tw->close_cb_data); + tw->close_cb_data = (void *)cbd; + + ops->set_close_cb(tw->ws_tw,text_win_close_cb,cbd); + + /* XXX: this is a bad way to do this - should copy the object on to the stack first */ + WSLUA_RETURN(1); /* The `TextWindow` object. */ +} + +WSLUA_METHOD TextWindow_set(lua_State* L) { /* Sets the text to be displayed. */ +#define WSLUA_ARG_TextWindow_set_TEXT 2 /* The text to be displayed. */ + + TextWindow tw = checkTextWindow(L,1); + const gchar* text = luaL_checkstring(L,WSLUA_ARG_TextWindow_set_TEXT); + + if (!ops->set_text) { + WSLUA_ERROR(TextWindow_set,"GUI not available"); + return 0; + } + + ops->set_text(tw->ws_tw,text); + + /* XXX: this is a bad way to do this - should copy the object on to the stack first */ + WSLUA_RETURN(1); /* The `TextWindow` object. */ +} + +WSLUA_METHOD TextWindow_append(lua_State* L) { /* Appends text to the current window contents. */ +#define WSLUA_ARG_TextWindow_append_TEXT 2 /* The text to be appended. */ + TextWindow tw = checkTextWindow(L,1); + const gchar* text = luaL_checkstring(L,WSLUA_ARG_TextWindow_append_TEXT); + + if (!ops->append_text) { + WSLUA_ERROR(TextWindow_append,"GUI not available"); + return 0; + } + + ops->append_text(tw->ws_tw,text); + + /* XXX: this is a bad way to do this - should copy the object on to the stack first */ + WSLUA_RETURN(1); /* The `TextWindow` object. */ +} + +WSLUA_METHOD TextWindow_prepend(lua_State* L) { /* Prepends text to the current window contents. */ +#define WSLUA_ARG_TextWindow_prepend_TEXT 2 /* The text to be prepended. */ + TextWindow tw = checkTextWindow(L,1); + const gchar* text = luaL_checkstring(L,WSLUA_ARG_TextWindow_prepend_TEXT); + + if (!ops->prepend_text) { + WSLUA_ERROR(TextWindow_prepend,"GUI not available"); + return 0; + } + + ops->prepend_text(tw->ws_tw,text); + + /* XXX: this is a bad way to do this - should copy the object on to the stack first */ + WSLUA_RETURN(1); /* The `TextWindow` object. */ +} + +WSLUA_METHOD TextWindow_clear(lua_State* L) { /* Erases all of the text in the window. */ + TextWindow tw = checkTextWindow(L,1); + + if (!ops->clear_text) { + WSLUA_ERROR(TextWindow_clear,"GUI not available"); + return 0; + } + + ops->clear_text(tw->ws_tw); + + /* XXX: this is a bad way to do this - should copy the object on to the stack first */ + WSLUA_RETURN(1); /* The `TextWindow` object. */ +} + +WSLUA_METHOD TextWindow_get_text(lua_State* L) { /* Get the text of the window. */ + TextWindow tw = checkTextWindow(L,1); + const gchar* text; + + if (!ops->get_text) { + WSLUA_ERROR(TextWindow_get_text,"GUI not available"); + return 0; + } + + text = ops->get_text(tw->ws_tw); + + lua_pushstring(L,text); + WSLUA_RETURN(1); /* The `TextWindow`++'++s text. */ +} + +WSLUA_METHOD TextWindow_close(lua_State* L) { /* Close the window. */ + TextWindow tw = checkTextWindow(L,1); + + if (!ops->destroy_text_window) { + WSLUA_ERROR(TextWindow_get_text,"GUI not available"); + return 0; + } + + ops->destroy_text_window(tw->ws_tw); + tw->ws_tw = NULL; + + return 0; +} + +/* Gets registered as metamethod automatically by WSLUA_REGISTER_CLASS/META */ +static int TextWindow__gc(lua_State* L) { + TextWindow tw = toTextWindow(L,1); + + if (!tw) + return 0; + + if (!tw->expired) { + tw->expired = TRUE; + if (ops->destroy_text_window) { + ops->destroy_text_window(tw->ws_tw); + } + } else { + g_free(tw->close_cb_data); + g_free(tw); + } + + return 0; +} + +WSLUA_METHOD TextWindow_set_editable(lua_State* L) { /* Make this text window editable. */ +#define WSLUA_OPTARG_TextWindow_set_editable_EDITABLE 2 /* `true` to make the text editable, `false` otherwise. Defaults to `true`. */ + + TextWindow tw = checkTextWindow(L,1); + gboolean editable = wslua_optbool(L,WSLUA_OPTARG_TextWindow_set_editable_EDITABLE,TRUE); + + if (!ops->set_editable) { + WSLUA_ERROR(TextWindow_set_editable,"GUI not available"); + return 0; + } + + ops->set_editable(tw->ws_tw,editable); + + WSLUA_RETURN(1); /* The `TextWindow` object. */ +} + +typedef struct _wslua_bt_cb_t { + lua_State* L; + int func_ref; + int wslua_tw_ref; +} wslua_bt_cb_t; + +static gboolean wslua_button_callback(funnel_text_window_t* ws_tw, void* data) { + wslua_bt_cb_t* cbd = (wslua_bt_cb_t *)data; + lua_State* L = cbd->L; + (void) ws_tw; /* ws_tw is unused since we need wslua_tw_ref and it is stored in cbd */ + + lua_settop(L,0); + lua_pushcfunction(L,dlg_cb_error_handler); + lua_rawgeti(L, LUA_REGISTRYINDEX, cbd->func_ref); + lua_rawgeti(L, LUA_REGISTRYINDEX, cbd->wslua_tw_ref); + + switch ( lua_pcall(L,1,0,1) ) { + case 0: + break; + case LUA_ERRRUN: + ws_warning("Runtime error while calling button callback"); + break; + case LUA_ERRMEM: + ws_warning("Memory alloc error while calling button callback"); + break; + case LUA_ERRERR: + ws_warning("Error while running the error handler function for button callback"); + break; + default: + ws_assert_not_reached(); + break; + } + + return TRUE; +} + +WSLUA_METHOD TextWindow_add_button(lua_State* L) { + /* Adds a button with an action handler to the text window. */ +#define WSLUA_ARG_TextWindow_add_button_LABEL 2 /* The button label. */ +#define WSLUA_ARG_TextWindow_add_button_FUNCTION 3 /* The Lua function to be called when the button is pressed. */ + TextWindow tw = checkTextWindow(L,1); + const gchar* label = luaL_checkstring(L,WSLUA_ARG_TextWindow_add_button_LABEL); + + funnel_bt_t* fbt; + wslua_bt_cb_t* cbd; + + if (!ops->add_button) { + WSLUA_ERROR(TextWindow_add_button,"GUI not available"); + return 0; + } + + if (! lua_isfunction(L,WSLUA_ARG_TextWindow_add_button_FUNCTION) ) { + WSLUA_ARG_ERROR(TextWindow_add_button,FUNCTION,"must be a function"); + return 0; + } + + lua_settop(L,3); + + if (ops->add_button) { + fbt = g_new(funnel_bt_t, 1); + cbd = g_new(wslua_bt_cb_t, 1); + + fbt->tw = tw->ws_tw; + fbt->func = wslua_button_callback; + fbt->data = cbd; + fbt->free_fcn = g_free; + fbt->free_data_fcn = g_free; + + cbd->L = L; + cbd->func_ref = luaL_ref(L, LUA_REGISTRYINDEX); + cbd->wslua_tw_ref = luaL_ref(L, LUA_REGISTRYINDEX); + + ops->add_button(tw->ws_tw,fbt,label); + } + + WSLUA_RETURN(1); /* The `TextWindow` object. */ +} + +WSLUA_METHODS TextWindow_methods[] = { + WSLUA_CLASS_FNREG(TextWindow,new), + WSLUA_CLASS_FNREG(TextWindow,set), + WSLUA_CLASS_FNREG(TextWindow,append), + WSLUA_CLASS_FNREG(TextWindow,prepend), + WSLUA_CLASS_FNREG(TextWindow,clear), + WSLUA_CLASS_FNREG(TextWindow,set_atclose), + WSLUA_CLASS_FNREG(TextWindow,set_editable), + WSLUA_CLASS_FNREG(TextWindow,get_text), + WSLUA_CLASS_FNREG(TextWindow,add_button), + WSLUA_CLASS_FNREG(TextWindow,close), + { NULL, NULL } +}; + +WSLUA_META TextWindow_meta[] = { + {"__tostring", TextWindow_get_text}, + { NULL, NULL } +}; + +int TextWindow_register(lua_State* L) { + + ops = funnel_get_funnel_ops(); + + WSLUA_REGISTER_CLASS(TextWindow); + + return 0; +} + + +WSLUA_FUNCTION wslua_retap_packets(lua_State* L) { + /* + Rescans all packets and runs each <<lua_class_Listener, tap listener>> without reconstructing the display. + */ + if ( ops->retap_packets ) { + ops->retap_packets(ops->ops_id); + } else { + WSLUA_ERROR(wslua_retap_packets, "GUI not available"); + } + + return 0; +} + + +WSLUA_FUNCTION wslua_copy_to_clipboard(lua_State* L) { /* Copy a string into the clipboard. Requires a GUI. */ +#define WSLUA_ARG_copy_to_clipboard_TEXT 1 /* The string to be copied into the clipboard. */ + const char* copied_str = luaL_checkstring(L,WSLUA_ARG_copy_to_clipboard_TEXT); + GString* gstr; + if (!ops->copy_to_clipboard) { + WSLUA_ERROR(copy_to_clipboard, "GUI not available"); + return 0; + } + + gstr = g_string_new(copied_str); + + ops->copy_to_clipboard(gstr); + + g_string_free(gstr,TRUE); + + return 0; +} + +WSLUA_FUNCTION wslua_open_capture_file(lua_State* L) { /* Open and display a capture file. Requires a GUI. */ +#define WSLUA_ARG_open_capture_file_FILENAME 1 /* The name of the file to be opened. */ +#define WSLUA_ARG_open_capture_file_FILTER 2 /* The https://gitlab.com/wireshark/wireshark/-/wikis/DisplayFilters[display filter] to be applied once the file is opened. */ + + const char* fname = luaL_checkstring(L,WSLUA_ARG_open_capture_file_FILENAME); + const char* filter = luaL_optstring(L,WSLUA_ARG_open_capture_file_FILTER,NULL); + char* error = NULL; + + if (!ops->open_file) { + WSLUA_ERROR(open_capture_file, "GUI not available"); + return 0; + } + + if (! ops->open_file(ops->ops_id, fname, filter, &error) ) { + lua_pushboolean(L,FALSE); + + if (error) { + lua_pushstring(L,error); + g_free(error); + } else + lua_pushnil(L); + + return 2; + } else { + lua_pushboolean(L,TRUE); + return 1; + } +} + +WSLUA_FUNCTION wslua_get_filter(lua_State* L) { /* Get the main filter text. */ + const char *filter_str = NULL; + + if (!ops->get_filter) { + WSLUA_ERROR(get_filter, "GUI not available"); + return 0; + } + + filter_str = ops->get_filter(ops->ops_id); + lua_pushstring(L,filter_str); + + return 1; +} + +WSLUA_FUNCTION wslua_set_filter(lua_State* L) { /* Set the main filter text. */ +#define WSLUA_ARG_set_filter_TEXT 1 /* The filter's text. */ + const char* filter_str = luaL_checkstring(L,WSLUA_ARG_set_filter_TEXT); + + if (!ops->set_filter) { + WSLUA_ERROR(set_filter, "GUI not available"); + return 0; + } + + ops->set_filter(ops->ops_id, filter_str); + + return 0; +} + +WSLUA_FUNCTION wslua_get_color_filter_slot(lua_State* L) { /* + Gets the current https://gitlab.com/wireshark/wireshark/-/wikis/ColoringRules[packet coloring rule] (by index) for the + current session. Wireshark reserves 10 slots for these coloring rules. Requires a GUI. +*/ +#define WSLUA_ARG_get_color_filter_slot_ROW 1 /* + The index (1-10) of the desired color filter value in the temporary coloring rules list. + + .Default background colors + [cols="3",options="header"] + |=== + |Index |RGB (hex) |Color + |1 |ffc0c0 |{set:cellbgcolor:#ffc0c0} pink 1 + |2 |ffc0ff |{set:cellbgcolor:#ffc0ff} pink 2 + |3 |e0c0e0 |{set:cellbgcolor:#e0c0e0} purple 1 + |4 |c0c0ff |{set:cellbgcolor:#c0c0ff} purple 2 + |5 |c0e0e0 |{set:cellbgcolor:#c0e0e0} green 1 + |6 |c0ffff |{set:cellbgcolor:#c0ffff} green 2 + |7 |c0ffc0 |{set:cellbgcolor:#c0ffc0} green 3 + |8 |ffffc0 |{set:cellbgcolor:#ffffc0} yellow 1 + |9 |e0e0c0 |{set:cellbgcolor:#e0e0c0} yellow 2 + |10 |e0e0e0 |{set:cellbgcolor:#e0e0e0} gray + |=== + */ + guint8 row = (guint8)luaL_checkinteger(L, WSLUA_ARG_get_color_filter_slot_ROW); + gchar* filter_str = NULL; + + if (!ops->get_color_filter_slot) { + WSLUA_ERROR(get_color_filter_slot, "GUI not available"); + return 0; + } + + filter_str = ops->get_color_filter_slot(row); + if (filter_str == NULL) { + lua_pushnil(L); + } else { + lua_pushstring(L, filter_str); + g_free(filter_str); + } + + return 1; +} + +WSLUA_FUNCTION wslua_set_color_filter_slot(lua_State* L) { /* + Sets a https://gitlab.com/wireshark/wireshark/-/wikis/ColoringRules[packet coloring rule] (by index) for the current session. + Wireshark reserves 10 slots for these coloring rules. + Requires a GUI. +*/ +#define WSLUA_ARG_set_color_filter_slot_ROW 1 /* + The index (1-10) of the desired color in the temporary coloring rules list. + The default foreground is black and the default backgrounds are listed below. + + // XXX We need get the colors working, e.g. by adding them to a stylesheet. + .Default background colors + [cols="3",options="header"] + |=== + |Index |RGB (hex) |Color + |1 |ffc0c0 |{set:cellbgcolor:#ffc0c0} pink 1 + |2 |ffc0ff |{set:cellbgcolor:#ffc0ff} pink 2 + |3 |e0c0e0 |{set:cellbgcolor:#e0c0e0} purple 1 + |4 |c0c0ff |{set:cellbgcolor:#c0c0ff} purple 2 + |5 |c0e0e0 |{set:cellbgcolor:#c0e0e0} green 1 + |6 |c0ffff |{set:cellbgcolor:#c0ffff} green 2 + |7 |c0ffc0 |{set:cellbgcolor:#c0ffc0} green 3 + |8 |ffffc0 |{set:cellbgcolor:#ffffc0} yellow 1 + |9 |e0e0c0 |{set:cellbgcolor:#e0e0c0} yellow 2 + |10 |e0e0e0 |{set:cellbgcolor:#e0e0e0} gray + |=== + + The color list can be set from the command line using two unofficial preferences: `gui.colorized_frame.bg` and `gui.colorized_frame.fg`, which require 10 hex RGB codes (6 hex digits each), e.g. + ---- + wireshark -o gui.colorized_frame.bg:${RGB0},${RGB1},${RGB2},${RGB3},${RGB4},${RGB5},${RGB6},${RGB7},${RGB8},${RGB9} + ---- + + For example, this command yields the same results as the table above (and with all foregrounds set to black): + ---- + wireshark -o gui.colorized_frame.bg:ffc0c0,ffc0ff,e0c0e0,c0c0ff,c0e0e0,c0ffff,c0ffc0,ffffc0,e0e0c0,e0e0e0 -o gui.colorized_frame.fg:000000,000000,000000,000000,000000,000000,000000,000000,000000,000000 + ---- + */ +#define WSLUA_ARG_set_color_filter_slot_TEXT 2 /* The https://gitlab.com/wireshark/wireshark/-/wikis/DisplayFilters[display filter] for selecting packets to be colorized +. */ + guint8 row = (guint8)luaL_checkinteger(L,WSLUA_ARG_set_color_filter_slot_ROW); + const gchar* filter_str = luaL_checkstring(L,WSLUA_ARG_set_color_filter_slot_TEXT); + + if (!ops->set_color_filter_slot) { + WSLUA_ERROR(set_color_filter_slot, "GUI not available"); + return 0; + } + + ops->set_color_filter_slot(row, filter_str); + + return 0; +} + +WSLUA_FUNCTION wslua_apply_filter(lua_State* L) { /* + Apply the filter in the main filter box. + Requires a GUI. + + [WARNING] + ==== + Avoid calling this from within a dissector function or else an infinite loop can occur if it causes the dissector to be called again. + This function is best used in a button callback (from a dialog or text window) or menu callback. + ==== + */ + if (!ops->apply_filter) { + WSLUA_ERROR(apply_filter, "GUI not available"); + return 0; + } + + ops->apply_filter(ops->ops_id); + + return 0; +} + + +WSLUA_FUNCTION wslua_reload(lua_State* L) { /* Reload the current capture file. Deprecated. Use reload_packets() instead. */ + + if (!ops->reload_packets) { + WSLUA_ERROR(reload, "GUI not available"); + return 0; + } + + ops->reload_packets(ops->ops_id); + + return 0; +} + + +WSLUA_FUNCTION wslua_reload_packets(lua_State* L) { /* + Reload the current capture file. + Requires a GUI. + + [WARNING] + ==== + Avoid calling this from within a dissector function or else an infinite loop can occur if it causes the dissector to be called again. + This function is best used in a button callback (from a dialog or text window) or menu callback. + ==== + */ + + if (!ops->reload_packets) { + WSLUA_ERROR(reload, "GUI not available"); + return 0; + } + + ops->reload_packets(ops->ops_id); + + return 0; +} + + +WSLUA_FUNCTION wslua_redissect_packets(lua_State* L) { /* + Redissect all packets in the current capture file. + Requires a GUI. + + [WARNING] + ==== + Avoid calling this from within a dissector function or else an infinite loop can occur if it causes the dissector to be called again. + This function is best used in a button callback (from a dialog or text window) or menu callback. + ==== + */ + + if (!ops->redissect_packets) { + WSLUA_ERROR(reload, "GUI not available"); + return 0; + } + + ops->redissect_packets(ops->ops_id); + + return 0; +} + + +WSLUA_FUNCTION wslua_reload_lua_plugins(lua_State* L) { /* Reload all Lua plugins. */ + + if (!ops->reload_lua_plugins) { + WSLUA_ERROR(reload_lua_plugins, "GUI not available"); + return 0; + } + + ops->reload_lua_plugins(ops->ops_id); + + return 0; +} + + +WSLUA_FUNCTION wslua_browser_open_url(lua_State* L) { /* + Opens an URL in a web browser. Requires a GUI. + + [WARNING] + ==== + Do not pass an untrusted URL to this function. + + It will be passed to the system's URL handler, which might execute malicious code, switch on your Bluetooth-connected foghorn, or any of a number of unexpected or harmful things. + ==== + */ +#define WSLUA_ARG_browser_open_url_URL 1 /* The url. */ + const char* url = luaL_checkstring(L,WSLUA_ARG_browser_open_url_URL); + + if (!ops->browser_open_url) { + WSLUA_ERROR(browser_open_url, "GUI not available"); + return 0; + } + + ops->browser_open_url(url); + + return 0; +} + +WSLUA_FUNCTION wslua_browser_open_data_file(lua_State* L) { /* + Open a file located in the data directory (specified in the Wireshark preferences) in the web browser. + If the file does not exist, the function silently ignores the request. + Requires a GUI. + + [WARNING] + ==== + Do not pass an untrusted URL to this function. + + It will be passed to the system's URL handler, which might execute malicious code, switch on your Bluetooth-connected foghorn, or any of a number of unexpected or harmful things. + ==== + */ +#define WSLUA_ARG_browser_open_data_file_FILENAME 1 /* The file name. */ + const char* file = luaL_checkstring(L,WSLUA_ARG_browser_open_data_file_FILENAME); + + if (!ops->browser_open_data_file) { + WSLUA_ERROR(browser_open_data_file, "GUI not available"); + return 0; + } + + ops->browser_open_data_file(file); + + 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: + */ diff --git a/epan/wslua/wslua_int64.c b/epan/wslua/wslua_int64.c new file mode 100644 index 00000000..e540d6d2 --- /dev/null +++ b/epan/wslua/wslua_int64.c @@ -0,0 +1,1170 @@ +/* + * A Lua userdata object for 64-bit signed/unsigned integers. + * + * I, Hadriel Kaplan, the author of wslua_int6464.c, wish to put it in + * the Public Domain. That is not universally accepted, however, + * so you may license it under the FreeBSD License instead, which is an open + * source license approved for GPL use as well as commercial etc. + * It's even less restrictive than the MIT license, because it requires + * no attribution anywhere - I don't *want* attribution. + +Copyright (C) 2013 Hadriel Kaplan <hadrielk@yahoo.com> +All rights reserved. + +SPDX-License-Identifier: BSD-2-Clause + +The views and conclusions contained in the software and documentation are those +of the authors and should not be interpreted as representing official policies, +either expressed or implied, of the FreeBSD Project. +*/ + +#include "config.h" + +#include "wslua.h" + + /* + WSLUA_MODULE Int64 Handling 64-bit Integers + + Lua uses one single number representation which can be chosen at compile time and since it is often set to IEEE 754 double precision floating point, one cannot store 64 bit integers with full precision. + + Lua numbers are stored as floating point (doubles) internally, not integers; thus while they can represent incredibly large numbers, above 2^53 they lose integral precision -- they can't represent every whole integer value. + For example if you set a lua variable to the number 9007199254740992 and tried to increment it by 1, you'd get the same number because it can't represent 9007199254740993 (only the even number 9007199254740994). + + Therefore, in order to count higher than 2^53 in integers, we need a true integer type. + The way this is done is with an explicit 'Int64' or 'UInt64' object (i.e., Lua userdata). + This object has metamethods for all of the math and comparison operators, so you can handle it like any number variable. + For the math operators, it can even be mixed with plain Lua numbers. + + For example 'my64num = my64num + 1' will work even if 'my64num' is a <<lua_class_Int64,`Int64`>> or <<lua_class_UInt64,`UInt64`>> object. + Note that comparison operators ('==','$$<=$$','>', etc.) will not work with plain numbers -- only other Int64/UInt64 objects. + This is a limitation of Lua itself, in terms of how it handles operator overloading. + + // Previous to Wireshark release 1.11, Int64 and UInt64 could only be created by tvbrange:int64() or tvbrange:le_int64(), or tvbrange:uint64() or tvbrange:le_uint64() or tvbrange:bitfield(), and had only a couple functions (the metamethods tostring() and concat()). + // All of the functions on this page are only available starting in Wireshark 1.11 and higher. + + [WARNING] + ==== + Many of the UInt64/Int64 functions accept a Lua number as an argument. + You should be very careful to never use Lua numbers bigger than 32 bits (i.e., the number value 4,294,967,295 or the literal 0xFFFFFFFF) for such arguments, because Lua itself does not handle bigger numbers consistently across platforms (32-bit vs. 64-bit systems), and because a Lua number is a C-code double which cannot have more than 53 bits of precision. + Instead, use a Int64 or UInt64 for the argument. + ==== + + For example, do this... + + [source,lua] + ---- + local mynum = UInt64(0x2b89dd1e, 0x3f91df0b) + ---- + + ...instead of this: + + [source,lua] + ---- + -- Bad. Leads to inconsistent results across platforms + local mynum = UInt64(0x3f91df0b2b89dd1e) + ---- + + And do this... + + [source,lua] + ---- + local masked = mynum:band(UInt64(0, 0xFFFFFFFF)) + ---- + + ...instead of this: + + [source,lua] + ---- + -- Bad. Leads to inconsistent results across platforms + local masked = mynum:band(0xFFFFFFFF00000000) + ---- + */ + +#define LUATYPE64_STRING_SIZE 21 /* string to hold 18446744073709551615 */ + +#if G_BYTE_ORDER == G_LITTLE_ENDIAN +#define IS_LITTLE_ENDIAN TRUE +#else +#define IS_LITTLE_ENDIAN FALSE +#endif + +WSLUA_CLASS_DEFINE_BASE(Int64,NOP,0); + /* + <<lua_class_Int64,`Int64`>> represents a 64 bit signed integer. + + Note the caveats <<lua_module_Int64,listed above>>. + */ + +/* A checkInt64 but that also auto-converts numbers, strings, and UINT64 to a gint64 */ +static gint64 getInt64(lua_State *L, int i) +{ + gchar *end = NULL; + (void) end; + switch (lua_type(L,i)) + { + case LUA_TNUMBER: + return wslua_checkgint64(L,i); + case LUA_TSTRING: + return g_ascii_strtoll(luaL_checkstring(L,i),&end,10); + case LUA_TUSERDATA: + if (isUInt64(L, i)) { + return (Int64) toUInt64(L, i); + } + /* fall through */ + default: + return checkInt64(L,i); + } +} + + +/* Encodes Int64 userdata into Lua string struct with given endianness */ +void Int64_pack(lua_State* L, luaL_Buffer *b, gint idx, gboolean asLittleEndian) { + gint64 value = checkInt64(L,idx); + gint8 buff[sizeof(gint64)]; + + if (asLittleEndian) { + guint i; + for (i = 0; i < sizeof(gint64); i++) { + buff[i] = (value & 0xff); + value >>= 8; + } + } + else { + gint i; + for (i = sizeof(gint64) - 1; i >= 0; i--) { + buff[i] = (value & 0xff); + value >>= 8; + } + } + luaL_addlstring(b, (char*)buff, sizeof(gint64)); +} + +WSLUA_METHOD Int64_encode(lua_State* L) { + /* Encodes the <<lua_class_Int64,`Int64`>> number into an 8-byte Lua string using the given endianness. + @since 1.11.3 + */ +#define WSLUA_OPTARG_Int64_encode_ENDIAN 2 /* If set to true then little-endian is used, + if false then big-endian; if missing or `nil`, + native host endian. */ + luaL_Buffer b; + gboolean asLittleEndian = IS_LITTLE_ENDIAN; + + if (lua_gettop(L) >= WSLUA_OPTARG_Int64_encode_ENDIAN) { + if (lua_type(L,WSLUA_OPTARG_Int64_encode_ENDIAN) == LUA_TBOOLEAN) + asLittleEndian = lua_toboolean(L,WSLUA_OPTARG_Int64_encode_ENDIAN); + } + + luaL_buffinit(L, &b); + + Int64_pack(L, &b, 1, asLittleEndian); + + luaL_pushresult(&b); + WSLUA_RETURN(1); /* The Lua string. */ +} + +/* Decodes from string buffer struct into Int64 userdata, with given endianness */ +int Int64_unpack(lua_State* L, const gchar *buff, gboolean asLittleEndian) { + gint64 value = 0; + gint i; + + if (asLittleEndian) { + for (i = sizeof(gint64) - 1; i >= 0; i--) { + value <<= 8; + value |= (gint64)(guchar)buff[i]; + } + } + else { + for (i = 0; i < (gint) sizeof(gint64); i++) { + value <<= 8; + value |= (gint64)(guchar)buff[i]; + } + } + + pushInt64(L,value); + return 1; +} + +WSLUA_CONSTRUCTOR Int64_decode(lua_State* L) { + /* Decodes an 8-byte Lua string, using the given endianness, into a new <<lua_class_Int64,`Int64`>> object. + @since 1.11.3 + */ +#define WSLUA_ARG_Int64_decode_STRING 1 /* The Lua string containing a binary 64-bit integer. */ +#define WSLUA_OPTARG_Int64_decode_ENDIAN 2 /* If set to true then little-endian is used, + if false then big-endian; if missing or `nil`, native + host endian. */ + gboolean asLittleEndian = IS_LITTLE_ENDIAN; + size_t len = 0; + const gchar *s = luaL_checklstring(L, WSLUA_ARG_Int64_decode_STRING, &len); + + if (lua_gettop(L) >= WSLUA_OPTARG_Int64_decode_ENDIAN) { + if (lua_type(L,WSLUA_OPTARG_Int64_decode_ENDIAN) == LUA_TBOOLEAN) + asLittleEndian = lua_toboolean(L,WSLUA_OPTARG_Int64_decode_ENDIAN); + } + + if (len == sizeof(gint64)) { + Int64_unpack(L, s, asLittleEndian); + } else { + lua_pushnil(L); + } + + WSLUA_RETURN(1); /* The <<lua_class_Int64,`Int64`>> object created, or nil on failure. */ +} + +WSLUA_CONSTRUCTOR Int64_new(lua_State* L) { + /* Creates a <<lua_class_Int64,`Int64`>> Object. + @since 1.11.3 + */ +#define WSLUA_OPTARG_Int64_new_VALUE 1 /* A number, <<lua_class_UInt64,`UInt64`>>, <<lua_class_Int64,`Int64`>>, or string of ASCII digits + to assign the value of the new <<lua_class_Int64,`Int64`>>. Default is 0. */ +#define WSLUA_OPTARG_Int64_new_HIGHVALUE 2 /* If this is a number and the first argument was + a number, then the first will be treated as a + lower 32 bits, and this is the high-order 32 + bit number. */ + gint64 value = 0; + + if (lua_gettop(L) >= 1) { + switch(lua_type(L, WSLUA_OPTARG_Int64_new_VALUE)) { + case LUA_TNUMBER: + value = wslua_togint64(L, WSLUA_OPTARG_Int64_new_VALUE); + if (lua_gettop(L) == 2 && + lua_type(L, WSLUA_OPTARG_Int64_new_HIGHVALUE) == LUA_TNUMBER) { + gint64 h = wslua_togint64(L, WSLUA_OPTARG_Int64_new_HIGHVALUE); + value &= G_GUINT64_CONSTANT(0x00000000FFFFFFFF); + h <<= 32; h &= G_GUINT64_CONSTANT(0xFFFFFFFF00000000); + value += h; + } + break; + case LUA_TSTRING: + case LUA_TUSERDATA: + value = getInt64(L,WSLUA_OPTARG_Int64_new_VALUE); + break; + default: + WSLUA_OPTARG_ERROR(Int64_new,VALUE,"must be a number, UInt64, Int64, or string"); + break; + } + } + + pushInt64(L,value); + + WSLUA_RETURN(1); /* The new <<lua_class_Int64,`Int64`>> object. */ +} + +WSLUA_METAMETHOD Int64__call(lua_State* L) { + /* Creates a <<lua_class_Int64,`Int64`>> object. + @since 1.11.3 + */ + lua_remove(L,1); /* remove the table */ + WSLUA_RETURN(Int64_new(L)); /* The new <<lua_class_Int64,`Int64`>> object. */ +} + +WSLUA_CONSTRUCTOR Int64_max(lua_State* L) { + /* Creates an <<lua_class_Int64,`Int64`>> of the maximum possible positive value. In other words, this should return an Int64 object of the number 9,223,372,036,854,775,807. + @since 1.11.3 + */ + pushInt64(L, G_MAXINT64); + WSLUA_RETURN(1); /* The new <<lua_class_Int64,`Int64`>> object of the maximum value. */ +} + +WSLUA_CONSTRUCTOR Int64_min(lua_State* L) { + /* Creates an <<lua_class_Int64,`Int64`>> of the minimum possible negative value. In other words, this should return an Int64 object of the number -9,223,372,036,854,775,808. + @since 1.11.3 + */ + pushInt64(L, G_MININT64); + WSLUA_RETURN(1); /* The new <<lua_class_Int64,`Int64`>> object of the minimum value. */ +} + + +WSLUA_METHOD Int64_tonumber(lua_State* L) { + /* Returns a Lua number of the <<lua_class_Int64,`Int64`>> value. Note that this may lose precision. + @since 1.11.3 + */ + lua_pushnumber(L, (lua_Number)(checkInt64(L,1))); + WSLUA_RETURN(1); /* The Lua number. */ +} + +WSLUA_CONSTRUCTOR Int64_fromhex(lua_State* L) { + /* Creates an <<lua_class_Int64,`Int64`>> object from the given hexadecimal string. + @since 1.11.3 + */ +#define WSLUA_ARG_Int64_fromhex_HEX 1 /* The hex-ASCII Lua string. */ + guint64 result = 0; + size_t len = 0; + const gchar *s = luaL_checklstring(L,WSLUA_ARG_Int64_fromhex_HEX,&len); + + if (len > 0) { + if (sscanf(s, "%" SCNx64, &result) != 1) { + return luaL_error(L, "Error decoding the passed-in hex string"); + } + } + pushInt64(L,(gint64)result); + WSLUA_RETURN(1); /* The new <<lua_class_Int64,`Int64`>> object. */ +} + +WSLUA_METHOD Int64_tohex(lua_State* L) { + /* Returns a hexadecimal string of the <<lua_class_Int64,`Int64`>> value. + @since 1.11.3 + */ +#define WSLUA_OPTARG_Int64_tohex_NUMBYTES 2 /* The number of hex chars/nibbles to generate. + A negative value generates uppercase. Default is 16. */ + gint64 b = getInt64(L,1); + lua_Integer n = luaL_optinteger(L, WSLUA_OPTARG_Int64_tohex_NUMBYTES, 16); + const gchar *hexdigits = "0123456789abcdef"; + gchar buf[16]; + lua_Integer i; + if (n < 0) { n = -n; hexdigits = "0123456789ABCDEF"; } + if (n > 16) n = 16; + for (i = n-1; i >= 0; --i) { buf[i] = hexdigits[b & 15]; b >>= 4; } + lua_pushlstring(L, buf, (size_t)n); + WSLUA_RETURN(1); /* The string hex. */ +} + +WSLUA_METHOD Int64_higher(lua_State* L) { + /* Returns a Lua number of the higher 32 bits of the <<lua_class_Int64,`Int64`>> value. A negative <<lua_class_Int64,`Int64`>> + will return a negative Lua number. + @since 1.11.3 + */ + gint64 num = getInt64(L,1); + gint64 b = num; + lua_Number n = 0; + if (b < 0) b = -b; /* masking/shifting negative int64 isn't working on some platforms */ + b &= G_GUINT64_CONSTANT(0x7FFFFFFF00000000); + b >>= 32; + n = (lua_Number)(guint32)(b & G_GUINT64_CONSTANT(0x00000000FFFFFFFFF)); + if (num < 0) n = -n; + lua_pushnumber(L,n); + WSLUA_RETURN(1); /* The Lua number. */ +} + +WSLUA_METHOD Int64_lower(lua_State* L) { + /* Returns a Lua number of the lower 32 bits of the <<lua_class_Int64,`Int64`>> value. This will always be positive. + @since 1.11.3 + */ + gint64 b = getInt64(L,1); + if (b < 0) b = -b; /* masking/shifting negative int64 isn't working on some platforms */ + lua_pushnumber(L,(guint32)(b & G_GUINT64_CONSTANT(0x00000000FFFFFFFFF))); + WSLUA_RETURN(1); /* The Lua number. */ +} + +WSLUA_METAMETHOD Int64__tostring(lua_State* L) { + /* Converts the <<lua_class_Int64,`Int64`>> into a string of decimal digits. */ + gint64 num = getInt64(L,1); + gchar s[LUATYPE64_STRING_SIZE]; + if (snprintf(s, LUATYPE64_STRING_SIZE, "%" PRId64, num) < 0) { + return luaL_error(L, "Error writing Int64 to a string"); + } + lua_pushstring(L,s); + WSLUA_RETURN(1); /* The Lua string. */ +} + +WSLUA_METAMETHOD Int64__unm(lua_State* L) { + /* Returns the negative of the <<lua_class_Int64,`Int64`>> as a new <<lua_class_Int64,`Int64`>>. + @since 1.11.3 + */ + pushInt64(L,-(getInt64(L,1))); + WSLUA_RETURN(1); /* The new <<lua_class_Int64,`Int64`>>. */ +} + +#define WSLUA_MATH_OP_FUNC(obj,op) \ + /* use the 'get' form so we can accept numbers as well */ \ + obj num1 = get##obj(L,1); \ + obj num2 = get##obj(L,2); \ + push##obj(L,(num1) op (num2)); \ + return 1 + +WSLUA_METAMETHOD Int64__add(lua_State* L) { + /* Adds two <<lua_class_Int64,`Int64`>> together and returns a new one. The value may wrapped. + @since 1.11.3 + */ + WSLUA_MATH_OP_FUNC(Int64,+); +} + +WSLUA_METAMETHOD Int64__sub(lua_State* L) { + /* Subtracts two <<lua_class_Int64,`Int64`>> and returns a new one. The value may wrapped. + @since 1.11.3 + */ + WSLUA_MATH_OP_FUNC(Int64,-); +} + +WSLUA_METAMETHOD Int64__mul(lua_State* L) { + /* Multiplies two <<lua_class_Int64,`Int64`>> and returns a new one. The value may truncated. + @since 1.11.3 + */ + WSLUA_MATH_OP_FUNC(Int64,*); +} + +WSLUA_METAMETHOD Int64__div(lua_State* L) { + /* Divides two <<lua_class_Int64,`Int64`>> and returns a new one. Integer divide, no remainder. + Trying to divide by zero results in a Lua error. + @since 1.11.3 + */ + Int64 num1 = getInt64(L,1); + Int64 num2 = getInt64(L,2); + if (num2 == 0) { + return luaL_error(L, "Trying to divide Int64 by zero"); + } + pushInt64(L, num1 / num2); + WSLUA_RETURN(1); /* The <<lua_class_Int64,`Int64`>> object. */ +} + +WSLUA_METAMETHOD Int64__mod(lua_State* L) { + /* Divides two <<lua_class_Int64,`Int64`>> and returns a new one of the remainder. + Trying to modulo by zero results in a Lua error. + @since 1.11.3 + */ + Int64 num1 = getInt64(L,1); + Int64 num2 = getInt64(L,2); + if (num2 == 0) { + return luaL_error(L, "Trying to modulo Int64 by zero"); + } + pushInt64(L, num1 % num2); + WSLUA_RETURN(1); /* The <<lua_class_Int64,`Int64`>> object. */ +} + +WSLUA_METAMETHOD Int64__pow(lua_State* L) { + /* The first <<lua_class_Int64,`Int64`>> is taken to the power of the second <<lua_class_Int64,`Int64`>>, returning a new + one. This may truncate the value. + @since 1.11.3 + */ + gint64 num1 = getInt64(L,1); + gint64 num2 = getInt64(L,2); + gint64 result; + if (num1 == 2) { + result = (num2 >= 8 * (gint64) sizeof(gint64)) ? 0 : ((gint64)1 << num2); + } + else { + for (result = 1; num2 > 0; num2 >>= 1) { + if (num2 & 1) result *= num1; + num1 *= num1; + } + } + pushInt64(L,result); + WSLUA_RETURN(1); /* The <<lua_class_Int64,`Int64`>> object. */ +} + +#define WSLUA_COMP_OP_FUNC(obj,op) \ + obj num1 = get##obj(L,1); \ + obj num2 = get##obj(L,2); \ + lua_pushboolean(L,(num1) op (num2)); \ + return 1 + +WSLUA_METAMETHOD Int64__eq(lua_State* L) { + /* Returns `true` if both <<lua_class_Int64,`Int64`>> are equal. + @since 1.11.3 + */ + WSLUA_COMP_OP_FUNC(Int64,==); +} + +WSLUA_METAMETHOD Int64__lt(lua_State* L) { + /* Returns `true` if first <<lua_class_Int64,`Int64`>> is less than the second. + @since 1.11.3 + */ + WSLUA_COMP_OP_FUNC(Int64,<); +} + +WSLUA_METAMETHOD Int64__le(lua_State* L) { + /* Returns `true` if the first <<lua_class_Int64,`Int64`>> is less than or equal to the second. + @since 1.11.3 + */ + WSLUA_COMP_OP_FUNC(Int64,<=); +} + +WSLUA_METHOD Int64_bnot(lua_State* L) { + /* Returns a <<lua_class_Int64,`Int64`>> of the bitwise 'not' operation. + @since 1.11.3 + */ + pushInt64(L,~(getInt64(L,1))); + WSLUA_RETURN(1); /* The <<lua_class_Int64,`Int64`>> object. */ +} + +#define WSLUA_BIT_OP_FUNC(obj,op) \ + gint32 i; \ + obj num = get##obj(L,1); \ + for (i = lua_gettop(L); i > 1; i--) { \ + num op get##obj(L,i); \ + } \ + push##obj(L,num); \ + return 1 + +WSLUA_METHOD Int64_band(lua_State* L) { + /* Returns a <<lua_class_Int64,`Int64`>> of the bitwise 'and' operation with the given number/`Int64`/`UInt64`. + Note that multiple arguments are allowed. + @since 1.11.3 + */ + WSLUA_BIT_OP_FUNC(Int64,&=); +} + +WSLUA_METHOD Int64_bor(lua_State* L) { + /* Returns a <<lua_class_Int64,`Int64`>> of the bitwise 'or' operation, with the given number/`Int64`/`UInt64`. + Note that multiple arguments are allowed. + @since 1.11.3 + */ + WSLUA_BIT_OP_FUNC(Int64,|=); +} + +WSLUA_METHOD Int64_bxor(lua_State* L) { + /* Returns a <<lua_class_Int64,`Int64`>> of the bitwise 'xor' operation, with the given number/`Int64`/`UInt64`. + Note that multiple arguments are allowed. + @since 1.11.3 + */ + WSLUA_BIT_OP_FUNC(Int64,^=); +} + +WSLUA_METHOD Int64_lshift(lua_State* L) { + /* Returns a <<lua_class_Int64,`Int64`>> of the bitwise logical left-shift operation, by the given + number of bits. + @since 1.11.3 + */ +#define WSLUA_ARG_Int64_lshift_NUMBITS 2 /* The number of bits to left-shift by. */ + guint64 b = (guint64) getInt64(L,1); + guint32 n = wslua_checkguint32(L,WSLUA_ARG_Int64_lshift_NUMBITS); + pushInt64(L,(gint64)(b << n)); + WSLUA_RETURN(1); /* The <<lua_class_Int64,`Int64`>> object. */ +} + +WSLUA_METHOD Int64_rshift(lua_State* L) { + /* Returns a <<lua_class_Int64,`Int64`>> of the bitwise logical right-shift operation, by the + given number of bits. + @since 1.11.3 + */ +#define WSLUA_ARG_Int64_rshift_NUMBITS 2 /* The number of bits to right-shift by. */ + guint64 b = (guint64) getInt64(L,1); + guint32 n = wslua_checkguint32(L,WSLUA_ARG_Int64_rshift_NUMBITS); + pushInt64(L,(gint64)(b >> n)); + WSLUA_RETURN(1); /* The <<lua_class_Int64,`Int64`>> object. */ +} + +WSLUA_METHOD Int64_arshift(lua_State* L) { + /* Returns a <<lua_class_Int64,`Int64`>> of the bitwise arithmetic right-shift operation, by the + given number of bits. + @since 1.11.3 + */ +#define WSLUA_ARG_Int64_arshift_NUMBITS 2 /* The number of bits to right-shift by. */ + gint64 b = getInt64(L,1); + gint32 n = wslua_checkgint32(L,WSLUA_ARG_Int64_arshift_NUMBITS); + pushInt64(L,(b >> n)); + WSLUA_RETURN(1); /* The <<lua_class_Int64,`Int64`>> object. */ +} + +WSLUA_METHOD Int64_rol(lua_State* L) { + /* Returns a <<lua_class_Int64,`Int64`>> of the bitwise left rotation operation, by the given number of + bits (up to 63). + @since 1.11.3 + */ +#define WSLUA_ARG_Int64_rol_NUMBITS 2 /* The number of bits to roll left by. */ + guint64 b = (guint64) getInt64(L,1); + guint32 n = wslua_checkguint32(L,WSLUA_ARG_Int64_rol_NUMBITS); + pushInt64(L,(gint64)((b << n) | (b >> (64-n)))); + WSLUA_RETURN(1); /* The <<lua_class_Int64,`Int64`>> object. */ +} + +WSLUA_METHOD Int64_ror(lua_State* L) { + /* Returns a <<lua_class_Int64,`Int64`>> of the bitwise right rotation operation, by the given number of + bits (up to 63). + @since 1.11.3 + */ +#define WSLUA_ARG_Int64_ror_NUMBITS 2 /* The number of bits to roll right by. */ + guint64 b = (guint64) getInt64(L,1); + guint32 n = wslua_checkguint32(L,WSLUA_ARG_Int64_ror_NUMBITS); + pushInt64(L,(gint64)((b << (64-n)) | (b >> n))); + WSLUA_RETURN(1); /* The <<lua_class_Int64,`Int64`>> object. */ +} + +WSLUA_METHOD Int64_bswap(lua_State* L) { + /* Returns a <<lua_class_Int64,`Int64`>> of the bytes swapped. This can be used to convert little-endian + 64-bit numbers to big-endian 64 bit numbers or vice versa. + @since 1.11.3 + */ + guint64 b = (guint64) getInt64(L,1); + guint64 result = 0; + size_t i; + for (i = 0; i < sizeof(gint64); i++) { + result <<= 8; + result |= (b & G_GUINT64_CONSTANT(0x00000000000000FF)); + b >>= 8; + } + pushInt64(L,(gint64)result); + WSLUA_RETURN(1); /* The <<lua_class_Int64,`Int64`>> object. */ +} + +/* Gets registered as metamethod automatically by WSLUA_REGISTER_CLASS/META. */ +static int Int64__gc(lua_State* L _U_) { + return 0; +} + +WSLUA_METHODS Int64_methods[] = { + WSLUA_CLASS_FNREG(Int64,new), + WSLUA_CLASS_FNREG(Int64,max), + WSLUA_CLASS_FNREG(Int64,min), + WSLUA_CLASS_FNREG(Int64,tonumber), + WSLUA_CLASS_FNREG(Int64,fromhex), + WSLUA_CLASS_FNREG(Int64,tohex), + WSLUA_CLASS_FNREG(Int64,higher), + WSLUA_CLASS_FNREG(Int64,lower), + WSLUA_CLASS_FNREG(Int64,encode), + WSLUA_CLASS_FNREG(Int64,decode), + WSLUA_CLASS_FNREG(Int64,bnot), + WSLUA_CLASS_FNREG(Int64,band), + WSLUA_CLASS_FNREG(Int64,bor), + WSLUA_CLASS_FNREG(Int64,bxor), + WSLUA_CLASS_FNREG(Int64,lshift), + WSLUA_CLASS_FNREG(Int64,rshift), + WSLUA_CLASS_FNREG(Int64,arshift), + WSLUA_CLASS_FNREG(Int64,rol), + WSLUA_CLASS_FNREG(Int64,ror), + WSLUA_CLASS_FNREG(Int64,bswap), + { NULL, NULL } +}; + +WSLUA_META Int64_meta[] = { + WSLUA_CLASS_MTREG(Int64,tostring), + WSLUA_CLASS_MTREG(Int64,call), + WSLUA_CLASS_MTREG(wslua,concat), + WSLUA_CLASS_MTREG(Int64,unm), + WSLUA_CLASS_MTREG(Int64,add), + WSLUA_CLASS_MTREG(Int64,sub), + WSLUA_CLASS_MTREG(Int64,mul), + WSLUA_CLASS_MTREG(Int64,div), + WSLUA_CLASS_MTREG(Int64,mod), + WSLUA_CLASS_MTREG(Int64,pow), + WSLUA_CLASS_MTREG(Int64,eq), + WSLUA_CLASS_MTREG(Int64,lt), + WSLUA_CLASS_MTREG(Int64,le), + { NULL, NULL } +}; + +LUALIB_API int Int64_register(lua_State* L) { + WSLUA_REGISTER_CLASS(Int64); + return 0; +} + + + +WSLUA_CLASS_DEFINE_BASE(UInt64,NOP,0); + /* + <<lua_class_UInt64,`UInt64`>> represents a 64 bit unsigned integer, similar to <<lua_class_Int64,`Int64`>>. + + Note the caveats <<lua_module_Int64,listed above>>. + */ + +/* A checkUInt64 but that also auto-converts numbers, strings, and <<lua_class_Int64,`Int64`>> to a guint64. */ +guint64 getUInt64(lua_State *L, int i) +{ + gchar *end = NULL; + (void) end; + switch (lua_type(L,i)) + { + case LUA_TNUMBER: + return wslua_checkguint64(L,i); + case LUA_TSTRING: + return g_ascii_strtoull(luaL_checkstring(L,i), &end, 10); + case LUA_TUSERDATA: + if (isInt64(L, i)) { + return (UInt64) toInt64(L, i); + } + /* fall through */ + default: + return checkUInt64(L,i); + } +} + +/* Encodes <<lua_class_UInt64,`UInt64`>> userdata into Lua string struct with given endianness */ +void UInt64_pack(lua_State* L, luaL_Buffer *b, gint idx, gboolean asLittleEndian) { + guint64 value = checkUInt64(L,idx); + gint8 buff[sizeof(guint64)]; + + if (asLittleEndian) { + guint i; + for (i = 0; i < sizeof(guint64); i++) { + buff[i] = (value & 0xff); + value >>= 8; + } + } + else { + gint i; + for (i = sizeof(guint64) - 1; i >= 0; i--) { + buff[i] = (value & 0xff); + value >>= 8; + } + } + luaL_addlstring(b, (char*)buff, sizeof(guint64)); +} + +WSLUA_METHOD UInt64_encode(lua_State* L) { + /* Encodes the <<lua_class_UInt64,`UInt64`>> number into an 8-byte Lua binary string, using given endianness. + @since 1.11.3 + */ +#define WSLUA_OPTARG_UInt64_encode_ENDIAN 2 /* If set to true then little-endian is used, + if false then big-endian; if missing or `nil`, + native host endian. */ + luaL_Buffer b; + gboolean asLittleEndian = IS_LITTLE_ENDIAN; + + if (lua_gettop(L) >= 2) { + if (lua_type(L,2) == LUA_TBOOLEAN) + asLittleEndian = lua_toboolean(L,2); + } + + luaL_buffinit(L, &b); + + UInt64_pack(L, &b, 1, asLittleEndian); + + luaL_pushresult(&b); + WSLUA_RETURN(1); /* The Lua binary string. */ +} + +/* Decodes from string buffer struct into <<lua_class_UInt64,`UInt64`>> userdata, with given endianness. */ +int UInt64_unpack(lua_State* L, const gchar *buff, gboolean asLittleEndian) { + guint64 value = 0; + gint i; + + if (asLittleEndian) { + for (i = sizeof(guint64) - 1; i >= 0; i--) { + value <<= 8; + value |= (guint64)(guchar)buff[i]; + } + } + else { + for (i = 0; i < (gint) sizeof(guint64); i++) { + value <<= 8; + value |= (guint64)(guchar)buff[i]; + } + } + + pushUInt64(L,value); + return 1; +} + +WSLUA_CONSTRUCTOR UInt64_decode(lua_State* L) { + /* Decodes an 8-byte Lua binary string, using given endianness, into a new <<lua_class_UInt64,`UInt64`>> object. + @since 1.11.3 + */ +#define WSLUA_ARG_UInt64_decode_STRING 1 /* The Lua string containing a binary 64-bit integer. */ +#define WSLUA_OPTARG_UInt64_decode_ENDIAN 2 /* If set to true then little-endian is used, + if false then big-endian; if missing or `nil`, + native host endian. */ + gboolean asLittleEndian = IS_LITTLE_ENDIAN; + size_t len = 0; + const gchar *s = luaL_checklstring(L, WSLUA_ARG_UInt64_decode_STRING, &len); + + if (lua_gettop(L) >= WSLUA_OPTARG_UInt64_decode_ENDIAN) { + if (lua_type(L,WSLUA_OPTARG_UInt64_decode_ENDIAN) == LUA_TBOOLEAN) + asLittleEndian = lua_toboolean(L,WSLUA_OPTARG_UInt64_decode_ENDIAN); + } + + if (len == sizeof(guint64)) { + UInt64_unpack(L, s, asLittleEndian); + } else { + lua_pushnil(L); + } + + WSLUA_RETURN(1); /* The <<lua_class_UInt64,`UInt64`>> object created, or nil on failure. */ +} + +WSLUA_CONSTRUCTOR UInt64_new(lua_State* L) { + /* Creates a <<lua_class_UInt64,`UInt64`>> Object. + @since 1.11.3 + */ +#define WSLUA_OPTARG_UInt64_new_VALUE 1 /* A number, <<lua_class_UInt64,`UInt64`>>, <<lua_class_Int64,`Int64`>>, or string of digits + to assign the value of the new <<lua_class_UInt64,`UInt64`>>. Default is 0. */ +#define WSLUA_OPTARG_UInt64_new_HIGHVALUE 2 /* If this is a number and the first argument was + a number, then the first will be treated as a + lower 32 bits, and this is the high-order + 32-bit number. */ + guint64 value = 0; + + if (lua_gettop(L) >= 1) { + switch(lua_type(L, WSLUA_OPTARG_UInt64_new_VALUE)) { + case LUA_TNUMBER: + value = wslua_toguint64(L, WSLUA_OPTARG_UInt64_new_VALUE); + if (lua_gettop(L) == 2 && + lua_type(L, WSLUA_OPTARG_UInt64_new_HIGHVALUE) == LUA_TNUMBER) { + guint64 h = wslua_toguint64(L, WSLUA_OPTARG_UInt64_new_HIGHVALUE); + value &= G_GUINT64_CONSTANT(0x00000000FFFFFFFF); + h <<= 32; h &= G_GUINT64_CONSTANT(0xFFFFFFFF00000000); + value += h; + } + break; + case LUA_TSTRING: + case LUA_TUSERDATA: + value = getUInt64(L, WSLUA_OPTARG_UInt64_new_VALUE); + break; + default: + WSLUA_OPTARG_ERROR(UInt64_new,VALUE,"must be a number, UInt64, Int64, or string"); + break; + } + } + + pushUInt64(L,value); + + WSLUA_RETURN(1); /* The new <<lua_class_UInt64,`UInt64`>> object. */ +} + +WSLUA_METAMETHOD UInt64__call(lua_State* L) { + /* Creates a <<lua_class_UInt64,`UInt64`>> object. + @since 1.11.3 + */ + lua_remove(L,1); /* remove the table */ + WSLUA_RETURN(UInt64_new(L)); /* The new <<lua_class_UInt64,`UInt64`>> object. */ +} + +WSLUA_CONSTRUCTOR UInt64_max(lua_State* L) { + /* Creates a <<lua_class_UInt64,`UInt64`>> of the maximum possible value. In other words, this should return an UInt64 object of the number 18,446,744,073,709,551,615. + @since 1.11.3 + */ + pushUInt64(L,G_MAXUINT64); + WSLUA_RETURN(1); /* The maximum value. */ +} + +WSLUA_CONSTRUCTOR UInt64_min(lua_State* L) { + /* Creates a <<lua_class_UInt64,`UInt64`>> of the minimum possible value. In other words, this should return an UInt64 object of the number 0. + @since 1.11.3 + */ + pushUInt64(L,0); + WSLUA_RETURN(1); /* The minimum value. */ +} + +WSLUA_METHOD UInt64_tonumber(lua_State* L) { + /* Returns a Lua number of the <<lua_class_UInt64,`UInt64`>> value. This may lose precision. + @since 1.11.3 + */ + lua_pushnumber(L,(lua_Number)(checkUInt64(L,1))); + WSLUA_RETURN(1); /* The Lua number. */ +} + +WSLUA_METAMETHOD UInt64__tostring(lua_State* L) { + /* Converts the <<lua_class_UInt64,`UInt64`>> into a string. */ + guint64 num = getUInt64(L,1); + gchar s[LUATYPE64_STRING_SIZE]; + if (snprintf(s, LUATYPE64_STRING_SIZE, "%" PRIu64,(guint64)num) < 0) { + return luaL_error(L, "Error writing UInt64 to a string"); + } + lua_pushstring(L,s); + WSLUA_RETURN(1); /* The Lua string. */ +} + +WSLUA_CONSTRUCTOR UInt64_fromhex(lua_State* L) { + /* Creates a <<lua_class_UInt64,`UInt64`>> object from the given hex string. + @since 1.11.3 + */ +#define WSLUA_ARG_UInt64_fromhex_HEX 1 /* The hex-ASCII Lua string. */ + guint64 result = 0; + size_t len = 0; + const gchar *s = luaL_checklstring(L,WSLUA_ARG_UInt64_fromhex_HEX,&len); + + if (len > 0) { + if (sscanf(s, "%" SCNx64, &result) != 1) { + return luaL_error(L, "Error decoding the passed-in hex string"); + } + } + pushUInt64(L,result); + WSLUA_RETURN(1); /* The new <<lua_class_UInt64,`UInt64`>> object. */ +} + +WSLUA_METHOD UInt64_tohex(lua_State* L) { + /* Returns a hex string of the <<lua_class_UInt64,`UInt64`>> value. + @since 1.11.3 + */ +#define WSLUA_OPTARG_UInt64_tohex_NUMBYTES 2 /* The number of hex-chars/nibbles to generate. + Negative means uppercase Default is 16. */ + guint64 b = getUInt64(L,1); + lua_Integer n = luaL_optinteger(L, WSLUA_OPTARG_UInt64_tohex_NUMBYTES, 16); + const gchar *hexdigits = "0123456789abcdef"; + gchar buf[16]; + lua_Integer i; + if (n < 0) { n = -n; hexdigits = "0123456789ABCDEF"; } + if (n > 16) n = 16; + for (i = n-1; i >= 0; --i) { buf[i] = hexdigits[b & 15]; b >>= 4; } + lua_pushlstring(L, buf, (size_t)n); + WSLUA_RETURN(1); /* The string hex. */ +} + +WSLUA_METHOD UInt64_higher(lua_State* L) { + /* Returns a Lua number of the higher 32 bits of the <<lua_class_UInt64,`UInt64`>> value. */ + guint64 num = getUInt64(L,1); + guint64 b = num; + lua_Number n = 0; + b &= G_GUINT64_CONSTANT(0xFFFFFFFF00000000); + b >>= 32; + n = (lua_Number)(guint32)(b & G_GUINT64_CONSTANT(0x00000000FFFFFFFFF)); + lua_pushnumber(L,n); + WSLUA_RETURN(1); /* The Lua number. */ +} + +WSLUA_METHOD UInt64_lower(lua_State* L) { + /* Returns a Lua number of the lower 32 bits of the <<lua_class_UInt64,`UInt64`>> value. */ + guint64 b = getUInt64(L,1); + lua_pushnumber(L,(guint32)(b & G_GUINT64_CONSTANT(0x00000000FFFFFFFFF))); + WSLUA_RETURN(1); /* The Lua number. */ +} + +WSLUA_METAMETHOD UInt64__unm(lua_State* L) { + /* Returns the <<lua_class_UInt64,`UInt64`>> in a new <<lua_class_UInt64,`UInt64`>>, since unsigned integers can't be negated. + @since 1.11.3 + */ + pushUInt64(L,getUInt64(L,1)); + WSLUA_RETURN(1); /* The <<lua_class_UInt64,`UInt64`>> object. */ +} + +WSLUA_METAMETHOD UInt64__add(lua_State* L) { + /* Adds two <<lua_class_UInt64,`UInt64`>> together and returns a new one. This may wrap the value. + @since 1.11.3 + */ + WSLUA_MATH_OP_FUNC(UInt64,+); +} + +WSLUA_METAMETHOD UInt64__sub(lua_State* L) { + /* Subtracts two <<lua_class_UInt64,`UInt64`>> and returns a new one. This may wrap the value. + @since 1.11.3 + */ + WSLUA_MATH_OP_FUNC(UInt64,-); +} + +WSLUA_METAMETHOD UInt64__mul(lua_State* L) { + /* Multiplies two <<lua_class_UInt64,`UInt64`>> and returns a new one. This may truncate the value. + @since 1.11.3 + */ + WSLUA_MATH_OP_FUNC(UInt64,*); +} + +WSLUA_METAMETHOD UInt64__div(lua_State* L) { + /* Divides two <<lua_class_UInt64,`UInt64`>> and returns a new one. Integer divide, no remainder. + Trying to divide by zero results in a Lua error. + @since 1.11.3 + */ + UInt64 num1 = getUInt64(L,1); + UInt64 num2 = getUInt64(L,2); + if (num2 == 0) { + return luaL_error(L, "Trying to divide UInt64 by zero"); + } + pushUInt64(L, num1 / num2); + WSLUA_RETURN(1); /* The <<lua_class_UInt64,`UInt64`>> result. */ +} + +WSLUA_METAMETHOD UInt64__mod(lua_State* L) { + /* Divides two <<lua_class_UInt64,`UInt64`>> and returns a new one of the remainder. + Trying to modulo by zero results in a Lua error. + @since 1.11.3 + */ + UInt64 num1 = getUInt64(L,1); + UInt64 num2 = getUInt64(L,2); + if (num2 == 0) { + return luaL_error(L, "Trying to modulo UInt64 by zero"); + } + pushUInt64(L, num1 % num2); + WSLUA_RETURN(1); /* The <<lua_class_UInt64,`UInt64`>> result. */ +} + +WSLUA_METAMETHOD UInt64__pow(lua_State* L) { + /* The first <<lua_class_UInt64,`UInt64`>> is taken to the power of the second <<lua_class_UInt64,`UInt64`>>/number, + returning a new one. This may truncate the value. + @since 1.11.3 + */ + guint64 num1 = getUInt64(L,1); + guint64 num2 = getUInt64(L,2); + guint64 result; + if (num1 == 2) { + result = (num2 >= 8 * (guint64) sizeof(guint64)) ? 0 : ((guint64)1 << num2); + } + else { + for (result = 1; num2 > 0; num2 >>= 1) { + if (num2 & 1) result *= num1; + num1 *= num1; + } + } + pushUInt64(L,result); + WSLUA_RETURN(1); /* The <<lua_class_UInt64,`UInt64`>> object. */ +} + +WSLUA_METAMETHOD UInt64__eq(lua_State* L) { + /* Returns true if both <<lua_class_UInt64,`UInt64`>> are equal. + @since 1.11.3 + */ + WSLUA_COMP_OP_FUNC(UInt64,==); +} + +WSLUA_METAMETHOD UInt64__lt(lua_State* L) { + /* Returns true if first <<lua_class_UInt64,`UInt64`>> is less than the second. + @since 1.11.3 + */ + WSLUA_COMP_OP_FUNC(UInt64,<); +} + +WSLUA_METAMETHOD UInt64__le(lua_State* L) { + /* Returns true if first <<lua_class_UInt64,`UInt64`>> is less than or equal to the second. + @since 1.11.3 + */ + WSLUA_COMP_OP_FUNC(UInt64,<=); +} + +WSLUA_METHOD UInt64_bnot(lua_State* L) { + /* Returns a <<lua_class_UInt64,`UInt64`>> of the bitwise 'not' operation. + @since 1.11.3 + */ + pushUInt64(L,~(getUInt64(L,1))); + WSLUA_RETURN(1); /* The <<lua_class_UInt64,`UInt64`>> object. */ +} + +WSLUA_METHOD UInt64_band(lua_State* L) { + /* Returns a <<lua_class_UInt64,`UInt64`>> of the bitwise 'and' operation, with the given number/`Int64`/`UInt64`. + Note that multiple arguments are allowed. + @since 1.11.3 + */ + WSLUA_BIT_OP_FUNC(UInt64,&=); +} + +WSLUA_METHOD UInt64_bor(lua_State* L) { + /* Returns a <<lua_class_UInt64,`UInt64`>> of the bitwise 'or' operation, with the given number/`Int64`/`UInt64`. + Note that multiple arguments are allowed. + @since 1.11.3 + */ + WSLUA_BIT_OP_FUNC(UInt64,|=); +} + +WSLUA_METHOD UInt64_bxor(lua_State* L) { + /* Returns a <<lua_class_UInt64,`UInt64`>> of the bitwise 'xor' operation, with the given number/`Int64`/`UInt64`. + Note that multiple arguments are allowed. + @since 1.11.3 + */ + WSLUA_BIT_OP_FUNC(UInt64,^=); +} + +WSLUA_METHOD UInt64_lshift(lua_State* L) { + /* Returns a <<lua_class_UInt64,`UInt64`>> of the bitwise logical left-shift operation, by the + given number of bits. + @since 1.11.3 + */ +#define WSLUA_ARG_UInt64_lshift_NUMBITS 2 /* The number of bits to left-shift by. */ + guint64 b = getUInt64(L,1); + guint32 n = wslua_checkguint32(L,WSLUA_ARG_UInt64_lshift_NUMBITS); + pushUInt64(L,(b << n)); + WSLUA_RETURN(1); /* The <<lua_class_UInt64,`UInt64`>> object. */ +} + +WSLUA_METHOD UInt64_rshift(lua_State* L) { + /* Returns a <<lua_class_UInt64,`UInt64`>> of the bitwise logical right-shift operation, by the + given number of bits. + @since 1.11.3 + */ +#define WSLUA_ARG_UInt64_rshift_NUMBITS 2 /* The number of bits to right-shift by. */ + guint64 b = getUInt64(L,1); + guint32 n = wslua_checkguint32(L,WSLUA_ARG_UInt64_rshift_NUMBITS); + pushUInt64(L,(b >> n)); + WSLUA_RETURN(1); /* The <<lua_class_UInt64,`UInt64`>> object. */ +} + +WSLUA_METHOD UInt64_arshift(lua_State* L) { + /* Returns a <<lua_class_UInt64,`UInt64`>> of the bitwise arithmetic right-shift operation, by the + given number of bits. + @since 1.11.3 + */ +#define WSLUA_ARG_UInt64_arshift_NUMBITS 2 /* The number of bits to right-shift by. */ + guint64 b = getUInt64(L,1); + guint32 n = wslua_checkguint32(L,WSLUA_ARG_UInt64_arshift_NUMBITS); + pushUInt64(L,(b >> n)); + WSLUA_RETURN(1); /* The <<lua_class_UInt64,`UInt64`>> object. */ +} + +WSLUA_METHOD UInt64_rol(lua_State* L) { + /* Returns a <<lua_class_UInt64,`UInt64`>> of the bitwise left rotation operation, by the + given number of bits (up to 63). + @since 1.11.3 + */ +#define WSLUA_ARG_UInt64_rol_NUMBITS 2 /* The number of bits to roll left by. */ + guint64 b = getUInt64(L,1); + guint32 n = wslua_checkguint32(L,WSLUA_ARG_UInt64_rol_NUMBITS); + pushUInt64(L,((b << n) | (b >> (64-n)))); + WSLUA_RETURN(1); /* The <<lua_class_UInt64,`UInt64`>> object. */ +} + +WSLUA_METHOD UInt64_ror(lua_State* L) { + /* Returns a <<lua_class_UInt64,`UInt64`>> of the bitwise right rotation operation, by the + given number of bits (up to 63). + @since 1.11.3 + */ +#define WSLUA_ARG_UInt64_ror_NUMBITS 2 /* The number of bits to roll right by. */ + guint64 b = getUInt64(L,1); + guint32 n = wslua_checkguint32(L,WSLUA_ARG_UInt64_ror_NUMBITS); + pushUInt64(L,((b << (64-n)) | (b >> n))); + WSLUA_RETURN(1); /* The <<lua_class_UInt64,`UInt64`>> object. */ +} + +WSLUA_METHOD UInt64_bswap(lua_State* L) { + /* Returns a <<lua_class_UInt64,`UInt64`>> of the bytes swapped. This can be used to convert little-endian + 64-bit numbers to big-endian 64 bit numbers or vice versa. + @since 1.11.3 + */ + guint64 b = getUInt64(L,1); + guint64 result = 0; + size_t i; + for (i = 0; i < sizeof(guint64); i++) { + result <<= 8; + result |= (b & G_GUINT64_CONSTANT(0x00000000000000FF)); + b >>= 8; + } + pushUInt64(L,result); + WSLUA_RETURN(1); /* The <<lua_class_UInt64,`UInt64`>> object. */ +} + +/* Gets registered as metamethod automatically by WSLUA_REGISTER_CLASS/META */ +static int UInt64__gc(lua_State* L _U_) { + return 0; +} + +WSLUA_METHODS UInt64_methods[] = { + WSLUA_CLASS_FNREG(UInt64,new), + WSLUA_CLASS_FNREG(UInt64,max), + WSLUA_CLASS_FNREG(UInt64,min), + WSLUA_CLASS_FNREG(UInt64,tonumber), + WSLUA_CLASS_FNREG(UInt64,fromhex), + WSLUA_CLASS_FNREG(UInt64,tohex), + WSLUA_CLASS_FNREG(UInt64,higher), + WSLUA_CLASS_FNREG(UInt64,lower), + WSLUA_CLASS_FNREG(UInt64,encode), + WSLUA_CLASS_FNREG(UInt64,decode), + WSLUA_CLASS_FNREG(UInt64,bnot), + WSLUA_CLASS_FNREG(UInt64,band), + WSLUA_CLASS_FNREG(UInt64,bor), + WSLUA_CLASS_FNREG(UInt64,bxor), + WSLUA_CLASS_FNREG(UInt64,lshift), + WSLUA_CLASS_FNREG(UInt64,rshift), + WSLUA_CLASS_FNREG(UInt64,arshift), + WSLUA_CLASS_FNREG(UInt64,rol), + WSLUA_CLASS_FNREG(UInt64,ror), + WSLUA_CLASS_FNREG(UInt64,bswap), + { NULL, NULL } +}; + +WSLUA_META UInt64_meta[] = { + WSLUA_CLASS_MTREG(UInt64,tostring), + WSLUA_CLASS_MTREG(UInt64,call), + WSLUA_CLASS_MTREG(wslua,concat), + WSLUA_CLASS_MTREG(UInt64,unm), + WSLUA_CLASS_MTREG(UInt64,add), + WSLUA_CLASS_MTREG(UInt64,sub), + WSLUA_CLASS_MTREG(UInt64,mul), + WSLUA_CLASS_MTREG(UInt64,div), + WSLUA_CLASS_MTREG(UInt64,mod), + WSLUA_CLASS_MTREG(UInt64,pow), + WSLUA_CLASS_MTREG(UInt64,eq), + WSLUA_CLASS_MTREG(UInt64,lt), + WSLUA_CLASS_MTREG(UInt64,le), + { NULL, NULL } +}; + +LUALIB_API int UInt64_register(lua_State* L) { + WSLUA_REGISTER_CLASS(UInt64); + 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: + */ diff --git a/epan/wslua/wslua_internals.c b/epan/wslua/wslua_internals.c new file mode 100644 index 00000000..186d6fa5 --- /dev/null +++ b/epan/wslua/wslua_internals.c @@ -0,0 +1,629 @@ +/* + * wslua_internals.c + * + * Wireshark's interface to the Lua Programming Language + * + * This file is for internal WSLUA functions - not ones exposed into Lua. + * + * (c) 2013, Hadriel Kaplan <hadrielk@yahoo.com> + * + * 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" +#include "wslua.h" +#include <stdio.h> + +/* Several implementation details (__getters, __setters, __methods) were exposed + * to Lua code. These are normally not used by dissectors, just for debugging + * (and the "wslua global" test). Enable by setting WSLUA_WITH_INTROSPECTION */ +#define WSLUA_WITH_INTROSPECTION + +#if LUA_VERSION_NUM == 501 +/* Compatibility with Lua 5.1, function was added in 5.2 */ +static +int lua_absindex(lua_State *L, int idx) { + return (idx > 0 || idx <= LUA_REGISTRYINDEX) + ? idx + : lua_gettop(L) + 1 + idx; +} +#endif + +WSLUA_API int wslua__concat(lua_State* L) { + /* Concatenate two objects to a string */ + if (!luaL_callmeta(L,1,"__tostring")) + lua_pushvalue(L,1); + if (!luaL_callmeta(L,2,"__tostring")) + lua_pushvalue(L,2); + + lua_concat(L,2); + + return 1; +} + +/* like lua_toboolean, except only coerces int, nil, and bool, and errors on other types. + note that normal lua_toboolean returns 1 for any Lua value different from false and + nil; otherwise it returns 0. So a string would give a 0, as would a number of 1. + This function errors if the arg is a string, and sets the boolean to 1 for any + number other than 0. Like toboolean, this returns FALSE if the arg was missing. */ +WSLUA_API gboolean wslua_toboolean(lua_State* L, int n) { + gboolean val = FALSE; + + if ( lua_isboolean(L,n) || lua_isnil(L,n) || lua_gettop(L) < n ) { + val = lua_toboolean(L,n); + } else if ( lua_type(L,n) == LUA_TNUMBER ) { + int num = (int)luaL_checkinteger(L,n); + val = num != 0 ? TRUE : FALSE; + } else { + luaL_argerror(L,n,"must be a boolean or number"); + } + + return val; +} + +/* like luaL_checkinteger, except for booleans - this does not coerce other types */ +WSLUA_API gboolean wslua_checkboolean(lua_State* L, int n) { + + if (!lua_isboolean(L,n) ) { + luaL_argerror(L,n,"must be a boolean"); + } + + return lua_toboolean(L,n);; +} + +WSLUA_API gboolean wslua_optbool(lua_State* L, int n, gboolean def) { + gboolean val = FALSE; + + if ( lua_isboolean(L,n) ) { + val = lua_toboolean(L,n); + } else if ( lua_isnil(L,n) || lua_gettop(L) < n ){ + val = def; + } else { + luaL_argerror(L,n,"must be a boolean"); + } + + return val; +} + +/* like lua_tointeger, except only coerces int, nil, and bool, and errors on other types. + note that normal lua_tointeger does not coerce nil or bool, but does coerce strings. */ +WSLUA_API lua_Integer wslua_tointeger(lua_State* L, int n) { + lua_Integer val = 0; + + if ( lua_type(L,n) == LUA_TNUMBER) { + val = lua_tointeger(L,n); + } else if ( lua_isboolean(L,n) ) { + val = (lua_Integer) (lua_toboolean(L,n)); + } else if ( lua_isnil(L,n) ) { + val = 0; + } else { + luaL_argerror(L,n,"must be a integer, boolean or nil"); + } + + return val; +} + +/* like luaL_optint, except converts/handles Lua booleans as well */ +WSLUA_API int wslua_optboolint(lua_State* L, int n, int def) { + int val = 0; + + if ( lua_isnumber(L,n) ) { + val = (int)lua_tointeger(L,n); + } else if ( lua_isboolean(L,n) ) { + val = lua_toboolean(L,n) ? 1 : 0; + } else if ( lua_isnil(L,n) || lua_gettop(L) < n ){ + val = def; + } else { + luaL_argerror(L,n,"must be a boolean or integer"); + } + + return val; +} + +/* like luaL_checklstring, except no coercion */ +WSLUA_API const char* wslua_checklstring_only(lua_State* L, int n, size_t *l) { + + if (lua_type(L,n) != LUA_TSTRING) { + luaL_argerror(L,n,"must be a Lua string"); + } + + return luaL_checklstring(L, n, l); +} + +/* like luaL_checkstring, except no coercion */ +WSLUA_API const char* wslua_checkstring_only(lua_State* L, int n) { + return wslua_checklstring_only(L, n, NULL); +} + +/* following is based on the luaL_setfuncs() from Lua 5.2, so we can use it in pre-5.2 */ +WSLUA_API void wslua_setfuncs(lua_State *L, const luaL_Reg *l, int nup) { + luaL_checkstack(L, nup, "too many upvalues"); + for (; l->name != NULL; l++) { /* fill the table with given functions */ + int i; + for (i = 0; i < nup; i++) /* copy upvalues to the top */ + lua_pushvalue(L, -nup); + lua_pushcclosure(L, l->func, nup); /* closure with those upvalues */ + lua_setfield(L, -(nup + 2), l->name); + } + lua_pop(L, nup); /* remove upvalues */ +} + +/** + * Identical to lua_getfield, but without triggering the __newindex metamethod. + * The resulting value is returned on the Lua stack. + */ +static void lua_rawgetfield(lua_State *L, int idx, const char *k) { + idx = lua_absindex(L, idx); + lua_pushstring(L, k); + lua_rawget(L, idx); +} + +/** + * Identical to lua_setfield, but without triggering the __newindex metamethod. + * The value to be set is taken from the Lua stack. + */ +static void lua_rawsetfield (lua_State *L, int idx, const char *k) { + idx = lua_absindex(L, idx); + lua_pushstring(L, k); + lua_insert(L, -2); + lua_rawset(L, idx); +} + +WSLUA_API void wslua_print_stack(char* s, lua_State* L) { + int i; + + for (i=1;i<=lua_gettop(L);i++) { + printf("%s-%i: %s\n",s,i,lua_typename (L,lua_type(L, i))); + } + printf("\n"); +} + +/* C-code function equivalent of the typeof() function we created in Lua. + * The Lua one is for Lua scripts to use, this one is for C-code to use. + */ +const gchar* wslua_typeof_unknown = "UNKNOWN"; +const gchar* wslua_typeof(lua_State *L, int idx) { + const gchar *classname = wslua_typeof_unknown; + /* we'll try getting the class name for error reporting*/ + if (luaL_getmetafield(L, idx, WSLUA_TYPEOF_FIELD)) { + classname = luaL_optstring(L, -1, wslua_typeof_unknown); + lua_pop(L,1); /* pop __typeof result */ + } + else if (lua_type(L,idx) == LUA_TTABLE) { + lua_rawgetfield(L, idx, WSLUA_TYPEOF_FIELD); + classname = luaL_optstring(L, -1, wslua_typeof_unknown); + lua_pop(L,1); /* pop __typeof result */ + } + return classname; +} + +/* this gets a Lua table of the given name, from the table at the given + * location idx. If it does not get a table, it pops whatever it got + * and returns false. + */ +gboolean wslua_get_table(lua_State *L, int idx, const gchar *name) { + gboolean result = TRUE; + lua_rawgetfield(L, idx, name); + if (!lua_istable(L,-1)) { + lua_pop(L,1); + result = FALSE; + } + return result; +} + +/* this gets a table field of the given name, from the table at the given + * location idx. If it does not get a field, it pops whatever it got + * and returns false. + */ +gboolean wslua_get_field(lua_State *L, int idx, const gchar *name) { + gboolean result = TRUE; + lua_rawgetfield(L, idx, name); + if (lua_isnil(L,-1)) { + lua_pop(L,1); + result = FALSE; + } + return result; +} + +/** + * The __index metamethod for classes. Expected upvalues: class name. + */ +static int wslua_classmeta_index(lua_State *L) { + const char *fieldname = luaL_checkstring(L, 2); + const char *classname = luaL_checkstring(L, lua_upvalueindex(1)); + + return luaL_error(L, "No such '%s' function/property for object type '%s'", fieldname, classname); +} + +/** + * The __index/__newindex metamethod for class instances. Expected upvalues: + * class name, getters/getters, __index/__newindex class instance metamethod, + * class methods (getters only). See wslua_register_classinstance_meta. + * + * It first tries to find an attribute getter/setter, then an instance method + * (getters only), then the __index/__newindex metamethod of the class instance + * metatable and finally it gives up with an error. + * + * Getters are invoked with the table as parameter. Setters are invoked with the + * table and the value as parameter. + */ +static int wslua_instancemeta_index_impl(lua_State *L, gboolean is_getter) +{ + const char *fieldname = luaL_checkstring(L, 2); + const int attr_idx = lua_upvalueindex(2); + const int fallback_idx = lua_upvalueindex(3); + const int methods_idx = lua_upvalueindex(4); + + /* Check for getter/setter */ + if (lua_istable(L, attr_idx)) { + lua_rawgetfield(L, attr_idx, fieldname); + if (lua_iscfunction(L, -1)) { + lua_CFunction cfunc = lua_tocfunction(L, -1); + lua_pop(L, 1); /* Remove cfunction from stack */ + lua_remove(L, 2); /* Remove key from stack */ + /* + * Note: This re-uses the current closure as optimization, exposing + * its upvalues via pseudo-indices. The alternative is to create a + * new C closure (via lua_call), but this is more expensive. + * Callees should not rely on the availability of the upvalues. + */ + return (*cfunc)(L); + } + } + + /* If this is a getter, and the getter has methods, try them. */ + if (is_getter && lua_istable(L, methods_idx)) { + lua_rawgetfield(L, methods_idx, fieldname); + if (!lua_isnil(L, -1)) { + /* Return method from methods table. */ + return 1; + } + lua_pop(L, 1); /* Remove nil from stack. */ + } + + /* Use function from the class instance metatable (if any). */ + if (lua_iscfunction(L, fallback_idx)) { + lua_CFunction cfunc = lua_tocfunction(L, fallback_idx); + /* Note, unlike getters/setters functions, the key must be preserved! */ + return (*cfunc)(L); + } + + const char *classname = luaL_checkstring(L, lua_upvalueindex(1)); + return luaL_error(L, "No such '%s' method/field for object type '%s'", fieldname, classname); +} + +static int wslua_instancemeta_index(lua_State *L) +{ + return wslua_instancemeta_index_impl(L, TRUE); +} + +static int wslua_instancemeta_newindex(lua_State *L) +{ + return wslua_instancemeta_index_impl(L, FALSE); +} + +/* Pushes a hex string of the binary data argument. */ +int wslua_bin2hex(lua_State* L, const guint8* data, const guint len, const gboolean lowercase, const gchar* sep) { + luaL_Buffer b; + guint i = 0; + static const char byte_to_str_upper[256][3] = { + "00","01","02","03","04","05","06","07","08","09","0A","0B","0C","0D","0E","0F", + "10","11","12","13","14","15","16","17","18","19","1A","1B","1C","1D","1E","1F", + "20","21","22","23","24","25","26","27","28","29","2A","2B","2C","2D","2E","2F", + "30","31","32","33","34","35","36","37","38","39","3A","3B","3C","3D","3E","3F", + "40","41","42","43","44","45","46","47","48","49","4A","4B","4C","4D","4E","4F", + "50","51","52","53","54","55","56","57","58","59","5A","5B","5C","5D","5E","5F", + "60","61","62","63","64","65","66","67","68","69","6A","6B","6C","6D","6E","6F", + "70","71","72","73","74","75","76","77","78","79","7A","7B","7C","7D","7E","7F", + "80","81","82","83","84","85","86","87","88","89","8A","8B","8C","8D","8E","8F", + "90","91","92","93","94","95","96","97","98","99","9A","9B","9C","9D","9E","9F", + "A0","A1","A2","A3","A4","A5","A6","A7","A8","A9","AA","AB","AC","AD","AE","AF", + "B0","B1","B2","B3","B4","B5","B6","B7","B8","B9","BA","BB","BC","BD","BE","BF", + "C0","C1","C2","C3","C4","C5","C6","C7","C8","C9","CA","CB","CC","CD","CE","CF", + "D0","D1","D2","D3","D4","D5","D6","D7","D8","D9","DA","DB","DC","DD","DE","DF", + "E0","E1","E2","E3","E4","E5","E6","E7","E8","E9","EA","EB","EC","ED","EE","EF", + "F0","F1","F2","F3","F4","F5","F6","F7","F8","F9","FA","FB","FC","FD","FE","FF" + }; + static const char byte_to_str_lower[256][3] = { + "00","01","02","03","04","05","06","07","08","09","0a","0b","0c","0d","0e","0f", + "10","11","12","13","14","15","16","17","18","19","1a","1b","1c","1d","1e","1f", + "20","21","22","23","24","25","26","27","28","29","2a","2b","2c","2d","2e","2f", + "30","31","32","33","34","35","36","37","38","39","3a","3b","3c","3d","3e","3f", + "40","41","42","43","44","45","46","47","48","49","4a","4b","4c","4d","4e","4f", + "50","51","52","53","54","55","56","57","58","59","5a","5b","5c","5d","5e","5f", + "60","61","62","63","64","65","66","67","68","69","6a","6b","6c","6d","6e","6f", + "70","71","72","73","74","75","76","77","78","79","7a","7b","7c","7d","7e","7f", + "80","81","82","83","84","85","86","87","88","89","8a","8b","8c","8d","8e","8f", + "90","91","92","93","94","95","96","97","98","99","9a","9b","9c","9d","9e","9f", + "a0","a1","a2","a3","a4","a5","a6","a7","a8","a9","aa","ab","ac","ad","ae","af", + "b0","b1","b2","b3","b4","b5","b6","b7","b8","b9","ba","bb","bc","bd","be","bf", + "c0","c1","c2","c3","c4","c5","c6","c7","c8","c9","ca","cb","cc","cd","ce","cf", + "d0","d1","d2","d3","d4","d5","d6","d7","d8","d9","da","db","dc","dd","de","df", + "e0","e1","e2","e3","e4","e5","e6","e7","e8","e9","ea","eb","ec","ed","ee","ef", + "f0","f1","f2","f3","f4","f5","f6","f7","f8","f9","fa","fb","fc","fd","fe","ff" + }; + const char (*byte_to_str)[3] = byte_to_str_upper; + const guint last = len - 1; + + if (lowercase) byte_to_str = byte_to_str_lower; + + luaL_buffinit(L, &b); + + for (i = 0; i < len; i++) { + luaL_addlstring(&b, &(*byte_to_str[data[i]]), 2); + if (sep && i < last) luaL_addstring(&b, sep); + } + + luaL_pushresult(&b); + + return 1; +} + +/* Pushes a binary string of the hex-ascii data argument. */ +int wslua_hex2bin(lua_State* L, const char* data, const guint len, const gchar* sep) { + luaL_Buffer b; + guint i = 0; + guint seplen = 0; + gint8 c, d; + + static const gint8 str_to_nibble[256] = { + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,-1,-1,-1,-1,-1,-1, + -1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 + }; + + if (sep) seplen = (guint) strlen(sep); + + luaL_buffinit(L, &b); + + for (i = 0; i < len;) { + c = str_to_nibble[(guchar)data[i]]; + if (c < 0) { + if (seplen && strncmp(&data[i], sep, seplen) == 0) { + i += seplen; + continue; + } else { + break; + } + } + d = str_to_nibble[(guchar)data[++i]]; + if (d < 0) break; + luaL_addchar(&b, (c * 16) + d); + i++; + } + + luaL_pushresult(&b); + + return 1; +} + +/** + * Creates a table of getters/setters and pushes it on the Lua stack. + * + * Additionally, a sanity check is performed to detect colliding getters/setters + * and method names. + */ +static void wslua_push_attributes(lua_State *L, const wslua_attribute_table *t, gboolean is_getter, int methods_idx) +{ + if (!t) { + /* No property accessors? Nothing to do. */ + //lua_pushnil(L); + lua_newtable(L); /* wslua_reg_attributes requires a table for the moment. */ + return; + } + + /* If there is a methods table, prepare for a collission check. */ + if (lua_istable(L, methods_idx)) { + methods_idx = lua_absindex(L, methods_idx); + } else { + methods_idx = 0; + } + + lua_newtable(L); + /* Fill the getter/setter table with given functions. */ + for (; t->fieldname != NULL; t++) { + lua_CFunction cfunc = is_getter ? t->getfunc : t->setfunc; + if (cfunc) { + /* if there's a previous methods table, make sure this attribute name doesn't collide */ + if (methods_idx) { + lua_rawgetfield(L, methods_idx, t->fieldname); + if (!lua_isnil(L, -1)) { + ws_error("'%s' attribute name already exists as method name for class\n", t->fieldname); + } + lua_pop(L,1); /* pop the nil */ + } + lua_pushcfunction(L, cfunc); + lua_rawsetfield(L, -2, t->fieldname); + } + } +} + +/** + * Registers the metatable for class instances. See the documentation of + * wslua_register_class for the exact metatable. + */ +void wslua_register_classinstance_meta(lua_State *L, const wslua_class *cls_def) +{ + /* Register metatable for use by class instances. STACK = { MT } */ + /* NOTE: The name can be changed as long as luaL_checkudata is also adapted */ + luaL_newmetatable(L, cls_def->name); + if (cls_def->instance_meta) { + wslua_setfuncs(L, cls_def->instance_meta, 0); + } + + /* Set the __typeof attribute to the class name (for use by "typeof" in Lua code). */ + lua_pushstring(L, cls_def->name); + lua_rawsetfield(L, -2, WSLUA_TYPEOF_FIELD); + + /* Create table to store method names. STACK = { MT, methods } */ + if (cls_def->instance_methods) { + lua_newtable(L); + wslua_setfuncs(L, cls_def->instance_methods, 0); + } else { + lua_pushnil(L); + } + + /* Prepare __index method on metatable. */ + lua_pushstring(L, cls_def->name); /* upval 1: class name */ + wslua_push_attributes(L, cls_def->attrs, TRUE, -2); /* upval 2: getters table */ +#ifdef WSLUA_WITH_INTROSPECTION + lua_pushvalue(L, -1); + lua_rawsetfield(L, -5, "__getters"); /* set (transition) property on mt, remove later! */ +#endif + lua_rawgetfield(L, -4, "__index"); /* upval 3: fallback __index method from metatable */ + lua_pushvalue(L, -4); /* upval 4: class methods table */ + lua_pushcclosure(L, wslua_instancemeta_index, 4); + lua_rawsetfield(L, -3, "__index"); + + /* Prepare __newindex method on metatable. */ + lua_pushstring(L, cls_def->name); /* upval 1: class name */ + wslua_push_attributes(L, cls_def->attrs, FALSE, -2); /* upval 2: setters table */ +#ifdef WSLUA_WITH_INTROSPECTION + lua_pushvalue(L, -1); + lua_rawsetfield(L, -5, "__setters"); /* set (transition) property on mt, remove later! */ +#endif + lua_rawgetfield(L, -4, "__newindex"); /* upval 3: fallback __newindex method from metatable */ + lua_pushcclosure(L, wslua_instancemeta_newindex, 3); + lua_rawsetfield(L, -3, "__newindex"); + + /* Pop metatable + methods table. STACK = { } */ + lua_pop(L, 2); +} + +/** + * Registers a new class for use in Lua with the specified properties. The + * metatable for the class instance is internally registered with the given + * name. + * + * This functions basically creates a class (type table) with this structure: + * + * Class = { class_methods } + * Class.__typeof = "Class" -- NOTE: Might be removed in future + * Class.__metatable = { class_meta } + * Class.__metatable.__typeof = "Class" -- NOTE: Might be removed in future + * Class.__metatable.__index = function_that_errors_out + * Class.__metatable.__newindex = function_that_errors_out + * + * It also registers another metatable for class instances (type userdata): + * + * mt = { instance_meta } + * mt.__typeof = "Class" + * -- will be passed upvalues (see wslua_instancemeta_index_impl). + * mt.__index = function_that_finds_right_property_or_method_getter + * mt.__newindex = function_thaon_that_finds_right_property_or_method_setter + * + * For backwards compatibility, introspection is still possible (this detail + * might be removed in the future though, do not rely on this!): + * + * Class.__metatable.__methods = Class + * Class.__metatable.__getters = { __typeof = "getter", getter_attrs } + * Class.__metatable.__setters = { __typeof = "setter", setter_attrs } + */ +void wslua_register_class(lua_State *L, const wslua_class *cls_def) +{ + /* Check for existing global variables/classes with the same name. */ + lua_getglobal(L, cls_def->name); + if (!lua_isnil (L, -1)) { + ws_error("Attempt to register class '%s' which already exists in global Lua table\n", cls_def->name); + } + lua_pop(L, 1); + + /* Create new table for class. STACK = { table } */ + lua_newtable(L); + if (cls_def->class_methods) { + wslua_setfuncs(L, cls_def->class_methods, 0); + } + +#ifdef WSLUA_WITH_INTROSPECTION + /* Set __typeof to the class name, used by wslua_typeof. Might be removed in + * the future as the type can already be determined from the metatable. */ + lua_pushstring(L, cls_def->name); + lua_rawsetfield(L, -2, WSLUA_TYPEOF_FIELD); +#endif + + /* Create new metatable for class. STACK = { table, CLASSMT } */ + lua_newtable(L); + if (cls_def->class_meta) { + /* Set metamethods on metatable for class. */ + wslua_setfuncs(L, cls_def->class_meta, 0); + } +#ifdef WSLUA_WITH_INTROSPECTION + /* Set __typeof to the class name. Might be removed in the future, a "class" + * is not of the type "(name of class)", instead it is of type "class". + * Instances of this class should be of type "(name of class)". */ + lua_pushstring(L, cls_def->name); + lua_rawsetfield(L, -2, WSLUA_TYPEOF_FIELD); +#endif + + /* For backwards compatibility, error out when a non-existing property is being accessed. */ + lua_pushstring(L, cls_def->name); + lua_pushcclosure(L, wslua_classmeta_index, 1); + lua_rawsetfield(L, -2, "__index"); + + /* Prevent properties from being set on classes. Previously this was always + * forbidden for classes with attributes (such as Listener), this extends + * the restriction to all classes. */ + lua_pushstring(L, cls_def->name); + lua_pushcclosure(L, wslua_classmeta_index, 1); + lua_rawsetfield(L, -2, "__newindex"); + + /* Set metatable on class. STACK = { table } */ + lua_setmetatable(L, -2); + + wslua_register_classinstance_meta(L, cls_def); + +#ifdef WSLUA_WITH_INTROSPECTION + /* XXX remove these? It looks like an internal implementation detail that is + * no longer needed but is added here to pass the wslua tests (API check) */ + lua_getmetatable(L, -1); /* Stack = { table, CLASSMT } */ + luaL_getmetatable(L, cls_def->name); /* Stack = { table, CLASSMT, MT } */ + + lua_rawgetfield(L, -1, "__getters"); /* __getters from instance MT */ + lua_pushstring(L, "getter"); + lua_rawsetfield(L, -2, WSLUA_TYPEOF_FIELD); + lua_rawsetfield(L, -3, "__getters"); /* Set property on class MT */ + + lua_rawgetfield(L, -1, "__setters"); /* setters from instance MT */ + lua_pushstring(L, "setter"); + lua_rawsetfield(L, -2, WSLUA_TYPEOF_FIELD); + lua_rawsetfield(L, -3, "__setters"); /* Set property on class MT */ + lua_pop(L, 1); /* Stack = { table, CLASSMT } */ + + lua_pushvalue(L, -2); + lua_rawsetfield(L, -2, "__methods"); /* CLASSMT.__methods = Class */ + lua_pop(L, 1); /* Stack = { table } */ +#endif + + /* Set the class methods table as global name. STACK = { } */ + lua_setglobal(L, cls_def->name); +} + +/* + * 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: + */ diff --git a/epan/wslua/wslua_listener.c b/epan/wslua/wslua_listener.c new file mode 100644 index 00000000..669a64f9 --- /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: + */ diff --git a/epan/wslua/wslua_nstime.c b/epan/wslua/wslua_nstime.c new file mode 100644 index 00000000..b3012e71 --- /dev/null +++ b/epan/wslua/wslua_nstime.c @@ -0,0 +1,236 @@ +/* + * wslua_nstime.c + * + * Wireshark's interface to the Lua Programming Language + * + * (c) 2006, Luis E. Garcia Ontanon <luis@ontanon.org> + * (c) 2008, Balint Reczey <balint.reczey@ericsson.com> + * (c) 2011, Stig Bjorlykke <stig@bjorlykke.org> + * (c) 2014, Hadriel Kaplan <hadrielk@yahoo.com> + * + * 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" + +#include "wslua.h" +#include <wsutil/nstime.h> + +/* 1s = 10^9 ns. */ +#define NS_PER_S 1000000000 + +/* WSLUA_CONTINUE_MODULE Pinfo */ + + +WSLUA_CLASS_DEFINE(NSTime,FAIL_ON_NULL("NSTime")); +/* NSTime represents a nstime_t. This is an object with seconds and nanoseconds. */ + +WSLUA_CONSTRUCTOR NSTime_new(lua_State *L) { + /* Creates a new NSTime object. */ +#define WSLUA_OPTARG_NSTime_new_SECONDS 1 /* Seconds. */ +#define WSLUA_OPTARG_NSTime_new_NSECONDS 2 /* Nano seconds. */ + lua_Integer secs = luaL_optinteger(L,WSLUA_OPTARG_NSTime_new_SECONDS,0); + lua_Integer nsecs = luaL_optinteger(L,WSLUA_OPTARG_NSTime_new_NSECONDS,0); + + NSTime nstime = g_new(nstime_t, 1); + if (!nstime) return 0; + nstime->secs = (time_t) secs; + nstime->nsecs = (int) nsecs; + + pushNSTime(L,nstime); + + WSLUA_RETURN(1); /* The new NSTime object. */ +} + +WSLUA_METAMETHOD NSTime__call(lua_State* L) { /* Creates a NSTime object. */ +#define WSLUA_OPTARG_NSTime__call_SECONDS 1 /* Seconds. */ +#define WSLUA_OPTARG_NSTime__call_NSECONDS 2 /* Nanoseconds. */ + lua_remove(L,1); /* remove the table */ + WSLUA_RETURN(NSTime_new(L)); /* The new NSTime object. */ +} + +WSLUA_METHOD NSTime_tonumber(lua_State* L) { + /* Returns a Lua number of the `NSTime` representing seconds from epoch + @since 2.4.0 + */ + NSTime nstime = checkNSTime(L,1); + lua_pushnumber(L, (lua_Number)nstime_to_sec(nstime)); + WSLUA_RETURN(1); /* The Lua number. */ +} + +WSLUA_METAMETHOD NSTime__tostring(lua_State* L) { + NSTime nstime = checkNSTime(L,1); + gchar *str; + long secs = (long)nstime->secs; + gint nsecs = nstime->nsecs; + gboolean negative_zero = FALSE; + + /* Time is defined as sec + nsec/10^9, both parts can be negative. + * Translate this into the more familiar sec.nsec notation instead. */ + if (secs > 0 && nsecs < 0) { + /* sign mismatch: (2, -3ns) -> 1.7 */ + nsecs += NS_PER_S; + secs--; + } else if (secs < 0 && nsecs > 0) { + /* sign mismatch: (-2, 3ns) -> -1.7 */ + nsecs = NS_PER_S - nsecs; + secs--; + } else if (nsecs < 0) { + /* Drop sign, the integer part already has it: (-2, -3ns) -> -2.3 */ + nsecs = -nsecs; + /* In case the integer part is zero, it does not has a sign, so remember + * that it must be explicitly added. */ + negative_zero = secs == 0; + } + + if (negative_zero) { + str = wmem_strdup_printf(NULL, "-0.%09d", nsecs); + } else { + str = wmem_strdup_printf(NULL, "%ld.%09d", secs, nsecs); + } + lua_pushstring(L, str); + wmem_free(NULL, str); + + WSLUA_RETURN(1); /* The string representing the nstime. */ +} +WSLUA_METAMETHOD NSTime__add(lua_State* L) { /* Calculates the sum of two NSTimes. */ + NSTime time1 = checkNSTime(L,1); + NSTime time2 = checkNSTime(L,2); + NSTime time3 = (NSTime)g_malloc (sizeof (nstime_t)); + + nstime_sum (time3, time1, time2); + pushNSTime (L, time3); + + return 1; +} + +WSLUA_METAMETHOD NSTime__sub(lua_State* L) { /* Calculates the diff of two NSTimes. */ + NSTime time1 = checkNSTime(L,1); + NSTime time2 = checkNSTime(L,2); + NSTime time3 = (NSTime)g_malloc (sizeof (nstime_t)); + + nstime_delta (time3, time1, time2); + pushNSTime (L, time3); + + return 1; +} + +WSLUA_METAMETHOD NSTime__unm(lua_State* L) { /* Calculates the negative NSTime. */ + NSTime time1 = checkNSTime(L,1); + NSTime time2 = (NSTime)g_malloc (sizeof (nstime_t)); + + nstime_set_zero (time2); + nstime_subtract (time2, time1); + pushNSTime (L, time2); + + return 1; +} + +WSLUA_METAMETHOD NSTime__eq(lua_State* L) { /* Compares two NSTimes. */ + NSTime time1 = checkNSTime(L,1); + NSTime time2 = checkNSTime(L,2); + gboolean result = FALSE; + + if (nstime_cmp(time1, time2) == 0) + result = TRUE; + + lua_pushboolean(L,result); + + return 1; +} + +WSLUA_METAMETHOD NSTime__le(lua_State* L) { /* Compares two NSTimes. */ + NSTime time1 = checkNSTime(L,1); + NSTime time2 = checkNSTime(L,2); + gboolean result = FALSE; + + if (nstime_cmp(time1, time2) <= 0) + result = TRUE; + + lua_pushboolean(L,result); + + return 1; +} + +WSLUA_METAMETHOD NSTime__lt(lua_State* L) { /* Compares two NSTimes. */ + NSTime time1 = checkNSTime(L,1); + NSTime time2 = checkNSTime(L,2); + gboolean result = FALSE; + + if (nstime_cmp(time1, time2) < 0) + result = TRUE; + + lua_pushboolean(L,result); + + return 1; +} + + +/* WSLUA_ATTRIBUTE NSTime_secs RW The NSTime seconds. */ +WSLUA_ATTRIBUTE_NUMBER_GETTER(NSTime,secs); +WSLUA_ATTRIBUTE_NUMBER_SETTER(NSTime,secs,time_t); + +/* WSLUA_ATTRIBUTE NSTime_nsecs RW The NSTime nano seconds. */ +WSLUA_ATTRIBUTE_NUMBER_GETTER(NSTime,nsecs); +WSLUA_ATTRIBUTE_NUMBER_SETTER(NSTime,nsecs,int); + +/* Gets registered as metamethod automatically by WSLUA_REGISTER_CLASS/META */ +static int NSTime__gc(lua_State* L) { + NSTime nstime = toNSTime(L,1); + + if (!nstime) return 0; + + g_free (nstime); + 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 NSTime_attributes[] = { + WSLUA_ATTRIBUTE_RWREG(NSTime,secs), + WSLUA_ATTRIBUTE_RWREG(NSTime,nsecs), + { NULL, NULL, NULL } +}; + +WSLUA_METHODS NSTime_methods[] = { + WSLUA_CLASS_FNREG(NSTime,new), + WSLUA_CLASS_FNREG(NSTime,tonumber), + { NULL, NULL } +}; + +WSLUA_META NSTime_meta[] = { + WSLUA_CLASS_MTREG(NSTime,tostring), + WSLUA_CLASS_MTREG(NSTime,add), + WSLUA_CLASS_MTREG(NSTime,sub), + WSLUA_CLASS_MTREG(NSTime,unm), + WSLUA_CLASS_MTREG(NSTime,eq), + WSLUA_CLASS_MTREG(NSTime,le), + WSLUA_CLASS_MTREG(NSTime,lt), + WSLUA_CLASS_MTREG(NSTime,call), + { NULL, NULL } +}; + +int NSTime_register(lua_State* L) { + WSLUA_REGISTER_CLASS_WITH_ATTRS(NSTime); + 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: + */ diff --git a/epan/wslua/wslua_pinfo.c b/epan/wslua/wslua_pinfo.c new file mode 100644 index 00000000..a735d601 --- /dev/null +++ b/epan/wslua/wslua_pinfo.c @@ -0,0 +1,495 @@ +/* + * wslua_pinfo.c + * + * Wireshark's interface to the Lua Programming Language + * + * (c) 2006, Luis E. Garcia Ontanon <luis@ontanon.org> + * (c) 2008, Balint Reczey <balint.reczey@ericsson.com> + * (c) 2011, Stig Bjorlykke <stig@bjorlykke.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" + +#include "wslua_pinfo_common.h" + +#include <epan/wmem_scopes.h> +#include <epan/conversation.h> +#include <string.h> + + +/* WSLUA_MODULE Pinfo Obtaining Packet Information */ + + +/* + * Track pointers to wireshark's structures. + * see comment on wslua_tvb.c + */ + +static GPtrArray* outstanding_Pinfo = NULL; +static GPtrArray* outstanding_PrivateTable = NULL; + +CLEAR_OUTSTANDING(Pinfo,expired, TRUE) +CLEAR_OUTSTANDING(PrivateTable,expired, TRUE) + +Pinfo* push_Pinfo(lua_State* L, packet_info* ws_pinfo) { + Pinfo pinfo = NULL; + if (ws_pinfo) { + pinfo = (Pinfo)g_malloc(sizeof(struct _wslua_pinfo)); + pinfo->ws_pinfo = ws_pinfo; + pinfo->expired = FALSE; + g_ptr_array_add(outstanding_Pinfo,pinfo); + } + return pushPinfo(L,pinfo); +} + +#define PUSH_PRIVATE_TABLE(L,c) {g_ptr_array_add(outstanding_PrivateTable,c);pushPrivateTable(L,c);} + + +WSLUA_CLASS_DEFINE(PrivateTable,FAIL_ON_NULL_OR_EXPIRED("PrivateTable")); +/* PrivateTable represents the pinfo->private_table. */ + +WSLUA_METAMETHOD PrivateTable__tostring(lua_State* L) { + /* Gets debugging type information about the private table. */ + PrivateTable priv = toPrivateTable(L,1); + GString *key_string; + GList *keys, *key; + + if (!priv) return 0; + + key_string = g_string_new (""); + keys = g_hash_table_get_keys (priv->table); + key = g_list_first (keys); + while (key) { + key_string = g_string_append (key_string, (const gchar *)key->data); + key = g_list_next (key); + if (key) { + key_string = g_string_append_c (key_string, ','); + } + } + + lua_pushstring(L,key_string->str); + + g_string_free (key_string, TRUE); + g_list_free (keys); + + WSLUA_RETURN(1); /* A string with all keys in the table, mostly for debugging. */ +} + +static int PrivateTable__index(lua_State* L) { + /* Gets the text of a specific entry. */ + PrivateTable priv = checkPrivateTable(L,1); + const gchar* name = luaL_checkstring(L,2); + const gchar* string; + + string = (const gchar *)(g_hash_table_lookup (priv->table, name)); + + if (string) { + lua_pushstring(L, string); + } else { + lua_pushnil(L); + } + + return 1; +} + +static int PrivateTable__newindex(lua_State* L) { + /* Sets the text of a specific entry. */ + PrivateTable priv = checkPrivateTable(L,1); + const gchar* name = luaL_checkstring(L,2); + const gchar* string = NULL; + + if (lua_isstring(L,3)) { + /* This also catches numbers, which is converted to string */ + string = luaL_checkstring(L,3); + } else if (lua_isboolean(L,3)) { + /* We support boolean by setting a empty string if true and NULL if false */ + string = lua_toboolean(L,3) ? "" : NULL; + } else if (!lua_isnil(L,3)) { + luaL_error(L,"unsupported type: %s", lua_typename(L,3)); + return 0; + } + + if (string) { + g_hash_table_replace (priv->table, (gpointer) g_strdup(name), (gpointer) g_strdup(string)); + } else { + g_hash_table_remove (priv->table, (gconstpointer) name); + } + + return 1; +} + +/* Gets registered as metamethod automatically by WSLUA_REGISTER_CLASS/META */ +static int PrivateTable__gc(lua_State* L) { + PrivateTable priv = toPrivateTable(L,1); + + if (!priv) return 0; + + if (!priv->expired) { + priv->expired = TRUE; + } else { + if (priv->is_allocated) { + g_hash_table_destroy (priv->table); + } + g_free(priv); + } + + return 0; +} + +WSLUA_META PrivateTable_meta[] = { + WSLUA_CLASS_MTREG(PrivateTable,index), + WSLUA_CLASS_MTREG(PrivateTable,newindex), + WSLUA_CLASS_MTREG(PrivateTable,tostring), + { NULL, NULL } +}; + +int PrivateTable_register(lua_State* L) { + WSLUA_REGISTER_META(PrivateTable); + return 0; +} + + +WSLUA_CLASS_DEFINE(Pinfo,FAIL_ON_NULL_OR_EXPIRED("Pinfo")); +/* Packet information. */ + +static int Pinfo__tostring(lua_State *L) { lua_pushstring(L,"a Pinfo"); return 1; } + +#define PINFO_ADDRESS_GETTER(name) \ + WSLUA_ATTRIBUTE_GET(Pinfo,name, { \ + Address addr = g_new(address,1); \ + copy_address(addr, &(obj->ws_pinfo->name)); \ + pushAddress(L,addr); \ + }) + +/* + * Addresses within the Pinfo structure are only valid for a single packet, so + * allocate memory from the pinfo pool. + */ +#define PINFO_ADDRESS_SETTER(name) \ + WSLUA_ATTRIBUTE_SET(Pinfo,name, { \ + const address* from = checkAddress(L,-1); \ + copy_address_wmem(obj->ws_pinfo->pool, &(obj->ws_pinfo->name), from); \ + }) + +#define PINFO_NAMED_BOOLEAN_GETTER(name,member) \ + WSLUA_ATTRIBUTE_NAMED_BOOLEAN_GETTER(Pinfo,name,ws_pinfo->member) + +#define PINFO_NAMED_BOOLEAN_SETTER(name,member) \ + WSLUA_ATTRIBUTE_NAMED_BOOLEAN_SETTER(Pinfo,name,ws_pinfo->member) + +#define PINFO_NUMBER_GETTER(name) \ + WSLUA_ATTRIBUTE_NAMED_NUMBER_GETTER(Pinfo,name,ws_pinfo->name) + +#define PINFO_NAMED_NUMBER_GETTER(name,member) \ + WSLUA_ATTRIBUTE_NAMED_NUMBER_GETTER(Pinfo,name,ws_pinfo->member) + +#define PINFO_NUMBER_SETTER(name,cast) \ + WSLUA_ATTRIBUTE_NAMED_NUMBER_SETTER(Pinfo,name,ws_pinfo->name,cast) + +#define PINFO_NAMED_NUMBER_SETTER(name,member,cast) \ + WSLUA_ATTRIBUTE_NAMED_NUMBER_SETTER(Pinfo,name,ws_pinfo->member,cast) + +static double +lua_nstime_to_sec(const nstime_t *nstime) +{ + return (((double)nstime->secs) + (((double)nstime->nsecs) / 1000000000.0)); +} + +static double +lua_delta_nstime_to_sec(const Pinfo pinfo, const frame_data *fd, guint32 prev_num) +{ + nstime_t del; + + frame_delta_abs_time(pinfo->ws_pinfo->epan, fd, prev_num, &del); + return lua_nstime_to_sec(&del); +} + + +/* WSLUA_ATTRIBUTE Pinfo_visited RO Whether this packet has been already visited. */ +PINFO_NAMED_BOOLEAN_GETTER(visited,fd->visited); + +/* WSLUA_ATTRIBUTE Pinfo_number RO The number of this packet in the current file. */ +PINFO_NAMED_NUMBER_GETTER(number,num); + +/* WSLUA_ATTRIBUTE Pinfo_len RO The length of the frame. */ +PINFO_NAMED_NUMBER_GETTER(len,fd->pkt_len); + +/* WSLUA_ATTRIBUTE Pinfo_caplen RO The captured length of the frame. */ +PINFO_NAMED_NUMBER_GETTER(caplen,fd->cap_len); + +/* WSLUA_ATTRIBUTE Pinfo_abs_ts RO When the packet was captured. */ +WSLUA_ATTRIBUTE_BLOCK_NUMBER_GETTER(Pinfo,abs_ts,lua_nstime_to_sec(&obj->ws_pinfo->abs_ts)); + +/* WSLUA_ATTRIBUTE Pinfo_rel_ts RO Number of seconds passed since beginning of capture. */ +WSLUA_ATTRIBUTE_BLOCK_NUMBER_GETTER(Pinfo,rel_ts,lua_nstime_to_sec(&obj->ws_pinfo->rel_ts)); + +/* WSLUA_ATTRIBUTE Pinfo_delta_ts RO Number of seconds passed since the last captured packet. */ +WSLUA_ATTRIBUTE_BLOCK_NUMBER_GETTER(Pinfo,delta_ts,lua_delta_nstime_to_sec(obj, obj->ws_pinfo->fd, obj->ws_pinfo->num - 1)); + +/* WSLUA_ATTRIBUTE Pinfo_delta_dis_ts RO Number of seconds passed since the last displayed packet. */ +WSLUA_ATTRIBUTE_BLOCK_NUMBER_GETTER(Pinfo,delta_dis_ts,lua_delta_nstime_to_sec(obj, obj->ws_pinfo->fd, obj->ws_pinfo->fd->prev_dis_num)); + +/* WSLUA_ATTRIBUTE Pinfo_curr_proto RO Which Protocol are we dissecting. */ +WSLUA_ATTRIBUTE_NAMED_STRING_GETTER(Pinfo,curr_proto,ws_pinfo->current_proto); + +/* WSLUA_ATTRIBUTE Pinfo_can_desegment RW Set if this segment could be desegmented. */ +PINFO_NUMBER_GETTER(can_desegment); +PINFO_NUMBER_SETTER(can_desegment,guint16); + +/* WSLUA_ATTRIBUTE Pinfo_desegment_len RW Estimated number of additional bytes required for completing the PDU. */ +PINFO_NUMBER_GETTER(desegment_len); +PINFO_NUMBER_SETTER(desegment_len,guint32); + +/* WSLUA_ATTRIBUTE Pinfo_desegment_offset RW Offset in the tvbuff at which the dissector will continue processing when next called. */ +PINFO_NUMBER_GETTER(desegment_offset); +PINFO_NUMBER_SETTER(desegment_offset,int); + +/* WSLUA_ATTRIBUTE Pinfo_fragmented RO If the protocol is only a fragment. */ +PINFO_NAMED_BOOLEAN_GETTER(fragmented,fragmented); + +/* WSLUA_ATTRIBUTE Pinfo_in_error_pkt RW If we're inside an error packet. */ +PINFO_NAMED_BOOLEAN_GETTER(in_error_pkt,flags.in_error_pkt); +PINFO_NAMED_BOOLEAN_SETTER(in_error_pkt,flags.in_error_pkt); + +/* WSLUA_ATTRIBUTE Pinfo_match_uint RO Matched uint for calling subdissector from table. */ +PINFO_NUMBER_GETTER(match_uint); + +/* WSLUA_ATTRIBUTE Pinfo_match_string RO Matched string for calling subdissector from table. */ +WSLUA_ATTRIBUTE_NAMED_STRING_GETTER(Pinfo,match_string,ws_pinfo->match_string); + +/* WSLUA_ATTRIBUTE Pinfo_port_type RW Type of Port of .src_port and .dst_port. */ +PINFO_NAMED_NUMBER_GETTER(port_type,ptype); + +/* WSLUA_ATTRIBUTE Pinfo_src_port RW Source Port of this Packet. */ +PINFO_NAMED_NUMBER_GETTER(src_port,srcport); +PINFO_NAMED_NUMBER_SETTER(src_port,srcport,guint32); + +/* WSLUA_ATTRIBUTE Pinfo_dst_port RW Destination Port of this Packet. */ +PINFO_NAMED_NUMBER_GETTER(dst_port,destport); +PINFO_NAMED_NUMBER_SETTER(dst_port,destport,guint32); + +/* WSLUA_ATTRIBUTE Pinfo_dl_src RW Data Link Source Address of this Packet. */ +PINFO_ADDRESS_GETTER(dl_src); +PINFO_ADDRESS_SETTER(dl_src); + +/* WSLUA_ATTRIBUTE Pinfo_dl_dst RW Data Link Destination Address of this Packet. */ +PINFO_ADDRESS_GETTER(dl_dst); +PINFO_ADDRESS_SETTER(dl_dst); + +/* WSLUA_ATTRIBUTE Pinfo_net_src RW Network Layer Source Address of this Packet. */ +PINFO_ADDRESS_GETTER(net_src); +PINFO_ADDRESS_SETTER(net_src); + +/* WSLUA_ATTRIBUTE Pinfo_net_dst RW Network Layer Destination Address of this Packet. */ +PINFO_ADDRESS_GETTER(net_dst); +PINFO_ADDRESS_SETTER(net_dst); + +/* WSLUA_ATTRIBUTE Pinfo_src RW Source Address of this Packet. */ +PINFO_ADDRESS_GETTER(src); +PINFO_ADDRESS_SETTER(src); + +/* WSLUA_ATTRIBUTE Pinfo_dst RW Destination Address of this Packet. */ +PINFO_ADDRESS_GETTER(dst); +PINFO_ADDRESS_SETTER(dst); + +/* WSLUA_ATTRIBUTE Pinfo_p2p_dir RW Direction of this Packet. (incoming / outgoing) */ +PINFO_NUMBER_GETTER(p2p_dir); +PINFO_NUMBER_SETTER(p2p_dir,int); + +/* WSLUA_ATTRIBUTE Pinfo_match RO Port/Data we are matching. */ +static int Pinfo_get_match(lua_State *L) { + Pinfo pinfo = checkPinfo(L,1); + + if (pinfo->ws_pinfo->match_string) { + lua_pushstring(L,pinfo->ws_pinfo->match_string); + } else { + lua_pushnumber(L,(lua_Number)(pinfo->ws_pinfo->match_uint)); + } + + return 1; +} + +/* WSLUA_ATTRIBUTE Pinfo_columns RO Access to the packet list columns. */ +/* WSLUA_ATTRIBUTE Pinfo_cols RO Access to the packet list columns (equivalent to pinfo.columns). */ +static int Pinfo_get_columns(lua_State *L) { + Columns cols = NULL; + Pinfo pinfo = checkPinfo(L,1); + const gchar* colname = luaL_optstring(L,2,NULL); + + cols = (Columns)g_malloc(sizeof(struct _wslua_cols)); + cols->cinfo = pinfo->ws_pinfo->cinfo; + cols->expired = FALSE; + + if (!colname) { + Push_Columns(L,cols); + } else { + lua_settop(L,0); + Push_Columns(L,cols); + lua_pushstring(L,colname); + return get_Columns_index(L); + } + return 1; +} + +/* WSLUA_ATTRIBUTE Pinfo_private RO Access to the private table entries. */ +static int Pinfo_get_private(lua_State *L) { + PrivateTable priv = NULL; + Pinfo pinfo = checkPinfo(L,1); + const gchar* privname = luaL_optstring(L,2,NULL); + gboolean is_allocated = FALSE; + + if (!pinfo->ws_pinfo->private_table) { + pinfo->ws_pinfo->private_table = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); + is_allocated = TRUE; + } + + priv = (PrivateTable)g_malloc(sizeof(struct _wslua_private_table)); + priv->table = pinfo->ws_pinfo->private_table; + priv->is_allocated = is_allocated; + priv->expired = FALSE; + + if (!privname) { + PUSH_PRIVATE_TABLE(L,priv); + } else { + lua_settop(L,0); + PUSH_PRIVATE_TABLE(L,priv); + lua_pushstring(L,privname); + return PrivateTable__index(L); + } + return 1; +} + +/* WSLUA_ATTRIBUTE Pinfo_hi RW Higher Address of this Packet. */ +static int Pinfo_get_hi(lua_State *L) { + Pinfo pinfo = checkPinfo(L,1); + Address addr; + + addr = (Address)g_malloc(sizeof(address)); + if (cmp_address(&(pinfo->ws_pinfo->src), &(pinfo->ws_pinfo->dst) ) >= 0) { + copy_address(addr, &(pinfo->ws_pinfo->src)); + } else { + copy_address(addr, &(pinfo->ws_pinfo->dst)); + } + + pushAddress(L,addr); + return 1; +} + +/* WSLUA_ATTRIBUTE Pinfo_lo RO Lower Address of this Packet. */ +static int Pinfo_get_lo(lua_State *L) { + Pinfo pinfo = checkPinfo(L,1); + Address addr; + + addr = (Address)g_malloc(sizeof(address)); + if (cmp_address(&(pinfo->ws_pinfo->src), &(pinfo->ws_pinfo->dst) ) < 0) { + copy_address(addr, &(pinfo->ws_pinfo->src)); + } else { + copy_address(addr, &(pinfo->ws_pinfo->dst)); + } + + pushAddress(L,addr); + return 1; +} + +/* WSLUA_ATTRIBUTE Pinfo_conversation WO Sets the packet conversation to the given Proto object. */ +static int Pinfo_set_conversation(lua_State *L) { + Pinfo pinfo = checkPinfo(L,1); + Proto proto = checkProto(L,2); + conversation_t *conversation; + + if (!proto->handle) { + luaL_error(L,"Proto %s has no registered dissector", proto->name? proto->name:"<UNKNOWN>"); + return 0; + } + + conversation = find_or_create_conversation(pinfo->ws_pinfo); + conversation_set_dissector(conversation,proto->handle); + + return 0; +} + +/* Gets registered as metamethod automatically by WSLUA_REGISTER_CLASS/META */ +static int Pinfo__gc(lua_State* L) { + Pinfo pinfo = toPinfo(L,1); + + if (!pinfo) return 0; + + if (!pinfo->expired) + pinfo->expired = TRUE; + else + g_free(pinfo); + + 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 Pinfo_attributes[] = { + WSLUA_ATTRIBUTE_ROREG(Pinfo,number), + WSLUA_ATTRIBUTE_ROREG(Pinfo,len), + WSLUA_ATTRIBUTE_ROREG(Pinfo,caplen), + WSLUA_ATTRIBUTE_ROREG(Pinfo,abs_ts), + WSLUA_ATTRIBUTE_ROREG(Pinfo,rel_ts), + WSLUA_ATTRIBUTE_ROREG(Pinfo,delta_ts), + WSLUA_ATTRIBUTE_ROREG(Pinfo,delta_dis_ts), + WSLUA_ATTRIBUTE_ROREG(Pinfo,visited), + WSLUA_ATTRIBUTE_RWREG(Pinfo,src), + WSLUA_ATTRIBUTE_RWREG(Pinfo,dst), + WSLUA_ATTRIBUTE_ROREG(Pinfo,lo), + WSLUA_ATTRIBUTE_ROREG(Pinfo,hi), + WSLUA_ATTRIBUTE_RWREG(Pinfo,dl_src), + WSLUA_ATTRIBUTE_RWREG(Pinfo,dl_dst), + WSLUA_ATTRIBUTE_RWREG(Pinfo,net_src), + WSLUA_ATTRIBUTE_RWREG(Pinfo,net_dst), + WSLUA_ATTRIBUTE_ROREG(Pinfo,port_type), + WSLUA_ATTRIBUTE_RWREG(Pinfo,src_port), + WSLUA_ATTRIBUTE_RWREG(Pinfo,dst_port), + WSLUA_ATTRIBUTE_ROREG(Pinfo,match), + WSLUA_ATTRIBUTE_ROREG(Pinfo,curr_proto), + WSLUA_ATTRIBUTE_ROREG(Pinfo,columns), + { "cols", Pinfo_get_columns, NULL }, + WSLUA_ATTRIBUTE_RWREG(Pinfo,can_desegment), + WSLUA_ATTRIBUTE_RWREG(Pinfo,desegment_len), + WSLUA_ATTRIBUTE_RWREG(Pinfo,desegment_offset), + WSLUA_ATTRIBUTE_ROREG(Pinfo,private), + WSLUA_ATTRIBUTE_ROREG(Pinfo,fragmented), + WSLUA_ATTRIBUTE_RWREG(Pinfo,in_error_pkt), + WSLUA_ATTRIBUTE_ROREG(Pinfo,match_uint), + WSLUA_ATTRIBUTE_ROREG(Pinfo,match_string), + WSLUA_ATTRIBUTE_WOREG(Pinfo,conversation), + WSLUA_ATTRIBUTE_RWREG(Pinfo,p2p_dir), + { NULL, NULL, NULL } +}; + +WSLUA_META Pinfo_meta[] = { + WSLUA_CLASS_MTREG(Pinfo,tostring), + { NULL, NULL } +}; + +int Pinfo_register(lua_State* L) { + WSLUA_REGISTER_META_WITH_ATTRS(Pinfo); + outstanding_Pinfo = g_ptr_array_new(); + outstanding_PrivateTable = g_ptr_array_new(); + 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: + */ diff --git a/epan/wslua/wslua_pinfo_common.h b/epan/wslua/wslua_pinfo_common.h new file mode 100644 index 00000000..3af3dfce --- /dev/null +++ b/epan/wslua/wslua_pinfo_common.h @@ -0,0 +1,21 @@ +/** @file + * + * Wireshark's interface to the Lua Programming Language + * + * (c) 2006, Luis E. Garcia Ontanon <luis@ontanon.org> + * (c) 2008, Balint Reczey <balint.reczey@ericsson.com> + * (c) 2011, Stig Bjorlykke <stig@bjorlykke.org> + * (c) 2014, Hadriel Kaplan <hadrielk@yahoo.com> + * + * Wireshark - Network traffic analyzer + * By Gerald Combs <gerald@wireshark.org> + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + + +#include "wslua.h" + +void Push_Columns(lua_State *L, Columns c); +int get_Columns_index(lua_State *L); diff --git a/epan/wslua/wslua_pref.c b/epan/wslua/wslua_pref.c new file mode 100644 index 00000000..234170d3 --- /dev/null +++ b/epan/wslua/wslua_pref.c @@ -0,0 +1,567 @@ +/* + * wslua_pref.c + * + * Wireshark's interface to the Lua Programming Language + * + * (c) 2006, Luis E. Garcia Ontanon <luis@ontanon.org> + * (c) 2008, Balint Reczey <balint.reczey@ericsson.com> + * (c) 2011, Stig Bjorlykke <stig@bjorlykke.org> + * (c) 2014, Hadriel Kaplan <hadrielk@yahoo.com> + * + * 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" + +#include "wslua.h" + +/* WSLUA_CONTINUE_MODULE Proto */ + + +WSLUA_CLASS_DEFINE(Pref,NOP); /* A preference of a <<lua_class_Proto,`Proto`>>. */ + +static range_t* get_range(lua_State *L, int idx_r, int idx_m); + +static enum_val_t* get_enum(lua_State *L, int idx) +{ + double seq; + const gchar *str1, *str2; + enum_val_t *ret, last = {NULL, NULL, -1}; + GArray* es = g_array_new(TRUE,TRUE,sizeof(enum_val_t)); + + luaL_checktype(L, idx, LUA_TTABLE); + lua_pushnil(L); /* first key */ + + while (lua_next(L, idx)) { + enum_val_t e = {NULL, NULL, -1}; + + luaL_checktype(L, -1, LUA_TTABLE); + lua_pushnil(L); + lua_next(L, -2); + if (! lua_isstring(L,-1)) { + luaL_argerror(L,idx,"First value of an enum table must be string"); + g_array_free(es,TRUE); + return NULL; + } + str1 = lua_tostring(L, -1); + + lua_pop(L, 1); + lua_next(L, -2); + if (! lua_isstring(L,-1)) { + luaL_argerror(L,idx,"Second value of an enum table must be string"); + g_array_free(es,TRUE); + return NULL; + } + str2 = lua_tostring(L, -1); + + lua_pop(L, 1); + lua_next(L, -2); + if (! lua_isnumber(L,-1)) { + luaL_argerror(L,idx,"Third value of an enum table must be an integer"); + g_array_free(es,TRUE); + return NULL; + } + seq = lua_tonumber(L, -1); + + e.name = g_strdup(str1); + e.description = g_strdup(str2); + e.value = (guint32)seq; + + g_array_append_val(es,e); + + lua_pop(L, 3); /* removes 'value'; keeps 'key' for next iteration */ + } + + g_array_append_val(es,last); + + ret = (enum_val_t*)(void*)g_array_free(es, FALSE); + + return ret; +} + +static int new_pref(lua_State* L, pref_type_t type) { + const gchar* label = luaL_optstring(L,1,NULL); + const gchar* descr = luaL_optstring(L,3,""); + + Pref pref = g_new0(wslua_pref_t, 1); + pref->label = g_strdup(label); + pref->desc = g_strdup(descr); + pref->type = type; + pref->ref = LUA_NOREF; + + switch(type) { + case PREF_BOOL: { + gboolean def = wslua_toboolean(L,2); + pref->value.b = def; + break; + } + case PREF_UINT: { + guint32 def = wslua_optgint32(L,2,0); + pref->value.u = def; + break; + } + case PREF_STRING: { + gchar* def = g_strdup(luaL_optstring(L,2,"")); + /* + * prefs_register_string_preference() assumes that the + * variable for the preference points to a static + * string that is the initial (default) value of the + * preference. It makes a g_strdup()ed copy of that + * string, and assigns a pointer to that string to + * the variable. + * + * Our default string is *not* a static string, it's + * a g_strdup()ed copy of a string from Lua, so it would + * be leaked. + * + * We save it in info.default_s, as well as setting the + * initial value of the preference from it, so that we + * can free it after prefs_register_string_preference() + * returns. + * + * (Would that we were programming in a language where + * the details of memory management were handled by the + * compiler and language support....) + */ + pref->value.s = def; + pref->info.default_s = def; + break; + } + case PREF_ENUM: { + guint32 def = wslua_optgint32(L,2,0); + enum_val_t *enum_val = get_enum(L,4); + gboolean radio = wslua_toboolean(L,5); + pref->value.e = def; + pref->info.enum_info.enumvals = enum_val; + pref->info.enum_info.radio_buttons = radio; + break; + } + case PREF_RANGE: { + range_t *range = get_range(L,2,4); + guint32 max = wslua_optgint32(L,4,0); + pref->value.r = range; + pref->info.max_value = max; + break; + } + case PREF_STATIC_TEXT: { + /* This is just a static text. */ + break; + } + default: + ws_assert_not_reached(); + break; + + } + + pushPref(L,pref); + return 1; +} + +WSLUA_CONSTRUCTOR Pref_bool(lua_State* L) { + /* + Creates a boolean preference to be added to a <<lua_class_attrib_proto_prefs,`Proto.prefs`>> Lua table. + + ===== Example + + [source,lua] + ---- + -- create a Boolean preference named "bar" for Foo Protocol + -- (assuming Foo doesn't already have a preference named "bar") + proto_foo.prefs.bar = Pref.bool( "Bar", true, "Baz and all the rest" ) + ---- + */ +#define WSLUA_ARG_Pref_bool_LABEL 1 /* The Label (text in the right side of the + preference input) for this preference. */ +#define WSLUA_ARG_Pref_bool_DEFAULT 2 /* The default value for this preference. */ +#define WSLUA_ARG_Pref_bool_DESCR 3 /* A description of this preference. */ + return new_pref(L,PREF_BOOL); +} + +WSLUA_CONSTRUCTOR Pref_uint(lua_State* L) { + /* Creates an (unsigned) integer preference to be added to a <<lua_class_attrib_proto_prefs,`Proto.prefs`>> Lua table. */ +#define WSLUA_ARG_Pref_uint_LABEL 1 /* The Label (text in the right side of the + preference input) for this preference. */ +#define WSLUA_ARG_Pref_uint_DEFAULT 2 /* The default value for this preference. */ +#define WSLUA_ARG_Pref_uint_DESCR 3 /* A description of what this preference is. */ + return new_pref(L,PREF_UINT); +} + +WSLUA_CONSTRUCTOR Pref_string(lua_State* L) { + /* Creates a string preference to be added to a <<lua_class_attrib_proto_prefs,`Proto.prefs`>> Lua table. */ +#define WSLUA_ARG_Pref_string_LABEL 1 /* The Label (text in the right side of the + preference input) for this preference. */ +#define WSLUA_ARG_Pref_string_DEFAULT 2 /* The default value for this preference. */ +#define WSLUA_ARG_Pref_string_DESCR 3 /* A description of what this preference is. */ + return new_pref(L,PREF_STRING); +} + +WSLUA_CONSTRUCTOR Pref_enum(lua_State* L) { + /* + Creates an enum preference to be added to a <<lua_class_attrib_proto_prefs,`Proto.prefs`>> Lua table. + + ===== Example: + + [source,lua] + ---- + local OUTPUT_OFF = 0 + local OUTPUT_DEBUG = 1 + local OUTPUT_INFO = 2 + local OUTPUT_WARN = 3 + local OUTPUT_ERROR = 4 + + local output_tab = { + { 1, "Off" , OUTPUT_OFF }, + { 2, "Debug" , OUTPUT_DEBUG }, + { 3, "Information" , OUTPUT_INFO }, + { 4, "Warning" , OUTPUT_WARN }, + { 5, "Error" , OUTPUT_ERROR }, + } + + -- Create enum preference that shows as Combo Box under + -- Foo Protocol's preferences + proto_foo.prefs.outputlevel = Pref.enum( + "Output Level", -- label + OUTPUT_INFO, -- default value + "Verbosity of log output", -- description + output_tab, -- enum table + false -- show as combo box + ) + + -- Then, we can query the value of the selected preference. + -- This line prints "Output Level: 3" assuming the selected + -- output level is _INFO. + debug( "Output Level: " .. proto_foo.prefs.outputlevel ) + ---- + */ +#define WSLUA_ARG_Pref_enum_LABEL 1 /* The Label (text in the right side of the + preference input) for this preference. */ +#define WSLUA_ARG_Pref_enum_DEFAULT 2 /* The default value for this preference. */ +#define WSLUA_ARG_Pref_enum_DESCR 3 /* A description of what this preference is. */ +#define WSLUA_ARG_Pref_enum_ENUM 4 /* An enum Lua table. */ +#define WSLUA_ARG_Pref_enum_RADIO 5 /* Radio button (true) or Combobox (false). */ + return new_pref(L,PREF_ENUM); +} + +WSLUA_CONSTRUCTOR Pref_range(lua_State* L) { + /* Creates a range (numeric text entry) preference to be added to a <<lua_class_attrib_proto_prefs,`Proto.prefs`>> Lua table. */ +#define WSLUA_ARG_Pref_range_LABEL 1 /* The Label (text in the right side of the preference + input) for this preference. */ +#define WSLUA_ARG_Pref_range_DEFAULT 2 /* The default value for this preference, e.g., "53", + "10-30", or "10-30,53,55,100-120". */ +#define WSLUA_ARG_Pref_range_DESCR 3 /* A description of what this preference is. */ +#define WSLUA_ARG_Pref_range_MAX 4 /* The maximum value. */ + return new_pref(L,PREF_RANGE); +} + +WSLUA_CONSTRUCTOR Pref_statictext(lua_State* L) { + /* Creates a static text string to be added to a <<lua_class_attrib_proto_prefs,`Proto.prefs`>> Lua table. */ +#define WSLUA_ARG_Pref_statictext_LABEL 1 /* The static text. */ +#define WSLUA_ARG_Pref_statictext_DESCR 2 /* The static text description. */ + return new_pref(L,PREF_STATIC_TEXT); +} + +static range_t* get_range(lua_State *L, int idx_r, int idx_m) +{ + static range_t *ret = NULL; + const gchar *pattern = luaL_checkstring(L, idx_r); + + switch (range_convert_str(wmem_epan_scope(), &ret, pattern, wslua_togint32(L, idx_m))) { + case CVT_NO_ERROR: + break; + case CVT_SYNTAX_ERROR: + WSLUA_ARG_ERROR(Pref_range,DEFAULT,"syntax error in default range"); + return 0; + case CVT_NUMBER_TOO_BIG: + WSLUA_ARG_ERROR(Pref_range,DEFAULT,"value too large in default range"); + return 0; + default: + WSLUA_ARG_ERROR(Pref_range,DEFAULT,"unknown error in default range"); + return 0; + } + + return ret; +} + +/* Gets registered as metamethod automatically by WSLUA_REGISTER_CLASS/META */ +static int Pref__gc(lua_State* L) { + Pref pref = toPref(L,1); + + if (pref->ref != LUA_NOREF) { + // Did the user try to call __gc explicitly while it was registered to a + // protocol? Forbid that! + luaL_error(L, "Direct call to __gc is forbidden"); + return 0; + } + + g_free(pref->name); + g_free(pref->label); + g_free(pref->desc); + switch (pref->type) { + case PREF_STRING: + /* + * Free the initial string value; if it's not NULL, that + * means this is a never-registered preference, so the + * initial value hasn't been freed. + */ + g_free(pref->info.default_s); + break; + case PREF_ENUM: { + /* + * Free the enum values allocated in get_enum(). + */ + const enum_val_t *enum_valp = pref->info.enum_info.enumvals; + while (enum_valp->name) { + g_free((char *)enum_valp->name); + g_free((char *)enum_valp->description); + enum_valp++; + } + g_free((enum_val_t *)pref->info.enum_info.enumvals); + break; + } + default: + break; + } + g_free(pref); + + return 0; +} + +WSLUA_METHODS Pref_methods[] = { + WSLUA_CLASS_FNREG(Pref,bool), + WSLUA_CLASS_FNREG(Pref,uint), + WSLUA_CLASS_FNREG(Pref,string), + WSLUA_CLASS_FNREG(Pref,enum), + WSLUA_CLASS_FNREG(Pref,range), + WSLUA_CLASS_FNREG(Pref,statictext), + { NULL, NULL } +}; + +WSLUA_META Pref_meta[] = { + { NULL, NULL } +}; + + +WSLUA_REGISTER Pref_register(lua_State* L) { + WSLUA_REGISTER_CLASS(Pref); + return 0; +} + +WSLUA_CLASS_DEFINE(Prefs,NOP); /* The table of preferences of a protocol. */ + +WSLUA_METAMETHOD Prefs__newindex(lua_State* L) { + /* Creates a new preference. */ +#define WSLUA_ARG_Prefs__newindex_NAME 2 /* The abbreviation of this preference. */ +#define WSLUA_ARG_Prefs__newindex_PREF 3 /* A valid but still unassigned Pref object. */ + + Pref prefs_p = checkPrefs(L,1); + const gchar* name = luaL_checkstring(L,WSLUA_ARG_Prefs__newindex_NAME); + Pref pref = checkPref(L,WSLUA_ARG_Prefs__newindex_PREF); + Pref p; + const gchar *c; + + if (! prefs_p ) return 0; + + if (! pref ) { + WSLUA_ARG_ERROR(Prefs__newindex,PREF,"must be a valid Pref"); + return 0; + } + + if (pref->name) { + WSLUA_ARG_ERROR(Prefs__newindex,NAME,"cannot change existing preference"); + return 0; + } + + if (pref->proto) { + WSLUA_ARG_ERROR(Prefs__newindex,PREF,"cannot be added to more than one protocol"); + return 0; + } + + p = prefs_p; + + do { + if ( p->name && g_str_equal(p->name,name) ) { + luaL_error(L,"a preference named %s exists already",name); + return 0; + } + /* + * Make sure that only lower-case ASCII letters, numbers, + * underscores, and dots appear in the preference name. + */ + for (c = name; *c != '\0'; c++) { + if (!g_ascii_islower(*c) && !g_ascii_isdigit(*c) && *c != '_' && *c != '.') + { + luaL_error(L,"illegal preference name \"%s\", only lower-case ASCII letters, " + "numbers, underscores and dots may be used", name); + return 0; + } + } + + if ( ! p->next) { + // Keep a reference to the Pref to ensure it remains valid + // until the protocol is deregistered. + lua_pushvalue(L, WSLUA_ARG_Prefs__newindex_PREF); + pref->ref = luaL_ref(L, LUA_REGISTRYINDEX); + + p->next = pref; + pref->name = g_strdup(name); + + if (!pref->label) + pref->label = g_strdup(name); + + if (!prefs_p->proto->prefs_module) { + prefs_p->proto->prefs_module = prefs_register_protocol(prefs_p->proto->hfid, + wslua_prefs_changed); + } + + switch(pref->type) { + case PREF_BOOL: + prefs_register_bool_preference(prefs_p->proto->prefs_module, + pref->name, + pref->label, + pref->desc, + &(pref->value.b)); + break; + case PREF_UINT: + prefs_register_uint_preference(prefs_p->proto->prefs_module, + pref->name, + pref->label, + pref->desc, + 10, + &(pref->value.u)); + break; + case PREF_STRING: + prefs_register_string_preference(prefs_p->proto->prefs_module, + pref->name, + pref->label, + pref->desc, + (const char **)(&(pref->value.s))); + /* + * We're finished with the initial string value; see + * the comment in new_pref(). + */ + g_free(pref->info.default_s); + pref->info.default_s = NULL; + break; + case PREF_ENUM: + prefs_register_enum_preference(prefs_p->proto->prefs_module, + pref->name, + pref->label, + pref->desc, + &(pref->value.e), + pref->info.enum_info.enumvals, + pref->info.enum_info.radio_buttons); + break; + case PREF_RANGE: + prefs_register_range_preference(prefs_p->proto->prefs_module, + pref->name, + pref->label, + pref->desc, + &(pref->value.r), + pref->info.max_value); + break; + case PREF_STATIC_TEXT: + prefs_register_static_text_preference(prefs_p->proto->prefs_module, + pref->name, + pref->label, + pref->desc); + break; + default: + WSLUA_ERROR(Prefs__newindex,"Unknown Pref type"); + break; + } + + pref->proto = p->proto; + + WSLUA_RETURN(0); + } + } while (( p = p->next )); + + luaL_error(L,"this should not happen!"); + + WSLUA_RETURN(0); +} + +WSLUA_METAMETHOD Prefs__index(lua_State* L) { + /* + Get the value of a preference setting. + + ===== Example + + [source,lua] + ---- + -- print the value of Foo's preference named "bar" + debug( "bar = " .. proto_foo.prefs.bar ) + ---- + */ +#define WSLUA_ARG_Prefs__index_NAME 2 /* The abbreviation of this preference. */ + + Pref prefs_p = checkPrefs(L,1); + const gchar* name = luaL_checkstring(L,WSLUA_ARG_Prefs__index_NAME); + + if (! prefs_p ) return 0; + + if (!prefs_p->next) { + luaL_error(L,"No preference is registered yet"); + return 0; + } + + prefs_p = prefs_p->next; + + do { + if ( g_str_equal(prefs_p->name,name) ) { + switch (prefs_p->type) { + case PREF_BOOL: lua_pushboolean(L, prefs_p->value.b); break; + case PREF_UINT: lua_pushnumber(L,(lua_Number)prefs_p->value.u); break; + case PREF_STRING: lua_pushstring(L,prefs_p->value.s); break; + case PREF_ENUM: lua_pushnumber(L,(lua_Number)prefs_p->value.e); break; + case PREF_RANGE: + { + char *push_str = range_convert_range(NULL, prefs_p->value.r); + lua_pushstring(L, push_str); + wmem_free(NULL, push_str); + } + break; + default: WSLUA_ERROR(Prefs__index,"Unknown Pref type"); return 0; + } + WSLUA_RETURN(1); /* The current value of the preference. */ + } + } while (( prefs_p = prefs_p->next )); + + WSLUA_ARG_ERROR(Prefs__index,NAME,"no preference named like this"); + return 0; +} + +/* Gets registered as metamethod automatically by WSLUA_REGISTER_CLASS/META */ +static int Prefs__gc(lua_State* L _U_) { + /* do NOT free Prefs, it's a static part of Proto */ + return 0; +} + +WSLUA_META Prefs_meta[] = { + WSLUA_CLASS_MTREG(Prefs,newindex), + WSLUA_CLASS_MTREG(Prefs,index), + { NULL, NULL } +}; + +WSLUA_REGISTER Prefs_register(lua_State* L) { + WSLUA_REGISTER_META(Prefs); + 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: + */ diff --git a/epan/wslua/wslua_proto.c b/epan/wslua/wslua_proto.c new file mode 100644 index 00000000..363975b2 --- /dev/null +++ b/epan/wslua/wslua_proto.c @@ -0,0 +1,936 @@ +/* + * wslua_proto.c + * + * wireshark's interface to the Lua Programming Language + * + * (c) 2006, Luis E. Garcia Ontanon <luis@ontanon.org> + * (c) 2007, Tamas Regos <tamas.regos@ericsson.com> + * (c) 2014, Stig Bjorlykke <stig@bjorlykke.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" + +#include "wslua.h" +#include <epan/dissectors/packet-tcp.h> +#include <epan/exceptions.h> + +/* WSLUA_MODULE Proto Functions For New Protocols And Dissectors + + The classes and functions in this chapter allow Lua scripts to create new protocols for Wireshark. + <<lua_class_Proto,`Proto`>> protocol objects can have <<lua_class_Pref,`Pref`>> preferences, <<lua_class_ProtoField,`ProtoField`>> fields for filterable values that can be displayed in a details view tree, functions for dissecting the new protocol, and so on. + + The dissection function can be hooked into existing protocol tables through <<lua_class_DissectorTable,`DissectorTable`>> so that the new protocol dissector function gets called by that protocol, and the new dissector can itself call on other, already existing protocol dissectors by retrieving and calling the <<lua_class_Dissector,`Dissector`>> object. + A <<lua_class_Proto,`Proto`>> dissector can also be used as a post-dissector, at the end of every frame's dissection, or as a heuristic dissector. +*/ + + +/* + * _func_saver stores function refs so that Lua won't garbage collect them prematurely. + * It is only used by tcp_dissect_pdus right now. + */ +typedef struct _func_saver { + lua_State* state; + int get_len_ref; + int dissect_ref; +} func_saver_t; + +static GPtrArray* outstanding_FuncSavers = NULL; + +void clear_outstanding_FuncSavers(void) { + while (outstanding_FuncSavers->len) { + func_saver_t* fs = (func_saver_t*)g_ptr_array_remove_index_fast(outstanding_FuncSavers,0); + if (fs->state) { + lua_State* L = fs->state; + if (fs->get_len_ref != LUA_NOREF) { + luaL_unref(L, LUA_REGISTRYINDEX, fs->get_len_ref); + } + if (fs->dissect_ref != LUA_NOREF) { + luaL_unref(L, LUA_REGISTRYINDEX, fs->dissect_ref); + } + } + g_free(fs); + } +} + + +WSLUA_CLASS_DEFINE(Proto,FAIL_ON_NULL("Proto")); +/* + A new protocol in Wireshark. + Protocols have several uses. + The main one is to dissect a protocol, but they can also be dummies used to register preferences for other purposes. + */ + +static int protocols_table_ref = LUA_NOREF; + +WSLUA_CONSTRUCTOR Proto_new(lua_State* L) { /* Creates a new <<lua_class_Proto,`Proto`>> object. */ +#define WSLUA_ARG_Proto_new_NAME 1 /* The name of the protocol. */ +#define WSLUA_ARG_Proto_new_DESC 2 /* A Long Text description of the protocol (usually lowercase). */ + const gchar* name = luaL_checkstring(L,WSLUA_ARG_Proto_new_NAME); + const gchar* desc = luaL_checkstring(L,WSLUA_ARG_Proto_new_DESC); + Proto proto; + gchar *loname, *hiname; + + /* TODO: should really make a common function for all of wslua that does checkstring and non-empty at same time */ + if (!name[0]) { + WSLUA_ARG_ERROR(Proto_new,NAME,"must not be an empty string"); + return 0; + } + + if (!desc[0]) { + WSLUA_ARG_ERROR(Proto_new,DESC,"must not be an empty string"); + return 0; + } + + if (proto_name_already_registered(desc)) { + WSLUA_ARG_ERROR(Proto_new,DESC,"there cannot be two protocols with the same description"); + return 0; + } + + loname = g_ascii_strdown(name, -1); + if (proto_check_field_name(loname)) { + g_free(loname); + WSLUA_ARG_ERROR(Proto_new,NAME,"invalid character in name"); + return 0; + } + + hiname = g_ascii_strup(name, -1); + if ((proto_get_id_by_short_name(hiname) != -1) || + (proto_get_id_by_filter_name(loname) != -1)) + { + g_free(loname); + g_free(hiname); + WSLUA_ARG_ERROR(Proto_new,NAME,"there cannot be two protocols with the same name"); + return 0; + } + + proto = g_new0(wslua_proto_t, 1); + + proto->name = hiname; + proto->loname = loname; + proto->desc = g_strdup(desc); + proto->hfid = proto_register_protocol(proto->desc,hiname,loname); + proto->ett = -1; + proto->is_postdissector = FALSE; + proto->expired = FALSE; + + lua_newtable (L); + proto->fields = luaL_ref(L, LUA_REGISTRYINDEX); + + lua_newtable (L); + proto->expert_info_table_ref = luaL_ref(L, LUA_REGISTRYINDEX); + proto->expert_module = expert_register_protocol(proto->hfid); + + proto->prefs.name = NULL; + proto->prefs.label = NULL; + proto->prefs.desc = NULL; + proto->prefs.value.u = 0; + proto->prefs.next = NULL; + proto->prefs.proto = proto; + + proto->prefs_module = NULL; + proto->handle = NULL; + + lua_rawgeti(L, LUA_REGISTRYINDEX, protocols_table_ref); + + lua_pushstring(L,loname); + pushProto(L,proto); + + lua_settable(L, -3); + + pushProto(L,proto); + + WSLUA_RETURN(1); /* The newly created <<lua_class_Proto,`Proto`>> object. */ +} + +WSLUA_METAMETHOD Proto__call(lua_State* L) { /* Creates a <<lua_class_Proto,`Proto`>> object. */ +#define WSLUA_ARG_Proto__call_NAME 1 /* The name of the protocol. */ +#define WSLUA_ARG_Proto__call_DESC 2 /* A Long Text description of the protocol (usually lowercase). */ + lua_remove(L,1); /* remove the table */ + WSLUA_RETURN(Proto_new(L)); /* The new <<lua_class_Proto,`Proto`>> object. */ +} + +static int Proto__tostring(lua_State* L) { + Proto proto = checkProto(L,1); + + lua_pushfstring(L, "Proto: %s", proto->name); + + return 1; +} + +WSLUA_FUNCTION wslua_register_postdissector(lua_State* L) { + /* Make a <<lua_class_Proto,`Proto`>> protocol (with a dissector function) a post-dissector. + It will be called for every frame after dissection. */ +#define WSLUA_ARG_register_postdissector_PROTO 1 /* The protocol to be used as post-dissector. */ +#define WSLUA_OPTARG_register_postdissector_ALLFIELDS 2 /* Whether to generate all fields. + Note: This impacts performance (default=false). */ + + Proto proto = checkProto(L,WSLUA_ARG_register_postdissector_PROTO); + const gboolean all_fields = wslua_optbool(L, WSLUA_OPTARG_register_postdissector_ALLFIELDS, FALSE); + + if(!proto->is_postdissector) { + if (! proto->handle) { + proto->handle = register_dissector(proto->loname, dissect_lua, proto->hfid); + } + + register_postdissector(proto->handle); + proto->is_postdissector = TRUE; + } else { + luaL_argerror(L,1,"this protocol is already registered as postdissector"); + } + + if (all_fields) { + /* + * XXX - are there any Lua postdissectors that need "all fields", + * i.e. the entire protocol tree, or do they just look for + * *particular* fields, with field extractors? + * + * And do all of them require the actual *displayed* format of + * the fields they need? + * + * If not, this is overkill. + */ + epan_set_always_visible(TRUE); + } + + return 0; +} + +WSLUA_METHOD Proto_register_heuristic(lua_State* L) { + /* Registers a heuristic dissector function for this <<lua_class_Proto,`Proto`>> protocol, + for the given heuristic list name. + + When later called, the passed-in function will be given: + 1. A <<lua_class_Tvb,`Tvb`>> object + 2. A <<lua_class_Pinfo,`Pinfo`>> object + 3. A <<lua_class_TreeItem,`TreeItem`>> object + + The function must return `true` if the payload is for it, else `false`. + + The function should perform as much verification as possible to ensure the payload is for it, + and dissect the packet (including setting TreeItem info and such) only if the payload is for it, + before returning true or false. + + Since version 1.99.1, this function also accepts a Dissector object as the second argument, + to allow re-using the same Lua code as the `function proto.dissector(...)`. In this case, + the Dissector must return a Lua number of the number of bytes consumed/parsed: if 0 is returned, + it will be treated the same as a `false` return for the heuristic; if a positive or negative + number is returned, then the it will be treated the same as a `true` return for the heuristic, + meaning the packet is for this protocol and no other heuristic will be tried. + + @since 1.11.3 + */ +#define WSLUA_ARG_Proto_register_heuristic_LISTNAME 2 /* The heuristic list name this function + is a heuristic for (e.g., "udp" or + "infiniband.payload"). */ +#define WSLUA_ARG_Proto_register_heuristic_FUNC 3 /* A Lua function that will be invoked for + heuristic dissection. */ + Proto proto = checkProto(L,1); + const gchar *listname = luaL_checkstring(L, WSLUA_ARG_Proto_register_heuristic_LISTNAME); + const gchar *proto_name = proto->name; + const int top _U_ = lua_gettop(L); + + gchar *short_name; + + if (!proto_name || proto->hfid == -1) { + /* this shouldn't happen - internal bug if it does */ + luaL_error(L,"Proto_register_heuristic: got NULL proto name or invalid hfid"); + return 0; + } + + /* verify listname has a heuristic list */ + if (!has_heur_dissector_list(listname)) { + luaL_error(L, "there is no heuristic list for '%s'", listname); + return 0; + } + + short_name = wmem_strconcat(NULL, proto->loname, "_", listname, NULL); + + /* verify that this is not already registered */ + if (find_heur_dissector_by_unique_short_name(short_name)) { + wmem_free(NULL, short_name); + luaL_error(L, "'%s' is already registered as heuristic", proto->loname); + return 0; + } + wmem_free(NULL, short_name); + + /* we'll check if the second form of this function was called: when the second arg is + a Dissector obejct. The truth is we don't need the Dissector object to do this + form of registration, but someday we might... so we're using it as a boolean arg + right now and in the future might use it for other things in this registration. + */ + if (isDissector(L, WSLUA_ARG_Proto_register_heuristic_FUNC)) { + /* retrieve the Dissector's Lua function... first get the table of all dissector funcs */ + lua_rawgeti(L, LUA_REGISTRYINDEX, lua_dissectors_table_ref); + /* then get the one for this Proto */ + lua_getfield(L, -1, proto_name); + + if (!lua_isfunction(L,-1)) { + /* this shouldn't be possible */ + luaL_error(L,"Proto_register_heuristic: could not get lua function from lua_dissectors_table"); + return 0; + } + /* replace the Dissector with the function */ + lua_replace(L, WSLUA_ARG_Proto_register_heuristic_FUNC); + /* pop the lua_dissectors_table */ + lua_pop(L, 1); + ws_assert(top == lua_gettop(L)); + } + + /* heuristic functions are stored in a table in the registry; the registry has a + * table at reference lua_heur_dissectors_table_ref, and that table has keys for + * the heuristic listname (e.g., "udp", "tcp", etc.), and that key's value is a + * table of keys of the Proto->name, and their value is the function. + * So it's like registry[table_ref][heur_list_name][proto_name] = func + */ + if (lua_isfunction(L,WSLUA_ARG_Proto_register_heuristic_FUNC)) { + /* insert the heur dissector into the heur dissectors table */ + lua_rawgeti(L, LUA_REGISTRYINDEX, lua_heur_dissectors_table_ref); + /* the heuristic lists table is now at -1 */ + if (!lua_istable(L,-1)) { + /* this shouldn't be possible */ + luaL_error(L,"Proto_register_heuristic: could not get lua_heur_dissectors table from registry"); + return 0; + } + + if (!wslua_get_table(L,-1,listname)) { + /* no one's registered a lua heuristic for this list, so make a new list table */ + lua_newtable(L); + lua_pushvalue(L,-1); /* duplicate the table so we can set it as a field */ + lua_setfield(L,-3,listname); /* sets this new list table into the lists table */ + } + else if (wslua_get_field(L,-1,proto_name)) { + luaL_error(L,"A heuristic dissector for Proto '%s' is already registered for the '%s' list", proto_name, listname); + return 0; + } + + /* copy the func, set it as the value for key proto_name in listname's table */ + lua_pushvalue(L,WSLUA_ARG_Proto_register_heuristic_FUNC); + lua_setfield(L,-2,proto_name); + + /* ok, we're done with lua stuff, pop what we added to the stack */ + lua_pop(L,2); /* pop the lists table and the listname table */ + ws_assert(top == lua_gettop(L)); + + short_name = wmem_strconcat(NULL, proto->loname, "_", listname, NULL); + + /* now register the single/common heur_dissect_lua function */ + /* XXX - ADD PARAMETERS FOR NEW heur_dissector_add PARAMETERS!!! */ + heur_dissector_add(listname, heur_dissect_lua, proto_name, short_name, proto->hfid, HEURISTIC_ENABLE); + + wmem_free(NULL, short_name); + } else { + luaL_argerror(L,3,"The heuristic dissector must be a function"); + } + return 0; +} + +/* WSLUA_ATTRIBUTE Proto_dissector RW The protocol's dissector, a function you define. + + When later called, the function will be given: + 1. A <<lua_class_Tvb,`Tvb`>> object + 2. A <<lua_class_Pinfo,`Pinfo`>> object + 3. A <<lua_class_TreeItem,`TreeItem`>> object +*/ +static int Proto_get_dissector(lua_State* L) { + Proto proto = checkProto(L,1); + + if (proto->handle) { + pushDissector(L,proto->handle); + return 1; + } else { + luaL_error(L,"The protocol hasn't been registered yet"); + return 0; + } +} + +static int Proto_set_dissector(lua_State* L) { + Proto proto = checkProto(L,1); + + if (lua_isfunction(L,2)) { + /* insert the dissector into the dissectors table */ + lua_rawgeti(L, LUA_REGISTRYINDEX, lua_dissectors_table_ref); + lua_replace(L, 1); + lua_pushstring(L,proto->name); + lua_insert(L, 2); /* function is now at 3 */ + lua_settable(L,1); + + if (! proto->handle) { + proto->handle = register_dissector(proto->loname, dissect_lua, proto->hfid); + } + } else { + luaL_argerror(L,2,"The dissector of a protocol must be a function"); + } + return 0; +} + +/* WSLUA_ATTRIBUTE Proto_prefs RO The preferences of this dissector. */ +static int Proto_get_prefs(lua_State* L) { + Proto proto = checkProto(L,1); + pushPrefs(L,&proto->prefs); + return 1; +} + +/* WSLUA_ATTRIBUTE Proto_prefs_changed WO The preferences changed routine of this dissector, + a Lua function you define. + + The function is called when the protocol's preferences are changed. + It is passed no arguments. + */ +static int Proto_set_prefs_changed(lua_State* L) { + Proto proto = checkProto(L,1); + + if (lua_isfunction(L,2)) { + /* insert the prefs changed callback into the prefs_changed table */ + lua_getglobal(L, WSLUA_PREFS_CHANGED); + lua_replace(L, 1); + lua_pushstring(L,proto->name); + lua_insert(L, 2); /* function is now at 3 */ + lua_settable(L,1); + } else { + luaL_argerror(L,2,"The prefs of a protocol must be a function"); + } + return 0; +} + +/* WSLUA_ATTRIBUTE Proto_init WO The init routine of this dissector, a function you define. + + The init function is called when the a new capture file is opened or when + the open capture file is closed. It is passed no arguments. +*/ +static int Proto_set_init(lua_State* L) { + Proto proto = checkProto(L,1); + + if (lua_isfunction(L,2)) { + /* insert the init routine into the init_routines table */ + lua_getglobal(L, WSLUA_INIT_ROUTINES); + lua_replace(L, 1); + lua_pushstring(L,proto->name); + lua_insert(L, 2); /* function is now at 3 */ + lua_settable(L,1); + } else { + luaL_argerror(L,2,"The initializer of a protocol must be a function"); + } + return 0; +} + +/* WSLUA_ATTRIBUTE Proto_name RO The name given to this dissector. */ +WSLUA_ATTRIBUTE_STRING_GETTER(Proto,name); + +/* WSLUA_ATTRIBUTE Proto_description RO The description given to this dissector. */ +WSLUA_ATTRIBUTE_NAMED_STRING_GETTER(Proto,description,desc); + +/* WSLUA_ATTRIBUTE Proto_fields RW The `ProtoField`++'++s Lua table of this dissector. */ +static int Proto_get_fields(lua_State* L) { + Proto proto = checkProto(L,1); + lua_rawgeti(L, LUA_REGISTRYINDEX, proto->fields); + return 1; +} + +static int Proto_set_fields(lua_State* L) { + Proto proto = checkProto(L,1); +#define FIELDS_TABLE 2 +#define NEW_TABLE 3 +#define NEW_FIELD 3 + + lua_rawgeti(L, LUA_REGISTRYINDEX, proto->fields); + lua_insert(L,FIELDS_TABLE); + + if( lua_istable(L,NEW_TABLE)) { + for (lua_pushnil(L); lua_next(L, NEW_TABLE); ) { + if (isProtoField(L,5)) { + luaL_ref(L,FIELDS_TABLE); + } else if (! lua_isnil(L,5) ) { + return luaL_error(L,"only ProtoFields should be in the table"); + } + } + } else if (isProtoField(L,NEW_FIELD)){ + lua_pushvalue(L, NEW_FIELD); + luaL_ref(L,FIELDS_TABLE); + + } else { + return luaL_error(L,"either a ProtoField or an array of protofields"); + } + + lua_pushvalue(L, 3); + + return 1; +} + +/* WSLUA_ATTRIBUTE Proto_experts RW The expert info Lua table of this `Proto`. + + @since 1.11.3 + */ +static int Proto_get_experts(lua_State* L) { + Proto proto = checkProto(L,1); + lua_rawgeti(L, LUA_REGISTRYINDEX, proto->expert_info_table_ref); + return 1; +} + +static int Proto_set_experts(lua_State* L) { + Proto proto = checkProto(L,1); +#define EI_TABLE 2 +#define NEW_TABLE 3 +#define NEW_FIELD 3 + + lua_rawgeti(L, LUA_REGISTRYINDEX, proto->expert_info_table_ref); + lua_insert(L,EI_TABLE); + + if( lua_istable(L,NEW_TABLE)) { + for (lua_pushnil(L); lua_next(L, NEW_TABLE); ) { + if (isProtoExpert(L,5)) { + luaL_ref(L,EI_TABLE); + } else if (! lua_isnil(L,5) ) { + return luaL_error(L,"only ProtoExperts should be in the table"); + } + } + } else if (isProtoExpert(L,NEW_FIELD)){ + lua_pushvalue(L, NEW_FIELD); + luaL_ref(L,EI_TABLE); + + } else { + return luaL_error(L,"either a ProtoExpert or an array of ProtoExperts"); + } + + lua_pushvalue(L, 3); + + return 1; +} + +/* Gets registered as metamethod automatically by WSLUA_REGISTER_CLASS/META */ +static int Proto__gc(lua_State* L) { + /* Proto is registered twice, once in protocols_table_ref and once returned from Proto_new. + * It will not be freed unless deregistered. + */ + Proto proto = toProto(L,1); + + if (!proto->expired) { + proto->expired = TRUE; + } else if (proto->hfid == -2) { + /* Only free deregistered Proto */ + g_free(proto); + } + + 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 Proto_attributes[] = { + WSLUA_ATTRIBUTE_RWREG(Proto,dissector), + WSLUA_ATTRIBUTE_RWREG(Proto,fields), + WSLUA_ATTRIBUTE_RWREG(Proto,experts), + WSLUA_ATTRIBUTE_ROREG(Proto,prefs), + WSLUA_ATTRIBUTE_WOREG(Proto,prefs_changed), + WSLUA_ATTRIBUTE_WOREG(Proto,init), + WSLUA_ATTRIBUTE_ROREG(Proto,name), + WSLUA_ATTRIBUTE_ROREG(Proto,description), + { NULL, NULL, NULL } +}; + +WSLUA_METHODS Proto_methods[] = { + WSLUA_CLASS_FNREG(Proto,new), + WSLUA_CLASS_FNREG(Proto,register_heuristic), + { NULL, NULL } +}; + +WSLUA_META Proto_meta[] = { + WSLUA_CLASS_MTREG(Proto,tostring), + WSLUA_CLASS_MTREG(Proto,call), + { NULL, NULL } +}; + +int Proto_register(lua_State* L) { + WSLUA_REGISTER_CLASS_WITH_ATTRS(Proto); + + outstanding_FuncSavers = g_ptr_array_new(); + + lua_newtable(L); + protocols_table_ref = luaL_ref(L, LUA_REGISTRYINDEX); + + return 0; +} + +/** + * Query field abbr that is defined and bound to a Proto in lua. + * They are not registered until the end of the initialization. + */ +ProtoField wslua_is_field_available(lua_State* L, const char* field_abbr) { + lua_rawgeti(L, LUA_REGISTRYINDEX, protocols_table_ref); + lua_pushnil(L); + while (lua_next(L, -2)) { + Proto proto; + proto = checkProto(L, -1); + + lua_rawgeti(L, LUA_REGISTRYINDEX, proto->fields); + + lua_pushnil(L); + while (lua_next(L, -2)) { + ProtoField f = checkProtoField(L, -1); + if (strcmp(field_abbr, f->abbrev) == 0) { + /* found! */ + lua_pop(L, 6); + return f; + } + lua_pop(L, 1); /* table value */ + } + lua_pop(L, 2); /* proto->fields and table value */ + } + lua_pop(L, 1); /* protocols_table_ref */ + + return NULL; +} + +int wslua_deregister_heur_dissectors(lua_State* L) { + /* for each registered heur dissector do... */ + lua_rawgeti(L, LUA_REGISTRYINDEX, lua_heur_dissectors_table_ref); + for (lua_pushnil(L); lua_next(L, -2); lua_pop(L, 1)) { + const gchar *listname = luaL_checkstring(L, -2); + for (lua_pushnil(L); lua_next(L, -2); lua_pop(L, 1)) { + const gchar *proto_name = luaL_checkstring(L, -2); + int proto_id = proto_get_id_by_short_name(proto_name); + heur_dissector_delete(listname, heur_dissect_lua, proto_id); + } + } + lua_pop(L, 1); /* lua_heur_dissectors_table_ref */ + + return 0; +} + +int wslua_deregister_protocols(lua_State* L) { + /* for each registered Proto protocol do... */ + lua_rawgeti(L, LUA_REGISTRYINDEX, protocols_table_ref); + for (lua_pushnil(L); lua_next(L, -2); lua_pop(L, 1)) { + Proto proto; + proto = checkProto(L, -1); + + if (proto->handle) { + deregister_dissector(proto->loname); + } + if (proto->prefs_module) { + Pref pref; + prefs_deregister_protocol(proto->hfid); + /* Preferences are unregistered, now free its memory via Pref__gc */ + for (pref = proto->prefs.next; pref; pref = pref->next) { + int pref_ref = pref->ref; + pref->ref = LUA_NOREF; + luaL_unref(L, LUA_REGISTRYINDEX, pref_ref); + } + } + if (proto->expert_module) { + expert_deregister_protocol(proto->expert_module); + } + proto_deregister_protocol(proto->name); + + /* for each registered ProtoField do... */ + lua_rawgeti(L, LUA_REGISTRYINDEX, proto->fields); + for (lua_pushnil(L); lua_next(L, -2); lua_pop(L, 1)) { + ProtoField f = checkProtoField(L, -1); + + /* Memory ownership was previously transferred to epan in Proto_commit */ + f->name = NULL; + f->abbrev = NULL; + f->vs = NULL; + f->blob = NULL; + + f->hfid = -2; /* Deregister ProtoField, freed in ProtoField__gc */ + } + lua_pop(L, 1); + + /* for each registered ProtoExpert do... */ + lua_rawgeti(L, LUA_REGISTRYINDEX, proto->expert_info_table_ref); + for (lua_pushnil(L); lua_next(L, -2); lua_pop(L, 1)) { + ProtoExpert pe = checkProtoExpert(L,-1); + + /* Memory ownership was previously transferred to epan in Proto_commit */ + pe->abbrev = NULL; + pe->text = NULL; + + pe->ids.hf = -2; /* Deregister ProtoExpert, freed in ProtoExpert__gc */ + } + lua_pop(L, 1); + + if (proto->hfa && proto->hfa->len) { + proto_add_deregistered_data(g_array_free(proto->hfa,FALSE)); + } else { + g_array_free(proto->hfa,TRUE); + } + + /* No need for deferred deletion of subtree indexes */ + g_array_free(proto->etta,TRUE); + + if (proto->eia && proto->eia->len) { + proto_add_deregistered_data(g_array_free(proto->eia,FALSE)); + } else { + g_array_free(proto->eia,TRUE); + } + + proto->hfid = -2; /* Deregister Proto, freed in Proto__gc */ + } + + lua_pop(L, 1); /* protocols_table_ref */ + + return 0; +} + +int Proto_commit(lua_State* L) { + lua_settop(L,0); + /* the following gets the table of registered Proto protocols and puts it on the stack (index=1) */ + lua_rawgeti(L, LUA_REGISTRYINDEX, protocols_table_ref); + + /* for each registered Proto protocol do... */ + for (lua_pushnil(L); lua_next(L, 1); lua_pop(L, 2)) { + /* lua_next() pop'ed the nil, pushed a table entry key at index=2, with value at index=3. + In our case, the key is the Proto's name, and the value is the Proto object. + At next iteration, the value (Proto object) and ProtoExperts table will be pop'ed due + to lua_pop(L, 2), and when lua_next() returns 0 (no more table entries), it will have + pop'ed the final key itself, leaving just the protocols_table_ref table on the stack. + */ + Proto proto = checkProto(L,3); + gint* ettp = NULL; + + proto->hfa = g_array_new(TRUE,TRUE,sizeof(hf_register_info)); + proto->etta = g_array_new(TRUE,TRUE,sizeof(gint*)); + proto->eia = g_array_new(TRUE,TRUE,sizeof(ei_register_info)); + + ettp = &(proto->ett); + g_array_append_val(proto->etta,ettp); + + /* get the Lua table of ProtoFields, push it on the stack (index=3) */ + lua_rawgeti(L, LUA_REGISTRYINDEX, proto->fields); + + /* for each ProtoField in the Lua table do... */ + for (lua_pushnil(L); lua_next(L, 4); lua_pop(L, 1)) { + ProtoField f = checkProtoField(L,6); + hf_register_info hfri = { NULL, { NULL, NULL, FT_NONE, 0, NULL, 0, NULL, HFILL } }; + ettp = &(f->ett); + + hfri.p_id = &(f->hfid); + hfri.hfinfo.name = f->name; + hfri.hfinfo.abbrev = f->abbrev; + hfri.hfinfo.type = f->type; + hfri.hfinfo.display = f->base; + hfri.hfinfo.strings = VALS(f->vs); + hfri.hfinfo.bitmask = f->mask; + hfri.hfinfo.blurb = f->blob; + + // XXX this will leak resources. + if (f->hfid != -2) { + return luaL_error(L,"fields can be registered only once"); + } + + f->hfid = -1; + g_array_append_val(proto->hfa,hfri); + g_array_append_val(proto->etta,ettp); + } + + /* register the proto fields */ + proto_register_field_array(proto->hfid,(hf_register_info*)(void*)proto->hfa->data,proto->hfa->len); + proto_register_subtree_array((gint**)(void*)proto->etta->data,proto->etta->len); + + lua_pop(L,1); /* pop the table of ProtoFields */ + + /* now do the same thing for expert fields */ + + /* get the Lua table of ProtoExperts, push it on the stack (index=2) */ + lua_rawgeti(L, LUA_REGISTRYINDEX, proto->expert_info_table_ref); + + /* for each ProtoExpert in the Lua table do... */ + for (lua_pushnil(L); lua_next(L, 4); lua_pop(L, 1)) { + ProtoExpert e = checkProtoExpert(L,6); + ei_register_info eiri = { NULL, { NULL, 0, 0, NULL, EXPFILL } }; + + eiri.ids = &(e->ids); + eiri.eiinfo.name = e->abbrev; + eiri.eiinfo.group = e->group; + eiri.eiinfo.severity = e->severity; + eiri.eiinfo.summary = e->text; + + if (e->ids.ei != EI_INIT_EI || e->ids.hf != -2) { + return luaL_error(L,"expert fields can be registered only once"); + } + + e->ids.hf = -1; + g_array_append_val(proto->eia,eiri); + } + + expert_register_field_array(proto->expert_module, (ei_register_info*)(void*)proto->eia->data, proto->eia->len); + + /* Proto object and ProtoFields table will be pop'ed by lua_pop(L, 2) in for statement */ + } + + lua_pop(L,1); /* pop the protocols_table_ref */ + + return 0; +} + +static guint +wslua_dissect_tcp_get_pdu_len(packet_info *pinfo, tvbuff_t *tvb, + int offset, void *data) +{ + /* WARNING: called from a TRY block, do not call luaL_error! */ + func_saver_t* fs = (func_saver_t*)data; + lua_State* L = fs->state; + int pdu_len = 0; + + lua_settop(L, 0); + lua_rawgeti(L, LUA_REGISTRYINDEX, fs->get_len_ref); + + if (lua_isfunction(L,1)) { + + push_Tvb(L,tvb); + push_Pinfo(L,pinfo); + lua_pushinteger(L,offset); + + if ( lua_pcall(L,3,1,0) ) { + THROW_LUA_ERROR("Lua Error in dissect_tcp_pdus get_len_func: %s", lua_tostring(L,-1)); + } else { + /* if the Lua dissector reported the consumed bytes, pass it to our caller */ + if (lua_isnumber(L, -1)) { + /* we got the pdu_len */ + pdu_len = wslua_togint(L, -1); + lua_pop(L, 1); + } else { + THROW_LUA_ERROR("Lua Error dissect_tcp_pdus: get_len_func did not return a Lua number of the PDU length"); + } + } + + } else { + REPORT_DISSECTOR_BUG("Lua Error in dissect_tcp_pdus: did not find the get_len_func dissector"); + } + + return pdu_len; +} + +static int +wslua_dissect_tcp_dissector(tvbuff_t *tvb, packet_info *pinfo, + proto_tree *tree, void *data) +{ + /* WARNING: called from a TRY block, do not call luaL_error! */ + func_saver_t* fs = (func_saver_t*)data; + lua_State* L = fs->state; + int consumed_bytes = 0; + + lua_settop(L, 0); + lua_rawgeti(L, LUA_REGISTRYINDEX, fs->dissect_ref); + + if (lua_isfunction(L,1)) { + + push_Tvb(L,tvb); + push_Pinfo(L,pinfo); + /* XXX: not sure if it's kosher to just use the tree as the item */ + push_TreeItem(L, tree, (proto_item*)tree); + + if ( lua_pcall(L,3,1,0) ) { + THROW_LUA_ERROR("dissect_tcp_pdus dissect_func: %s", lua_tostring(L, -1)); + } else { + /* if the Lua dissector reported the consumed bytes, pass it to our caller */ + if (lua_isnumber(L, -1)) { + /* we got the consumed bytes or the missing bytes as a negative number */ + consumed_bytes = wslua_togint(L, -1); + lua_pop(L, 1); + } + } + + } else { + REPORT_DISSECTOR_BUG("dissect_tcp_pdus: did not find the dissect_func dissector"); + } + + return consumed_bytes; +} + + +WSLUA_FUNCTION wslua_dissect_tcp_pdus(lua_State* L) { + /* Make the TCP-layer invoke the given Lua dissection function for each + PDU in the TCP segment, of the length returned by the given get_len_func + function. + + This function is useful for protocols that run over TCP and that are + either a fixed length always, or have a minimum size and have a length + field encoded within that minimum portion that identifies their full + length. For such protocols, their protocol dissector function can invoke + this `dissect_tcp_pdus()` function to make it easier to handle dissecting + their protocol's messages (i.e., their protocol data unit (PDU)). This + function shouild not be used for protocols whose PDU length cannot be + determined from a fixed minimum portion, such as HTTP or Telnet. + + @since 1.99.2 + */ +#define WSLUA_ARG_dissect_tcp_pdus_TVB 1 /* The Tvb buffer to dissect PDUs from. */ +#define WSLUA_ARG_dissect_tcp_pdus_TREE 2 /* The Tvb buffer to dissect PDUs from. */ +#define WSLUA_ARG_dissect_tcp_pdus_MIN_HEADER_SIZE 3 /* The number of bytes + in the fixed-length part of the PDU. */ +#define WSLUA_ARG_dissect_tcp_pdus_GET_LEN_FUNC 4 /* A Lua function that will be + called for each PDU, to determine the full length of the + PDU. The called function will be given (1) the `Tvb` object + of the whole `Tvb` (possibly reassembled), (2) the `Pinfo` object, + and (3) an offset number of the index of the first byte + of the PDU (i.e., its first header byte). The Lua function + must return a Lua number of the full length of the PDU. */ +#define WSLUA_ARG_dissect_tcp_pdus_DISSECT_FUNC 5 /* A Lua function that will be + called for each PDU, to dissect the PDU. The called + function will be given (1) the `Tvb` object of the PDU's + `Tvb` (possibly reassembled), (2) the `Pinfo` object, + and (3) the `TreeItem` object. The Lua function must + return a Lua number of the number of bytes read/handled, + which would typically be the `Tvb:len()`.*/ +#define WSLUA_OPTARG_dissect_tcp_pdus_DESEGMENT 6 /* Whether to reassemble PDUs + crossing TCP segment boundaries or not. (default=true) */ + Tvb tvb = checkTvb(L,WSLUA_ARG_dissect_tcp_pdus_TVB); + TreeItem ti = checkTreeItem(L,WSLUA_ARG_dissect_tcp_pdus_TREE); + guint fixed_len = (guint)luaL_checkinteger(L,WSLUA_ARG_dissect_tcp_pdus_MIN_HEADER_SIZE); + gboolean proto_desegment = wslua_optbool(L, WSLUA_OPTARG_dissect_tcp_pdus_DESEGMENT, TRUE); + + if (!lua_pinfo) { + luaL_error(L,"dissect_tcp_pdus can only be invoked while in a dissect function"); + return 0; + } + + if (lua_isfunction(L,WSLUA_ARG_dissect_tcp_pdus_GET_LEN_FUNC) && + lua_isfunction(L,WSLUA_ARG_dissect_tcp_pdus_DISSECT_FUNC)) + { + /* save the Lua functions so that we can call them later */ + func_saver_t* fs = g_new(func_saver_t, 1); + + lua_settop(L, WSLUA_ARG_dissect_tcp_pdus_DISSECT_FUNC); + + fs->state = L; + /* the following pops the top function and sets a ref to it in the registry */ + fs->dissect_ref = luaL_ref(L, LUA_REGISTRYINDEX); + fs->get_len_ref = luaL_ref(L, LUA_REGISTRYINDEX); + + /* save the passed-in function refs, so Lua's garbage collector won't + destroy them before they get invoked */ + g_ptr_array_add(outstanding_FuncSavers, fs); + + WRAP_NON_LUA_EXCEPTIONS( + tcp_dissect_pdus(tvb->ws_tvb, lua_pinfo, ti->tree, proto_desegment, + fixed_len, wslua_dissect_tcp_get_pdu_len, + wslua_dissect_tcp_dissector, (void*)fs); + ) + } else { + luaL_error(L,"The third and fourth arguments need to be Lua functions"); + } + 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: + */ diff --git a/epan/wslua/wslua_proto_expert.c b/epan/wslua/wslua_proto_expert.c new file mode 100644 index 00000000..a7c7c82a --- /dev/null +++ b/epan/wslua/wslua_proto_expert.c @@ -0,0 +1,190 @@ +/* + * wslua_proto_expert.c + * + * Wireshark's interface to the Lua Programming Language + * + * (c) 2006, Luis E. Garcia Ontanon <luis@ontanon.org> + * (c) 2008, Balint Reczey <balint.reczey@ericsson.com> + * (c) 2011, Stig Bjorlykke <stig@bjorlykke.org> + * (c) 2014, Hadriel Kaplan <hadrielk@yahoo.com> + * + * 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" + +#include "wslua.h" + + +/* WSLUA_CONTINUE_MODULE Proto */ + + +WSLUA_CLASS_DEFINE(ProtoExpert,FAIL_ON_NULL("null ProtoExpert")); + /* A Protocol expert info field, to be used when adding items to the dissection tree. + + @since 1.11.3 + */ + +WSLUA_CONSTRUCTOR ProtoExpert_new(lua_State* L) { + /* Creates a new `ProtoExpert` object to be used for a protocol's expert information notices. + + @since 1.11.3 + */ +#define WSLUA_ARG_ProtoExpert_new_ABBR 1 /* Filter name of the expert info field (the string that + is used in filters). */ +#define WSLUA_ARG_ProtoExpert_new_TEXT 2 /* The default text of the expert field. */ +#define WSLUA_ARG_ProtoExpert_new_GROUP 3 /* Expert group type: one of: `expert.group.CHECKSUM`, + `expert.group.SEQUENCE`, `expert.group.RESPONSE_CODE`, + `expert.group.REQUEST_CODE`, `expert.group.UNDECODED`, + `expert.group.REASSEMBLE`, `expert.group.MALFORMED`, + `expert.group.DEBUG`, `expert.group.PROTOCOL`, + `expert.group.SECURITY`, `expert.group.COMMENTS_GROUP`, + `expert.group.DECRYPTION`, `expert.group.ASSUMPTION` + or `expert.group.DEPRECATED`. */ +#define WSLUA_ARG_ProtoExpert_new_SEVERITY 4 /* Expert severity type: one of: + `expert.severity.COMMENT`, `expert.severity.CHAT`, + `expert.severity.NOTE`, `expert.severity.WARN`, + or `expert.severity.ERROR`. */ + + ProtoExpert pe = NULL; + const gchar* abbr = wslua_checkstring_only(L,WSLUA_ARG_ProtoExpert_new_ABBR); + const gchar* text = wslua_checkstring_only(L,WSLUA_ARG_ProtoExpert_new_TEXT); + int group = (int)luaL_checkinteger(L, WSLUA_ARG_ProtoExpert_new_GROUP); + int severity = (int)luaL_checkinteger(L, WSLUA_ARG_ProtoExpert_new_SEVERITY); + + if (!abbr[0]) { + luaL_argerror(L, WSLUA_ARG_ProtoExpert_new_ABBR, "Empty field name abbrev"); + return 0; + } + + if (proto_check_field_name(abbr)) { + luaL_argerror(L, WSLUA_ARG_ProtoExpert_new_ABBR, "Invalid char in abbrev"); + return 0; + } + + if (proto_registrar_get_byname(abbr)) { + luaL_argerror(L, WSLUA_ARG_ProtoExpert_new_ABBR, "This abbrev already exists"); + return 0; + } + + if (!text[0]) { + luaL_argerror(L, WSLUA_ARG_ProtoExpert_new_TEXT, "Empty text"); + return 0; + } + + switch (group) { + case PI_CHECKSUM: + case PI_SEQUENCE: + case PI_RESPONSE_CODE: + case PI_REQUEST_CODE: + case PI_UNDECODED: + case PI_REASSEMBLE: + case PI_MALFORMED: + case PI_DEBUG: + case PI_PROTOCOL: + case PI_SECURITY: + case PI_COMMENTS_GROUP: + case PI_DECRYPTION: + case PI_ASSUMPTION: + case PI_DEPRECATED: + break; + default: + luaL_argerror(L, WSLUA_ARG_ProtoExpert_new_GROUP, "Group must be one of expert.group.*"); + return 0; + } + + switch (severity) { + case PI_COMMENT: + case PI_CHAT: + case PI_NOTE: + case PI_WARN: + case PI_ERROR: + break; + default: + luaL_argerror(L, WSLUA_ARG_ProtoExpert_new_SEVERITY, "Severity must be one of expert.severity.*"); + return 0; + } + + pe = g_new(wslua_expert_field_t,1); + + pe->ids.ei = EI_INIT_EI; + pe->ids.hf = -2; + pe->abbrev = g_strdup(abbr); + pe->text = g_strdup(text); + pe->group = group; + pe->severity = severity; + + pushProtoExpert(L,pe); + + WSLUA_RETURN(1); /* The newly created `ProtoExpert` object. */ +} + +WSLUA_METAMETHOD ProtoExpert__tostring(lua_State* L) { + /* Returns a string with debugging information about a `ProtoExpert` object. + + @since 1.11.3 + */ + ProtoExpert pe = toProtoExpert(L,1); + + if (!pe) { + lua_pushstring(L,"ProtoExpert pointer is NULL!"); + } else { + lua_pushfstring(L, "ProtoExpert: ei=%d, hf=%d, abbr=%s, text=%s, group=%d, severity=%d", + pe->ids.ei, pe->ids.hf, pe->abbrev, pe->text, pe->group, pe->severity); + } + return 1; +} + +static int ProtoExpert__gc(lua_State* L) { + ProtoExpert pe = toProtoExpert(L,1); + + /* + * Initialized to -2 in ProtoExpert_new, + * changed to -1 in Proto_commit and subsequently replaced by + * an allocated number in proto_register_field_array. + * Reset to -2 again in wslua_deregister_protocols. + */ + if (pe->ids.hf != -2) { + /* Only free unregistered and deregistered ProtoExpert */ + return 0; + } + + g_free((gchar *)pe->abbrev); + g_free((gchar *)pe->text); + g_free(pe); + + return 0; +} + +WSLUA_METHODS ProtoExpert_methods[] = { + WSLUA_CLASS_FNREG(ProtoExpert,new), + { NULL, NULL } +}; + +WSLUA_META ProtoExpert_meta[] = { + WSLUA_CLASS_MTREG(ProtoExpert,tostring), + { NULL, NULL } +}; + +int ProtoExpert_register(lua_State* L) { + WSLUA_REGISTER_CLASS(ProtoExpert); + 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: + */ diff --git a/epan/wslua/wslua_proto_field.c b/epan/wslua/wslua_proto_field.c new file mode 100644 index 00000000..86f3652e --- /dev/null +++ b/epan/wslua/wslua_proto_field.c @@ -0,0 +1,1551 @@ +/* + * wslua_proto_field.c + * + * Wireshark's interface to the Lua Programming Language + * + * (c) 2006, Luis E. Garcia Ontanon <luis@ontanon.org> + * (c) 2008, Balint Reczey <balint.reczey@ericsson.com> + * (c) 2011, Stig Bjorlykke <stig@bjorlykke.org> + * (c) 2014, Hadriel Kaplan <hadrielk@yahoo.com> + * + * 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" + +#include "wslua.h" + +/* WSLUA_CONTINUE_MODULE Proto */ + + +WSLUA_CLASS_DEFINE(ProtoField,FAIL_ON_NULL("null ProtoField")); + /* A Protocol field (to be used when adding items to the dissection tree). */ + +static const wslua_ft_types_t ftenums[] = { + {"ftypes.NONE", FT_NONE}, + {"ftypes.BOOLEAN", FT_BOOLEAN}, + {"ftypes.CHAR", FT_CHAR}, + {"ftypes.UINT8", FT_UINT8}, + {"ftypes.UINT16", FT_UINT16}, + {"ftypes.UINT24", FT_UINT24}, + {"ftypes.UINT32", FT_UINT32}, + {"ftypes.UINT64", FT_UINT64}, + {"ftypes.INT8", FT_INT8}, + {"ftypes.INT16", FT_INT16}, + {"ftypes.INT24", FT_INT24}, + {"ftypes.INT32", FT_INT32}, + {"ftypes.INT64", FT_INT64}, + {"ftypes.FLOAT", FT_FLOAT}, + {"ftypes.DOUBLE", FT_DOUBLE}, + {"ftypes.ABSOLUTE_TIME", FT_ABSOLUTE_TIME}, + {"ftypes.RELATIVE_TIME", FT_RELATIVE_TIME}, + {"ftypes.STRING", FT_STRING}, + {"ftypes.STRINGZ", FT_STRINGZ}, + {"ftypes.ETHER", FT_ETHER}, + {"ftypes.BYTES", FT_BYTES}, + {"ftypes.UINT_BYTES", FT_UINT_BYTES}, + {"ftypes.IPv4", FT_IPv4}, + {"ftypes.IPv6", FT_IPv6}, + {"ftypes.IPXNET", FT_IPXNET}, + {"ftypes.FRAMENUM", FT_FRAMENUM}, + {"ftypes.GUID", FT_GUID}, + {"ftypes.OID", FT_OID}, + {"ftypes.SYSTEM_ID", FT_SYSTEM_ID}, + {"ftypes.REL_OID", FT_REL_OID}, + {"ftypes.EUI64", FT_EUI64}, + {"ftypes.FCWWN", FT_FCWWN}, + {NULL, FT_NONE} +}; + +static enum ftenum get_ftenum(const gchar* type) { + const wslua_ft_types_t* ts; + for (ts = ftenums; ts->str; ts++) { + if ( g_str_equal(ts->str,type) ) { + return ts->id; + } + } + return FT_NONE; +} + +static const gchar* ftenum_to_string(enum ftenum ft) { + const wslua_ft_types_t* ts; + for (ts = ftenums; ts->str; ts++) { + if ( ts->id == ft ) { + return ts->str; + } + } + return NULL; +} + +struct field_display_string_t { + const gchar* str; + unsigned base; +}; + +/* + * This table is primarily used to convert from string representation + * to int representation in string_to_base(). + * Some string values are added for backward compatibility. + */ +static const struct field_display_string_t base_displays[] = { + {"base.NONE", BASE_NONE}, + {"base.DEC", BASE_DEC}, + {"base.HEX", BASE_HEX}, + {"base.OCT", BASE_OCT}, + {"base.DEC_HEX", BASE_DEC_HEX}, + {"base.HEX_DEC", BASE_HEX_DEC}, + {"base.UNIT_STRING", BASE_UNIT_STRING}, + /* Byte separators */ + {"base.DOT", SEP_DOT}, + {"base.DASH", SEP_DASH}, + {"base.COLON", SEP_COLON}, + {"base.SPACE", SEP_SPACE}, + /* for FT_BOOLEAN, how wide the parent bitfield is */ + {"8",8}, + {"16",16}, + {"24",24}, + {"32",32}, + /* FT_ABSOLUTE_TIME */ + {"base.LOCAL", ABSOLUTE_TIME_LOCAL}, + {"base.UTC", ABSOLUTE_TIME_UTC}, + {"base.DOY_UTC", ABSOLUTE_TIME_DOY_UTC}, + {"LOCAL", ABSOLUTE_TIME_LOCAL}, /* for backward compatibility */ + {"UTC", ABSOLUTE_TIME_UTC}, /* for backward compatibility */ + {"DOY_UTC", ABSOLUTE_TIME_DOY_UTC}, /* for backward compatibility */ + {NULL,0} +}; + +static const gchar* base_to_string(unsigned base) { + const struct field_display_string_t* b; + for (b=base_displays;b->str;b++) { + if ( base == b->base) + return b->str; + } + return NULL; +} + +static unsigned string_to_base(const gchar* str) { + const struct field_display_string_t* b; + for (b=base_displays;b->str;b++) { + if ( g_str_equal(str,b->str)) + return b->base; + } + return BASE_NONE; +} + +static void cleanup_range_string(GArray *rs) { + range_string *rs32 = (range_string *)(void *)(rs->data); + + while (rs32->strptr) { + g_free((gchar *)rs32->strptr); + rs32++; + } + g_array_free(rs, TRUE); +} + +static range_string * range_string_from_table(lua_State* L, int idx) { + GArray* rs; + range_string* rs32; + + if (lua_isnil(L,idx)) { + return NULL; + } else if (!lua_istable(L,idx)) { + luaL_argerror(L,idx,"must be a table"); + return NULL; + } + + /* + * The first parameter set to TRUE means give us a zero-filled + * terminal entry. + */ + rs = g_array_new(TRUE,TRUE,sizeof(range_string)); + + lua_pushnil(L); + + while (lua_next(L, idx) != 0) { + int inner_idx; + int key_count = 0; + range_string r = {0,0,NULL}; + + if (!lua_istable(L, -1)) { + cleanup_range_string(rs); + luaL_argerror(L, idx, "All values of a table used as a range_string must be tables"); + return NULL; + } + + /* + * Now process the table ... it must have three elements, + * the min value, the max, both integers and a string. + * + * However, they are each separate items in the table and we + * ignore their keys. + */ + inner_idx = lua_gettop(L); + lua_pushnil(L); + + /* + * First two elements must be numbers, third is a string + */ + while (lua_next(L, inner_idx) != 0) { + if (++key_count > 3) { + break; + } + + switch (key_count) { + case 1: + case 2: + if (!lua_isnumber(L, -1)) { + cleanup_range_string(rs); + luaL_argerror(L, idx, "First two elements of a range string value must be integers"); + return NULL; + } + if (key_count == 1) /* We incremented it above */ + r.value_min = wslua_toguint64(L, -1); + else + r.value_max = wslua_toguint64(L, -1); + break; + + case 3: + if (lua_type(L, -1) != LUA_TSTRING) { + cleanup_range_string(rs); + luaL_argerror(L, idx, "Third element of a range string value must be a string"); + return NULL; + } + r.strptr = g_strdup(lua_tostring(L,-1)); + /* + * We append the value here to avoid a mem leak if there + * are more than three entries in the table. + */ + g_array_append_val(rs,r); + break; + } + + lua_pop(L, 1); + } + + if (key_count != 3) { + cleanup_range_string(rs); + luaL_argerror(L, idx, "Values of a range string must be tables with exactly three elements"); + return NULL; + } + + lua_pop(L, 1); + } + + rs32 = (range_string*)(void*)g_array_free(rs, FALSE); + + return rs32; +} + +static value_string* value_string_from_table(lua_State* L, int idx) { + GArray* vs; + value_string* vs32; + + if (lua_isnil(L,idx)) { + return NULL; + } else if (!lua_istable(L,idx)) { + luaL_argerror(L,idx,"must be a table"); + return NULL; + } + + /* + * The first parameter set to TRUE means give us a zero-filled + * terminal entry. + */ + vs = g_array_new(TRUE,TRUE,sizeof(value_string)); + + lua_pushnil(L); + + while (lua_next(L, idx) != 0) { + value_string v = {0,NULL}; + + if (! lua_isnumber(L,-2)) { + vs32 = (value_string *)(void *)vs->data; + while (vs32->strptr) { + g_free((gchar *)vs32->strptr); + vs32++; + } + g_array_free(vs,TRUE); + luaL_argerror(L,idx,"All keys of a table used as value_string must be integers"); + return NULL; + } + + if (! lua_isstring(L,-1)) { + vs32 = (value_string *)(void *)vs->data; + while (vs32->strptr) { + g_free((gchar *)vs32->strptr); + vs32++; + } + g_array_free(vs,TRUE); + luaL_argerror(L,idx,"All values of a table used as value_string must be strings"); + return NULL; + } + + v.value = wslua_toguint32(L,-2); + v.strptr = g_strdup(lua_tostring(L,-1)); + + g_array_append_val(vs,v); + + lua_pop(L, 1); + } + + vs32 = (value_string*)(void*)g_array_free(vs, FALSE); + + return vs32; +} + +static val64_string* val64_string_from_table(lua_State* L, int idx) { + GArray* vs; + val64_string* vs64; + + if (lua_isnil(L,idx)) { + return NULL; + } else if (!lua_istable(L,idx)) { + luaL_argerror(L,idx,"must be a table"); + return NULL; + } + + /* + * The first parameter set to TRUE means give us a zero-filled + * terminal entry. + */ + vs = g_array_new(TRUE,TRUE,sizeof(val64_string)); + + lua_pushnil(L); + + while (lua_next(L, idx) != 0) { + val64_string v = {0,NULL}; + + if (! lua_isnumber(L,-2)) { + vs64 = (val64_string *)(void *)vs->data; + while (vs64->strptr) { + g_free((gchar *)vs64->strptr); + vs64++; + } + g_array_free(vs,TRUE); + luaL_argerror(L,idx,"All keys of a table used as value string must be integers"); + return NULL; + } + + if (! lua_isstring(L,-1)) { + vs64 = (val64_string *)(void *)vs->data; + while (vs64->strptr) { + g_free((gchar *)vs64->strptr); + vs64++; + } + g_array_free(vs,TRUE); + luaL_argerror(L,idx,"All values of a table used as value string must be strings"); + return NULL; + } + + v.value = wslua_toguint64(L, -2); + v.strptr = g_strdup(lua_tostring(L,-1)); + + g_array_append_val(vs,v); + + lua_pop(L, 1); + } + + vs64 = (val64_string*)(void*)g_array_free(vs, FALSE); + + return vs64; +} + +static true_false_string* true_false_string_from_table(lua_State* L, int idx) { + true_false_string* tfs; + gchar *true_string; + gchar *false_string; + + if (lua_isnil(L,idx)) { + return NULL; + } else if (!lua_istable(L,idx)) { + luaL_argerror(L,idx,"must be a table"); + return NULL; + } + + true_string = g_strdup("True"); + false_string = g_strdup("False"); + + lua_pushnil(L); + + while (lua_next(L, idx)) { + + if (! lua_isnumber(L,-2)) { + g_free (true_string); + g_free (false_string); + luaL_argerror(L,idx,"All keys of a table used as true_false_string must be integers"); + return NULL; + } + + if (! lua_isstring(L,-1)) { + g_free (true_string); + g_free (false_string); + luaL_argerror(L,idx,"All values of a table used as true_false_string must be strings"); + return NULL; + } + + /* Arrays in Lua start with index number 1 */ + switch (lua_tointeger(L,-2)) { + case 1: + g_free(true_string); + true_string = g_strdup(lua_tostring(L,-1)); + break; + case 2: + g_free(false_string); + false_string = g_strdup(lua_tostring(L,-1)); + break; + default: + g_free (true_string); + g_free (false_string); + luaL_argerror(L,idx,"The true_false_string table can have maximum two strings with key value 1 and 2"); + return NULL; + } + + lua_pop(L, 1); + } + + tfs = g_new(true_false_string, 1); + tfs->true_string = true_string; + tfs->false_string = false_string; + + return tfs; +} + +static guint64 get_mask(lua_State* L, int idx, guint64 default_value) { + guint64 mask = default_value; + + switch(lua_type(L, idx)) { + case LUA_TNUMBER: + mask = (guint64)wslua_optguint32(L, idx, (lua_Number)default_value); + break; + case LUA_TSTRING: + case LUA_TUSERDATA: + mask = getUInt64(L,idx); + break; + case LUA_TNIL: + case LUA_TNONE: + break; + default: + luaL_argerror(L,idx,"MASK field must be a number, UInt64 or string"); + break; + } + return mask; +} + +static unit_name_string* unit_name_string_from_table(lua_State* L, int idx) { + unit_name_string* units; + + if (lua_isnil(L,idx)) { + return NULL; + } else if (!lua_istable(L,idx)) { + luaL_argerror(L,idx,"must be a table"); + return NULL; + } + + units = g_new0(unit_name_string, 1); + + lua_pushnil(L); + + while (lua_next(L, idx)) { + + if (! lua_isnumber(L,-2)) { + g_free(units->singular); + g_free(units->plural); + g_free(units); + luaL_argerror(L,idx,"All keys of a table used as unit name must be integers"); + return NULL; + } + + if (! lua_isstring(L,-1)) { + g_free(units->singular); + g_free(units->plural); + g_free(units); + luaL_argerror(L,idx,"All values of a table used as unit name must be strings"); + return NULL; + } + + /* Arrays in Lua start with index number 1 */ + switch (lua_tointeger(L,-2)) { + case 1: + g_free((gchar *)units->singular); + units->singular = g_strdup(lua_tostring(L,-1)); + break; + case 2: + g_free((gchar *)units->plural); + units->plural = g_strdup(lua_tostring(L,-1)); + break; + default: + g_free(units->singular); + g_free(units->plural); + g_free(units); + luaL_argerror(L,idx,"The unit name table can have maximum two strings with key value 1 and 2"); + return NULL; + } + + lua_pop(L, 1); + } + + if (!units->singular) { + g_free(units->plural); + g_free(units); + luaL_argerror(L,idx,"The unit name table must have a singular entry (key value 1)"); + return NULL; + } + + return units; +} + +static const gchar* check_field_name(lua_State* L, const int abbr_idx, const enum ftenum type) { + const gchar* abbr = luaL_checkstring(L,abbr_idx); + const header_field_info* hfinfo = NULL; + + if (!abbr[0]) { + luaL_argerror(L, abbr_idx, "Empty field name abbreviation"); + return NULL; + } + + if (proto_check_field_name(abbr)) { + luaL_argerror(L, abbr_idx, "Invalid char in abbrev"); + return NULL; + } + + hfinfo = proto_registrar_get_byname(abbr); + + if (hfinfo && !ftype_similar_types(type, hfinfo->type)) { + luaL_argerror(L, abbr_idx, "A field of an incompatible ftype with this abbrev already exists"); + return NULL; + } + + return abbr; +} + +WSLUA_CONSTRUCTOR ProtoField_new(lua_State* L) { + /* Creates a new <<lua_class_ProtoField,`ProtoField`>> object to be used for a protocol field. */ +#define WSLUA_ARG_ProtoField_new_NAME 1 /* Actual name of the field (the string that + appears in the tree). */ +#define WSLUA_ARG_ProtoField_new_ABBR 2 /* Filter name of the field (the string that + is used in filters). */ +#define WSLUA_ARG_ProtoField_new_TYPE 3 /* Field Type: one of: `ftypes.BOOLEAN`, `ftypes.CHAR`, `ftypes.UINT8`, + `ftypes.UINT16`, `ftypes.UINT24`, `ftypes.UINT32`, `ftypes.UINT64`, `ftypes.INT8`, + `ftypes.INT16`, `ftypes.INT24`, `ftypes.INT32`, `ftypes.INT64`, `ftypes.FLOAT`, + `ftypes.DOUBLE` , `ftypes.ABSOLUTE_TIME`, `ftypes.RELATIVE_TIME`, `ftypes.STRING`, + `ftypes.STRINGZ`, `ftypes.UINT_STRING`, `ftypes.ETHER`, `ftypes.BYTES`, + `ftypes.UINT_BYTES`, `ftypes.IPv4`, `ftypes.IPv6`, `ftypes.IPXNET`, `ftypes.FRAMENUM`, + `ftypes.PCRE`, `ftypes.GUID`, `ftypes.OID`, `ftypes.PROTOCOL`, `ftypes.REL_OID`, + `ftypes.SYSTEM_ID`, `ftypes.EUI64` or `ftypes.NONE`. + */ +#define WSLUA_OPTARG_ProtoField_new_VALUESTRING 4 /* A table containing the text that + corresponds to the values, or a table containing tables of range string values that + corresponds to the values ({min, max, "string"}) if the base is `base.RANGE_STRING`, or a table containing unit name + for the values if base is `base.UNIT_STRING`, or one of `frametype.NONE`, `frametype.REQUEST`, + `frametype.RESPONSE`, `frametype.ACK` or `frametype.DUP_ACK` if field type is ftypes.FRAMENUM. */ +#define WSLUA_OPTARG_ProtoField_new_BASE 5 /* The representation, one of: `base.NONE`, `base.DEC`, + `base.HEX`, `base.OCT`, `base.DEC_HEX`, + `base.HEX_DEC`, `base.UNIT_STRING` or + `base.RANGE_STRING`. */ +#define WSLUA_OPTARG_ProtoField_new_MASK 6 /* The bitmask to be used. */ +#define WSLUA_OPTARG_ProtoField_new_DESCR 7 /* The description of the field. */ + + ProtoField f; + int nargs = lua_gettop(L); + const gchar* name = luaL_checkstring(L,WSLUA_ARG_ProtoField_new_NAME); + const gchar* abbr = NULL; + enum ftenum type; + enum ft_framenum_type framenum_type = FT_FRAMENUM_NONE; + range_string *rs32 = NULL; + value_string *vs32 = NULL; + val64_string *vs64 = NULL; + true_false_string *tfs = NULL; + unit_name_string *uns = NULL; + unsigned base; + guint64 mask = get_mask(L,WSLUA_OPTARG_ProtoField_new_MASK, 0x0); + const gchar *blob = luaL_optstring(L,WSLUA_OPTARG_ProtoField_new_DESCR,NULL); + gboolean base_unit_string = FALSE; + gboolean base_range_string = FALSE; + + if (!name[0]) { + WSLUA_ARG_ERROR(ProtoField_new,NAME,"cannot be an empty string"); + return 0; + } + + if (lua_isnumber(L,WSLUA_ARG_ProtoField_new_TYPE)) { + type = (enum ftenum)luaL_checkinteger(L,WSLUA_ARG_ProtoField_new_TYPE); + } else { + type = get_ftenum(luaL_checkstring(L,WSLUA_ARG_ProtoField_new_TYPE)); + } + + abbr = check_field_name(L,WSLUA_ARG_ProtoField_new_ABBR,type); + + if (lua_isnumber(L, WSLUA_OPTARG_ProtoField_new_BASE)) { + base = (unsigned)luaL_optinteger(L, WSLUA_OPTARG_ProtoField_new_BASE, BASE_NONE); + } else { + base = string_to_base(luaL_optstring(L, WSLUA_OPTARG_ProtoField_new_BASE, "BASE_NONE")); + } + + switch (type) { + case FT_FRAMENUM: + if (base != BASE_NONE) { + WSLUA_OPTARG_ERROR(ProtoField_new,BASE,"FRAMENUM must use base.NONE"); + return 0; + } + if (mask) { + WSLUA_OPTARG_ERROR(ProtoField_new,MASK,"FRAMENUM can not have a bitmask"); + return 0; + } + if (nargs >= WSLUA_OPTARG_ProtoField_new_VALUESTRING && !lua_isnil(L,WSLUA_OPTARG_ProtoField_new_VALUESTRING)) { + framenum_type = (enum ft_framenum_type) luaL_checkinteger(L, 4); + if (framenum_type >= FT_FRAMENUM_NUM_TYPES) { + WSLUA_OPTARG_ERROR(ProtoField_new,VALUESTRING,"Invalid frametype"); + return 0; + } + } + break; + case FT_CHAR: + if (nargs < WSLUA_OPTARG_ProtoField_new_BASE || lua_isnil(L, WSLUA_OPTARG_ProtoField_new_BASE)) { + base = BASE_OCT; /* Default base for characters (BASE_HEX instead?) */ + } + if (base & BASE_UNIT_STRING) { + WSLUA_OPTARG_ERROR(ProtoField_new, BASE, "Character type can not use base.UNIT_STRING"); + return 0; + } + /* FALLTHRU */ + case FT_UINT8: + case FT_UINT16: + case FT_UINT24: + case FT_UINT32: + case FT_UINT64: + case FT_INT8: + case FT_INT16: + case FT_INT24: + case FT_INT32: + case FT_INT64: + if (base & BASE_UNIT_STRING) { + base_unit_string = TRUE; + base &= ~BASE_UNIT_STRING; + } + if (base & BASE_RANGE_STRING) { + base_range_string = TRUE; + base &= ~BASE_RANGE_STRING; + } + if (base_unit_string && base_range_string) { + WSLUA_OPTARG_ERROR(ProtoField_new, BASE, "Only one of base.UNIT_STRING and base.RANGE_STRING can be specified"); + return 0; + } + if (type != FT_CHAR && base == BASE_NONE) { + base = BASE_DEC; /* Default base for integer */ + } + if (type == FT_CHAR) { + if (base != BASE_NONE && base != BASE_HEX && base != BASE_OCT) { + luaL_argerror(L, 3, "Base must be either base.NONE, base.HEX or base.OCT"); + return 0; + } + } else if ((base != BASE_DEC) && + (type == FT_INT8 || type == FT_INT16 || type == FT_INT24 || type == FT_INT32 || type == FT_INT64)) + { + WSLUA_OPTARG_ERROR(ProtoField_new,BASE,"Base must be either base.DEC or base.UNIT_STRING"); + return 0; + } else if (base < BASE_DEC || base > BASE_HEX_DEC) { + WSLUA_OPTARG_ERROR(ProtoField_new,BASE,"Base must be either base.DEC, base.HEX, base.OCT," + " base.DEC_HEX, base.HEX_DEC or base.UNIT_STRING"); + return 0; + } + if (nargs >= WSLUA_OPTARG_ProtoField_new_VALUESTRING) { + if (base_unit_string) { + uns = unit_name_string_from_table(L,WSLUA_OPTARG_ProtoField_new_VALUESTRING); + } else if (base_range_string) { + rs32 = range_string_from_table(L, WSLUA_OPTARG_ProtoField_new_VALUESTRING); + } else if (type == FT_UINT64 || type == FT_INT64) { + vs64 = val64_string_from_table(L,WSLUA_OPTARG_ProtoField_new_VALUESTRING); + } else { + vs32 = value_string_from_table(L,WSLUA_OPTARG_ProtoField_new_VALUESTRING); + } + } + if (type == FT_CHAR && base == BASE_NONE && rs32 == NULL && vs32 == NULL) { + luaL_argerror(L, 3, "Base base.NONE must be used with a valuestring"); + return 0; + } + break; + case FT_BOOLEAN: + if (mask == 0x0 && base != BASE_NONE) { + WSLUA_OPTARG_ERROR(ProtoField_new,BASE,"Base must be base.NONE if bitmask is zero."); + return 0; + } + if (mask != 0x0 && (base < 1 || base > 64)) { + WSLUA_OPTARG_ERROR(ProtoField_new,BASE,"Base must be between 1 and 64 if bitmask is non-zero."); + return 0; + } + if (nargs >= WSLUA_OPTARG_ProtoField_new_VALUESTRING && !lua_isnil(L,WSLUA_OPTARG_ProtoField_new_VALUESTRING)) { + tfs = true_false_string_from_table(L,WSLUA_OPTARG_ProtoField_new_VALUESTRING); + } + break; + case FT_ABSOLUTE_TIME: + if (base == BASE_NONE) { + base = ABSOLUTE_TIME_LOCAL; /* Default base for FT_ABSOLUTE_TIME */ + } else if (!FIELD_DISPLAY_IS_ABSOLUTE_TIME(base)) { + WSLUA_OPTARG_ERROR(ProtoField_new,BASE,"Base must be either base.LOCAL, base.UTC, or base.DOY_UTC"); + return 0; + } + if (mask) { + WSLUA_OPTARG_ERROR(ProtoField_new,MASK,"ABSOLUTE_TIME can not have a bitmask"); + return 0; + } + break; + case FT_STRING: + case FT_STRINGZ: + if (base != BASE_NONE) { + WSLUA_OPTARG_ERROR(ProtoField_new,BASE,"Display must be base.NONE"); + return 0; + } + if (mask) { + WSLUA_OPTARG_ERROR(ProtoField_new,MASK,"This type can not have a bitmask"); + return 0; + } + break; + case FT_BYTES: + case FT_UINT_BYTES: + if (base != BASE_NONE && (base < SEP_DOT || base > SEP_SPACE)) { + WSLUA_OPTARG_ERROR(ProtoField_new,BASE,"Display must be either base.NONE, base.DOT, base.DASH, base.COLON or base.SPACE"); + return 0; + } + if (mask) { + WSLUA_OPTARG_ERROR(ProtoField_new,MASK,"This type can not have a bitmask"); + return 0; + } + break; + case FT_FLOAT: + case FT_DOUBLE: + if (base & BASE_UNIT_STRING) { + base_unit_string = TRUE; + base &= ~BASE_UNIT_STRING; + } + if (nargs >= WSLUA_OPTARG_ProtoField_new_VALUESTRING) { + uns = unit_name_string_from_table(L,WSLUA_OPTARG_ProtoField_new_VALUESTRING); + } + /* FALLTHRU */ + case FT_NONE: + case FT_IPv4: + case FT_IPv6: + case FT_IPXNET: + case FT_ETHER: + case FT_RELATIVE_TIME: + case FT_GUID: + case FT_OID: + case FT_PROTOCOL: + case FT_SYSTEM_ID: + case FT_REL_OID: + case FT_EUI64: + case FT_VINES: + case FT_FCWWN: + if (base != BASE_NONE) { + WSLUA_OPTARG_ERROR(ProtoField_new,BASE,"Base must be base.NONE"); + return 0; + } + if (mask) { + WSLUA_OPTARG_ERROR(ProtoField_new,MASK,"This type can not have a bitmask"); + return 0; + } + break; + /* TODO: not handled yet */ + case FT_UINT40: + case FT_UINT48: + case FT_UINT56: + case FT_INT40: + case FT_INT48: + case FT_INT56: + case FT_IEEE_11073_SFLOAT: + case FT_IEEE_11073_FLOAT: + case FT_UINT_STRING: + case FT_AX25: + case FT_STRINGZPAD: + case FT_STRINGZTRUNC: + WSLUA_ARG_ERROR(ProtoField_new,TYPE,"Unsupported ProtoField field type"); + break; + default: + WSLUA_ARG_ERROR(ProtoField_new,TYPE,"Invalid ProtoField field type"); + break; + } + + if (base_unit_string && !uns) { + WSLUA_OPTARG_ERROR(ProtoField_new,VALUESTRING, "Base contains base.UNIT_STRING but no table was provided"); + return 0; + } + + if (base_range_string && !rs32) { + WSLUA_OPTARG_ERROR(ProtoField_new, VALUESTRING, "Base contains bas.RANGE_STRING but no table was provided") + return 0; + } + + f = g_new(wslua_field_t,1); + + f->hfid = -2; + f->ett = -1; + f->name = g_strdup(name); + f->abbrev = g_strdup(abbr); + f->type = type; + f->base = base; + if (tfs) { + f->vs = TFS(tfs); + } else if (vs32) { + f->vs = VALS(vs32); + } else if (rs32) { + f->base |= BASE_RANGE_STRING; + f->vs = RVALS(rs32); + } else if (vs64) { + /* Indicate that we are using val64_string */ + f->base |= BASE_VAL64_STRING; + f->vs = VALS64(vs64); + } else if (uns) { + f->base |= BASE_UNIT_STRING; + f->vs = uns; + } else if (framenum_type) { + f->vs = FRAMENUM_TYPE(framenum_type); + } else { + f->vs = NULL; + } + f->mask = mask; + if (blob && strcmp(blob, f->name) != 0) { + f->blob = g_strdup(blob); + } else { + f->blob = NULL; + } + + pushProtoField(L,f); + + WSLUA_RETURN(1); /* The newly created <<lua_class_ProtoField,`ProtoField`>> object. */ +} + +static int ProtoField_integer(lua_State* L, enum ftenum type) { + ProtoField f; + const gchar* abbr = check_field_name(L,1,type); + const gchar* name = luaL_optstring(L,2,abbr); + unsigned default_base = (type == FT_FRAMENUM) ? BASE_NONE : ((type == FT_CHAR) ? BASE_OCT : BASE_DEC); + unsigned base = (unsigned)luaL_optinteger(L, 3, default_base); + enum ft_framenum_type framenum_type = FT_FRAMENUM_NONE; + value_string* vs32 = NULL; + range_string* rs32 = NULL; + val64_string* vs64 = NULL; + unit_name_string* uns = NULL; + guint64 mask = get_mask(L,5,0); + const gchar* blob = luaL_optstring(L,6,NULL); + gboolean base_unit_string = FALSE; + gboolean base_range_string = FALSE; + + if (!name[0]) { + luaL_argerror(L, 2, "cannot be an empty string"); + return 0; + } + + if (type == FT_CHAR && base & BASE_UNIT_STRING) { + luaL_argerror(L, 3, "Character type can not use base.UNIT_STRING"); + return 0; + } + + if (base & BASE_UNIT_STRING) { + base_unit_string = TRUE; + base &= ~BASE_UNIT_STRING; + if (base == BASE_NONE) { + base = BASE_DEC; + } + } + + if (base & BASE_RANGE_STRING) { + base_range_string = TRUE; + base &= ~BASE_RANGE_STRING; + if (type != FT_CHAR && base == BASE_NONE) { + base = BASE_DEC; + } + } + + if (base_unit_string && base_range_string) { + luaL_argerror(L, 3, "Only one of base.RANGE_STRING and base.UNIT_STRING can be specified"); + return 0; + } + + if (lua_gettop(L) > 3 && !lua_isnil(L, 4)) { + if (type == FT_FRAMENUM) { + framenum_type = (enum ft_framenum_type) luaL_checkinteger(L, 4); + if (framenum_type >= FT_FRAMENUM_NUM_TYPES) { + luaL_argerror(L, 4, "Invalid frametype"); + return 0; + } + } else if (base_unit_string) { + uns = unit_name_string_from_table(L,4); + } else if (base_range_string) { + rs32 = range_string_from_table(L, 4); + } else if (type == FT_UINT64 || type == FT_INT64) { + vs64 = val64_string_from_table(L,4); + } else { + vs32 = value_string_from_table(L,4); + } + } + + if (type == FT_FRAMENUM) { + if (base != BASE_NONE) + luaL_argerror(L, 3, "FRAMENUM must use base.NONE"); + else if (mask) + luaL_argerror(L, 5, "FRAMENUM can not have a bitmask"); + } else if (type == FT_CHAR) { + if (base != BASE_NONE && base != BASE_HEX && base != BASE_OCT) { + luaL_argerror(L, 3, "Base must be either base.NONE, base.HEX or base.OCT"); + return 0; + } + if (base == BASE_NONE && rs32 == NULL && vs32 == NULL) { + luaL_argerror(L, 3, "Base base.NONE must be used with a valuestring"); + return 0; + } + } else if ((base != BASE_DEC) && + (type == FT_INT8 || type == FT_INT16 || type == FT_INT24 || type == FT_INT32 || type == FT_INT64)) { + luaL_argerror(L, 3, "Base must be either base.DEC or base.UNIT_STRING"); + return 0; + } else if (base < BASE_DEC || base > BASE_HEX_DEC) { + luaL_argerror(L, 3, "Base must be either base.DEC, base.HEX, base.OCT," + " base.DEC_HEX, base.HEX_DEC or base.UNIT_STRING"); + return 0; + } + + if (base_unit_string && !uns) { + luaL_argerror(L, 4, "Base contains base.UNIT_STRING but no table was given"); + return 0; + } + + if (base_range_string && !rs32) { + luaL_argerror(L, 4, "Base contains base.RANGE_STRING but no table was given"); + return 0; + } + + f = g_new(wslua_field_t,1); + + f->hfid = -2; + f->ett = -1; + f->name = g_strdup(name); + f->abbrev = g_strdup(abbr); + f->type = type; + f->base = base; + if (vs64) { + /* Indicate that we are using val64_string */ + f->base |= BASE_VAL64_STRING; + f->vs = VALS64(vs64); + } else if (rs32) { + f->base |= BASE_RANGE_STRING; + f->vs = rs32; + } else if (vs32) { + f->vs = VALS(vs32); + } else if (uns) { + f->base |= BASE_UNIT_STRING; + f->vs = uns; + } else if (framenum_type) { + f->vs = FRAMENUM_TYPE(framenum_type); + } else { + f->vs = NULL; + } + f->mask = mask; + if (blob && strcmp(blob, f->name) != 0) { + f->blob = g_strdup(blob); + } else { + f->blob = NULL; + } + + pushProtoField(L,f); + + return 1; +} + +#define PROTOFIELD_INTEGER(lower,FT) static int ProtoField_##lower(lua_State* L) { return ProtoField_integer(L,FT); } +/* _WSLUA_CONSTRUCTOR_ ProtoField_char Creates a <<lua_class_ProtoField,`ProtoField`>> of an 8-bit ASCII character. */ +/* WSLUA_ARG_ProtoField_char_ABBR Abbreviated name of the field (the string used in filters). */ +/* WSLUA_OPTARG_ProtoField_char_NAME Actual name of the field (the string that appears in the tree). */ +/* WSLUA_OPTARG_ProtoField_char_BASE One of `base.NONE`, `base.HEX`, `base.OCT` or `base.RANGE_STRING`. */ +/* WSLUA_OPTARG_ProtoField_char_VALUESTRING A table containing the text that corresponds to the values, or a table containing tables of range string values that correspond to the values ({min, max, "string"}) if the base is `base.RANGE_STRING`. */ +/* WSLUA_OPTARG_ProtoField_char_MASK Integer mask of this field. */ +/* WSLUA_OPTARG_ProtoField_char_DESC Description of the field. */ +/* _WSLUA_RETURNS_ A <<lua_class_ProtoField,`ProtoField`>> object to be added to a table set to the <<lua_class_attrib_proto_fields,`Proto.fields`>> attribute. */ + +/* _WSLUA_CONSTRUCTOR_ ProtoField_uint8 Creates a <<lua_class_ProtoField,`ProtoField`>> of an unsigned 8-bit integer (i.e., a byte). */ +/* WSLUA_ARG_ProtoField_uint8_ABBR Abbreviated name of the field (the string used in filters). */ +/* WSLUA_OPTARG_ProtoField_uint8_NAME Actual name of the field (the string that appears in the tree). */ +/* WSLUA_OPTARG_ProtoField_uint8_BASE One of `base.DEC`, `base.HEX` or `base.OCT`, `base.DEC_HEX`, `base.HEX_DEC`, `base.UNIT_STRING` or `base.RANGE_STRING`. */ +/* WSLUA_OPTARG_ProtoField_uint8_VALUESTRING A table containing the text that corresponds to the values, or a table containing tables of range string values that correspond to the values ({min, max, "string"}) if the base is `base.RANGE_STRING`, or a table containing the unit name for the values if base is `base.UNIT_STRING`. */ +/* WSLUA_OPTARG_ProtoField_uint8_MASK Integer, String or UInt64 mask of this field. */ +/* WSLUA_OPTARG_ProtoField_uint8_DESC Description of the field. */ +/* _WSLUA_RETURNS_ A <<lua_class_ProtoField,`ProtoField`>> object to be added to a table set to the <<lua_class_attrib_proto_fields,`Proto.fields`>> attribute. */ + +/* _WSLUA_CONSTRUCTOR_ ProtoField_uint16 Creates a <<lua_class_ProtoField,`ProtoField`>> of an unsigned 16-bit integer. */ +/* WSLUA_ARG_ProtoField_uint16_ABBR Abbreviated name of the field (the string used in filters). */ +/* WSLUA_OPTARG_ProtoField_uint16_NAME Actual name of the field (the string that appears in the tree). */ +/* WSLUA_OPTARG_ProtoField_uint16_BASE One of `base.DEC`, `base.HEX`, `base.OCT`, `base.DEC_HEX`, `base.HEX_DEC`, `base.UNIT_STRING` or `base.RANGE_STRING`. */ +/* WSLUA_OPTARG_ProtoField_uint16_VALUESTRING A table containing the text that corresponds to the values, or a table containing tables of range string values that correspond to the values ({min, max, "string"}) if the base is `base.RANGE_STRING`, or a table containing unit name for the values if base is `base.UNIT_STRING`. */ +/* WSLUA_OPTARG_ProtoField_uint16_MASK Integer, String or UInt64 mask of this field. */ +/* WSLUA_OPTARG_ProtoField_uint16_DESC Description of the field. */ +/* _WSLUA_RETURNS_ A <<lua_class_ProtoField,`ProtoField`>> object to be added to a table set to the <<lua_class_attrib_proto_fields,`Proto.fields`>> attribute. */ + +/* _WSLUA_CONSTRUCTOR_ ProtoField_uint24 Creates a <<lua_class_ProtoField,`ProtoField`>> of an unsigned 24-bit integer. */ +/* WSLUA_ARG_ProtoField_uint24_ABBR Abbreviated name of the field (the string used in filters). */ +/* WSLUA_OPTARG_ProtoField_uint24_NAME Actual name of the field (the string that appears in the tree). */ +/* WSLUA_OPTARG_ProtoField_uint24_BASE One of `base.DEC`, `base.HEX`, `base.OCT`, `base.DEC_HEX`, `base.HEX_DEC`, `base.UNIT_STRING`, or `base.RANGE_STRING`. */ +/* WSLUA_OPTARG_ProtoField_uint24_VALUESTRING A table containing the text that corresponds to the values, or a table containing tables of range string values that correspond to the values ({min, max, "string"}) if the base is `base.RANGE_STRING`, or a table containing the unit name for the values if base is `base.UNIT_STRING`. */ +/* WSLUA_OPTARG_ProtoField_uint24_MASK Integer, String or UInt64 mask of this field. */ +/* WSLUA_OPTARG_ProtoField_uint24_DESC Description of the field. */ +/* _WSLUA_RETURNS_ A <<lua_class_ProtoField,`ProtoField`>> object to be added to a table set to the <<lua_class_attrib_proto_fields,`Proto.fields`>> attribute. */ + +/* _WSLUA_CONSTRUCTOR_ ProtoField_uint32 Creates a <<lua_class_ProtoField,`ProtoField`>> of an unsigned 32-bit integer. */ +/* WSLUA_ARG_ProtoField_uint32_ABBR Abbreviated name of the field (the string used in filters). */ +/* WSLUA_OPTARG_ProtoField_uint32_NAME Actual name of the field (the string that appears in the tree). */ +/* WSLUA_OPTARG_ProtoField_uint32_BASE One of `base.DEC`, `base.HEX`, `base.OCT`, `base.DEC_HEX`, `base.HEX_DEC`, `base.UNIT_STRING`, or `base.RANGE_STRING`. */ +/* WSLUA_OPTARG_ProtoField_uint32_VALUESTRING A table containing the text that corresponds to the values, or a table containing tables of range string values that correspond to the values ({min, max, "string"}) if the base is `base.RANGE_STRING`, or a table containing the unit name for the values if base is `base.UNIT_STRING`. */ +/* WSLUA_OPTARG_ProtoField_uint32_MASK Integer, String or UInt64 mask of this field. */ +/* WSLUA_OPTARG_ProtoField_uint32_DESC Description of the field. */ +/* _WSLUA_RETURNS_ A <<lua_class_ProtoField,`ProtoField`>> object to be added to a table set to the <<lua_class_attrib_proto_fields,`Proto.fields`>> attribute. */ + +/* _WSLUA_CONSTRUCTOR_ ProtoField_uint64 Creates a <<lua_class_ProtoField,`ProtoField`>> of an unsigned 64-bit integer. */ +/* WSLUA_ARG_ProtoField_uint64_ABBR Abbreviated name of the field (the string used in filters). */ +/* WSLUA_OPTARG_ProtoField_uint64_NAME Actual name of the field (the string that appears in the tree). */ +/* WSLUA_OPTARG_ProtoField_uint64_BASE One of `base.DEC`, `base.HEX`, `base.OCT`, `base.DEC_HEX`, `base.HEX_DEC`, `base.UNIT_STRING`, or `base.RANGE_STRING`. */ +/* WSLUA_OPTARG_ProtoField_uint64_VALUESTRING A table containing the text that corresponds to the values, or a table containing tables of range string values that correspond to the values ({min, max, "string"}) if the base is `base.RANGE_STRING`, or a table containing the unit name for the values if base is `base.UNIT_STRING`. */ +/* WSLUA_OPTARG_ProtoField_uint64_MASK Integer, String or UInt64 mask of this field. */ +/* WSLUA_OPTARG_ProtoField_uint64_DESC Description of the field. */ +/* _WSLUA_RETURNS_ A <<lua_class_ProtoField,`ProtoField`>> object to be added to a table set to the <<lua_class_attrib_proto_fields,`Proto.fields`>> attribute. */ + +/* _WSLUA_CONSTRUCTOR_ ProtoField_int8 Creates a <<lua_class_ProtoField,`ProtoField`>> of a signed 8-bit integer (i.e., a byte). */ +/* WSLUA_ARG_ProtoField_int8_ABBR Abbreviated name of the field (the string used in filters). */ +/* WSLUA_OPTARG_ProtoField_int8_NAME Actual name of the field (the string that appears in the tree). */ +/* WSLUA_OPTARG_ProtoField_int8_BASE One of `base.DEC`, `base.UNIT_STRING`, or `base.RANGE_STRING`. */ +/* WSLUA_OPTARG_ProtoField_int8_VALUESTRING A table containing the text that corresponds to the values, or a table containing tables of range string values that correspond to the values ({min, max, "string"}) if the base is `base.RANGE_STRING`, or a table containing unit name for the values if base is `base.UNIT_STRING`. */ +/* WSLUA_OPTARG_ProtoField_int8_MASK Integer, String or UInt64 mask of this field. */ +/* WSLUA_OPTARG_ProtoField_int8_DESC Description of the field. */ +/* _WSLUA_RETURNS_ A <<lua_class_ProtoField,`ProtoField`>> object to be added to a table set to the <<lua_class_attrib_proto_fields,`Proto.fields`>> attribute. */ + +/* _WSLUA_CONSTRUCTOR_ ProtoField_int16 Creates a <<lua_class_ProtoField,`ProtoField`>> of a signed 16-bit integer. */ +/* WSLUA_ARG_ProtoField_int16_ABBR Abbreviated name of the field (the string used in filters). */ +/* WSLUA_OPTARG_ProtoField_int16_NAME Actual name of the field (the string that appears in the tree). */ +/* WSLUA_OPTARG_ProtoField_int16_BASE One of `base.DEC`, `base.UNIT_STRING`, or `base.RANGE_STRING`. */ +/* WSLUA_OPTARG_ProtoField_int16_VALUESTRING A table containing the text that corresponds to the values, or a table containing tables of range string values that correspond to the values ({min, max, "string"}) if the base is `base.RANGE_STRING`, or a table containing unit name for the values if base is `base.UNIT_STRING`. */ +/* WSLUA_OPTARG_ProtoField_int16_MASK Integer, String or UInt64 mask of this field. */ +/* WSLUA_OPTARG_ProtoField_int16_DESC Description of the field. */ +/* _WSLUA_RETURNS_ A <<lua_class_ProtoField,`ProtoField`>> object to be added to a table set to the <<lua_class_attrib_proto_fields,`Proto.fields`>> attribute. */ + +/* _WSLUA_CONSTRUCTOR_ ProtoField_int24 Creates a <<lua_class_ProtoField,`ProtoField`>> of a signed 24-bit integer. */ +/* WSLUA_ARG_ProtoField_int24_ABBR Abbreviated name of the field (the string used in filters). */ +/* WSLUA_OPTARG_ProtoField_int24_NAME Actual name of the field (the string that appears in the tree). */ +/* WSLUA_OPTARG_ProtoField_int24_BASE One of `base.DEC`, `base.UNIT_STRING`, or `base.RANGE_STRING`. */ +/* WSLUA_OPTARG_ProtoField_int24_VALUESTRING A table containing the text that corresponds to the values, or a table containing tables of range string values that correspond to the values ({min, max, "string"}) if the base is `base.RANGE_STRING`, or a table containing unit name for the values if base is `base.UNIT_STRING`. */ +/* WSLUA_OPTARG_ProtoField_int24_MASK Integer, String or UInt64 mask of this field. */ +/* WSLUA_OPTARG_ProtoField_int24_DESC Description of the field. */ +/* _WSLUA_RETURNS_ A <<lua_class_ProtoField,`ProtoField`>> object to be added to a table set to the <<lua_class_attrib_proto_fields,`Proto.fields`>> attribute. */ + +/* _WSLUA_CONSTRUCTOR_ ProtoField_int32 Creates a <<lua_class_ProtoField,`ProtoField`>> of a signed 32-bit integer. */ +/* WSLUA_ARG_ProtoField_int32_ABBR Abbreviated name of the field (the string used in filters). */ +/* WSLUA_OPTARG_ProtoField_int32_NAME Actual name of the field (the string that appears in the tree). */ +/* WSLUA_OPTARG_ProtoField_int32_BASE One of `base.DEC`, `base.UNIT_STRING`, or `base.RANGE_STRING`. */ +/* WSLUA_OPTARG_ProtoField_int32_VALUESTRING A table containing the text that corresponds to the values, or a table containing tables of range string values that correspond to the values ({min, max, "string"}) if the base is `base.RANGE_STRING`, or a table containing unit name for the values if base is `base.UNIT_STRING`. */ +/* WSLUA_OPTARG_ProtoField_int32_MASK Integer, String or UInt64 mask of this field. */ +/* WSLUA_OPTARG_ProtoField_int32_DESC Description of the field. */ +/* _WSLUA_RETURNS_ A <<lua_class_ProtoField,`ProtoField`>> object to be added to a table set to the <<lua_class_attrib_proto_fields,`Proto.fields`>> attribute. */ + +/* _WSLUA_CONSTRUCTOR_ ProtoField_int64 Creates a <<lua_class_ProtoField,`ProtoField`>> of a signed 64-bit integer. */ +/* WSLUA_ARG_ProtoField_int64_ABBR Abbreviated name of the field (the string used in filters). */ +/* WSLUA_OPTARG_ProtoField_int64_NAME Actual name of the field (the string that appears in the tree). */ +/* WSLUA_OPTARG_ProtoField_int64_BASE One of `base.DEC`, `base.UNIT_STRING`, or `base.RANGE_STRING`. */ +/* WSLUA_OPTARG_ProtoField_int64_VALUESTRING A table containing the text that corresponds to the values, or a table containing tables of range string values that correspond to the values ({min, max, "string"}) if the base is `base.RANGE_STRING`, or a table containing unit name for the values if base is `base.UNIT_STRING`. */ +/* WSLUA_OPTARG_ProtoField_int64_MASK Integer, String or UInt64 mask of this field. */ +/* WSLUA_OPTARG_ProtoField_int64_DESC Description of the field. */ +/* _WSLUA_RETURNS_ A <<lua_class_ProtoField,`ProtoField`>> object to be added to a table set to the <<lua_class_attrib_proto_fields,`Proto.fields`>> attribute. */ + +/* _WSLUA_CONSTRUCTOR_ ProtoField_framenum Creates a <<lua_class_ProtoField,`ProtoField`>> for a frame number (for hyperlinks between frames). */ +/* WSLUA_ARG_ProtoField_framenum_ABBR Abbreviated name of the field (the string used in filters). */ +/* WSLUA_OPTARG_ProtoField_framenum_NAME Actual name of the field (the string that appears in the tree). */ +/* WSLUA_OPTARG_ProtoField_framenum_BASE Only `base.NONE` is supported for framenum. */ +/* WSLUA_OPTARG_ProtoField_framenum_FRAMETYPE One of `frametype.NONE`, `frametype.REQUEST`, `frametype.RESPONSE`, `frametype.ACK` or `frametype.DUP_ACK`. */ +/* WSLUA_OPTARG_ProtoField_framenum_MASK Integer, String or UInt64 mask of this field, which must be 0 for framenum. */ +/* WSLUA_OPTARG_ProtoField_framenum_DESC Description of the field. */ +/* _WSLUA_RETURNS_ A <<lua_class_ProtoField,`ProtoField`>> object to be added to a table set to the <<lua_class_attrib_proto_fields,`Proto.fields`>> attribute. */ + +PROTOFIELD_INTEGER(char,FT_CHAR) +PROTOFIELD_INTEGER(uint8,FT_UINT8) +PROTOFIELD_INTEGER(uint16,FT_UINT16) +PROTOFIELD_INTEGER(uint24,FT_UINT24) +PROTOFIELD_INTEGER(uint32,FT_UINT32) +PROTOFIELD_INTEGER(uint64,FT_UINT64) +PROTOFIELD_INTEGER(int8,FT_INT8) +PROTOFIELD_INTEGER(int16,FT_INT16) +PROTOFIELD_INTEGER(int24,FT_INT24) +PROTOFIELD_INTEGER(int32,FT_INT32) +PROTOFIELD_INTEGER(int64,FT_INT64) +PROTOFIELD_INTEGER(framenum,FT_FRAMENUM) + +static int ProtoField_boolean(lua_State* L, enum ftenum type) { + ProtoField f; + const gchar* abbr = check_field_name(L,1,type); + const gchar* name = luaL_optstring(L,2,abbr); + unsigned base = (unsigned)luaL_optinteger(L, 3, BASE_NONE); + true_false_string* tfs = NULL; + guint64 mask = get_mask(L,5,0); + const gchar* blob = luaL_optstring(L,6,NULL); + + if (!name[0]) { + luaL_argerror(L, 2, "cannot be an empty string"); + return 0; + } + + if (mask == 0x0 && base != BASE_NONE) { + luaL_argerror(L,3,"Fieldbase (fielddisplay) must be base.NONE" + " if bitmask is zero."); + return 0; + } + + if (mask != 0x0 && (base < 1 || base > 64)) { + luaL_argerror(L,3,"Fieldbase (fielddisplay) must be between 1 and 64" + " if bitmask is non-zero."); + return 0; + } + + if (lua_gettop(L) > 3 && !lua_isnil(L,4)) { + tfs = true_false_string_from_table(L,4); + } + + f = g_new(wslua_field_t,1); + + f->hfid = -2; + f->ett = -1; + f->name = g_strdup(name); + f->abbrev = g_strdup(abbr); + f->type = type; + f->vs = TFS(tfs); + f->base = base; + f->mask = mask; + if (blob && strcmp(blob, f->name) != 0) { + f->blob = g_strdup(blob); + } else { + f->blob = NULL; + } + + pushProtoField(L,f); + + return 1; +} + +#define PROTOFIELD_BOOL(lower,FT) static int ProtoField_##lower(lua_State* L) { return ProtoField_boolean(L,FT); } +/* _WSLUA_CONSTRUCTOR_ ProtoField_bool Creates a <<lua_class_ProtoField,`ProtoField`>> for a boolean true/false value. */ +/* WSLUA_ARG_ProtoField_bool_ABBR Abbreviated name of the field (the string used in filters). */ +/* WSLUA_OPTARG_ProtoField_bool_NAME Actual name of the field (the string that appears in the tree). */ +/* WSLUA_OPTARG_ProtoField_bool_DISPLAY How wide the parent bitfield is (`base.NONE` is used for NULL-value). */ +/* WSLUA_OPTARG_ProtoField_bool_VALUESTRING A table containing the text that corresponds to the values. */ +/* WSLUA_OPTARG_ProtoField_bool_MASK Integer, String or UInt64 mask of this field. */ +/* WSLUA_OPTARG_ProtoField_bool_DESC Description of the field. */ +/* _WSLUA_RETURNS_ A <<lua_class_ProtoField,`ProtoField`>> object to be added to a table set to the <<lua_class_attrib_proto_fields,`Proto.fields`>> attribute. */ + +PROTOFIELD_BOOL(bool,FT_BOOLEAN) + +static int ProtoField_time(lua_State* L,enum ftenum type) { + ProtoField f; + const gchar* abbr = check_field_name(L,1,type); + const gchar* name = luaL_optstring(L,2,abbr); + unsigned base = (unsigned)luaL_optinteger(L,3,ABSOLUTE_TIME_LOCAL); + const gchar* blob = luaL_optstring(L,4,NULL); + + if (!name[0]) { + luaL_argerror(L, 2, "cannot be an empty string"); + return 0; + } + + if (type == FT_ABSOLUTE_TIME) { + if (!FIELD_DISPLAY_IS_ABSOLUTE_TIME(base)) { + luaL_argerror(L, 3, "Base must be either base.LOCAL, base.UTC, or base.DOY_UTC"); + return 0; + } + } + + f = g_new(wslua_field_t,1); + + f->hfid = -2; + f->ett = -1; + f->name = g_strdup(name); + f->abbrev = g_strdup(abbr); + f->type = type; + f->vs = NULL; + f->base = base; + f->mask = 0; + if (blob && strcmp(blob, f->name) != 0) { + f->blob = g_strdup(blob); + } else { + f->blob = NULL; + } + + pushProtoField(L,f); + + return 1; +} + +#define PROTOFIELD_TIME(lower,FT) static int ProtoField_##lower(lua_State* L) { return ProtoField_time(L,FT); } +/* _WSLUA_CONSTRUCTOR_ ProtoField_absolute_time Creates a <<lua_class_ProtoField,`ProtoField`>> of a time_t structure value. */ +/* WSLUA_ARG_ProtoField_absolute_time_ABBR Abbreviated name of the field (the string used in filters). */ +/* WSLUA_OPTARG_ProtoField_absolute_time_NAME Actual name of the field (the string that appears in the tree). */ +/* WSLUA_OPTARG_ProtoField_absolute_time_BASE One of `base.LOCAL`, `base.UTC` or `base.DOY_UTC`. */ +/* WSLUA_OPTARG_ProtoField_absolute_time_DESC Description of the field. */ +/* _WSLUA_RETURNS_ A <<lua_class_ProtoField,`ProtoField`>> object to be added to a table set to the <<lua_class_attrib_proto_fields,`Proto.fields`>> attribute. */ + +/* _WSLUA_CONSTRUCTOR_ ProtoField_relative_time Creates a <<lua_class_ProtoField,`ProtoField`>> of a time_t structure value. */ +/* WSLUA_ARG_ProtoField_relative_time_ABBR Abbreviated name of the field (the string used in filters). */ +/* WSLUA_OPTARG_ProtoField_relative_time_NAME Actual name of the field (the string that appears in the tree). */ +/* WSLUA_OPTARG_ProtoField_relative_time_DESC Description of the field. */ +/* _WSLUA_RETURNS_ A <<lua_class_ProtoField,`ProtoField`>> object to be added to a table set to the <<lua_class_attrib_proto_fields,`Proto.fields`>> attribute. */ + + +PROTOFIELD_TIME(absolute_time,FT_ABSOLUTE_TIME) + +static int ProtoField_floating(lua_State* L,enum ftenum type) { + ProtoField f; + const gchar* abbr = check_field_name(L,1,type); + const gchar* name = luaL_optstring(L,2,abbr); + unit_name_string* uns = NULL; + const gchar* blob; + + if (!name[0]) { + luaL_argerror(L, 2, "cannot be an empty string"); + return 0; + } + + if (lua_istable(L, 3)) { + uns = unit_name_string_from_table(L,3); + blob = luaL_optstring(L,4,NULL); + } else { + blob = luaL_optstring(L,3,NULL); + } + + f = g_new(wslua_field_t,1); + + f->hfid = -2; + f->ett = -1; + f->name = g_strdup(name); + f->abbrev = g_strdup(abbr); + f->type = type; + if (uns) { + f->vs = uns; + f->base = BASE_NONE | BASE_UNIT_STRING; + } else { + f->vs = NULL; + f->base = BASE_NONE; + } + f->mask = 0; + if (blob && strcmp(blob, f->name) != 0) { + f->blob = g_strdup(blob); + } else { + f->blob = NULL; + } + + pushProtoField(L,f); + + return 1; +} + +#define PROTOFIELD_FLOATING(lower,FT) static int ProtoField_##lower(lua_State* L) { return ProtoField_floating(L,FT); } +/* _WSLUA_CONSTRUCTOR_ ProtoField_float Creates a <<lua_class_ProtoField,`ProtoField`>> of a floating point number (4 bytes). */ +/* WSLUA_ARG_ProtoField_float_ABBR Abbreviated name of the field (the string used in filters). */ +/* WSLUA_OPTARG_ProtoField_float_NAME Actual name of the field (the string that appears in the tree). */ +/* WSLUA_OPTARG_ProtoField_float_VALUESTRING A table containing unit name for the values. */ +/* WSLUA_OPTARG_ProtoField_float_DESC Description of the field. */ +/* _WSLUA_RETURNS_ A <<lua_class_ProtoField,`ProtoField`>> object to be added to a table set to the <<lua_class_attrib_proto_fields,`Proto.fields`>> attribute. */ + +/* _WSLUA_CONSTRUCTOR_ ProtoField_double Creates a <<lua_class_ProtoField,`ProtoField`>> of a double-precision floating point (8 bytes). */ +/* WSLUA_ARG_ProtoField_double_ABBR Abbreviated name of the field (the string used in filters). */ +/* WSLUA_OPTARG_ProtoField_double_NAME Actual name of the field (the string that appears in the tree). */ +/* WSLUA_OPTARG_ProtoField_double_VALUESTRING A table containing unit name for the values. */ +/* WSLUA_OPTARG_ProtoField_double_DESC Description of the field. */ +/* _WSLUA_RETURNS_ A <<lua_class_ProtoField,`ProtoField`>> object to be added to a table set to the <<lua_class_attrib_proto_fields,`Proto.fields`>> attribute. */ + +PROTOFIELD_FLOATING(float,FT_FLOAT) +PROTOFIELD_FLOATING(double,FT_DOUBLE) + +static int ProtoField_other_display(lua_State* L,enum ftenum type) { + ProtoField f; + const gchar* abbr = check_field_name(L,1,type); + const gchar* name = luaL_optstring(L,2,abbr); + unsigned base = BASE_NONE; + const gchar* blob; + + if (!name[0]) { + luaL_argerror(L, 2, "cannot be an empty string"); + return 0; + } + + if (lua_isnumber(L, 3)) { + base = (unsigned)luaL_optinteger(L,3,BASE_NONE); + if (type == FT_STRING || type == FT_STRINGZ) { + if (base != BASE_NONE) { + luaL_argerror(L, 3, "Display must be base.NONE"); + return 0; + } + } else if (type == FT_BYTES || type == FT_UINT_BYTES) { + if (base != BASE_NONE && (base < SEP_DOT || base > SEP_SPACE)) { + luaL_argerror(L, 3, "Display must be either base.NONE, base.DOT, base.DASH, base.COLON or base.SPACE"); + return 0; + } + } + + blob = luaL_optstring(L,4,NULL); + } else { + blob = luaL_optstring(L,3,NULL); + } + + f = g_new(wslua_field_t,1); + + f->hfid = -2; + f->ett = -1; + f->name = g_strdup(name); + f->abbrev = g_strdup(abbr); + f->type = type; + f->vs = NULL; + f->base = base; + f->mask = 0; + if (blob && strcmp(blob, f->name) != 0) { + f->blob = g_strdup(blob); + } else { + f->blob = NULL; + } + + pushProtoField(L,f); + + return 1; +} + +#define PROTOFIELD_OTHER_DISPLAY(lower,FT) static int ProtoField_##lower(lua_State* L) { return ProtoField_other_display(L,FT); } +/* _WSLUA_CONSTRUCTOR_ ProtoField_string Creates a <<lua_class_ProtoField,`ProtoField`>> of a string value. */ +/* WSLUA_ARG_ProtoField_string_ABBR Abbreviated name of the field (the string used in filters). */ +/* WSLUA_OPTARG_ProtoField_string_NAME Actual name of the field (the string that appears in the tree). */ +/* WSLUA_OPTARG_ProtoField_string_DISPLAY One of `base.ASCII` or `base.UNICODE`. */ +/* WSLUA_OPTARG_ProtoField_string_DESC Description of the field. */ +/* _WSLUA_RETURNS_ A <<lua_class_ProtoField,`ProtoField`>> object to be added to a table set to the <<lua_class_attrib_proto_fields,`Proto.fields`>> attribute. */ + +/* _WSLUA_CONSTRUCTOR_ ProtoField_stringz Creates a <<lua_class_ProtoField,`ProtoField`>> of a zero-terminated string value. */ +/* WSLUA_ARG_ProtoField_stringz_ABBR Abbreviated name of the field (the string used in filters). */ +/* WSLUA_OPTARG_ProtoField_stringz_NAME Actual name of the field (the string that appears in the tree). */ +/* WSLUA_OPTARG_ProtoField_stringz_DISPLAY One of `base.ASCII` or `base.UNICODE`. */ +/* WSLUA_OPTARG_ProtoField_stringz_DESC Description of the field. */ +/* _WSLUA_RETURNS_ A <<lua_class_ProtoField,`ProtoField`>> object to be added to a table set to the <<lua_class_attrib_proto_fields,`Proto.fields`>> attribute. */ + +/* _WSLUA_CONSTRUCTOR_ ProtoField_bytes Creates a <<lua_class_ProtoField,`ProtoField`>> for an arbitrary number of bytes. */ +/* WSLUA_ARG_ProtoField_bytes_ABBR Abbreviated name of the field (the string used in filters). */ +/* WSLUA_OPTARG_ProtoField_bytes_NAME Actual name of the field (the string that appears in the tree). */ +/* WSLUA_OPTARG_ProtoField_bytes_DISPLAY One of `base.NONE`, `base.DOT`, `base.DASH`, `base.COLON` or `base.SPACE`. */ +/* WSLUA_OPTARG_ProtoField_bytes_DESC Description of the field. */ +/* _WSLUA_RETURNS_ A <<lua_class_ProtoField,`ProtoField`>> object to be added to a table set to the <<lua_class_attrib_proto_fields,`Proto.fields`>> attribute. */ + +/* _WSLUA_CONSTRUCTOR_ ProtoField_ubytes Creates a <<lua_class_ProtoField,`ProtoField`>> for an arbitrary number of unsigned bytes. */ +/* WSLUA_ARG_ProtoField_ubytes_ABBR Abbreviated name of the field (the string used in filters). */ +/* WSLUA_OPTARG_ProtoField_ubytes_NAME Actual name of the field (the string that appears in the tree). */ +/* WSLUA_OPTARG_ProtoField_ubytes_DISPLAY One of `base.NONE`, `base.DOT`, `base.DASH`, `base.COLON` or `base.SPACE`. */ +/* WSLUA_OPTARG_ProtoField_ubytes_DESC Description of the field. */ +/* _WSLUA_RETURNS_ A <<lua_class_ProtoField,`ProtoField`>> object to be added to a table set to the <<lua_class_attrib_proto_fields,`Proto.fields`>> attribute. */ + + +PROTOFIELD_OTHER_DISPLAY(string,FT_STRING) +PROTOFIELD_OTHER_DISPLAY(stringz,FT_STRINGZ) +PROTOFIELD_OTHER_DISPLAY(bytes,FT_BYTES) +PROTOFIELD_OTHER_DISPLAY(ubytes,FT_UINT_BYTES) + +static int ProtoField_other(lua_State* L,enum ftenum type) { + ProtoField f; + const gchar* abbr = check_field_name(L,1,type); + const gchar* name = luaL_optstring(L,2,abbr); + const gchar* blob = luaL_optstring(L,3,NULL); + + if (!name[0]) { + luaL_argerror(L, 2, "cannot be an empty string"); + return 0; + } + + f = g_new(wslua_field_t,1); + + f->hfid = -2; + f->ett = -1; + f->name = g_strdup(name); + f->abbrev = g_strdup(abbr); + f->type = type; + f->vs = NULL; + f->base = BASE_NONE; + f->mask = 0; + if (blob && strcmp(blob, f->name) != 0) { + f->blob = g_strdup(blob); + } else { + f->blob = NULL; + } + + pushProtoField(L,f); + + return 1; +} + +#define PROTOFIELD_OTHER(lower,FT) static int ProtoField_##lower(lua_State* L) { return ProtoField_other(L,FT); } +/* _WSLUA_CONSTRUCTOR_ ProtoField_none Creates a <<lua_class_ProtoField,`ProtoField`>> of an unstructured type. */ +/* WSLUA_ARG_ProtoField_none_ABBR Abbreviated name of the field (the string used in filters). */ +/* WSLUA_OPTARG_ProtoField_none_NAME Actual name of the field (the string that appears in the tree). */ +/* WSLUA_OPTARG_ProtoField_none_DESC Description of the field. */ +/* _WSLUA_RETURNS_ A <<lua_class_ProtoField,`ProtoField`>> object to be added to a table set to the <<lua_class_attrib_proto_fields,`Proto.fields`>> attribute. */ + +/* _WSLUA_CONSTRUCTOR_ ProtoField_ipv4 Creates a <<lua_class_ProtoField,`ProtoField`>> of an IPv4 address (4 bytes). */ +/* WSLUA_ARG_ProtoField_ipv4_ABBR Abbreviated name of the field (the string used in filters). */ +/* WSLUA_OPTARG_ProtoField_ipv4_NAME Actual name of the field (the string that appears in the tree). */ +/* WSLUA_OPTARG_ProtoField_ipv4_DESC Description of the field. */ +/* _WSLUA_RETURNS_ A <<lua_class_ProtoField,`ProtoField`>> object to be added to a table set to the <<lua_class_attrib_proto_fields,`Proto.fields`>> attribute. */ + +/* _WSLUA_CONSTRUCTOR_ ProtoField_ipv6 Creates a <<lua_class_ProtoField,`ProtoField`>> of an IPv6 address (16 bytes). */ +/* WSLUA_ARG_ProtoField_ipv6_ABBR Abbreviated name of the field (the string used in filters). */ +/* WSLUA_OPTARG_ProtoField_ipv6_NAME Actual name of the field (the string that appears in the tree). */ +/* WSLUA_OPTARG_ProtoField_ipv6_DESC Description of the field. */ +/* _WSLUA_RETURNS_ A <<lua_class_ProtoField,`ProtoField`>> object to be added to a table set to the <<lua_class_attrib_proto_fields,`Proto.fields`>> attribute. */ + +/* _WSLUA_CONSTRUCTOR_ ProtoField_ether Creates a <<lua_class_ProtoField,`ProtoField`>> of an Ethernet address (6 bytes). */ +/* WSLUA_ARG_ProtoField_ether_ABBR Abbreviated name of the field (the string used in filters). */ +/* WSLUA_OPTARG_ProtoField_ether_NAME Actual name of the field (the string that appears in the tree). */ +/* WSLUA_OPTARG_ProtoField_ether_DESC Description of the field. */ +/* _WSLUA_RETURNS_ A <<lua_class_ProtoField,`ProtoField`>> object to be added to a table set to the <<lua_class_attrib_proto_fields,`Proto.fields`>> attribute. */ + +/* _WSLUA_CONSTRUCTOR_ ProtoField_guid Creates a <<lua_class_ProtoField,`ProtoField`>> for a Globally Unique IDentifier (GUID). */ +/* WSLUA_ARG_ProtoField_guid_ABBR Abbreviated name of the field (the string used in filters). */ +/* WSLUA_OPTARG_ProtoField_guid_NAME Actual name of the field (the string that appears in the tree). */ +/* WSLUA_OPTARG_ProtoField_guid_DESC Description of the field. */ +/* _WSLUA_RETURNS_ A <<lua_class_ProtoField,`ProtoField`>> object to be added to a table set to the <<lua_class_attrib_proto_fields,`Proto.fields`>> attribute. */ + +/* _WSLUA_CONSTRUCTOR_ ProtoField_oid Creates a <<lua_class_ProtoField,`ProtoField`>> for an ASN.1 Organizational IDentified (OID). */ +/* WSLUA_ARG_ProtoField_oid_ABBR Abbreviated name of the field (the string used in filters). */ +/* WSLUA_OPTARG_ProtoField_oid_NAME Actual name of the field (the string that appears in the tree). */ +/* WSLUA_OPTARG_ProtoField_oid_DESC Description of the field. */ +/* _WSLUA_RETURNS_ A <<lua_class_ProtoField,`ProtoField`>> object to be added to a table set to the <<lua_class_attrib_proto_fields,`Proto.fields`>> attribute. */ + +/* _WSLUA_CONSTRUCTOR_ ProtoField_protocol Creates a <<lua_class_ProtoField,`ProtoField`>> for a sub-protocol. Since 1.99.9. */ +/* WSLUA_ARG_ProtoField_protocol_ABBR Abbreviated name of the field (the string used in filters). */ +/* WSLUA_OPTARG_ProtoField_protocol_NAME Actual name of the field (the string that appears in the tree). */ +/* WSLUA_OPTARG_ProtoField_protocol_DESC Description of the field. */ +/* _WSLUA_RETURNS_ A <<lua_class_ProtoField,`ProtoField`>> object to be added to a table set to the <<lua_class_attrib_proto_fields,`Proto.fields`>> attribute. */ + +/* _WSLUA_CONSTRUCTOR_ ProtoField_rel_oid Creates a <<lua_class_ProtoField,`ProtoField`>> for an ASN.1 Relative-OID. */ +/* WSLUA_ARG_ProtoField_rel_oid_ABBR Abbreviated name of the field (the string used in filters). */ +/* WSLUA_OPTARG_ProtoField_rel_oid_NAME Actual name of the field (the string that appears in the tree). */ +/* WSLUA_OPTARG_ProtoField_rel_oid_DESC Description of the field. */ +/* _WSLUA_RETURNS_ A <<lua_class_ProtoField,`ProtoField`>> object to be added to a table set to the <<lua_class_attrib_proto_fields,`Proto.fields`>> attribute. */ + +/* _WSLUA_CONSTRUCTOR_ ProtoField_systemid Creates a <<lua_class_ProtoField,`ProtoField`>> for an OSI System ID. */ +/* WSLUA_ARG_ProtoField_systemid_ABBR Abbreviated name of the field (the string used in filters). */ +/* WSLUA_OPTARG_ProtoField_systemid_NAME Actual name of the field (the string that appears in the tree). */ +/* WSLUA_OPTARG_ProtoField_systemid_DESC Description of the field. */ +/* _WSLUA_RETURNS_ A <<lua_class_ProtoField,`ProtoField`>> object to be added to a table set to the <<lua_class_attrib_proto_fields,`Proto.fields`>> attribute. */ + +/* _WSLUA_CONSTRUCTOR_ ProtoField_eui64 Creates a <<lua_class_ProtoField,`ProtoField`>> for an EUI64. */ +/* WSLUA_ARG_ProtoField_eui64_ABBR Abbreviated name of the field (the string used in filters). */ +/* WSLUA_OPTARG_ProtoField_eui64_NAME Actual name of the field (the string that appears in the tree). */ +/* WSLUA_OPTARG_ProtoField_eui64_DESC Description of the field. */ +/* _WSLUA_RETURNS_ A <<lua_class_ProtoField,`ProtoField`>> object to be added to a table set to the <<lua_class_attrib_proto_fields,`Proto.fields`>> attribute. */ + +PROTOFIELD_OTHER(none,FT_NONE) +PROTOFIELD_OTHER(ipv4,FT_IPv4) +PROTOFIELD_OTHER(ipv6,FT_IPv6) +PROTOFIELD_OTHER(ipx,FT_IPXNET) +PROTOFIELD_OTHER(ether,FT_ETHER) +PROTOFIELD_OTHER(relative_time,FT_RELATIVE_TIME) +PROTOFIELD_OTHER(guid,FT_GUID) +PROTOFIELD_OTHER(oid,FT_OID) +PROTOFIELD_OTHER(protocol,FT_PROTOCOL) +PROTOFIELD_OTHER(rel_oid,FT_REL_OID) +PROTOFIELD_OTHER(systemid,FT_SYSTEM_ID) +PROTOFIELD_OTHER(eui64,FT_EUI64) + +WSLUA_METAMETHOD ProtoField__tostring(lua_State* L) { + /* Returns a string with info about a protofield (for debugging purposes). */ + ProtoField f = checkProtoField(L,1); + gchar* s = ws_strdup_printf("ProtoField(%i): %s %s %s %s %p %.16" PRIu64 "x %s", + f->hfid,f->name,f->abbrev, + ftenum_to_string(f->type), + base_to_string(f->base), + f->vs,f->mask,f->blob); + lua_pushstring(L,s); + g_free(s); + return 1; +} + +static int ProtoField__gc(lua_State* L) { + ProtoField f = toProtoField(L,1); + + /* + * Initialized to -2 in ProtoField_new, + * changed to -1 in Proto_commit and subsequently replaced by + * an allocated number in proto_register_field_array. + * Reset to -2 again in wslua_deregister_protocols. + */ + if (f->hfid != -2) { + /* Only free unregistered and deregistered ProtoField */ + return 0; + } + + /* Note: name, abbrev, blob and vs will be NULL after Proto deregistration. */ + g_free(f->name); + g_free(f->abbrev); + g_free(f->blob); + proto_free_field_strings(f->type, f->base, f->vs); + g_free(f); + + return 0; +} + +WSLUA_METHODS ProtoField_methods[] = { + WSLUA_CLASS_FNREG(ProtoField,new), + WSLUA_CLASS_FNREG(ProtoField,none), + WSLUA_CLASS_FNREG(ProtoField,char), + WSLUA_CLASS_FNREG(ProtoField,uint8), + WSLUA_CLASS_FNREG(ProtoField,uint16), + WSLUA_CLASS_FNREG(ProtoField,uint24), + WSLUA_CLASS_FNREG(ProtoField,uint32), + WSLUA_CLASS_FNREG(ProtoField,uint64), + WSLUA_CLASS_FNREG(ProtoField,int8), + WSLUA_CLASS_FNREG(ProtoField,int16), + WSLUA_CLASS_FNREG(ProtoField,int24), + WSLUA_CLASS_FNREG(ProtoField,int32), + WSLUA_CLASS_FNREG(ProtoField,int64), + WSLUA_CLASS_FNREG(ProtoField,framenum), + WSLUA_CLASS_FNREG(ProtoField,ipv4), + WSLUA_CLASS_FNREG(ProtoField,ipv6), + WSLUA_CLASS_FNREG(ProtoField,ipx), + WSLUA_CLASS_FNREG(ProtoField,ether), + WSLUA_CLASS_FNREG(ProtoField,bool), + WSLUA_CLASS_FNREG(ProtoField,float), + WSLUA_CLASS_FNREG(ProtoField,double), + WSLUA_CLASS_FNREG(ProtoField,absolute_time), + WSLUA_CLASS_FNREG(ProtoField,relative_time), + WSLUA_CLASS_FNREG(ProtoField,string), + WSLUA_CLASS_FNREG(ProtoField,stringz), + WSLUA_CLASS_FNREG(ProtoField,bytes), + WSLUA_CLASS_FNREG(ProtoField,ubytes), + WSLUA_CLASS_FNREG(ProtoField,guid), + WSLUA_CLASS_FNREG(ProtoField,oid), + WSLUA_CLASS_FNREG(ProtoField,protocol), + WSLUA_CLASS_FNREG(ProtoField,rel_oid), + WSLUA_CLASS_FNREG(ProtoField,systemid), + WSLUA_CLASS_FNREG(ProtoField,eui64), + { NULL, NULL } +}; + +WSLUA_META ProtoField_meta[] = { + WSLUA_CLASS_MTREG(ProtoField,tostring), + { NULL, NULL } +}; + +int ProtoField_register(lua_State* L) { + WSLUA_REGISTER_CLASS(ProtoField); + 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: + */ diff --git a/epan/wslua/wslua_struct.c b/epan/wslua/wslua_struct.c new file mode 100644 index 00000000..7d62dc09 --- /dev/null +++ b/epan/wslua/wslua_struct.c @@ -0,0 +1,671 @@ +/****************************************************************************** +* Copyright (C) 2010-2012 Lua.org, PUC-Rio. All rights reserved. +* +* SPDX-License-Identifier: MIT +* +******************************************************************************/ +/* +** {====================================================== +** Library for packing/unpacking structures. +** See Copyright Notice above. +** +** Small changes were made by Hadriel Kaplan - those changes +** are in the Public Domain. +** +** Some changes are based on a patch to struct.h from +** Flemming Madsen, from here: +** http://lua-users.org/lists/lua-l/2009-10/msg00572.html +** In particular, these changes from him: +** -Can handle 'long long' integers (i8 / I8); though they're converted to doubles +** -Can insert/specify padding anywhere in a struct. ('X' eg. when a string is following a union) +** -Can report current offset in both pack and unpack ('=') +** -Can mask out return values when you only want to calculate sizes or unmarshal pascal-style strings. '(' & ')' +** +** Changes I made: +** -Added support for Int64/UInt64 being packed/unpacked, using 'e'/'E' +** -Made it follow Wireshark's conventions so we could get API docs +** ======================================================= +*/ +/* +** Valid formats: +** > - big endian +** < - little endian +** ![num] - alignment +** x[num] - pad num bytes, default 1 +** X[num] - pad to num align, default MAXALIGN +** +** Following are system-dependent sizes: +** i/I - signed/unsigned int +** l/L - signed/unsigned long +** f - float +** T - size_t +** +** Following are system-independent sizes: +** b/B - signed/unsigned byte +** h/H - signed/unsigned short +** in/In - signed/unsigned integer of size `n' bytes + Note: Unpack of i/I is done to a Lua_number, typically a double, + so unpacking a 64-bit field (i8/I8) will lose precision. + Use e/E to unpack into a Wireshark Int64/UInt64 object/userdata instead. +** e/E - signed/unsigned eight-byte Integer (64bits, long long), to/from Int64/UInt64 object +** d - double +** cn - sequence of `n' chars (from/to a string); when packing, n==0 means + the whole string; when unpacking, n==0 means use the previous + read number as the string length +** s - zero-terminated string +** ' ' - ignored +** '(' ')' - stop assigning items. ')' start assigning (padding when packing) +** '=' - return current position / offset +*/ + +#include "config.h" + +#include <assert.h> +#include <limits.h> +#include <stddef.h> +#include <string.h> + +#include <stdio.h> + +#include "wslua.h" + +/* WSLUA_MODULE Struct Binary encode/decode support + + The Struct class offers basic facilities to convert Lua values to and from C-style structs + in binary Lua strings. This is based on Roberto Ierusalimschy's Lua struct library found + in http://www.inf.puc-rio.br/~roberto/struct/, with some minor modifications as follows: + * Added support for `Int64`/`UInt64` being packed/unpacked, using 'e'/'E'. + * Can handle 'long long' integers (i8 / I8); though they're converted to doubles. + * Can insert/specify padding anywhere in a struct. ('X' eg. when a string is following a union). + * Can report current offset in both `pack` and `unpack` ('`=`'). + * Can mask out return values when you only want to calculate sizes or unmarshal + pascal-style strings using '`(`' & '`)`'. + + All but the first of those changes are based on an email from Flemming Madsen, on the lua-users + mailing list, which can be found http://lua-users.org/lists/lua-l/2009-10/msg00572.html[here]. + + The main functions are `Struct.pack`, which packs multiple Lua values into a struct-like + Lua binary string; and `Struct.unpack`, which unpacks multiple Lua values from a given + struct-like Lua binary string. There are some additional helper functions available as well. + + All functions in the Struct library are called as static member functions, not object methods, + so they are invoked as "Struct.pack(...)" instead of "object:pack(...)". + + The fist argument to several of the `Struct` functions is a format string, which describes + the layout of the structure. The format string is a sequence of conversion elements, which + respect the current endianness and the current alignment requirements. Initially, the + current endianness is the machine's native endianness and the current alignment requirement + is 1 (meaning no alignment at all). You can change these settings with appropriate directives + in the format string. + + The supported elements in the format string are as follows: + + * `$$ $$' (empty space) ignored. + * `++!++__n__' flag to set the current alignment requirement to 'n' (necessarily a power of 2); + an absent 'n' means the machine's native alignment. + * `++>++' flag to set mode to big endian (i.e., network-order). + * `++<++' flag to set mode to little endian. + * `++x++' a padding zero byte with no corresponding Lua value. + * `++b++' a signed char. + * `++B++' an unsigned char. + * `++h++' a signed short (native size). + * `++H++' an unsigned short (native size). + * `++l++' a signed long (native size). + * `++L++' an unsigned long (native size). + * `++T++' a size_t (native size). + * `++i++__n__' a signed integer with 'n' bytes. An absent 'n' means the native size of an int. + * `++I++__n__' like `++i++__n__' but unsigned. + * `++e++' signed 8-byte Integer (64-bits, long long), to/from a +Int64+ object. + * `++E++' unsigned 8-byte Integer (64-bits, long long), to/from a +UInt64+ object. + * `++f++' a float (native size). + * `++d++' a double (native size). + * `++s++' a zero-terminated string. + * `++c++__n__' a sequence of exactly 'n' chars corresponding to a single Lua string. An absent 'n' + means 1. When packing, the given string must have at least 'n' characters (extra + characters are discarded). + * `++c0++' this is like `++c++__n__', except that the 'n' is given by other means: When packing, 'n' is + the length of the given string; when unpacking, 'n' is the value of the previous unpacked + value (which must be a number). In that case, this previous value is not returned. + * `++x++__n__' pad to 'n' number of bytes, default 1. + * `++X++__n__' pad to 'n' alignment, default MAXALIGN. + * `++(++' to stop assigning items, and `++)++' start assigning (padding when packing). + * `++=++' to return the current position / offset. + + [NOTE] + ==== + Using `i`, `I`, `h`, `H`, `l`, `L`, `f`, and `T` is strongly discouraged, as those sizes + are system-dependent. Use the explicitly sized variants instead, such as `i4` or `E`. + + Unpacking of `i`/`I` is done to a Lua number, a double-precision floating point, + so unpacking a 64-bit field (`i8`/`I8`) will lose precision. + Use `e`/`E` to unpack into a Wireshark `Int64`/`UInt64` object instead. + ==== + + @since 1.11.3 + */ + + +/* The following line is here so that make-reg.py does the right thing. This 'Struct' class + isn't really a class, so it doesn't have the checkStruct/pushStruct/etc. functions + the following macro would generate; but it does need to be registered and such, so... + WSLUA_CLASS_DEFINE_BASE(Struct,NOP,0); + */ + +/* basic integer type - yes this is system-specific size - it's meant to be */ +#if !defined(STRUCT_INT) +#define STRUCT_INT long +#endif + +typedef STRUCT_INT Inttype; + +/* corresponding unsigned version */ +typedef unsigned STRUCT_INT Uinttype; + +/* maximum size (in bytes) for integral types */ +#define MAXINTSIZE 32 + +/* is 'x' a power of 2? */ +#define isp2(x) ((x) > 0 && ((x) & ((x) - 1)) == 0) + +/* dummy structure to get padding/alignment requirements */ +struct cD { + gchar c; + gdouble d; +}; + + +#define PADDING (sizeof(struct cD) - sizeof(gdouble)) +#define MAXALIGN (PADDING > sizeof(int) ? PADDING : sizeof(int)) + + +/* endian options */ +#define BIG 0 +#define LITTLE 1 + +/* trick to determine native endianness of system */ +static union { + int dummy; + gchar endian; +} const native = {1}; + +/* settings info */ +typedef struct Header { + int endian; + int align; + gboolean noassign; +} Header; + +/* For options that take a number argument, gets the number */ +static int getnum (const gchar **fmt, int df) { + if (!g_ascii_isdigit(**fmt)) /* no number? */ + return df; /* return default value */ + else { + int a = 0; + do { + a = a*10 + *((*fmt)++) - '0'; + } while (g_ascii_isdigit(**fmt)); + return a; + } +} + + +#define defaultoptions(h) ((h)->endian = native.endian, (h)->align = 1, (h)->noassign = FALSE) + + +/* gets size (number of bytes) for a given type */ +static size_t optsize (lua_State *L, gchar opt, const gchar **fmt) { + switch (opt) { + case 'B': case 'b': return sizeof(gchar); + case 'H': case 'h': return sizeof(gshort); + case 'L': case 'l': return sizeof(glong); + case 'E': case 'e': return sizeof(gint64); + case 'T': return sizeof(size_t); + case 'f': return sizeof(gfloat); + case 'd': return sizeof(gdouble); + case 'x': return getnum(fmt, 1); + case 'X': return getnum(fmt, MAXALIGN); + case 'c': return getnum(fmt, 1); + case 'i': case 'I': { + int sz = getnum(fmt, sizeof(int)); + if (sz > MAXINTSIZE) + luaL_error(L, "integral size %d is larger than limit of %d", + sz, MAXINTSIZE); + return sz; + } + case 's': case ' ': + case '<': case '>': + case '(': case ')': + case '!': case '=': + return 0; /* these cases do not have a size */ + default: { + const gchar *msg = lua_pushfstring(L, "invalid format option [%c]", opt); + return luaL_argerror(L, 1, msg); + } + } +} + + +/* +** return number of bytes needed to align an element of size 'size' +** at current position 'len' +*/ +static int gettoalign (size_t len, Header *h, int opt, size_t size) { + if (size == 0 || opt == 'c' || opt == 's') return 0; + if (size > (size_t)h->align) + size = h->align; /* respect max. alignment */ + return (int)((size - (len & (size - 1))) & (size - 1)); +} + + +/* +** options to control endianness and alignment settings +*/ +static void controloptions (lua_State *L, int opt, const gchar **fmt, + Header *h) { + switch (opt) { + case ' ': return; /* ignore white spaces */ + case '>': h->endian = BIG; return; + case '<': h->endian = LITTLE; return; + case '(': h->noassign = TRUE; return; + case ')': h->noassign = FALSE; return; + case '!': { + int a = getnum(fmt, MAXALIGN); + if (!isp2(a)) + luaL_error(L, "alignment %d is not a power of 2", a); + h->align = a; + return; + } + default: { + const char *msg = lua_pushfstring(L, "invalid format option '%c'", opt); + luaL_argerror(L, 1, msg); + } + } +} + +/* Encodes a Lua number as an integer of given size and endianness into a string struct */ +static void putinteger (lua_State *L, luaL_Buffer *b, int arg, int endian, + int size) { + lua_Number n = luaL_checknumber(L, arg); + /* this one's not system dependent size - it's a long long */ + gint64 value; + gchar buff[MAXINTSIZE]; + if (n < 0) + value = (guint64)(gint64)n; + else + value = (guint64)n; + if (endian == LITTLE) { + int i; + for (i = 0; i < size; i++) { + buff[i] = (value & 0xff); + value >>= 8; + } + } + else { + int i; + for (i = size - 1; i >= 0; i--) { + buff[i] = (value & 0xff); + value >>= 8; + } + } + luaL_addlstring(b, buff, size); +} + +/* corrects endianness - usually done by other functions themselves, but is + * used for float/doubles, since on some platforms they're endian'ed as well + */ +static void correctbytes (gchar *b, int size, int endian) { + if (endian != native.endian) { + int i = 0; + while (i < --size) { + gchar temp = b[i]; + b[i++] = b[size]; + b[size] = temp; + } + } +} + + +WSLUA_CONSTRUCTOR Struct_pack (lua_State *L) { + /* Returns a string containing the values arg1, arg2, etc. packed/encoded according to the format string. */ +#define WSLUA_ARG_Struct_pack_FORMAT 1 /* The format string */ +#define WSLUA_ARG_Struct_pack_VALUE 2 /* One or more Lua value(s) to encode, based on the given format. */ + luaL_Buffer b; + const char *fmt = wslua_checkstring_only(L, WSLUA_ARG_Struct_pack_FORMAT); + Header h; + int poscnt = 0; + int posBuf[10]; + int arg = 2; + size_t totalsize = 0; + defaultoptions(&h); + lua_pushnil(L); /* mark to separate arguments from string buffer */ + luaL_buffinit(L, &b); + while (*fmt != '\0') { + int opt = *fmt++; + size_t size = optsize(L, opt, &fmt); + int toalign = gettoalign(totalsize, &h, opt, size); + totalsize += toalign; + while (toalign-- > 0) luaL_addchar(&b, '\0'); + if (opt == 'X') size = 0; /* 'X' is about alignment, not size */ + if (h.noassign && size) opt = 'x'; /* for pack, "(i4)" is the same as "x4" */ + switch (opt) { + case 'b': case 'B': case 'h': case 'H': + case 'l': case 'L': case 'T': case 'i': case 'I': { /* integer types */ + putinteger(L, &b, arg++, h.endian, (int)size); + break; + } + case 'e': { + Int64_pack(L, &b, arg++, h.endian == LITTLE); + break; + } + case 'E': { + UInt64_pack(L, &b, arg++, h.endian == LITTLE); + break; + } + case 'x': case 'X': { + size_t len = size; + while (len-- > 0) + luaL_addchar(&b, '\0'); + break; + } + case 'f': { + gfloat f = (gfloat)luaL_checknumber(L, arg++); + correctbytes((gchar *)&f, (int)size, h.endian); + luaL_addlstring(&b, (gchar *)&f, size); + break; + } + case 'd': { + gdouble d = luaL_checknumber(L, arg++); + correctbytes((gchar *)&d, (int)size, h.endian); + luaL_addlstring(&b, (gchar *)&d, size); + break; + } + case 'c': case 's': { + size_t l; + const gchar *s = luaL_checklstring(L, arg++, &l); + if (size == 0) size = l; + luaL_argcheck(L, l >= (size_t)size, arg, "string too short"); + luaL_addlstring(&b, s, size); + if (opt == 's') { + luaL_addchar(&b, '\0'); /* add zero at the end */ + size++; + } + break; + } + case '=': { + if (poscnt < (int)(sizeof(posBuf)/sizeof(posBuf[0]))) + posBuf[poscnt++] = (int)totalsize + 1; + break; + } + default: controloptions(L, opt, &fmt, &h); + } + totalsize += size; + } + luaL_pushresult(&b); + for (arg = 0; arg < poscnt; arg++) + lua_pushinteger(L, posBuf[arg]); + WSLUA_RETURN(poscnt + 1); /* The packed binary Lua string, plus any positions due to '=' being used in format. */ +} + +/* Decodes an integer from a string struct into a Lua number, based on + * given endianness and size. If the integer type is signed, this makes + * the Lua number be +/- correctly as well. + */ +static lua_Number getinteger (const gchar *buff, int endian, + int issigned, int size) { + Uinttype l = 0; + int i; + if (endian == BIG) { + for (i = 0; i < size; i++) { + l <<= 8; + l |= (Uinttype)(guchar)buff[i]; + } + } + else { + for (i = size - 1; i >= 0; i--) { + l <<= 8; + l |= (Uinttype)(guchar)buff[i]; + } + } + if (!issigned) + return (lua_Number)l; + else { /* signed format */ + Uinttype mask = (Uinttype)(~((Uinttype)0)) << (size*8 - 1); + if (l & mask) /* negative value? */ + l |= mask; /* signal extension */ + return (lua_Number)(Inttype)l; + } +} + +#define b_pushnumber(n) { if (!h.noassign) lua_pushnumber(L, (lua_Number)(n)); } + +WSLUA_CONSTRUCTOR Struct_unpack (lua_State *L) { + /* Unpacks/decodes multiple Lua values from a given struct-like binary Lua string. + The number of returned values depends on the format given, plus an additional value of the position where it stopped reading is returned. */ +#define WSLUA_ARG_Struct_unpack_FORMAT 1 /* The format string */ +#define WSLUA_ARG_Struct_unpack_STRUCT 2 /* The binary Lua string to unpack */ +#define WSLUA_OPTARG_Struct_unpack_BEGIN 3 /* The position to begin reading from (default=1) */ + Header h; + const char *fmt = wslua_checkstring_only(L, WSLUA_ARG_Struct_unpack_FORMAT); + size_t ld; + const char *data = wslua_checklstring_only(L, WSLUA_ARG_Struct_unpack_STRUCT, &ld); + size_t pos = luaL_optinteger(L, WSLUA_OPTARG_Struct_unpack_BEGIN, 1) - 1; + defaultoptions(&h); + lua_settop(L, 2); + while (*fmt) { + int opt = *fmt++; + size_t size = optsize(L, opt, &fmt); + pos += gettoalign(pos, &h, opt, size); + luaL_argcheck(L, pos+size <= ld, 2, "data string too short"); + + if (opt == 'X') size = 0; + if (h.noassign && size > 0) { + /* if we're not assigning, and the opt type has a size, then loop again */ + /* this will not be the case for control options, 'c0', 's', and '=' */ + pos += size; + continue; + } + + luaL_checkstack(L, 1, "too many results"); + switch (opt) { + case 'b': case 'B': case 'h': case 'H': + case 'l': case 'L': case 'T': case 'i': case 'I': { /* integer types */ + int issigned = g_ascii_islower(opt); + lua_Number res = getinteger(data+pos, h.endian, issigned, (int)size); + lua_pushnumber(L, res); + break; + } + case 'e': { + Int64_unpack(L, data+pos, h.endian == LITTLE); + break; + } + case 'E': { + UInt64_unpack(L, data+pos, h.endian == LITTLE); + break; + } + case 'x': case 'X': { + break; + } + case 'f': { + gfloat f; + memcpy(&f, data+pos, size); + correctbytes((gchar *)&f, sizeof(f), h.endian); + lua_pushnumber(L, f); + break; + } + case 'd': { + gdouble d; + memcpy(&d, data+pos, size); + correctbytes((gchar *)&d, sizeof(d), h.endian); + lua_pushnumber(L, d); + break; + } + case 'c': { + if (size == 0) { + if (!lua_isnumber(L, -1)) + luaL_error(L, "format `c0' needs a previous size"); + size = wslua_toguint32(L, -1); + lua_pop(L, 1); + luaL_argcheck(L, pos+size <= ld, 2, "data string too short"); + } + if (!h.noassign) + lua_pushlstring(L, data+pos, size); + break; + } + case 's': { + const gchar *e = (const char *)memchr(data+pos, '\0', ld - pos); + if (e == NULL) + luaL_error(L, "unfinished string in data"); + size = (e - (data+pos)) + 1; + if (!h.noassign) + lua_pushlstring(L, data+pos, size - 1); + break; + } + case '=': { + lua_pushinteger(L, pos + 1); + break; + } + default: controloptions(L, opt, &fmt, &h); + } + pos += size; + } + lua_pushinteger(L, pos + 1); + WSLUA_RETURN(lua_gettop(L) - 2); /* One or more values based on format, plus the position it stopped unpacking. */ +} + + +WSLUA_CONSTRUCTOR Struct_size (lua_State *L) { + /* Returns the length of a binary string that would be consumed/handled by the given format string. */ +#define WSLUA_ARG_Struct_size_FORMAT 1 /* The format string */ + Header h; + const gchar *fmt = wslua_checkstring_only(L, WSLUA_ARG_Struct_size_FORMAT); + size_t pos = 0; + defaultoptions(&h); + while (*fmt) { + int opt = *fmt++; + size_t size = optsize(L, opt, &fmt); + pos += gettoalign(pos, &h, opt, size); + if (opt == 's') + luaL_argerror(L, 1, "option 's' has no fixed size"); + else if (opt == 'c' && size == 0) + luaL_argerror(L, 1, "option 'c0' has no fixed size"); + if (!g_ascii_isalnum(opt)) + controloptions(L, opt, &fmt, &h); + pos += size; + } + lua_pushinteger(L, pos); + WSLUA_RETURN(1); /* The size number */ +} + +WSLUA_CONSTRUCTOR Struct_values (lua_State *L) { + /* Returns the number of Lua values contained in the given format string. + This will be the number of returned values from a call to Struct.unpack() + not including the extra return value of offset position. (i.e., Struct.values() + does not count that extra return value) This will also be the number of + arguments Struct.pack() expects, not including the format string argument. */ +#define WSLUA_ARG_Struct_values_FORMAT 1 /* The format string */ + Header h; + const gchar *fmt = wslua_checkstring_only(L, WSLUA_ARG_Struct_values_FORMAT); + size_t vals = 0; + defaultoptions(&h); + while (*fmt) { + int opt = *fmt++; + /* we use a size != 0 to mean it is a value */ + size_t size = optsize(L, opt, &fmt); + /* but some will be zero and not be a value, or vice-versa */ + switch (opt) { + case 's': case 'c': + /* these are values */ + size = 1; + break; + case 'x': case 'X': + /* these are not */ + size = 0; + break; + default: + break; + } + if (!g_ascii_isalnum(opt)) + controloptions(L, opt, &fmt, &h); + else if (size && !h.noassign) + vals++; + } + lua_pushinteger(L, vals); + WSLUA_RETURN(1); /* The number of values */ +} + +WSLUA_CONSTRUCTOR Struct_tohex (lua_State *L) { + /* Converts the passed-in binary string to a hex-ascii string. */ +#define WSLUA_ARG_Struct_tohex_BYTESTRING 1 /* A Lua string consisting of binary bytes */ +#define WSLUA_OPTARG_Struct_tohex_LOWERCASE 2 /* True to use lower-case hex characters (default=false). */ +#define WSLUA_OPTARG_Struct_tohex_SEPARATOR 3 /* A string separator to insert between hex bytes (default=nil). */ + const gchar* s = NULL; + size_t len = 0; + gboolean lowercase = FALSE; + const gchar* sep = NULL; + + /* luaL_checklstring coerces the argument to a string, and that's ok for tohex, + just not fromhex. In fact, we should accept/coerce a Int64/UInt64 here too someday. */ + s = luaL_checklstring(L, WSLUA_ARG_Struct_tohex_BYTESTRING, &len); + + lowercase = wslua_optbool(L,WSLUA_OPTARG_Struct_tohex_LOWERCASE,FALSE); + sep = luaL_optstring(L,WSLUA_OPTARG_Struct_tohex_SEPARATOR,NULL); + + wslua_bin2hex(L, s, (guint)len, lowercase, sep); + WSLUA_RETURN(1); /* The Lua hex-ascii string */ +} + +WSLUA_CONSTRUCTOR Struct_fromhex (lua_State *L) { + /* Converts the passed-in hex-ascii string to a binary string. */ +#define WSLUA_ARG_Struct_fromhex_HEXBYTES 1 /* A string consisting of hexadecimal bytes like "00 B1 A2" or "1a2b3c4d" */ +#define WSLUA_OPTARG_Struct_fromhex_SEPARATOR 2 /* A string separator between hex bytes/words (default none). */ + const gchar* s = NULL; + size_t len = 0; + const gchar* sep = NULL; + + /* luaL_checklstring coerces the argument to a string, and we don't want to do that */ + s = wslua_checklstring_only(L, WSLUA_ARG_Struct_fromhex_HEXBYTES, &len); + + sep = luaL_optstring(L,WSLUA_OPTARG_Struct_fromhex_SEPARATOR,NULL); + + wslua_hex2bin(L, s, (guint)len, sep); + WSLUA_RETURN(1); /* The Lua binary string */ +} + +/* }====================================================== */ + +/* Gets registered as metamethod automatically by WSLUA_REGISTER_CLASS/META */ +static int Struct__gc(lua_State* L _U_) { + return 0; +} + +WSLUA_METHODS Struct_methods[] = { + WSLUA_CLASS_FNREG(Struct,pack), + WSLUA_CLASS_FNREG(Struct,unpack), + WSLUA_CLASS_FNREG(Struct,size), + WSLUA_CLASS_FNREG(Struct,values), + WSLUA_CLASS_FNREG(Struct,tohex), + WSLUA_CLASS_FNREG(Struct,fromhex), + { NULL, NULL } +}; + +WSLUA_META Struct_meta[] = { + { NULL, NULL } +}; + +LUALIB_API int Struct_register(lua_State* L) { + WSLUA_REGISTER_CLASS(Struct); + return 0; +} + +/* + * Editor modelines - https://www.wireshark.org/tools/modelines.html + * + * Local Variables: + * c-basic-offset: 2 + * tab-width: 8 + * indent-tabs-mode: nil + * End: + * + * vi: set shiftwidth=2 tabstop=8 expandtab: + * :indentSize=2:tabSize=8:noTabs=true: + */ diff --git a/epan/wslua/wslua_tree.c b/epan/wslua/wslua_tree.c new file mode 100644 index 00000000..503b8b7b --- /dev/null +++ b/epan/wslua/wslua_tree.c @@ -0,0 +1,1185 @@ +/* + * wslua_tree.c + * + * Wireshark's interface to the Lua Programming Language + * + * (c) 2006, Luis E. Garcia Ontanon <luis@ontanon.org> + * (c) 2008, Balint Reczey <balint.reczey@ericsson.com> + * + * 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 Tree Adding Information To The Dissection Tree */ + +#include "wslua.h" +#include <epan/exceptions.h> +#include <epan/show_exception.h> + +static gint wslua_ett = -1; + +static GPtrArray* outstanding_TreeItem = NULL; + + +/* pushing a TreeItem with a NULL item or subtree is completely valid for this function */ +TreeItem push_TreeItem(lua_State *L, proto_tree *tree, proto_item *item) { + TreeItem ti = g_new(struct _wslua_treeitem, 1); + + ti->tree = tree; + ti->item = item; + ti->expired = FALSE; + + g_ptr_array_add(outstanding_TreeItem, ti); + + return *(pushTreeItem(L,ti)); +} + +/* creates the TreeItem but does NOT push it into Lua */ +TreeItem create_TreeItem(proto_tree* tree, proto_item* item) +{ + TreeItem tree_item = (TreeItem)g_malloc(sizeof(struct _wslua_treeitem)); + tree_item->tree = tree; + tree_item->item = item; + tree_item->expired = FALSE; + + return tree_item; +} + +CLEAR_OUTSTANDING(TreeItem, expired, TRUE) + +WSLUA_CLASS_DEFINE(TreeItem,FAIL_ON_NULL_OR_EXPIRED("TreeItem")); +/* <<lua_class_TreeItem,`TreeItem`>>s represent information in the https://www.wireshark.org/docs/wsug_html_chunked/ChUsePacketDetailsPaneSection.html[packet details] pane of Wireshark, and the packet details view of TShark. + A <<lua_class_TreeItem,`TreeItem`>> represents a node in the tree, which might also be a subtree and have a list of children. + The children of a subtree have zero or more siblings which are other children of the same <<lua_class_TreeItem,`TreeItem`>> subtree. + + During dissection, heuristic-dissection, and post-dissection, a root <<lua_class_TreeItem,`TreeItem`>> is passed to dissectors as the third argument of the function + callback (e.g., `myproto.dissector(tvbuf,pktinfo,root)`). + + In some cases the tree is not truly added to, in order to improve performance. + For example for packets not currently displayed/selected in Wireshark's visible + window pane, or if TShark isn't invoked with the `-V` switch. However the + "add" type <<lua_class_TreeItem,`TreeItem`>> functions can still be called, and still return <<lua_class_TreeItem,`TreeItem`>> + objects - but the info isn't really added to the tree. Therefore you do not + typically need to worry about whether there's a real tree or not. If, for some + reason, you need to know it, you can use the <<lua_class_attrib_treeitem_visible,`TreeItem.visible`>> attribute getter + to retrieve the state. + */ + +/* the following is used by TreeItem_add_packet_field() - this can THROW errors */ +static proto_item * +try_add_packet_field(lua_State *L, TreeItem tree_item, TvbRange tvbr, const int hfid, + const ftenum_t type, const guint encoding, gint *ret_err) +{ + gint err = 0; + proto_item *volatile item = NULL; + gint endoff = 0; + + switch(type) { + /* these all generate ByteArrays */ + case FT_BYTES: + case FT_UINT_BYTES: + case FT_OID: + case FT_REL_OID: + case FT_SYSTEM_ID: + { + /* GByteArray and its data will be g_free'd by Lua */ + GByteArray *gba = g_byte_array_new(); + item = proto_tree_add_bytes_item(tree_item->tree, hfid, tvbr->tvb->ws_tvb, + tvbr->offset, tvbr->len, encoding, + gba, &endoff, &err); + if (err == 0) { + pushByteArray(L, gba); + lua_pushinteger(L, endoff); + } + } + break; + + case FT_ABSOLUTE_TIME: + case FT_RELATIVE_TIME: + { + /* nstime_t will be g_free'd by Lua */ + nstime_t *nstime = g_new0(nstime_t, 1); + item = proto_tree_add_time_item(tree_item->tree, hfid, tvbr->tvb->ws_tvb, + tvbr->offset, tvbr->len, encoding, + nstime, &endoff, &err); + if (err == 0) { + pushNSTime(L,nstime); + lua_pushinteger(L, endoff); + } + } + break; + + case FT_INT8: + case FT_INT16: + case FT_INT24: + case FT_INT32: + { + gint32 ret; + item = proto_tree_add_item_ret_int(tree_item->tree, hfid, tvbr->tvb->ws_tvb, + tvbr->offset, tvbr->len, encoding, + &ret); + lua_pushnumber(L, (lua_Number)ret); + lua_pushinteger(L, tvbr->offset + tvbr->len); + } + break; + + case FT_INT40: + case FT_INT48: + case FT_INT56: + case FT_INT64: + { + gint64 ret; + item = proto_tree_add_item_ret_int64(tree_item->tree, hfid, tvbr->tvb->ws_tvb, + tvbr->offset, tvbr->len, encoding, + &ret); + pushInt64(L, ret); + lua_pushinteger(L, tvbr->offset + tvbr->len); + } + break; + + case FT_CHAR: + case FT_UINT8: + case FT_UINT16: + case FT_UINT24: + case FT_UINT32: + { + guint32 ret; + item = proto_tree_add_item_ret_uint(tree_item-> tree, hfid, tvbr->tvb->ws_tvb, + tvbr->offset, tvbr->len, encoding, + &ret); + lua_pushnumber(L, (lua_Number)ret); + lua_pushinteger(L, tvbr->offset + tvbr->len); + } + break; + + case FT_UINT40: + case FT_UINT48: + case FT_UINT56: + case FT_UINT64: + { + guint64 ret; + item = proto_tree_add_item_ret_uint64(tree_item->tree, hfid, tvbr->tvb->ws_tvb, + tvbr->offset, tvbr->len, encoding, + &ret); + pushUInt64(L, ret); + lua_pushinteger(L, tvbr->offset + tvbr->len); + } + break; + + case FT_BOOLEAN: + { + gboolean ret; + item = proto_tree_add_item_ret_boolean(tree_item->tree, hfid, tvbr->tvb->ws_tvb, + tvbr->offset, tvbr->len, encoding, + &ret); + lua_pushboolean(L, ret); + lua_pushinteger(L, tvbr->offset + tvbr->len); + } + break; + + case FT_STRING: + { + const guint8 *ret; + gint len; + item = proto_tree_add_item_ret_string_and_length(tree_item->tree, hfid, tvbr->tvb->ws_tvb, + tvbr->offset, tvbr->len, encoding, + NULL, &ret, &len); + lua_pushstring(L, ret); + lua_pushinteger(L, tvbr->offset + len); + wmem_free(NULL, (void*)ret); + } + break; + + case FT_STRINGZ: + { + const guint8 *ret; + gint len; + item = proto_tree_add_item_ret_string_and_length(tree_item->tree, hfid, tvbr->tvb->ws_tvb, + tvbr->offset, -1, encoding, + NULL, &ret, &len); + lua_pushstring(L, ret); + lua_pushinteger(L, tvbr->offset + len); + wmem_free(NULL, (void*)ret); + } + break; + + case FT_FLOAT: + { + gfloat ret; + item = proto_tree_add_item_ret_float(tree_item->tree, hfid, tvbr->tvb->ws_tvb, + tvbr->offset, tvbr->len, encoding, + &ret); + lua_pushnumber(L, (lua_Number)ret); + lua_pushinteger(L, tvbr->offset + tvbr->len); + } + break; + + case FT_DOUBLE: + { + gdouble ret; + item = proto_tree_add_item_ret_double(tree_item->tree, hfid, tvbr->tvb->ws_tvb, + tvbr->offset, tvbr->len, encoding, + &ret); + lua_pushnumber(L, (lua_Number)ret); + lua_pushinteger(L, tvbr->offset + tvbr->len); + } + break; + + case FT_IPv4: + { + Address addr = g_new(address,1); + ws_in4_addr ret; + item = proto_tree_add_item_ret_ipv4(tree_item->tree, hfid, tvbr->tvb->ws_tvb, + tvbr->offset, tvbr->len, encoding, + &ret); + alloc_address_wmem(NULL, addr, AT_IPv4, sizeof(ret), &ret); + pushAddress(L, addr); + lua_pushinteger(L, tvbr->offset + tvbr->len); + } + break; + + case FT_IPv6: + { + Address addr = g_new(address, 1); + ws_in6_addr ret; + item = proto_tree_add_item_ret_ipv6(tree_item->tree, hfid, tvbr->tvb->ws_tvb, + tvbr->offset, tvbr->len, encoding, + &ret); + alloc_address_wmem(NULL, addr, AT_IPv6, sizeof(ret), &ret); + pushAddress(L, addr); + lua_pushinteger(L, tvbr->offset + tvbr->len); + } + break; + + case FT_ETHER: + { + Address addr = g_new(address, 1); + guint8 bytes[FT_ETHER_LEN]; + + item = proto_tree_add_item_ret_ether(tree_item->tree, hfid, tvbr->tvb->ws_tvb, + tvbr->offset, tvbr->len, encoding, + bytes); + alloc_address_wmem(NULL, addr, AT_ETHER, sizeof(bytes), bytes); + pushAddress(L, addr); + lua_pushinteger(L, tvbr->offset + tvbr->len); + } + break; + + default: + item = proto_tree_add_item(tree_item->tree, hfid, tvbr->tvb->ws_tvb, tvbr->offset, tvbr->len, encoding); + lua_pushnil(L); + lua_pushnil(L); + break; + } + + if (ret_err) *ret_err = err; + + return item; +} + +WSLUA_METHOD TreeItem_add_packet_field(lua_State *L) { + /* + Adds a new child tree for the given <<lua_class_ProtoField,`ProtoField`>> object to this tree item, + returning the new child <<lua_class_TreeItem,`TreeItem`>>. + + Unlike `TreeItem:add()` and `TreeItem:add_le()`, the <<lua_class_ProtoField,`ProtoField`>> argument + is not optional, and cannot be a `Proto` object. Instead, this function always + uses the <<lua_class_ProtoField,`ProtoField`>> to determine the type of field to extract from the + passed-in `TvbRange`, highlighting the relevant bytes in the Packet Bytes pane + of the GUI (if there is a GUI), etc. If no <<lua_class_TvbRange,`TvbRange`>>is given, no bytes are + highlighted and the field's value cannot be determined; the <<lua_class_ProtoField,`ProtoField`>> must + have been defined/created not to have a length in such a case, or an error will + occur. For backwards-compatibility reasons the `encoding` argument, however, + must still be given. + + Unlike `TreeItem:add()` and `TreeItem:add_le()`, this function performs both + big-endian and little-endian decoding, by setting the `encoding` argument to + be `ENC_BIG_ENDIAN` or `ENC_LITTLE_ENDIAN`. + + The signature of this function: + + [source,lua] + ---- + tree_item:add_packet_field(proto_field [,tvbrange], encoding, ...) + ---- + + In Wireshark version 1.11.3, this function was changed to return more than + just the new child <<lua_class_TreeItem,`TreeItem`>>. The child is the first return value, so that + function chaining will still work as before; but it now also returns more information. + The second return is the value of the extracted field (i.e., a number, `UInt64`, `Address`, etc.). + The third return is is the offset where data should be read next. This is useful when the length of the + field is not known in advance. The additional return values may be null if the field type + is not well supported in the Lua API. + + Another new feature added to this function in Wireshark version 1.11.3 is the + ability to extract native number `ProtoField`++s++ from string encoding in the + `TvbRange`, for ASCII-based and similar string encodings. For example, a + <<lua_class_ProtoField,`ProtoField`>> of type `ftypes.UINT32` can be extracted from a `TvbRange` + containing the ASCII string "123", and it will correctly decode the ASCII to + the number `123`, both in the tree as well as for the second return value of + this function. To do so, you must set the `encoding` argument of this function + to the appropriate string `ENC_*` value, bitwise-or'd with the `ENC_STRING` + value. `ENC_STRING` is guaranteed to be a unique bit flag, and + thus it can added instead of bitwise-or'ed as well. Only single-byte ASCII digit + string encoding types can be used for this, such as `ENC_ASCII` and `ENC_UTF_8`. + + For example, assuming the <<lua_class_Tvb,`Tvb`>> named "`tvb`" contains the string "123": + + [source,lua] + ---- + -- this is done earlier in the script + local myfield = ProtoField.new("Transaction ID", "myproto.trans_id", ftypes.UINT16) + + -- this is done inside a dissector, post-dissector, or heuristic function + -- child will be the created child tree, and value will be the number 123 or nil on failure + local child, value = tree:add_packet_field(myfield, tvb:range(0,3), ENC_UTF_8 + ENC_STRING) + ---- + + */ +#define WSLUA_ARG_TreeItem_add_packet_field_PROTOFIELD 2 /* The ProtoField field object to add to the tree. */ +#define WSLUA_OPTARG_TreeItem_add_packet_field_TVBRANGE 3 /* The <<lua_class_TvbRange,`TvbRange`>> of bytes in the packet this tree item covers/represents. */ +#define WSLUA_ARG_TreeItem_add_packet_field_ENCODING 4 /* The field's encoding in the `TvbRange`. */ +#define WSLUA_OPTARG_TreeItem_add_packet_field_LABEL 5 /* One or more strings to append to the created <<lua_class_TreeItem,`TreeItem`>>. */ + volatile TvbRange tvbr; + ProtoField field; + int hfid; + volatile int ett; + ftenum_t type; + TreeItem tree_item = shiftTreeItem(L,1); + guint encoding; + proto_item* item = NULL; + volatile int nargs; + volatile gint err = 0; + const char *volatile error = NULL; + + if (!tree_item) { + return luaL_error(L,"not a TreeItem!"); + } + if (tree_item->expired) { + luaL_error(L,"expired TreeItem"); + return 0; + } + + if (! ( field = shiftProtoField(L,1) ) ) { + luaL_error(L,"TreeField:add_packet_field not passed a ProtoField"); + return 0; + } + hfid = field->hfid; + type = field->type; + ett = field->ett; + + tvbr = shiftTvbRange(L,1); + if (!tvbr) { + /* No TvbRange specified */ + tvbr = wmem_new(lua_pinfo->pool, struct _wslua_tvbrange); + tvbr->tvb = wmem_new(lua_pinfo->pool, struct _wslua_tvb); + tvbr->tvb->ws_tvb = lua_tvb; + tvbr->offset = 0; + tvbr->len = 0; + } + + encoding = wslua_checkguint(L,1); + lua_remove(L,1); + + /* get the number of additional args before we add more to the stack */ + nargs = lua_gettop(L); + + /* XXX: why is this being done? If the length was -1, FT_STRINGZ figures out + * the right length in tvb_get_stringz_enc(); if it was 0, it should remain zero; + * if it was greater than zero, then it's the length the caller wanted. + */ + if (type == FT_STRINGZ) { + switch (encoding & ENC_CHARENCODING_MASK) { + + case ENC_UTF_16: + case ENC_UCS_2: + tvbr->len = tvb_unicode_strsize (tvbr->tvb->ws_tvb, tvbr->offset); + break; + + default: + if (tvb_find_guint8 (tvbr->tvb->ws_tvb, tvbr->offset, -1, 0) == -1) { + luaL_error(L,"out of bounds"); + return 0; + } + tvbr->len = tvb_strsize (tvbr->tvb->ws_tvb, tvbr->offset); + break; + } + } + + TRY { + gint errx = 0; + item = try_add_packet_field(L, tree_item, tvbr, hfid, type, encoding, &errx); + err = errx; + } CATCH_ALL { + show_exception(tvbr->tvb->ws_tvb, lua_pinfo, tree_item->tree, EXCEPT_CODE, GET_MESSAGE); + error = "Lua programming error"; + } ENDTRY; + + if (error) { WSLUA_ERROR(TreeItem_add_packet_field,error); } + + if (err != 0) { + lua_pushnil(L); + lua_pushnil(L); + } + + while(nargs) { + const gchar* s; + s = lua_tostring(L,1); + if (s) proto_item_append_text(item, " %s", s); + lua_remove(L,1); + nargs--; + } + + push_TreeItem(L, proto_item_add_subtree(item,ett > 0 ? ett : wslua_ett), item); + + /* move the tree object before the field value */ + lua_insert(L, 1); + + WSLUA_RETURN(3); /* The new child <<lua_class_TreeItem,`TreeItem`>>, the field's extracted value or nil, and offset or nil. */ +} + +static int TreeItem_add_item_any(lua_State *L, gboolean little_endian) { + TvbRange tvbr; + Proto proto; + ProtoField field; + int hfid = -1; + int ett = -1; + ftenum_t type = FT_NONE; + TreeItem tree_item = shiftTreeItem(L,1); + proto_item* item = NULL; + + if (!tree_item) { + return luaL_error(L,"not a TreeItem!"); + } + if (tree_item->expired) { + luaL_error(L,"expired TreeItem"); + return 0; + } + + if (! ( field = shiftProtoField(L,1) ) ) { + if (( proto = shiftProto(L,1) )) { + hfid = proto->hfid; + type = FT_PROTOCOL; + ett = proto->ett; + } else if (lua_isnil(L, 1)) { + return luaL_error(L, "first argument to TreeItem:add is nil!"); + } + } else { + hfid = field->hfid; + type = field->type; + ett = field->ett; + } + + tvbr = shiftTvbRange(L,1); + + if (!tvbr) { + tvbr = wmem_new(lua_pinfo->pool, struct _wslua_tvbrange); + tvbr->tvb = wmem_new(lua_pinfo->pool, struct _wslua_tvb); + tvbr->tvb->ws_tvb = lua_tvb; + tvbr->offset = 0; + tvbr->len = 0; + } + + if (hfid > 0 ) { + /* hfid is > 0 when the first arg was a ProtoField or Proto */ + + if (type == FT_STRINGZ) { + if (tvb_find_guint8 (tvbr->tvb->ws_tvb, tvbr->offset, -1, 0) == -1) { + luaL_error(L,"out of bounds"); + return 0; + } + tvbr->len = tvb_strsize (tvbr->tvb->ws_tvb, tvbr->offset); + } + + if (lua_gettop(L)) { + /* if we got here, the (L,1) index is the value to add, instead of decoding from the Tvb */ + + switch(type) { + case FT_PROTOCOL: + item = proto_tree_add_item(tree_item->tree,hfid,tvbr->tvb->ws_tvb,tvbr->offset,tvbr->len,ENC_NA); + lua_pushnumber(L,0); + lua_insert(L,1); + break; + case FT_BOOLEAN: + { + /* this needs to use checkinteger so that it can accept a Lua boolean and coerce it to an int */ + guint32 val = (guint32) (wslua_tointeger(L,1)); + item = proto_tree_add_boolean(tree_item->tree,hfid,tvbr->tvb->ws_tvb,tvbr->offset,tvbr->len,val); + } + break; + case FT_CHAR: + case FT_UINT8: + case FT_UINT16: + case FT_UINT24: + case FT_UINT32: + case FT_FRAMENUM: + item = proto_tree_add_uint(tree_item->tree,hfid,tvbr->tvb->ws_tvb,tvbr->offset,tvbr->len,wslua_checkguint32(L,1)); + break; + case FT_INT8: + case FT_INT16: + case FT_INT24: + case FT_INT32: + item = proto_tree_add_int(tree_item->tree,hfid,tvbr->tvb->ws_tvb,tvbr->offset,tvbr->len,wslua_checkguint32(L,1)); + break; + case FT_FLOAT: + item = proto_tree_add_float(tree_item->tree,hfid,tvbr->tvb->ws_tvb,tvbr->offset,tvbr->len,(float)luaL_checknumber(L,1)); + break; + case FT_DOUBLE: + item = proto_tree_add_double(tree_item->tree,hfid,tvbr->tvb->ws_tvb,tvbr->offset,tvbr->len,(double)luaL_checknumber(L,1)); + break; + case FT_ABSOLUTE_TIME: + case FT_RELATIVE_TIME: + item = proto_tree_add_time(tree_item->tree,hfid,tvbr->tvb->ws_tvb,tvbr->offset,tvbr->len,checkNSTime(L,1)); + break; + case FT_STRING: + case FT_STRINGZ: + item = proto_tree_add_string(tree_item->tree,hfid,tvbr->tvb->ws_tvb,tvbr->offset,tvbr->len,luaL_checkstring(L,1)); + break; + case FT_BYTES: + item = proto_tree_add_bytes(tree_item->tree,hfid,tvbr->tvb->ws_tvb,tvbr->offset,tvbr->len, (const guint8*) luaL_checkstring(L,1)); + break; + case FT_UINT64: + item = proto_tree_add_uint64(tree_item->tree,hfid,tvbr->tvb->ws_tvb,tvbr->offset,tvbr->len,checkUInt64(L,1)); + break; + case FT_INT64: + item = proto_tree_add_int64(tree_item->tree,hfid,tvbr->tvb->ws_tvb,tvbr->offset,tvbr->len,checkInt64(L,1)); + break; + case FT_IPv4: + { + Address addr = checkAddress(L,1); + guint32 addr_value; + + if (addr->type != AT_IPv4) { + luaL_error(L, "Expected IPv4 address for FT_IPv4 field"); + return 0; + } + + /* + * The address is not guaranteed to be aligned on a + * 32-bit boundary, so we can't safely dereference + * the pointer as if it were so aligned. + */ + memcpy(&addr_value, addr->data, sizeof addr_value); + item = proto_tree_add_ipv4(tree_item->tree,hfid,tvbr->tvb->ws_tvb,tvbr->offset,tvbr->len,addr_value); + } + break; + case FT_IPv6: + { + Address addr = checkAddress(L,1); + if (addr->type != AT_IPv6) { + luaL_error(L, "Expected IPv6 address for FT_IPv6 field"); + return 0; + } + + item = proto_tree_add_ipv6(tree_item->tree, hfid, tvbr->tvb->ws_tvb, tvbr->offset, tvbr->len, (const ws_in6_addr *)addr->data); + } + break; + case FT_ETHER: + { + Address addr = checkAddress(L,1); + if (addr->type != AT_ETHER) { + luaL_error(L, "Expected MAC address for FT_ETHER field"); + return 0; + } + + item = proto_tree_add_ether(tree_item->tree, hfid, tvbr->tvb->ws_tvb, tvbr->offset, tvbr->len, (const guint8 *)addr->data); + } + break; + case FT_UINT_BYTES: + case FT_IPXNET: + case FT_GUID: + case FT_OID: + case FT_REL_OID: + case FT_SYSTEM_ID: + case FT_VINES: + case FT_FCWWN: + default: + luaL_error(L,"FT_ not yet supported"); + return 0; + } + + lua_remove(L,1); + + } else { + if (type == FT_FRAMENUM) { + luaL_error(L, "ProtoField FRAMENUM cannot fetch value from Tvb"); + return 0; + } + /* the Lua stack is empty - no value was given - so decode the value from the tvb */ + item = proto_tree_add_item(tree_item->tree, hfid, tvbr->tvb->ws_tvb, tvbr->offset, tvbr->len, little_endian ? ENC_LITTLE_ENDIAN : ENC_BIG_ENDIAN); + } + + if ( lua_gettop(L) ) { + /* if there was a value, it was removed earlier, so what's left is the display string to set */ + const gchar* s = lua_tostring(L,1); + if (s) proto_item_set_text(item,"%s",s); + lua_remove(L,1); + } + + } else { + /* no ProtoField or Proto was given */ + if (lua_gettop(L)) { + const gchar* s = lua_tostring(L,1); + const int hf = get_hf_wslua_text(); + if (hf > -1) { + /* use proto_tree_add_none_format() instead? */ + item = proto_tree_add_item(tree_item->tree, hf, tvbr->tvb->ws_tvb, tvbr->offset, tvbr->len, ENC_NA); + proto_item_set_text(item, "%s", s); + } else { + luaL_error(L,"Internal error: hf_wslua_text not registered"); + } + lua_remove(L,1); + } else { + luaL_error(L,"Tree item ProtoField/Protocol handle is invalid (ProtoField/Proto not registered?)"); + } + } + + while(lua_gettop(L)) { + /* keep appending more text */ + const gchar* s = lua_tostring(L,1); + if (s) proto_item_append_text(item, " %s", s); + lua_remove(L,1); + } + + push_TreeItem(L, proto_item_add_subtree(item,ett > 0 ? ett : wslua_ett), item); + + return 1; +} + + +WSLUA_METHOD TreeItem_add(lua_State *L) { + /* + Adds a child item to this tree item, returning the new child <<lua_class_TreeItem,`TreeItem`>>. + + If the <<lua_class_ProtoField,`ProtoField`>> represents a numeric value (int, uint or float), then it's treated as a Big Endian (network order) value. + + This function has a complicated form: 'treeitem:add([protofield,] [tvbrange,] [[value], label]])', such that if the first + argument is a <<lua_class_ProtoField,`ProtoField`>> or a <<lua_class_Proto,`Proto`>>, the second argument is a <<lua_class_TvbRange,`TvbRange`>>, and a third argument is given, it's a value; + but if the second argument is a non-<<lua_class_TvbRange,`TvbRange`>>, then it's the value (as opposed to filling that argument with 'nil', + which is invalid for this function). If the first argument is a non-<<lua_class_ProtoField,`ProtoField`>> and a non-<<lua_class_Proto,`Proto`>> then this argument can + be either a <<lua_class_TvbRange,`TvbRange`>> or a label, and the value is not in use. + + ==== Example + + [source,lua] + ---- + local proto_foo = Proto("foo", "Foo Protocol") + proto_foo.fields.bytes = ProtoField.bytes("foo.bytes", "Byte array") + proto_foo.fields.u16 = ProtoField.uint16("foo.u16", "Unsigned short", base.HEX) + + function proto_foo.dissector(buf, pinfo, tree) + -- ignore packets less than 4 bytes long + if buf:len() < 4 then return end + + -- ############################################## + -- # Assume buf(0,4) == {0x00, 0x01, 0x00, 0x02} + -- ############################################## + + local t = tree:add( proto_foo, buf() ) + + -- Adds a byte array that shows as: "Byte array: 00010002" + t:add( proto_foo.fields.bytes, buf(0,4) ) + + -- Adds a byte array that shows as "Byte array: 313233" + -- (the ASCII char code of each character in "123") + t:add( proto_foo.fields.bytes, buf(0,4), "123" ) + + -- Adds a tree item that shows as: "Unsigned short: 0x0001" + t:add( proto_foo.fields.u16, buf(0,2) ) + + -- Adds a tree item that shows as: "Unsigned short: 0x0064" + t:add( proto_foo.fields.u16, buf(0,2), 100 ) + + -- Adds a tree item that shows as: "Unsigned short: 0x0064 ( big endian )" + t:add( proto_foo.fields.u16, buf(1,2), 100, nil, "(", nil, "big", 999, nil, "endian", nil, ")" ) + + -- LITTLE ENDIAN: Adds a tree item that shows as: "Unsigned short: 0x0100" + t:add_le( proto_foo.fields.u16, buf(0,2) ) + + -- LITTLE ENDIAN: Adds a tree item that shows as: "Unsigned short: 0x6400" + t:add_le( proto_foo.fields.u16, buf(0,2), 100 ) + + -- LITTLE ENDIAN: Adds a tree item that shows as: "Unsigned short: 0x6400 ( little endian )" + t:add_le( proto_foo.fields.u16, buf(1,2), 100, nil, "(", nil, "little", 999, nil, "endian", nil, ")" ) + end + + udp_table = DissectorTable.get("udp.port") + udp_table:add(7777, proto_foo) + ---- + */ +#define WSLUA_OPTARG_TreeItem_add_PROTOFIELD 2 /* The <<lua_class_ProtoField,`ProtoField`>> field or <<lua_class_Proto,`Proto`>> protocol object to add to the tree. */ +#define WSLUA_OPTARG_TreeItem_add_TVBRANGE 3 /* The <<lua_class_TvbRange,`TvbRange`>> of bytes in the packet this tree item covers/represents. */ +#define WSLUA_OPTARG_TreeItem_add_VALUE 4 /* The field's value, instead of the ProtoField/Proto one. */ +#define WSLUA_OPTARG_TreeItem_add_LABEL 5 /* One or more strings to use for the tree item label, instead of the ProtoField/Proto one. */ + WSLUA_RETURN(TreeItem_add_item_any(L,FALSE)); /* The new child TreeItem. */ +} + +WSLUA_METHOD TreeItem_add_le(lua_State *L) { + /* + Adds a child item to this tree item, returning the new child <<lua_class_TreeItem,`TreeItem`>>. + + If the <<lua_class_ProtoField,`ProtoField`>> represents a numeric value (int, uint or float), then it's treated as a Little Endian value. + + This function has a complicated form: 'treeitem:add_le([protofield,] [tvbrange,] [[value], label]])', such that if the first + argument is a <<lua_class_ProtoField,`ProtoField`>> or a <<lua_class_Proto,`Proto`>>, the second argument is a <<lua_class_TvbRange,`TvbRange`>>, and a third argument is given, it's a value; + but if the second argument is a non-<<lua_class_TvbRange,`TvbRange`>>, then it's the value (as opposed to filling that argument with 'nil', + which is invalid for this function). If the first argument is a non-<<lua_class_ProtoField,`ProtoField`>> and a non-<<lua_class_Proto,`Proto`>> then this argument can + be either a <<lua_class_TvbRange,`TvbRange`>> or a label, and the value is not in use. + */ +#define WSLUA_OPTARG_TreeItem_add_le_PROTOFIELD 2 /* The ProtoField field or Proto protocol object to add to the tree. */ +#define WSLUA_OPTARG_TreeItem_add_le_TVBRANGE 3 /* The TvbRange of bytes in the packet this tree item covers/represents. */ +#define WSLUA_OPTARG_TreeItem_add_le_VALUE 4 /* The field's value, instead of the ProtoField/Proto one. */ +#define WSLUA_OPTARG_TreeItem_add_le_LABEL 5 /* One or more strings to use for the tree item label, instead of the ProtoField/Proto one. */ + WSLUA_RETURN(TreeItem_add_item_any(L,TRUE)); /* The new child TreeItem. */ +} + +/* WSLUA_ATTRIBUTE TreeItem_text RW Set/get the <<lua_class_TreeItem,`TreeItem`>>'s display string (string). + + For the getter, if the TreeItem has no display string, then nil is returned. + + @since 1.99.3 + */ +static int TreeItem_get_text(lua_State* L) { + TreeItem ti = checkTreeItem(L,1); + gchar label_str[ITEM_LABEL_LENGTH+1]; + gchar *label_ptr; + + if (ti->item) { + field_info *fi = PITEM_FINFO(ti->item); + + if (!fi->rep) { + label_ptr = label_str; + proto_item_fill_label(fi, label_str); + } else + label_ptr = fi->rep->representation; + + if (label_ptr) { + lua_pushstring(L, label_ptr); + } else { + lua_pushnil(L); + } + } else { + lua_pushnil(L); + } + + return 1; +} + +/* the following is used as both a method and attribute */ +WSLUA_METHOD TreeItem_set_text(lua_State *L) { + /* Sets the text of the label. + + This used to return nothing, but as of 1.11.3 it returns the same tree item to allow chained calls. + */ +#define WSLUA_ARG_TreeItem_set_text_TEXT 2 /* The text to be used. */ + TreeItem ti = checkTreeItem(L,1); + const gchar* s = luaL_checkstring(L,WSLUA_ARG_TreeItem_set_text_TEXT); + + proto_item_set_text(ti->item,"%s",s); + + /* copy the TreeItem userdata so we give it back */ + lua_pushvalue(L, 1); + + WSLUA_RETURN(1); /* The same TreeItem. */ +} + +WSLUA_METHOD TreeItem_append_text(lua_State *L) { + /* Appends text to the label. + + This used to return nothing, but as of 1.11.3 it returns the same tree item to allow chained calls. + */ +#define WSLUA_ARG_TreeItem_append_text_TEXT 2 /* The text to be appended. */ + TreeItem ti = checkTreeItem(L,1); + const gchar* s = luaL_checkstring(L,WSLUA_ARG_TreeItem_append_text_TEXT); + + proto_item_append_text(ti->item,"%s",s); + + /* copy the TreeItem userdata so we give it back */ + lua_pushvalue(L, 1); + + WSLUA_RETURN(1); /* The same TreeItem. */ +} + +WSLUA_METHOD TreeItem_prepend_text(lua_State *L) { + /* Prepends text to the label. + + This used to return nothing, but as of 1.11.3 it returns the same tree item to allow chained calls. + */ +#define WSLUA_ARG_TreeItem_prepend_text_TEXT 2 /* The text to be prepended. */ + TreeItem ti = checkTreeItem(L,1); + const gchar* s = luaL_checkstring(L,WSLUA_ARG_TreeItem_prepend_text_TEXT); + + proto_item_prepend_text(ti->item,"%s",s); + + /* copy the TreeItem userdata so we give it back */ + lua_pushvalue(L, 1); + + WSLUA_RETURN(1); /* The same TreeItem. */ +} + +WSLUA_METHOD TreeItem_add_expert_info(lua_State *L) { + /* Sets the expert flags of the item and adds expert info to the packet. + + This function does *not* create a truly filterable expert info for a protocol. + Instead you should use `TreeItem.add_proto_expert_info()`. + + Note: This function is provided for backwards compatibility only, and should not + be used in new Lua code. It may be removed in the future. You should only + use `TreeItem.add_proto_expert_info()`. + */ +#define WSLUA_OPTARG_TreeItem_add_expert_info_GROUP 2 /* One of `PI_CHECKSUM`, `PI_SEQUENCE`, + `PI_RESPONSE_CODE`, `PI_REQUEST_CODE`, + `PI_UNDECODED`, `PI_REASSEMBLE`, + `PI_MALFORMED` or `PI_DEBUG`. */ +#define WSLUA_OPTARG_TreeItem_add_expert_info_SEVERITY 3 /* One of `PI_CHAT`, `PI_NOTE`, + `PI_WARN`, or `PI_ERROR`. */ +#define WSLUA_OPTARG_TreeItem_add_expert_info_TEXT 4 /* The text for the expert info display. */ + TreeItem ti = checkTreeItem(L,1); + int group = (int)luaL_optinteger(L,WSLUA_OPTARG_TreeItem_add_expert_info_GROUP,PI_DEBUG); + int severity = (int)luaL_optinteger(L,WSLUA_OPTARG_TreeItem_add_expert_info_SEVERITY,PI_CHAT); + expert_field* ei_info = wslua_get_expert_field(group, severity); + const gchar* str; + + if (lua_gettop(L) >= WSLUA_OPTARG_TreeItem_add_expert_info_TEXT) { + str = wslua_checkstring_only(L, WSLUA_OPTARG_TreeItem_add_expert_info_TEXT); + expert_add_info_format(lua_pinfo, ti->item, ei_info, "%s", str); + } else { + expert_add_info(lua_pinfo, ti->item, ei_info); + } + + /* copy the TreeItem userdata so we give it back */ + lua_pushvalue(L, 1); + + WSLUA_RETURN(1); /* The same TreeItem. */ +} + +WSLUA_METHOD TreeItem_add_proto_expert_info(lua_State *L) { + /* Sets the expert flags of the tree item and adds expert info to the packet. + + @since 1.11.3 + */ +#define WSLUA_ARG_TreeItem_add_proto_expert_info_EXPERT 2 /* The <<lua_class_ProtoExpert,`ProtoExpert`>> object to add to the tree. */ +#define WSLUA_OPTARG_TreeItem_add_proto_expert_info_TEXT 3 /* Text for the expert info display + (default is to use the registered + text). */ + TreeItem ti = checkTreeItem(L,1); + ProtoExpert expert = checkProtoExpert(L,WSLUA_ARG_TreeItem_add_proto_expert_info_EXPERT); + const gchar* str; + + if (expert->ids.ei == EI_INIT_EI || expert->ids.hf == EI_INIT_HF) { + luaL_error(L, "ProtoExpert is not registered"); + return 0; + } + + if (lua_gettop(L) >= WSLUA_OPTARG_TreeItem_add_proto_expert_info_TEXT) { + str = wslua_checkstring_only(L, WSLUA_OPTARG_TreeItem_add_proto_expert_info_TEXT); + expert_add_info_format(lua_pinfo, ti->item, &expert->ids, "%s", str); + } else { + expert_add_info(lua_pinfo, ti->item, &expert->ids); + } + + /* copy the TreeItem userdata so we give it back */ + lua_pushvalue(L, 1); + + WSLUA_RETURN(1); /* The same TreeItem. */ +} + +WSLUA_METHOD TreeItem_add_tvb_expert_info(lua_State *L) { + /* Sets the expert flags of the tree item and adds expert info to the packet + associated with the <<lua_class_Tvb,`Tvb`>> or <<lua_class_TvbRange,`TvbRange`>> bytes in the packet. + + @since 1.11.3 + */ +#define WSLUA_ARG_TreeItem_add_tvb_expert_info_EXPERT 2 /* The <<lua_class_ProtoExpert,`ProtoExpert`>> object to add to the tree. */ +#define WSLUA_ARG_TreeItem_add_tvb_expert_info_TVB 3 /* The <<lua_class_Tvb,`Tvb`>> or <<lua_class_TvbRange,`TvbRange`>> object bytes to associate + the expert info with. */ +#define WSLUA_OPTARG_TreeItem_add_tvb_expert_info_TEXT 4 /* Text for the expert info display + (default is to use the registered + text). */ + TreeItem ti = checkTreeItem(L,1); + ProtoExpert expert = checkProtoExpert(L,WSLUA_ARG_TreeItem_add_proto_expert_info_EXPERT); + TvbRange tvbr; + const gchar* str; + + if (expert->ids.ei == EI_INIT_EI || expert->ids.hf == EI_INIT_HF) { + luaL_error(L, "ProtoExpert is not registered"); + return 0; + } + + tvbr = shiftTvbRange(L,WSLUA_ARG_TreeItem_add_tvb_expert_info_TVB); + + if (!tvbr) { + tvbr = wmem_new(lua_pinfo->pool, struct _wslua_tvbrange); + tvbr->tvb = shiftTvb(L,WSLUA_ARG_TreeItem_add_tvb_expert_info_TVB); + if (!tvbr->tvb) { + tvbr->tvb = wmem_new(lua_pinfo->pool, struct _wslua_tvb); + } + tvbr->tvb->ws_tvb = lua_tvb; + tvbr->offset = 0; + tvbr->len = 0; + } + + if (lua_gettop(L) >= WSLUA_OPTARG_TreeItem_add_proto_expert_info_TEXT) { + str = wslua_checkstring_only(L, WSLUA_OPTARG_TreeItem_add_proto_expert_info_TEXT); + proto_tree_add_expert_format(ti->tree, lua_pinfo, &expert->ids, + tvbr->tvb->ws_tvb, tvbr->offset, tvbr->len, + "%s", str); + } else { + proto_tree_add_expert(ti->tree, lua_pinfo, &expert->ids, + tvbr->tvb->ws_tvb, tvbr->offset, tvbr->len); + } + + /* copy the TreeItem userdata so we give it back */ + lua_pushvalue(L, 1); + + WSLUA_RETURN(1); /* The same TreeItem. */ +} + + +/* WSLUA_ATTRIBUTE TreeItem_visible RO Get the <<lua_class_TreeItem,`TreeItem`>>'s subtree visibility status (boolean). + + @since 1.99.8 + */ +static int TreeItem_get_visible(lua_State* L) { + TreeItem ti = checkTreeItem(L,1); + + if (ti->tree) { + lua_pushboolean(L, PTREE_DATA(ti->tree)->visible); + } + else { + lua_pushboolean(L, FALSE); + } + + return 1; +} + + +/* WSLUA_ATTRIBUTE TreeItem_generated RW Set/get the <<lua_class_TreeItem,`TreeItem`>>'s generated state (boolean). + + @since 1.99.8 + */ +static int TreeItem_get_generated(lua_State* L) { + TreeItem ti = checkTreeItem(L,1); + + lua_pushboolean(L, proto_item_is_generated(ti->item)); + + return 1; +} + +/* the following is used as both a method and attribute. As a method it defaults + to setting the value, because that's what it used to do before. */ +WSLUA_METHOD TreeItem_set_generated(lua_State *L) { + /* Marks the <<lua_class_TreeItem,`TreeItem`>> as a generated field (with data inferred but not contained in the packet). + + This used to return nothing, but as of 1.11.3 it returns the same tree item to allow chained calls. + */ +#define WSLUA_OPTARG_TreeItem_set_generated_BOOL 2 /* A Lua boolean, which if `true` sets the <<lua_class_TreeItem,`TreeItem`>> + generated flag, else clears it (default=true) */ + TreeItem ti = checkTreeItem(L,1); + gboolean set = wslua_optbool(L, WSLUA_OPTARG_TreeItem_set_generated_BOOL, TRUE); + + if (set) { + proto_item_set_generated(ti->item); + } else { + if (ti->item) + FI_RESET_FLAG(PITEM_FINFO(ti->item), FI_GENERATED); + } + + /* copy the TreeItem userdata so we give it back */ + lua_pushvalue(L, 1); + + WSLUA_RETURN(1); /* The same TreeItem. */ +} + +/* WSLUA_ATTRIBUTE TreeItem_hidden RW Set/get <<lua_class_TreeItem,`TreeItem`>>'s hidden state (boolean). + + @since 1.99.8 + */ +static int TreeItem_get_hidden(lua_State* L) { + TreeItem ti = checkTreeItem(L,1); + + lua_pushboolean(L, proto_item_is_hidden(ti->item)); + + return 1; +} + +/* the following is used as both a method and attribute. As a method it defaults + to setting the value, because that's what it used to do before. */ +WSLUA_METHOD TreeItem_set_hidden(lua_State *L) { + /* + Marks the <<lua_class_TreeItem,`TreeItem`>> as a hidden field (neither displayed nor used in filters). + Deprecated + + This used to return nothing, but as of 1.11.3 it returns the same tree item to allow chained calls. + */ +#define WSLUA_OPTARG_TreeItem_set_hidden_BOOL 2 /* A Lua boolean, which if `true` sets the <<lua_class_TreeItem,`TreeItem`>> + hidden flag, else clears it. Default is `true`. */ + TreeItem ti = checkTreeItem(L,1); + gboolean set = wslua_optbool(L, WSLUA_OPTARG_TreeItem_set_hidden_BOOL, TRUE); + + if (set) { + proto_item_set_hidden(ti->item); + } else { + proto_item_set_visible(ti->item); + } + + /* copy the TreeItem userdata so we give it back */ + lua_pushvalue(L, 1); + + WSLUA_RETURN(1); /* The same TreeItem. */ +} + +/* WSLUA_ATTRIBUTE TreeItem_len RW Set/get <<lua_class_TreeItem,`TreeItem`>>'s length inside tvb, after it has already been created. + + @since 1.99.8 + */ +static int TreeItem_get_len(lua_State* L) { + TreeItem ti = checkTreeItem(L,1); + int len = 0; + + /* XXX - this is *NOT* guaranteed to return a correct value! */ + len = proto_item_get_len(ti->item); + + lua_pushinteger(L, len > 0 ? len : 0); + + return 1; +} + +WSLUA_METHOD TreeItem_set_len(lua_State *L) { + /* Set <<lua_class_TreeItem,`TreeItem`>>'s length inside tvb, after it has already been created. + + This used to return nothing, but as of 1.11.3 it returns the same tree item to allow chained calls. + */ +#define WSLUA_ARG_TreeItem_set_len_LEN 2 /* The length to be used. */ + TreeItem ti = checkTreeItem(L,1); + gint len = (int)luaL_checkinteger(L,WSLUA_ARG_TreeItem_set_len_LEN); + + proto_item_set_len(ti->item, len); + + /* copy the TreeItem userdata so we give it back */ + lua_pushvalue(L, 1); + + WSLUA_RETURN(1); /* The same TreeItem. */ +} + +WSLUA_METHOD TreeItem_referenced(lua_State *L) { + /* Checks if a <<lua_class_ProtoField,`ProtoField`>> or <<lua_class_Dissector,`Dissector`>> is referenced by a filter/tap/UI. + + If this function returns `false`, it means that the field (or dissector) does not need to be dissected + and can be safely skipped. By skipping a field rather than dissecting it, the dissector will + usually run faster since Wireshark will not do extra dissection work when it doesn't need the field. + + You can use this in conjunction with the TreeItem.visible attribute. This function will always return + TRUE when the TreeItem is visible. When it is not visible and the field is not referenced, you can + speed up the dissection by not dissecting the field as it is not needed for display or filtering. + + This function takes one parameter that can be a <<lua_class_ProtoField,`ProtoField`>> or <<lua_class_Dissector,`Dissector`>>. + The <<lua_class_Dissector,`Dissector`>> form is useful when you need to decide whether to call a sub-dissector. + + @since 2.4.0 + */ +#define WSLUA_ARG_TreeItem_referenced_PROTOFIELD 2 /* The <<lua_class_ProtoField,`ProtoField`>> or <<lua_class_Dissector,`Dissector`>> to check if referenced. */ + TreeItem ti = checkTreeItem(L, 1); + if (!ti) return 0; + ProtoField f = shiftProtoField(L, WSLUA_ARG_TreeItem_referenced_PROTOFIELD); + if (f) { + lua_pushboolean(L, proto_field_is_referenced(ti->tree, f->hfid)); + } + else { + Dissector d = checkDissector(L, WSLUA_ARG_TreeItem_referenced_PROTOFIELD); + if (!d) return 0; + lua_pushboolean(L, proto_field_is_referenced(ti->tree, dissector_handle_get_protocol_index(d))); + } + WSLUA_RETURN(1); /* A boolean indicating if the ProtoField/Dissector is referenced */ +} + +WSLUA_METAMETHOD TreeItem__tostring(lua_State* L) { + /* Returns string debug information about the <<lua_class_TreeItem,`TreeItem`>>. + + @since 1.99.8 + */ + TreeItem ti = toTreeItem(L,1); + + if (ti) { + lua_pushfstring(L, + "TreeItem: expired=%s, has item=%s, has subtree=%s, they are %sthe same", + ti->expired ? "true" : "false", + ti->item ? "true" : "false", + ti->tree ? "true" : "false", + (ti->tree == ti->item) ? "" : "not "); + } + else { + lua_pushstring(L, "No TreeItem object!"); + } + + return 1; +} + +/* Gets registered as metamethod automatically by WSLUA_REGISTER_CLASS/META */ +static int TreeItem__gc(lua_State* L) { + TreeItem ti = toTreeItem(L,1); + if (!ti) return 0; + if (!ti->expired) + ti->expired = TRUE; + else + g_free(ti); + return 0; +} + +WSLUA_ATTRIBUTES TreeItem_attributes[] = { + WSLUA_ATTRIBUTE_RWREG(TreeItem,generated), + WSLUA_ATTRIBUTE_RWREG(TreeItem,hidden), + WSLUA_ATTRIBUTE_RWREG(TreeItem,len), + WSLUA_ATTRIBUTE_RWREG(TreeItem,text), + WSLUA_ATTRIBUTE_ROREG(TreeItem,visible), + { NULL, NULL, NULL } +}; + +WSLUA_METHODS TreeItem_methods[] = { + WSLUA_CLASS_FNREG(TreeItem,add_packet_field), + WSLUA_CLASS_FNREG(TreeItem,add), + WSLUA_CLASS_FNREG(TreeItem,add_le), + WSLUA_CLASS_FNREG(TreeItem,set_text), + WSLUA_CLASS_FNREG(TreeItem,append_text), + WSLUA_CLASS_FNREG(TreeItem,prepend_text), + WSLUA_CLASS_FNREG(TreeItem,add_expert_info), + WSLUA_CLASS_FNREG(TreeItem,add_proto_expert_info), + WSLUA_CLASS_FNREG(TreeItem,add_tvb_expert_info), + WSLUA_CLASS_FNREG(TreeItem,set_generated), + WSLUA_CLASS_FNREG(TreeItem,set_hidden), + WSLUA_CLASS_FNREG(TreeItem,set_len), + WSLUA_CLASS_FNREG(TreeItem,referenced), + { NULL, NULL } +}; + +WSLUA_META TreeItem_meta[] = { + WSLUA_CLASS_MTREG(TreeItem,tostring), + { NULL, NULL } +}; + +int TreeItem_register(lua_State *L) { + gint* etts[] = { &wslua_ett }; + wslua_ett = -1; /* Reset to support reload Lua plugins */ + WSLUA_REGISTER_CLASS_WITH_ATTRS(TreeItem); + outstanding_TreeItem = g_ptr_array_new(); + proto_register_subtree_array(etts,1); + 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: + */ diff --git a/epan/wslua/wslua_tvb.c b/epan/wslua/wslua_tvb.c new file mode 100644 index 00000000..7951c7d9 --- /dev/null +++ b/epan/wslua/wslua_tvb.c @@ -0,0 +1,1508 @@ +/* + * wslua_tvb.c + * + * Wireshark's interface to the Lua Programming Language + * + * (c) 2006, Luis E. Garcia Ontanon <luis@ontanon.org> + * (c) 2008, Balint Reczey <balint.reczey@ericsson.com> + * (c) 2009, Stig Bjorlykke <stig@bjorlykke.org> + * (c) 2014, Hadriel Kaplan <hadrielk@yahoo.com> + * + * 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" + +#include "wslua.h" +#include <epan/wmem_scopes.h> + + +/* WSLUA_MODULE Tvb Functions For Handling Packet Data */ + + +/* + * Tvb & TvbRange + * + * a Tvb represents a tvbuff_t in Lua. + * a TvbRange represents a range in a tvb (tvb,offset,length) its main purpose is to do bounds checking, + * It helps, too, simplifying argument passing to Tree. In wireshark terms this is worthless nothing + * not already done by the TVB itself. In lua's terms it's necessary to avoid abusing TRY{}CATCH(){} + * via preemptive bounds checking. + * + * These lua objects refer to structures in wireshark that are freed independently from Lua's garbage collector. + * To avoid using pointers from Lua to Wireshark structures that are already freed, we maintain a list of the + * pointers each with a marker that tracks its expiry. + * + * All pointers are marked as expired when the dissection of the current frame is finished or when the garbage + * collector tries to free the object referring to the pointer, whichever comes first. + * + * All allocated memory chunks used for tracking the pointers' state are freed after marking the pointer as expired + * by the garbage collector or by the end of the dissection of the current frame, whichever comes second. + * + * We check the expiry state of the pointer before each access. + * + */ + +WSLUA_CLASS_DEFINE(Tvb,FAIL_ON_NULL_OR_EXPIRED("Tvb")); +/* A <<lua_class_Tvb,`Tvb`>> represents the packet's buffer. It is passed as an argument to listeners and dissectors, + and can be used to extract information (via <<lua_class_TvbRange,`TvbRange`>>) from the packet's data. + + To create a <<lua_class_TvbRange,`TvbRange`>> the <<lua_class_Tvb,`Tvb`>> must be called with offset and length as optional arguments; + the offset defaults to 0 and the length to `tvb:captured_len()`. + + [WARNING] + ==== + Tvbs are usable only by the current listener or dissector call and are destroyed + as soon as the listener or dissector returns, so references to them are unusable once the function + has returned. + ==== +*/ + +static GPtrArray* outstanding_Tvb = NULL; +static GPtrArray* outstanding_TvbRange = NULL; + +/* this is used to push Tvbs that were created brand new by wslua code */ +int push_wsluaTvb(lua_State* L, Tvb t) { + g_ptr_array_add(outstanding_Tvb,t); + pushTvb(L,t); + return 1; +} + +#define PUSH_TVBRANGE(L,t) {g_ptr_array_add(outstanding_TvbRange,t);pushTvbRange(L,t);} + + +static void free_Tvb(Tvb tvb) { + if (!tvb) return; + + if (!tvb->expired) { + tvb->expired = TRUE; + } else { + if (tvb->need_free) + tvb_free(tvb->ws_tvb); + g_free(tvb); + } +} + +void clear_outstanding_Tvb(void) { + while (outstanding_Tvb->len) { + Tvb tvb = (Tvb)g_ptr_array_remove_index_fast(outstanding_Tvb,0); + free_Tvb(tvb); + } +} + +/* this is used to push Tvbs that just point to pre-existing C-code Tvbs */ +Tvb* push_Tvb(lua_State* L, tvbuff_t* ws_tvb) { + Tvb tvb = (Tvb)g_malloc(sizeof(struct _wslua_tvb)); + tvb->ws_tvb = ws_tvb; + tvb->expired = FALSE; + tvb->need_free = FALSE; + g_ptr_array_add(outstanding_Tvb,tvb); + return pushTvb(L,tvb); +} + + +WSLUA_METAMETHOD Tvb__tostring(lua_State* L) { + /* + Convert the bytes of a <<lua_class_Tvb,`Tvb`>> into a string. + This is primarily useful for debugging purposes since the string will be truncated if it is too long. + */ + Tvb tvb = checkTvb(L,1); + int len = tvb_captured_length(tvb->ws_tvb); + char* str = tvb_bytes_to_str(NULL,tvb->ws_tvb,0,len); + + lua_pushfstring(L, "TVB(%d) : %s", len, str); + + wmem_free(NULL, str); + + WSLUA_RETURN(1); /* The string. */ +} + +/* Gets registered as metamethod automatically by WSLUA_REGISTER_CLASS/META */ +static int Tvb__gc(lua_State* L) { + Tvb tvb = toTvb(L,1); + + free_Tvb(tvb); + + return 0; + +} + +WSLUA_METHOD Tvb_reported_len(lua_State* L) { + /* Obtain the reported length (length on the network) of a <<lua_class_Tvb,`Tvb`>>. */ + Tvb tvb = checkTvb(L,1); + + lua_pushnumber(L,tvb_reported_length(tvb->ws_tvb)); + WSLUA_RETURN(1); /* The reported length of the <<lua_class_Tvb,`Tvb`>>. */ +} + +WSLUA_METHOD Tvb_captured_len(lua_State* L) { + /* Obtain the captured length (amount saved in the capture process) of a <<lua_class_Tvb,`Tvb`>>. */ + Tvb tvb = checkTvb(L,1); + + lua_pushnumber(L,tvb_captured_length(tvb->ws_tvb)); + WSLUA_RETURN(1); /* The captured length of the <<lua_class_Tvb,`Tvb`>>. */ +} + +WSLUA_METHOD Tvb_len(lua_State* L) { + /* Obtain the captured length (amount saved in the capture process) of a <<lua_class_Tvb,`Tvb`>>. + Same as captured_len; kept only for backwards compatibility */ + Tvb tvb = checkTvb(L,1); + + lua_pushnumber(L,tvb_captured_length(tvb->ws_tvb)); + WSLUA_RETURN(1); /* The captured length of the <<lua_class_Tvb,`Tvb`>>. */ +} + +WSLUA_METHOD Tvb_reported_length_remaining(lua_State* L) { + /* Obtain the reported (not captured) length of packet data to end of a <<lua_class_Tvb,`Tvb`>> or 0 if the + offset is beyond the end of the <<lua_class_Tvb,`Tvb`>>. */ +#define Tvb_reported_length_remaining_OFFSET 2 /* offset */ + Tvb tvb = checkTvb(L,1); + int offset = (int) luaL_optinteger(L, Tvb_reported_length_remaining_OFFSET, 0); + + lua_pushnumber(L,tvb_reported_length_remaining(tvb->ws_tvb, offset)); + WSLUA_RETURN(1); /* The captured length of the <<lua_class_Tvb,`Tvb`>>. */ +} + +WSLUA_METHOD Tvb_bytes(lua_State* L) { + /* Obtain a <<lua_class_ByteArray,`ByteArray`>> from a <<lua_class_Tvb,`Tvb`>>. + + @since 1.99.8 + */ +#define WSLUA_OPTARG_Tvb_bytes_OFFSET 2 /* The offset (in octets) from the beginning of the <<lua_class_Tvb,`Tvb`>>. Defaults to 0. */ +#define WSLUA_OPTARG_Tvb_bytes_LENGTH 3 /* The length (in octets) of the range. Defaults to until the end of the <<lua_class_Tvb,`Tvb`>>. */ + Tvb tvb = checkTvb(L,1); + GByteArray* ba; + int offset = luaL_optint(L, WSLUA_OPTARG_Tvb_bytes_OFFSET, 0); + int len = luaL_optint(L,WSLUA_OPTARG_Tvb_bytes_LENGTH,-1); + + if (tvb->expired) { + luaL_error(L,"expired tvb"); + return 0; + } + + if (len < 0) { + len = tvb_captured_length_remaining(tvb->ws_tvb,offset); + if (len < 0) { + luaL_error(L,"out of bounds"); + return 0; + } + } else if ( (guint)(len + offset) > tvb_captured_length(tvb->ws_tvb)) { + luaL_error(L,"Range is out of bounds"); + return 0; + } + + ba = g_byte_array_new(); + g_byte_array_append(ba, tvb_get_ptr(tvb->ws_tvb, offset, len), len); + pushByteArray(L,ba); + + WSLUA_RETURN(1); /* The <<lua_class_ByteArray,`ByteArray`>> object or nil. */ +} + +WSLUA_METHOD Tvb_offset(lua_State* L) { + /* Returns the raw offset (from the beginning of the source <<lua_class_Tvb,`Tvb`>>) of a sub <<lua_class_Tvb,`Tvb`>>. */ + Tvb tvb = checkTvb(L,1); + + lua_pushnumber(L,tvb_raw_offset(tvb->ws_tvb)); + WSLUA_RETURN(1); /* The raw offset of the <<lua_class_Tvb,`Tvb`>>. */ +} + + +#if USED_FOR_DOC_PURPOSES +WSLUA_METAMETHOD Tvb__call(lua_State* L) { + /* Equivalent to tvb:range(...) */ + return 0; +} +#endif + + +WSLUA_METHOD Tvb_range(lua_State* L) { + /* Creates a <<lua_class_TvbRange,`TvbRange`>> from this <<lua_class_Tvb,`Tvb`>>. */ +#define WSLUA_OPTARG_Tvb_range_OFFSET 2 /* The offset (in octets) from the beginning of the <<lua_class_Tvb,`Tvb`>>. Defaults to 0. */ +#define WSLUA_OPTARG_Tvb_range_LENGTH 3 /* The length (in octets) of the range. Defaults to -1, which specifies the remaining bytes in the <<lua_class_Tvb,`Tvb`>>. */ + + Tvb tvb = checkTvb(L,1); + int offset = (int) luaL_optinteger(L,WSLUA_OPTARG_Tvb_range_OFFSET,0); + int len = (int) luaL_optinteger(L,WSLUA_OPTARG_Tvb_range_LENGTH,-1); + + if (push_TvbRange(L,tvb->ws_tvb,offset,len)) { + WSLUA_RETURN(1); /* The TvbRange */ + } + + return 0; +} + +WSLUA_METHOD Tvb_raw(lua_State* L) { + /* Obtain a Lua string of the binary bytes in a <<lua_class_Tvb,`Tvb`>>. + + @since 1.11.3 + */ +#define WSLUA_OPTARG_Tvb_raw_OFFSET 2 /* The position of the first byte. Default is 0, or the first byte. */ +#define WSLUA_OPTARG_Tvb_raw_LENGTH 3 /* The length of the segment to get. Default is -1, or the remaining bytes in the <<lua_class_Tvb,`Tvb`>>. */ + Tvb tvb = checkTvb(L,1); + int offset = (int) luaL_optinteger(L,WSLUA_OPTARG_Tvb_raw_OFFSET,0); + int len = (int) luaL_optinteger(L,WSLUA_OPTARG_Tvb_raw_LENGTH,-1); + + if (!tvb) return 0; + if (tvb->expired) { + luaL_error(L,"expired tvb"); + return 0; + } + + if ((guint)offset > tvb_captured_length(tvb->ws_tvb)) { + WSLUA_OPTARG_ERROR(Tvb_raw,OFFSET,"offset beyond end of Tvb"); + return 0; + } + + if (len == -1) { + len = tvb_captured_length_remaining(tvb->ws_tvb,offset); + if (len < 0) { + luaL_error(L,"out of bounds"); + return FALSE; + } + } else if ( (guint)(len + offset) > tvb_captured_length(tvb->ws_tvb)) { + luaL_error(L,"Range is out of bounds"); + return FALSE; + } + + lua_pushlstring(L, tvb_get_ptr(tvb->ws_tvb, offset, len), len); + + WSLUA_RETURN(1); /* A Lua string of the binary bytes in the <<lua_class_Tvb,`Tvb`>>. */ +} + +WSLUA_METAMETHOD Tvb__eq(lua_State* L) { + /* Checks whether contents of two <<lua_class_Tvb,`Tvb`>>s are equal. + + @since 1.99.8 + */ + Tvb tvb_l = checkTvb(L,1); + Tvb tvb_r = checkTvb(L,2); + + int len_l = tvb_captured_length(tvb_l->ws_tvb); + int len_r = tvb_captured_length(tvb_r->ws_tvb); + + /* it is not an error if their ds_tvb are different... they're just not equal */ + if (len_l == len_r) + { + const gchar* lp = tvb_get_ptr(tvb_l->ws_tvb, 0, len_l); + const gchar* rp = tvb_get_ptr(tvb_r->ws_tvb, 0, len_r); + int i = 0; + + for (; i < len_l; ++i) { + if (lp[i] != rp[i]) { + lua_pushboolean(L,0); + return 1; + } + } + lua_pushboolean(L,1); + } else { + lua_pushboolean(L,0); + } + + return 1; +} + +WSLUA_METHODS Tvb_methods[] = { + WSLUA_CLASS_FNREG(Tvb,bytes), + WSLUA_CLASS_FNREG(Tvb,range), + WSLUA_CLASS_FNREG(Tvb,offset), + WSLUA_CLASS_FNREG(Tvb,reported_len), + WSLUA_CLASS_FNREG(Tvb,reported_length_remaining), + WSLUA_CLASS_FNREG(Tvb,captured_len), + WSLUA_CLASS_FNREG(Tvb,len), + WSLUA_CLASS_FNREG(Tvb,raw), + { NULL, NULL } +}; + +WSLUA_META Tvb_meta[] = { + WSLUA_CLASS_MTREG(Tvb,eq), + WSLUA_CLASS_MTREG(Tvb,tostring), + {"__call", Tvb_range}, + { NULL, NULL } +}; + +int Tvb_register(lua_State* L) { + WSLUA_REGISTER_CLASS(Tvb); + outstanding_Tvb = g_ptr_array_new(); + return 0; +} + + + + +WSLUA_CLASS_DEFINE(TvbRange,FAIL_ON_NULL("TvbRange")); + /* + A <<lua_class_TvbRange,`TvbRange`>> represents a usable range of a <<lua_class_Tvb,`Tvb`>> and is used to extract data from the <<lua_class_Tvb,`Tvb`>> that generated it. + + <<lua_class_TvbRange,`TvbRange`>>s are created by calling a <<lua_class_Tvb,`Tvb`>> (e.g. 'tvb(offset,length)'). + A length of -1, which is the default, means to use the bytes up to the end of the <<lua_class_Tvb,`Tvb`>>. + If the <<lua_class_TvbRange,`TvbRange`>> span is outside the <<lua_class_Tvb,`Tvb`>>'s range the creation will cause a runtime error. + */ + +static void free_TvbRange(TvbRange tvbr) { + if (!(tvbr && tvbr->tvb)) return; + + if (!tvbr->tvb->expired) { + tvbr->tvb->expired = TRUE; + } else { + free_Tvb(tvbr->tvb); + g_free(tvbr); + } +} + +void clear_outstanding_TvbRange(void) { + while (outstanding_TvbRange->len) { + TvbRange tvbr = (TvbRange)g_ptr_array_remove_index_fast(outstanding_TvbRange,0); + free_TvbRange(tvbr); + } +} + + +gboolean push_TvbRange(lua_State* L, tvbuff_t* ws_tvb, int offset, int len) { + TvbRange tvbr; + + if (!ws_tvb) { + luaL_error(L,"expired tvb"); + return FALSE; + } + + if (len == -1) { + len = tvb_captured_length_remaining(ws_tvb,offset); + if (len < 0) { + luaL_error(L,"out of bounds"); + return FALSE; + } + } else if (len < -1) { + luaL_error(L, "negative length in tvb range"); + return FALSE; + } else if ( (guint)(len + offset) > tvb_captured_length(ws_tvb)) { + luaL_error(L,"Range is out of bounds"); + return FALSE; + } + + tvbr = (TvbRange)g_malloc(sizeof(struct _wslua_tvbrange)); + tvbr->tvb = (Tvb)g_malloc(sizeof(struct _wslua_tvb)); + tvbr->tvb->ws_tvb = ws_tvb; + tvbr->tvb->expired = FALSE; + tvbr->tvb->need_free = FALSE; + tvbr->offset = offset; + tvbr->len = len; + + PUSH_TVBRANGE(L,tvbr); + + return TRUE; +} + + +WSLUA_METHOD TvbRange_tvb(lua_State *L) { + /* Creates a new <<lua_class_Tvb,`Tvb`>> from a <<lua_class_TvbRange,`TvbRange`>>. */ + + TvbRange tvbr = checkTvbRange(L,1); + Tvb tvb; + + if (! (tvbr && tvbr->tvb)) return 0; + if (tvbr->tvb->expired) { + luaL_error(L,"expired tvb"); + return 0; + } + + if (tvb_offset_exists(tvbr->tvb->ws_tvb, tvbr->offset + tvbr->len -1 )) { + tvb = (Tvb)g_malloc(sizeof(struct _wslua_tvb)); + tvb->expired = FALSE; + tvb->need_free = FALSE; + tvb->ws_tvb = tvb_new_subset_length(tvbr->tvb->ws_tvb,tvbr->offset,tvbr->len); + return push_wsluaTvb(L, tvb); + } else { + luaL_error(L,"Out Of Bounds"); + return 0; + } +} + + +/* + * get a Blefuscuoan unsigned integer from a tvb + */ +WSLUA_METHOD TvbRange_uint(lua_State* L) { + /* Get a Big Endian (network order) unsigned integer from a <<lua_class_TvbRange,`TvbRange`>>. + The range must be 1-4 octets long. */ + TvbRange tvbr = checkTvbRange(L,1); + if (!(tvbr && tvbr->tvb)) return 0; + if (tvbr->tvb->expired) { + luaL_error(L,"expired tvb"); + return 0; + } + + switch (tvbr->len) { + case 1: + lua_pushnumber(L,tvb_get_guint8(tvbr->tvb->ws_tvb,tvbr->offset)); + return 1; + case 2: + lua_pushnumber(L,tvb_get_ntohs(tvbr->tvb->ws_tvb,tvbr->offset)); + return 1; + case 3: + lua_pushnumber(L,tvb_get_ntoh24(tvbr->tvb->ws_tvb,tvbr->offset)); + return 1; + case 4: + lua_pushnumber(L,tvb_get_ntohl(tvbr->tvb->ws_tvb,tvbr->offset)); + WSLUA_RETURN(1); /* The unsigned integer value. */ + default: + luaL_error(L,"TvbRange:uint() does not handle %d byte integers",tvbr->len); + return 0; + } +} + +/* + * get a Lilliputian unsigned integer from a tvb + */ +WSLUA_METHOD TvbRange_le_uint(lua_State* L) { + /* Get a Little Endian unsigned integer from a <<lua_class_TvbRange,`TvbRange`>>. + The range must be 1-4 octets long. */ + TvbRange tvbr = checkTvbRange(L,1); + if (!(tvbr && tvbr->tvb)) return 0; + if (tvbr->tvb->expired) { + luaL_error(L,"expired tvb"); + return 0; + } + + switch (tvbr->len) { + case 1: + /* XXX unsigned anyway */ + lua_pushnumber(L,(lua_Number)(guint)tvb_get_guint8(tvbr->tvb->ws_tvb,tvbr->offset)); + return 1; + case 2: + lua_pushnumber(L,tvb_get_letohs(tvbr->tvb->ws_tvb,tvbr->offset)); + return 1; + case 3: + lua_pushnumber(L,tvb_get_letoh24(tvbr->tvb->ws_tvb,tvbr->offset)); + return 1; + case 4: + lua_pushnumber(L,tvb_get_letohl(tvbr->tvb->ws_tvb,tvbr->offset)); + WSLUA_RETURN(1); /* The unsigned integer value */ + default: + luaL_error(L,"TvbRange:le_uint() does not handle %d byte integers",tvbr->len); + return 0; + } +} + +/* + * get a Blefuscuoan unsigned 64 bit integer from a tvb + */ +WSLUA_METHOD TvbRange_uint64(lua_State* L) { + /* Get a Big Endian (network order) unsigned 64 bit integer from a <<lua_class_TvbRange,`TvbRange`>>, as a <<lua_class_UInt64,`UInt64`>> object. + The range must be 1-8 octets long. */ + TvbRange tvbr = checkTvbRange(L,1); + if (!(tvbr && tvbr->tvb)) return 0; + if (tvbr->tvb->expired) { + luaL_error(L,"expired tvb"); + return 0; + } + + switch (tvbr->len) { + case 1: + pushUInt64(L,tvb_get_guint8(tvbr->tvb->ws_tvb,tvbr->offset)); + return 1; + case 2: + pushUInt64(L,tvb_get_ntohs(tvbr->tvb->ws_tvb,tvbr->offset)); + return 1; + case 3: + pushUInt64(L,tvb_get_ntoh24(tvbr->tvb->ws_tvb,tvbr->offset)); + return 1; + case 4: + pushUInt64(L,tvb_get_ntohl(tvbr->tvb->ws_tvb,tvbr->offset)); + return 1; + case 5: + pushUInt64(L,tvb_get_ntoh40(tvbr->tvb->ws_tvb,tvbr->offset)); + return 1; + case 6: + pushUInt64(L,tvb_get_ntoh48(tvbr->tvb->ws_tvb,tvbr->offset)); + return 1; + case 7: + pushUInt64(L,tvb_get_ntoh56(tvbr->tvb->ws_tvb,tvbr->offset)); + return 1; + case 8: + pushUInt64(L,tvb_get_ntoh64(tvbr->tvb->ws_tvb,tvbr->offset)); + WSLUA_RETURN(1); /* The <<lua_class_UInt64,`UInt64`>> object. */ + default: + luaL_error(L,"TvbRange:uint64() does not handle %d byte integers",tvbr->len); + return 0; + } +} + +/* + * get a Lilliputian unsigned 64 bit integer from a tvb + */ +WSLUA_METHOD TvbRange_le_uint64(lua_State* L) { + /* Get a Little Endian unsigned 64 bit integer from a <<lua_class_TvbRange,`TvbRange`>>, as a <<lua_class_UInt64,`UInt64`>> object. + The range must be 1-8 octets long. */ + TvbRange tvbr = checkTvbRange(L,1); + if (!(tvbr && tvbr->tvb)) return 0; + if (tvbr->tvb->expired) { + luaL_error(L,"expired tvb"); + return 0; + } + + switch (tvbr->len) { + case 1: + pushUInt64(L,tvb_get_guint8(tvbr->tvb->ws_tvb,tvbr->offset)); + return 1; + case 2: + pushUInt64(L,tvb_get_letohs(tvbr->tvb->ws_tvb,tvbr->offset)); + return 1; + case 3: + pushUInt64(L,tvb_get_letoh24(tvbr->tvb->ws_tvb,tvbr->offset)); + return 1; + case 4: + pushUInt64(L,tvb_get_letohl(tvbr->tvb->ws_tvb,tvbr->offset)); + return 1; + case 5: + pushUInt64(L,tvb_get_letoh40(tvbr->tvb->ws_tvb,tvbr->offset)); + return 1; + case 6: + pushUInt64(L,tvb_get_letoh48(tvbr->tvb->ws_tvb,tvbr->offset)); + return 1; + case 7: + pushUInt64(L,tvb_get_letoh56(tvbr->tvb->ws_tvb,tvbr->offset)); + return 1; + case 8: + pushUInt64(L,tvb_get_letoh64(tvbr->tvb->ws_tvb,tvbr->offset)); + WSLUA_RETURN(1); /* The <<lua_class_UInt64,`UInt64`>> object. */ + default: + luaL_error(L,"TvbRange:le_uint64() does not handle %d byte integers",tvbr->len); + return 0; + } +} + +/* + * get a Blefuscuoan signed integer from a tvb + */ +WSLUA_METHOD TvbRange_int(lua_State* L) { + /* Get a Big Endian (network order) signed integer from a <<lua_class_TvbRange,`TvbRange`>>. + The range must be 1-4 octets long. */ + TvbRange tvbr = checkTvbRange(L,1); + if (!(tvbr && tvbr->tvb)) return 0; + if (tvbr->tvb->expired) { + luaL_error(L,"expired tvb"); + return 0; + } + + switch (tvbr->len) { + case 1: + lua_pushnumber(L,tvb_get_gint8(tvbr->tvb->ws_tvb,tvbr->offset)); + return 1; + case 2: + lua_pushnumber(L,tvb_get_ntohis(tvbr->tvb->ws_tvb,tvbr->offset)); + return 1; + case 3: + lua_pushnumber(L,tvb_get_ntohi24(tvbr->tvb->ws_tvb,tvbr->offset)); + return 1; + case 4: + lua_pushnumber(L,tvb_get_ntohil(tvbr->tvb->ws_tvb,tvbr->offset)); + WSLUA_RETURN(1); /* The signed integer value. */ + /* + * XXX: + * lua uses double so we have 52 bits to play with + * we are missing 5 and 6 byte integers within lua's range + * and 64 bit integers are not supported (there's a lib for + * lua that does). + */ + default: + luaL_error(L,"TvbRange:int() does not handle %d byte integers",tvbr->len); + return 0; + } +} + +/* + * get a Lilliputian signed integer from a tvb + */ +WSLUA_METHOD TvbRange_le_int(lua_State* L) { + /* Get a Little Endian signed integer from a <<lua_class_TvbRange,`TvbRange`>>. + The range must be 1-4 octets long. */ + TvbRange tvbr = checkTvbRange(L,1); + if (!(tvbr && tvbr->tvb)) return 0; + if (tvbr->tvb->expired) { + luaL_error(L,"expired tvb"); + return 0; + } + + switch (tvbr->len) { + case 1: + lua_pushnumber(L,tvb_get_gint8(tvbr->tvb->ws_tvb,tvbr->offset)); + return 1; + case 2: + lua_pushnumber(L,tvb_get_letohis(tvbr->tvb->ws_tvb,tvbr->offset)); + return 1; + case 3: + lua_pushnumber(L,tvb_get_letohi24(tvbr->tvb->ws_tvb,tvbr->offset)); + return 1; + case 4: + lua_pushnumber(L,tvb_get_letohil(tvbr->tvb->ws_tvb,tvbr->offset)); + WSLUA_RETURN(1); /* The signed integer value. */ + default: + luaL_error(L,"TvbRange:le_int() does not handle %d byte integers",tvbr->len); + return 0; + } +} + +/* + * get a Blefuscuoan signed 64 bit integer from a tvb + */ +WSLUA_METHOD TvbRange_int64(lua_State* L) { + /* Get a Big Endian (network order) signed 64 bit integer from a <<lua_class_TvbRange,`TvbRange`>>, as an <<lua_class_Int64,`Int64`>> object. + The range must be 1-8 octets long. */ + TvbRange tvbr = checkTvbRange(L,1); + if (!(tvbr && tvbr->tvb)) return 0; + if (tvbr->tvb->expired) { + luaL_error(L,"expired tvb"); + return 0; + } + + switch (tvbr->len) { + case 1: + pushInt64(L,tvb_get_gint8(tvbr->tvb->ws_tvb,tvbr->offset)); + return 1; + case 2: + pushInt64(L,tvb_get_ntohis(tvbr->tvb->ws_tvb,tvbr->offset)); + return 1; + case 3: + pushInt64(L,tvb_get_ntohi24(tvbr->tvb->ws_tvb,tvbr->offset)); + return 1; + case 4: + pushInt64(L,tvb_get_ntohil(tvbr->tvb->ws_tvb,tvbr->offset)); + return 1; + case 5: + pushInt64(L,tvb_get_ntohi40(tvbr->tvb->ws_tvb,tvbr->offset)); + return 1; + case 6: + pushInt64(L,tvb_get_ntohi48(tvbr->tvb->ws_tvb,tvbr->offset)); + return 1; + case 7: + pushInt64(L,tvb_get_ntohi56(tvbr->tvb->ws_tvb,tvbr->offset)); + return 1; + case 8: + pushInt64(L,tvb_get_ntohi64(tvbr->tvb->ws_tvb,tvbr->offset)); + WSLUA_RETURN(1); /* The <<lua_class_Int64,`Int64`>> object. */ + default: + luaL_error(L,"TvbRange:int64() does not handle %d byte integers",tvbr->len); + return 0; + } +} + +/* + * get a Lilliputian signed 64 bit integer from a tvb + */ +WSLUA_METHOD TvbRange_le_int64(lua_State* L) { + /* Get a Little Endian signed 64 bit integer from a <<lua_class_TvbRange,`TvbRange`>>, as an <<lua_class_Int64,`Int64`>> object. + The range must be 1-8 octets long. */ + TvbRange tvbr = checkTvbRange(L,1); + if (!(tvbr && tvbr->tvb)) return 0; + if (tvbr->tvb->expired) { + luaL_error(L,"expired tvb"); + return 0; + } + + switch (tvbr->len) { + case 1: + pushInt64(L,tvb_get_gint8(tvbr->tvb->ws_tvb,tvbr->offset)); + return 1; + case 2: + pushInt64(L,tvb_get_letohis(tvbr->tvb->ws_tvb,tvbr->offset)); + return 1; + case 3: + pushInt64(L,tvb_get_letohi24(tvbr->tvb->ws_tvb,tvbr->offset)); + return 1; + case 4: + pushInt64(L,tvb_get_letohil(tvbr->tvb->ws_tvb,tvbr->offset)); + return 1; + case 5: + pushInt64(L,tvb_get_letohi40(tvbr->tvb->ws_tvb,tvbr->offset)); + return 1; + case 6: + pushInt64(L,tvb_get_letohi48(tvbr->tvb->ws_tvb,tvbr->offset)); + return 1; + case 7: + pushInt64(L,tvb_get_letohi56(tvbr->tvb->ws_tvb,tvbr->offset)); + return 1; + case 8: + pushInt64(L,tvb_get_letohi64(tvbr->tvb->ws_tvb,tvbr->offset)); + WSLUA_RETURN(1); /* The <<lua_class_Int64,`Int64`>> object. */ + default: + luaL_error(L,"TvbRange:le_int64() does not handle %d byte integers",tvbr->len); + return 0; + } +} + +/* + * get a Blefuscuoan float + */ +WSLUA_METHOD TvbRange_float(lua_State* L) { + /* Get a Big Endian (network order) floating point number from a <<lua_class_TvbRange,`TvbRange`>>. + The range must be 4 or 8 octets long. */ + TvbRange tvbr = checkTvbRange(L,1); + if (!(tvbr && tvbr->tvb)) return 0; + if (tvbr->tvb->expired) { + luaL_error(L,"expired tvb"); + return 0; + } + + switch (tvbr->len) { + case 4: + lua_pushnumber(L,(double)tvb_get_ntohieee_float(tvbr->tvb->ws_tvb,tvbr->offset)); + return 1; + case 8: + lua_pushnumber(L,tvb_get_ntohieee_double(tvbr->tvb->ws_tvb,tvbr->offset)); + WSLUA_RETURN(1); /* The floating point value. */ + default: + luaL_error(L,"TvbRange:float() does not handle %d byte floating numbers",tvbr->len); + return 0; + } +} + +/* + * get a Lilliputian float + */ +WSLUA_METHOD TvbRange_le_float(lua_State* L) { + /* Get a Little Endian floating point number from a <<lua_class_TvbRange,`TvbRange`>>. + The range must be 4 or 8 octets long. */ + TvbRange tvbr = checkTvbRange(L,1); + if (!(tvbr && tvbr->tvb)) return 0; + + switch (tvbr->len) { + case 4: + lua_pushnumber(L,tvb_get_letohieee_float(tvbr->tvb->ws_tvb,tvbr->offset)); + return 1; + case 8: + lua_pushnumber(L,tvb_get_letohieee_double(tvbr->tvb->ws_tvb,tvbr->offset)); + WSLUA_RETURN(1); /* The floating point value. */ + default: + luaL_error(L,"TvbRange:le_float() does not handle %d byte floating numbers",tvbr->len); + return 0; + } +} + +WSLUA_METHOD TvbRange_ipv4(lua_State* L) { + /* Get an IPv4 Address from a <<lua_class_TvbRange,`TvbRange`>>, as an <<lua_class_Address,`Address`>> object. */ + TvbRange tvbr = checkTvbRange(L,1); + Address addr; + + if ( !(tvbr && tvbr->tvb)) return 0; + if (tvbr->tvb->expired) { + luaL_error(L,"expired tvb"); + return 0; + } + + if (tvbr->len != 4) { + WSLUA_ERROR(TvbRange_ipv4,"The range must be 4 octets long"); + return 0; + } + + addr = g_new(address,1); + alloc_address_tvb(NULL,addr,AT_IPv4,sizeof(guint32),tvbr->tvb->ws_tvb,tvbr->offset); + pushAddress(L,addr); + + WSLUA_RETURN(1); /* The IPv4 <<lua_class_Address,`Address`>> object. */ +} + +WSLUA_METHOD TvbRange_le_ipv4(lua_State* L) { + /* Get an Little Endian IPv4 Address from a <<lua_class_TvbRange,`TvbRange`>>, as an <<lua_class_Address,`Address`>> object. */ + TvbRange tvbr = checkTvbRange(L,1); + Address addr; + guint32 ip_addr; + + if ( !(tvbr && tvbr->tvb)) return 0; + if (tvbr->tvb->expired) { + luaL_error(L,"expired tvb"); + return 0; + } + + if (tvbr->len != 4) { + WSLUA_ERROR(TvbRange_ipv4,"The range must be 4 octets long"); + return 0; + } + + addr = g_new(address,1); + ip_addr = GUINT32_SWAP_LE_BE(tvb_get_ipv4(tvbr->tvb->ws_tvb,tvbr->offset)); + alloc_address_wmem(NULL, addr, AT_IPv4, sizeof(ip_addr), &ip_addr); + pushAddress(L,addr); + + WSLUA_RETURN(1); /* The IPv4 <<lua_class_Address,`Address`>> object. */ +} + +WSLUA_METHOD TvbRange_ipv6(lua_State* L) { + /* Get an IPv6 Address from a <<lua_class_TvbRange,`TvbRange`>>, as an <<lua_class_Address,`Address`>> object. */ + TvbRange tvbr = checkTvbRange(L,1); + Address addr; + + if ( !(tvbr && tvbr->tvb)) return 0; + if (tvbr->tvb->expired) { + luaL_error(L,"expired tvb"); + return 0; + } + + if (tvbr->len != 16) { + WSLUA_ERROR(TvbRange_ipv6,"The range must be 16 octets long"); + return 0; + } + + addr = g_new(address,1); + alloc_address_tvb(NULL,addr,AT_IPv6,16,tvbr->tvb->ws_tvb,tvbr->offset); + pushAddress(L,addr); + + WSLUA_RETURN(1); /* The IPv6 <<lua_class_Address,`Address`>> object. */ +} + +WSLUA_METHOD TvbRange_ether(lua_State* L) { + /* Get an Ethernet Address from a <<lua_class_TvbRange,`TvbRange`>>, as an <<lua_class_Address,`Address`>> object. */ + TvbRange tvbr = checkTvbRange(L,1); + Address addr; + + if ( !(tvbr && tvbr->tvb)) return 0; + if (tvbr->tvb->expired) { + luaL_error(L,"expired tvb"); + return 0; + } + + if (tvbr->len != 6) { + WSLUA_ERROR(TvbRange_ether,"The range must be 6 bytes long"); + return 0; + } + + addr = g_new(address,1); + alloc_address_tvb(NULL,addr,AT_ETHER,6,tvbr->tvb->ws_tvb,tvbr->offset); + pushAddress(L,addr); + + WSLUA_RETURN(1); /* The Ethernet <<lua_class_Address,`Address`>> object. */ +} + +WSLUA_METHOD TvbRange_nstime(lua_State* L) { + /* Obtain a time_t structure from a <<lua_class_TvbRange,`TvbRange`>>, as an <<lua_class_NSTime,`NSTime`>> object. */ +#define WSLUA_OPTARG_TvbRange_nstime_ENCODING 2 /* An optional ENC_* encoding value to use */ + TvbRange tvbr = checkTvbRange(L,1); + NSTime nstime; + const guint encoding = (guint) luaL_optinteger(L, WSLUA_OPTARG_TvbRange_nstime_ENCODING, 0); + + if ( !(tvbr && tvbr->tvb)) return 0; + if (tvbr->tvb->expired) { + luaL_error(L,"expired tvb"); + return 0; + } + + if (encoding & ~ENC_STR_TIME_MASK) { + WSLUA_OPTARG_ERROR(TvbRange_nstime, ENCODING, "invalid encoding value"); + return 0; + } + + nstime = g_new(nstime_t,1); + + if (encoding == 0) { + if (tvbr->len == 4) { + nstime->secs = tvb_get_ntohl(tvbr->tvb->ws_tvb, tvbr->offset); + nstime->nsecs = 0; + } else if (tvbr->len == 8) { + nstime->secs = tvb_get_ntohl(tvbr->tvb->ws_tvb, tvbr->offset); + nstime->nsecs = tvb_get_ntohl(tvbr->tvb->ws_tvb, tvbr->offset + 4); + } else { + g_free(nstime); + WSLUA_ERROR(TvbRange_nstime,"The range must be 4 or 8 bytes long"); + return 0; + } + pushNSTime(L, nstime); + lua_pushinteger(L, tvbr->len); + } + else { + gint endoff = 0; + nstime_t *retval = tvb_get_string_time(tvbr->tvb->ws_tvb, tvbr->offset, tvbr->len, + encoding, nstime, &endoff); + if (!retval || endoff == 0) { + g_free(nstime); + /* push nil nstime and offset */ + lua_pushnil(L); + lua_pushnil(L); + } + else { + pushNSTime(L, nstime); + lua_pushinteger(L, endoff); + } + } + + WSLUA_RETURN(2); /* The <<lua_class_NSTime,`NSTime`>> object and number of bytes used, or nil on failure. */ +} + +WSLUA_METHOD TvbRange_le_nstime(lua_State* L) { + /* Obtain a nstime from a <<lua_class_TvbRange,`TvbRange`>>, as an <<lua_class_NSTime,`NSTime`>> object. */ + TvbRange tvbr = checkTvbRange(L,1); + NSTime nstime; + + if ( !(tvbr && tvbr->tvb)) return 0; + if (tvbr->tvb->expired) { + luaL_error(L,"expired tvb"); + return 0; + } + + nstime = g_new(nstime_t,1); + + if (tvbr->len == 4) { + nstime->secs = tvb_get_letohl(tvbr->tvb->ws_tvb, tvbr->offset); + nstime->nsecs = 0; + } else if (tvbr->len == 8) { + nstime->secs = tvb_get_letohl(tvbr->tvb->ws_tvb, tvbr->offset); + nstime->nsecs = tvb_get_letohl(tvbr->tvb->ws_tvb, tvbr->offset + 4); + } else { + g_free(nstime); + WSLUA_ERROR(TvbRange_nstime,"The range must be 4 or 8 bytes long"); + return 0; + } + + pushNSTime(L, nstime); + + WSLUA_RETURN(1); /* The <<lua_class_NSTime,`NSTime`>> object. */ +} + +WSLUA_METHOD TvbRange_string(lua_State* L) { + /* Obtain a string from a <<lua_class_TvbRange,`TvbRange`>>. */ +#define WSLUA_OPTARG_TvbRange_string_ENCODING 2 /* The encoding to use. Defaults to ENC_ASCII. */ + TvbRange tvbr = checkTvbRange(L,1); + guint encoding = (guint)luaL_optinteger(L,WSLUA_OPTARG_TvbRange_string_ENCODING, ENC_ASCII|ENC_NA); + char * str; + + if ( !(tvbr && tvbr->tvb)) return 0; + if (tvbr->tvb->expired) { + luaL_error(L,"expired tvb"); + return 0; + } + + str = (gchar*)tvb_get_string_enc(NULL,tvbr->tvb->ws_tvb,tvbr->offset,tvbr->len,encoding); + lua_pushlstring(L, str, strlen(str)); + wmem_free(NULL, str); + + WSLUA_RETURN(1); /* A string containing all bytes in the <<lua_class_TvbRange,`TvbRange`>> including all zeroes (e.g., "a\000bc\000"). */ +} + +static int TvbRange_ustring_any(lua_State* L, gboolean little_endian) { + /* Obtain a UTF-16 encoded string from a <<lua_class_TvbRange,`TvbRange`>>. */ + TvbRange tvbr = checkTvbRange(L,1); + gchar * str; + + if ( !(tvbr && tvbr->tvb)) return 0; + if (tvbr->tvb->expired) { + luaL_error(L,"expired tvb"); + return 0; + } + + str = (gchar*)tvb_get_string_enc(NULL,tvbr->tvb->ws_tvb,tvbr->offset,tvbr->len,(little_endian ? ENC_UTF_16|ENC_LITTLE_ENDIAN : ENC_UTF_16|ENC_BIG_ENDIAN)); + lua_pushlstring(L, str, strlen(str)); + wmem_free(NULL, str); + + return 1; /* The string */ +} + +WSLUA_METHOD TvbRange_ustring(lua_State* L) { + /* Obtain a Big Endian (network order) UTF-16 encoded string from a <<lua_class_TvbRange,`TvbRange`>>. */ + WSLUA_RETURN(TvbRange_ustring_any(L, FALSE)); /* A string containing all bytes in the <<lua_class_TvbRange,`TvbRange`>> including all zeroes (e.g., "a\000bc\000"). */ +} + +WSLUA_METHOD TvbRange_le_ustring(lua_State* L) { + /* Obtain a Little Endian UTF-16 encoded string from a <<lua_class_TvbRange,`TvbRange`>>. */ + WSLUA_RETURN(TvbRange_ustring_any(L, TRUE)); /* A string containing all bytes in the <<lua_class_TvbRange,`TvbRange`>> including all zeroes (e.g., "a\000bc\000"). */ +} + +WSLUA_METHOD TvbRange_stringz(lua_State* L) { + /* Obtain a zero terminated string from a <<lua_class_TvbRange,`TvbRange`>>. */ +#define WSLUA_OPTARG_TvbRange_stringz_ENCODING 2 /* The encoding to use. Defaults to ENC_ASCII. */ + TvbRange tvbr = checkTvbRange(L,1); + guint encoding = (guint)luaL_optinteger(L,WSLUA_OPTARG_TvbRange_stringz_ENCODING, ENC_ASCII|ENC_NA); + gint offset; + gunichar2 uchar; + gchar *str; + + if ( !(tvbr && tvbr->tvb)) return 0; + if (tvbr->tvb->expired) { + luaL_error(L,"expired tvb"); + return 0; + } + + switch (encoding & ENC_CHARENCODING_MASK) { + + case ENC_UTF_16: + case ENC_UCS_2: + offset = tvbr->offset; + do { + if (!tvb_bytes_exist (tvbr->tvb->ws_tvb, offset, 2)) { + luaL_error(L,"out of bounds"); + return 0; + } + /* Endianness doesn't matter when looking for null */ + uchar = tvb_get_ntohs (tvbr->tvb->ws_tvb, offset); + offset += 2; + } while(uchar != 0); + break; + + default: + if (tvb_find_guint8 (tvbr->tvb->ws_tvb, tvbr->offset, -1, 0) == -1) { + luaL_error(L,"out of bounds"); + return 0; + } + break; + } + + str = (gchar*)tvb_get_stringz_enc(NULL,tvbr->tvb->ws_tvb,tvbr->offset,NULL,encoding); + lua_pushstring(L, str); + wmem_free(NULL, str); + + WSLUA_RETURN(1); /* The string containing all bytes in the <<lua_class_TvbRange,`TvbRange`>> up to the first terminating zero. */ +} + +WSLUA_METHOD TvbRange_strsize(lua_State* L) { + /* + Find the size of a zero terminated string from a <<lua_class_TvbRange,`TvbRange`>>. + The size of the string includes the terminating zero. + + @since 1.11.3 + */ +#define WSLUA_OPTARG_TvbRange_strsize_ENCODING 2 /* The encoding to use. Defaults to ENC_ASCII. */ + TvbRange tvbr = checkTvbRange(L,1); + guint encoding = (guint)luaL_optinteger(L,WSLUA_OPTARG_TvbRange_strsize_ENCODING, ENC_ASCII|ENC_NA); + gint offset; + gunichar2 uchar; + + if ( !(tvbr && tvbr->tvb)) return 0; + if (tvbr->tvb->expired) { + luaL_error(L,"expired tvb"); + return 0; + } + + switch (encoding & ENC_CHARENCODING_MASK) { + + case ENC_UTF_16: + case ENC_UCS_2: + offset = tvbr->offset; + do { + if (!tvb_bytes_exist (tvbr->tvb->ws_tvb, offset, 2)) { + luaL_error(L,"out of bounds"); + return 0; + } + /* Endianness doesn't matter when looking for null */ + uchar = tvb_get_ntohs (tvbr->tvb->ws_tvb, offset); + offset += 2; + } while (uchar != 0); + lua_pushinteger(L, tvb_unicode_strsize(tvbr->tvb->ws_tvb, tvbr->offset)); + break; + + default: + if (tvb_find_guint8 (tvbr->tvb->ws_tvb, tvbr->offset, -1, 0) == -1) { + luaL_error(L,"out of bounds"); + return 0; + } + lua_pushinteger(L, tvb_strsize(tvbr->tvb->ws_tvb, tvbr->offset)); + break; + } + + WSLUA_RETURN(1); /* Length of the zero terminated string. */ +} + + +static int TvbRange_ustringz_any(lua_State* L, gboolean little_endian) { + /* Obtain a zero terminated string from a TvbRange */ + gint count; + TvbRange tvbr = checkTvbRange(L,1); + gint offset; + gunichar2 uchar; + gchar *str; + + if ( !(tvbr && tvbr->tvb)) return 0; + if (tvbr->tvb->expired) { + luaL_error(L,"expired tvb"); + return 0; + } + + offset = tvbr->offset; + do { + if (!tvb_bytes_exist (tvbr->tvb->ws_tvb, offset, 2)) { + luaL_error(L,"out of bounds"); + return 0; + } + /* Endianness doesn't matter when looking for null */ + uchar = tvb_get_ntohs (tvbr->tvb->ws_tvb, offset); + offset += 2; + } while (uchar != 0); + + str = (gchar*)tvb_get_stringz_enc(NULL,tvbr->tvb->ws_tvb,tvbr->offset,&count, + (little_endian ? ENC_UTF_16|ENC_LITTLE_ENDIAN : ENC_UTF_16|ENC_BIG_ENDIAN)); + lua_pushstring(L, str); + lua_pushinteger(L,count); + wmem_free(NULL, str); + + return 2; /* The zero terminated string, the length found in tvbr */ +} + +WSLUA_METHOD TvbRange_ustringz(lua_State* L) { + /* Obtain a Big Endian (network order) UTF-16 encoded zero terminated string from a <<lua_class_TvbRange,`TvbRange`>>. */ + WSLUA_RETURN(TvbRange_ustringz_any(L, FALSE)); /* Two return values: the zero terminated string, and the length. */ +} + +WSLUA_METHOD TvbRange_le_ustringz(lua_State* L) { + /* Obtain a Little Endian UTF-16 encoded zero terminated string from a TvbRange */ + WSLUA_RETURN(TvbRange_ustringz_any(L, TRUE)); /* Two return values: the zero terminated string, and the length. */ +} + +WSLUA_METHOD TvbRange_bytes(lua_State* L) { + /* Obtain a <<lua_class_ByteArray,`ByteArray`>> from a <<lua_class_TvbRange,`TvbRange`>>. + + Starting in 1.11.4, this function also takes an optional `encoding` argument, + which can be set to `ENC_STR_HEX` to decode a hex-string from the <<lua_class_TvbRange,`TvbRange`>> + into the returned <<lua_class_ByteArray,`ByteArray`>>. The `encoding` can be bitwise-or'ed with one + or more separator encodings, such as `ENC_SEP_COLON`, to allow separators + to occur between each pair of hex characters. + + The return value also now returns the number of bytes used as a second return value. + + On failure or error, nil is returned for both return values. + + [NOTE] + ==== + The encoding type of the hex string should also be set, for example + `ENC_ASCII` or `ENC_UTF_8`, along with `ENC_STR_HEX`. + ==== + */ +#define WSLUA_OPTARG_TvbRange_bytes_ENCODING 2 /* An optional ENC_* encoding value to use */ + TvbRange tvbr = checkTvbRange(L,1); + GByteArray* ba; + guint8* raw; + const guint encoding = (guint)luaL_optinteger(L, WSLUA_OPTARG_TvbRange_bytes_ENCODING, 0); + + + if ( !(tvbr && tvbr->tvb)) return 0; + if (tvbr->tvb->expired) { + luaL_error(L,"expired tvb"); + return 0; + } + + if (encoding == 0) { + ba = g_byte_array_new(); + raw = (guint8 *)tvb_memdup(NULL,tvbr->tvb->ws_tvb,tvbr->offset,tvbr->len); + g_byte_array_append(ba,raw,tvbr->len); + wmem_free(NULL, raw); + pushByteArray(L,ba); + lua_pushinteger(L, tvbr->len); + } + else if ((encoding & ENC_STR_HEX) == 0) { + WSLUA_OPTARG_ERROR(TvbRange_nstime, ENCODING, "invalid encoding value"); + } + else { + gint endoff = 0; + GByteArray* retval; + + ba = g_byte_array_new(); + retval = tvb_get_string_bytes(tvbr->tvb->ws_tvb, tvbr->offset, tvbr->len, + encoding, ba, &endoff); + if (!retval || endoff == 0) { + g_byte_array_free(ba, TRUE); + /* push nil nstime and offset */ + lua_pushnil(L); + lua_pushnil(L); + } + else { + pushByteArray(L,ba); + lua_pushinteger(L, endoff); + } + } + + WSLUA_RETURN(2); /* The <<lua_class_ByteArray,`ByteArray`>> object or nil, and number of bytes consumed or nil. */ +} + +WSLUA_METHOD TvbRange_bitfield(lua_State* L) { + /* Get a bitfield from a <<lua_class_TvbRange,`TvbRange`>>. */ +#define WSLUA_OPTARG_TvbRange_bitfield_POSITION 2 /* The bit offset (link:https://en.wikipedia.org/wiki/Bit_numbering#MSB_0_bit_numbering[MSB 0 bit numbering]) from the beginning of the <<lua_class_TvbRange,`TvbRange`>>. Defaults to 0. */ +#define WSLUA_OPTARG_TvbRange_bitfield_LENGTH 3 /* The length in bits of the field. Defaults to 1. */ + + TvbRange tvbr = checkTvbRange(L,1); + int pos = (int)luaL_optinteger(L,WSLUA_OPTARG_TvbRange_bitfield_POSITION,0); + int len = (int)luaL_optinteger(L,WSLUA_OPTARG_TvbRange_bitfield_LENGTH,1); + + if (!(tvbr && tvbr->tvb)) return 0; + if (tvbr->tvb->expired) { + luaL_error(L,"expired tvb"); + return 0; + } + + if ((pos+len) > (tvbr->len<<3)) { + luaL_error(L, "Requested bitfield out of range"); + return 0; + } + + if (len <= 8) { + lua_pushnumber(L,(lua_Number)(guint)tvb_get_bits8(tvbr->tvb->ws_tvb,tvbr->offset*8 + pos, len)); + return 1; + } else if (len <= 16) { + lua_pushnumber(L,tvb_get_bits16(tvbr->tvb->ws_tvb,tvbr->offset*8 + pos, len, FALSE)); + return 1; + } else if (len <= 32) { + lua_pushnumber(L,tvb_get_bits32(tvbr->tvb->ws_tvb,tvbr->offset*8 + pos, len, FALSE)); + return 1; + } else if (len <= 64) { + pushUInt64(L,tvb_get_bits64(tvbr->tvb->ws_tvb,tvbr->offset*8 + pos, len, FALSE)); + WSLUA_RETURN(1); /* The bitfield value */ + } else { + luaL_error(L,"TvbRange:bitfield() does not handle %d bits",len); + return 0; + } +} + +WSLUA_METHOD TvbRange_range(lua_State* L) { + /* Creates a sub-<<lua_class_TvbRange,`TvbRange`>> from this <<lua_class_TvbRange,`TvbRange`>>. */ +#define WSLUA_OPTARG_TvbRange_range_OFFSET 2 /* The offset (in octets) from the beginning of the <<lua_class_TvbRange,`TvbRange`>>. Defaults to 0. */ +#define WSLUA_OPTARG_TvbRange_range_LENGTH 3 /* The length (in octets) of the range. Defaults to until the end of the <<lua_class_TvbRange,`TvbRange`>>. */ + + TvbRange tvbr = checkTvbRange(L,1); + int offset = (int)luaL_optinteger(L,WSLUA_OPTARG_TvbRange_range_OFFSET,0); + int len; + + if (!(tvbr && tvbr->tvb)) return 0; + + len = (int)luaL_optinteger(L,WSLUA_OPTARG_TvbRange_range_LENGTH,tvbr->len-offset); + + if (tvbr->tvb->expired) { + luaL_error(L,"expired tvb"); + return 0; + } + + if (offset >= tvbr->len || (len + offset) > tvbr->len) { + luaL_error(L,"Range is out of bounds"); + return 0; + } + + if (push_TvbRange(L,tvbr->tvb->ws_tvb,tvbr->offset+offset,len)) { + WSLUA_RETURN(1); /* The <<lua_class_TvbRange,`TvbRange`>>. */ + } + + return 0; +} + +WSLUA_METHOD TvbRange_uncompress(lua_State* L) { + /* Obtain an uncompressed <<lua_class_TvbRange,`TvbRange`>> from a <<lua_class_TvbRange,`TvbRange`>> */ +#define WSLUA_ARG_TvbRange_uncompress_NAME 2 /* The name to be given to the new data-source. */ + TvbRange tvbr = checkTvbRange(L,1); +#ifdef HAVE_ZLIB + const gchar* name = luaL_optstring(L,WSLUA_ARG_TvbRange_uncompress_NAME,"Uncompressed"); + tvbuff_t *uncompr_tvb; +#endif + + if (!(tvbr && tvbr->tvb)) return 0; + + if (tvbr->tvb->expired) { + luaL_error(L,"expired tvb"); + return 0; + } + +#ifdef HAVE_ZLIB + uncompr_tvb = tvb_child_uncompress(tvbr->tvb->ws_tvb, tvbr->tvb->ws_tvb, tvbr->offset, tvbr->len); + if (uncompr_tvb) { + add_new_data_source (lua_pinfo, uncompr_tvb, name); + if (push_TvbRange(L,uncompr_tvb,0,tvb_captured_length(uncompr_tvb))) { + WSLUA_RETURN(1); /* The <<lua_class_TvbRange,`TvbRange`>>. */ + } + } +#else + luaL_error(L,"Missing support for ZLIB"); +#endif + + return 0; +} + +/* Gets registered as metamethod automatically by WSLUA_REGISTER_CLASS/META */ +static int TvbRange__gc(lua_State* L) { + TvbRange tvbr = checkTvbRange(L,1); + + free_TvbRange(tvbr); + + return 0; + +} + +WSLUA_METHOD TvbRange_len(lua_State* L) { + /* Obtain the length of a <<lua_class_TvbRange,`TvbRange`>>. */ + TvbRange tvbr = checkTvbRange(L,1); + + if (!(tvbr && tvbr->tvb)) return 0; + if (tvbr->tvb->expired) { + luaL_error(L,"expired tvb"); + return 0; + } + lua_pushnumber(L,(lua_Number)tvbr->len); + return 1; +} + +WSLUA_METHOD TvbRange_offset(lua_State* L) { + /* Obtain the offset in a <<lua_class_TvbRange,`TvbRange`>>. */ + TvbRange tvbr = checkTvbRange(L,1); + + if (!(tvbr && tvbr->tvb)) return 0; + if (tvbr->tvb->expired) { + luaL_error(L,"expired tvb"); + return 0; + } + lua_pushnumber(L,(lua_Number)tvbr->offset); + return 1; +} + +WSLUA_METHOD TvbRange_raw(lua_State* L) { + /* Obtain a Lua string of the binary bytes in a <<lua_class_TvbRange,`TvbRange`>>. + + @since 1.11.3 + */ +#define WSLUA_OPTARG_TvbRange_raw_OFFSET 2 /* The position of the first byte within the range. Default is 0, or first byte. */ +#define WSLUA_OPTARG_TvbRange_raw_LENGTH 3 /* The length of the segment to get. Default is -1, or the remaining bytes in the range. */ + TvbRange tvbr = checkTvbRange(L,1); + int offset = (int)luaL_optinteger(L,WSLUA_OPTARG_TvbRange_raw_OFFSET,0); + int len = (int)luaL_optinteger(L,WSLUA_OPTARG_TvbRange_raw_LENGTH,-1); + + if (!tvbr || !tvbr->tvb) return 0; + if (tvbr->tvb->expired) { + luaL_error(L,"expired tvb"); + return 0; + } + + if (offset < 0) { + WSLUA_OPTARG_ERROR(TvbRange_raw,OFFSET,"offset before start of TvbRange"); + return 0; + } + if (offset > tvbr->len) { + WSLUA_OPTARG_ERROR(TvbRange_raw,OFFSET,"offset beyond end of TvbRange"); + return 0; + } + + if (len == -1) { + len = tvbr->len - offset; + } + if (len < 0) { + luaL_error(L,"out of bounds"); + return FALSE; + } else if ( (len + offset) > tvbr->len) { + luaL_error(L,"Range is out of bounds"); + return FALSE; + } + + lua_pushlstring(L, tvb_get_ptr(tvbr->tvb->ws_tvb, tvbr->offset+offset, len), len); + + WSLUA_RETURN(1); /* A Lua string of the binary bytes in the <<lua_class_TvbRange,`TvbRange`>>. */ +} + +WSLUA_METAMETHOD TvbRange__eq(lua_State* L) { + /* Checks whether the contents of two <<lua_class_TvbRange,`TvbRange`>>s are equal. + + @since 1.99.8 + */ + TvbRange tvb_l = checkTvbRange(L,1); + TvbRange tvb_r = checkTvbRange(L,2); + + /* it is not an error if their ds_tvb are different... they're just not equal */ + if (tvb_l->len == tvb_r->len && + tvb_l->len <= tvb_captured_length_remaining(tvb_l->tvb->ws_tvb, tvb_l->offset) && + tvb_r->len <= tvb_captured_length_remaining(tvb_r->tvb->ws_tvb, tvb_r->offset)) + { + const gchar* lp = tvb_get_ptr(tvb_l->tvb->ws_tvb, tvb_l->offset, tvb_l->len); + const gchar* rp = tvb_get_ptr(tvb_r->tvb->ws_tvb, tvb_r->offset, tvb_r->len); + int i = 0; + + for (; i < tvb_r->len; ++i) { + if (lp[i] != rp[i]) { + lua_pushboolean(L,0); + return 1; + } + } + lua_pushboolean(L,1); + } else { + lua_pushboolean(L,0); + } + + return 1; +} + +WSLUA_METAMETHOD TvbRange__tostring(lua_State* L) { + /* + Converts the <<lua_class_TvbRange,`TvbRange`>> into a string. + The string can be truncated, so this is primarily useful for debugging or in cases where truncation is preferred, e.g. "67:89:AB:...". + */ + TvbRange tvbr = checkTvbRange(L,1); + char* str = NULL; + + if (!(tvbr && tvbr->tvb)) return 0; + if (tvbr->tvb->expired) { + luaL_error(L,"expired tvb"); + return 0; + } + + if (tvbr->len == 0) { + lua_pushstring(L, "<EMPTY>"); + } else { + str = tvb_bytes_to_str(NULL,tvbr->tvb->ws_tvb,tvbr->offset,tvbr->len); + lua_pushstring(L,str); + wmem_free(NULL, str); + } + + WSLUA_RETURN(1); /* A Lua hex string of the <<lua_class_TvbRange,`TvbRange`>> truncated to 24 bytes. */ +} + +WSLUA_METHODS TvbRange_methods[] = { + WSLUA_CLASS_FNREG(TvbRange,uint), + WSLUA_CLASS_FNREG(TvbRange,le_uint), + WSLUA_CLASS_FNREG(TvbRange,int), + WSLUA_CLASS_FNREG(TvbRange,le_int), + WSLUA_CLASS_FNREG(TvbRange,uint64), + WSLUA_CLASS_FNREG(TvbRange,le_uint64), + WSLUA_CLASS_FNREG(TvbRange,int64), + WSLUA_CLASS_FNREG(TvbRange,le_int64), + WSLUA_CLASS_FNREG(TvbRange,float), + WSLUA_CLASS_FNREG(TvbRange,le_float), + WSLUA_CLASS_FNREG(TvbRange,ether), + WSLUA_CLASS_FNREG(TvbRange,ipv4), + WSLUA_CLASS_FNREG(TvbRange,le_ipv4), + WSLUA_CLASS_FNREG(TvbRange,ipv6), + WSLUA_CLASS_FNREG(TvbRange,nstime), + WSLUA_CLASS_FNREG(TvbRange,le_nstime), + WSLUA_CLASS_FNREG(TvbRange,string), + WSLUA_CLASS_FNREG(TvbRange,stringz), + WSLUA_CLASS_FNREG(TvbRange,strsize), + WSLUA_CLASS_FNREG(TvbRange,bytes), + WSLUA_CLASS_FNREG(TvbRange,bitfield), + WSLUA_CLASS_FNREG(TvbRange,range), + WSLUA_CLASS_FNREG(TvbRange,len), + WSLUA_CLASS_FNREG(TvbRange,offset), + WSLUA_CLASS_FNREG(TvbRange,tvb), + WSLUA_CLASS_FNREG(TvbRange,le_ustring), + WSLUA_CLASS_FNREG(TvbRange,ustring), + WSLUA_CLASS_FNREG(TvbRange,le_ustringz), + WSLUA_CLASS_FNREG(TvbRange,ustringz), + WSLUA_CLASS_FNREG(TvbRange,uncompress), + WSLUA_CLASS_FNREG(TvbRange,raw), + { NULL, NULL } +}; + +WSLUA_META TvbRange_meta[] = { + WSLUA_CLASS_MTREG(TvbRange,tostring), + WSLUA_CLASS_MTREG(wslua,concat), + WSLUA_CLASS_MTREG(TvbRange,eq), + {"__call", TvbRange_range}, + { NULL, NULL } +}; + +int TvbRange_register(lua_State* L) { + outstanding_TvbRange = g_ptr_array_new(); + WSLUA_REGISTER_CLASS(TvbRange); + 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: + */ diff --git a/epan/wslua/wslua_utility.c b/epan/wslua/wslua_utility.c new file mode 100644 index 00000000..3756c511 --- /dev/null +++ b/epan/wslua/wslua_utility.c @@ -0,0 +1,544 @@ +/* + * wslua_utility.c + * + * (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 Utility Utility Functions */ + +#include "wslua.h" +#include <math.h> +#include <epan/stat_tap_ui.h> +#include <epan/prefs.h> +#include <epan/prefs-int.h> + + +WSLUA_FUNCTION wslua_get_version(lua_State* L) { /* Gets the Wireshark version as a string. */ + const gchar* str = VERSION; + lua_pushstring(L,str); + WSLUA_RETURN(1); /* The version string, e.g. "3.2.5". */ +} + + +static gchar* current_plugin_version = NULL; + +const gchar* get_current_plugin_version(void) { + return current_plugin_version ? current_plugin_version : ""; +} + +void clear_current_plugin_version(void) { + if (current_plugin_version != NULL) { + g_free(current_plugin_version); + current_plugin_version = NULL; + } +} + +WSLUA_FUNCTION wslua_set_plugin_info(lua_State* L) { + /* + Set a Lua table with meta-data about the plugin, such as version. + + The passed-in Lua table entries need to be keyed/indexed by the following: + + * "version" with a string value identifying the plugin version (required) + * "description" with a string value describing the plugin (optional) + * "author" with a string value of the author's name(s) (optional) + * "repository" with a string value of a URL to a repository (optional) + + Not all of the above key entries need to be in the table. The 'version' + entry is required, however. The others are not currently used for anything, but + might be in the future and thus using them might be useful. Table entries keyed + by other strings are ignored, and do not cause an error. + + ===== Example + + [source,lua] + ---- + local my_info = { + version = "1.0.1", + author = "Jane Doe", + repository = "https://github.com/octocat/Spoon-Knife" + } + + set_plugin_info(my_info) + ---- + + @since 1.99.8 + */ +#define WSLUA_ARG_set_plugin_info_TABLE 1 /* The Lua table of information. */ + + if ( lua_istable(L,WSLUA_ARG_set_plugin_info_TABLE) ) { + int top; + lua_getfield(L, WSLUA_ARG_set_plugin_info_TABLE, "version"); + top = lua_gettop(L); + if (lua_isstring(L, top)) { + clear_current_plugin_version(); + current_plugin_version = g_strdup( luaL_checkstring(L, top) ); + /* pop the string */ + lua_pop(L, 1); + } + else { + return luaL_error(L,"the Lua table must have a 'version' key entry with a string value"); + } + } else { + return luaL_error(L,"a Lua table with at least a 'version' string entry"); + } + + return 0; +} + + +WSLUA_FUNCTION wslua_format_date(lua_State* LS) { /* Formats an absolute timestamp into a human readable date. */ +#define WSLUA_ARG_format_date_TIMESTAMP 1 /* A timestamp value to convert. */ + lua_Number timestamp = luaL_checknumber(LS,WSLUA_ARG_format_date_TIMESTAMP); + nstime_t then; + gchar* str; + + then.secs = (time_t)(floor(timestamp)); + then.nsecs = (guint32) ( (timestamp-(double)(then.secs))*1000000000); + str = abs_time_to_str(NULL, &then, ABSOLUTE_TIME_LOCAL, TRUE); + lua_pushstring(LS,str); + wmem_free(NULL, str); + + WSLUA_RETURN(1); /* A string with the formated date */ +} + +WSLUA_FUNCTION wslua_format_time(lua_State* LS) { /* Formats a relative timestamp in a human readable time. */ +#define WSLUA_ARG_format_time_TIMESTAMP 1 /* A timestamp value to convert. */ + lua_Number timestamp = luaL_checknumber(LS,WSLUA_ARG_format_time_TIMESTAMP); + nstime_t then; + gchar* str; + + then.secs = (time_t)(floor(timestamp)); + then.nsecs = (guint32) ( (timestamp-(double)(then.secs))*1000000000); + str = rel_time_to_str(NULL, &then); + lua_pushstring(LS,str); + wmem_free(NULL, str); + + WSLUA_RETURN(1); /* A string with the formated time */ +} + +WSLUA_FUNCTION wslua_get_preference(lua_State *L) { + /* Get a preference value. @since 3.5.0 */ +#define WSLUA_ARG_get_preference_PREFERENCE 1 /* The name of the preference. */ + const gchar* preference = luaL_checkstring(L,WSLUA_ARG_get_preference_PREFERENCE); + + /* Split preference from module.preference */ + gchar *module_name = g_strdup(preference); + gchar *preference_name = strchr(module_name, '.'); + pref_t *pref = NULL; + + if (preference_name) { + *preference_name = '\0'; + preference_name++; + + module_t *module = prefs_find_module(module_name); + pref = prefs_find_preference(module, preference_name); + } + g_free (module_name); + + if (pref) { + switch (prefs_get_type(pref)) { + case PREF_UINT: + { + guint uint_value = prefs_get_uint_value_real(pref, pref_current); + lua_pushinteger(L, uint_value); + break; + } + case PREF_BOOL: + { + gboolean bool_value = prefs_get_bool_value(pref, pref_current); + lua_pushboolean(L, bool_value); + break; + } + case PREF_ENUM: + { + const enum_val_t *enums; + gint enum_value = prefs_get_enum_value(pref, pref_current); + + for (enums = prefs_get_enumvals(pref); enums->name; enums++) { + if (enums->value == enum_value) { + lua_pushstring(L,enums->name); + break; + } + } + + if (!enums || !enums->name) { + /* Enum preference has an unknown value. */ + lua_pushstring(L,""); + } + break; + } + case PREF_STRING: + case PREF_SAVE_FILENAME: + case PREF_OPEN_FILENAME: + case PREF_DIRNAME: + { + const gchar *string_value = prefs_get_string_value(pref, pref_current); + lua_pushstring(L,string_value); + break; + } + case PREF_RANGE: + { + char *range_value = range_convert_range(NULL, prefs_get_range_value_real(pref, pref_current)); + lua_pushstring(L,range_value); + wmem_free(NULL, range_value); + break; + } + default: + /* Get not supported for this type. */ + return luaL_error(L, "preference type %d is not supported.", prefs_get_type(pref)); + } + } else { + /* No such preference. */ + lua_pushnil(L); + } + + WSLUA_RETURN(1); /* The preference value, or nil if not found. */ +} + +WSLUA_FUNCTION wslua_set_preference(lua_State *L) { + /* Set a preference value. @since 3.5.0 */ +#define WSLUA_ARG_set_preference_PREFERENCE 1 /* The name of the preference. */ +#define WSLUA_ARG_set_preference_VALUE 2 /* The preference value to set. */ + const gchar* preference = luaL_checkstring(L,WSLUA_ARG_set_preference_PREFERENCE); + + /* Split preference from module.preference */ + gchar *module_name = g_strdup(preference); + gchar *preference_name = strchr(module_name, '.'); + module_t *module = NULL; + pref_t *pref = NULL; + + if (preference_name) { + *preference_name = '\0'; + preference_name++; + + module = prefs_find_module(module_name); + pref = prefs_find_preference(module, preference_name); + } + g_free (module_name); + + if (pref) { + unsigned int changed = 0; + switch (prefs_get_type(pref)) { + case PREF_UINT: + { + guint uint_value = (guint)luaL_checkinteger(L,WSLUA_ARG_set_preference_VALUE); + changed = prefs_set_uint_value(pref, uint_value, pref_current); + module->prefs_changed_flags |= changed; + lua_pushboolean(L, changed); + break; + } + case PREF_BOOL: + { + gboolean bool_value = wslua_checkboolean(L, WSLUA_ARG_set_preference_VALUE); + changed = prefs_set_bool_value(pref, bool_value, pref_current); + module->prefs_changed_flags |= changed; + lua_pushboolean(L, changed); + break; + } + case PREF_ENUM: + { + const gchar *enum_value = luaL_checkstring(L,WSLUA_ARG_set_preference_VALUE); + changed = prefs_set_enum_string_value(pref, enum_value, pref_current); + module->prefs_changed_flags |= changed; + lua_pushboolean(L, changed); + break; + } + case PREF_STRING: + case PREF_SAVE_FILENAME: + case PREF_OPEN_FILENAME: + case PREF_DIRNAME: + { + const gchar *string_value = luaL_checkstring(L,WSLUA_ARG_set_preference_VALUE); + changed = prefs_set_string_value(pref, string_value, pref_current); + module->prefs_changed_flags |= changed; + lua_pushboolean(L, changed); + break; + } + case PREF_RANGE: + { + const gchar *range_value = luaL_checkstring(L,WSLUA_ARG_set_preference_VALUE); + range_t *range = NULL; + convert_ret_t ret = range_convert_str(NULL, &range, range_value, prefs_get_max_value(pref)); + if (ret == CVT_NUMBER_TOO_BIG) { + return luaL_error(L, "illegal range (number too big)"); + } else if (ret != CVT_NO_ERROR) { + return luaL_error(L, "illegal range (syntax error)"); + } + changed = prefs_set_range_value(pref, range, pref_current); + wmem_free(NULL, range); + module->prefs_changed_flags |= changed; + lua_pushboolean(L, changed); + break; + } + default: + /* Set not supported for this type. */ + return luaL_error(L, "preference type %d is not supported.", prefs_get_type(pref)); + } + } else { + /* No such preference. */ + lua_pushnil(L); + } + + WSLUA_RETURN(1); /* true if changed, false if unchanged or nil if not found. */ +} + +WSLUA_FUNCTION wslua_reset_preference(lua_State *L) { + /* Reset a preference to default value. @since 3.5.0 */ +#define WSLUA_ARG_reset_preference_PREFERENCE 1 /* The name of the preference. */ + const gchar* preference = luaL_checkstring(L,WSLUA_ARG_reset_preference_PREFERENCE); + + // Split preference from module.preference + gchar *module_name = g_strdup(preference); + gchar *preference_name = strchr(module_name, '.'); + pref_t *pref = NULL; + + if (preference_name) { + *preference_name = '\0'; + preference_name++; + + module_t *module = prefs_find_module(module_name); + pref = prefs_find_preference(module, preference_name); + } + + if (pref) { + reset_pref(pref); + lua_pushboolean(L, TRUE); + } else { + /* No such preference. */ + lua_pushnil(L); + } + + g_free(module_name); + WSLUA_RETURN(1); /* true if valid preference */ +} + +WSLUA_FUNCTION wslua_apply_preferences(lua_State *L) { + /* Write preferences to file and apply changes. @since 3.5.0 */ + char *pf_path = NULL; + int err = write_prefs(&pf_path); + + if (err) { + /* Make a copy of pf_path because luaL_error() will return */ + gchar pf_path_copy[256]; + (void) g_strlcpy(pf_path_copy, pf_path, sizeof pf_path_copy); + g_free(pf_path); + + return luaL_error(L, "can't open preferences file\n\"%s\": %s.", + pf_path_copy, g_strerror(err)); + } else { + prefs_apply_all(); + } + + return 0; +} + +WSLUA_FUNCTION wslua_report_failure(lua_State* LS) { /* Reports a failure to the user. */ +#define WSLUA_ARG_report_failure_TEXT 1 /* Message text to report. */ + const gchar* s = luaL_checkstring(LS,WSLUA_ARG_report_failure_TEXT); + report_failure("%s",s); + return 0; +} + +/* The returned filename is g_malloc()'d so the caller must free it */ +/* except when NULL is returned if file doesn't exist */ +char* wslua_get_actual_filename(const char* fname) { + char fname_clean[256]; + char* f; + char* filename; + + (void) g_strlcpy(fname_clean,fname,255); + fname_clean[255] = '\0'; + + for(f = fname_clean; *f; f++) { + switch(*f) { + case '/': case '\\': + *f = *(G_DIR_SEPARATOR_S); + break; + default: + break; + } + } + + if ( file_exists(fname_clean) ) { + return g_strdup(fname_clean); + } + + filename = get_persconffile_path(fname_clean,FALSE); + + if ( file_exists(filename) ) { + return filename; + } + g_free(filename); + + /* + * Try to look in global data directory, nothing extraordinary for normal + * installations. For executions from the build dir, it will look for files + * copied to DATAFILE_DIR. + */ + filename = get_datafile_path(fname_clean); + if ( file_exists(filename) ) { + return filename; + } + g_free(filename); + + return NULL; +} + +WSLUA_FUNCTION wslua_loadfile(lua_State* L) { + /* + Loads a Lua file and compiles it into a Lua chunk, similar to the standard + https://www.lua.org/manual/5.1/manual.html#pdf-loadfile[loadfile] + but searches additional directories. + The search order is the current directory, followed by the user's + https://www.wireshark.org/docs/wsug_html_chunked/ChAppFilesConfigurationSection.html[personal configuration] + directory, and finally the + https://www.wireshark.org/docs/wsug_html_chunked/ChAppFilesConfigurationSection.html[global configuration] + directory. + + ===== Example + + [source,lua] + ---- + -- Assume foo.lua contains definition for foo(a,b). Load the chunk + -- from the file and execute it to add foo(a,b) to the global table. + -- These two lines are effectively the same as dofile('foo.lua'). + local loaded_chunk = assert(loadfile('foo.lua')) + loaded_chunk() + + -- ok to call foo at this point + foo(1,2) + ---- + */ +#define WSLUA_ARG_loadfile_FILENAME 1 /* Name of the file to be loaded. If the file does not exist in the current directory, the user and system directories are searched. */ + const char *given_fname = luaL_checkstring(L, WSLUA_ARG_loadfile_FILENAME); + char* filename; + + filename = wslua_get_actual_filename(given_fname); + + if (!filename) { + WSLUA_ARG_ERROR(loadfile,FILENAME,"file does not exist"); + return 0; + } + + if (luaL_loadfile(L, filename) == 0) { + g_free(filename); + return 1; + } else { + g_free(filename); + lua_pushnil(L); + lua_insert(L, -2); + return 2; + } +} + +WSLUA_FUNCTION wslua_dofile(lua_State* L) { + /* + Loads a Lua file and executes it as a Lua chunk, similar to the standard + https://www.lua.org/manual/5.1/manual.html#pdf-dofile[dofile] + but searches additional directories. + The search order is the current directory, followed by the user's + https://www.wireshark.org/docs/wsug_html_chunked/ChAppFilesConfigurationSection.html[personal configuration] + directory, and finally the + https://www.wireshark.org/docs/wsug_html_chunked/ChAppFilesConfigurationSection.html[global configuration] + directory. + */ +#define WSLUA_ARG_dofile_FILENAME 1 /* Name of the file to be run. If the file does not exist in the current directory, the user and system directories are searched. */ + const char *given_fname = luaL_checkstring(L, WSLUA_ARG_dofile_FILENAME); + char* filename = wslua_get_actual_filename(given_fname); + int n; + + if (!filename) { + WSLUA_ARG_ERROR(dofile,FILENAME,"file does not exist"); + return 0; + } + + n = lua_gettop(L); + if (luaL_loadfile(L, filename) != 0) lua_error(L); + g_free(filename); + lua_call(L, 0, LUA_MULTRET); + return lua_gettop(L) - n; +} + + +typedef struct _statcmd_t { + lua_State* L; + int func_ref; +} statcmd_t; + +static int statcmd_init_cb_error_handler(lua_State* L _U_) { + return 0; +} + +static void statcmd_init(const char *opt_arg, void* userdata) { + statcmd_t* sc = (statcmd_t *)userdata; + lua_State* L = sc->L; + + lua_settop(L,0); + lua_pushcfunction(L,statcmd_init_cb_error_handler); + lua_rawgeti(L, LUA_REGISTRYINDEX, sc->func_ref); + + lua_pushstring(L,opt_arg); + + switch ( lua_pcall(L,1,0,1) ) { + case 0: + break; + case LUA_ERRRUN: + ws_warning("Runtime error while calling statcmd callback"); + break; + case LUA_ERRMEM: + ws_warning("Memory alloc error while calling statcmd callback"); + break; + case LUA_ERRERR: + ws_warning("Error while running the error handler function for statcmd callback"); + break; + default: + ws_assert_not_reached(); + break; + } + +} + +WSLUA_FUNCTION wslua_register_stat_cmd_arg(lua_State* L) { + /* Register a function to handle a `-z` option */ +#define WSLUA_ARG_register_stat_cmd_arg_ARGUMENT 1 /* The name of the option argument. */ +#define WSLUA_OPTARG_register_stat_cmd_arg_ACTION 2 /* The function to be called when the command is invoked. */ + const char* arg = luaL_checkstring(L,WSLUA_ARG_register_stat_cmd_arg_ARGUMENT); + statcmd_t* sc = g_new0(statcmd_t, 1); /* XXX leaked */ + stat_tap_ui ui_info; + + sc->L = L; + lua_pushvalue(L, WSLUA_OPTARG_register_stat_cmd_arg_ACTION); + sc->func_ref = luaL_ref(L, LUA_REGISTRYINDEX); + lua_remove(L,1); + + ui_info.group = REGISTER_PACKET_STAT_GROUP_UNSORTED; /* XXX - need an argument? */ + ui_info.title = NULL; + ui_info.cli_string = arg; + ui_info.tap_init_cb = statcmd_init; + ui_info.nparams = 0; + ui_info.params = NULL; + register_stat_tap_ui(&ui_info, sc); + 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: + */ diff --git a/epan/wslua/wslua_wtap.c b/epan/wslua/wslua_wtap.c new file mode 100644 index 00000000..524ed7c3 --- /dev/null +++ b/epan/wslua/wslua_wtap.c @@ -0,0 +1,160 @@ +/* + * wslua_wtap.c + * + * Wireshark's interface to the Lua Programming Language + * for various libwiretap utility functions. + * + * 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 Wtap Wtap Functions For Handling Capture File Types */ + +#include <limits.h> + +#include "wslua.h" +#include <wiretap/wtap.h> + +/* + * Solely for the function that gets the table of backwards-compatibility + * Lua names for file types/subtypes. + */ +#include <wiretap/wtap-int.h> + +WSLUA_FUNCTION wslua_wtap_file_type_subtype_description(lua_State* LS) { + /* + Get a string describing a capture file type, given a filetype + value for that file type. + + @since 3.2.12, 3.4.4 + */ +#define WSLUA_ARG_wtap_file_type_subtype_description_FILETYPE 1 /* The type for which the description is to be fetched - a number returned by `wtap_name_to_file_type_subtype()`. */ + lua_Number filetype = luaL_checknumber(LS,WSLUA_ARG_wtap_file_type_subtype_description_FILETYPE); + /* wtap_file_type_subtype_description()'s name isn't really descriptive. */ + if (filetype > INT_MAX) { + /* Too big. */ + lua_pushnil(LS); + } else { + const gchar* str = wtap_file_type_subtype_description((int)filetype); + if (str == NULL) + lua_pushnil(LS); + else + lua_pushstring(LS,str); + } + WSLUA_RETURN(1); /* The description of the file type with that filetype value, or nil if there is no such file type. */ +} + +WSLUA_FUNCTION wslua_wtap_file_type_subtype_name(lua_State* LS) { + /* + Get a string giving the name for a capture file type, given a filetype + value for that file type. + + @since 3.2.12, 3.4.4 + */ +#define WSLUA_ARG_wtap_file_type_subtype_name_FILETYPE 1 /* The type for which the name is to be fetched - a number returned by `wtap_name_to_file_type_subtype()`. */ + lua_Number filetype = luaL_checknumber(LS,WSLUA_ARG_wtap_file_type_subtype_name_FILETYPE); + /* wtap_file_type_subtype_description()'s name isn't really descriptive. */ + if (filetype > INT_MAX) { + /* Too big. */ + lua_pushnil(LS); + } else { + const gchar* str = wtap_file_type_subtype_name((int)filetype); + if (str == NULL) + lua_pushnil(LS); + else + lua_pushstring(LS,str); + } + WSLUA_RETURN(1); /* The name of the file type with that filetype value, or nil if there is no such file type. */ +} + +WSLUA_FUNCTION wslua_wtap_name_to_file_type_subtype(lua_State* LS) { + /* + Get a filetype value for a file type, given the name for that + file type. + + @since 3.2.12, 3.4.4 + */ +#define WSLUA_ARG_wtap_name_to_file_type_subtype_NAME 1 /* The name of a file type. */ + const char* name = luaL_checkstring(LS,WSLUA_ARG_wtap_name_to_file_type_subtype_NAME); + lua_Number filetype = wtap_name_to_file_type_subtype(name); + if (filetype == -1) + lua_pushnil(LS); + else + lua_pushnumber(LS,filetype); + WSLUA_RETURN(1); /* The filetype value for the file type with that name, or nil if there is no such file type. */ +} + +WSLUA_FUNCTION wslua_wtap_pcap_file_type_subtype(lua_State* LS) { + /* + Get the filetype value for pcap files. + + @since 3.2.12, 3.4.4 + */ + lua_Number filetype = wtap_pcap_file_type_subtype(); + lua_pushnumber(LS,filetype); + WSLUA_RETURN(1); /* The filetype value for pcap files. */ +} + +WSLUA_FUNCTION wslua_wtap_pcap_nsec_file_type_subtype(lua_State* LS) { + /* + Get the filetype value for nanosecond-resolution pcap files. + + @since 3.2.12, 3.4.4 + */ + lua_Number filetype = wtap_pcap_nsec_file_type_subtype(); + lua_pushnumber(LS,filetype); + WSLUA_RETURN(1); /* The filetype value for nanosecond-resolution pcap files. */ +} + +WSLUA_FUNCTION wslua_wtap_pcapng_file_type_subtype(lua_State* LS) { + /* + Get the filetype value for pcapng files. + + @since 3.2.12, 3.4.4 + */ + lua_Number filetype = wtap_pcapng_file_type_subtype(); + lua_pushnumber(LS,filetype); + WSLUA_RETURN(1); /* The filetype value for pcapng files. */ +} + +/* + * init.wslua-only function to return a table to assign to + * wtap_filetypes. + */ +extern void wslua_init_wtap_filetypes(lua_State* LS) { + /* Get the GArray from which we initialize this. */ + const GArray *table = get_backwards_compatibility_lua_table(); + + /* + * Create the table; it's indexted by strings, not numbers, + * so none of the entries will be in a sequence. + */ + lua_createtable(LS,0,table->len); + for (guint i = 0; i < table->len; i++) { + struct backwards_compatibiliity_lua_name *entry; + + entry = &g_array_index(table, + struct backwards_compatibiliity_lua_name, i); + /* + * Push the name and the ft, in order, so that the ft, + * which should be the value at the top of the stack, + * is at the top of the stack, and the name, which should + * be the value just below that, is the value just below + * it. + */ + lua_pushstring(LS, entry->name); + lua_pushnumber(LS, entry->ft); + /* + * The -3 is the index, relative to the top of the stack, of + * the table; the two elements on top of it are the ft and + * the name, so it's -3. + */ + lua_settable(LS, -3); + } + lua_setglobal(LS, "wtap_filetypes"); +} |