summaryrefslogtreecommitdiff
path: root/src/main/NsyncMultiget.lua
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/NsyncMultiget.lua')
-rw-r--r--src/main/NsyncMultiget.lua177
1 files changed, 177 insertions, 0 deletions
diff --git a/src/main/NsyncMultiget.lua b/src/main/NsyncMultiget.lua
new file mode 100644
index 0000000..52d218d
--- /dev/null
+++ b/src/main/NsyncMultiget.lua
@@ -0,0 +1,177 @@
+if INCGUARD_06BMkSmD5M3PQVff then
+ error( "Module loaded twice: E_20230526154810" )
+else
+ INCGUARD_06BMkSmD5M3PQVff = true
+
+
+local CONTENT_TYPE_MULTIGET_RESPONSE = "application/multiget-response"
+local MGET_CONTENT_CHUNK = 0x15
+local MGET_CONTENT_TYPE = 0x0B
+local MGET_EOF = 0x1B
+local MGET_PATH = 0x01
+
+local log = io.stderr
+
+local attachInfoToTree, collectInfo, dissector, dissectorProtected, init, seal
+
+
+function init()
+ local app = seal{
+ proto = Proto("Multiget", "Nsync Multiget Response"),
+ field_frameNr = Field.new("frame.number"),
+ field_tcpStrm = Field.new("tcp.stream"),
+ field_httpReqMth = Field.new("http.request.method"),
+ field_httpRspCode = Field.new("http.response.code"),
+ field_path = ProtoField.string("multiget.path", "Path"),
+ field_cType = ProtoField.string("multiget.contentType", "Content Type"),
+ field_chunk = ProtoField.string("multiget.chunk", "Chunk"),
+ field_eof = ProtoField.string("multiget.eof", "EOF"),
+ field_error = ProtoField.string("multiget.error", "Error"),
+ frameNrMax = 0,
+ tcpBuf = {}, -- key=tcpStream+srcPort val=string - half-baked buffer from last packet
+ prevReqContentType = {},
+ prevRspContentType = {},
+ frameInfo = {}, -- key=frameNr val=info
+ }
+ app.proto.fields = {
+ app.field_path, app.field_cType, app.field_chunk, app.field_eof, app.field_error
+ }
+ app.proto.dissector = function(...)return dissectorProtected(app, ...)end
+ DissectorTable.get("media_type"):add(CONTENT_TYPE_MULTIGET_RESPONSE, app.proto)
+end
+
+
+-- I disagree that concealing errors is funny. Therefore I prefer to
+-- make them visible again with help of this workaround.
+function dissectorProtected( app, ... )
+ local ok, a, b, c = pcall(dissector, app, ...)
+ if not ok then
+ log:write("[ERROR] "..(a or"nil").."\n")
+ error(a)
+ else
+ return a, b, c
+ end
+end
+
+
+function dissector( app, buf, pinfo, tree )
+ local frameNr = app.field_frameNr().value
+ local info = app.frameInfo[frameNr]
+ if not info then
+ --log:write("[DEBUG] Dissect frame "..tostring(frameNr).."\n")
+ if frameNr <= app.frameNrMax then
+ log:write("[WARN ] frameNr "..frameNr.." unexpectedly smaller than ".. app.frameNrMax .."\n")
+ end
+ app.frameNrMax = frameNr
+ collectInfo(app, buf, pinfo, tree)
+ end
+ attachInfoToTree(app, frameNr, buf, pinfo, tree)
+end
+
+
+function collectInfo( app, buf, pinfo, tree )
+ local frameNr = app.field_frameNr().value
+ assert(app.frameInfo[frameNr] == nil)
+ local info = seal{
+ mgetMsgs = {},
+ }
+ app.frameInfo[frameNr] = info
+ --
+ local tcpStream = app.field_tcpStrm()
+ if not tcpStream then return --[[TODO Why is tcpStream nil?]] end
+ tcpStream = tcpStream.value
+ --if #app.oldBuf > 0 then log:write("[WARN ] TODO oldBuf has data. Use it!\n") end
+ --
+ local off = 0
+ local raw = buf:raw()
+ while true do
+ local msg = {
+ msgOff = off, msgLen = raw:len() - off,
+ errStr = false,
+ tagOff = false, tagLen = false, tag = false,
+ lenOff = false, lenLen = false, len = false,
+ }
+ msg.tag = buf(off, 1):uint()
+ if msg.tag > 0x7F then -- TODO
+ log:write("[WARN ] Multibyte tag val (".. tostring(msg.tag) ..") not impl yet!\n")
+ end
+ msg.tagOff = off
+ msg.tagLen = 1 -- TODO multibyte support
+ off = off + 1
+ --log:write("Found multiget tag ".. tostring(msg.tag) .."\n")
+ local count, shift, len = 0, 0, 0
+ while true do
+ local b = buf(off, 1):uint();
+ len = bit.bor(len, (bit.lshift(bit.band(b, 0x7F), shift)))
+ off = off + 1; count = count + 1; shift = shift + 7
+ if bit.band(b, 0x80) == 0 then break end
+ if count > 8 then
+ msg.errStr = "Msg Length looks too large"
+ table.insert(info.mgetMsgs, msg)
+ return
+ end
+ end
+ msg.len = len
+ msg.lenOff = off - count
+ msg.lenLen = count
+ msg.valOff = off
+ msg.valLen = len
+ off = off + len
+ msg.msgLen = off - msg.msgOff
+ table.insert(info.mgetMsgs, msg)
+ ---
+ if off == raw:len() then break end
+ assert(off < raw:len())
+ end
+end
+
+
+function attachInfoToTree( app, frameNr, buf, pinfo, tree )
+ local info = app.frameInfo[frameNr]
+ local protoTree = tree:add(app.proto, buf(), "Nsync Multiget Protocol")
+ for _, mgetMsg in ipairs(info.mgetMsgs) do
+ local tileStr = "Nsync Multiget Message"
+ if mgetMsg.errStr then tileStr = tileStr .." (ERROR)" end
+ local msgTree = protoTree:add(app.proto, buf(mgetMsg.msgOff, mgetMsg.msgLen), tileStr)
+ if mgetMsg.errStr then
+ local msg = "ERROR: ".. mgetMsg.errStr
+ msgTree:add(buf(mgetMsg.tagOff,buf:raw():len()-mgetMsg.tagOff), msg)
+ msgTree:add(app.field_error, msg)
+ else
+ local tagStr, fieldFunc
+ if mgetMsg.tag == MGET_PATH then
+ tagStr = "Type PATH (0x01)"
+ fieldFunc = app.field_path
+ elseif mgetMsg.tag == MGET_CONTENT_TYPE then
+ tagStr = "Type CONTENT_TYPE (0x0B)"
+ fieldFunc = app.field_cType
+ elseif mgetMsg.tag == MGET_CONTENT_CHUNK then
+ tagStr = "Type CONTENT_CHUNK (0x15)"
+ fieldFunc = app.field_chunk
+ elseif mgetMsg.tag == MGET_EOF then
+ tagStr = "Type EOF (0x1B)"
+ fieldFunc = app.field_eof
+ else
+ tagStr = "N/A ("..tostring(mgetMsg.tag)..")"
+ fieldFunc = app.field_error
+ end
+ msgTree:add(buf(mgetMsg.tagOff,mgetMsg.tagLen), tagStr)
+ msgTree:add(buf(mgetMsg.lenOff,mgetMsg.lenLen), "Length ".. mgetMsg.len .." bytes")
+ msgTree:add(fieldFunc, buf(mgetMsg.valOff, mgetMsg.valLen))
+ --
+ end
+ end
+end
+
+
+function seal(obj)
+ return setmetatable(obj, {
+ __index = function(t,k,v)error("No such field '"..(k or"nil").."'")end,
+ __newindex = function(t,k,v)error("No such field '"..(k or"nil").."'")end,
+ })
+end
+
+
+init()
+
+end -- INCGUARD_06BMkSmD5M3PQVff