summaryrefslogtreecommitdiff
path: root/src/main/lua/wireshark/HttpTime.lua
blob: b06c0a738a88f5cdd343352bf234f01c318fca7a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
if INCGUARD_20211104184619 then
    error( "Module loaded twice: E_20230526154713" )
else
    INCGUARD_20211104184619 = true


local out, log = io.stdout, io.stderr
local mod = {}


function mod.init()
    local that = mod.seal{
        proto = Proto("__", "Additional Metadata"),
        f_andy_httpTime = ProtoField.float("_.httpTime", "HttpTime"),
        f_andy_synSeen = ProtoField.bool("_.synSeen", "SynSeen"),
        f_andy_uri = ProtoField.string("_.uri", "Request URI"),
        f_andy_contentType = ProtoField.string("_.contentType", "Content Type"),
        f_andy_xService = ProtoField.string("_.xService", "X Service"),
        f_andy_xVehicleId = ProtoField.string("_.xVehicleId", "X Vehicleid"),
        f_http_request = Field.new("http.request"),
        f_http_time = Field.new("http.time"),
        f_http_uri = Field.new("http.request.uri"),
        f_tcp_flags_syn = Field.new("tcp.flags.syn"),
        f_tcp_stream = Field.new("tcp.stream"),
        f_frame_number = Field.new("frame.number"),
        frameNrMax = 0,
        frameInfo = {},
        prevUris = {}, -- key=tcpStream val=string - URL path of the last http request.
        prevContentType = {},
        prevXService = {}, -- key=tcpStream val=string - Value of 'X-Service' HTTP header.
        prevXVehicleId = {},
        synSeen = {}, -- key=tcpStream val=bool - true if we have a SYN for this stream.
    }
    that.proto.dissector = function(...)return mod.dissectorProtected(that,...)end
    that.proto.fields = {
        that.f_andy_httpTime,
        that.f_andy_synSeen, that.f_andy_uri, that.f_andy_contentType,
        that.f_andy_xService, that.f_andy_xVehicleId,
    }
    register_postdissector(that.proto)
end


function mod.dissector( that, buf, pinfo, tree )
    local frameNr = that.f_frame_number().value
    local info = that.frameInfo[frameNr]
    if not info then
        if frameNr <= that.frameNrMax then
            log:write("[WARN ] frameNr "..frameNr.." unexpectedly smaller than ".. that.frameNrMax .."\n")
        end
        that.frameNrMax = frameNr
        mod.collectInfo(that, buf, pinfo, tree, frameNr)
    end
    mod.attachInfoToTree(that, buf, pinfo, tree, frameNr)
end


function mod.collectInfo( that, buf, pinfo, tree, frameNr )
    assert(that.frameInfo[frameNr] == nil)
    local info = mod.seal{
        synSeen = false,
        httpTime = false,
        httpUri = false,
        cType = false,
        xService = false,
        xVehicleId = false,
    } that.frameInfo[frameNr] = info
    -- Tcp Stream
    local tcpStream = that.f_tcp_stream()
    if not tcpStream then return --[[TODO Why is tcpStream nil?]] end
    tcpStream = tcpStream.value
    --
    if that.f_tcp_flags_syn().value then
        that.synSeen[tcpStream] = true
        info.synSeen = true
    end
    -- HTTP
    local httpTime = that.f_http_time()
    local isHttpRequest = that.f_http_request()
    local isHttpResponse = (httpTime ~= nil)
    if isHttpRequest then
        local tmp
        tmp = tostring(that.f_http_uri())
        that.prevUris[tcpStream] = tmp
        info.httpUri = tmp or false
        tmp = buf:raw():match("\x0A[Cc][Oo][Nn][Tt][Ee][Nn][Tt]%-[Tt][Yy][Pp][Ee]:[%s]*([^\x0D\x0A]+)[\x0D\x0A]")
        that.prevContentType[tcpStream] = tmp or nil
        info.cType = tmp or false
        tmp = buf:raw():match("\x0A[Xx]%-[Ss][Ee][Rr][Vv][Ii][Cc][Ee]:[%s]*([^\x0D\x0A]+)[\x0D\x0A]")
        that.prevXService[tcpStream] = tmp or nil
        info.xService = tmp or false
        tmp = buf:raw():match("\x0A[Xx]%-[Vv][Ee][Hh][Ii][Cc][Ll][Ee][Ii][Dd]:[%s]*([^\x0D\x0A]+)[\x0D\x0A]")
        that.prevXVehicleId[tcpStream] = tmp or nil
        info.xVehicleId = tmp or false
    elseif isHttpResponse then
        -- Need to re-evaluate Content-Type, as response has this too
        local contentType = buf:raw():match("\x0A[Cc][Oo][Nn][Tt][Ee][Nn][Tt]%-[Tt][Yy][Pp][Ee]:[%s]*([^\x0D\x0A]+)[\x0D\x0A]")
        that.prevContentType[tcpStream] = (contentType)and(contentType)or(nil)
        info.httpTime = tonumber(tostring(httpTime))
    end
    -- Enrich by info from other packets around this same tcpStream
    info.synSeen = that.synSeen[tcpStream] or false
    info.httpUri = that.prevUris[tcpStream] or false
    info.cType = that.prevContentType[tcpStream] or false
    info.xService = that.prevXService[tcpStream] or false
    info.xVehicleId = that.prevXVehicleId[tcpStream] or false
end


function mod.attachInfoToTree( that, buf, pinfo, tree, frameNr )
    local info = that.frameInfo[frameNr]
    local metaTree = tree:add(that.proto, "AdditionalMetadata")
    local tmp
    metaTree:add(that.f_andy_synSeen, info.synSeen)
    if info.httpUri then metaTree:add(that.f_andy_uri, info.httpUri) end
    if info.cType then metaTree:add(that.f_andy_contentType, info.cType) end
    if info.xService then metaTree:add(that.f_andy_xService, info.xService) end
    if info.xVehicleId then metaTree:add(that.f_andy_xVehicleId, info.xVehicleId) end
    if info.httpTime then metaTree:add(that.f_andy_httpTime, info.httpTime) end
end


-- Looks like tshark just conceals all errors inside this callback. This
-- wrapper is to make our problems visible again.
function mod.dissectorProtected( that, buf, pinfo, tree )
    local ok, msg = pcall(function( that, buf, pinfo, tree )
        mod.dissector( that, buf, pinfo, tree )
    end, that, buf, pinfo, tree )
    if not ok then
        log:write("[ERROR] "..(msg or"nil").."\n")
    end
end


function mod.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



mod.init()    

end -- INCGUARD_20211104184619