diff options
Diffstat (limited to 'doc/wsdg_src/wsdg_lua_support.adoc')
-rw-r--r-- | doc/wsdg_src/wsdg_lua_support.adoc | 436 |
1 files changed, 436 insertions, 0 deletions
diff --git a/doc/wsdg_src/wsdg_lua_support.adoc b/doc/wsdg_src/wsdg_lua_support.adoc new file mode 100644 index 00000000..26c3849d --- /dev/null +++ b/doc/wsdg_src/wsdg_lua_support.adoc @@ -0,0 +1,436 @@ +[#wsluarm] + +// Attributes +:build_dir: .. + +== Lua Support in Wireshark + +[#wsluarm_intro] + +=== Introduction + +Lua is a powerful light-weight programming language designed for extending +applications. Wireshark contains an embedded Lua interpreter which can +be used to write dissectors, taps, and capture file readers and writers. +Wireshark versions 4.2.x and earlier support Lua 5.1 and 5.2, and newer +versions support Lua 5.3 and 5.4. The Lua BitOp library is bundled with +all version of Wireshark; Lua 5.3 and later also have native support for +https://www.lua.org/manual/5.4/manual.html#3.4.2[bitwise operators]. + +If Lua is enabled, Wireshark will first try to load a file named `init.lua` +from the global link:{wireshark-users-guide-url}ChPluginFolders.html[_plugins directory_]. +and then from the user’s +link:{wireshark-users-guide-url}ChAppFilesConfigurationSection.html[_personal plugins directory_]. +Then all files ending with _.lua_ are loaded from the global plugins +directory and its subdirectories. Then all files ending with _.lua_ in the +personal Lua plugins directory and its subdirectories are loaded. The +files are processed in ASCIIbetical order (compared byte-by-byte, as `strcmp`), +descending into each subdirectory depth-first in order. + +Whether or not Lua scripts are enabled can be controlled via the +_$$enable_lua$$_ variable. Lua scripts are enabled by +default. To disable Lua scripts, set the _$$enable_lua$$_ variable to _false_. +Wireshark 2.6 and earlier enabled or disabled Lua scripts using +the variable _$$disable_lua$$_ (deprecated). If both _$$enable_lua$$_ and +_$$disable_lua$$_ are present, _$$disable_lua$$_ is ignored. + +.Example for init.lua +[source,lua] +---- +-- Set enable_lua to false to disable Lua support. +enable_lua = true + +if not enable_lua then + return +end + +-- If false and Wireshark was started as (setuid) root, then the user +-- will not be able to execute custom Lua scripts from the personal +-- configuration directory, the -Xlua_script command line option or +-- the Lua Evaluate menu option in the GUI. +-- Note: Not checked on Windows. running_superuser is always false. +run_user_scripts_when_superuser = true +---- + +The command line option _$$-X lua_script:$$++file.lua++_ can also be used to load +specific Lua scripts. Arguments can be given to a script loaded at the command +line with the option _$$-X lua_scriptN:$$++arg++_, where _N_ is the ordinal +index of the script on the command line. For example, if two scripts were loaded +on the command line with _$$-X lua_script:$$++my.lua++_ and +_$$-X lua_script:$$++other.lua++_ in that order, then _$$-X lua_script1:$$++foo++_ +would pass _foo_ to _my.lua_ and _$$-X lua_script2:$$++bar++_ would pass _bar_ to +_other.lua_. Multiple command line options could be passed to _my.lua_ by +repeating the option _$$-X lua_script1:$$_. Arguments are available in a script in +a global table called _arg_, similar to when +link:https://www.lua.org/manual/5.4/manual.html#7[running Lua standalone]. + +[IMPORTANT] +.Loading order matters +==== +Lua dissectors, unlike <<ChapterDissection,compiled protocol dissectors>>, do +not have separate <<ChDissectSetup,registration and handoff>> stages yet +(see wsbuglink:15907[]). Each Lua dissector's registration and handoff is +completed before moving to the next Lua file in turn. +That means that the order in which Lua files are read is quite important; +in order for a Lua dissector to register in a dissector table set up by another +dissector, the latter dissector must have been already processed. The easiest +way to ensure this is to put Lua dissectors that need to be registered first +in files whose name is earlier in ASCIIbetical order (the name of the script +does not necessarily need to relate to the name of the dissector.) + +The Lua code is executed after all compiled dissectors, both built-in and plugin, +are initialized and before reading any file. +This means that Lua dissectors can add themselves to tables registered by compiled +dissectors, but not vice versa; compiled dissectors cannot add themselves to +dissector tables registered by Lua dissectors. +==== + +Wireshark for Windows uses a modified Lua runtime +(link:https://github.com/Lekensteyn/lua-unicode[lua-unicode]) to +support Unicode (UTF-8) filesystem paths. This brings consistency with other +platforms (for example, Linux and macOS). + +[#wslua_menu_example] + +=== Example: Creating a Menu with Lua + +The code below adds a menu "Lua Dialog Test" under the Tools menu. +When selected, it opens a dialog prompting the user for input +and then opens a text window with the output. + +[source,lua] +---- + +-- Define the menu entry's callback +local function dialog_menu() + local function dialog_func(person,eyes,hair) + local window = TextWindow.new("Person Info"); + local message = string.format("Person %s with %s eyes and %s hair.", person, eyes, hair); + window:set(message); + end + + new_dialog("Dialog Test",dialog_func,"A Person","Eyes","Hair") +end + +-- Create the menu entry +register_menu("Lua Dialog Test",dialog_menu,MENU_TOOLS_UNSORTED) + +-- Notify the user that the menu was created +if gui_enabled() then + local splash = TextWindow.new("Hello!"); + splash:set("Wireshark has been enhanced with a useless feature.\n") + splash:append("Go to 'Tools->Lua Dialog Test' and check it out!") +end + +---- + +[#wslua_dissector_example] + +=== Example: Dissector written in Lua + +[source,lua] +---- +local p_multi = Proto("multi", "MultiProto"); + +local vs_protos = { + [2] = "mtp2", + [3] = "mtp3", + [4] = "alcap", + [5] = "h248", + [6] = "ranap", + [7] = "rnsap", + [8] = "nbap" +} + +local f_proto = ProtoField.uint8("multi.protocol", "Protocol", base.DEC, vs_protos) +local f_dir = ProtoField.uint8("multi.direction", "Direction", base.DEC, { [1] = "incoming", [0] = "outgoing"}) +local f_text = ProtoField.string("multi.text", "Text") + +p_multi.fields = { f_proto, f_dir, f_text } + +local data_dis = Dissector.get("data") + +local protos = { + [2] = Dissector.get("mtp2"), + [3] = Dissector.get("mtp3"), + [4] = Dissector.get("alcap"), + [5] = Dissector.get("h248"), + [6] = Dissector.get("ranap"), + [7] = Dissector.get("rnsap"), + [8] = Dissector.get("nbap"), + [9] = Dissector.get("rrc"), + [10] = DissectorTable.get("sctp.ppi"):get_dissector(3), -- m3ua + [11] = DissectorTable.get("ip.proto"):get_dissector(132), -- sctp +} + +function p_multi.dissector(buf, pkt, tree) + + local subtree = tree:add(p_multi, buf(0,2)) + subtree:add(f_proto, buf(0,1)) + subtree:add(f_dir, buf(1,1)) + + local proto_id = buf(0,1):uint() + + local dissector = protos[proto_id] + + if dissector ~= nil then + -- Dissector was found, invoke subdissector with a new Tvb, + -- created from the current buffer (skipping first two bytes). + dissector:call(buf(2):tvb(), pkt, tree) + elseif proto_id < 2 then + subtree:add(f_text, buf(2)) + -- pkt.cols.info:set(buf(2, buf:len() - 3):string()) + else + -- fallback dissector that just shows the raw data. + data_dis:call(buf(2):tvb(), pkt, tree) + end + +end + +local wtap_encap_table = DissectorTable.get("wtap_encap") +local udp_encap_table = DissectorTable.get("udp.port") + +wtap_encap_table:add(wtap.USER15, p_multi) +wtap_encap_table:add(wtap.USER12, p_multi) +udp_encap_table:add(7555, p_multi) +---- + +[#wslua_tap_example] + +=== Example: Listener written in Lua + +[source,lua] +---- +-- This program will register a menu that will open a window with a count of occurrences +-- of every address in the capture + +local function menuable_tap() + -- Declare the window we will use + local tw = TextWindow.new("Address Counter") + + -- This will contain a hash of counters of appearances of a certain address + local ips = {} + + -- this is our tap + local tap = Listener.new(); + + local function remove() + -- this way we remove the listener that otherwise will remain running indefinitely + tap:remove(); + end + + -- we tell the window to call the remove() function when closed + tw:set_atclose(remove) + + -- this function will be called once for each packet + function tap.packet(pinfo,tvb) + local src = ips[tostring(pinfo.src)] or 0 + local dst = ips[tostring(pinfo.dst)] or 0 + + ips[tostring(pinfo.src)] = src + 1 + ips[tostring(pinfo.dst)] = dst + 1 + end + + -- this function will be called once every few seconds to update our window + function tap.draw(t) + tw:clear() + for ip,num in pairs(ips) do + tw:append(ip .. "\t" .. num .. "\n"); + end + end + + -- this function will be called whenever a reset is needed + -- e.g. when reloading the capture file + function tap.reset() + tw:clear() + ips = {} + end + + -- Ensure that all existing packets are processed. + retap_packets() +end + +-- using this function we register our function +-- to be called when the user selects the Tools->Test->Packets menu +register_menu("Test/Packets", menuable_tap, MENU_TOOLS_UNSORTED) +---- + +[#wslua_require_example] + +=== Example: Lua scripts with shared modules + +Lua plugins that depend on protocols, dissectors, dissector tables, and other +items registered with Wireshark by other Lua scripts can access those through +the Wireshark Lua API. The key is ensuring that the providing script is +read first, as previously mentioned. + +It is also possible to depend on Lua functions defined in other Lua scripts. +The recommended method is to load those scripts as +link:https://www.lua.org/manual/5.4/manual.html#6.3[modules] via +link:https://www.lua.org/manual/5.4/manual.html#pdf-require[require]. +Modules preferably should avoid defining globals, and should return a +table containing functions indexed by name. Globals defined in modules will +leak into the global namespace when `require()` is used, and name collisions +can cause unexpected results. (As an aside, local variables are faster in +Lua because global variables require extra table lookups.) Directories +containing loaded Lua scripts (including those specified on the command line +with _$$-X lua_script:$$++my.lua++_) are automatically added to the `require()` +search path. + +For example, suppose there is a Lua script in the personal plugins directory +named _bar.lua_ as follows: + +[source,lua] +---- +-- bar.lua +-- Converts an integer representing an IPv4 address into its dotted quad +-- string representation. + +-- This is the module object, which will be returned at the end of this file. +local M = { +} + +M.GetIPAddressString = function(ip) + -- Lua BitOp library, included in all versions of Wireshark + --local octet1 = bit.rshift(bit.band(0xFF000000, ip), 24) + --local octet2 = bit.rshift(bit.band(0x00FF0000, ip), 16) + --local octet3 = bit.rshift(bit.band(0x0000FF00, ip), 8) + --local octet4 = bit.band(0x000000FF, ip) + + -- Lua >= 5.3 native bit operators, supported in Wireshark >= 4.4 + local octet1 = ip >> 24 + local octet2 = ip >> 16 & 0xFF + local octet3 = ip >> 8 & 0xFF + local octet4 = ip & 0xFF + + return octet1 .. "." .. octet2 .. "." .. octet3 .. "." .. octet4 +end + +-- Return the table we've created, which will be accessible as the return +-- value of require() or dofile(), and at the global package.loaded["bar"] +return M +---- + +Other Lua plugins that wish to use the module can then `require()` it +(note that the _.lua_ extension is not used in `require()`, unlike the +similar `dofile()`): + +[source,lua] +---- +-- Foo dissector +local p_foo = Proto("foo", "Foo") + +local bar = require("bar") + +local f_ip = ProtoField.ipv4("foo.ip", "IP") +local f_ipint = ProtoField.uint32("foo.ipint", "IP as Uint32") +local f_ipstr = ProtoField.string("foo.ipstr", "IP as String") + +p_foo.fields = { f_ip, f_ipint, f_ipstr } + +function p_foo.dissector(tvbuf, pktinfo, tree) + + -- Set the protocol column to show this name + pktinfo.cols.protocol:set("FooMessage") + + local pktlen = tvbuf:reported_length_remaining() + + local subtree = tree:add(p_foo, tvbuf:range(0,pktlen)) + + local child, ipaddr = subtree:add_packet_field(f_ip, tvbuf(8, 4), ENC_BIG_ENDIAN) + local child, ipint = subtree:add_packet_field(f_ipint, tvbuf(8, 4), ENC_BIG_ENDIAN) + + -- These two are the same string + subtree:add(f_ipstr, tvbuf(8,4), bar.GetIPAddressString(ipint)) + subtree:add(f_ipstr, tvbuf(8,4), tostring(ipaddr)) + + return pktlen +end + +DissectorTable.get("udp.port"):add(2012, p_foo) +---- + +Using `require()` is another way to control the order in which files are loaded. +Lua `require()` ensures that a module is only executed once. Subsequent calls +will return the same table already loaded. + +[IMPORTANT] +.Avoid duplicate registration +==== +In versions of Wireshark before 4.4, the initial loading of Lua plugins in the +plugins directory does not register them in the table of already loaded modules +used by `require()`. This means that Lua script in the plugins directory that +are initially loaded can be executed a second time by `require()`. For scripts +that register dissectors or tables with Wireshark, this will result in errors like +`Proto new: there cannot be two protocols with the same description`. It is +safer to `require()` only Lua scripts that define common functions but do not +call the Wireshark Lua API to register protocols, dissectors, etc. + +In 4.4 and later, scripts in the plugin directories are loaded using the same +internal methods as `require()`, which eliminates duplicate registration errors +from loading of files in the plugin directory and using `require()`. This also +means that the order in which plugins are loaded can be adjusted by using +`require()` in addition to changing file names. However, duplicate registration +errors can still happen with other methods of executing a file that do +not check if it has already been loaded, like `dofile()`. +==== + +Lua scripts loaded on the command line are sandboxed into their own environment +and globals defined in them do not leak in the general global environment. +Modules loaded via `require()` within those scripts can escape that sandboxing, +however. Plugins in the personal (but not global) directory had similar +sandboxing prior to Wireshark 4.4, but now globals defined in plugins in the +personal directory will enter the global namespace for other plugins, as has +always been the case for plugins in the global plugin directory. + +[#wsluarm_modules] + +== Wireshark’s Lua API Reference Manual + +This Part of the User Guide describes the Wireshark specific functions in the embedded Lua. + +Classes group certain functionality, the following notational conventions are +used: + +* _Class.function()_ represents a class method (named _function_) on class + _Class_, taking no arguments. + +* _Class.function(a)_ represents a class method taking one argument. + +* _Class.function(...)_ represents a class method taking a variable number of + arguments. + +* _class:method()_ represents an instance method (named _method_) on an instance + of class _Class_, taking no arguments. Note the lowercase notation in the + documentation to clarify an instance. + +* _class.prop_ represents a property _prop_ on the instance of class _Class_. + +Trying to access a non-existing property, function or method currently gives an +error, but do not rely on it as the behavior may change in the future. + + +include::{build_dir}/wsluarm_src/wslua_utility.adoc[] +include::{build_dir}/wsluarm_src/wslua_gui.adoc[] +include::{build_dir}/wsluarm_src/wslua_proto.adoc[] +include::{build_dir}/wsluarm_src/wslua_field.adoc[] +include::{build_dir}/wsluarm_src/wslua_pinfo.adoc[] +include::{build_dir}/wsluarm_src/wslua_tvb.adoc[] +include::{build_dir}/wsluarm_src/wslua_tree.adoc[] +include::{build_dir}/wsluarm_src/wslua_listener.adoc[] +include::{build_dir}/wsluarm_src/wslua_dumper.adoc[] +include::{build_dir}/wsluarm_src/wslua_wtap.adoc[] +include::{build_dir}/wsluarm_src/wslua_file.adoc[] +include::{build_dir}/wsluarm_src/wslua_dir.adoc[] +include::{build_dir}/wsluarm_src/wslua_int64.adoc[] +include::{build_dir}/wsluarm_src/wslua_struct.adoc[] + +[#lua_module_PCRE2] + +=== PCRE2 Regular Expressions + +Lua has its own native _pattern_ syntax in the string library, but sometimes a +real regex engine is more useful. Wireshark comes with Perl Compatible Regular +Expressions version 2 (PCRE2). This engine is exposed into Wireshark’s Lua engine through the +well-known Lrexlib library. The module is loaded in the global environment using +the "rex_pcre2" table. The manual is available at https://rrthomas.github.io/lrexlib/manual.html. |