234 lines
8.7 KiB
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)
|