forked from whitequark/zmtp-wireshark
-
Notifications
You must be signed in to change notification settings - Fork 0
/
zmtp-dissector.lua
567 lines (492 loc) · 29.1 KB
/
zmtp-dissector.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
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
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
-- Copyright (c) 2019-2020 Viveris Technologies <adrien.destugues@opensource.viveris.fr>
-- Copyright (c) 2014 Peter Zotov <whitequark@whitequark.org>
-- Copyright (c) 2011, Robert G. Jakabosky <bobby@sharedrealm.com> All rights reserved.
-- cache globals to local for speed.
local format=string.format
local tostring=tostring
local tonumber=tonumber
local sqrt=math.sqrt
local pairs=pairs
-- wireshark API globals
local Pref = Pref
local Proto = Proto
local ProtoField = ProtoField
local DissectorTable = DissectorTable
local ByteArray = ByteArray
local PI_MALFORMED = PI_MALFORMED
local PI_ERROR = PI_ERROR
-- zmq protocol example
-- declare our protocol
local zmtp_proto = Proto("zmtp", "ZMTP", "ZeroMQ Message Transport Protocol")
-- setup protocol fields.
vs_stype = {
[0] = "pair",
[1] = "publish",
[2] = "subscribe",
[3] = "request",
[4] = "reply",
[5] = "dealer",
[6] = "router",
[7] = "pull",
[8] = "push"
}
zmtp_proto.fields = {}
local fds = zmtp_proto.fields
fds.greeting = ProtoField.new("Signature", "zmtp.greeting", ftypes.BYTES)
fds.version = ProtoField.new("ZMTP Version", "zmtp.greeting.version", ftypes.UINT16, nil, base.HEX)
fds.version_major = ProtoField.new("ZMTP Major Version", "zmtp.greeting.version.major", ftypes.UINT8, nil, base.DEC)
fds.version_minor = ProtoField.new("ZMTP Minor Version", "zmtp.greeting.version.minor", ftypes.UINT8, nil, base.DEC)
fds.mechanism = ProtoField.new("ZMTP Security Mechanism", "zmtp.greeting.mechanism", ftypes.STRINGZ)
fds.as_server = ProtoField.new("Is a ZMTP Server", "zmtp.greeting.as_server", ftypes.BOOLEAN)
fds.stype = ProtoField.new("Socket type", "zmtp.stype", ftypes.UINT8, vs_stype, base.HEX)
fds.frame = ProtoField.new("Frame", "zmtp.frame", ftypes.BYTES)
fds.flags = ProtoField.new("Flags", "zmtp.frame.flags", ftypes.UINT8, nil, base.HEX, "0xFF")
fds.flags_more = ProtoField.new("Has More", "zmtp.frame.flags.more", ftypes.UINT8, {[1]="Yes",[0]="No"}, base.DEC, "0x01")
fds.flags_long = ProtoField.new("64-bit Length", "zmtp.frame.flags.64bit", ftypes.UINT8, {[1]="Yes",[0]="No"}, base.DEC, "0x02")
fds.flags_cmd = ProtoField.new("Is Command", "zmtp.frame.flags.command", ftypes.UINT8, {[1]="Yes",[0]="No"}, base.DEC, "0x04")
fds.length = ProtoField.new("Payload Length", "zmtp.frame.length", ftypes.UINT64, nil, base.DEC)
fds.protocol = ProtoField.new("Protocol", "zmtp.frame.protocol", ftypes.STRING, nil, base.NONE, 0, "Set protocol in Preferences → ZMTP")
fds.command_name = ProtoField.new("Command Name", "zmtp.command.name", ftypes.STRING)
fds.cmd_unknown_data = ProtoField.new("Unknown Command", "zmtp.command.unknown", ftypes.BYTES)
fds.cmd_ready = ProtoField.new("READY Command", "zmtp.command.ready", ftypes.BYTES)
fds.cmd_ready_curvezmq_nonce = ProtoField.new("CurveZMQ Server Nonce", "zmtp.command.ready.curvezmq.nonce", ftypes.BYTES)
fds.cmd_ready_curvezmq_box = ProtoField.new("CurveZMQ Box (metadata)", "zmtp.command.ready.curvezmq.box", ftypes.BYTES)
fds.cmd_initiate = ProtoField.new("INITIATE Command", "zmtp.command.initiate", ftypes.BYTES)
fds.cmd_initiate_curvezmq_cookie = ProtoField.new("CurveZMQ Server Cookie", "zmtp.command.initiate.curvezmq.cookie", ftypes.BYTES)
fds.cmd_initiate_curvezmq_nonce = ProtoField.new("CurveZMQ Client Nonce", "zmtp.command.initiate.curvezmq.nonce", ftypes.BYTES)
fds.cmd_initiate_curvezmq_box = ProtoField.new("CurveZMQ Box (client permanent public key, vouch, metadata)", "zmtp.command.initiate.curvezmq.box", ftypes.BYTES)
fds.cmd_metadata_key = ProtoField.new("Metadata Key", "zmtp.command.metadata.key", ftypes.STRING)
fds.cmd_metadata_value = ProtoField.new("Metadata Value", "zmtp.command.metadata.value", ftypes.STRING)
fds.cmd_hello = ProtoField.new("HELLO Command", "zmtp.command.hello", ftypes.BYTES)
fds.cmd_hello_username = ProtoField.new("Username", "zmtp.command.hello.username", ftypes.STRING)
fds.cmd_hello_password = ProtoField.new("Password", "zmtp.command.hello.password", ftypes.STRING)
fds.cmd_hello_curvezmq_version = ProtoField.new("CurveZMQ Version", "zmtp.command.hello.curvezmq.version", ftypes.UINT16, nil, base.HEX)
fds.cmd_hello_curvezmq_version_major = ProtoField.new("CurveZMQ Major", "zmtp.command.hello.curvezmq.version.major", ftypes.UINT8, nil, base.DEC)
fds.cmd_hello_curvezmq_version_minor = ProtoField.new("CurveZMQ Minor", "zmtp.command.hello.curvezmq.version.minor", ftypes.UINT8, nil, base.DEC)
fds.cmd_hello_curvezmq_padding = ProtoField.new("CurveZMQ Padding", "zmtp.command.hello.curvezmq.padding", ftypes.BYTES)
fds.cmd_hello_curvezmq_pubkey = ProtoField.new("CurveZMQ Transient Public Key", "zmtp.command.hello.curvezmq.pubkey", ftypes.BYTES)
fds.cmd_hello_curvezmq_nonce = ProtoField.new("CurveZMQ Nonce", "zmtp.command.hello.curvezmq.nonce", ftypes.BYTES)
fds.cmd_hello_curvezmq_signature = ProtoField.new("CurveZMQ Signature", "zmtp.command.hello.curvezmq.Signature", ftypes.BYTES)
fds.cmd_welcome = ProtoField.new("WELCOME Command", "zmtp.command.welcome", ftypes.BYTES)
fds.cmd_welcome_curvezmq_nonce = ProtoField.new("CurveZMQ Server Nonce", "zmtp.command.welcome.curvezmq.nonce", ftypes.BYTES)
fds.cmd_welcome_curvezmq_box = ProtoField.new("CurveZMQ Box (server public transient key, server cookie)", "zmtp.command.welcome.curvezmq.box", ftypes.BYTES)
fds.message = ProtoField.new("Encrypted MESSAGE", "zmtp.message", ftypes.BYTES)
fds.message_curvezmq_nonce = ProtoField.new("CurveZMQ Message Nonce", "zmtp.message.curvezmq.nonce", ftypes.BYTES)
fds.message_curvezmq_box = ProtoField.new("CurveZMQ Message Box (flags, payload)", "zmtp.message.curvezmq.box", ftypes.BYTES)
fds.cmd_error = ProtoField.new("ERROR Command", "zmtp.command.error", ftypes.BYTES)
fds.cmd_error_reason = ProtoField.new("ERROR Reason", "zmtp.command.error.reason", ftypes.STRING)
fds.cmd_ping = ProtoField.new("PING Command", "zmtp.command.ping", ftypes.BYTES)
fds.cmd_ping_ttl = ProtoField.new("Time To Live (deciseconds)", "zmtp.command.ping.ttl", ftypes.UINT16)
fds.cmd_ping_context = ProtoField.new("Context", "zmtp.command.ping.context", ftypes.STRING)
fds.cmd_pong = ProtoField.new("PONG Command", "zmtp.command.pong", ftypes.BYTES)
fds.cmd_pong_context = ProtoField.new("Context", "zmtp.command.pong.context", ftypes.STRING)
local tcp_stream_id = Field.new("tcp.stream")
local subdissectors = DissectorTable.new("zmtp.protocol", "ZMTP", ftypes.UINT16)
local stream_mechanisms = {}
local function zmq_dissect_frame(buffer, pinfo, frame_tree, tap, toplevel_tree, stream_id)
local flags_rang = buffer(0, 1)
local flags = flags_rang:uint()
if flags == 0xff then
-- greeting
frame_tree:add(fds.greeting, buffer(0, 10))
frame_tree:set_text(format("Greeting"))
local version_tree = frame_tree:add(fds.version, buffer(10, 2))
local version_major_rang = buffer(10, 1)
version_major = version_major_rang:uint()
if version_major >= 2 then
version_tree:add(fds.version_major, version_major_rang)
local version_minor_rang = buffer(11, 1)
version_tree:add(fds.version_minor, version_minor_rang)
local mechanism_rang = buffer(12, 20)
frame_tree:add(fds.mechanism, mechanism_rang)
local as_server_rang = buffer(32, 1)
frame_tree:add(fds.as_server, as_server_rang)
tap.mechanism = mechanism_rang:stringz()
stream_mechanisms[stream_id] = tap.mechanism
if tap.mechanism == "NULL" then
return format("Greeting (ZMTP %d.%d, %s Mechanism)",
version_major_rang:uint(),
version_minor_rang:uint(),
mechanism_rang:string())
else
local role
if as_server_rang:uint() == 1 then role = "Server" else role = "Client" end
return format("Greeting (ZMTP %d.%d, %s Mechanism, %s)",
version_major_rang:uint(),
version_minor_rang:uint(),
mechanism_rang:string(), role)
end
else
version_tree:add(fds.version_major, version_major_rang)
local type_rang = buffer(11, 1)
frame_tree:add(fds.stype, type_rang)
return format("Greeting (ZMTP %d), %s",
version_major_rang:uint(),
vs_stype[type_rang:uint()])
end
end
local flags_tree = frame_tree:add(fds.flags, buffer(0, 1))
flags_tree:add(fds.flags_more, buffer(0, 1))
flags_tree:add(fds.flags_long, buffer(0, 1))
flags_tree:add(fds.flags_cmd, buffer(0, 1))
local flag_more = (bit.band(flags, 0x01) ~= 0)
local flag_long = (bit.band(flags, 0x02) ~= 0)
local flag_cmd = (bit.band(flags, 0x04) ~= 0)
local len_rang
local body_offset
local body_len
if flag_long then -- LONG
len_rang = buffer(1, 8)
-- not before wireshark 1.11; http://wiki.wireshark.org/LuaAPI/Int64
-- body_len = len_rang:uint64():tonumber()
body_len = tonumber(tostring(len_rang:uint64()))
body_offset = 9
else
len_rang = buffer(1, 1)
body_len = len_rang:uint()
body_offset = 2
end
frame_tree:add(fds.length, len_rang)
local flags_desc = {}
if flag_more then table.insert(flags_desc, "More") end
if flag_cmd then table.insert(flags_desc, "Command") end
if #flags_desc > 0 then
flags_tree:append_text(format(" (%s)", table.concat(flags_desc, ", ")))
end
tap.body_bytes = tap.body_bytes + body_len
local has_more = ""
if flag_more then
has_more = " [Has More]"
end
local mechanism = stream_mechanisms[stream_id]
local body_rang = buffer(body_offset, body_len)
if flag_cmd then
tap.commands = tap.commands + 1
local cmd_name_len = body_rang:range(0, 1):uint()
local cmd_name_rang = body_rang:range(1, cmd_name_len)
local cmd_name = cmd_name_rang:string()
frame_tree:add(fds.command_name, cmd_name_rang)
local cmd_data_rang
if body_rang:len() > 1 + cmd_name_len then
cmd_data_rang = body_rang:range(1 + cmd_name_len)
end
local function parse_metadata(cmd_tree)
local metadata = {}
local md_offset = 0
while md_offset < cmd_data_rang:len() do
local key_len = cmd_data_rang:range(md_offset, 1):uint()
local key_rang = cmd_data_rang:range(md_offset + 1, key_len)
cmd_tree:add(fds.cmd_metadata_key, key_rang)
md_offset = md_offset + 1 + key_len
local value_len = cmd_data_rang:range(md_offset, 4):uint()
if value_len > 0 then
local value_rang = cmd_data_rang:range(md_offset + 4, value_len)
cmd_tree:add(fds.cmd_metadata_value, value_rang)
md_offset = md_offset + 4 + value_len
table.insert(metadata, format("%s: %s",
key_rang:string(), value_rang:string()))
else
md_offset = md_offset + 4
table.insert(metadata, format("%s: (empty)",
key_rang:string()))
end
end
return table.concat(metadata, ", ")
end
if cmd_name == "READY" and mechanism ~= "CURVE" then
local ready_tree = frame_tree:add(fds.cmd_ready, cmd_data_rang)
frame_tree:set_text(format("Command READY%s: %s",
has_more, parse_metadata(ready_tree)))
elseif cmd_name == "READY" and mechanism == "CURVE" then
local ready_tree = frame_tree:add(fds.cmd_ready, cmd_data_rang)
ready_tree:add(fds.cmd_ready_curvezmq_nonce,
cmd_data_rang:range(0, 8))
ready_tree:add(fds.cmd_ready_curvezmq_box,
cmd_data_rang:range(8, cmd_data_rang:len() - 8))
frame_tree:set_text(format("Command READY"))
elseif cmd_name == "HELLO" and mechanism == "PLAIN" then
local hello_tree = frame_tree:add(fds.cmd_hello, cmd_data_rang)
local username_len = cmd_data_rang:range(0, 1):uint()
local username_rang
if username_len > 0 then
username_rang = cmd_data_rang:range(1, username_len)
end
local password_len = cmd_data_rang:range(1 + username_len, 1):uint()
local password_rang
if password_len > 0 then
password_rang = cmd_data_rang:range(1 + username_len + 1, password_len)
end
local username
if username_rang then
username = username_rang:string()
hello_tree:add(fds.cmd_hello_username, username_rang)
else
username = "(empty)"
end
local password
if password_rang then
password = password_rang:string()
hello_tree:add(fds.cmd_hello_password, password_rang)
else
password = "(empty)"
end
frame_tree:set_text(format("Command HELLO%s: Username: %s, Password: %s",
has_more, username, password))
elseif cmd_name == "HELLO" and mechanism == "CURVE" then
local hello_tree = frame_tree:add(fds.cmd_hello, cmd_data_rang)
local ver_major = cmd_data_rang:range(0, 1)
local ver_minor = cmd_data_rang:range(1, 1)
local version_tree = hello_tree:add(fds.cmd_hello_curvezmq_version,
cmd_data_rang:range(0, 2))
version_tree:add(fds.cmd_hello_curvezmq_version_major, ver_major)
version_tree:add(fds.cmd_hello_curvezmq_version_minor, ver_minor)
if ver_major:uint() == 1 and ver_minor:uint() == 0 then
local padding_rang = cmd_data_rang:range(2, 72)
local padding_tree = hello_tree:add(fds.cmd_hello_curvezmq_padding, padding_rang)
if padding_rang:string() ~= string.rep("\0", 72) then
padding_tree:add_expert_info(PI_PROTOCOL, PI_ERROR,
"Non-zero padding")
end
hello_tree:add(fds.cmd_hello_curvezmq_pubkey,
cmd_data_rang:range(74, 32))
hello_tree:add(fds.cmd_hello_curvezmq_nonce,
cmd_data_rang:range(106, 8))
hello_tree:add(fds.cmd_hello_curvezmq_signature,
cmd_data_rang:range(114, 80))
else
version_tree:add_expert_info(PI_UNDECODED, PI_ERROR,
"Unsupported CurveZMQ version")
end
frame_tree:set_text(format("Command HELLO%s: CurveZMQ %d.%d",
has_more, ver_major:uint(), ver_minor:uint()))
elseif cmd_name == "WELCOME" and mechanism == "CURVE" then
local welcome_tree = frame_tree:add(fds.cmd_welcome, cmd_data_rang)
welcome_tree:add(fds.cmd_welcome_curvezmq_nonce,
cmd_data_rang:range(0, 16))
welcome_tree:add(fds.cmd_welcome_curvezmq_box,
cmd_data_rang:range(16, 144))
frame_tree:set_text(format("Command WELCOME"))
elseif cmd_name == "INITIATE" and mechanism == "PLAIN" then
local initiate_tree = frame_tree:add(fds.cmd_initiate, cmd_data_rang)
frame_tree:set_text(format("Command INITIATE%s: %s",
has_more, parse_metadata(initiate_tree)))
elseif cmd_name == "INITIATE" and mechanism == "CURVE" then
local initiate_tree = frame_tree:add(fds.cmd_initiate, cmd_data_rang)
initiate_tree:add(fds.cmd_initiate_curvezmq_cookie,
cmd_data_rang:range(0, 96))
initiate_tree:add(fds.cmd_initiate_curvezmq_nonce,
cmd_data_rang:range(96, 8))
initiate_tree:add(fds.cmd_initiate_curvezmq_box,
cmd_data_rang:range(104, cmd_data_rang:len() - 104))
frame_tree:set_text(format("Command INITIATE"))
elseif cmd_name == "ERROR" then
local error_tree = frame_tree:add(fds.cmd_error, cmd_data_rang)
local reason_len = cmd_data_rang:range(0, 1):uint()
local reason_rang = cmd_data_rang:range(1, reason_len)
error_tree:add(fds.cmd_error_reason, reason_rang)
frame_tree:set_text(format("Command ERROR%s: %s",
has_more, reason_rang:string()))
elseif cmd_name == "PING" then
local ping_tree = frame_tree:add(fds.cmd_ping, cmd_data_rang)
local ping_ttl = cmd_data_rang:range(0, 2)
local ttl_tree = ping_tree:add(fds.cmd_ping_ttl,
cmd_data_rang:range(0, 2))
if cmd_data_rang:len() - 2 > 0 then
local ping_context = cmd_data_rang:range(2, cmd_data_rang:len() - 2)
local ttl_tree = ping_tree:add(fds.cmd_ping_context,
ping_context)
frame_tree:set_text(format("Command PING%s: TTL: %d Context: %s",
has_more, ping_ttl:uint(), ping_context))
else
frame_tree:set_text(format("Command PING%s: TTL: %d",
has_more, ping_ttl:uint()))
end
elseif cmd_name == "PONG" then
if cmd_data_rang then
local pong_tree = frame_tree:add(fds.cmd_pong, cmd_data_rang)
local pong_context = cmd_data_rang:range(0, cmd_data_rang:len())
local context_tree = pong_tree:add(fds.cmd_pong_context,
pong_context)
frame_tree:set_text(format("Command PONG%s: Context: %s",
has_more, pong_context))
end
else
if cmd_data_rang then
frame_tree:add(fds.cmd_unknown_data, cmd_data_rang)
frame_tree:set_text(format("Command %s%s: %s",
cmd_name, has_more, tostring(cmd_data_rang)))
else
frame_tree:set_text(format("Command %s%s",
cmd_name, has_more))
end
end
return format("Command (%s)", cmd_name)
else
tap.messages = tap.messages + 1
if mechanism == "CURVE" and body_len >= 33 then
local cmd_name_len = body_rang:range(0, 1):uint()
if cmd_name_len == 7 then
local cmd_name_rang = body_rang:range(1, cmd_name_len)
local cmd_name = cmd_name_rang:string()
frame_tree:add(fds.command_name, cmd_name_rang)
local cmd_data_rang
if body_rang:len() > 1 + cmd_name_len then
cmd_data_rang = body_rang:range(1 + cmd_name_len)
end
if cmd_name == "MESSAGE" then
local message_tree = frame_tree:add(fds.message, cmd_data_rang)
message_tree:add(fds.message_curvezmq_nonce,
cmd_data_rang:range(0, 8))
message_tree:add(fds.message_curvezmq_box,
cmd_data_rang:range(8, cmd_data_rang:len() - 8))
frame_tree:set_text(format("MESSAGE"))
end
end
elseif body_len > 0 then
-- get right subdissectors if existing from src or dst port
local port = pinfo.src_port
local cur_dissector = subdissectors:get_dissector(port)
if (cur_dissector == nil) then
port = pinfo.dst_port
cur_dissector = subdissectors:get_dissector(port)
end
frame_tree:set_text(format("Data%s, Length: %u", has_more,
body_len, tostring(body_rang)))
if (cur_dissector) then
--frame_tree:add(fds.protocol, cur_dissector:__tostring()):set_generated()
subdissectors:try(port, body_rang:tvb(), pinfo, toplevel_tree)
return nil
else
return "Data"
end
else
frame_tree:set_text(format("Empty%s", has_more))
return "Empty"
end
end
end
local DESEGMENT_ONE_MORE_SEGMENT = 0x0fffffff
local DESEGMENT_UNTIL_FIN = 0x0ffffffe
-- packet dissector
function zmtp_proto.dissector(tvb, pinfo, tree)
local offset = 0
local rang
local zmq_frames
local tap = {}
local desc = {}
tap.mechanism = ""
tap.frames = 0
tap.commands = 0
tap.messages = 0
tap.body_bytes = 0
pinfo.cols.info:clear()
subdissector_found = false
local function ensure_length(len)
if offset + len > tvb:len() then
pinfo.desegment_offset = offset
pinfo.desegment_len = DESEGMENT_ONE_MORE_SEGMENT
return false
else
return true
end
end
-- print(format("zmtp_proto.dissector: offset:%d len:%d reported_len:%d", offset, tvb:len(), tvb:reported_len()), tvb(offset, 5))
local stream_id = tcp_stream_id().value
while offset < tvb:len() do
if not ensure_length(1) then break end
-- decode flags
rang = tvb(offset, 1)
local flags = rang:uint()
if flags == 0xff then
-- greeting
if not ensure_length(12) then break end
local version_major_rang = tvb(offset + 10, 1)
local version_major = version_major_rang:uint()
if version_major >= 2 then
if not ensure_length(64) then break end
pdu_len = 64
else
pdu_len = 12
end
elseif (bit.band(flags, 0x02) ~= 0) then
-- long frame
if not ensure_length(9) then break end
rang = tvb(offset + 1, 8)
-- not before wireshark 1.11; http://wiki.wireshark.org/LuaAPI/Int64
-- frame_len = rang:uint64():tonumber()
frame_len = tonumber(tostring(rang:uint64()))
pdu_len = frame_len + 9
else
-- short frame
if not ensure_length(2) then break end
rang = tvb(offset + 1, 1)
frame_len = rang:uint()
pdu_len = frame_len + 2
end
-- provide hints to tcp
if not pinfo.visited then
local remaining_bytes = tvb:len() - offset
if pdu_len > remaining_bytes then
pinfo.want_pdu_tracking = 2
pinfo.bytes_until_next_pdu = pdu_len - remaining_bytes
end
end
local truncated = false
-- check if we need more bytes to dissect this frame.
if offset + pdu_len > tvb:len() then
if tvb:len() == tvb:reported_len() then
pinfo.desegment_offset = offset
pinfo.desegment_len = offset + pdu_len - tvb:len()
-- print(format("zmtp_proto.dissector: desegment offset:%d len:%d", pinfo.desegment_offset, pinfo.desegment_len))
break
else
-- already tried to dissect, but the desegmenter failed
pdu_len = tvb:len() - offset
truncated = true
end
end
if not zmq_frames then
zmq_frames = tree:add(zmtp_proto, tvb())
end
if (bit.band(flags, 0xf8) ~= 0) and flags ~= 0xff then
zmq_frames:add_expert_info(PI_REASSEMBLE, PI_ERROR, "Framing error")
return
end
-- dissect zmq frame
rang = tvb(offset, pdu_len)
local frame_tree = zmq_frames:add(fds.frame, rang)
if truncated then
frame_tree:add_expert_info(PI_REASSEMBLE, PI_ERROR, "Message truncated")
end
local frame_desc = zmq_dissect_frame(rang:tvb(), pinfo, frame_tree, tap, tree, stream_id)
-- zmq_dissect_frame returns nil when it passed the payload to a subdissector,
-- and a string describing the packet otherwise.
if frame_desc then
table.insert(desc, frame_desc)
else
subdissector_found = true
end
tap.frames = tap.frames + 1
-- step to next frame.
local offset_before = offset
offset = offset + pdu_len
if offset < offset_before then break end
end
if zmq_frames then
zmq_frames:set_text(format("ZeroMQ Message Transport Protocol, Frames: %u", tap.frames))
end
if tap.messages > 0 then
table.insert(desc, format("Data Frames: %u", tap.messages))
end
-- Info column (only if subdissector did not fill it)
if not subdissector_found then
pinfo.cols.protocol = "ZMTP"
pinfo.cols.info:prepend(table.concat(desc, "; "))
--pinfo.tap_data = tap
end
return
end
-- Register ZMTP for "decode as"
function zmtp_proto.init(arg1, arg2)
DissectorTable.get("tcp.port"):add_for_decode_as(zmtp_proto)
end