From 101d72f99220a6111e25809539512a02ba92b2c7 Mon Sep 17 00:00:00 2001 From: Andreas Fankhauser hiddenalpha.ch Date: Mon, 13 May 2024 22:29:25 +0200 Subject: Add from existing sources. Originates from: > commit f1d070047a080e33d773330b53e81435062bc42c > Author: Andreas Fankhauser hiddenalpha.ch <23085769+hiddenalpha@users.noreply.github.com> > Date: 2023-05-26 15:51:46 +0200 --- src/main/NsyncMultiget.lua | 177 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 177 insertions(+) create mode 100644 src/main/NsyncMultiget.lua (limited to 'src') 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 -- cgit v1.3.1