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