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