forked from jzwinck/redis-wireshark
-
Notifications
You must be signed in to change notification settings - Fork 0
/
redis-wireshark.lua
116 lines (94 loc) · 4.71 KB
/
redis-wireshark.lua
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
-- Wireshark packet dissector for Redis
-- Protocol specification: http://redis.io/topics/protocol
-- Written by John Zwinck, 29 November 2011
do -- scope
local proto = Proto('redis', 'Redis')
local f = proto.fields
-- we could make more of these, e.g. to distinguish keys from values
f.value = ProtoField.string('redis.value', 'Value')
function proto.dissector(buffer, pinfo, tree)
pinfo.cols.protocol = 'Redis'
mtypes = {
['+'] = 'Status',
['-'] = 'Error',
[':'] = 'Integer',
['$'] = 'Bulk',
['*'] = 'Multi-Bulk',
}
local CRLF = 2 -- constant length of \r\n
-- recursively parse and generate a tree of data from messages in a packet
-- parent: the tree root to populate under
-- buffer: the entire packet buffer
-- offset: the current offset in the buffer
-- matches: a one-pass generator function which yields parsed lines from the packet
-- returns: the new offset (i.e. the input offset plus the number of bytes consumed)
local function recurse(parent, buffer, offset, matches)
local line = matches() -- get next line
local length = line:len()
local prefix, text = line:match('([-+:$*])(.+)')
local mtype = mtypes[prefix]
assert(prefix and text, 'unrecognized line: '..line)
assert(mtype, 'unrecognized message type: '..prefix)
if prefix == '*' then -- multi-bulk, contains multiple sub-messages
local replies = tonumber(text)
-- this is a bit gross: we parse (part of) the buffer again to
-- calculate the length of the entire multi-bulk message
-- if we don't do this, Wireshark will highlight only our prologue
local bytes = 0
local remainder = buffer():string():sub(offset + length + CRLF)
local submatches = remainder:gmatch('[^\r\n]+')
local replies_left = replies
while replies_left > 0 do
local submatch = submatches()
if submatch:sub(1,1) ~= '$' then -- bulk messages contain an extra CRLF
replies_left = replies_left - 1
end
bytes = bytes + submatch:len() + CRLF
end
local child = parent:add(proto, buffer(offset, length + CRLF + bytes),
'Redis '..mtype..' Reply')
offset = offset + length + CRLF
-- recurse down for each message contained in this multi-bulk message
for ii = 1, replies do
offset = recurse(child, buffer, offset, matches)
end
elseif prefix == '$' then -- bulk, contains one binary string
local bytes = tonumber(text)
if bytes == -1 then
local child = parent:add(proto, buffer(offset, length + CRLF),
'Redis '..mtype..' Reply')
offset = offset + length + CRLF
child:add(f.value, '<null>')
else
local child = parent:add(proto, buffer(offset, length + CRLF + bytes + CRLF),
'Redis '..mtype..' Reply')
offset = offset + length + CRLF
-- get the string contained within this bulk message
local line = matches()
local length = line:len()
child:add(f.value, buffer(offset, length))
offset = offset + length + CRLF
end
else -- integer, status or error
local child = parent:add(proto, buffer(offset, length + CRLF),
'Redis '..mtype..' Reply')
child:add(f.value, buffer(offset + prefix:len(), length - prefix:len()))
offset = offset + length + CRLF
end
return offset
end
-- parse top-level messages until the buffer is exhausted
local matches = buffer():string():gmatch('[^\r\n]+')
local offset = 0
while offset < buffer():len() do
offset = recurse(tree, buffer, offset, matches)
end
-- check that we consumed exactly the right number of bytes
assert(offset == buffer():len(), 'consumed '..offset..' bytes of '..buffer():len())
end
-- register this dissector for the standard Redis ports
local dissectors = DissectorTable.get('tcp.port')
for _, port in ipairs{ 6379, } do
dissectors:add(port, proto)
end
end