You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
307 lines
8.4 KiB
Lua
307 lines
8.4 KiB
Lua
-- Copyright (c) 2010, Robert G. Jakabosky <bobby@sharedrealm.com> All rights reserved.
|
|
|
|
-- cache globals to local for speed.
|
|
local format=string.format
|
|
local tostring=tostring
|
|
local tonumber=tonumber
|
|
local sqrt=math.sqrt
|
|
local pairs=pairs
|
|
|
|
-- wireshark API globals
|
|
local Pref = Pref
|
|
local Proto = Proto
|
|
local ProtoField = ProtoField
|
|
local DissectorTable = DissectorTable
|
|
local ByteArray = ByteArray
|
|
local PI_MALFORMED = PI_MALFORMED
|
|
local PI_ERROR = PI_ERROR
|
|
|
|
-- zmq protocol example
|
|
-- declare our protocol
|
|
local zmq_proto = Proto("zmq","ZMQ","ZeroMQ Protocol")
|
|
|
|
-- setup preferences
|
|
zmq_proto.prefs["tcp_port_start"] =
|
|
Pref.string("TCP port range start", "5555", "First TCP port to decode as this protocol")
|
|
zmq_proto.prefs["tcp_port_end"] =
|
|
Pref.string("TCP port range end", "5555", "Last TCP port to decode as this protocol")
|
|
-- current preferences settings.
|
|
local current_settings = {
|
|
tcp_port_start = -1,
|
|
tcp_port_end = -1,
|
|
}
|
|
|
|
-- setup protocol fields.
|
|
zmq_proto.fields = {}
|
|
local fds = zmq_proto.fields
|
|
fds.frame = ProtoField.new("Frame", "zmq.frame", "FT_BYTES", nil, "BASE_NONE")
|
|
fds.length = ProtoField.new("Frame Length", "zmq.frame.len", "FT_UINT64", nil, "BASE_DEC")
|
|
fds.length8 = ProtoField.new("Frame 8bit Length", "zmq.frame.len8", "FT_UINT8", nil, "BASE_DEC")
|
|
fds.length64 = ProtoField.new("Frame 64bit Length", "zmq.frame.len64", "FT_UINT64", nil, "BASE_DEC")
|
|
fds.flags = ProtoField.new("Frame Flags", "zmq.frame.flags", "FT_UINT8", nil, "BASE_HEX", "0xFF")
|
|
fds.flags_more = ProtoField.new("More", "zmq.frame.flags.more", "FT_UINT8", nil, "BASE_HEX", "0x01")
|
|
fds.body = ProtoField.new("Frame body", "zmq.frame.body", "FT_BYTES", nil, "BASE_NONE")
|
|
|
|
-- un-register zmq to handle tcp port range
|
|
local function unregister_tcp_port_range(start_port, end_port)
|
|
if not start_port or start_port <= 0 or not end_port or end_port <= 0 then
|
|
return
|
|
end
|
|
local tcp_port_table = DissectorTable.get("tcp.port")
|
|
for port = start_port,end_port do
|
|
tcp_port_table:remove(port,zmq_proto)
|
|
end
|
|
end
|
|
|
|
-- register zmq to handle tcp port range
|
|
local function register_tcp_port_range(start_port, end_port)
|
|
if not start_port or start_port <= 0 or not end_port or end_port <= 0 then
|
|
return
|
|
end
|
|
local tcp_port_table = DissectorTable.get("tcp.port")
|
|
for port = start_port,end_port do
|
|
tcp_port_table:add(port,zmq_proto)
|
|
end
|
|
end
|
|
|
|
-- handle preferences changes.
|
|
function zmq_proto.init(arg1, arg2)
|
|
local old_start, old_end
|
|
local new_start, new_end
|
|
-- check if preferences have changed.
|
|
for pref_name,old_v in pairs(current_settings) do
|
|
local new_v = zmq_proto.prefs[pref_name]
|
|
if new_v ~= old_v then
|
|
if pref_name == "tcp_port_start" then
|
|
old_start = old_v
|
|
new_start = new_v
|
|
elseif pref_name == "tcp_port_end" then
|
|
old_end = old_v
|
|
new_end = new_v
|
|
end
|
|
-- save new value.
|
|
current_settings[pref_name] = new_v
|
|
end
|
|
end
|
|
-- un-register old port range
|
|
if old_start and old_end then
|
|
unregister_tcp_port_range(tonumber(old_start), tonumber(old_end))
|
|
end
|
|
-- register new port range.
|
|
if new_start and new_end then
|
|
register_tcp_port_range(tonumber(new_start), tonumber(new_end))
|
|
end
|
|
end
|
|
|
|
-- parse flag bits.
|
|
local BITS = {
|
|
MORE = 0x01,
|
|
RESERVED = 0x7E,
|
|
}
|
|
local flag_names = {"MORE"}
|
|
local bits_lookup = {}
|
|
local bits_list = {
|
|
{},
|
|
{MORE = true},
|
|
{MORE = true, RESERVED = true},
|
|
{RESERVED = true},
|
|
}
|
|
local function parse_flags(flags)
|
|
return bits_lookup[flags] or bits_lookup[1]
|
|
end
|
|
|
|
-- make bits object
|
|
local function make_bits(bits)
|
|
local proxy = newproxy(true)
|
|
local meta = getmetatable(proxy)
|
|
meta.__index = bits
|
|
meta.__tostring = function()
|
|
return bits.flags
|
|
end
|
|
-- combind bits into string description.
|
|
local flags = nil
|
|
for i=1,#flag_names do
|
|
local name = flag_names[i]
|
|
if bits[name] then
|
|
if flags then
|
|
flags = flags .. ',' .. name
|
|
else
|
|
flags = name
|
|
end
|
|
end
|
|
end
|
|
-- combind bits into one byte value.
|
|
local byte = 0x00
|
|
for k,v in pairs(bits) do
|
|
local bit = assert(BITS[k], "Invalid bit name.")
|
|
byte = byte + BITS[k]
|
|
end
|
|
bits.flags = flags or ''
|
|
bits.byte = byte
|
|
return proxy
|
|
end
|
|
-- make bits objects in bis_lookup
|
|
for i=1,#bits_list do
|
|
local bits = bits_list[i]
|
|
bits = make_bits(bits)
|
|
bits_lookup[bits.byte] = bits
|
|
end
|
|
|
|
local function zmq_dissect_frame(buffer, pinfo, frame_tree, tap)
|
|
local rang,offset
|
|
-- Frame length
|
|
offset = 0
|
|
local len_off = offset
|
|
local len8_rang = buffer(offset,1)
|
|
local len_rang = len8_rang
|
|
local frame_len = len8_rang:uint()
|
|
-- 8bit length field
|
|
local ti = frame_tree:add(fds.length8, len8_rang)
|
|
ti:set_hidden()
|
|
offset = offset + 1
|
|
if frame_len == 255 then
|
|
local len64_rang = buffer(offset, 8)
|
|
len_rang = buffer(len_off, 9)
|
|
frame_len = tonumber(tostring(len64_rang:uint64()))
|
|
-- 64bit length field.
|
|
local ti = frame_tree:add(fds.length64, len64_rang)
|
|
ti:set_hidden()
|
|
offset = offset + 8
|
|
local ti = frame_tree:add(fds.length, len_rang)
|
|
ti:set_text(format("Frame Length: %d", frame_length))
|
|
else
|
|
frame_tree:add(fds.length, len_rang)
|
|
end
|
|
-- Frame flags
|
|
rang = buffer(offset,1)
|
|
local flags = rang:uint()
|
|
local flags_bits = parse_flags(flags)
|
|
local flags_list = flags_bits.flags
|
|
local flags_tree = frame_tree:add(fds.flags, rang)
|
|
flags_tree:set_text(format('Flags: 0x%02X (%s)', flags, flags_list))
|
|
flags_tree:add(fds.flags_more, rang)
|
|
offset = offset + 1
|
|
if flags_bits.MORE then
|
|
tap.more = tap.more + 1
|
|
else
|
|
-- if the 'more' flag is not set then this is the last frame in a message.
|
|
tap.msgs = tap.msgs + 1
|
|
end
|
|
-- Frame body
|
|
local body_len = frame_len - 1
|
|
local body = ''
|
|
if body_len > 0 then
|
|
tap.body_bytes = tap.body_bytes + body_len
|
|
rang = buffer(offset, body_len)
|
|
local ti = frame_tree:add_le(fds.body, rang)
|
|
if body_len <= 4 then
|
|
body = format("%08x", rang:uint())
|
|
else
|
|
body = tostring(rang)
|
|
end
|
|
ti:set_text(format("%s", body))
|
|
end
|
|
offset = offset + body_len
|
|
-- frame summary
|
|
if body_len > 0 then
|
|
if flags_bits.MORE then
|
|
frame_tree:set_text(format("Frame: [MORE] Body[%u]=%s", body_len, body))
|
|
else
|
|
frame_tree:set_text(format("Frame: Body[%u]=%s", body_len, body))
|
|
end
|
|
else
|
|
if flags_bits.MORE then
|
|
frame_tree:set_text(format("Frame: [MORE] No data"))
|
|
else
|
|
frame_tree:set_text(format("Frame: No data"))
|
|
end
|
|
end
|
|
end
|
|
|
|
local DESEGMENT_ONE_MORE_SEGMENT = 0x0fffffff
|
|
local DESEGMENT_UNTIL_FIN = 0x0ffffffe
|
|
|
|
-- packet dissector
|
|
function zmq_proto.dissector(tvb,pinfo,tree)
|
|
local offset = 0
|
|
local tvb_length = tvb:len()
|
|
local reported_length = tvb:reported_len()
|
|
local length_remaining
|
|
local zmq_tree
|
|
local rang
|
|
local frames = 0
|
|
local tap = {}
|
|
|
|
tap.frames = 0
|
|
tap.msgs = 0
|
|
tap.more = 0
|
|
tap.body_bytes = 0
|
|
|
|
while(offset < reported_length and offset < tvb_length) do
|
|
length_remaining = tvb_length - offset
|
|
-- check for fixed part of PDU
|
|
if length_remaining < 2 then
|
|
pinfo.desegment_offset = offset
|
|
pinfo.desegment_len = DESEGMENT_ONE_MORE_SEGMENT
|
|
break
|
|
end
|
|
-- decode frame length
|
|
-- decode single byte frame length
|
|
rang = tvb(offset, 1)
|
|
local frame_len = rang:le_uint()
|
|
local pdu_len = frame_len + 1
|
|
if frame_len == 255 then
|
|
-- make sure there is enough bytes
|
|
if length_remaining < 10 then
|
|
pinfo.desegment_offset = offset
|
|
pinfo.desegment_len = DESEGMENT_ONE_MORE_SEGMENT
|
|
break
|
|
end
|
|
-- decode extra long frame length.
|
|
rang = tvb(offset + 1, 8)
|
|
frame_len = tonumber(tostring(rang:uint64()))
|
|
pdu_len = frame_len + 9
|
|
end
|
|
-- provide hints to tcp
|
|
if not pinfo.visited then
|
|
local remaining_bytes = reported_length - offset
|
|
if pdu_len > remaining_bytes then
|
|
pinfo.want_pdu_tracking = 2
|
|
pinfo.bytes_until_next_pdu = pdu_len - remaining_bytes
|
|
end
|
|
end
|
|
-- check if we need more bytes to dissect this frame.
|
|
if length_remaining < pdu_len then
|
|
pinfo.desegment_offset = offset
|
|
pinfo.desegment_len = (pdu_len - length_remaining)
|
|
break
|
|
end
|
|
-- dissect zmq frame
|
|
if not zmq_tree then
|
|
zmq_tree = tree:add(zmq_proto,tvb(),"ZMQ frames")
|
|
end
|
|
rang = tvb(offset, pdu_len)
|
|
local frame_tree = zmq_tree:add(fds.frame, rang)
|
|
zmq_dissect_frame(rang:tvb(), pinfo, frame_tree, tap)
|
|
frames = frames + 1
|
|
-- step to next frame.
|
|
local offset_before = offset
|
|
offset = offset + pdu_len
|
|
if offset < offset_before then break end
|
|
end
|
|
if zmq_tree then
|
|
zmq_tree:set_text(format("ZMQ frames=%u", frames))
|
|
end
|
|
if frames > 0 then
|
|
tap.frames = frames
|
|
pinfo.tap_data = tap
|
|
end
|
|
-- Info column
|
|
pinfo.cols.protocol = "ZMQ"
|
|
pinfo.cols.info = format('ZMQ frames=%u',frames)
|
|
end
|
|
|
|
-- register zmq to handle tcp ports 5550-5560
|
|
register_tcp_port_range(5550,5560)
|
|
|