Coverage Report

Created: 2026-11-12 07:01

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/wireshark/epan/dissectors/packet-rtmpt.c
Line
Count
Source
1
/* packet-rtmpt.c
2
 * Routines for Real Time Messaging Protocol packet dissection
3
 * metatech <metatech@flashmail.com>
4
 *
5
 * Wireshark - Network traffic analyzer
6
 * By Gerald Combs <gerald@wireshark.org>
7
 * Copyright 1998 Gerald Combs
8
 *
9
 * SPDX-License-Identifier: GPL-2.0-or-later
10
 */
11
12
/*  This dissector is called RTMPT to avoid a conflict with
13
*   the other RTMP protocol (Routing Table Maintenance Protocol) implemented in packet-atalk.c
14
*   (RTMPT normally stands for RTMP-Tunnel via http)
15
*
16
*   RTMP in a nutshell
17
*
18
*   The protocol has very few "magic words" to facilitate detection,
19
*   but rather has "magic lengths".
20
*   This protocol has plenty of special cases and few general rules,
21
*   especially regarding the lengths and the structures.
22
*
23
*   Documentation:
24
*      Adobe's RTMP, AMF and FLV specifications are collected by the
25
*      Enhanced RTMP project at
26
*
27
*          https://veovera.org/docs/legacy/
28
*
29
*      The RTMP specification is
30
*
31
*          https://veovera.org/docs/legacy/rtmp-v1-0-spec.pdf
32
*
33
*      It was formerly available from Adobe at
34
*
35
*          https://web.archive.org/web/20210926083727/https://www.adobe.com/devnet/rtmp.html
36
*
37
*      RTMP protocol description on Wiki of Red5 Open Source Flash Server
38
*      (archived) at
39
*
40
*          https://web.archive.org/web/20120530065236/http://trac.red5.org/wiki/Codecs/RTMPSpecification
41
*
42
*      and the pages to which it links (archived):
43
*
44
*          https://web.archive.org/web/20070315085249/http://osflash.org/documentation/rtmp
45
*          https://web.archive.org/web/20150905175559/http://wiki.gnashdev.org/RTMP
46
*          https://web.archive.org/web/20150907021630/http://wiki.gnashdev.org/RTMP_Messages_Decoded
47
*          https://web.archive.org/web/20060613154925/http://www.acmewebworks.com/Downloads/openCS/TheAMF.pdf
48
*
49
*   For AMF, see:
50
*
51
*          http://download.macromedia.com/pub/labs/amf/amf0_spec_121207.pdf
52
*
53
*      (also at https://veovera.org/docs/legacy/amf0-file-format-spec.pdf)
54
*
55
*   for AMF0 and
56
*
57
*          https://veovera.org/docs/legacy/amf3-file-format-spec.pdf
58
*
59
*   for AMF3.
60
*
61
*   For FLV, see:
62
*
63
*          https://veovera.org/docs/legacy/video-file-format-v10-1-spec.pdf
64
*
65
*   For Enhanced RTMP, see:
66
*
67
*          https://veovera.org/docs/enhanced/enhanced-rtmp-v2.pdf
68
*
69
*   Default TCP port is 1935
70
*/
71
72
#include "config.h"
73
0
#define WS_LOG_DOMAIN "RTMPT"
74
75
#include <epan/packet.h>
76
#include <wsutil/pint.h>
77
78
#include <epan/prefs.h>
79
#include <epan/to_str.h>
80
#include <epan/expert.h>
81
#include "packet-tcp.h"
82
83
5
#define MAX_AMF_ITERATIONS 1000
84
85
void proto_register_rtmpt(void);
86
void proto_reg_handoff_rtmpt(void);
87
88
void proto_register_amf(void);
89
90
static int proto_rtmpt;
91
92
static int hf_rtmpt_handshake_c0;
93
static int hf_rtmpt_handshake_s0;
94
static int hf_rtmpt_handshake_c1;
95
static int hf_rtmpt_handshake_s1;
96
static int hf_rtmpt_handshake_c2;
97
static int hf_rtmpt_handshake_s2;
98
99
static int hf_rtmpt_header_format;
100
static int hf_rtmpt_header_csid;
101
static int hf_rtmpt_header_timestamp;
102
static int hf_rtmpt_header_timestamp_delta;
103
static int hf_rtmpt_header_body_size;
104
static int hf_rtmpt_header_typeid;
105
static int hf_rtmpt_header_streamid;
106
static int hf_rtmpt_header_ets;
107
108
static int hf_rtmpt_scm_chunksize;
109
static int hf_rtmpt_scm_csid;
110
static int hf_rtmpt_scm_seq;
111
static int hf_rtmpt_scm_was;
112
static int hf_rtmpt_scm_limittype;
113
114
static int hf_rtmpt_ucm_eventtype;
115
116
static int hf_rtmpt_function_call;
117
static int hf_rtmpt_function_response;
118
119
static int hf_rtmpt_audio_control;
120
static int hf_rtmpt_audio_multitrack_control;
121
static int hf_rtmpt_audio_is_ex_header;
122
static int hf_rtmpt_audio_format;
123
static int hf_rtmpt_audio_rate;
124
static int hf_rtmpt_audio_size;
125
static int hf_rtmpt_audio_type;
126
static int hf_rtmpt_audio_packet_type;
127
static int hf_rtmpt_audio_multitrack_type;
128
static int hf_rtmpt_audio_multitrack_packet_type;
129
static int hf_rtmpt_audio_fourcc;
130
static int hf_rtmpt_audio_track_id;
131
static int hf_rtmpt_audio_track_length;
132
static int hf_rtmpt_audio_data;
133
134
static int hf_rtmpt_video_control;
135
static int hf_rtmpt_video_multitrack_control;
136
static int hf_rtmpt_video_is_ex_header;
137
static int hf_rtmpt_video_type;
138
static int hf_rtmpt_video_command;
139
static int hf_rtmpt_video_format;
140
static int hf_rtmpt_video_packet_type;
141
static int hf_rtmpt_video_multitrack_type;
142
static int hf_rtmpt_video_multitrack_packet_type;
143
static int hf_rtmpt_video_fourcc;
144
static int hf_rtmpt_video_track_id;
145
static int hf_rtmpt_video_track_length;
146
static int hf_rtmpt_video_data;
147
148
static int hf_rtmpt_tag_type;
149
static int hf_rtmpt_tag_datasize;
150
static int hf_rtmpt_tag_timestamp;
151
static int hf_rtmpt_tag_ets;
152
static int hf_rtmpt_tag_streamid;
153
static int hf_rtmpt_tag_tagsize;
154
155
static expert_field ei_amf_loop;
156
157
static int ett_rtmpt;
158
static int ett_rtmpt_handshake;
159
static int ett_rtmpt_header;
160
static int ett_rtmpt_body;
161
static int ett_rtmpt_ucm;
162
static int ett_rtmpt_audio_control;
163
static int ett_rtmpt_video_control;
164
static int ett_rtmpt_audio_multitrack_control;
165
static int ett_rtmpt_audio_multitrack_track;
166
static int ett_rtmpt_video_multitrack_control;
167
static int ett_rtmpt_video_multitrack_track;
168
static int ett_rtmpt_tag;
169
static int ett_rtmpt_tag_data;
170
171
static dissector_handle_t amf_handle;
172
static dissector_handle_t rtmpt_tcp_handle;
173
static dissector_handle_t rtmpt_http_handle;
174
175
static bool rtmpt_desegment = true;
176
177
16
#define RTMP_PORT                     1935 /* Not IANA registered */
178
179
9.79k
#define RTMPT_MAGIC                   0x03
180
24
#define RTMPT_HANDSHAKE_OFFSET_1         1
181
4.89k
#define RTMPT_HANDSHAKE_OFFSET_2      1538
182
#define RTMPT_HANDSHAKE_OFFSET_3      3074
183
#define RTMPT_HANDSHAKE_LENGTH_1      1537
184
#define RTMPT_HANDSHAKE_LENGTH_2      3073
185
#define RTMPT_HANDSHAKE_LENGTH_3      1536
186
0
#define RTMPT_INITIAL_CHUNK_SIZE       128
187
188
static unsigned rtmpt_default_chunk_size = 128;
189
190
28.5k
#define RTMPT_ID_MAX                     65599
191
0
#define RTMPT_TYPE_HANDSHAKE_1        0x100001
192
0
#define RTMPT_TYPE_HANDSHAKE_2        0x100002
193
0
#define RTMPT_TYPE_HANDSHAKE_3        0x100003
194
195
11.8k
#define RTMPT_TYPE_CHUNK_SIZE         0x01
196
5.71k
#define RTMPT_TYPE_ABORT_MESSAGE      0x02
197
5.67k
#define RTMPT_TYPE_ACKNOWLEDGEMENT    0x03
198
2.65k
#define RTMPT_TYPE_UCM                0x04
199
2.90k
#define RTMPT_TYPE_WINDOW             0x05
200
2.79k
#define RTMPT_TYPE_PEER_BANDWIDTH     0x06
201
71
#define RTMPT_TYPE_AUDIO_DATA         0x08
202
33
#define RTMPT_TYPE_VIDEO_DATA         0x09
203
5.43k
#define RTMPT_TYPE_DATA_AMF3          0x0F
204
#define RTMPT_TYPE_SHARED_AMF3        0x10
205
10.8k
#define RTMPT_TYPE_COMMAND_AMF3       0x11
206
10.8k
#define RTMPT_TYPE_DATA_AMF0          0x12
207
#define RTMPT_TYPE_SHARED_AMF0        0x13
208
10.8k
#define RTMPT_TYPE_COMMAND_AMF0       0x14
209
0
#define RTMPT_TYPE_AGGREGATE          0x16
210
211
12
#define RTMPT_UCM_STREAM_BEGIN        0x00
212
10
#define RTMPT_UCM_STREAM_EOF          0x01
213
10
#define RTMPT_UCM_STREAM_DRY          0x02
214
4
#define RTMPT_UCM_SET_BUFFER          0x03
215
4
#define RTMPT_UCM_STREAM_ISRECORDED   0x04
216
#define RTMPT_UCM_PING_REQUEST        0x06
217
#define RTMPT_UCM_PING_RESPONSE       0x07
218
219
142
#define RTMPT_IS_EX_AUDIO_HEADER      0x90
220
33
#define RTMPT_IS_EX_VIDEO_HEADER      0x80
221
50
#define RTMPT_IS_PACKET_TYPE_METADATA 0x04
222
33
#define RTMPT_IS_FRAME_TYPE_COMMAND   0x05
223
58
#define RTMPT_IS_AUDIO_MULTITRACK     0x05
224
25
#define RTMPT_IS_VIDEO_MULTITRACK     0x06
225
0
#define RTMPT_IS_ONETRACK             0x00
226
0
#define RTMPT_IS_MANYTRACKSMANYCODECS 0x02
227
228
2.92k
#define RTMPT_TEXT_RTMP_HEADER        "RTMP Header"
229
2.02k
#define RTMPT_TEXT_RTMP_BODY          "RTMP Body"
230
231
static const value_string rtmpt_handshake_vals[] = {
232
        { RTMPT_TYPE_HANDSHAKE_1,           "Handshake C0+C1" },
233
        { RTMPT_TYPE_HANDSHAKE_2,           "Handshake S0+S1+S2" },
234
        { RTMPT_TYPE_HANDSHAKE_3,           "Handshake C2" },
235
        { 0, NULL }
236
};
237
238
static const value_string rtmpt_opcode_vals[] = {
239
        { RTMPT_TYPE_CHUNK_SIZE,            "Set Chunk Size" },
240
        { RTMPT_TYPE_ABORT_MESSAGE,         "Abort Message" },
241
        { RTMPT_TYPE_ACKNOWLEDGEMENT,       "Acknowledgement" },
242
        { RTMPT_TYPE_UCM,                   "User Control Message" },
243
        { RTMPT_TYPE_WINDOW,                "Window Acknowledgement Size" },
244
        { RTMPT_TYPE_PEER_BANDWIDTH,        "Set Peer Bandwidth" },
245
        { RTMPT_TYPE_AUDIO_DATA,            "Audio Data" },
246
        { RTMPT_TYPE_VIDEO_DATA,            "Video Data" },
247
        { RTMPT_TYPE_DATA_AMF3,             "AMF3 Data" },
248
        { RTMPT_TYPE_SHARED_AMF3,           "AMF3 Shared Object" },
249
        { RTMPT_TYPE_COMMAND_AMF3,          "AMF3 Command" },
250
        { RTMPT_TYPE_DATA_AMF0,             "AMF0 Data" },
251
        { RTMPT_TYPE_SHARED_AMF0,           "AMF0 Shared Object" },
252
        { RTMPT_TYPE_COMMAND_AMF0,          "AMF0 Command" },
253
        { RTMPT_TYPE_AGGREGATE,             "Aggregate" },
254
        { 0, NULL }
255
};
256
257
static const value_string rtmpt_limit_vals[] = {
258
/* These are a complete guess, from the order of the documented
259
 * options - the values aren't actually specified */
260
        { 0,                                "Hard" },
261
        { 1,                                "Soft" },
262
        { 2,                                "Dynamic" },
263
        { 0, NULL }
264
};
265
266
static const value_string rtmpt_ucm_vals[] = {
267
        { RTMPT_UCM_STREAM_BEGIN,           "Stream Begin" },
268
        { RTMPT_UCM_STREAM_EOF,             "Stream EOF" },
269
        { RTMPT_UCM_STREAM_DRY,             "Stream Dry" },
270
        { RTMPT_UCM_SET_BUFFER,             "Set Buffer Length" },
271
        { RTMPT_UCM_STREAM_ISRECORDED,      "Stream Is Recorded" },
272
        { RTMPT_UCM_PING_REQUEST,           "Ping Request" },
273
        { RTMPT_UCM_PING_RESPONSE,          "Ping Response" },
274
        { 0, NULL }
275
};
276
277
static const value_string rtmpt_tag_vals[] = {
278
        { RTMPT_TYPE_AUDIO_DATA,            "Audio Tag" },
279
        { RTMPT_TYPE_VIDEO_DATA,            "Video Tag" },
280
        { RTMPT_TYPE_DATA_AMF0,             "Script Tag" },
281
        { 0, NULL }
282
};
283
284
/* [Spec] https://veovera.org/docs/legacy/rtmp-v1-0-spec.pdf */
285
/* [DevG] https://web.archive.org/web/20150226180406/http://help.adobe.com/en_US/flashmediaserver/devguide/index.html "working with Live Video" => Adding metadata to a live stream */
286
/* [SWF] https://github.com/blackears/raven/blob/master/proj/SWFParser/doc/swf_file_format_spec_v10.pdf */
287
static const value_string rtmpt_audio_codecs[] = {
288
        {  0,                               "Uncompressed" },             /* [DevG] */
289
        {  1,                               "ADPCM" },                    /* [DevG] */
290
        {  2,                               "MP3" },                      /* [DevG] */
291
        {  3,                               "Uncompressed, little-endian"}, /* [SWF] */
292
        {  4,                               "Nellymoser 16kHz" },          /* [SWF] */
293
        {  5,                               "Nellymoser 8kHz" },          /* [DevG] [SWF]*/
294
        {  6,                               "Nellymoser" },               /* [DevG] [SWF]*/
295
        {  7,                               "G711A" },                    /* [Spec] */
296
        {  8,                               "G711U" },                    /* [Spec] */
297
        {  9,                               "Nellymoser 16kHz" },         /* [Spec] */
298
        { 10,                               "HE-AAC" },                   /* [DevG] */
299
        { 11,                               "SPEEX" },                    /* [DevG] */
300
        { 0, NULL }
301
};
302
303
static const value_string rtmpt_audio_packet_types[] = {
304
        { 0,                                "PacketTypeSequenceStart" },
305
        { 1,                                "PacketTypeCodedFrames" },
306
        { 4,                                "PacketTypeMultichannelConfig" },
307
        { 5,                                "PacketTypeMultitrack" },
308
        { 0, NULL }
309
};
310
311
static const value_string rtmpt_av_multitrack_types[] = {
312
        { 0,                                "AvMultitrackTypeOneTrack" },
313
        { 1,                                "AvMultitrackTypeManyTracks" },
314
        { 2,                                "AvMultitrackTypeManyTracksManyCodecs" },
315
        { 0, NULL }
316
};
317
318
static const value_string rtmpt_audio_rates[] = {
319
        { 0,                                "5.5 kHz" },
320
        { 1,                                "11 kHz" },
321
        { 2,                                "22 kHz" },
322
        { 3,                                "44 kHz" },
323
        { 0, NULL }
324
};
325
326
static const value_string rtmpt_audio_sizes[] = {
327
        { 0,                                "8 bit" },
328
        { 1,                                "16 bit" },
329
        { 0, NULL }
330
};
331
332
static const value_string rtmpt_audio_types[] = {
333
        { 0,                                "mono" },
334
        { 1,                                "stereo" },
335
        { 0, NULL }
336
};
337
338
/* from FLV v10.1 section E.4.3.1 */
339
static const value_string rtmpt_video_types[] = {
340
        { 1,                                "keyframe" },
341
        { 2,                                "inter-frame" },
342
        { 3,                                "disposable inter-frame" },
343
        { 4,                                "generated key frame" },
344
        { 5,                                "video info/command frame" },
345
        { 0, NULL }
346
};
347
348
/* From Enhanced RTMP */
349
static const value_string rtmpt_video_commands[] = {
350
        { 0,                                "StartSeek" },
351
        { 1,                                "EndSeek" },
352
        { 0, NULL }
353
};
354
355
/* from FLV v10.1 section E.4.3.1 */
356
static const value_string rtmpt_video_codecs[] = {
357
        {  2,                               "Sorensen H.263" },
358
        {  3,                               "Screen video" },
359
        {  4,                               "On2 VP6" },
360
        {  5,                               "On2 VP6+alpha" },
361
        {  6,                               "Screen video version 2" },
362
        {  7,                               "H.264" },
363
        { 12,                               "H.265" },
364
        { 0, NULL }
365
};
366
367
/*
368
 * https://veovera.org/docs/enhanced/enhanced-rtmp-v2.pdf
369
 */
370
static const value_string rtmpt_video_packet_types[] = {
371
        { 0,                                "PacketTypeSequenceStart" },
372
        { 1,                                "PacketTypeCodedFrames" },
373
        { 2,                                "PacketTypeSequenceEnd" },
374
        { 3,                                "PacketTypeCodedFramesX" },
375
        { 4,                                "PacketTypeMetadata" },
376
        { 5,                                "PacketTypeMPEG2TSSequenceStart" },
377
        { 6,                                "PacketTypeMultitrack" },
378
        { 0, NULL }
379
};
380
381
static int proto_amf;
382
383
static int hf_amf_version;
384
static int hf_amf_header_count;
385
static int hf_amf_header_name;
386
static int hf_amf_header_must_understand;
387
static int hf_amf_header_length;
388
/* static int hf_amf_header_value_type; */
389
static int hf_amf_message_count;
390
static int hf_amf_message_target_uri;
391
static int hf_amf_message_response_uri;
392
static int hf_amf_message_length;
393
394
static int hf_amf_amf0_type;
395
static int hf_amf_amf3_type;
396
static int hf_amf_number;
397
static int hf_amf_integer;
398
static int hf_amf_boolean;
399
static int hf_amf_stringlength;
400
static int hf_amf_string;
401
static int hf_amf_string_reference;
402
static int hf_amf_object_reference;
403
static int hf_amf_date;
404
/* static int hf_amf_longstringlength; */
405
static int hf_amf_longstring;
406
static int hf_amf_xml_doc;
407
static int hf_amf_xmllength;
408
static int hf_amf_xml;
409
static int hf_amf_int64;
410
static int hf_amf_bytearraylength;
411
static int hf_amf_bytearray;
412
413
static int hf_amf_object;
414
static int hf_amf_traitcount;
415
static int hf_amf_classnamelength;
416
static int hf_amf_classname;
417
static int hf_amf_membernamelength;
418
static int hf_amf_membername;
419
static int hf_amf_trait_reference;
420
static int hf_amf_ecmaarray;
421
static int hf_amf_strictarray;
422
static int hf_amf_array;
423
static int hf_amf_arraylength;
424
static int hf_amf_arraydenselength;
425
426
static int hf_amf_end_of_object_marker;
427
static int hf_amf_end_of_associative_part;
428
static int hf_amf_end_of_dynamic_members;
429
430
static int ett_amf;
431
static int ett_amf_headers;
432
static int ett_amf_messages;
433
static int ett_amf_value;
434
static int ett_amf_property;
435
static int ett_amf_string;
436
static int ett_amf_array_element;
437
static int ett_amf_traits;
438
static int ett_amf_trait_member;
439
440
/* AMF0 type markers */
441
14
#define AMF0_NUMBER              0x00
442
0
#define AMF0_BOOLEAN             0x01
443
1
#define AMF0_STRING              0x02
444
0
#define AMF0_OBJECT              0x03
445
#define AMF0_MOVIECLIP           0x04
446
0
#define AMF0_NULL                0x05
447
0
#define AMF0_UNDEFINED           0x06
448
0
#define AMF0_REFERENCE           0x07
449
0
#define AMF0_ECMA_ARRAY          0x08
450
0
#define AMF0_END_OF_OBJECT       0x09
451
0
#define AMF0_STRICT_ARRAY        0x0A
452
0
#define AMF0_DATE                0x0B
453
0
#define AMF0_LONG_STRING         0x0C
454
0
#define AMF0_UNSUPPORTED         0x0D
455
#define AMF0_RECORDSET           0x0E
456
0
#define AMF0_XML                 0x0F
457
0
#define AMF0_TYPED_OBJECT        0x10
458
0
#define AMF0_AMF3_MARKER         0x11
459
0
#define AMF0_INT64               0x22
460
461
/* AMF3 type markers */
462
0
#define AMF3_UNDEFINED           0x00
463
0
#define AMF3_NULL                0x01
464
0
#define AMF3_FALSE               0x02
465
0
#define AMF3_TRUE                0x03
466
0
#define AMF3_INTEGER             0x04
467
0
#define AMF3_DOUBLE              0x05
468
0
#define AMF3_STRING              0x06
469
#define AMF3_XML_DOC             0x07
470
0
#define AMF3_DATE                0x08
471
0
#define AMF3_ARRAY               0x09
472
0
#define AMF3_OBJECT              0x0A
473
0
#define AMF3_XML                 0x0B
474
0
#define AMF3_BYTEARRAY           0x0C
475
476
static const value_string amf0_type_vals[] = {
477
        { AMF0_NUMBER,                "Number" },
478
        { AMF0_BOOLEAN,               "Boolean" },
479
        { AMF0_STRING,                "String" },
480
        { AMF0_OBJECT,                "Object" },
481
        { AMF0_MOVIECLIP,             "Movie clip" },
482
        { AMF0_NULL,                  "Null" },
483
        { AMF0_UNDEFINED,             "Undefined" },
484
        { AMF0_REFERENCE,             "Reference" },
485
        { AMF0_ECMA_ARRAY,            "ECMA array" },
486
        { AMF0_END_OF_OBJECT,         "End of object" },
487
        { AMF0_STRICT_ARRAY,          "Strict array" },
488
        { AMF0_DATE,                  "Date" },
489
        { AMF0_LONG_STRING,           "Long string" },
490
        { AMF0_UNSUPPORTED,           "Unsupported" },
491
        { AMF0_RECORDSET,             "Record set" },
492
        { AMF0_XML,                   "XML" },
493
        { AMF0_TYPED_OBJECT,          "Typed object" },
494
        { AMF0_AMF3_MARKER,           "Switch to AMF3" },
495
        { AMF0_INT64,                 "Int64" },
496
        { 0, NULL }
497
};
498
499
static const value_string amf3_type_vals[] = {
500
        { AMF3_UNDEFINED,             "Undefined" },
501
        { AMF3_NULL,                  "Null" },
502
        { AMF3_FALSE,                 "False" },
503
        { AMF3_TRUE,                  "True" },
504
        { AMF3_INTEGER,               "Integer" },
505
        { AMF3_DOUBLE,                "Double" },
506
        { AMF3_STRING,                "String" },
507
        { AMF3_XML_DOC,               "XML document" },
508
        { AMF3_DATE,                  "Date" },
509
        { AMF3_ARRAY,                 "Array" },
510
        { AMF3_OBJECT,                "Object" },
511
        { AMF3_XML,                   "XML" },
512
        { AMF3_BYTEARRAY,             "ByteArray" },
513
        { 0, NULL }
514
};
515
516
/* Holds the reassembled data for a packet during un-chunking
517
 */
518
/* XXX: Because we don't use the TCP dissector's built-in desegmentation,
519
 * or the standard reassembly API, we don't get FT_FRAMENUM links for
520
 * chunk fragments, and we don't mark depended upon frames for export.
521
 * Ideally we'd use the standard API (mark pinfo->desegment_offset and
522
 * pinfo->desegment_len for TCP, or use the reassembly API), or call
523
 * mark_frame_as_depended_upon and add the FT_FRAMENUM fields ourselves.
524
 * To do the latter, we should have a data structure indicating which
525
 * frames contributed to the packet.
526
 */
527
typedef struct rtmpt_packet {
528
        uint32_t         seq;
529
        uint32_t         lastseq;
530
531
        int              resident;
532
        union {
533
                uint8_t *p;
534
                uint32_t offset;
535
        } data;
536
537
        wmem_list_t *frames;
538
539
        /* used during unchunking */
540
        int              alloc;
541
        int              want;
542
        int              have;
543
        int              chunkwant;
544
        int              chunkhave;
545
546
        uint8_t          bhlen;
547
        uint8_t          mhlen;
548
549
        /* Chunk Basic Header */
550
        uint8_t          fmt;   /* byte 0 */
551
        uint32_t         id;    /* byte 0 */
552
553
        /* Chunk Message Header (offsets assume bhlen == 1) */
554
        uint32_t         ts;    /* bytes 1-3, or from ETS @ mhlen-4 if -1 */
555
        uint32_t         len;   /* bytes 4-6 */
556
        uint8_t          cmd;   /* byte 7 */
557
        uint32_t         src;   /* bytes 8-11 */
558
559
        uint32_t         txid;
560
        int              isresponse;
561
        int              otherframe;
562
563
} rtmpt_packet_t;
564
565
/* Represents a header or a chunk that is split over two TCP
566
 * segments
567
 */
568
typedef struct rtmpt_frag {
569
        int     ishdr;
570
        uint32_t seq;
571
        uint32_t lastseq;
572
        int     have;
573
        int     len;
574
575
        union {
576
                uint8_t d[18]; /* enough for a complete header (3 + 11 + 4) */
577
                uint32_t id;
578
        } saved;
579
} rtmpt_frag_t;
580
581
/* The full message header information for the last packet on a particular
582
 * ID - used for defaulting short headers
583
 */
584
typedef struct rtmpt_id {
585
        uint32_t ts;   /* bytes 1-3 */
586
        uint32_t tsd;
587
        uint32_t len;  /* bytes 4-6 */
588
        uint32_t src;  /* bytes 8-11 */
589
        uint8_t cmd;  /* byte 7 */
590
591
        wmem_tree_t *packets;
592
} rtmpt_id_t;
593
594
/* Historical view of a whole TCP connection
595
 */
596
typedef struct rtmpt_conv {
597
        wmem_tree_t *seqs[2];
598
        wmem_tree_t *frags[2];
599
        wmem_tree_t *ids[2];
600
        wmem_tree_t *packets[2];
601
        wmem_tree_t *chunksize[2];
602
        wmem_tree_t *txids[2];
603
} rtmpt_conv_t;
604
605
static void
606
rtmpt_packet_mark_depended(void *data, void *user_data)
607
35
{
608
35
    frame_data *fd = (frame_data *)user_data;
609
35
    uint32_t frame_num = GPOINTER_TO_UINT(data);
610
35
    mark_frame_as_depended_upon(fd, frame_num);
611
35
}
612
613
/* Header length helpers */
614
615
static int rtmpt_basic_header_length(int id)
616
4.89k
{
617
4.89k
        switch (id & 0x3f) {
618
768
        case 0: return 2;
619
85
        case 1: return 3;
620
4.04k
        default: return 1;
621
4.89k
        }
622
4.89k
}
623
624
static int rtmpt_message_header_length(int id)
625
4.89k
{
626
4.89k
        switch ((id>>6) & 3) {
627
1.29k
        case 0: return 11;
628
295
        case 1: return 7;
629
333
        case 2: return 3;
630
2.98k
        default: return 0;
631
4.89k
        }
632
4.89k
}
633
634
static uint32_t rtmpt_chunk_stream_id(const uint8_t *basic_header)
635
2.98k
{
636
2.98k
        uint32_t id = basic_header[0] & 0x3f;
637
638
2.98k
        if (id == 0)
639
14
                return basic_header[1] + 64;
640
2.96k
        if (id == 1)
641
10
                return pletohu16(basic_header+1) + 64;
642
2.95k
        return id;
643
2.96k
}
644
645
/* A type 3 header that continues a message whose header had an extended
646
 * timestamp repeats it (#15718). Returns the length of that timestamp for
647
 * the type 3 header starting at seq, so the whole header can be reassembled
648
 * when it is split across TCP segments.
649
 */
650
static int rtmpt_type3_ext_ts_length(rtmpt_conv_t *rconv, int cdir, const uint8_t *basic_header, uint32_t seq)
651
2.98k
{
652
2.98k
        uint32_t        id = rtmpt_chunk_stream_id(basic_header);
653
2.98k
        rtmpt_id_t     *ti;
654
2.98k
        rtmpt_packet_t *tp;
655
656
2.98k
        if (id > RTMPT_ID_MAX)
657
0
                return 0;
658
2.98k
        ti = (rtmpt_id_t *)wmem_tree_lookup32(rconv->ids[cdir], id);
659
2.98k
        if (!ti)
660
115
                return 0;
661
2.86k
        tp = (rtmpt_packet_t *)wmem_tree_lookup32_le(ti->packets, seq-1);
662
2.86k
        if (tp && tp->resident && tp->have < tp->want && tp->chunkhave == tp->chunkwant
663
632
            && tp->have > tp->bhlen + 3 && pntohu24(tp->data.p+tp->bhlen) == 0xffffff)
664
156
                return 4;
665
2.70k
        return 0;
666
2.86k
}
667
668
/* Lightweight access to AMF0 blobs - more complete dissection is done
669
 * in dissect_rtmpt_body_command */
670
671
static uint32_t
672
rtmpt_get_amf_length(tvbuff_t *tvb, int offset, proto_item* pi)
673
5
{
674
5
        uint8_t iObjType;
675
5
        int     remain  = tvb_reported_length_remaining(tvb, offset);
676
5
        uint32_t depth   = 0;
677
5
        uint32_t itemlen = 0;
678
5
        uint32_t rv      = 0;
679
5
        unsigned   iterations = MAX_AMF_ITERATIONS;
680
681
8
        while (rv == 0 || depth > 0) {
682
683
5
                if (--iterations == 0) {
684
0
                        expert_add_info(NULL, pi, &ei_amf_loop);
685
0
                        return 0;
686
0
                }
687
688
5
                if (depth > 0) {
689
0
                        if (remain-rv < 2)
690
0
                                return remain;
691
0
                        itemlen = tvb_get_ntohs(tvb, offset+rv) + 2;
692
0
                        if (remain-rv<itemlen+1)
693
0
                                return remain;
694
0
                        rv += itemlen;
695
0
                }
696
697
5
                if (remain-rv < 1)
698
0
                        return remain;
699
5
                iObjType = tvb_get_uint8(tvb, offset+rv);
700
701
5
                if (depth > 0 && itemlen == 2 && iObjType == AMF0_END_OF_OBJECT) {
702
0
                        rv++;
703
0
                        depth--;
704
0
                        continue;
705
0
                }
706
707
5
                switch (iObjType) {
708
3
                case AMF0_NUMBER:
709
3
                        itemlen = 9;
710
3
                        break;
711
0
                case AMF0_BOOLEAN:
712
0
                        itemlen = 2;
713
0
                        break;
714
1
                case AMF0_STRING:
715
1
                        if (remain-rv < 3)
716
0
                                return remain;
717
1
                        itemlen = tvb_get_ntohs(tvb, offset+rv+1) + 3;
718
1
                        break;
719
0
                case AMF0_NULL:
720
0
                case AMF0_UNDEFINED:
721
0
                case AMF0_UNSUPPORTED:
722
0
                        itemlen = 1;
723
0
                        break;
724
0
                case AMF0_DATE:
725
0
                        itemlen = 11;
726
0
                        break;
727
0
                case AMF0_LONG_STRING:
728
0
                case AMF0_XML:
729
0
                        if (remain-rv < 5)
730
0
                                return remain;
731
0
                        itemlen = tvb_get_ntohl(tvb, offset+rv+1) + 5;
732
0
                        break;
733
0
                case AMF0_INT64:
734
0
                        itemlen = 9;
735
0
                        break;
736
0
                case AMF0_OBJECT:
737
0
                        itemlen = 1;
738
0
                        depth++;
739
0
                        break;
740
0
                case AMF0_ECMA_ARRAY:
741
0
                        itemlen = 5;
742
0
                        depth++;
743
0
                        break;
744
1
                default:
745
1
                        return remain;
746
5
                }
747
748
4
                if (remain-rv < itemlen)
749
1
                        return remain;
750
3
                rv += itemlen;
751
752
3
        }
753
754
3
        return rv;
755
5
}
756
757
static char *
758
rtmpt_get_amf_param(wmem_allocator_t* allocator, tvbuff_t *tvb, int offset, proto_item* pi, int param, const char *prop)
759
0
{
760
0
        uint32_t remain = tvb_reported_length_remaining(tvb, offset);
761
0
        uint32_t itemlen;
762
0
        uint32_t iStringLength;
763
764
0
        while (remain > 0 && param > 0) {
765
0
                itemlen = rtmpt_get_amf_length(tvb, offset, pi);
766
0
                if (itemlen == 0)
767
0
                        break;
768
0
                offset += itemlen;
769
0
                remain -= itemlen;
770
0
                param--;
771
0
        }
772
773
0
        if (remain > 0 && param == 0) {
774
0
                uint8_t iObjType = tvb_get_uint8(tvb, offset);
775
776
0
                if (!prop && iObjType == AMF0_STRING && remain >= 3) {
777
0
                        iStringLength = tvb_get_ntohs(tvb, offset+1);
778
0
                        if (remain >= iStringLength+3) {
779
0
                                return (char*)tvb_get_string_enc(allocator, tvb, offset+3, iStringLength, ENC_ASCII);
780
0
                        }
781
0
                }
782
783
0
                if (prop && iObjType == AMF0_OBJECT) {
784
0
                        offset++;
785
0
                        remain--;
786
787
0
                        while (remain > 2) {
788
0
                                uint32_t iPropLength = tvb_get_ntohs(tvb, offset);
789
0
                                if (remain < 2+iPropLength+3)
790
0
                                        break;
791
792
0
                                if (tvb_strneql(tvb, offset+2, prop, strlen(prop)) == 0) {
793
0
                                        if (tvb_get_uint8(tvb, offset+2+iPropLength) != AMF0_STRING)
794
0
                                                break;
795
796
0
                                        iStringLength = tvb_get_ntohs(tvb, offset+2+iPropLength+1);
797
0
                                        if (remain < 2+iPropLength+3+iStringLength)
798
0
                                                break;
799
800
0
                                        return (char*)tvb_get_string_enc(allocator, tvb, offset+2+iPropLength+3, iStringLength, ENC_ASCII);
801
0
                                }
802
803
0
                                itemlen = rtmpt_get_amf_length(tvb, offset+2+iPropLength, pi);
804
0
                                if (itemlen == 0)
805
0
                                        break;
806
0
                                offset += 2+iPropLength+itemlen;
807
0
                                remain -= 2+iPropLength+itemlen;
808
0
                        }
809
0
                }
810
0
        }
811
812
0
        return NULL;
813
0
}
814
815
static uint32_t
816
rtmpt_get_amf_txid(tvbuff_t *tvb, int offset, proto_item* pi)
817
6
{
818
6
        uint32_t remain = tvb_reported_length_remaining(tvb, offset);
819
820
6
        if (remain > 0) {
821
5
                uint32_t itemlen = rtmpt_get_amf_length(tvb, offset, pi);
822
5
                if (itemlen == 0 || remain < itemlen)
823
0
                        return 0;
824
5
                offset += itemlen;
825
5
                remain -= itemlen;
826
5
        }
827
6
        if (remain >= 9) {
828
3
                uint8_t iObjType = tvb_get_uint8(tvb, offset);
829
3
                if (iObjType == AMF0_NUMBER) {
830
2
                        return (uint32_t)tvb_get_ntohieee_double(tvb, offset+1);
831
2
                }
832
3
        }
833
834
4
        return 0;
835
6
}
836
837
838
/* Generate a useful description for various packet types */
839
840
static char *
841
rtmpt_get_packet_desc(wmem_allocator_t* allocator, tvbuff_t *tvb, uint32_t offset, proto_item* pi, uint32_t remain, rtmpt_conv_t *rconv, int cdir,
842
        rtmpt_packet_t *tp, bool *deschasopcode)
843
2.92k
{
844
2.92k
        if (tp->cmd == RTMPT_TYPE_CHUNK_SIZE || tp->cmd == RTMPT_TYPE_ABORT_MESSAGE ||
845
2.63k
            tp->cmd == RTMPT_TYPE_ACKNOWLEDGEMENT || tp->cmd == RTMPT_TYPE_WINDOW) {
846
372
                if (tp->len >= 4 && remain >= 4) {
847
192
                        *deschasopcode = true;
848
192
                        return wmem_strdup_printf(allocator, "%s %d",
849
192
                                                val_to_str(allocator, tp->cmd, rtmpt_opcode_vals, "Unknown (0x%01x)"),
850
192
                                                tvb_get_ntohl(tvb, offset));
851
192
                }
852
853
2.55k
        } else if (tp->cmd == RTMPT_TYPE_PEER_BANDWIDTH) {
854
28
                if (tp->len >= 5 && remain >= 5) {
855
23
                        *deschasopcode = true;
856
23
                        return wmem_strdup_printf(allocator, "%s %d,%s",
857
23
                                                val_to_str(allocator, tp->cmd, rtmpt_opcode_vals, "Unknown (0x%01x)"),
858
23
                                                tvb_get_ntohl(tvb, offset),
859
23
                                                val_to_str(allocator, tvb_get_uint8(tvb, offset+4), rtmpt_limit_vals, "Unknown (%d)"));
860
23
                }
861
862
2.52k
        } else if (tp->cmd == RTMPT_TYPE_UCM) {
863
12
                uint16_t iUCM = -1;
864
12
                const char *sFunc;
865
12
                const char *sParam = "";
866
867
12
                if (tp->len < 2 || remain < 2)
868
6
                        return NULL;
869
870
6
                iUCM = tvb_get_ntohs(tvb, offset);
871
6
                sFunc = try_val_to_str(iUCM, rtmpt_ucm_vals);
872
6
                if (sFunc == NULL) {
873
4
                        *deschasopcode = true;
874
4
                        sFunc = wmem_strdup_printf(allocator, "User Control Message 0x%01x", iUCM);
875
4
                }
876
877
6
                if (iUCM == RTMPT_UCM_STREAM_BEGIN || iUCM == RTMPT_UCM_STREAM_EOF ||
878
4
                    iUCM == RTMPT_UCM_STREAM_DRY || iUCM == RTMPT_UCM_STREAM_ISRECORDED) {
879
2
                        if (tp->len >= 6 && remain >= 6) {
880
2
                                sParam = wmem_strdup_printf(allocator, " %d", tvb_get_ntohl(tvb, offset+2));
881
2
                        }
882
4
                } else if (iUCM == RTMPT_UCM_SET_BUFFER) {
883
0
                        if (tp->len >= 10 && remain >= 10) {
884
0
                                sParam = wmem_strdup_printf(allocator, " %d,%dms",
885
0
                                                          tvb_get_ntohl(tvb, offset+2),
886
0
                                                          tvb_get_ntohl(tvb, offset+6));
887
0
                        }
888
0
                }
889
890
6
                return wmem_strdup_printf(allocator, "%s%s", sFunc, sParam);
891
892
2.51k
        } else if (tp->cmd == RTMPT_TYPE_COMMAND_AMF0 || tp->cmd == RTMPT_TYPE_COMMAND_AMF3 ||
893
2.50k
                   tp->cmd == RTMPT_TYPE_DATA_AMF0 || tp->cmd == RTMPT_TYPE_DATA_AMF3) {
894
6
                uint32_t slen = 0;
895
6
                uint32_t soff = 0;
896
6
                char *sFunc = NULL;
897
6
                char *sParam = NULL;
898
899
6
                if (tp->cmd == RTMPT_TYPE_COMMAND_AMF3 || tp->cmd == RTMPT_TYPE_DATA_AMF3) {
900
1
                        soff = 1;
901
1
                }
902
6
                if (tp->len >= 3+soff && remain >= 3+soff) {
903
5
                        slen = tvb_get_ntohs(tvb, offset+1+soff);
904
5
                }
905
6
                if (slen > 0) {
906
3
                        sFunc = (char*)tvb_get_string_enc(allocator, tvb, offset+3+soff, slen, ENC_ASCII);
907
3
                        ws_debug("got function call '%s'", sFunc);
908
909
3
                        if (strcmp(sFunc, "connect") == 0) {
910
0
                                sParam = rtmpt_get_amf_param(allocator, tvb, offset+soff, pi, 2, "app");
911
3
                        } else if (strcmp(sFunc, "play") == 0) {
912
0
                                sParam = rtmpt_get_amf_param(allocator, tvb, offset+soff, pi, 3, NULL);
913
3
                        } else if (strcmp(sFunc, "play2") == 0) {
914
0
                                sParam = rtmpt_get_amf_param(allocator, tvb, offset+soff, pi, 3, "streamName");
915
3
                        } else if (strcmp(sFunc, "releaseStream") == 0) {
916
0
                                sParam = rtmpt_get_amf_param(allocator, tvb, offset+soff, pi, 3, NULL);
917
3
                        } else if (strcmp(sFunc, "FCPublish") == 0) {
918
0
                                sParam = rtmpt_get_amf_param(allocator, tvb, offset+soff, pi, 3, NULL);
919
3
                        } else if (strcmp(sFunc, "publish") == 0) {
920
0
                                sParam = rtmpt_get_amf_param(allocator, tvb, offset+soff, pi, 3, NULL);
921
3
                        } else if (strcmp(sFunc, "onStatus") == 0) {
922
0
                                if (tp->cmd == RTMPT_TYPE_COMMAND_AMF0 || tp->cmd == RTMPT_TYPE_COMMAND_AMF3) {
923
0
                                        sParam = rtmpt_get_amf_param(allocator, tvb, offset+soff, pi, 3, "code");
924
0
                                } else {
925
0
                                        sParam = rtmpt_get_amf_param(allocator, tvb, offset+soff, pi, 1, "code");
926
0
                                }
927
3
                        } else if (strcmp(sFunc, "onPlayStatus") == 0) {
928
0
                                sParam = rtmpt_get_amf_param(allocator, tvb, offset+soff, pi, 1, "code");
929
3
                        } else if (strcmp(sFunc, "_result") == 0) {
930
0
                                sParam = rtmpt_get_amf_param(allocator, tvb, offset+soff, pi, 3, "code");
931
0
                                tp->isresponse = true;
932
3
                        } else if (strcmp(sFunc, "_error") == 0) {
933
0
                                sParam = rtmpt_get_amf_param(allocator, tvb, offset+soff, pi, 3, "code");
934
0
                                tp->isresponse = true;
935
0
                        }
936
937
3
                        if (tp->txid != 0 && tp->otherframe == 0) {
938
0
                                tp->otherframe = GPOINTER_TO_INT(wmem_tree_lookup32(rconv->txids[cdir^1], tp->txid));
939
0
                                if (tp->otherframe) {
940
0
                                        ws_debug("got otherframe=%d", tp->otherframe);
941
0
                                }
942
0
                        }
943
3
                }
944
945
6
                if (sFunc) {
946
1
                        if (sParam) {
947
0
                                return wmem_strdup_printf(allocator, "%s('%s')", sFunc, sParam);
948
1
                        } else {
949
1
                                return wmem_strdup_printf(allocator, "%s()", sFunc);
950
1
                        }
951
1
                }
952
6
        }
953
954
2.69k
        return NULL;
955
2.92k
}
956
957
958
/* Tree dissection helpers for various packet body forms */
959
960
static void
961
dissect_rtmpt_body_scm(tvbuff_t *tvb, int offset, proto_tree *rtmpt_tree, unsigned scm)
962
223
{
963
223
        switch (scm) {
964
99
        case RTMPT_TYPE_CHUNK_SIZE:
965
99
                proto_tree_add_item(rtmpt_tree, hf_rtmpt_scm_chunksize, tvb, offset, 4, ENC_BIG_ENDIAN);
966
99
                break;
967
19
        case RTMPT_TYPE_ABORT_MESSAGE:
968
19
                proto_tree_add_item(rtmpt_tree, hf_rtmpt_scm_csid, tvb, offset, 4, ENC_BIG_ENDIAN);
969
19
                break;
970
1
        case RTMPT_TYPE_ACKNOWLEDGEMENT:
971
1
                proto_tree_add_item(rtmpt_tree, hf_rtmpt_scm_seq, tvb, offset, 4, ENC_BIG_ENDIAN);
972
1
                break;
973
6
        case RTMPT_TYPE_UCM:
974
6
                proto_tree_add_item(rtmpt_tree, hf_rtmpt_ucm_eventtype, tvb, offset, 2, ENC_BIG_ENDIAN);
975
6
                break;
976
75
        case RTMPT_TYPE_WINDOW:
977
75
                proto_tree_add_item(rtmpt_tree, hf_rtmpt_scm_was, tvb, offset, 4, ENC_BIG_ENDIAN);
978
75
                break;
979
23
        case RTMPT_TYPE_PEER_BANDWIDTH:
980
23
                proto_tree_add_item(rtmpt_tree, hf_rtmpt_scm_was, tvb, offset, 4, ENC_BIG_ENDIAN);
981
23
                proto_tree_add_item(rtmpt_tree, hf_rtmpt_scm_limittype, tvb, offset+4, 1, ENC_BIG_ENDIAN);
982
23
                break;
983
223
        }
984
223
}
985
986
static int
987
dissect_amf0_value_type(tvbuff_t *tvb, packet_info *pinfo, int offset, proto_tree *tree, bool *amf3_encoding, proto_item *parent_ti);
988
989
/*
990
 * A "property list" is a sequence of name/value pairs, terminated by
991
 * and "end of object" indicator.  AMF0 "object"s and "ECMA array"s
992
 * are encoded as property lists.
993
 */
994
static int
995
// NOLINTNEXTLINE(misc-no-recursion)
996
dissect_amf0_property_list(tvbuff_t *tvb, packet_info *pinfo, int offset, proto_tree *tree, unsigned *countp, bool *amf3_encoding)
997
0
{
998
0
        proto_item *prop_ti;
999
0
        proto_tree *prop_tree;
1000
0
        proto_tree *name_tree;
1001
0
        unsigned    iStringLength;
1002
0
        char       *iStringValue;
1003
0
        unsigned    count = 0;
1004
1005
        /*
1006
         * XXX - at least as I read "3.1 AVM+ Type Marker" in the AMF0
1007
         * specification, the AVM+ Type Marker only affects "the following
1008
         * Object".  For now, we have a single "AMF3 encoding" flag, and
1009
         * set it when we see the type marker, and never clear it.
1010
         */
1011
0
        for (;;) {
1012
                /* UTF-8: property name */
1013
0
                iStringLength = tvb_get_ntohs(tvb, offset);
1014
0
                if (iStringLength == 0 &&
1015
0
                    tvb_get_uint8(tvb, offset + 2) == AMF0_END_OF_OBJECT)
1016
0
                        break;
1017
0
                count++;
1018
0
                iStringValue = (char*)tvb_get_string_enc(pinfo->pool, tvb, offset + 2, iStringLength, ENC_ASCII);
1019
0
                prop_tree = proto_tree_add_subtree_format(tree, tvb, offset, -1,
1020
0
                                              ett_amf_property, &prop_ti, "Property '%s'",
1021
0
                                              iStringValue);
1022
1023
0
                name_tree = proto_tree_add_subtree_format(prop_tree, tvb,
1024
0
                                              offset, 2+iStringLength,
1025
0
                                              ett_amf_string, NULL, "Name: %s", iStringValue);
1026
1027
0
                proto_tree_add_uint(name_tree, hf_amf_stringlength, tvb, offset, 2, iStringLength);
1028
0
                offset += 2;
1029
0
                proto_tree_add_item(name_tree, hf_amf_string, tvb, offset, iStringLength, ENC_UTF_8);
1030
0
                offset += iStringLength;
1031
1032
                /* value-type: property value */
1033
0
                offset = dissect_amf0_value_type(tvb, pinfo, offset, prop_tree, amf3_encoding, prop_ti);
1034
0
                proto_item_set_end(prop_ti, tvb, offset);
1035
0
        }
1036
0
        proto_tree_add_item(tree, hf_amf_end_of_object_marker, tvb, offset, 3, ENC_NA);
1037
0
        offset += 3;
1038
1039
0
        *countp = count;
1040
1041
0
        return offset;
1042
0
}
1043
1044
static int
1045
// NOLINTNEXTLINE(misc-no-recursion)
1046
dissect_amf0_value_type(tvbuff_t *tvb, packet_info *pinfo, int offset, proto_tree *tree, bool *amf3_encoding, proto_item *parent_ti)
1047
11
{
1048
11
        uint8_t     iObjType;
1049
11
        proto_item *ti;
1050
11
        proto_tree *val_tree;
1051
11
        int         iValueOffset = offset;
1052
11
        uint32_t    iIntegerValue;
1053
11
        double      iDoubleValue;
1054
11
        bool        iBooleanValue;
1055
11
        unsigned    iStringLength;
1056
11
        char       *iStringValue;
1057
11
        unsigned    iArrayLength;
1058
11
        unsigned    i;
1059
11
        nstime_t    t;
1060
11
        int64_t     iInteger64Value;
1061
11
        unsigned    count;
1062
1063
11
        iObjType = tvb_get_uint8(tvb, offset);
1064
11
        if (parent_ti != NULL)
1065
0
                proto_item_append_text(parent_ti, " %s",
1066
0
                                       val_to_str_const(iObjType, amf0_type_vals, "Unknown"));
1067
11
        switch (iObjType) {
1068
1069
0
        case AMF0_OBJECT:
1070
                /*
1071
                 * For object types, make the top-level protocol tree
1072
                 * item a field for that type.
1073
                 */
1074
0
                ti = proto_tree_add_item(tree, hf_amf_object, tvb, offset, -1, ENC_NA);
1075
0
                val_tree = proto_item_add_subtree(ti, ett_amf_value);
1076
0
                break;
1077
1078
0
        case AMF0_ECMA_ARRAY:
1079
                /*
1080
                 * For ECMA array types, make the top-level protocol tree
1081
                 * item a field for that type.
1082
                 */
1083
0
                ti = proto_tree_add_item(tree, hf_amf_ecmaarray, tvb, offset, -1, ENC_NA);
1084
0
                val_tree = proto_item_add_subtree(ti, ett_amf_value);
1085
0
                break;
1086
1087
0
        case AMF0_STRICT_ARRAY:
1088
                /*
1089
                 * For strict array types, make the top-level protocol tree
1090
                 * item a field for that type.
1091
                 */
1092
0
                ti = proto_tree_add_item(tree, hf_amf_strictarray, tvb, offset, -1, ENC_NA);
1093
0
                val_tree = proto_item_add_subtree(ti, ett_amf_value);
1094
0
                break;
1095
1096
11
        default:
1097
                /*
1098
                 * For all other types, make it just a text item; the
1099
                 * field for that type will be used for the value.
1100
                 */
1101
11
                val_tree = proto_tree_add_subtree(tree, tvb, offset, -1, ett_amf_value, &ti,
1102
11
                                         val_to_str_const(iObjType, amf0_type_vals, "Unknown"));
1103
11
                break;
1104
11
        }
1105
1106
11
        proto_tree_add_uint(val_tree, hf_amf_amf0_type, tvb, iValueOffset, 1, iObjType);
1107
11
        iValueOffset++;
1108
1109
11
        increment_dissection_depth(pinfo);
1110
11
        switch (iObjType) {
1111
8
        case AMF0_NUMBER:
1112
8
                iDoubleValue = tvb_get_ntohieee_double(tvb, iValueOffset);
1113
8
                proto_tree_add_double(val_tree, hf_amf_number, tvb, iValueOffset, 8, iDoubleValue);
1114
8
                iValueOffset += 8;
1115
8
                proto_item_append_text(ti, " %." G_STRINGIFY(DBL_DIG) "g", iDoubleValue);
1116
8
                if (parent_ti != NULL)
1117
0
                        proto_item_append_text(parent_ti, " %." G_STRINGIFY(DBL_DIG) "g", iDoubleValue);
1118
8
                break;
1119
0
        case AMF0_BOOLEAN:
1120
0
                iBooleanValue = tvb_get_uint8(tvb, iValueOffset);
1121
0
                proto_tree_add_boolean(val_tree, hf_amf_boolean, tvb, iValueOffset, 1, iBooleanValue);
1122
0
                iValueOffset += 1;
1123
0
                proto_item_append_text(ti, iBooleanValue ? " true" : " false");
1124
0
                if (parent_ti != NULL)
1125
0
                        proto_item_append_text(parent_ti, iBooleanValue ? " true" : " false");
1126
0
                break;
1127
0
        case AMF0_STRING:
1128
0
                iStringLength = tvb_get_ntohs(tvb, iValueOffset);
1129
0
                proto_tree_add_uint(val_tree, hf_amf_stringlength, tvb, iValueOffset, 2, iStringLength);
1130
0
                iValueOffset += 2;
1131
0
                iStringValue = (char*)tvb_get_string_enc(pinfo->pool, tvb, iValueOffset, iStringLength, ENC_UTF_8|ENC_NA);
1132
0
                if (iStringLength != 0)
1133
0
                        proto_tree_add_string(val_tree, hf_amf_string, tvb, iValueOffset, iStringLength, iStringValue);
1134
0
                iValueOffset += iStringLength;
1135
0
                proto_item_append_text(ti, " '%s'", iStringValue);
1136
0
                if (parent_ti != NULL)
1137
0
                        proto_item_append_text(parent_ti, " '%s'", iStringValue);
1138
0
                break;
1139
0
        case AMF0_OBJECT:
1140
0
                iValueOffset = dissect_amf0_property_list(tvb, pinfo, iValueOffset, val_tree, &count, amf3_encoding);
1141
0
                proto_item_append_text(ti, " (%u items)", count);
1142
0
                break;
1143
0
        case AMF0_NULL:
1144
0
        case AMF0_UNDEFINED:
1145
0
                break;
1146
0
        case AMF0_REFERENCE:
1147
0
                iIntegerValue = tvb_get_ntohs(tvb, iValueOffset);
1148
0
                proto_tree_add_uint(val_tree, hf_amf_object_reference, tvb, iValueOffset, 2, iIntegerValue);
1149
0
                iValueOffset += 2;
1150
0
                proto_item_append_text(ti, " %d", iIntegerValue);
1151
0
                break;
1152
0
        case AMF0_ECMA_ARRAY:
1153
                /*
1154
                 * Counted list type, with end marker. The count appears to be
1155
                 * more of a hint than a rule, and is sometimes sent as 0 or
1156
                 * invalid.
1157
                 *
1158
                 * Basically the same as OBJECT but with the extra count field.
1159
                 * There being many strange encoders/metadata injectors out
1160
                 * there, sometimes you see a valid count and no end marker.
1161
                 * Figuring out which you've got for a deeply nested structure
1162
                 * is non-trivial.
1163
                 */
1164
0
                iArrayLength = tvb_get_ntohl(tvb, iValueOffset);
1165
0
                proto_tree_add_uint(val_tree, hf_amf_arraylength, tvb, iValueOffset, 4, iArrayLength);
1166
0
                iValueOffset += 4;
1167
0
                iValueOffset = dissect_amf0_property_list(tvb, pinfo, iValueOffset, val_tree, &count, amf3_encoding);
1168
0
                proto_item_append_text(ti, " (%u items)", count);
1169
0
                break;
1170
0
        case AMF0_END_OF_OBJECT:
1171
0
                proto_tree_add_item(tree, hf_amf_end_of_object_marker, tvb, iValueOffset, 3, ENC_NA);
1172
0
                iValueOffset += 3;
1173
0
                break;
1174
0
        case AMF0_STRICT_ARRAY:
1175
                /*
1176
                 * Counted list type, without end marker. Number of values
1177
                 * is determined by count, values are assumed to form a
1178
                 * [0..N-1] numbered array and are presented as plain AMF
1179
                 * types, not OBJECT or ECMA_ARRAY style named properties.
1180
                 */
1181
0
                iArrayLength = tvb_get_ntohl(tvb, iValueOffset);
1182
0
                proto_tree_add_uint(val_tree, hf_amf_arraylength, tvb, iValueOffset, 4, iArrayLength);
1183
0
                iValueOffset += 4;
1184
0
                for (i = 0; i < iArrayLength; i++)
1185
0
                        iValueOffset = dissect_amf0_value_type(tvb, pinfo, iValueOffset, val_tree, amf3_encoding, NULL);
1186
0
                proto_item_append_text(ti, " (%u items)", iArrayLength);
1187
0
                break;
1188
0
        case AMF0_DATE:
1189
0
                iDoubleValue = tvb_get_ntohieee_double(tvb, iValueOffset);
1190
0
                t.secs = (time_t)(iDoubleValue/1000);
1191
0
                t.nsecs = (int)((iDoubleValue - 1000*(double)t.secs) * 1000000);
1192
0
                proto_tree_add_time(val_tree, hf_amf_date, tvb, iValueOffset, 8, &t);
1193
0
                iValueOffset += 8;
1194
0
                proto_item_append_text(ti, " %s", abs_time_to_str(pinfo->pool, &t, ABSOLUTE_TIME_LOCAL, true));
1195
0
                if (parent_ti != NULL)
1196
0
                        proto_item_append_text(parent_ti, " %s", abs_time_to_str(pinfo->pool, &t, ABSOLUTE_TIME_LOCAL, true));
1197
                /* time-zone */
1198
0
                iValueOffset += 2;
1199
0
                break;
1200
0
        case AMF0_LONG_STRING:
1201
0
        case AMF0_XML: /* same representation */
1202
0
                iStringLength = tvb_get_ntohl(tvb, iValueOffset);
1203
0
                proto_tree_add_uint(val_tree, hf_amf_stringlength, tvb, iValueOffset, 4, iStringLength);
1204
0
                iValueOffset += 4;
1205
0
                iStringValue = (char*)tvb_get_string_enc(pinfo->pool, tvb, iValueOffset, iStringLength, ENC_UTF_8|ENC_NA);
1206
0
                if (iStringLength != 0)
1207
0
                        proto_tree_add_string(val_tree, (iObjType == AMF0_XML) ? hf_amf_xml_doc : hf_amf_longstring, tvb, iValueOffset, iStringLength, iStringValue);
1208
0
                iValueOffset += iStringLength;
1209
0
                proto_item_append_text(ti, " '%s'", iStringValue);
1210
0
                if (parent_ti != NULL)
1211
0
                        proto_item_append_text(parent_ti, " '%s'", iStringValue);
1212
0
                break;
1213
0
        case AMF0_UNSUPPORTED:
1214
0
                break;
1215
0
        case AMF0_TYPED_OBJECT:
1216
                /* class-name */
1217
0
                iStringLength = tvb_get_ntohs(tvb, iValueOffset);
1218
0
                proto_tree_add_uint(val_tree, hf_amf_stringlength, tvb, iValueOffset, 2, iStringLength);
1219
0
                iValueOffset += 2;
1220
0
                iStringValue = (char*)tvb_get_string_enc(pinfo->pool, tvb, iValueOffset, iStringLength, ENC_UTF_8|ENC_NA);
1221
0
                proto_tree_add_string(val_tree, hf_amf_string, tvb, iValueOffset, iStringLength, iStringValue);
1222
0
                iValueOffset += iStringLength;
1223
0
                iValueOffset = dissect_amf0_property_list(tvb, pinfo, iValueOffset, val_tree, &count, amf3_encoding);
1224
0
                break;
1225
0
        case AMF0_AMF3_MARKER:
1226
0
                *amf3_encoding = true;
1227
0
                break;
1228
0
        case AMF0_INT64:
1229
0
                iInteger64Value = tvb_get_ntoh64(tvb, iValueOffset);
1230
0
                proto_tree_add_int64(val_tree, hf_amf_int64, tvb, iValueOffset, 8, iInteger64Value);
1231
0
                iValueOffset += 8;
1232
0
                proto_item_append_text(ti," %" PRId64, iInteger64Value);
1233
0
                if (parent_ti != NULL)
1234
0
                        proto_item_append_text(parent_ti," %" PRId64, iInteger64Value);
1235
0
                break;
1236
3
        default:
1237
                /*
1238
                 * If we can't determine the length, don't carry on;
1239
                 * just skip to the end of the tvbuff.
1240
                 */
1241
3
                iValueOffset = tvb_reported_length(tvb);
1242
3
                break;
1243
11
        }
1244
11
        decrement_dissection_depth(pinfo);
1245
11
        proto_item_set_end(ti, tvb, iValueOffset);
1246
11
        return iValueOffset;
1247
11
}
1248
1249
static uint32_t
1250
amf_get_u29(tvbuff_t *tvb, int offset, unsigned *lenp)
1251
0
{
1252
0
        unsigned   len = 0;
1253
0
        uint8_t iByte;
1254
0
        uint32_t iValue;
1255
1256
0
        iByte  = tvb_get_uint8(tvb, offset);
1257
0
        iValue = (iByte & 0x7F);
1258
0
        offset++;
1259
0
        len++;
1260
0
        if (!(iByte & 0x80)) {
1261
                /* 1 byte value */
1262
0
                *lenp = len;
1263
0
                return iValue;
1264
0
        }
1265
0
        iByte = tvb_get_uint8(tvb, offset);
1266
0
        iValue = (iValue << 7) | (iByte & 0x7F);
1267
0
        offset++;
1268
0
        len++;
1269
0
        if (!(iByte & 0x80)) {
1270
                /* 2 byte value */
1271
0
                *lenp = len;
1272
0
                return iValue;
1273
0
        }
1274
0
        iByte = tvb_get_uint8(tvb, offset);
1275
0
        iValue = (iValue << 7) | (iByte & 0x7F);
1276
0
        offset++;
1277
0
        len++;
1278
0
        if (!(iByte & 0x80)) {
1279
                /* 3 byte value */
1280
0
                *lenp = len;
1281
0
                return iValue;
1282
0
        }
1283
0
        iByte = tvb_get_uint8(tvb, offset);
1284
0
        iValue = (iValue << 8) | iByte;
1285
0
        len++;
1286
0
        *lenp = len;
1287
0
        return iValue;
1288
0
}
1289
1290
static int
1291
// NOLINTNEXTLINE(misc-no-recursion)
1292
dissect_amf3_value_type(tvbuff_t *tvb, packet_info *pinfo, int offset, proto_tree *tree, proto_item *parent_ti)
1293
0
{
1294
0
        uint8_t     iObjType;
1295
0
        proto_item *ti;
1296
0
        proto_tree *val_tree;
1297
0
        int         iValueOffset = offset;
1298
0
        unsigned    iValueLength;
1299
0
        uint32_t    iIntegerValue;
1300
0
        double      iDoubleValue;
1301
0
        unsigned    iStringLength;
1302
0
        char       *iStringValue;
1303
0
        unsigned    iArrayLength;
1304
0
        proto_item *subval_ti;
1305
0
        proto_tree *subval_tree;
1306
0
        unsigned    i;
1307
0
        bool        iTypeIsDynamic;
1308
0
        unsigned    iTraitCount;
1309
0
        proto_item *traits_ti;
1310
0
        proto_tree *traits_tree;
1311
0
        proto_tree *name_tree;
1312
0
        proto_tree *member_tree;
1313
0
        uint8_t    *iByteArrayValue;
1314
1315
0
        iObjType = tvb_get_uint8(tvb, offset);
1316
0
        if (parent_ti != NULL)
1317
0
                proto_item_append_text(parent_ti, " %s",
1318
0
                                       val_to_str_const(iObjType, amf3_type_vals, "Unknown"));
1319
0
        switch (iObjType) {
1320
1321
0
        case AMF3_ARRAY:
1322
                /*
1323
                 * For array types, make the top-level protocol tree
1324
                 * item a field for that type.
1325
                 */
1326
0
                ti = proto_tree_add_item(tree, hf_amf_array, tvb, offset, -1, ENC_NA);
1327
0
                val_tree = proto_item_add_subtree(ti, ett_amf_value);
1328
0
                break;
1329
1330
0
        case AMF3_OBJECT:
1331
                /*
1332
                 * For object types, make the top-level protocol tree
1333
                 * item a field for that type.
1334
                 */
1335
0
                ti = proto_tree_add_item(tree, hf_amf_object, tvb, offset, -1, ENC_NA);
1336
0
                val_tree = proto_item_add_subtree(ti, ett_amf_value);
1337
0
                break;
1338
1339
0
        default:
1340
                /*
1341
                 * For all other types, make it just a text item; the
1342
                 * field for that type will be used for the value.
1343
                 */
1344
0
                val_tree = proto_tree_add_subtree(tree, tvb, offset, -1, ett_amf_value, &ti,
1345
0
                                         val_to_str_const(iObjType, amf3_type_vals, "Unknown"));
1346
0
                break;
1347
0
        }
1348
1349
0
        proto_tree_add_uint(val_tree, hf_amf_amf3_type, tvb, iValueOffset, 1, iObjType);
1350
0
        iValueOffset++;
1351
1352
0
        increment_dissection_depth(pinfo);
1353
0
        switch (iObjType) {
1354
0
        case AMF3_UNDEFINED:
1355
0
        case AMF3_NULL:
1356
0
                break;
1357
0
        case AMF3_FALSE:
1358
0
                proto_tree_add_boolean(val_tree, hf_amf_boolean, tvb, 0, 0, false);
1359
0
                proto_item_append_text(ti, " false");
1360
0
                break;
1361
0
        case AMF3_TRUE:
1362
0
                proto_tree_add_boolean(val_tree, hf_amf_boolean, tvb, 0, 0, true);
1363
0
                proto_item_append_text(ti, " true");
1364
0
                break;
1365
0
        case AMF3_INTEGER:
1366
                /* XXX - signed or unsigned? */
1367
0
                iIntegerValue = amf_get_u29(tvb, iValueOffset, &iValueLength);
1368
0
                proto_tree_add_uint(val_tree, hf_amf_integer, tvb, iValueOffset, iValueLength, iIntegerValue);
1369
0
                proto_item_append_text(ti, " %u", iIntegerValue);
1370
0
                if (parent_ti != NULL)
1371
0
                        proto_item_append_text(parent_ti, " %u", iIntegerValue);
1372
0
                iValueOffset += iValueLength;
1373
0
                break;
1374
0
        case AMF3_DOUBLE:
1375
0
                iDoubleValue = tvb_get_ntohieee_double(tvb, iValueOffset);
1376
0
                proto_tree_add_double(val_tree, hf_amf_number, tvb, iValueOffset, 8, iDoubleValue);
1377
0
                iValueOffset += 8;
1378
0
                proto_item_append_text(ti, " %." G_STRINGIFY(DBL_DIG) "g", iDoubleValue);
1379
0
                if (parent_ti != NULL)
1380
0
                        proto_item_append_text(parent_ti, " %." G_STRINGIFY(DBL_DIG) "g", iDoubleValue);
1381
0
                break;
1382
0
        case AMF3_STRING:
1383
0
                iIntegerValue = amf_get_u29(tvb, iValueOffset, &iValueLength);
1384
0
                if (iIntegerValue & 0x00000001) {
1385
                        /* the upper 28 bits of the integer value is a string length */
1386
0
                        iStringLength = iIntegerValue >> 1;
1387
0
                        proto_tree_add_uint(val_tree, hf_amf_stringlength, tvb, iValueOffset, iValueLength, iStringLength);
1388
0
                        iValueOffset += iValueLength;
1389
0
                        iStringValue = (char*)tvb_get_string_enc(pinfo->pool, tvb, iValueOffset, iStringLength, ENC_UTF_8|ENC_NA);
1390
0
                        if (iStringLength != 0)
1391
0
                                proto_tree_add_string(val_tree, hf_amf_string, tvb, iValueOffset, iStringLength, iStringValue);
1392
0
                        iValueOffset += iStringLength;
1393
0
                        proto_item_append_text(ti, " '%s'", iStringValue);
1394
0
                        if (parent_ti != NULL)
1395
0
                                proto_item_append_text(parent_ti, " '%s'", iStringValue);
1396
0
                } else {
1397
                        /* the upper 28 bits of the integer value are a string reference index */
1398
0
                        proto_tree_add_uint(val_tree, hf_amf_string_reference, tvb, iValueOffset, iValueLength, iIntegerValue >> 1);
1399
0
                        iValueOffset += iValueLength;
1400
0
                        proto_item_append_text(ti, " reference %u", iIntegerValue >> 1);
1401
0
                        if (parent_ti != NULL)
1402
0
                                proto_item_append_text(parent_ti, " reference %u", iIntegerValue >> 1);
1403
0
                        iValueOffset += iValueLength;
1404
0
                }
1405
0
                break;
1406
0
        case AMF3_DATE:
1407
0
                iIntegerValue = amf_get_u29(tvb, iValueOffset, &iValueLength);
1408
0
                if (iIntegerValue & 0x00000001) {
1409
                        /*
1410
                         * The upper 28 bits of the integer value are
1411
                         * ignored; what follows is a double
1412
                         * containing milliseconds since the Epoch.
1413
                         */
1414
0
                        nstime_t t;
1415
1416
0
                        iValueOffset += iValueLength;
1417
0
                        iDoubleValue = tvb_get_ntohieee_double(tvb, iValueOffset);
1418
0
                        t.secs = (time_t)(iDoubleValue/1000);
1419
0
                        t.nsecs = (int)((iDoubleValue - 1000*(double)t.secs) * 1000000);
1420
0
                        proto_tree_add_time(val_tree, hf_amf_date, tvb, iValueOffset, 8, &t);
1421
0
                        iValueOffset += 8;
1422
0
                        proto_item_append_text(ti, "%s", abs_time_to_str(pinfo->pool, &t, ABSOLUTE_TIME_LOCAL, true));
1423
0
                        if (parent_ti != NULL)
1424
0
                                proto_item_append_text(parent_ti, "%s", abs_time_to_str(pinfo->pool, &t, ABSOLUTE_TIME_LOCAL, true));
1425
0
                } else {
1426
                        /* the upper 28 bits of the integer value are an object reference index */
1427
0
                        proto_tree_add_uint(val_tree, hf_amf_object_reference, tvb, iValueOffset, iValueLength, iIntegerValue >> 1);
1428
0
                        iValueOffset += iValueLength;
1429
0
                        proto_item_append_text(ti, " object reference %u", iIntegerValue >> 1);
1430
0
                        if (parent_ti != NULL)
1431
0
                                proto_item_append_text(parent_ti, " object reference %u", iIntegerValue >> 1);
1432
0
                }
1433
0
                break;
1434
0
        case AMF3_ARRAY:
1435
0
                iIntegerValue = amf_get_u29(tvb, iValueOffset, &iValueLength);
1436
0
                if (iIntegerValue & 0x00000001) {
1437
                        /*
1438
                         * The upper 28 bits of the integer value are
1439
                         * a count of the number of elements in
1440
                         * the dense portion of the array.
1441
                         */
1442
0
                        iArrayLength = iIntegerValue >> 1;
1443
0
                        proto_tree_add_uint(val_tree, hf_amf_arraydenselength, tvb, iValueOffset, iValueLength, iArrayLength);
1444
0
                        iValueOffset += iValueLength;
1445
1446
                        /*
1447
                         * The AMF3 spec bit on the Array type is slightly
1448
                         * confusingly written, but seems to be saying that
1449
                         * the associative portion of the array follows the
1450
                         * size of the dense portion of the array, and the
1451
                         * dense portion of the array follows the associative
1452
                         * portion.
1453
                         *
1454
                         * Dissect the associative portion.
1455
                         */
1456
0
                        for (;;) {
1457
                                /* Fetch the name */
1458
0
                                iIntegerValue = amf_get_u29(tvb, iValueOffset, &iValueLength);
1459
0
                                if (iIntegerValue & 0x00000001) {
1460
                                        /* the upper 28 bits of the integer value is a string length */
1461
0
                                        iStringLength = iIntegerValue >> 1;
1462
0
                                        if (iStringLength == 0) {
1463
                                                /* null name marks the end of the associative part */
1464
0
                                                proto_tree_add_item(val_tree, hf_amf_end_of_associative_part, tvb, iValueOffset, iValueLength, ENC_NA);
1465
0
                                                iValueOffset += iValueLength;
1466
0
                                                break;
1467
0
                                        }
1468
0
                                        iStringValue = (char*)tvb_get_string_enc(pinfo->pool, tvb, iValueOffset+iValueLength, iStringLength, ENC_UTF_8|ENC_NA);
1469
0
                                        subval_tree = proto_tree_add_subtree(val_tree, tvb, iValueOffset, iValueLength+iStringLength,
1470
0
                                                                    ett_amf_array_element, &subval_ti, iStringValue);
1471
0
                                        proto_tree_add_uint(subval_tree, hf_amf_stringlength, tvb, iValueOffset, iValueLength, iStringLength);
1472
0
                                        iValueOffset += iValueLength;
1473
0
                                        proto_tree_add_string(subval_tree, hf_amf_string, tvb, iValueOffset, iStringLength, iStringValue);
1474
0
                                        iValueOffset += iStringLength;
1475
0
                                } else {
1476
                                        /* the upper 28 bits of the integer value are a string reference index */
1477
0
                                        subval_tree = proto_tree_add_subtree_format(val_tree, tvb, iValueOffset, iValueLength,
1478
0
                                                        ett_amf_array_element, &subval_ti, "Reference %u:", iIntegerValue >> 1);
1479
0
                                        proto_tree_add_uint(subval_tree, hf_amf_string_reference, tvb, iValueOffset, iValueLength, iIntegerValue >> 1);
1480
0
                                        iValueOffset += iValueLength;
1481
0
                                }
1482
1483
                                /* Fetch the value */
1484
0
                                iValueOffset = dissect_amf3_value_type(tvb, pinfo, iValueOffset, subval_tree, subval_ti);
1485
0
                        }
1486
1487
                        /*
1488
                         * Dissect the dense portion.
1489
                         */
1490
0
                        for (i = 0; i < iArrayLength; i++)
1491
0
                                iValueOffset = dissect_amf3_value_type(tvb, pinfo, iValueOffset, val_tree, NULL);
1492
1493
0
                        proto_item_set_end(ti, tvb, iValueOffset);
1494
0
                } else {
1495
                        /* the upper 28 bits of the integer value are an object reference index */
1496
0
                        proto_tree_add_uint(val_tree, hf_amf_object_reference, tvb, iValueOffset, iValueLength, iIntegerValue >> 1);
1497
0
                        proto_item_append_text(ti, " reference %u", iIntegerValue >> 1);
1498
0
                        if (parent_ti != NULL)
1499
0
                                proto_item_append_text(parent_ti, " reference %u", iIntegerValue >> 1);
1500
0
                        iValueOffset += iValueLength;
1501
0
                }
1502
0
                break;
1503
0
        case AMF3_OBJECT:
1504
0
                iIntegerValue = amf_get_u29(tvb, iValueOffset, &iValueLength);
1505
0
                if (iIntegerValue & 0x00000001) {
1506
0
                        if (iIntegerValue & 0x00000002) {
1507
0
                                if (iIntegerValue & 0x00000004) {
1508
                                        /*
1509
                                         * U29O-traits-ext; the rest of
1510
                                         * iIntegerValue is not significant,
1511
                                         * and, worse, we have idea what
1512
                                         * follows the class name, or even
1513
                                         * how many bytes follow the class
1514
                                         * name - that's by convention between
1515
                                         * the client and server.
1516
                                         */
1517
0
                                        iValueOffset += iValueLength;
1518
0
                                } else {
1519
                                        /*
1520
                                         * U29O-traits; the 0x00000008 bit
1521
                                         * specifies whether the type is
1522
                                         * dynamic.
1523
                                         */
1524
0
                                        iTypeIsDynamic = (iIntegerValue & 0x00000008) ? true : false;
1525
0
                                        iTraitCount = iIntegerValue >> 4;
1526
0
                                        proto_tree_add_uint(val_tree, hf_amf_traitcount, tvb, iValueOffset, iValueLength, iTraitCount);
1527
0
                                        iValueOffset += iValueLength;
1528
0
                                        iIntegerValue = amf_get_u29(tvb, iValueOffset, &iValueLength);
1529
0
                                        if (iIntegerValue & 0x00000001) {
1530
                                                /* the upper 28 bits of the integer value is a string length */
1531
0
                                                iStringLength = iIntegerValue >> 1;
1532
0
                                                iStringValue = (char*)tvb_get_string_enc(pinfo->pool, tvb, iValueOffset+iValueLength, iStringLength, ENC_UTF_8|ENC_NA);
1533
0
                                                traits_tree = proto_tree_add_subtree_format(val_tree, tvb, iValueOffset, -1,
1534
0
                                                            ett_amf_traits, &traits_ti, "Traits for class %s (%u member names)", iStringValue, iTraitCount);
1535
0
                                                name_tree = proto_tree_add_subtree_format(traits_tree, tvb,
1536
0
                                                                              iValueOffset,
1537
0
                                                                              iValueLength+iStringLength,
1538
0
                                                                              ett_amf_string, NULL, "Class name: %s",
1539
0
                                                                              iStringValue);
1540
0
                                                proto_tree_add_uint(name_tree, hf_amf_classnamelength, tvb, iValueOffset, iValueLength, iStringLength);
1541
0
                                                iValueOffset += iValueLength;
1542
0
                                                proto_tree_add_string(name_tree, hf_amf_classname, tvb, iValueOffset, iStringLength, iStringValue);
1543
0
                                                iValueOffset += iStringLength;
1544
0
                                        } else {
1545
                                                /* the upper 28 bits of the integer value are a string reference index */
1546
0
                                                traits_tree = proto_tree_add_subtree_format(val_tree, tvb, iValueOffset, iValueLength,
1547
0
                                                    ett_amf_traits, &traits_ti, "Traits for class (reference %u for name)", iIntegerValue >> 1);
1548
0
                                                proto_tree_add_uint(traits_tree, hf_amf_string_reference, tvb, iValueOffset, iValueLength, iIntegerValue >> 1);
1549
0
                                                iValueOffset += iValueLength;
1550
0
                                        }
1551
0
                                        for (i = 0; i < iTraitCount; i++) {
1552
0
                                                iIntegerValue = amf_get_u29(tvb, iValueOffset, &iValueLength);
1553
0
                                                if (iIntegerValue & 0x00000001) {
1554
                                                        /* the upper 28 bits of the integer value is a string length */
1555
0
                                                        iStringLength = iIntegerValue >> 1;
1556
0
                                                        iStringValue = (char*)tvb_get_string_enc(pinfo->pool, tvb, iValueOffset+iValueLength, iStringLength, ENC_UTF_8|ENC_NA);
1557
0
                                                        member_tree = proto_tree_add_subtree_format(traits_tree, tvb, iValueOffset, iValueLength+iStringLength,
1558
0
                                                                                            ett_amf_trait_member, NULL, "Member '%s'", iStringValue);
1559
0
                                                        proto_tree_add_uint(member_tree, hf_amf_membernamelength, tvb, iValueOffset, iValueLength, iStringLength);
1560
0
                                                        iValueOffset += iValueLength;
1561
0
                                                        proto_tree_add_string(member_tree, hf_amf_membername, tvb, iValueOffset, iStringLength, iStringValue);
1562
0
                                                        iValueOffset += iStringLength;
1563
0
                                                } else {
1564
                                                        /* the upper 28 bits of the integer value are a string reference index */
1565
0
                                                        proto_tree_add_uint(traits_tree, hf_amf_string_reference, tvb, iValueOffset, iValueLength, iIntegerValue >> 1);
1566
0
                                                        iValueOffset += iValueLength;
1567
0
                                                }
1568
0
                                        }
1569
0
                                        for (i = 0; i < iTraitCount; i++)
1570
0
                                                iValueOffset = dissect_amf3_value_type(tvb, pinfo, iValueOffset, traits_tree, NULL);
1571
0
                                        if (iTypeIsDynamic) {
1572
0
                                                for (;;) {
1573
                                                        /* Fetch the name */
1574
0
                                                        iIntegerValue = amf_get_u29(tvb, iValueOffset, &iValueLength);
1575
0
                                                        if (iIntegerValue & 0x00000001) {
1576
                                                                /* the upper 28 bits of the integer value is a string length */
1577
0
                                                                iStringLength = iIntegerValue >> 1;
1578
0
                                                                if (iStringLength == 0) {
1579
                                                                        /* null name marks the end of the associative part */
1580
0
                                                                        proto_tree_add_item(traits_tree, hf_amf_end_of_dynamic_members, tvb, iValueOffset, iValueLength, ENC_NA);
1581
0
                                                                        iValueOffset += iValueLength;
1582
0
                                                                        break;
1583
0
                                                                }
1584
0
                                                                iStringValue = (char*)tvb_get_string_enc(pinfo->pool, tvb, iValueOffset+iValueLength, iStringLength, ENC_UTF_8|ENC_NA);
1585
0
                                                                subval_tree = proto_tree_add_subtree_format(traits_tree, tvb, iValueOffset, -1,
1586
0
                                                                            ett_amf_array_element, &subval_ti, "%s:", iStringValue);
1587
0
                                                                name_tree = proto_tree_add_subtree_format(subval_tree, tvb,
1588
0
                                                                                              iValueOffset,
1589
0
                                                                                              iValueLength+iStringLength,
1590
0
                                                                                              ett_amf_string, NULL, "Member name: %s",
1591
0
                                                                                              iStringValue);
1592
0
                                                                proto_tree_add_uint(name_tree, hf_amf_membernamelength, tvb, iValueOffset, iValueLength, iStringLength);
1593
0
                                                                iValueOffset += iValueLength;
1594
0
                                                                proto_tree_add_string(name_tree, hf_amf_membername, tvb, iValueOffset, iStringLength, iStringValue);
1595
0
                                                                iValueOffset += iStringLength;
1596
0
                                                        } else {
1597
                                                                /* the upper 28 bits of the integer value are a string reference index */
1598
0
                                                                subval_tree = proto_tree_add_subtree_format(traits_tree, tvb, iValueOffset, iValueLength,
1599
0
                                                                                    ett_amf_array_element, &subval_ti, "Reference %u:", iIntegerValue >> 1);
1600
0
                                                                proto_tree_add_uint(subval_tree, hf_amf_string_reference, tvb, iValueOffset, iValueLength, iIntegerValue >> 1);
1601
0
                                                                iValueOffset += iValueLength;
1602
0
                                                        }
1603
1604
                                                        /* Fetch the value */
1605
0
                                                        iValueOffset = dissect_amf3_value_type(tvb, pinfo, iValueOffset, subval_tree, subval_ti);
1606
0
                                                        proto_item_set_end(subval_ti, tvb, iValueOffset);
1607
0
                                                }
1608
0
                                        }
1609
0
                                        proto_item_set_end(traits_ti, tvb, iValueOffset);
1610
0
                                }
1611
0
                        } else {
1612
                                /*
1613
                                 * U29O-traits-ref; the upper 27 bits of
1614
                                 * the integer value are a traits reference
1615
                                 * index.
1616
                                 */
1617
0
                                proto_tree_add_uint(val_tree, hf_amf_trait_reference, tvb, iValueOffset, iValueLength, iIntegerValue >> 2);
1618
0
                                iValueOffset += iValueLength;
1619
0
                        }
1620
0
                } else {
1621
                        /*
1622
                         * U29O-ref; the upper 28 bits of the integer value
1623
                         * are an object reference index.
1624
                         */
1625
0
                        proto_tree_add_uint(val_tree, hf_amf_object_reference, tvb, iValueOffset, iValueLength, iIntegerValue >> 1);
1626
0
                        proto_item_append_text(ti, " reference %u", iIntegerValue >> 1);
1627
0
                        if (parent_ti != NULL)
1628
0
                                proto_item_append_text(parent_ti, " reference %u", iIntegerValue >> 1);
1629
0
                        iValueOffset += iValueLength;
1630
0
                }
1631
0
                break;
1632
0
        case AMF3_XML:
1633
0
                iIntegerValue = amf_get_u29(tvb, iValueOffset, &iValueLength);
1634
0
                if (iIntegerValue & 0x00000001) {
1635
                        /*
1636
                         * The upper 28 bits of the integer value are
1637
                         * a count of the number of bytes in the
1638
                         * XML string.
1639
                         */
1640
0
                        iStringLength = iIntegerValue >> 1;
1641
0
                        proto_tree_add_uint(val_tree, hf_amf_xmllength, tvb, iValueOffset, iValueLength, iStringLength);
1642
0
                        iValueOffset += iValueLength;
1643
0
                        proto_tree_add_item(val_tree, hf_amf_xml, tvb, iValueOffset, iStringLength, ENC_UTF_8);
1644
0
                        iValueOffset += iStringLength;
1645
0
                } else {
1646
                        /* the upper 28 bits of the integer value are a string reference index */
1647
0
                        proto_tree_add_uint(val_tree, hf_amf_object_reference, tvb, iValueOffset, iValueLength, iIntegerValue >> 1);
1648
0
                        proto_item_append_text(ti, " reference %u", iIntegerValue >> 1);
1649
0
                        if (parent_ti != NULL)
1650
0
                                proto_item_append_text(parent_ti, " reference %u", iIntegerValue >> 1);
1651
0
                        iValueOffset += iValueLength;
1652
0
                }
1653
0
                break;
1654
0
        case AMF3_BYTEARRAY:
1655
0
                iIntegerValue = amf_get_u29(tvb, iValueOffset, &iValueLength);
1656
0
                if (iIntegerValue & 0x00000001) {
1657
                        /*
1658
                         * The upper 28 bits of the integer value are
1659
                         * a count of the number of bytes in the
1660
                         * byte array.
1661
                         */
1662
0
                        iArrayLength = iIntegerValue >> 1;
1663
0
                        proto_tree_add_uint(val_tree, hf_amf_bytearraylength, tvb, iValueOffset, iValueLength, iArrayLength);
1664
0
                        iValueOffset += iValueLength;
1665
0
                        iByteArrayValue = (uint8_t *)tvb_memdup(pinfo->pool, tvb, iValueOffset, iArrayLength);
1666
0
                        proto_tree_add_bytes(val_tree, hf_amf_bytearray, tvb, iValueOffset, iArrayLength, iByteArrayValue);
1667
0
                        proto_item_append_text(ti, " %s", bytes_to_str(pinfo->pool, iByteArrayValue, iArrayLength));
1668
0
                        if (parent_ti != NULL)
1669
0
                                proto_item_append_text(parent_ti, " %s", bytes_to_str(pinfo->pool, iByteArrayValue, iArrayLength));
1670
0
                } else {
1671
                        /* the upper 28 bits of the integer value are a object reference index */
1672
0
                        proto_tree_add_uint(val_tree, hf_amf_object_reference, tvb, iValueOffset, iValueLength, iIntegerValue >> 1);
1673
0
                        proto_item_append_text(ti, " reference %u", iIntegerValue >> 1);
1674
0
                        if (parent_ti != NULL)
1675
0
                                proto_item_append_text(parent_ti, " reference %u", iIntegerValue >> 1);
1676
0
                        iValueOffset += iValueLength;
1677
0
                }
1678
0
                break;
1679
0
        default:
1680
                /*
1681
                 * If we can't determine the length, don't carry on;
1682
                 * just skip to the end of the tvbuff.
1683
                 */
1684
0
                iValueOffset = tvb_reported_length(tvb);
1685
0
                break;
1686
0
        }
1687
0
        decrement_dissection_depth(pinfo);
1688
0
        proto_item_set_end(ti, tvb, iValueOffset);
1689
0
        return iValueOffset;
1690
0
}
1691
1692
static int
1693
dissect_rtmpt_body_command(tvbuff_t *tvb, packet_info *pinfo, int offset, proto_tree *rtmpt_tree, bool amf3)
1694
3
{
1695
3
        bool        amf3_encoding = false;
1696
1697
3
        if (amf3) {
1698
                /* Looks like for the AMF3 variants we get a 0 byte here,
1699
                 * followed by AMF0 encoding - I've never seen actual AMF3
1700
                 * encoding used, which is completely different. I speculate
1701
                 * that if the byte is AMF0_AMF3_MARKER then the rest
1702
                 * will be in AMF3. For now, assume AMF0 only. */
1703
0
                offset++;
1704
0
        }
1705
1706
14
        while (tvb_reported_length_remaining(tvb, offset) > 0)
1707
11
        {
1708
11
                if (amf3_encoding)
1709
0
                        offset = dissect_amf3_value_type(tvb, pinfo, offset, rtmpt_tree, NULL);
1710
11
                else
1711
11
                        offset = dissect_amf0_value_type(tvb, pinfo, offset, rtmpt_tree, &amf3_encoding, NULL);
1712
11
        }
1713
3
        return offset;
1714
3
}
1715
1716
static void
1717
dissect_rtmpt_body_audio(tvbuff_t *tvb, int offset, proto_tree *rtmpt_tree)
1718
71
{
1719
71
        uint8_t     iCtl;
1720
71
        uint8_t     iAudioMultitrackCtl;
1721
71
        uint8_t     iAudioTrackId;
1722
71
        uint32_t    iAudioTrackLength;
1723
71
        proto_item *ai;
1724
71
        proto_tree *at;
1725
71
        bool        isAudioMultitrack = false;
1726
71
        bool        isOneTrack = false;
1727
71
        bool        isManyTracksManyCodecs = false;
1728
71
        bool        processAudioBody = true;
1729
1730
71
        iCtl = tvb_get_uint8(tvb, offset);
1731
71
        if ((iCtl & RTMPT_IS_EX_AUDIO_HEADER) == RTMPT_IS_EX_AUDIO_HEADER) {
1732
58
                ai = proto_tree_add_uint_format(rtmpt_tree, hf_rtmpt_audio_packet_type, tvb, offset, 1, iCtl,
1733
58
                        "Control: 0x%02x (%s)", iCtl,
1734
58
                        val_to_str_const((iCtl & 0xf), rtmpt_audio_packet_types, "Reserved audio packet type"));
1735
58
                at = proto_item_add_subtree(ai, ett_rtmpt_audio_control);
1736
1737
58
                proto_tree_add_uint(at, hf_rtmpt_audio_is_ex_header, tvb, offset, 1, iCtl);
1738
58
                proto_tree_add_uint(at, hf_rtmpt_audio_packet_type, tvb, offset, 1, iCtl);
1739
58
                offset += 1;
1740
1741
58
                isAudioMultitrack = (iCtl & 0xf) == RTMPT_IS_AUDIO_MULTITRACK;
1742
58
                if (isAudioMultitrack) {
1743
0
                        iAudioMultitrackCtl = tvb_get_uint8(tvb, offset);
1744
0
                        ai = proto_tree_add_uint_format(rtmpt_tree, hf_rtmpt_audio_multitrack_control, tvb, offset, 1, iAudioMultitrackCtl,
1745
0
                                "Audio Multitrack Control: 0x%02x (%s %s)", iAudioMultitrackCtl,
1746
0
                                val_to_str_const((iAudioMultitrackCtl & 0x0f), rtmpt_av_multitrack_types, "Reserved av multitrack type"),
1747
0
                                val_to_str_const((iAudioMultitrackCtl & 0xf0) >> 4, rtmpt_audio_packet_types, "Reserved audio packet type"));
1748
0
                        at = proto_item_add_subtree(ai, ett_rtmpt_audio_multitrack_control);
1749
0
                        proto_tree_add_uint(at, hf_rtmpt_audio_multitrack_packet_type, tvb, offset, 1, iAudioMultitrackCtl);
1750
0
                        proto_tree_add_uint(at, hf_rtmpt_audio_multitrack_type, tvb, offset, 1, iAudioMultitrackCtl);
1751
0
                        offset += 1;
1752
1753
0
                        isOneTrack = (iAudioMultitrackCtl & 0x0f) == RTMPT_IS_ONETRACK;
1754
0
                        isManyTracksManyCodecs = (iAudioMultitrackCtl & 0x0f) == RTMPT_IS_MANYTRACKSMANYCODECS;
1755
0
                        if (!isManyTracksManyCodecs) {
1756
0
                                proto_tree_add_item(rtmpt_tree, hf_rtmpt_audio_fourcc, tvb, offset, 4, ENC_ASCII);
1757
0
                                offset += 4;
1758
0
                        }
1759
58
                } else {
1760
58
                        proto_tree_add_item(rtmpt_tree, hf_rtmpt_audio_fourcc, tvb, offset, 4, ENC_ASCII);
1761
58
                        offset += 4;
1762
58
                }
1763
1764
61
                while(processAudioBody && tvb_reported_length_remaining(tvb, offset) > 0) {
1765
3
                        if (isAudioMultitrack) {
1766
0
                                iAudioTrackId = tvb_get_uint8(tvb, offset);
1767
0
                                ai = proto_tree_add_uint(rtmpt_tree, hf_rtmpt_audio_track_id, tvb, offset, 1, iAudioTrackId);
1768
0
                                at = proto_item_add_subtree(ai, ett_rtmpt_audio_multitrack_track);
1769
0
                                offset += 1;
1770
0
                                if (isManyTracksManyCodecs) {
1771
0
                                        proto_tree_add_item(at, hf_rtmpt_audio_fourcc, tvb, offset, 4, ENC_ASCII);
1772
0
                                        offset += 4;
1773
0
                                }
1774
0
                                if (!isOneTrack) {
1775
0
                                        iAudioTrackLength = tvb_get_uint24(tvb, offset, ENC_BIG_ENDIAN);
1776
0
                                        proto_tree_add_uint(at, hf_rtmpt_audio_track_length, tvb, offset, 3, iAudioTrackLength);
1777
0
                                        offset += 3;
1778
0
                                        proto_tree_add_item(at, hf_rtmpt_audio_data, tvb, offset, iAudioTrackLength, ENC_NA);
1779
0
                                        offset += iAudioTrackLength;
1780
0
                                } else {
1781
0
                                        proto_tree_add_item(at, hf_rtmpt_audio_data, tvb, offset, -1, ENC_NA);
1782
0
                                        processAudioBody = false;
1783
0
                                }
1784
3
                        } else {
1785
3
                                proto_tree_add_item(rtmpt_tree, hf_rtmpt_audio_data, tvb, offset, -1, ENC_NA);
1786
3
                                processAudioBody = false;
1787
3
                        }
1788
3
                }
1789
58
        } else {
1790
13
                ai = proto_tree_add_uint_format(rtmpt_tree, hf_rtmpt_audio_control, tvb, offset, 1, iCtl,
1791
13
                        "Control: 0x%02x (%s %s %s %s)", iCtl,
1792
13
                        val_to_str_const((iCtl & 0xf0) >> 4, rtmpt_audio_codecs, "Unknown codec"),
1793
13
                        val_to_str_const((iCtl & 0x0c) >> 2, rtmpt_audio_rates, "Unknown rate"),
1794
13
                        val_to_str_const((iCtl & 0x02) >> 1, rtmpt_audio_sizes, "Unknown sample size"),
1795
13
                        val_to_str_const(iCtl & 0x01, rtmpt_audio_types, "Unknown channel count"));
1796
1797
13
                at = proto_item_add_subtree(ai, ett_rtmpt_audio_control);
1798
13
                proto_tree_add_uint(at, hf_rtmpt_audio_format, tvb, offset, 1, iCtl);
1799
13
                proto_tree_add_uint(at, hf_rtmpt_audio_rate, tvb, offset, 1, iCtl);
1800
13
                proto_tree_add_uint(at, hf_rtmpt_audio_size, tvb, offset, 1, iCtl);
1801
13
                proto_tree_add_uint(at, hf_rtmpt_audio_type, tvb, offset, 1, iCtl);
1802
13
                proto_tree_add_item(rtmpt_tree, hf_rtmpt_audio_data, tvb, offset + 1, -1, ENC_NA);
1803
13
        }
1804
71
}
1805
1806
static void
1807
dissect_rtmpt_body_video(tvbuff_t *tvb, int offset, proto_tree *rtmpt_tree)
1808
33
{
1809
33
        uint8_t     iCtl;
1810
33
        uint8_t     iMultitrackCtl;
1811
33
        uint8_t     iVideoFrameType;
1812
33
        uint8_t     iVideoPacketType;
1813
33
        uint8_t     iAvMultitrackType;
1814
33
        uint32_t    iVideoTrackLength;
1815
33
        proto_item *vi;
1816
33
        proto_tree *vt;
1817
33
        bool        isVideoMultitrack = false;
1818
33
        bool        isOneTrack = false;
1819
33
        bool        isManyTracksManyCodecs = false;
1820
33
        bool        processVideoBody = true;
1821
1822
33
        iCtl = tvb_get_uint8(tvb, offset);
1823
33
        iVideoFrameType = (iCtl & 0x70) >> 4;
1824
1825
        /*
1826
         * https://veovera.org/docs/enhanced/enhanced-rtmp-v2.pdf
1827
         */
1828
33
        if (iCtl & RTMPT_IS_EX_VIDEO_HEADER) {
1829
25
                iVideoPacketType = iCtl & 0x0f;
1830
25
                isVideoMultitrack = iVideoPacketType == RTMPT_IS_VIDEO_MULTITRACK;
1831
1832
25
                vi = proto_tree_add_uint_format(rtmpt_tree, hf_rtmpt_video_control, tvb, offset, 1, iCtl,
1833
25
                                "Control: 0x%02x (%s %s)", iCtl,
1834
25
                                val_to_str_const(iVideoFrameType, rtmpt_video_types, "Reserved frame type"),
1835
25
                                val_to_str_const(iVideoPacketType, rtmpt_video_packet_types, "Reserved packet type"));
1836
25
                vt = proto_item_add_subtree(vi, ett_rtmpt_video_control);
1837
25
                proto_tree_add_item(vt, hf_rtmpt_video_is_ex_header, tvb, offset, 1, ENC_BIG_ENDIAN);
1838
25
                proto_tree_add_item(vt, hf_rtmpt_video_type, tvb, offset, 1, ENC_BIG_ENDIAN);
1839
25
                proto_tree_add_item(vt, hf_rtmpt_video_packet_type, tvb, offset, 1, ENC_BIG_ENDIAN);
1840
25
                offset += 1;
1841
1842
25
                if (iVideoPacketType != RTMPT_IS_PACKET_TYPE_METADATA && iVideoFrameType == RTMPT_IS_FRAME_TYPE_COMMAND) {
1843
2
                        proto_tree_add_item(vt, hf_rtmpt_video_command, tvb, offset, 1, ENC_BIG_ENDIAN);
1844
2
                        offset += 1;
1845
2
                        processVideoBody = false;
1846
23
                } else if (isVideoMultitrack) {
1847
0
                        iMultitrackCtl = tvb_get_uint8(tvb, offset);
1848
0
                        iAvMultitrackType = (iMultitrackCtl >> 4) & 0xf;
1849
0
                        vi = proto_tree_add_uint_format(rtmpt_tree, hf_rtmpt_video_multitrack_control, tvb, offset, 1, iMultitrackCtl,
1850
0
                                "Video Multitrack Control: 0x%02x (%s %s)", iMultitrackCtl,
1851
0
                                val_to_str_const(iAvMultitrackType, rtmpt_av_multitrack_types, "Reserved av multitrack type"),
1852
0
                                val_to_str_const(iMultitrackCtl & 0xf, rtmpt_video_packet_types, "Reserved video packet type"));
1853
0
                        vt = proto_item_add_subtree(vi, ett_rtmpt_video_multitrack_control);
1854
0
                        proto_tree_add_item(vt, hf_rtmpt_video_multitrack_type, tvb, offset, 1, ENC_BIG_ENDIAN);
1855
0
                        proto_tree_add_item(vt, hf_rtmpt_video_multitrack_packet_type, tvb, offset, 1, ENC_BIG_ENDIAN);
1856
0
                        offset += 1;
1857
1858
0
                        isOneTrack = (iAvMultitrackType & 0x0f) == RTMPT_IS_ONETRACK;
1859
0
                        isManyTracksManyCodecs = (iAvMultitrackType & 0x0f) == RTMPT_IS_MANYTRACKSMANYCODECS;
1860
0
                        if (!isManyTracksManyCodecs) {
1861
0
                                proto_tree_add_item(rtmpt_tree, hf_rtmpt_video_fourcc, tvb, offset, 4, ENC_ASCII);
1862
0
                                offset += 4;
1863
0
                        }
1864
23
                } else {
1865
23
                        proto_tree_add_item(rtmpt_tree, hf_rtmpt_video_fourcc, tvb, offset, 4, ENC_ASCII);
1866
23
                        offset += 4;
1867
23
                }
1868
1869
48
                while (processVideoBody && tvb_reported_length_remaining(tvb, offset) > 0){
1870
23
                        if (isVideoMultitrack) {
1871
0
                                vi = proto_tree_add_item(rtmpt_tree, hf_rtmpt_video_track_id, tvb, offset, 1, ENC_BIG_ENDIAN);
1872
0
                                vt = proto_item_add_subtree(vi, ett_rtmpt_video_multitrack_track);
1873
0
                                offset += 1;
1874
0
                                if (isManyTracksManyCodecs) {
1875
0
                                        proto_tree_add_item(vt, hf_rtmpt_video_fourcc, tvb, offset, 4, ENC_ASCII);
1876
0
                                        offset += 4;
1877
0
                                }
1878
0
                                if (!isOneTrack) {
1879
0
                                        iVideoTrackLength = tvb_get_uint24(tvb, offset, ENC_BIG_ENDIAN);
1880
0
                                        proto_tree_add_item(vt, hf_rtmpt_video_track_length, tvb, offset, 3, ENC_BIG_ENDIAN);
1881
0
                                        offset += 3;
1882
0
                                        proto_tree_add_item(vt, hf_rtmpt_video_data, tvb, offset, iVideoTrackLength, ENC_NA);
1883
0
                                        offset += iVideoTrackLength;
1884
0
                                } else {
1885
0
                                        proto_tree_add_item(vt, hf_rtmpt_video_data, tvb, offset, -1, ENC_NA);
1886
0
                                        processVideoBody = false;
1887
0
                                }
1888
23
                        } else {
1889
23
                                proto_tree_add_item(rtmpt_tree, hf_rtmpt_video_data, tvb, offset, -1, ENC_NA);
1890
23
                                processVideoBody = false;
1891
23
                        }
1892
23
                }
1893
25
        } else {
1894
8
            vi = proto_tree_add_uint_format(rtmpt_tree, hf_rtmpt_video_control, tvb, offset, 1, iCtl,
1895
8
                                            "Control: 0x%02x (%s %s)", iCtl,
1896
8
                                            val_to_str_const(iVideoFrameType, rtmpt_video_types, "Reserved frame type"),
1897
8
                                            val_to_str_const(iCtl & 0x0f, rtmpt_video_codecs, "Unknown codec"));
1898
1899
8
            vt = proto_item_add_subtree(vi, ett_rtmpt_video_control);
1900
8
            proto_tree_add_item(vt, hf_rtmpt_video_type, tvb, offset, 1, ENC_BIG_ENDIAN);
1901
8
            proto_tree_add_item(vt, hf_rtmpt_video_format, tvb, offset, 1, ENC_BIG_ENDIAN);
1902
8
            offset += 1;
1903
1904
8
            if (iVideoFrameType == RTMPT_IS_FRAME_TYPE_COMMAND) {
1905
0
                    proto_tree_add_item(vt, hf_rtmpt_video_command, tvb, offset, 1, ENC_BIG_ENDIAN);
1906
8
            } else {
1907
8
                    proto_tree_add_item(rtmpt_tree, hf_rtmpt_video_data, tvb, offset, -1, ENC_NA);
1908
8
            }
1909
8
        }
1910
33
}
1911
1912
static void
1913
dissect_rtmpt_body_aggregate(tvbuff_t *tvb, packet_info *pinfo, int offset, proto_tree *rtmpt_tree)
1914
0
{
1915
0
        proto_tree *tag_tree;
1916
1917
0
        proto_tree *data_tree;
1918
0
        tvbuff_t   *tag_tvb;
1919
1920
0
        while (tvb_reported_length_remaining(tvb, offset) > 0) {
1921
0
                uint8_t iTagType;
1922
0
                unsigned  iDataSize;
1923
1924
0
                iTagType  = tvb_get_uint8(tvb, offset + 0);
1925
0
                iDataSize = tvb_get_ntoh24(tvb, offset + 1);
1926
1927
0
                tag_tree  = proto_tree_add_subtree(rtmpt_tree, tvb, offset, 11+iDataSize+4, ett_rtmpt_tag, NULL,
1928
0
                                               val_to_str_const(iTagType, rtmpt_tag_vals, "Unknown Tag"));
1929
0
                proto_tree_add_item(tag_tree, hf_rtmpt_tag_type, tvb, offset+0, 1, ENC_BIG_ENDIAN);
1930
0
                proto_tree_add_item(tag_tree, hf_rtmpt_tag_datasize, tvb, offset+1, 3, ENC_BIG_ENDIAN);
1931
0
                proto_tree_add_item(tag_tree, hf_rtmpt_tag_timestamp, tvb, offset+4, 3, ENC_BIG_ENDIAN);
1932
0
                proto_tree_add_item(tag_tree, hf_rtmpt_tag_ets, tvb, offset+7, 1, ENC_BIG_ENDIAN);
1933
0
                proto_tree_add_item(tag_tree, hf_rtmpt_tag_streamid, tvb, offset+8, 3, ENC_BIG_ENDIAN);
1934
1935
0
                data_tree = proto_tree_add_subtree(tag_tree, tvb, offset+11, iDataSize, ett_rtmpt_tag_data, NULL, "Data");
1936
1937
                /* Dissect each tag only within its own iDataSize bytes. */
1938
0
                tag_tvb = tvb_new_subset_length(tvb, offset + 11, iDataSize);
1939
1940
0
                switch (iTagType) {
1941
0
                case 8:
1942
0
                        dissect_rtmpt_body_audio(tag_tvb, 0, data_tree);
1943
0
                        break;
1944
0
                case 9:
1945
0
                        dissect_rtmpt_body_video(tag_tvb, 0, data_tree);
1946
0
                        break;
1947
0
                case 18:
1948
0
                        dissect_rtmpt_body_command(tag_tvb, pinfo, 0, data_tree, false);
1949
0
                        break;
1950
0
                default:
1951
0
                        break;
1952
0
                }
1953
1954
0
                proto_tree_add_item(tag_tree, hf_rtmpt_tag_tagsize, tvb, offset+11+iDataSize, 4, ENC_BIG_ENDIAN);
1955
0
                offset += 11 + iDataSize + 4;
1956
0
        }
1957
0
}
1958
1959
/* The main dissector for unchunked packets */
1960
1961
static void
1962
dissect_rtmpt(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, rtmpt_conv_t *rconv, int cdir, rtmpt_packet_t *tp)
1963
2.92k
{
1964
2.92k
        int         offset         = 0;
1965
1966
2.92k
        char       *sDesc          = NULL;
1967
2.92k
        bool        deschasopcode  = false;
1968
2.92k
        bool        haveETS        = false;
1969
2.92k
        uint32_t    iBodyOffset    = 0;
1970
2.92k
        uint32_t    iBodyRemain    = 0;
1971
1972
2.92k
        col_set_str(pinfo->cinfo, COL_PROTOCOL, "RTMP");
1973
1974
2.92k
        ws_debug("Dissect: frame=%u visited=%d len=%d tree=%p",
1975
2.92k
                 pinfo->num, pinfo->fd->visited,
1976
2.92k
                 tvb_reported_length_remaining(tvb, offset), tree);
1977
1978
        /* Clear any previous data in Info column (RTMP packets are protected by a "fence") */
1979
2.92k
        col_clear(pinfo->cinfo, COL_INFO);
1980
1981
2.92k
        if (tvb_reported_length_remaining(tvb, offset) < 1) return;
1982
1983
2.92k
        if (tp->id <= RTMPT_ID_MAX) {
1984
2.92k
                if (tp->fmt < 3
1985
807
                    && tvb_reported_length_remaining(tvb, offset) >= tp->bhlen+3U
1986
807
                    && tvb_get_ntoh24(tvb, offset+tp->bhlen) == 0xffffff) {
1987
11
                        haveETS = true;
1988
11
                }
1989
1990
2.92k
                iBodyOffset = offset + tp->bhlen + tp->mhlen;
1991
2.92k
                iBodyRemain = tvb_reported_length_remaining(tvb, iBodyOffset);
1992
1993
2.92k
                if (tp->cmd == RTMPT_TYPE_CHUNK_SIZE && tp->len >= 4 && iBodyRemain >= 4) {
1994
97
                        int32_t newchunksize = tvb_get_ntohl(tvb, iBodyOffset);
1995
97
                        if (newchunksize > 0) {
1996
46
                                wmem_tree_insert32(rconv->chunksize[cdir], tp->lastseq, GINT_TO_POINTER(newchunksize));
1997
46
                        }
1998
97
                }
1999
2000
2.92k
                if (tp->cmd == RTMPT_TYPE_COMMAND_AMF0 || tp->cmd == RTMPT_TYPE_COMMAND_AMF3 ||
2001
2.92k
                    tp->cmd == RTMPT_TYPE_DATA_AMF0 || tp->cmd == RTMPT_TYPE_DATA_AMF3) {
2002
6
                        uint32_t soff = 0;
2003
6
                        if (tp->cmd == RTMPT_TYPE_COMMAND_AMF3 || tp->cmd == RTMPT_TYPE_DATA_AMF3) {
2004
1
                                soff = 1;
2005
1
                        }
2006
6
                        tp->txid = rtmpt_get_amf_txid(tvb, iBodyOffset+soff, tree);
2007
6
                        if (tp->txid != 0 && !PINFO_FD_VISITED(pinfo)) {
2008
0
                                ws_debug("got txid=%d", tp->txid);
2009
0
                                wmem_tree_insert32(rconv->txids[cdir], tp->txid, GINT_TO_POINTER(pinfo->num));
2010
0
                        }
2011
6
                }
2012
2.92k
        } else if (tp->id == RTMPT_TYPE_HANDSHAKE_2 || tp->id == RTMPT_TYPE_HANDSHAKE_3) {
2013
0
                uint32_t newchunksize = RTMPT_INITIAL_CHUNK_SIZE;
2014
0
                wmem_tree_insert32(rconv->chunksize[cdir], tp->lastseq, GINT_TO_POINTER(newchunksize));
2015
0
        }
2016
2017
2.92k
        if (tp->id <= RTMPT_ID_MAX)
2018
2.92k
        {
2019
2.92k
                sDesc = rtmpt_get_packet_desc(pinfo->pool, tvb, iBodyOffset, tree, iBodyRemain, rconv, cdir, tp, &deschasopcode);
2020
2.92k
        }
2021
2022
2.92k
        if (tp->id>RTMPT_ID_MAX) {
2023
0
                col_append_sep_str(pinfo->cinfo, COL_INFO, "|",
2024
0
                                val_to_str(pinfo->pool, tp->id, rtmpt_handshake_vals, "Unknown (0x%01x)"));
2025
0
                col_set_fence(pinfo->cinfo, COL_INFO);
2026
2.92k
        } else if (sDesc) {
2027
222
                col_append_sep_str(pinfo->cinfo, COL_INFO, "|", sDesc);
2028
222
                col_set_fence(pinfo->cinfo, COL_INFO);
2029
2.70k
        } else {
2030
2.70k
                col_append_sep_str(pinfo->cinfo, COL_INFO, "|",
2031
2.70k
                                val_to_str(pinfo->pool, tp->cmd, rtmpt_opcode_vals, "Unknown (0x%01x)"));
2032
2.70k
                col_set_fence(pinfo->cinfo, COL_INFO);
2033
2.70k
        }
2034
2035
2.92k
        if (tree)
2036
2.92k
        {
2037
2.92k
                proto_tree *rtmpt_tree     = NULL;
2038
2.92k
                proto_tree *rtmptroot_tree = NULL;
2039
2.92k
                proto_item *ti;
2040
2.92k
                ti = proto_tree_add_item(tree, proto_rtmpt, tvb, offset, -1, ENC_NA);
2041
2042
2.92k
                if (tp->id > RTMPT_ID_MAX) {
2043
0
                        char* str_handshake = val_to_str(pinfo->pool, tp->id, rtmpt_handshake_vals, "Unknown (0x%01x)");
2044
2045
                        /* Dissect handshake */
2046
0
                        proto_item_append_text(ti, " (%s)", str_handshake);
2047
0
                        rtmptroot_tree = proto_item_add_subtree(ti, ett_rtmpt);
2048
0
                        rtmpt_tree = proto_tree_add_subtree(rtmptroot_tree, tvb, offset, -1, ett_rtmpt_handshake, NULL, str_handshake);
2049
2050
0
                        if (tp->id == RTMPT_TYPE_HANDSHAKE_1)
2051
0
                        {
2052
0
                                proto_tree_add_item(rtmpt_tree, hf_rtmpt_handshake_c0, tvb, 0, 1, ENC_NA);
2053
0
                                proto_tree_add_item(rtmpt_tree, hf_rtmpt_handshake_c1, tvb, 1, 1536, ENC_NA);
2054
0
                        }
2055
0
                        else if (tp->id == RTMPT_TYPE_HANDSHAKE_2)
2056
0
                        {
2057
0
                                proto_tree_add_item(rtmpt_tree, hf_rtmpt_handshake_s0, tvb, 0, 1, ENC_NA);
2058
0
                                proto_tree_add_item(rtmpt_tree, hf_rtmpt_handshake_s1, tvb, 1, 1536, ENC_NA);
2059
0
                                proto_tree_add_item(rtmpt_tree, hf_rtmpt_handshake_s2, tvb, 1537, 1536, ENC_NA);
2060
0
                        }
2061
0
                        else if (tp->id == RTMPT_TYPE_HANDSHAKE_3)
2062
0
                        {
2063
0
                                proto_tree_add_item(rtmpt_tree, hf_rtmpt_handshake_c2, tvb, 0, 1536, ENC_NA);
2064
0
                        }
2065
2066
0
                        return;
2067
0
                }
2068
2069
2.92k
                if (sDesc && deschasopcode) {
2070
219
                        proto_item_append_text(ti, " (%s)", sDesc);
2071
2.70k
                } else if (sDesc) {
2072
3
                        proto_item_append_text(ti, " (%s %s)",
2073
3
                                               val_to_str(pinfo->pool, tp->cmd, rtmpt_opcode_vals, "Unknown (0x%01x)"), sDesc);
2074
2.70k
                } else {
2075
2.70k
                        proto_item_append_text(ti, " (%s)",
2076
2.70k
                                               val_to_str(pinfo->pool, tp->cmd, rtmpt_opcode_vals, "Unknown (0x%01x)"));
2077
2.70k
                }
2078
2.92k
                rtmptroot_tree = proto_item_add_subtree(ti, ett_rtmpt);
2079
2080
                /* Function call/response matching */
2081
2.92k
                if (tp->otherframe != 0) {
2082
0
                        proto_tree_add_uint(rtmptroot_tree,
2083
0
                                            tp->isresponse ? hf_rtmpt_function_response : hf_rtmpt_function_call,
2084
0
                                            tvb, offset, tp->bhlen+tp->mhlen+tp->len,
2085
0
                                            tp->otherframe);
2086
0
                }
2087
2088
                /* Dissect header fields */
2089
2.92k
                rtmpt_tree = proto_tree_add_subtree(rtmptroot_tree, tvb, offset, tp->bhlen+tp->mhlen, ett_rtmpt_header, NULL, RTMPT_TEXT_RTMP_HEADER);
2090
/*                proto_item_append_text(ti, " (%s)", val_to_str(pinfo->pool, tp->cmd, rtmpt_opcode_vals, "Unknown (0x%01x)")); */
2091
2092
2.92k
                if (tp->fmt <= 3) proto_tree_add_item(rtmpt_tree, hf_rtmpt_header_format, tvb, offset + 0, 1, ENC_BIG_ENDIAN);
2093
2.92k
                if (tp->fmt <= 3) proto_tree_add_uint(rtmpt_tree, hf_rtmpt_header_csid, tvb, offset + 0, tp->bhlen, tp->id);
2094
2.92k
                if (tp->fmt <= 2) {
2095
805
                        if (tp->fmt > 0) {
2096
289
                                proto_tree_add_item(rtmpt_tree, hf_rtmpt_header_timestamp_delta, tvb, offset + tp->bhlen, 3, ENC_BIG_ENDIAN);
2097
516
                        } else {
2098
516
                                proto_tree_add_item(rtmpt_tree, hf_rtmpt_header_timestamp, tvb, offset + tp->bhlen, 3, ENC_BIG_ENDIAN);
2099
516
                        }
2100
805
                        if (haveETS) {
2101
11
                                proto_tree_add_item(rtmpt_tree, hf_rtmpt_header_ets, tvb, offset + tp->bhlen + tp->mhlen - 4, 4, ENC_BIG_ENDIAN);
2102
11
                        }
2103
805
                }
2104
2.92k
                if ((tp->fmt>0 && !haveETS) || tp->fmt == 3) {
2105
2.39k
                        proto_tree_add_uint_format_value(rtmpt_tree, hf_rtmpt_header_timestamp, tvb, offset + tp->bhlen, 0, tp->ts, "%d (calculated)", tp->ts);
2106
2.39k
                }
2107
2.92k
                if (tp->fmt <= 1) proto_tree_add_item(rtmpt_tree, hf_rtmpt_header_body_size, tvb, offset + tp->bhlen + 3, 3, ENC_BIG_ENDIAN);
2108
2.92k
                if (tp->fmt <= 1) proto_tree_add_item(rtmpt_tree, hf_rtmpt_header_typeid, tvb, offset + tp->bhlen + 6, 1, ENC_BIG_ENDIAN);
2109
2.92k
                if (tp->fmt <= 0) proto_tree_add_item(rtmpt_tree, hf_rtmpt_header_streamid, tvb, offset + tp->bhlen + 7, 4, ENC_LITTLE_ENDIAN);
2110
2111
                /* Dissect body */
2112
2.92k
                if (tp->len == 0) return;
2113
2.02k
                offset = iBodyOffset;
2114
2115
2.02k
                rtmpt_tree = proto_tree_add_subtree(rtmptroot_tree, tvb, offset, -1, ett_rtmpt_body, NULL, RTMPT_TEXT_RTMP_BODY);
2116
2117
2.02k
                switch (tp->cmd) {
2118
99
                case RTMPT_TYPE_CHUNK_SIZE:
2119
118
                case RTMPT_TYPE_ABORT_MESSAGE:
2120
119
                case RTMPT_TYPE_ACKNOWLEDGEMENT:
2121
125
                case RTMPT_TYPE_UCM:
2122
200
                case RTMPT_TYPE_WINDOW:
2123
223
                case RTMPT_TYPE_PEER_BANDWIDTH:
2124
223
                        dissect_rtmpt_body_scm(tvb, offset, rtmpt_tree, tp->cmd);
2125
223
                        break;
2126
1
                case RTMPT_TYPE_COMMAND_AMF0:
2127
3
                case RTMPT_TYPE_DATA_AMF0:
2128
3
                        dissect_rtmpt_body_command(tvb, pinfo, offset, rtmpt_tree, false);
2129
3
                        break;
2130
0
                case RTMPT_TYPE_COMMAND_AMF3:
2131
0
                case RTMPT_TYPE_DATA_AMF3:
2132
0
                        dissect_rtmpt_body_command(tvb, pinfo, offset, rtmpt_tree, true);
2133
0
                        break;
2134
71
                case RTMPT_TYPE_AUDIO_DATA:
2135
71
                        dissect_rtmpt_body_audio(tvb, offset, rtmpt_tree);
2136
71
                        break;
2137
33
                case RTMPT_TYPE_VIDEO_DATA:
2138
33
                        dissect_rtmpt_body_video(tvb, offset, rtmpt_tree);
2139
33
                        break;
2140
0
                case RTMPT_TYPE_AGGREGATE:
2141
0
                        dissect_rtmpt_body_aggregate(tvb, pinfo, offset, rtmpt_tree);
2142
0
                        break;
2143
2.02k
                }
2144
2.02k
        }
2145
2.92k
}
2146
2147
/* Unchunk a data stream into individual RTMP packets */
2148
2149
static void
2150
dissect_rtmpt_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, rtmpt_conv_t *rconv, int cdir, uint32_t seq, uint32_t lastackseq)
2151
172
{
2152
172
        int     offset = 0;
2153
172
        int     remain;
2154
172
        int     want;
2155
2156
172
        uint8_t header_type;
2157
172
        int     basic_hlen;
2158
172
        int     message_hlen;
2159
2160
172
        uint32_t id;
2161
172
        uint32_t ts     = 0;
2162
172
        uint32_t tsd    = 0;
2163
172
        int     body_len;
2164
172
        uint8_t cmd;
2165
172
        uint32_t src;
2166
172
        int     chunk_size;
2167
2168
172
        rtmpt_frag_t   *tf;
2169
172
        rtmpt_id_t     *ti;
2170
172
        rtmpt_packet_t *tp;
2171
172
        tvbuff_t       *pktbuf;
2172
2173
172
        remain = tvb_reported_length(tvb);
2174
172
        if (!remain)
2175
0
                return;
2176
2177
172
        ws_debug("Segment: cdir=%d seq=%d-%d", cdir, seq, seq+remain-1);
2178
2179
172
        if (pinfo->fd->visited) {
2180
                /* Already done the work, so just dump the existing state */
2181
                /* XXX: If there's bogus sequence numbers and the
2182
                 * tcp.analyze_sequence_numbers pref is true, we can't actually
2183
                 * assume that we processed this frame the first time around,
2184
                 * since the TCP dissector might not have given it to us.
2185
                 */
2186
0
                wmem_stack_t *packets;
2187
2188
                /* List all RTMP packets terminating in this TCP segment, from end to beginning */
2189
2190
0
                packets = wmem_stack_new(pinfo->pool);
2191
0
                wmem_stack_push(packets, 0);
2192
2193
0
                tp = (rtmpt_packet_t *)wmem_tree_lookup32_le(rconv->packets[cdir], seq+remain-1);
2194
0
                while (tp && GE_SEQ(tp->lastseq, seq)) {
2195
                        /* Sequence numbers can wrap around (especially with
2196
                         * tcp.relative_sequence_numbers false), so use the
2197
                         * wrap around aware comparison from packet-tcp.h
2198
                         */
2199
0
                        wmem_stack_push(packets, tp);
2200
0
                        if (tp->seq == 0) {
2201
                                // reached first segment.
2202
                                /* XXX: Assuming tcp.relative_sequence_numbers
2203
                                 * is true, that is, since on TCP we just
2204
                                 * reuse the sequence numbers from tcpinfo.
2205
                                 */
2206
0
                                break;
2207
0
                        }
2208
0
                        if (tp->seq > tp->lastseq) {
2209
                                /* XXX: There are some problems with sequence
2210
                                 * numbers that wraparound in the middle of
2211
                                 * a segment and using wmem_tree_lookup32_le
2212
                                 * below. Break out here to guarantee that there
2213
                                 * is a limit to the tree lookups and we don't
2214
                                 * have infinite loops. Really a lot of this
2215
                                 * code should be rewritten to deal with
2216
                                 * sequence numbers that wrap around (especially
2217
                                 * (SYN packets with altered sequence numbers
2218
                                 * and out of order packets.)
2219
                                 */
2220
0
                                break;
2221
0
                        }
2222
0
                        tp = (rtmpt_packet_t *)wmem_tree_lookup32_le(rconv->packets[cdir], tp->seq-1);
2223
0
                }
2224
2225
                /* Dissect the generated list in reverse order (beginning to end) */
2226
2227
0
                while ((tp=(rtmpt_packet_t *)wmem_stack_pop(packets)) != NULL) {
2228
0
                        if (tp->resident) {
2229
0
                                pktbuf = tvb_new_child_real_data(tvb, tp->data.p, tp->have, tp->have);
2230
0
                                add_new_data_source(pinfo, pktbuf, "Unchunked RTMP");
2231
0
                        } else {
2232
0
                                pktbuf = tvb_new_subset_length(tvb, tp->data.offset, tp->have);
2233
0
                        }
2234
0
                        dissect_rtmpt(pktbuf, pinfo, tree, rconv, cdir, tp);
2235
0
                }
2236
2237
0
                return;
2238
0
        }
2239
2240
5.02k
        while (remain>0) {
2241
4.90k
                tf = NULL;
2242
4.90k
                ti = NULL;
2243
4.90k
                tp = NULL;
2244
2245
                /* Check for outstanding fragmented headers/chunks first */
2246
2247
4.90k
                if (offset == 0) {
2248
172
                        tf = (rtmpt_frag_t *)wmem_tree_lookup32_le(rconv->frags[cdir], seq+offset-1);
2249
2250
172
                        if (tf) {
2251
                                /* May need to reassemble cross-TCP-segment fragments */
2252
50
                                ws_noisy("  tf seq=%d lseq=%d h=%d l=%d", tf->seq, tf->lastseq, tf->have, tf->len);
2253
50
                                if (tf->have >= tf->len || seq+offset < tf->seq || seq+offset > tf->lastseq+tf->len-tf->have) {
2254
45
                                        tf = NULL;
2255
45
                                } else if (!tf->ishdr) {
2256
5
                                        ti = (rtmpt_id_t *)wmem_tree_lookup32(rconv->ids[cdir], tf->saved.id);
2257
5
                                        if (ti) {
2258
5
                                                tp = (rtmpt_packet_t *)wmem_tree_lookup32_le(ti->packets, seq+offset-1);
2259
5
                                        }
2260
5
                                        if (tp && tp->chunkwant) {
2261
5
                                                goto unchunk;
2262
5
                                        }
2263
0
                                        tf = NULL;
2264
0
                                        ti = NULL;
2265
0
                                        tp = NULL;
2266
0
                                }
2267
2268
45
                                if (tf) {
2269
                                        /* The preceding segment contained an incomplete chunk header */
2270
2271
0
                                        id = tf->saved.d[0];
2272
0
                                        header_type = (id>>6) & 3;
2273
0
                                        basic_hlen = rtmpt_basic_header_length(id);
2274
2275
                                        /* Whether there is an extended timestamp is
2276
                                         * known only once enough of the header is here,
2277
                                         * and then the header wants 4 more bytes.
2278
                                         */
2279
0
                                        while (tf->have < tf->len && remain > 0) {
2280
0
                                                want = tf->len - tf->have;
2281
0
                                                if (remain<want)
2282
0
                                                        want = remain;
2283
2284
0
                                                tvb_memcpy(tvb, tf->saved.d+tf->have, offset, want);
2285
2286
0
                                                if ((header_type < 3) && (tf->have < (basic_hlen+3)) && (tf->have+want >= (basic_hlen+3))) {
2287
0
                                                        if (pntohu24(tf->saved.d+basic_hlen) == 0xffffff) {
2288
0
                                                                tf->len += 4;
2289
0
                                                        }
2290
0
                                                } else if ((header_type == 3) && (tf->have < basic_hlen) && (tf->have+want >= basic_hlen)) {
2291
0
                                                        tf->len += rtmpt_type3_ext_ts_length(rconv, cdir, tf->saved.d, tf->seq);
2292
0
                                                }
2293
2294
0
                                                tf->have += want;
2295
0
                                                tf->lastseq = seq+offset+want-1;
2296
0
                                                remain -= want;
2297
0
                                                offset += want;
2298
0
                                        }
2299
2300
0
                                        if (tf->have < tf->len) {
2301
0
                                                return;
2302
0
                                        }
2303
0
                                }
2304
45
                        }
2305
172
                }
2306
2307
4.89k
                if (!tf) {
2308
                        /* No preceding data, get header data starting at current position */
2309
4.89k
                        id = tvb_get_uint8(tvb, offset);
2310
2311
4.89k
                        if (id == RTMPT_MAGIC && seq+offset == RTMPT_HANDSHAKE_OFFSET_1) {
2312
0
                                header_type = 4;
2313
0
                                basic_hlen = 1;
2314
0
                                message_hlen = 0;
2315
0
                                id = lastackseq == 1 ? RTMPT_TYPE_HANDSHAKE_1 : RTMPT_TYPE_HANDSHAKE_2;
2316
4.89k
                        } else if (seq+offset == RTMPT_HANDSHAKE_OFFSET_2) {
2317
0
                                header_type = 4;
2318
0
                                basic_hlen = 0;
2319
0
                                message_hlen = 0;
2320
0
                                id = RTMPT_TYPE_HANDSHAKE_3;
2321
4.89k
                        } else {
2322
4.89k
                                header_type = (id>>6) & 3;
2323
4.89k
                                basic_hlen = rtmpt_basic_header_length(id);
2324
4.89k
                                message_hlen = rtmpt_message_header_length(id);
2325
2326
4.89k
                                if ((header_type < 3) && (remain >= (basic_hlen+3))) {
2327
1.90k
                                        if (tvb_get_ntoh24(tvb, offset+basic_hlen) == 0xffffff) {
2328
41
                                                message_hlen += 4;
2329
41
                                        }
2330
2.99k
                                } else if ((header_type == 3) && (remain >= basic_hlen)) {
2331
2.98k
                                        message_hlen += rtmpt_type3_ext_ts_length(rconv, cdir, tvb_get_ptr(tvb, offset, basic_hlen), seq+offset);
2332
2.98k
                                }
2333
2334
4.89k
                                if (remain < (basic_hlen+message_hlen)) {
2335
                                        /* Ran out of packet mid-header, save and try again next time */
2336
48
                                        tf = wmem_new(wmem_file_scope(), rtmpt_frag_t);
2337
48
                                        tf->ishdr = 1;
2338
48
                                        tf->seq = seq + offset;
2339
48
                                        tf->lastseq = tf->seq + remain - 1;
2340
48
                                        tf->len = basic_hlen + message_hlen;
2341
48
                                        tvb_memcpy(tvb, tf->saved.d, offset, remain);
2342
48
                                        tf->have = remain;
2343
48
                                        wmem_tree_insert32(rconv->frags[cdir], seq+offset, tf);
2344
48
                                        return;
2345
48
                                }
2346
2347
4.85k
                                id = id & 0x3f;
2348
4.85k
                                if (id == 0)
2349
753
                                        id = tvb_get_uint8(tvb, offset+1) + 64;
2350
4.09k
                                else if (id == 1)
2351
84
                                        id = tvb_get_letohs(tvb, offset+1) + 64;
2352
4.85k
                        }
2353
2354
4.89k
                } else {
2355
                        /* Use reassembled header data */
2356
0
                        id = tf->saved.d[0];
2357
0
                        header_type = (id>>6) & 3;
2358
0
                        basic_hlen = rtmpt_basic_header_length(id);
2359
0
                        message_hlen = tf->len - basic_hlen;
2360
2361
0
                        id = rtmpt_chunk_stream_id(tf->saved.d);
2362
0
                }
2363
2364
                /* Calculate header values, defaulting from previous packets with same id */
2365
2366
4.85k
                if (id <= RTMPT_ID_MAX)
2367
4.85k
                        ti = (rtmpt_id_t *)wmem_tree_lookup32(rconv->ids[cdir], id);
2368
4.85k
                if (ti)
2369
4.09k
                        tp = (rtmpt_packet_t *)wmem_tree_lookup32_le(ti->packets, seq+offset-1);
2370
2371
4.85k
                if (header_type == 0)
2372
1.24k
                        src = tf ? pntohu32(tf->saved.d+basic_hlen+7) : tvb_get_ntohl(tvb, offset+basic_hlen+7);
2373
3.60k
                else if (ti)
2374
3.24k
                        src = ti->src;
2375
355
                else src = 0;
2376
2377
4.85k
                if (header_type < 2)
2378
1.54k
                        cmd = tf ? tf->saved.d[basic_hlen+6] : tvb_get_uint8(tvb, offset+basic_hlen+6);
2379
3.31k
                else if (ti)
2380
3.09k
                        cmd = ti->cmd;
2381
212
                else
2382
212
                        cmd = 0;
2383
2384
                /* Calculate chunk_size now as a last-resort default payload length */
2385
4.85k
                if (id > RTMPT_ID_MAX) {
2386
0
                        if (id == RTMPT_TYPE_HANDSHAKE_1)
2387
0
                                chunk_size = body_len = 1536;
2388
0
                        else if (id == RTMPT_TYPE_HANDSHAKE_2)
2389
0
                                chunk_size = body_len = 3072;
2390
0
                        else /* if (id == RTMPT_TYPE_HANDSHAKE_3) */
2391
0
                                chunk_size = body_len = 1536;
2392
4.85k
                } else {
2393
4.85k
                        chunk_size = GPOINTER_TO_INT(wmem_tree_lookup32_le(rconv->chunksize[cdir], seq+offset-1));
2394
4.85k
                        if (!chunk_size) {
2395
964
                                chunk_size = ((int)rtmpt_default_chunk_size > 0) ? rtmpt_default_chunk_size : INT_MAX;
2396
964
                        }
2397
2398
4.85k
                        if (header_type < 2)
2399
1.54k
                                body_len = tf ? pntohu24(tf->saved.d+basic_hlen+3) : tvb_get_ntoh24(tvb, offset+basic_hlen+3);
2400
3.31k
                        else if (ti)
2401
3.09k
                                body_len = ti->len;
2402
212
                        else
2403
212
                                body_len = chunk_size;
2404
4.85k
                }
2405
2406
4.85k
                if (!ti || !tp || header_type<3 || tp->have == tp->want || tp->chunkhave != tp->chunkwant) {
2407
                        /* Start a new packet if:
2408
                         *   no previous packet with same id
2409
                         *   not a short 1-byte header
2410
                         *   previous packet with same id was complete
2411
                         *   previous incomplete chunk not handled by fragment handler
2412
                         */
2413
4.22k
                        ws_noisy("New packet cdir=%d seq=%d ti=%p tp=%p header_type=%d header_len=%d id=%d tph=%d tpw=%d len=%d cs=%d",
2414
4.22k
                                    cdir, seq+offset,
2415
4.22k
                                    ti, tp, header_type, basic_hlen+message_hlen, id, tp?tp->have:0, tp?tp->want:0, body_len, chunk_size);
2416
2417
4.22k
                        if (tp && tp->resident && tp->have < tp->want) {
2418
                                /* The previous message on this chunk stream
2419
                                 * was never completed, and this header starts
2420
                                 * a new one. Incomplete messages are not added
2421
                                 * to rconv->packets, so its data will never be
2422
                                 * used. Free it, and mark the message complete
2423
                                 * so that nothing is appended to it later.
2424
                                 */
2425
437
                                wmem_free(wmem_file_scope(), tp->data.p);
2426
437
                                tp->data.p    = NULL;
2427
437
                                tp->alloc     = 0;
2428
437
                                tp->want      = tp->have;
2429
437
                                tp->chunkwant = 0;
2430
437
                                tp->chunkhave = 0;
2431
437
                        }
2432
2433
4.22k
                        if (!ti) {
2434
761
                                ti = wmem_new(wmem_file_scope(), rtmpt_id_t);
2435
761
                                ti->packets = wmem_tree_new(wmem_file_scope());
2436
761
                                ti->ts  = 0;
2437
761
                                ti->tsd = 0;
2438
761
                                wmem_tree_insert32(rconv->ids[cdir], id, ti);
2439
761
                        }
2440
2441
4.22k
                        if (header_type == 0) {
2442
1.24k
                                ts = tf ? pntohu24(tf->saved.d+basic_hlen) : tvb_get_ntoh24(tvb, offset+basic_hlen);
2443
1.24k
                                if (ts == 0xffffff) {
2444
14
                                        ts = tf ? pntohu32(tf->saved.d+basic_hlen+11) : tvb_get_ntohl(tvb, offset+basic_hlen+11);
2445
14
                                }
2446
1.24k
                                tsd = ts - ti->ts;
2447
2.97k
                        } else if (header_type < 3) {
2448
625
                                tsd = tf ? pntohu24(tf->saved.d+basic_hlen) : tvb_get_ntoh24(tvb, offset+basic_hlen);
2449
625
                                if (tsd == 0xffffff) {
2450
26
                                        ts  = tf ? pntohu32(tf->saved.d+basic_hlen+message_hlen-4) : tvb_get_ntohl(tvb, offset+basic_hlen+message_hlen-4);
2451
26
                                        tsd = ti->tsd; /* questionable */
2452
599
                                } else {
2453
599
                                        ts  = ti->ts + tsd;
2454
599
                                }
2455
2.34k
                        } else {
2456
2.34k
                                ts  = ti->ts + ti->tsd;
2457
2.34k
                                tsd = ti->tsd;
2458
2.34k
                        }
2459
2460
                        /* create a new packet structure */
2461
4.22k
                        tp             = wmem_new(wmem_file_scope(), rtmpt_packet_t);
2462
4.22k
                        tp->seq        = tp->lastseq = tf ? tf->seq : seq+offset;
2463
4.22k
                        tp->have       = 0;
2464
4.22k
                        tp->want       = basic_hlen + message_hlen + body_len;
2465
4.22k
                        tp->chunkwant  = 0;
2466
4.22k
                        tp->chunkhave  = 0;
2467
4.22k
                        tp->bhlen      = basic_hlen;
2468
4.22k
                        tp->mhlen      = message_hlen;
2469
4.22k
                        tp->fmt        = header_type;
2470
4.22k
                        tp->id         = id;
2471
4.22k
                        tp->ts         = ts;
2472
4.22k
                        tp->len        = body_len;
2473
4.22k
                        if (id > RTMPT_ID_MAX)
2474
0
                                tp->cmd = id;
2475
4.22k
                        else
2476
4.22k
                                tp->cmd = cmd & 0x7f;
2477
4.22k
                        tp->src        = src;
2478
4.22k
                        tp->txid       = 0;
2479
4.22k
                        tp->isresponse = false;
2480
4.22k
                        tp->otherframe = 0;
2481
2482
4.22k
                        tp->frames     = wmem_list_new(wmem_file_scope());
2483
4.22k
                        wmem_list_prepend(tp->frames, GUINT_TO_POINTER(pinfo->num));
2484
2485
                        /* Save the header information for future defaulting needs */
2486
4.22k
                        ti->ts  = ts;
2487
4.22k
                        ti->tsd = tsd;
2488
4.22k
                        ti->len = body_len;
2489
4.22k
                        ti->cmd = cmd;
2490
4.22k
                        ti->src = src;
2491
2492
                        /* store against the id only until unchunking is complete */
2493
4.22k
                        wmem_tree_insert32(ti->packets, tp->seq, tp);
2494
2495
4.22k
                        if (!tf && body_len <= chunk_size && tp->want <= remain) {
2496
                                /* The easy case - a whole packet contiguous and fully within this segment */
2497
2.89k
                                tp->resident    = false;
2498
2.89k
                                tp->data.offset = offset;
2499
2.89k
                                tp->lastseq     = seq+offset+tp->want-1;
2500
2.89k
                                tp->have        = tp->want;
2501
2502
2.89k
                                wmem_tree_insert32(rconv->packets[cdir], tp->lastseq, tp);
2503
2504
2.89k
                                pktbuf = tvb_new_subset_length(tvb, tp->data.offset, tp->have);
2505
2.89k
                                dissect_rtmpt(pktbuf, pinfo, tree, rconv, cdir, tp);
2506
2507
2.89k
                                offset += tp->want;
2508
2.89k
                                remain -= tp->want;
2509
2.89k
                                continue;
2510
2511
2.89k
                        } else {
2512
                                /* Some more reassembly required */
2513
1.32k
                                tp->resident = true;
2514
                                /* tp->want is how much data we think we want,
2515
                                 * up to a 24-bit length from the packet. Don't
2516
                                 * allocate it all at once, due to memory
2517
                                 * exhaustion on fuzzed data (#6898). Allocate
2518
                                 * just the header; the body is added below as
2519
                                 * the bytes arrive. Even a fixed initial size
2520
                                 * (it used to be 32 KiB) is a large multiplier,
2521
                                 * as each message on a chunk stream can declare
2522
                                 * a large length and send only a byte.
2523
                                 */
2524
1.32k
                                tp->alloc = basic_hlen + message_hlen;
2525
1.32k
                                tp->data.p = (uint8_t *)wmem_alloc(wmem_file_scope(), tp->alloc);
2526
2527
1.32k
                                if (tf && tf->ishdr) {
2528
0
                                        memcpy(tp->data.p, tf->saved.d, tf->len);
2529
1.32k
                                } else {
2530
1.32k
                                        tvb_memcpy(tvb, tp->data.p, offset, basic_hlen+message_hlen);
2531
1.32k
                                        offset += basic_hlen + message_hlen;
2532
1.32k
                                        remain -= basic_hlen + message_hlen;
2533
1.32k
                                }
2534
2535
1.32k
                                tp->lastseq = seq+offset-1;
2536
1.32k
                                tp->have = basic_hlen + message_hlen;
2537
2538
1.32k
                                if (tp->have == tp->want) {
2539
0
                                        wmem_tree_insert32(rconv->packets[cdir], tp->lastseq, tp);
2540
2541
0
                                        pktbuf = tvb_new_child_real_data(tvb, tp->data.p, tp->have, tp->have);
2542
0
                                        add_new_data_source(pinfo, pktbuf, "Unchunked RTMP");
2543
0
                                        dissect_rtmpt(pktbuf, pinfo, tree, rconv, cdir, tp);
2544
0
                                        continue;
2545
0
                                }
2546
2547
1.32k
                                tp->chunkwant = chunk_size;
2548
1.32k
                                if (tp->chunkwant > tp->want-tp->have)
2549
13
                                        tp->chunkwant = tp->want - tp->have;
2550
1.32k
                        }
2551
4.22k
                } else {
2552
                        /* message_hlen includes any extended timestamp that
2553
                         * header type 3 resends; see rtmpt_type3_ext_ts_length().
2554
                         */
2555
630
                        ws_noisy("Old packet cdir=%d seq=%d ti=%p tp=%p header_len=%d id=%d tph=%d tpw=%d len=%d cs=%d",
2556
630
                                 cdir, seq+offset,
2557
630
                                 ti, tp, basic_hlen+message_hlen, id, tp?tp->have:0, tp?tp->want:0, body_len, chunk_size);
2558
2559
630
                        tp->chunkwant = chunk_size;
2560
630
                        if (tp->chunkwant > tp->want-tp->have)
2561
0
                                tp->chunkwant = tp->want - tp->have;
2562
2563
630
                        if (!tf) {
2564
                                /* A reassembled header was already consumed */
2565
630
                                offset += basic_hlen + message_hlen;
2566
630
                                remain -= basic_hlen + message_hlen;
2567
630
                        }
2568
630
                }
2569
2570
1.95k
                tf = NULL;
2571
2572
                /* Last case to deal with is unchunking the packet body */
2573
1.96k
        unchunk:
2574
1.96k
                want = tp->chunkwant - tp->chunkhave;
2575
1.96k
                if (want > remain)
2576
116
                        want = remain;
2577
1.96k
                ws_noisy("  cw=%d ch=%d r=%d w=%d", tp->chunkwant, tp->chunkhave, remain, want);
2578
2579
                /* message length is a 3 byte number, never overflows an int */
2580
1.96k
                if (tp->alloc < tp->have + want) {
2581
                        /* tp->want - how much data is supposedly in the entire
2582
                         * unchunked packet, according to the header. Up to a
2583
                         * 24-bit integer. Actually allocating this amount can
2584
                         * cause memory exhaustion on fuzzed data.
2585
                         * want - how much more data we are going to copy from the
2586
                         * current tvbuff. No more than necessary to finish the
2587
                         * current chunk, or what's actually in the tvbuff.
2588
                         * Allocating this amount shouldn't cause memory exhaustion
2589
                         * because it's present in the frame.
2590
                         *
2591
                         * We should have calculated those values so that the
2592
                         * following assertion is true.
2593
                         */
2594
1.51k
                        DISSECTOR_ASSERT_CMPINT(tp->have + want, <=, tp->want);
2595
1.51k
                        tp->alloc = MAX(tp->alloc*2, tp->have + want);
2596
1.51k
                        tp->alloc = MIN(tp->alloc, tp->want);
2597
1.51k
                        tp->data.p = wmem_realloc(wmem_file_scope(), tp->data.p, tp->alloc);
2598
1.51k
                }
2599
1.96k
                tvb_memcpy(tvb, tp->data.p+tp->have, offset, want);
2600
1.96k
                wmem_list_frame_t *frame_head = wmem_list_head(tp->frames);
2601
1.96k
                if (wmem_list_frame_data(frame_head) != GUINT_TO_POINTER(pinfo->num)) {
2602
49
                        wmem_list_prepend(tp->frames, GUINT_TO_POINTER(pinfo->num));
2603
49
                }
2604
2605
1.96k
                if (tf) {
2606
5
                        tf->have += want;
2607
5
                        tf->lastseq = seq+offset+want-1;
2608
5
                }
2609
1.96k
                tp->lastseq = seq+offset+want-1;
2610
1.96k
                tp->have += want;
2611
1.96k
                tp->chunkhave += want;
2612
2613
1.96k
                offset += want;
2614
1.96k
                remain -= want;
2615
2616
1.96k
                if (tp->chunkhave == tp->chunkwant) {
2617
                        /* Chunk is complete - wait for next header */
2618
1.84k
                        tp->chunkhave = 0;
2619
1.84k
                        tp->chunkwant = 0;
2620
1.84k
                }
2621
2622
1.96k
                if (tp->have == tp->want) {
2623
                        /* Whole packet is complete */
2624
32
                        wmem_tree_insert32(rconv->packets[cdir], tp->lastseq, tp);
2625
32
                        wmem_list_foreach(tp->frames, rtmpt_packet_mark_depended, pinfo->fd);
2626
2627
32
                        pktbuf = tvb_new_child_real_data(tvb, tp->data.p, tp->have, tp->have);
2628
32
                        add_new_data_source(pinfo, pktbuf, "Unchunked RTMP");
2629
32
                        dissect_rtmpt(pktbuf, pinfo, tree, rconv, cdir, tp);
2630
1.93k
                } else if (tp->chunkhave < tp->chunkwant) {
2631
                        /* Chunk is split across segment boundary */
2632
116
                        rtmpt_frag_t *tf2 = wmem_new(wmem_file_scope(), rtmpt_frag_t);
2633
116
                        tf2->ishdr    = 0;
2634
116
                        tf2->seq      = seq + offset - want;
2635
116
                        tf2->lastseq  = tf2->seq + remain - 1 + want;
2636
116
                        tf2->have     = tp->chunkhave;
2637
116
                        tf2->len      = tp->chunkwant;
2638
116
                        tf2->saved.id = tp->id;
2639
116
                        ws_noisy("  inserting tf @ %d", seq+offset-want-1);
2640
116
                        wmem_tree_insert32(rconv->frags[cdir], seq+offset-want-1, tf2);
2641
116
                }
2642
1.96k
        }
2643
172
}
2644
2645
static rtmpt_conv_t *
2646
rtmpt_init_rconv(conversation_t *conv)
2647
68
{
2648
68
        rtmpt_conv_t *rconv = wmem_new(wmem_file_scope(), rtmpt_conv_t);
2649
68
        conversation_add_proto_data(conv, proto_rtmpt, rconv);
2650
2651
68
        rconv->seqs[0]      = wmem_tree_new(wmem_file_scope());
2652
68
        rconv->seqs[1]      = wmem_tree_new(wmem_file_scope());
2653
68
        rconv->frags[0]     = wmem_tree_new(wmem_file_scope());
2654
68
        rconv->frags[1]     = wmem_tree_new(wmem_file_scope());
2655
68
        rconv->ids[0]       = wmem_tree_new(wmem_file_scope());
2656
68
        rconv->ids[1]       = wmem_tree_new(wmem_file_scope());
2657
68
        rconv->packets[0]   = wmem_tree_new(wmem_file_scope());
2658
68
        rconv->packets[1]   = wmem_tree_new(wmem_file_scope());
2659
68
        rconv->chunksize[0] = wmem_tree_new(wmem_file_scope());
2660
68
        rconv->chunksize[1] = wmem_tree_new(wmem_file_scope());
2661
68
        rconv->txids[0]     = wmem_tree_new(wmem_file_scope());
2662
68
        rconv->txids[1]     = wmem_tree_new(wmem_file_scope());
2663
2664
68
        return rconv;
2665
68
}
2666
2667
static int
2668
dissect_rtmpt_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data)
2669
172
{
2670
172
        conversation_t *conv;
2671
172
        rtmpt_conv_t   *rconv;
2672
172
        int             cdir;
2673
172
        struct tcpinfo *tcpinfo;
2674
2675
        /* Reject the packet if data is NULL */
2676
172
        if (data == NULL) {
2677
0
                return 0;
2678
0
        }
2679
172
        tcpinfo = (struct tcpinfo*)data;
2680
2681
172
        conv = find_or_create_conversation(pinfo);
2682
2683
172
        rconv = (rtmpt_conv_t*)conversation_get_proto_data(conv, proto_rtmpt);
2684
172
        if (!rconv) {
2685
68
                rconv = rtmpt_init_rconv(conv);
2686
68
        }
2687
2688
172
        cdir = (addresses_equal(conversation_key_addr1(conv->key_ptr), &pinfo->src) &&
2689
172
                addresses_equal(conversation_key_addr2(conv->key_ptr), &pinfo->dst) &&
2690
172
                conversation_key_port1(conv->key_ptr) == pinfo->srcport &&
2691
172
                conversation_key_port2(conv->key_ptr) == pinfo->destport) ? 0 : 1;
2692
2693
172
        dissect_rtmpt_common(tvb, pinfo, tree, rconv, cdir, tcpinfo->seq, tcpinfo->lastackseq);
2694
172
        return tvb_reported_length(tvb);
2695
172
}
2696
2697
static int
2698
dissect_rtmpt_http(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_)
2699
0
{
2700
0
        conversation_t *conv;
2701
0
        rtmpt_conv_t   *rconv;
2702
0
        int             cdir;
2703
0
        uint32_t        seq;
2704
0
        uint32_t        lastackseq;
2705
0
        uint32_t        offset;
2706
0
        int             remain;
2707
2708
0
        offset = 0;
2709
0
        remain = tvb_reported_length_remaining(tvb, 0);
2710
2711
        /*
2712
         * Request flow:
2713
         *
2714
         *  POST /open/1
2715
         *    request body is a single non-RTMP byte
2716
         *    response contains a client ID <cid> followed by NL
2717
         *  POST /send/<cid>/<seq>
2718
         *    <seq> starts at 0 after open and increments on each
2719
         *    subsequent post
2720
         *    request body is pure RTMP data
2721
         *    response is a single non-RTMP byte followed by RTMP data
2722
         *  POST /idle/<cid>/<seq>
2723
         *    request contains a single non-RTMP byte
2724
         *    response is a single non-RTMP byte followed by RTMP data
2725
         *  POST /close/<cid>/<seq>
2726
         *    request and response contain a single non-RTMP byte
2727
         *
2728
         * Ideally here we'd know:
2729
         *
2730
         *  1) Whether this is was a HTTP request or response
2731
         *     (this gives us cdir directly)
2732
         *  2) The requested URL (for both cases)
2733
         *     (this tells us the type of framing bytes present,
2734
         *     so whether there are any real bytes present). We
2735
         *     could also use the client ID to identify the
2736
         *     conversation, since each POST is likely to be on
2737
         *     a different TCP connection, and there could be
2738
         *     multiple simultaneous sessions from a single
2739
         *     client (which we don't deal with here.)
2740
         *
2741
         *  As it is, we currently have to just guess, and are
2742
         *  likely easily confused.
2743
         */
2744
2745
0
        cdir = pinfo->srcport == pinfo->match_uint;
2746
2747
0
        if (cdir) {
2748
0
                conv = find_conversation(pinfo->num, &pinfo->dst, &pinfo->src, conversation_pt_to_conversation_type(pinfo->ptype), 0, pinfo->srcport, 0);
2749
0
                if (!conv) {
2750
0
                        ws_debug("RTMPT new conversation");
2751
0
                        conv = conversation_new(pinfo->num, &pinfo->dst, &pinfo->src, conversation_pt_to_conversation_type(pinfo->ptype), 0, pinfo->srcport, 0);
2752
0
                }
2753
0
        } else {
2754
0
                conv = find_conversation(pinfo->num, &pinfo->src, &pinfo->dst, conversation_pt_to_conversation_type(pinfo->ptype), 0, pinfo->destport, 0);
2755
0
                if (!conv) {
2756
0
                        ws_debug("RTMPT new conversation");
2757
0
                        conv = conversation_new(pinfo->num, &pinfo->src, &pinfo->dst, conversation_pt_to_conversation_type(pinfo->ptype), 0, pinfo->destport, 0);
2758
0
                }
2759
0
        }
2760
2761
0
        rconv = (rtmpt_conv_t*)conversation_get_proto_data(conv, proto_rtmpt);
2762
0
        if (!rconv) {
2763
0
                rconv = rtmpt_init_rconv(conv);
2764
0
        }
2765
2766
        /* Work out a TCP-like sequence numbers for the tunneled data stream.
2767
         * If we've seen the packet before we'll have stored the seq of our
2768
         * last byte against the frame number - since we know how big we are
2769
         * we can work out the seq of our first byte. If this is the first
2770
         * time, we use the stored seq of the last byte of the previous frame
2771
         * plus one. If there is no previous frame then we must be at seq=1!
2772
         * (This is per-conversation and per-direction, of course.) */
2773
2774
0
        lastackseq = GPOINTER_TO_INT(wmem_tree_lookup32_le(rconv->seqs[cdir ^ 1], pinfo->num))+1;
2775
2776
0
        if (cdir == 1 && lastackseq < 2 && remain == 17) {
2777
                /* Session startup: the client makes an /open/ request and
2778
                 * the server responds with a 16 bytes client
2779
                 * identifier followed by a newline */
2780
0
                offset += 17;
2781
0
                remain -= 17;
2782
0
        } else if (cdir || remain == 1) {
2783
                /* All other server responses start with one byte which
2784
                 * is not part of the RTMP stream. Client /idle/ requests
2785
                 * contain a single byte also not part of the stream. We
2786
                 * must discard these */
2787
0
                offset++;
2788
0
                remain--;
2789
0
        }
2790
2791
0
        seq = GPOINTER_TO_INT(wmem_tree_lookup32(rconv->seqs[cdir], pinfo->num));
2792
2793
0
        if (seq == 0) {
2794
0
                seq = GPOINTER_TO_INT(wmem_tree_lookup32_le(rconv->seqs[cdir], pinfo->num));
2795
0
                seq += remain;
2796
0
                wmem_tree_insert32(rconv->seqs[cdir], pinfo->num, GINT_TO_POINTER(seq));
2797
0
        }
2798
2799
0
        seq -= remain-1;
2800
2801
0
        ws_debug("RTMPT f=%d cdir=%d seq=%d lastackseq=%d len=%d", pinfo->num, cdir, seq, lastackseq, remain);
2802
2803
0
        if (remain < 1)
2804
0
                return offset;
2805
2806
0
        if (offset > 0) {
2807
0
                tvbuff_t *tvbrtmp = tvb_new_subset_length(tvb, offset, remain);
2808
0
                dissect_rtmpt_common(tvbrtmp, pinfo, tree, rconv, cdir, seq, lastackseq);
2809
0
        } else {
2810
0
                dissect_rtmpt_common(tvb, pinfo, tree, rconv, cdir, seq, lastackseq);
2811
0
        }
2812
0
        return tvb_captured_length(tvb);
2813
0
}
2814
2815
static bool
2816
dissect_rtmpt_heur(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data)
2817
0
{
2818
0
        conversation_t *conversation;
2819
0
        if (tvb_reported_length(tvb) >= 12)
2820
0
        {
2821
                /* To avoid a too high rate of false positive, this heuristics only matches the protocol
2822
                   from the first server response packet and not from the client request packets before.
2823
                   Therefore it is necessary to a "Decode as" to properly decode the first packets */
2824
0
                struct tcpinfo *tcpinfo = (struct tcpinfo *)data;
2825
0
                if (tcpinfo->lastackseq == RTMPT_HANDSHAKE_OFFSET_2
2826
0
                    && tcpinfo->seq == RTMPT_HANDSHAKE_OFFSET_1
2827
0
                    && tvb_get_uint8(tvb, 0) == RTMPT_MAGIC)
2828
0
                {
2829
                    /* Register this dissector for this conversation */
2830
0
                    conversation = find_or_create_conversation(pinfo);
2831
0
                    conversation_set_dissector(conversation, rtmpt_tcp_handle);
2832
2833
                    /* Dissect the packet */
2834
0
                    dissect_rtmpt_tcp(tvb, pinfo, tree, data);
2835
0
                    return true;
2836
0
                }
2837
0
        }
2838
0
        return false;
2839
0
}
2840
2841
static int
2842
dissect_amf(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_)
2843
0
{
2844
0
        proto_item *ti;
2845
0
        proto_tree *amf_tree, *headers_tree, *messages_tree;
2846
0
        int         offset;
2847
0
        unsigned    header_count, message_count, i;
2848
0
        unsigned    string_length;
2849
0
        unsigned    header_length, message_length;
2850
0
        bool        amf3_encoding = false;
2851
2852
        /*
2853
         * XXX - is "application/x-amf" just AMF3?
2854
         */
2855
0
        ti = proto_tree_add_item(tree, proto_amf, tvb, 0, -1, ENC_NA);
2856
0
        amf_tree = proto_item_add_subtree(ti, ett_amf);
2857
0
        offset = 0;
2858
0
        proto_tree_add_item(amf_tree, hf_amf_version, tvb, offset, 2, ENC_BIG_ENDIAN);
2859
0
        offset += 2;
2860
0
        proto_tree_add_item_ret_uint(amf_tree, hf_amf_header_count, tvb, offset, 2, ENC_BIG_ENDIAN, &header_count);
2861
0
        offset += 2;
2862
0
        if (header_count != 0) {
2863
0
                headers_tree = proto_tree_add_subtree(amf_tree, tvb, offset, -1, ett_amf_headers, NULL, "Headers");
2864
0
                for (i = 0; i < header_count; i++) {
2865
0
                        string_length = tvb_get_ntohs(tvb, offset);
2866
0
                        proto_tree_add_item(headers_tree, hf_amf_header_name, tvb, offset, 2, ENC_UTF_8|ENC_BIG_ENDIAN);
2867
0
                        offset += 2 + string_length;
2868
0
                        proto_tree_add_item(headers_tree, hf_amf_header_must_understand, tvb, offset, 1, ENC_NA);
2869
0
                        offset += 1;
2870
0
                        header_length = tvb_get_ntohl(tvb, offset);
2871
0
                        if (header_length == 0xFFFFFFFF)
2872
0
                                proto_tree_add_uint_format_value(headers_tree, hf_amf_header_length, tvb, offset, 4, header_length, "Unknown");
2873
0
                        else
2874
0
                                proto_tree_add_item(headers_tree, hf_amf_header_length, tvb, offset, 4, ENC_BIG_ENDIAN);
2875
0
                        offset += 4;
2876
0
                        if (amf3_encoding)
2877
0
                                offset = dissect_amf3_value_type(tvb, pinfo, offset, headers_tree, NULL);
2878
0
                        else
2879
0
                                offset = dissect_amf0_value_type(tvb, pinfo, offset, headers_tree, &amf3_encoding, NULL);
2880
0
                }
2881
0
        }
2882
0
        proto_tree_add_item_ret_uint(amf_tree, hf_amf_message_count, tvb, offset, 2, ENC_BIG_ENDIAN, &message_count);
2883
0
        offset += 2;
2884
0
        if (message_count != 0) {
2885
0
                messages_tree = proto_tree_add_subtree(amf_tree, tvb, offset, -1, ett_amf_messages, NULL, "Messages");
2886
0
                for (i = 0; i < message_count; i++) {
2887
0
                        string_length = tvb_get_ntohs(tvb, offset);
2888
0
                        proto_tree_add_item(messages_tree, hf_amf_message_target_uri, tvb, offset, 2, ENC_UTF_8|ENC_BIG_ENDIAN);
2889
0
                        offset += 2 + string_length;
2890
0
                        string_length = tvb_get_ntohs(tvb, offset);
2891
0
                        proto_tree_add_item(messages_tree, hf_amf_message_response_uri, tvb, offset, 2, ENC_UTF_8|ENC_BIG_ENDIAN);
2892
0
                        offset += 2 + string_length;
2893
0
                        message_length = tvb_get_ntohl(tvb, offset);
2894
0
                        if (message_length == 0xFFFFFFFF)
2895
0
                                proto_tree_add_uint_format_value(messages_tree, hf_amf_message_length, tvb, offset, 4, message_length, "Unknown");
2896
0
                        else
2897
0
                                proto_tree_add_item(messages_tree, hf_amf_message_length, tvb, offset, 4, ENC_BIG_ENDIAN);
2898
0
                        offset += 4;
2899
0
                        offset = dissect_rtmpt_body_command(tvb, pinfo, offset, messages_tree, false);
2900
0
                }
2901
0
        }
2902
0
        return tvb_captured_length(tvb);
2903
0
}
2904
2905
void
2906
proto_register_rtmpt(void)
2907
16
{
2908
16
        static hf_register_info hf[] = {
2909
/* RTMP Handshake data */
2910
16
                { &hf_rtmpt_handshake_c0,
2911
16
                  { "Protocol version", "rtmpt.handshake.c0", FT_BYTES, BASE_NONE,
2912
16
                    NULL, 0x0, "RTMPT Handshake C0", HFILL }},
2913
2914
16
                { &hf_rtmpt_handshake_s0,
2915
16
                  { "Protocol version", "rtmpt.handshake.s0", FT_BYTES, BASE_NONE,
2916
16
                    NULL, 0x0, "RTMPT Handshake S0", HFILL }},
2917
2918
16
                { &hf_rtmpt_handshake_c1,
2919
16
                  { "Handshake data", "rtmpt.handshake.c1", FT_BYTES, BASE_NONE,
2920
16
                    NULL, 0x0, "RTMPT Handshake C1", HFILL }},
2921
2922
16
                { &hf_rtmpt_handshake_s1,
2923
16
                  { "Handshake data", "rtmpt.handshake.s1", FT_BYTES, BASE_NONE,
2924
16
                    NULL, 0x0, "RTMPT Handshake S1", HFILL }},
2925
2926
16
                { &hf_rtmpt_handshake_c2,
2927
16
                  { "Handshake data", "rtmpt.handshake.c2", FT_BYTES, BASE_NONE,
2928
16
                    NULL, 0x0, "RTMPT Handshake C2", HFILL }},
2929
2930
16
                { &hf_rtmpt_handshake_s2,
2931
16
                  { "Handshake data", "rtmpt.handshake.s2", FT_BYTES, BASE_NONE,
2932
16
                    NULL, 0x0, "RTMPT Handshake S2", HFILL }},
2933
2934
/* RTMP chunk/packet header */
2935
16
                { &hf_rtmpt_header_format,
2936
16
                  { "Format", "rtmpt.header.format", FT_UINT8, BASE_DEC,
2937
16
                    NULL, 0xC0, "RTMPT Basic Header format", HFILL }},
2938
2939
16
                { &hf_rtmpt_header_csid,
2940
16
                  { "Chunk Stream ID", "rtmpt.header.csid", FT_UINT32, BASE_DEC,
2941
16
                    NULL, 0x0, "RTMPT Basic Header chunk stream ID", HFILL }},
2942
2943
16
                { &hf_rtmpt_header_timestamp,
2944
16
                  { "Timestamp", "rtmpt.header.timestamp", FT_UINT24, BASE_DEC,
2945
16
                    NULL, 0x0, "RTMPT Message Header timestamp", HFILL }},
2946
2947
16
                { &hf_rtmpt_header_timestamp_delta,
2948
16
                  { "Timestamp delta", "rtmpt.header.timestampdelta", FT_UINT24, BASE_DEC,
2949
16
                    NULL, 0x0, "RTMPT Message Header timestamp delta", HFILL }},
2950
2951
16
                { &hf_rtmpt_header_body_size,
2952
16
                  { "Body size", "rtmpt.header.bodysize", FT_UINT24, BASE_DEC,
2953
16
                    NULL, 0x0, "RTMPT Message Header body size", HFILL }},
2954
2955
16
                { &hf_rtmpt_header_typeid,
2956
16
                  { "Type ID", "rtmpt.header.typeid", FT_UINT8, BASE_HEX,
2957
16
                    VALS(rtmpt_opcode_vals), 0x0, "RTMPT Message Header type ID", HFILL }},
2958
2959
16
                { &hf_rtmpt_header_streamid,
2960
16
                  { "Stream ID", "rtmpt.header.streamid", FT_UINT32, BASE_DEC,
2961
16
                    NULL, 0x0, "RTMPT Header stream ID", HFILL }},
2962
2963
16
                { &hf_rtmpt_header_ets,
2964
16
                  { "Extended timestamp", "rtmpt.header.ets", FT_UINT32, BASE_DEC,
2965
16
                    NULL, 0x0, "RTMPT Message Header extended timestamp", HFILL }},
2966
2967
/* Stream Control Messages */
2968
2969
16
                { &hf_rtmpt_scm_chunksize,
2970
16
                  { "Chunk size", "rtmpt.scm.chunksize", FT_UINT32, BASE_DEC,
2971
16
                    NULL, 0x0, "RTMPT SCM chunk size", HFILL }},
2972
2973
16
                { &hf_rtmpt_scm_csid,
2974
16
                  { "Chunk stream ID", "rtmpt.scm.csid", FT_UINT32, BASE_DEC,
2975
16
                    NULL, 0x0, "RTMPT SCM chunk stream ID", HFILL }},
2976
2977
16
                { &hf_rtmpt_scm_seq,
2978
16
                  { "Sequence number", "rtmpt.scm.seq", FT_UINT32, BASE_DEC,
2979
16
                    NULL, 0x0, "RTMPT SCM acknowledgement sequence number", HFILL }},
2980
2981
16
                { &hf_rtmpt_scm_was,
2982
16
                  { "Window acknowledgement size", "rtmpt.scm.was", FT_UINT32, BASE_DEC,
2983
16
                    NULL, 0x0, "RTMPT SCM window acknowledgement size", HFILL }},
2984
2985
16
                { &hf_rtmpt_scm_limittype,
2986
16
                  { "Limit type", "rtmpt.scm.limittype", FT_UINT8, BASE_DEC,
2987
16
                    VALS(rtmpt_limit_vals), 0x0, "RTMPT SCM window acknowledgement size", HFILL }},
2988
2989
/* User Control Messages */
2990
16
                { &hf_rtmpt_ucm_eventtype,
2991
16
                  { "Event type", "rtmpt.ucm.eventtype", FT_UINT16, BASE_DEC,
2992
16
                    VALS(rtmpt_ucm_vals), 0x0, "RTMPT UCM event type", HFILL }},
2993
2994
/* Frame links */
2995
2996
16
                { &hf_rtmpt_function_call,
2997
16
                  { "Response to this call in frame", "rtmpt.function.call", FT_FRAMENUM, BASE_NONE,
2998
16
                    FRAMENUM_TYPE(FT_FRAMENUM_RESPONSE), 0x0, "RTMPT function call", HFILL }},
2999
3000
16
                { &hf_rtmpt_function_response,
3001
16
                  { "Call for this response in frame", "rtmpt.function.response", FT_FRAMENUM, BASE_NONE,
3002
16
                    FRAMENUM_TYPE(FT_FRAMENUM_REQUEST), 0x0, "RTMPT function response", HFILL }},
3003
3004
/* Audio packets */
3005
16
                { &hf_rtmpt_audio_control,
3006
16
                  { "Control", "rtmpt.audio.control", FT_UINT8, BASE_HEX,
3007
16
                    NULL, 0x0, NULL, HFILL }},
3008
3009
16
                { &hf_rtmpt_audio_is_ex_header,
3010
16
                  { "IsExAudioHeader", "rtmpt.audio.is_ex_header", FT_UINT8, BASE_DEC,
3011
16
                    NULL, 0x90, "RTMPT IsExHeader flag introduced in enhanced RTMP", HFILL } },
3012
3013
16
                { &hf_rtmpt_audio_multitrack_control,
3014
16
                  { "Audio multitrack control", "rtmpt.audio.multitrack.control", FT_UINT8, BASE_HEX,
3015
16
                    NULL, 0x0, NULL, HFILL } },
3016
3017
16
                { &hf_rtmpt_audio_multitrack_type,
3018
16
                  { "Audio multitrack type", "rtmpt.audio.multitrack.type", FT_UINT8, BASE_HEX,
3019
16
                    VALS(rtmpt_av_multitrack_types), 0x0f, NULL, HFILL } },
3020
3021
16
                { &hf_rtmpt_audio_multitrack_packet_type,
3022
16
                  { "Audio multitrack packet type", "rtmpt.audio.multitrack.track.packet_type", FT_UINT8, BASE_HEX,
3023
16
                    VALS(rtmpt_audio_packet_types), 0xf0, NULL, HFILL } },
3024
3025
16
                { &hf_rtmpt_audio_packet_type,
3026
16
                  { "Audio packet type", "rtmpt.audio.packet_type", FT_UINT8, BASE_HEX,
3027
16
                    VALS(rtmpt_audio_packet_types), 0x0f, NULL, HFILL } },
3028
3029
16
                { &hf_rtmpt_audio_fourcc,
3030
16
                  { "FourCC", "rtmpt.audio.fourcc", FT_STRING, BASE_NONE,
3031
16
                    NULL, 0x0, NULL, HFILL } },
3032
3033
16
                { &hf_rtmpt_audio_track_id,
3034
16
                  { "Track ID", "rtmpt.audio.multitrack.track.id", FT_UINT8, BASE_DEC,
3035
16
                    NULL, 0x0, NULL, HFILL } },
3036
3037
16
                { &hf_rtmpt_audio_track_length,
3038
16
                  { "Track length", "rtmpt.audio.multitrack.track.length", FT_UINT24, BASE_DEC,
3039
16
                    NULL, 0x0, NULL, HFILL } },
3040
3041
16
                { &hf_rtmpt_audio_format,
3042
16
                  { "Format", "rtmpt.audio.format", FT_UINT8, BASE_DEC,
3043
16
                    VALS(rtmpt_audio_codecs), 0xf0, NULL, HFILL }},
3044
3045
16
                { &hf_rtmpt_audio_rate,
3046
16
                  { "Sample rate", "rtmpt.audio.rate", FT_UINT8, BASE_DEC,
3047
16
                    VALS(rtmpt_audio_rates), 0x0c, NULL, HFILL }},
3048
3049
16
                { &hf_rtmpt_audio_size,
3050
16
                  { "Sample size", "rtmpt.audio.size", FT_UINT8, BASE_DEC,
3051
16
                    VALS(rtmpt_audio_sizes), 0x02, NULL, HFILL }},
3052
3053
16
                { &hf_rtmpt_audio_type,
3054
16
                  { "Channels", "rtmpt.audio.type", FT_UINT8, BASE_DEC,
3055
16
                    VALS(rtmpt_audio_types), 0x01, NULL, HFILL }},
3056
3057
16
                { &hf_rtmpt_audio_data,
3058
16
                  { "Audio data", "rtmpt.audio.data", FT_BYTES, BASE_NONE,
3059
16
                    NULL, 0x0, NULL, HFILL }},
3060
3061
/* Video packets */
3062
16
                { &hf_rtmpt_video_control,
3063
16
                  { "Control", "rtmpt.video.control", FT_UINT8, BASE_HEX,
3064
16
                    NULL, 0x0, NULL, HFILL }},
3065
3066
16
                { &hf_rtmpt_video_multitrack_control,
3067
16
                  { "Video multitrack control", "rtmpt.video.multitrack.control", FT_UINT8, BASE_HEX,
3068
16
                    NULL, 0x0, NULL, HFILL }},
3069
3070
16
                { &hf_rtmpt_video_is_ex_header,
3071
16
                  { "IsExVideoHeader", "rtmpt.video.is_ex_header", FT_UINT8, BASE_DEC,
3072
16
                    NULL, 0x80, "RTMPT IsExHeader flag introduced in enhanced RTMP", HFILL }},
3073
3074
16
                { &hf_rtmpt_video_type,
3075
16
                  { "Video type", "rtmpt.video.type", FT_UINT8, BASE_DEC,
3076
16
                    VALS(rtmpt_video_types), 0x70, NULL, HFILL }},
3077
3078
16
                { &hf_rtmpt_video_command,
3079
16
                  { "Video command", "rtmpt.video.command", FT_UINT8, BASE_DEC,
3080
16
                    VALS(rtmpt_video_commands), 0x0, NULL, HFILL}},
3081
3082
16
                { &hf_rtmpt_video_format,
3083
16
                  { "Format", "rtmpt.video.format", FT_UINT8, BASE_DEC,
3084
16
                    VALS(rtmpt_video_codecs), 0x0f, NULL, HFILL }},
3085
3086
16
                { &hf_rtmpt_video_packet_type,
3087
16
                  { "Video packet type", "rtmpt.video.packet_type", FT_UINT8, BASE_DEC,
3088
16
                    VALS(rtmpt_video_packet_types), 0x0f, NULL, HFILL }},
3089
3090
16
                { &hf_rtmpt_video_multitrack_type,
3091
16
                  { "Video multitrack type", "rtmpt.video.multitrack.type", FT_UINT8, BASE_DEC,
3092
16
                    VALS(rtmpt_av_multitrack_types), 0xf0, NULL, HFILL } },
3093
3094
16
                { &hf_rtmpt_video_multitrack_packet_type,
3095
16
                  { "Video multitrack packet type", "rtmpt.video.multitrack.packet_type", FT_UINT8, BASE_DEC,
3096
16
                    VALS(rtmpt_video_packet_types), 0x0f, NULL, HFILL } },
3097
3098
16
                { &hf_rtmpt_video_fourcc,
3099
16
                  { "FourCC", "rtmpt.video.fourcc", FT_STRING, BASE_NONE,
3100
16
                    NULL, 0x0, NULL, HFILL }},
3101
3102
16
                { &hf_rtmpt_video_track_id,
3103
16
                  { "Track ID", "rtmpt.video.multitrack.track.id", FT_UINT8, BASE_DEC,
3104
16
                    NULL, 0x0, NULL, HFILL } },
3105
3106
16
                { &hf_rtmpt_video_track_length,
3107
16
                  { "Track length", "rtmpt.video.multitrack.track.length", FT_UINT24, BASE_DEC,
3108
16
                    NULL, 0x0, NULL, HFILL } },
3109
3110
16
                { &hf_rtmpt_video_data,
3111
16
                  { "Video data", "rtmpt.video.data", FT_BYTES, BASE_NONE,
3112
16
                    NULL, 0x0, NULL, HFILL }},
3113
3114
/* Aggregate packets */
3115
16
                { &hf_rtmpt_tag_type,
3116
16
                  { "Type", "rtmpt.tag.type", FT_UINT8, BASE_DEC,
3117
16
                    VALS(rtmpt_tag_vals), 0x0, "RTMPT Aggregate tag type", HFILL }},
3118
3119
16
                { &hf_rtmpt_tag_datasize,
3120
16
                  { "Data size", "rtmpt.tag.datasize", FT_UINT24, BASE_DEC,
3121
16
                    NULL, 0x0, "RTMPT Aggregate tag data size", HFILL }},
3122
3123
16
                { &hf_rtmpt_tag_timestamp,
3124
16
                  { "Timestamp", "rtmpt.tag.timestamp", FT_UINT24, BASE_DEC,
3125
16
                    NULL, 0x0, "RTMPT Aggregate tag timestamp", HFILL }},
3126
3127
16
                { &hf_rtmpt_tag_ets,
3128
16
                  { "Timestamp Extended", "rtmpt.tag.ets", FT_UINT8, BASE_DEC,
3129
16
                    NULL, 0x0, "RTMPT Aggregate tag timestamp extended", HFILL }},
3130
3131
16
                { &hf_rtmpt_tag_streamid,
3132
16
                  { "Stream ID", "rtmpt.tag.streamid", FT_UINT24, BASE_DEC,
3133
16
                    NULL, 0x0, "RTMPT Aggregate tag stream ID", HFILL }},
3134
3135
16
                { &hf_rtmpt_tag_tagsize,
3136
16
                  { "Previous tag size", "rtmpt.tag.tagsize", FT_UINT32, BASE_DEC,
3137
16
                    NULL, 0x0, "RTMPT Aggregate previous tag size", HFILL }}
3138
16
        };
3139
16
        static int *ett[] = {
3140
16
                &ett_rtmpt,
3141
16
                &ett_rtmpt_handshake,
3142
16
                &ett_rtmpt_header,
3143
16
                &ett_rtmpt_body,
3144
16
                &ett_rtmpt_ucm,
3145
16
                &ett_rtmpt_audio_control,
3146
16
                &ett_rtmpt_video_control,
3147
16
                &ett_rtmpt_audio_multitrack_control,
3148
16
                &ett_rtmpt_audio_multitrack_track,
3149
16
                &ett_rtmpt_video_multitrack_control,
3150
16
                &ett_rtmpt_video_multitrack_track,
3151
16
                &ett_rtmpt_tag,
3152
16
                &ett_rtmpt_tag_data
3153
16
        };
3154
3155
16
        module_t *rtmpt_module;
3156
3157
16
        proto_rtmpt = proto_register_protocol("Real Time Messaging Protocol", "RTMPT", "rtmpt");
3158
16
        proto_register_field_array(proto_rtmpt, hf, array_length(hf));
3159
16
        proto_register_subtree_array(ett, array_length(ett));
3160
3161
16
        rtmpt_tcp_handle = register_dissector("rtmpt.tcp", dissect_rtmpt_tcp, proto_rtmpt);
3162
16
        rtmpt_http_handle = register_dissector("rtmpt.http", dissect_rtmpt_http, proto_rtmpt);
3163
3164
16
        rtmpt_module = prefs_register_protocol(proto_rtmpt, NULL);
3165
        /* XXX: This desegment preference doesn't do anything */
3166
16
        prefs_register_bool_preference(rtmpt_module, "desegment",
3167
16
                                       "Reassemble RTMPT messages spanning multiple TCP segments",
3168
16
                                       "Whether the RTMPT dissector should reassemble messages spanning multiple TCP segments."
3169
16
                                       " To use this option, you must also enable \"Allow subdissectors to reassemble TCP streams\""
3170
16
                                       " in the TCP protocol settings.",
3171
16
                                       &rtmpt_desegment);
3172
3173
16
        prefs_register_obsolete_preference(rtmpt_module, "max_packet_size");
3174
3175
16
        prefs_register_uint_preference(rtmpt_module, "default_chunk_size",
3176
16
                                       "Default chunk size",
3177
16
                                       "Chunk size to use for connections where the initial handshake is missing,"
3178
16
                                       " i.e. are already in progress at the beginning of the capture file.",
3179
16
                                       10, &rtmpt_default_chunk_size);
3180
16
}
3181
3182
void
3183
proto_register_amf(void)
3184
16
{
3185
16
        static hf_register_info hf[] = {
3186
16
                { &hf_amf_version,
3187
16
                  { "AMF version", "amf.version", FT_UINT16, BASE_DEC,
3188
16
                    NULL, 0x0, NULL, HFILL }},
3189
3190
16
                { &hf_amf_header_count,
3191
16
                  { "Header count", "amf.header_count", FT_UINT16, BASE_DEC,
3192
16
                    NULL, 0x0, NULL, HFILL }},
3193
3194
16
                { &hf_amf_header_name,
3195
16
                  { "Name", "amf.header.name", FT_UINT_STRING, BASE_NONE,
3196
16
                    NULL, 0x0, NULL, HFILL }},
3197
3198
16
                { &hf_amf_header_must_understand,
3199
16
                  { "Must understand", "amf.header.must_understand", FT_BOOLEAN, BASE_NONE,
3200
16
                    NULL, 0x0, NULL, HFILL }},
3201
3202
16
                { &hf_amf_header_length,
3203
16
                  { "Length", "amf.header.length", FT_UINT32, BASE_DEC,
3204
16
                    NULL, 0x0, NULL, HFILL }},
3205
3206
#if 0
3207
                { &hf_amf_header_value_type,
3208
                  { "Value type", "amf.header.value_type", FT_UINT32, BASE_HEX,
3209
                    VALS(rtmpt_type_vals), 0x0, NULL, HFILL }},
3210
#endif
3211
3212
16
                { &hf_amf_message_count,
3213
16
                  { "Message count", "amf.message_count", FT_UINT16, BASE_DEC,
3214
16
                    NULL, 0x0, NULL, HFILL }},
3215
3216
16
                { &hf_amf_message_target_uri,
3217
16
                  { "Target URI", "amf.message.target_uri", FT_UINT_STRING, BASE_NONE,
3218
16
                    NULL, 0x0, NULL, HFILL }},
3219
3220
16
                { &hf_amf_message_response_uri,
3221
16
                  { "Response URI", "amf.message.response_uri", FT_UINT_STRING, BASE_NONE,
3222
16
                    NULL, 0x0, NULL, HFILL }},
3223
3224
16
                { &hf_amf_message_length,
3225
16
                  { "Length", "amf.message.length", FT_UINT32, BASE_DEC,
3226
16
                    NULL, 0x0, NULL, HFILL }},
3227
3228
3229
/* AMF basic types */
3230
16
                { &hf_amf_amf0_type,
3231
16
                  { "AMF0 type", "amf.amf0_type", FT_UINT8, BASE_HEX,
3232
16
                    VALS(amf0_type_vals), 0x0, NULL, HFILL }},
3233
3234
16
                { &hf_amf_amf3_type,
3235
16
                  { "AMF3 type", "amf.amf3_type", FT_UINT8, BASE_HEX,
3236
16
                    VALS(amf3_type_vals), 0x0, NULL, HFILL }},
3237
3238
16
                { &hf_amf_number,
3239
16
                  { "Number", "amf.number", FT_DOUBLE, BASE_NONE,
3240
16
                    NULL, 0x0, "AMF number", HFILL }},
3241
3242
16
                { &hf_amf_integer,
3243
16
                  { "Integer", "amf.integer", FT_UINT32, BASE_DEC,
3244
16
                    NULL, 0x0, "RTMPT AMF3 integer", HFILL }},
3245
3246
16
                { &hf_amf_boolean,
3247
16
                  { "Boolean", "amf.boolean", FT_BOOLEAN, BASE_NONE,
3248
16
                    NULL, 0x0, "AMF boolean", HFILL }},
3249
3250
16
                { &hf_amf_stringlength,
3251
16
                  { "String length", "amf.stringlength", FT_UINT32, BASE_DEC,
3252
16
                    NULL, 0x0, "AMF string length", HFILL }},
3253
3254
16
                { &hf_amf_string,
3255
16
                  { "String", "amf.string", FT_STRING, BASE_NONE,
3256
16
                    NULL, 0x0, "AMF string", HFILL }},
3257
3258
16
                { &hf_amf_string_reference,
3259
16
                  { "String reference", "amf.string_reference", FT_UINT32, BASE_DEC,
3260
16
                    NULL, 0x0, "RTMPT AMF3 string reference", HFILL }},
3261
3262
16
                { &hf_amf_object_reference,
3263
16
                  { "Object reference", "amf.object_reference", FT_UINT32, BASE_DEC,
3264
16
                    NULL, 0x0, "AMF object reference", HFILL }},
3265
3266
16
                { &hf_amf_date,
3267
16
                  { "Date", "amf.date", FT_ABSOLUTE_TIME, ABSOLUTE_TIME_LOCAL,
3268
16
                    NULL, 0x0, "AMF date", HFILL }},
3269
3270
#if 0
3271
                { &hf_amf_longstringlength,
3272
                  { "String length", "amf.longstringlength", FT_UINT32, BASE_DEC,
3273
                    NULL, 0x0, "AMF long string length", HFILL }},
3274
#endif
3275
3276
16
                { &hf_amf_longstring,
3277
16
                  { "Long string", "amf.longstring", FT_STRING, BASE_NONE,
3278
16
                    NULL, 0x0, "AMF long string", HFILL }},
3279
3280
16
                { &hf_amf_xml_doc,
3281
16
                  { "XML document", "amf.xml_doc", FT_STRING, BASE_NONE,
3282
16
                    NULL, 0x0, "AMF XML document", HFILL }},
3283
3284
16
                { &hf_amf_xmllength,
3285
16
                  { "XML text length", "amf.xmllength", FT_UINT32, BASE_DEC,
3286
16
                    NULL, 0x0, "AMF E4X XML length", HFILL }},
3287
3288
16
                { &hf_amf_xml,
3289
16
                  { "XML", "amf.xml", FT_STRING, BASE_NONE,
3290
16
                    NULL, 0x0, "AMF E4X XML", HFILL }},
3291
3292
16
                { &hf_amf_int64,
3293
16
                  { "Int64", "amf.int64", FT_INT64, BASE_DEC,
3294
16
                    NULL, 0x0, "AMF int64", HFILL }},
3295
3296
16
                { &hf_amf_bytearraylength,
3297
16
                  { "ByteArray length", "amf.bytearraylength", FT_UINT32, BASE_DEC,
3298
16
                    NULL, 0x0, "RTMPT AMF3 ByteArray length", HFILL }},
3299
3300
16
                { &hf_amf_bytearray,
3301
16
                  { "ByteArray", "amf.bytearray", FT_BYTES, BASE_NONE,
3302
16
                    NULL, 0x0, "RTMPT AMF3 ByteArray", HFILL }},
3303
3304
/* AMF object types and subfields of the object types */
3305
16
                { &hf_amf_object,
3306
16
                  { "Object", "amf.object", FT_NONE, BASE_NONE,
3307
16
                    NULL, 0x0, "AMF object", HFILL }},
3308
3309
16
                { &hf_amf_traitcount,
3310
16
                  { "Trait count", "amf.traitcount", FT_UINT32, BASE_DEC,
3311
16
                    NULL, 0x0, "AMF count of traits for an object", HFILL }},
3312
3313
16
                { &hf_amf_classnamelength,
3314
16
                  { "Class name length", "amf.classnamelength", FT_UINT32, BASE_DEC,
3315
16
                    NULL, 0x0, "AMF class name length", HFILL }},
3316
3317
16
                { &hf_amf_classname,
3318
16
                  { "Class name", "amf.classname", FT_STRING, BASE_NONE,
3319
16
                    NULL, 0x0, "AMF class name", HFILL }},
3320
3321
16
                { &hf_amf_membernamelength,
3322
16
                  { "Member name length", "amf.membernamelength", FT_UINT32, BASE_DEC,
3323
16
                    NULL, 0x0, "AMF member name length", HFILL }},
3324
3325
16
                { &hf_amf_membername,
3326
16
                  { "Member name", "amf.membername", FT_STRING, BASE_NONE,
3327
16
                    NULL, 0x0, "AMF member name", HFILL }},
3328
3329
16
                { &hf_amf_trait_reference,
3330
16
                  { "Trait reference", "amf.trait_reference", FT_UINT32, BASE_DEC,
3331
16
                    NULL, 0x0, "AMF trait reference", HFILL }},
3332
3333
16
                { &hf_amf_ecmaarray,
3334
16
                  { "ECMA array", "amf.ecmaarray", FT_NONE, BASE_NONE,
3335
16
                    NULL, 0x0, "AMF ECMA array", HFILL }},
3336
3337
16
                { &hf_amf_strictarray,
3338
16
                  { "Strict array", "amf.strictarray", FT_NONE, BASE_NONE,
3339
16
                    NULL, 0x0, "AMF strict array", HFILL }},
3340
3341
16
                { &hf_amf_array,
3342
16
                  { "Array", "amf.array", FT_NONE, BASE_NONE,
3343
16
                    NULL, 0x0, "RTMPT AMF3 array", HFILL }},
3344
3345
16
                { &hf_amf_arraylength,
3346
16
                  { "Array length", "amf.arraylength", FT_UINT32, BASE_DEC,
3347
16
                    NULL, 0x0, "AMF array length", HFILL }},
3348
3349
16
                { &hf_amf_arraydenselength,
3350
16
                  { "Length of dense portion", "amf.arraydenselength", FT_UINT32, BASE_DEC,
3351
16
                    NULL, 0x0, "AMF length of dense portion of array", HFILL }},
3352
3353
16
                { &hf_amf_end_of_object_marker,
3354
16
                  { "End Of Object Marker", "amf.end_of_object_marker", FT_NONE, BASE_NONE,
3355
16
                    NULL, 0x0, NULL, HFILL }},
3356
3357
16
                { &hf_amf_end_of_associative_part,
3358
16
                  { "End of associative part", "amf.end_of_associative_part", FT_NONE, BASE_NONE,
3359
16
                    NULL, 0x0, NULL, HFILL }},
3360
3361
16
                { &hf_amf_end_of_dynamic_members,
3362
16
                  { "End Of dynamic members", "amf.end_of_dynamic_members", FT_NONE, BASE_NONE,
3363
16
                    NULL, 0x0, NULL, HFILL }},
3364
16
        };
3365
3366
16
        static ei_register_info ei[] = {
3367
16
                { &ei_amf_loop, { "amf.loop", PI_MALFORMED, PI_ERROR, "Loop in AMF dissection", EXPFILL }}
3368
16
        };
3369
3370
3371
16
        static int *ett[] = {
3372
16
                &ett_amf,
3373
16
                &ett_amf_headers,
3374
16
                &ett_amf_messages,
3375
16
                &ett_amf_value,
3376
16
                &ett_amf_property,
3377
16
                &ett_amf_string,
3378
16
                &ett_amf_array_element,
3379
16
                &ett_amf_traits,
3380
16
                &ett_amf_trait_member,
3381
16
        };
3382
3383
16
        expert_module_t* expert_amf;
3384
3385
16
        proto_amf = proto_register_protocol("Action Message Format", "AMF", "amf");
3386
16
        proto_register_field_array(proto_amf, hf, array_length(hf));
3387
16
        proto_register_subtree_array(ett, array_length(ett));
3388
16
        expert_amf = expert_register_protocol(proto_amf);
3389
16
        expert_register_field_array(expert_amf, ei, array_length(ei));
3390
3391
16
        amf_handle = register_dissector("amf", dissect_amf, proto_amf);
3392
16
}
3393
3394
void
3395
proto_reg_handoff_rtmpt(void)
3396
16
{
3397
16
        heur_dissector_add("tcp", dissect_rtmpt_heur, "RTMPT over TCP", "rtmpt_tcp", proto_rtmpt, HEURISTIC_DISABLE);
3398
16
        dissector_add_uint_with_preference("tcp.port", RTMP_PORT, rtmpt_tcp_handle);
3399
3400
16
        dissector_add_string("media_type", "application/x-fcs", rtmpt_http_handle);
3401
16
        dissector_add_string("media_type", "application/x-amf", amf_handle);
3402
16
}
3403
3404
/*
3405
 * Editor modelines  -  https://www.wireshark.org/tools/modelines.html
3406
 *
3407
 * Local variables:
3408
 * c-basic-offset: 8
3409
 * tab-width: 8
3410
 * indent-tabs-mode: nil
3411
 * End:
3412
 *
3413
 * vi: set shiftwidth=8 tabstop=8 expandtab:
3414
 * :indentSize=8:tabSize=8:noTabs=true:
3415
 */