Coverage Report

Created: 2026-05-14 06:28

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