ratatoskr-messenger/contrib/zephyr-dissector.lua

234 lines
8.7 KiB
Lua

-- Wireshark dissector for Zephyr
-- Place in ~/.local/lib/wireshark/plugins/2.6/
zephyr_protocol = Proto("Zephyr", "Zephyr IM Protocol")
local kind_names = {
[0] = "UNSAFE",
[1] = "UNACKED",
[2] = "ACKED",
[3] = "HMACK",
[4] = "HMCTL",
[5] = "SERVACK",
[6] = "SERVNAK",
[7] = "CLIENTACK",
[8] = "STAT"
}
zephyr_protocol.fields.version = ProtoField.stringz("zephyr.version", "Version")
zephyr_protocol.fields.numfields = ProtoField.uint32("zephyr.fields", "Field Count")
zephyr_protocol.fields.z_kind = ProtoField.uint32("zephyr.z_kind", "Kind", base.DEC, kind_names)
zephyr_protocol.fields.z_uid = ProtoField.stringz("zephyr.z_uid", "UID")
zephyr_protocol.fields.z_uid_addr = ProtoField.ipv4("zephyr.z_uid.zuid_addr", "Address")
zephyr_protocol.fields.z_uid_tv = ProtoField.absolute_time("zephyr.z_uid.tv", "Time")
zephyr_protocol.fields.z_port = ProtoField.uint16("zephyr.z_port", "Port")
zephyr_protocol.fields.z_auth = ProtoField.uint32("zephyr.z_auth", "Auth")
zephyr_protocol.fields.z_authent_len = ProtoField.uint32("zephyr.z_authent_len", "Authenticator Length")
zephyr_protocol.fields.z_ascii_authent = ProtoField.stringz("zephyr.z_ascii_authent", "Authenticator")
zephyr_protocol.fields.z_class = ProtoField.stringz("zephyr.z_class", "Class")
zephyr_protocol.fields.z_class_inst = ProtoField.stringz("zephyr.z_class_inst", "Instance")
zephyr_protocol.fields.z_opcode = ProtoField.stringz("zephyr.z_opcode", "Opcode")
zephyr_protocol.fields.z_sender = ProtoField.stringz("zephyr.z_sender", "Sender")
zephyr_protocol.fields.z_recipient = ProtoField.stringz("zephyr.z_recipient", "Recipient")
zephyr_protocol.fields.z_default_format = ProtoField.stringz("zephyr.z_default_format", "Default Format")
zephyr_protocol.fields.z_ascii_checksum = ProtoField.stringz("zephyr.z_ascii_checksum", "ASCII Checksum")
zephyr_protocol.fields.z_checksum = ProtoField.uint32("zephyr.z_checksum", "Checksum")
zephyr_protocol.fields.z_multinotice = ProtoField.stringz("zephyr.z_multinotice", "Multinotice")
zephyr_protocol.fields.z_multiuid = ProtoField.stringz("zephyr.z_multiuid", "MultiUID")
zephyr_protocol.fields.z_multiuid_addr = ProtoField.ipv4("zephyr.z_multiuid.zuid_addr", "Address")
zephyr_protocol.fields.z_multiuid_tv = ProtoField.absolute_time("zephyr.z_multiuid.tv", "Time")
zephyr_protocol.fields.z_sender_sockaddr = ProtoField.ipv4("zephyr.z_sender_sockaddr", "Sender sockaddr")
zephyr_protocol.fields.z_charset = ProtoField.uint16("zephyr.z_charset", "Character Set")
zephyr_protocol.fields.z_message = ProtoField.string("zephyr.z_message", "Message")
zephyr_protocol.fields.z_message_len = ProtoField.uint32("zephyr.z_message_len", "Message Length")
function parse_ascii(range)
local v = range(2):string()
return tonumber(string.match(v, "[0-9A-F]+"), 16)
end
function parse_ascii_ip(range)
local ip = "" .. tonumber(range(2, 2):string(), 16) .. "." .. tonumber(range(4, 2):string(), 16) .. "." .. tonumber(range(6, 2):string(), 16) .. "." .. tonumber(range(8, 2):string(), 16)
return Address.ip(ip)
end
function parse_zcode_ip(range)
local bytes = {}
local str = range(1,range:len()-2):bytes()
local i = 0
while i < str:len() do
local byte = str:get_index(i)
info(i .. ": " .. byte)
if (byte == 0xFF) then
if (str:get_index(i+1) == 0xF0) then
table.insert(bytes, 0)
elseif (str:get_index(i+1) == 0xF1) then
table.insert(bytes, 0xFF)
end
i = i + 2
else
table.insert(bytes, byte)
i = i + 1
end
end
info(bytes[1])
info(table.concat(bytes, "."))
return Address.ip(table.concat(bytes, "."))
end
function parse_ascii_tv(range)
info(range:string())
local seconds = parse_ascii(range)
local microseconds = parse_ascii(range(11))
return NSTime.new(seconds, microseconds*1000)
end
function zephyr_protocol.dissector(buffer, pinfo, tree)
length = buffer:len()
if length == 0 then return end
pinfo.cols.protocol = zephyr_protocol.name
local subtree = tree:add(zephyr_protocol, buffer(), "Zephyr")
local range = buffer()
local length = range:strsize()
local v = range:stringz()
subtree:add(zephyr_protocol.fields.version, range)
if v == "ZEPH0.2" then
range = buffer(range:offset()+range:len())
range = range(0, range:strsize())
local numfields = parse_ascii(range)
subtree:add(zephyr_protocol.fields.numfields, range, numfields)
numfields = numfields - 2
range = buffer(range:offset()+range:len())
range = range(0, range:strsize())
local kind = parse_ascii(range)
subtree:add(zephyr_protocol.fields.z_kind, range, kind)
numfields = numfields - 1
range = buffer(range:offset()+range:len())
range = range(0, range:strsize())
local z_uid = subtree:add(zephyr_protocol.fields.z_uid, range)
local addr = parse_ascii_ip(range)
local tv = parse_ascii_tv(range(11))
z_uid:add(zephyr_protocol.fields.z_uid_addr, range(0, 11), addr)
z_uid:add(zephyr_protocol.fields.z_uid_tv, range(11), tv)
z_uid:set_text("UID: " .. tostring(addr) .. " " .. tostring(tv))
numfields = numfields - 1
range = buffer(range:offset()+range:len())
range = range(0, range:strsize())
subtree:add(zephyr_protocol.fields.z_port, range, parse_ascii(range))
numfields = numfields - 1
range = buffer(range:offset()+range:len())
range = range(0, range:strsize())
subtree:add(zephyr_protocol.fields.z_auth, range, parse_ascii(range))
numfields = numfields - 1
range = buffer(range:offset()+range:len())
range = range(0, range:strsize())
local authent_length = parse_ascii(range)
subtree:add(zephyr_protocol.fields.z_authent_len, range, authent_length)
numfields = numfields - 1
function add(field)
range = buffer(range:offset()+range:len())
subtree:add(field, range)
numfields = numfields - 1
end
add(zephyr_protocol.fields.z_ascii_authent)
add(zephyr_protocol.fields.z_class)
local class = range:stringz()
add(zephyr_protocol.fields.z_class_inst)
local instance = range:stringz()
add(zephyr_protocol.fields.z_opcode)
local opcode = range:stringz()
add(zephyr_protocol.fields.z_sender)
add(zephyr_protocol.fields.z_recipient)
add(zephyr_protocol.fields.z_default_format)
if (numfields > 0) then
range = buffer(range:offset()+range:len())
range = range(0, range:strsize())
subtree:add(zephyr_protocol.fields.z_ascii_checksum, range)
local status, value = pcall(function() return parse_ascii(range) end)
if status then
subtree:add(zephyr_protocol.fields.z_checksum, range, value)
end
numfields = numfields - 1
-- TODO: validate checksum and flag if wrong
end
if (numfields > 0) then
range = buffer(range:offset()+range:len())
subtree:add(zephyr_protocol.fields.z_multinotice, range)
numfields = numfields - 1
end
if (numfields > 0) then
range = buffer(range:offset()+range:len())
range = range(0, range:strsize())
local z_multiuid = subtree:add(zephyr_protocol.fields.z_multiuid, range)
local addr = parse_ascii_ip(range)
local tv = parse_ascii_tv(range(11))
z_multiuid:add(zephyr_protocol.fields.z_multiuid_addr, range(0, 11), addr)
z_multiuid:add(zephyr_protocol.fields.z_multiuid_tv, range(11), tv)
z_multiuid:set_text("MultiUID: " .. tostring(addr) .. " " .. tostring(tv))
numfields = numfields - 1
-- else z_multiuid = z_uid
end
if (numfields > 0) then
range = buffer(range:offset()+range:len())
range = range(0, range:strsize())
if (range:string():byte(1) == 90) then
-- TODO: parse Zcode address for real
subtree:add(zephyr_protocol.fields.z_sender_sockaddr, range, parse_zcode_ip(range)):append_text(" (Zcode)")
else
subtree:add(zephyr_protocol.fields.z_sender_sockaddr, range, parse_ascii_ip(range)):append_text(" (NetASCII)")
end
numfields = numfields - 1
end
if (numfields > 0) then
range = buffer(range:offset()+range:len())
range = range(0, range:strsize())
subtree:add(zephyr_protocol.fields.z_charset, range, parse_ascii(range))
numfields = numfields - 1
end
if (numfields > 0) then
local otherfields = subtree:add("Other Fields")
for i = 1,numfields do
range = buffer(range:offset()+range:len())
range = range(0, range:strsize())
otherfields:add(range)
numfields = numfields - 1
end
end
range = buffer(range:offset()+range:len())
if (range:len() > 0) then
subtree:add(zephyr_protocol.fields.z_message_len, range:len())
subtree:add(zephyr_protocol.fields.z_message, range)
end
local info = kind_names[kind] .. " " .. class .. " " .. instance .. " " .. opcode
pinfo.columns.info = info
subtree.text = "Zephyr, " .. info
end
end
local udp_port = DissectorTable.get("udp.port")
udp_port:add(2102, zephyr_protocol)
udp_port:add(2103, zephyr_protocol)
udp_port:add(2104, zephyr_protocol)