Created
September 30, 2016 11:52
-
-
Save danielinux/d9e854962e88b33ee77d41009fc0497f to your computer and use it in GitHub Desktop.
Dozer utilities
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
---------------------------------------- | |
-- script-name: dozer.lua | |
-- | |
-- Based on example LUA dissector by Hadriel Kaplan <hadrielk at yahoo dot com> | |
-- Copyright (c) 2016 Daniele Lacamera | |
-- GPLv2 | |
-- | |
-- Wireshark dissector for the VLC implementation of the Dozer protocol | |
-- | |
-- To add the dissector, start your wireshark using: | |
-- wireshark -X lua_script:dozer.lua | |
-- | |
local debug_level = { | |
DISABLED = 0, | |
LEVEL_1 = 1, | |
LEVEL_2 = 2 | |
} | |
local DEBUG = debug_level.LEVEL_1 | |
local default_settings = | |
{ | |
debug_level = DEBUG, | |
port = 1236, | |
heur_enabled = true, | |
} | |
local args={...} -- get passed-in args | |
if args and #args > 0 then | |
for _, arg in ipairs(args) do | |
local name, value = arg:match("(.+)=(.+)") | |
if name and value then | |
if tonumber(value) then | |
value = tonumber(value) | |
elseif value == "true" or value == "TRUE" then | |
value = true | |
elseif value == "false" or value == "FALSE" then | |
value = false | |
elseif value == "DISABLED" then | |
value = debug_level.DISABLED | |
elseif value == "LEVEL_1" then | |
value = debug_level.LEVEL_1 | |
elseif value == "LEVEL_2" then | |
value = debug_level.LEVEL_2 | |
else | |
error("invalid commandline argument value") | |
end | |
else | |
error("invalid commandline argument syntax") | |
end | |
default_settings[name] = value | |
end | |
end | |
local dprint = function() end | |
local dprint2 = function() end | |
local function reset_debug_level() | |
if default_settings.debug_level > debug_level.DISABLED then | |
dprint = function(...) | |
print(table.concat({"Lua:", ...}," ")) | |
end | |
if default_settings.debug_level > debug_level.LEVEL_1 then | |
dprint2 = dprint | |
end | |
end | |
end | |
reset_debug_level() | |
dprint2("Wireshark version = ", get_version()) | |
dprint2("Lua version = ", _VERSION) | |
local major, minor, micro = get_version():match("(%d+)%.(%d+)%.(%d+)") | |
if major and tonumber(major) <= 1 and ((tonumber(minor) <= 10) or (tonumber(minor) == 11 and tonumber(micro) < 3)) then | |
error( "Sorry, but your Wireshark/Tshark version ("..get_version()..") is too old for this script!\n".. | |
"This script needs Wireshark/Tshark version 1.11.3 or higher.\n" ) | |
end | |
assert(ProtoExpert.new, "Wireshark does not have the ProtoExpert class, so it's too old - get the latest 1.11.3 or higher") | |
local dozer = Proto("dozer","Dozer Protocol") | |
local pf_signature = ProtoField.uint16 ("dozer.signature", "Signature", base.HEX) | |
local pf_type = ProtoField.uint16 ("dozer.type", "Type", base.HEX) | |
local pf_seq = ProtoField.uint16 ("dozer.seq", "Sequence") | |
local pf_win = ProtoField.uint16 ("dozer.win", "Window") | |
local pf_count = ProtoField.uint16 ("dozer.count", "Counter") | |
local pf_size = ProtoField.uint16 ("dozer.size", "Payload Size") | |
local pf_media = ProtoField.string ("dozer.media", "Payload") | |
dozer.fields = { pf_signature, pf_type, pf_seq, pf_win, pf_count, pf_size, pf_media } | |
local ef_transport = ProtoExpert.new("dozer.query.expert", "Dozer multimedia transport", | |
expert.group.REQUEST_CODE, expert.severity.CHAT) | |
local ef_too_short = ProtoExpert.new("dozer.too_short.expert", "Dozer message too short", | |
expert.group.MALFORMED, expert.severity.ERROR) | |
local ef_bad_signature = ProtoExpert.new("dozer.bad_signature.expert", "Dozer: bad packet signature", | |
expert.group.MALFORMED, expert.severity.ERROR) | |
dozer.experts = { ef_transport, ef_too_short, ef_bad_signature} | |
local debug_pref_enum = { | |
{ 1, "Disabled", debug_level.DISABLED }, | |
{ 2, "Level 1", debug_level.LEVEL_1 }, | |
{ 3, "Level 2", debug_level.LEVEL_2 }, | |
} | |
dozer.prefs.debug = Pref.enum("Debug", default_settings.debug_level, | |
"The debug printing level", debug_pref_enum) | |
dozer.prefs.port = Pref.uint("Port number", default_settings.port, | |
"The UDP port number for Dozer") | |
dozer.prefs.heur = Pref.bool("Heuristic enabled", default_settings.heur_enabled, | |
"Whether heuristic dissection is enabled or not") | |
function dozer.prefs_changed() | |
dprint2("prefs_changed called") | |
default_settings.debug_level = dozer.prefs.debug | |
reset_debug_level() | |
default_settings.heur_enabled = dozer.prefs.heur | |
if default_settings.port ~= dozer.prefs.port then | |
-- remove old one, if not 0 | |
if default_settings.port ~= 0 then | |
dprint2("removing Dozer from port",default_settings.port) | |
DissectorTable.get("udp.port"):remove(default_settings.port, dozer) | |
end | |
-- set our new default | |
default_settings.port = dozer.prefs.port | |
-- add new one, if not 0 | |
if default_settings.port ~= 0 then | |
dprint2("adding Dozer to port",default_settings.port) | |
DissectorTable.get("udp.port"):add(default_settings.port, dozer) | |
end | |
end | |
end | |
dprint2("Dozer Prefs registered") | |
function dozer.dissector(tvbuf,pktinfo,root) | |
dprint2("dozer.dissector called") | |
-- set the protocol column to show our protocol name | |
pktinfo.cols.protocol:set("VLC Dozer") | |
-- We want to check that the packet size is rational during dissection, so let's get the length of the | |
-- packet buffer (Tvb). | |
-- Because DNS has no additional payload data other than itself, and it rides on UDP without padding, | |
-- we can use tvb:len() or tvb:reported_len() here; but I prefer tvb:reported_length_remaining() as it's safer. | |
local pktlen = tvbuf:reported_length_remaining() | |
-- We start by adding our protocol to the dissection display tree. | |
-- A call to tree:add() returns the child created, so we can add more "under" it using that return value. | |
-- The second argument is how much of the buffer/packet this added tree item covers/represents - in this | |
-- case (DNS protocol) that's the remainder of the packet. | |
local tree = root:add(dozer, tvbuf:range(0,12)) | |
local signature = tvbuf:range(0,2):uint() | |
local ptype = tvbuf:range(2,2):uint() | |
local pseq = tvbuf:range(4,2):uint() | |
local pwin = tvbuf:range(6,2):uint() | |
local pcount = tvbuf:range(8,2):uint() | |
local psize = tvbuf:range(10,2):uint() | |
local payload = tvbuf:range(12, pktlen - 12) | |
if (signature ~= 0xd023) then | |
tree:add_proto_expert_info(ef_bad_signature) | |
dprint("packet length",pktlen,"too short") | |
return | |
end | |
-- now let's check it's not too short | |
if (pktlen - 12) < psize then | |
-- since we're going to add this protocol to a specific UDP port, we're going to | |
-- assume packets in this port are our protocol, so the packet being too short is an error | |
-- the old way: tree:add_expert_info(PI_MALFORMED, PI_ERROR, "packet too short") | |
-- the correct way now: | |
tree:add_proto_expert_info(ef_too_short) | |
dprint("packet length",pktlen,"too short") | |
return | |
end | |
tree:add(pf_signature, signature) | |
tree:add(pf_type, ptype) | |
tree:add(pf_seq, pseq) | |
tree:add(pf_win, pwin) | |
tree:add(pf_count, pcount) | |
tree:add(pf_size, psize) | |
-- now the mp2t part | |
local mpeg = tree:add(dozer, payload) | |
if (ptype == 0) then | |
local post_action = Dissector.get("mp2t") | |
post_action(payload:tvb(), pktinfo, mpeg) | |
else if (ptype == 0x1000) then | |
tree:add(pf_media, "FEC (Row)") | |
else | |
tree:add(pf_media, "FEC (Column)") | |
end | |
end | |
-- tell wireshark how much of tvbuff we dissected | |
return pktlen | |
end | |
DissectorTable.get("udp.port"):add(default_settings.port, dozer) | |
local function heur_dissect_dozer(tvbuf,pktinfo,root) | |
dprint2("heur_dissect_dozer called") | |
-- if our preferences tell us not to do this, return false | |
if not default_settings.heur_enabled then | |
return false | |
end | |
local check = tvbr:range(0,2):uint() | |
if check ~= 0xd023 then | |
dprint("heur_dissect_dozer: invalid signature:",check) | |
return false | |
end | |
local check = tvbr:range(2,2):uint() | |
if check > 2 then | |
dprint("heur_dissect_dozer: invalid type:",check) | |
return false | |
end | |
-- ok, looks like it's ours, so go dissect it | |
-- note: calling the dissector directly like this is new in 1.11.3 | |
-- also note that calling a Dissector object, as this does, means we don't | |
-- get back the return value of the dissector function we created previously | |
-- so it might be better to just call the function directly instead of doing | |
-- this, but this script is used for testing and this tests the call() function | |
dozer.dissector(tvbuf,pktinfo,root) | |
-- since this is over a transport protocol, such as UDP, we can set the | |
-- conversation to make it sticky for our dissector, so that all future | |
-- packets to/from the same address:port pair will just call our dissector | |
-- function directly instead of this heuristic function | |
-- this is a new attribute of pinfo in 1.11.3 | |
pktinfo.conversation = dozer | |
return true | |
end | |
dozer:register_heuristic("udp",heur_dissect_dozer) | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment