HACK: 2nd try to match RowsetProperties
[wireshark-wip.git] / epan / dissectors / packet-rtmpt.c
blob3fdc645a63efde4422adeee4ff0db4c1094968bb
1 /* packet-rtmpt.c
2 * Routines for Real Time Messaging Protocol packet dissection
3 * metatech <metatech@flashmail.com>
5 * $Id$
7 * Wireshark - Network traffic analyzer
8 * By Gerald Combs <gerald@wireshark.org>
9 * Copyright 1998 Gerald Combs
11 * This program is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU General Public License
13 * as published by the Free Software Foundation; either version 2
14 * of the License, or (at your option) any later version.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
26 /* This dissector is called RTMPT to avoid a conflict with
27 * the other RTMP protocol (Routing Table Maintenance Protocol) implemented in packet-atalk.c
28 * (RTMPT normally stands for RTMP-Tunnel via http)
30 * RTMP in a nutshell
32 * The protocol has very few "magic words" to facilitate detection,
33 * but rather has "magic lengths".
34 * This protocol has plenty of special cases and few general rules,
35 * especially regarding the lengths and the structures.
37 * Documentation:
38 * RTMP protocol description on Wiki of Red5 Open Source Flash Server at
40 * http://trac.red5.org/wiki/Codecs/RTMPSpecification
42 * and the pages to which it links:
44 * http://osflash.org/documentation/rtmp
45 * http://wiki.gnashdev.org/RTMP
46 * http://wiki.gnashdev.org/RTMP_Messages_Decoded
47 * http://www.acmewebworks.com/Downloads/openCS/TheAMF.pdf
48 * http://www.gnashdev.org/files/rtmp-decoded.pdf
50 * It's also available from Adobe at
52 * http://wwwimages.adobe.com/www.adobe.com/content/dam/Adobe/en/devnet/rtmp/pdf/rtmp_specification_1.0.pdf
54 * For AMF, see:
56 * http://download.macromedia.com/pub/labs/amf/amf0_spec_121207.pdf
58 * for AMF0 and
60 * http://amf3cplusplus.googlecode.com/svn-history/r4/trunk/doc/amf3_spec_05_05_08.pdf
62 * for AMF3.
64 * Default TCP port is 1935
67 #include "config.h"
69 #include <string.h>
71 #include <epan/packet.h>
72 #include <wsutil/pint.h>
74 #include <epan/wmem/wmem.h>
75 #include <epan/conversation.h>
76 #include <epan/strutil.h>
77 #include <epan/prefs.h>
78 #include "packet-tcp.h"
80 /* #define DEBUG_RTMPT 1 */
82 static int proto_rtmpt = -1;
84 static int hf_rtmpt_handshake_c0 = -1;
85 static int hf_rtmpt_handshake_s0 = -1;
86 static int hf_rtmpt_handshake_c1 = -1;
87 static int hf_rtmpt_handshake_s1 = -1;
88 static int hf_rtmpt_handshake_c2 = -1;
89 static int hf_rtmpt_handshake_s2 = -1;
91 static int hf_rtmpt_header_format = -1;
92 static int hf_rtmpt_header_csid = -1;
93 static int hf_rtmpt_header_timestamp = -1;
94 static int hf_rtmpt_header_timestamp_delta = -1;
95 static int hf_rtmpt_header_body_size = -1;
96 static int hf_rtmpt_header_typeid = -1;
97 static int hf_rtmpt_header_streamid = -1;
98 static int hf_rtmpt_header_ets = -1;
100 static int hf_rtmpt_scm_chunksize = -1;
101 static int hf_rtmpt_scm_csid = -1;
102 static int hf_rtmpt_scm_seq = -1;
103 static int hf_rtmpt_scm_was = -1;
104 static int hf_rtmpt_scm_limittype = -1;
106 static int hf_rtmpt_ucm_eventtype = -1;
108 static int hf_rtmpt_function_call = -1;
109 static int hf_rtmpt_function_response = -1;
111 static int hf_rtmpt_audio_control = -1;
112 static int hf_rtmpt_audio_format = -1;
113 static int hf_rtmpt_audio_rate = -1;
114 static int hf_rtmpt_audio_size = -1;
115 static int hf_rtmpt_audio_type = -1;
116 static int hf_rtmpt_audio_data = -1;
118 static int hf_rtmpt_video_control = -1;
119 static int hf_rtmpt_video_type = -1;
120 static int hf_rtmpt_video_format = -1;
121 static int hf_rtmpt_video_data = -1;
123 static int hf_rtmpt_tag_type = -1;
124 static int hf_rtmpt_tag_datasize = -1;
125 static int hf_rtmpt_tag_timestamp = -1;
126 static int hf_rtmpt_tag_ets = -1;
127 static int hf_rtmpt_tag_streamid = -1;
128 static int hf_rtmpt_tag_tagsize = -1;
130 static gint ett_rtmpt = -1;
131 static gint ett_rtmpt_handshake = -1;
132 static gint ett_rtmpt_header = -1;
133 static gint ett_rtmpt_body = -1;
134 static gint ett_rtmpt_ucm = -1;
135 static gint ett_rtmpt_audio_control = -1;
136 static gint ett_rtmpt_video_control = -1;
137 static gint ett_rtmpt_tag = -1;
138 static gint ett_rtmpt_tag_data = -1;
140 static dissector_handle_t rtmpt_tcp_handle;
141 static dissector_handle_t rtmpt_http_handle;
143 static gboolean rtmpt_desegment = TRUE;
145 /* Native Bandwidth Detection (using the checkBandwidth(), onBWCheck(),
146 * onBWDone() calls) transmits a series of increasing size packets over
147 * the course of 2 seconds. On a fast link the largest packet can just
148 * exceed 256KB, but setting the limit there can cause massive memory
149 * usage in some scenarios. For now make it a preference, but a better fix
150 * is really needed.
151 * See https://bugs.wireshark.org/bugzilla/show_bug.cgi?id=6898
153 static guint rtmpt_max_packet_size = 32768;
155 #define RTMP_PORT 1935
157 #define RTMPT_MAGIC 0x03
158 #define RTMPT_HANDSHAKE_OFFSET_1 1
159 #define RTMPT_HANDSHAKE_OFFSET_2 1538
160 #define RTMPT_HANDSHAKE_OFFSET_3 3074
161 #define RTMPT_HANDSHAKE_LENGTH_1 1537
162 #define RTMPT_HANDSHAKE_LENGTH_2 3073
163 #define RTMPT_HANDSHAKE_LENGTH_3 1536
164 #define RTMPT_DEFAULT_CHUNK_SIZE 128
166 #define RTMPT_ID_MAX 65599
167 #define RTMPT_TYPE_HANDSHAKE_1 0x100001
168 #define RTMPT_TYPE_HANDSHAKE_2 0x100002
169 #define RTMPT_TYPE_HANDSHAKE_3 0x100003
171 #define RTMPT_TYPE_CHUNK_SIZE 0x01
172 #define RTMPT_TYPE_ABORT_MESSAGE 0x02
173 #define RTMPT_TYPE_ACKNOWLEDGEMENT 0x03
174 #define RTMPT_TYPE_UCM 0x04
175 #define RTMPT_TYPE_WINDOW 0x05
176 #define RTMPT_TYPE_PEER_BANDWIDTH 0x06
177 #define RTMPT_TYPE_AUDIO_DATA 0x08
178 #define RTMPT_TYPE_VIDEO_DATA 0x09
179 #define RTMPT_TYPE_DATA_AMF3 0x0F
180 #define RTMPT_TYPE_SHARED_AMF3 0x10
181 #define RTMPT_TYPE_COMMAND_AMF3 0x11
182 #define RTMPT_TYPE_DATA_AMF0 0x12
183 #define RTMPT_TYPE_SHARED_AMF0 0x13
184 #define RTMPT_TYPE_COMMAND_AMF0 0x14
185 #define RTMPT_TYPE_AGGREGATE 0x16
187 #define RTMPT_UCM_STREAM_BEGIN 0x00
188 #define RTMPT_UCM_STREAM_EOF 0x01
189 #define RTMPT_UCM_STREAM_DRY 0x02
190 #define RTMPT_UCM_SET_BUFFER 0x03
191 #define RTMPT_UCM_STREAM_ISRECORDED 0x04
192 #define RTMPT_UCM_PING_REQUEST 0x06
193 #define RTMPT_UCM_PING_RESPONSE 0x07
195 #define RTMPT_TEXT_RTMP_HEADER "RTMP Header"
196 #define RTMPT_TEXT_RTMP_BODY "RTMP Body"
198 static const value_string rtmpt_handshake_vals[] = {
199 { RTMPT_TYPE_HANDSHAKE_1, "Handshake C0+C1" },
200 { RTMPT_TYPE_HANDSHAKE_2, "Handshake S0+S1+S2" },
201 { RTMPT_TYPE_HANDSHAKE_3, "Handshake C2" },
202 { 0, NULL }
205 static const value_string rtmpt_opcode_vals[] = {
206 { RTMPT_TYPE_CHUNK_SIZE, "Set Chunk Size" },
207 { RTMPT_TYPE_ABORT_MESSAGE, "Abort Message" },
208 { RTMPT_TYPE_ACKNOWLEDGEMENT, "Acknowledgement" },
209 { RTMPT_TYPE_UCM, "User Control Message" },
210 { RTMPT_TYPE_WINDOW, "Window Acknowledgement Size" },
211 { RTMPT_TYPE_PEER_BANDWIDTH, "Set Peer Bandwidth" },
212 { RTMPT_TYPE_AUDIO_DATA, "Audio Data" },
213 { RTMPT_TYPE_VIDEO_DATA, "Video Data" },
214 { RTMPT_TYPE_DATA_AMF3, "AMF3 Data" },
215 { RTMPT_TYPE_SHARED_AMF3, "AMF3 Shared Object" },
216 { RTMPT_TYPE_COMMAND_AMF3, "AMF3 Command" },
217 { RTMPT_TYPE_DATA_AMF0, "AMF0 Data" },
218 { RTMPT_TYPE_SHARED_AMF0, "AMF0 Shared Object" },
219 { RTMPT_TYPE_COMMAND_AMF0, "AMF0 Command" },
220 { RTMPT_TYPE_AGGREGATE, "Aggregate" },
221 { 0, NULL }
224 static const value_string rtmpt_limit_vals[] = {
225 /* These are a complete guess, from the order of the documented
226 * options - the values aren't actually specified */
227 { 0, "Hard" },
228 { 1, "Soft" },
229 { 2, "Dynamic" },
230 { 0, NULL }
233 static const value_string rtmpt_ucm_vals[] = {
234 { RTMPT_UCM_STREAM_BEGIN, "Stream Begin" },
235 { RTMPT_UCM_STREAM_EOF, "Stream EOF" },
236 { RTMPT_UCM_STREAM_DRY, "Stream Dry" },
237 { RTMPT_UCM_SET_BUFFER, "Set Buffer Length" },
238 { RTMPT_UCM_STREAM_ISRECORDED, "Stream Is Recorded" },
239 { RTMPT_UCM_PING_REQUEST, "Ping Request" },
240 { RTMPT_UCM_PING_RESPONSE, "Ping Response" },
241 { 0, NULL }
244 static const value_string rtmpt_tag_vals[] = {
245 { RTMPT_TYPE_AUDIO_DATA, "Audio Tag" },
246 { RTMPT_TYPE_VIDEO_DATA, "Video Tag" },
247 { RTMPT_TYPE_DATA_AMF0, "Script Tag" },
248 { 0, NULL }
251 /* [Spec] http://www.adobe.com/content/dam/Adobe/en/devnet/rtmp/pdf/rtmp_specification_1.0.pdf */
252 /* [DevG] http://help.adobe.com/en_US/flashmediaserver/devguide/index.html "working with Live Video" => Adding metadata to a live stream */
253 /* [SWF] http://www.adobe.com/content/dam/Adobe/en/devnet/swf/pdf/swf_file_format_spec_v10.pdf */
254 static const value_string rtmpt_audio_codecs[] = {
255 { 0, "Uncompressed" }, /* [DevG] */
256 { 1, "ADPCM" }, /* [DevG] */
257 { 2, "MP3" }, /* [DevG] */
258 { 3, "Uncompressed, little-endian"}, /* [SWF] */
259 { 4, "Nellymoser 16kHz" }, /* [SWF] */
260 { 5, "Nellymoser 8kHz" }, /* [DevG] [SWF]*/
261 { 6, "Nellymoser" }, /* [DevG] [SWF]*/
262 { 7, "G711A" }, /* [Spec] */
263 { 8, "G711U" }, /* [Spec] */
264 { 9, "Nellymoser 16kHz" }, /* [Spec] */
265 { 10, "HE-AAC" }, /* [DevG] */
266 { 11, "SPEEX" }, /* [DevG] */
267 { 0, NULL }
270 static const value_string rtmpt_audio_rates[] = {
271 { 0, "5.5 kHz" },
272 { 1, "11 kHz" },
273 { 2, "22 kHz" },
274 { 3, "44 kHz" },
275 { 0, NULL }
278 static const value_string rtmpt_audio_sizes[] = {
279 { 0, "8 bit" },
280 { 1, "16 bit" },
281 { 0, NULL }
284 static const value_string rtmpt_audio_types[] = {
285 { 0, "mono" },
286 { 1, "stereo" },
287 { 0, NULL }
290 static const value_string rtmpt_video_types[] = {
291 { 1, "keyframe" },
292 { 2, "inter-frame" },
293 { 3, "disposable inter-frame" },
294 { 0, NULL }
297 static const value_string rtmpt_video_codecs[] = {
298 { 2, "Sorensen H.263" },
299 { 3, "Screen video" },
300 { 4, "On2 VP6" },
301 { 5, "On2 VP6+alpha" },
302 { 6, "Screen video version 2" },
303 { 7, "H.264" },
304 { 0, NULL }
307 static int proto_amf = -1;
309 static int hf_amf_version = -1;
310 static int hf_amf_header_count = -1;
311 static int hf_amf_header_name = -1;
312 static int hf_amf_header_must_understand = -1;
313 static int hf_amf_header_length = -1;
314 /* static int hf_amf_header_value_type = -1; */
315 static int hf_amf_message_count = -1;
316 static int hf_amf_message_target_uri = -1;
317 static int hf_amf_message_response_uri = -1;
318 static int hf_amf_message_length = -1;
320 static int hf_amf_amf0_type = -1;
321 static int hf_amf_amf3_type = -1;
322 static int hf_amf_number = -1;
323 static int hf_amf_integer = -1;
324 static int hf_amf_boolean = -1;
325 static int hf_amf_stringlength = -1;
326 static int hf_amf_string = -1;
327 static int hf_amf_string_reference = -1;
328 static int hf_amf_object_reference = -1;
329 static int hf_amf_date = -1;
330 /* static int hf_amf_longstringlength = -1; */
331 static int hf_amf_longstring = -1;
332 static int hf_amf_xml_doc = -1;
333 static int hf_amf_xmllength = -1;
334 static int hf_amf_xml = -1;
335 static int hf_amf_int64 = -1;
336 static int hf_amf_bytearraylength = -1;
337 static int hf_amf_bytearray = -1;
339 static int hf_amf_object = -1;
340 static int hf_amf_traitcount = -1;
341 static int hf_amf_classnamelength = -1;
342 static int hf_amf_classname = -1;
343 static int hf_amf_membernamelength = -1;
344 static int hf_amf_membername = -1;
345 static int hf_amf_trait_reference = -1;
346 static int hf_amf_ecmaarray = -1;
347 static int hf_amf_strictarray = -1;
348 static int hf_amf_array = -1;
349 static int hf_amf_arraylength = -1;
350 static int hf_amf_arraydenselength = -1;
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;
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
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
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 }
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 }
438 /* Holds the reassembled data for a packet during un-chunking
440 typedef struct rtmpt_packet {
441 guint32 seq;
442 guint32 lastseq;
444 int resident;
445 union {
446 guint8 *p;
447 guint32 offset;
448 } data;
450 /* used during unchunking */
451 int want;
452 int have;
453 int chunkwant;
454 int chunkhave;
456 guint8 bhlen;
457 guint8 mhlen;
459 /* Chunk Basic Header */
460 guint8 fmt; /* byte 0 */
461 guint32 id; /* byte 0 */
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 */
469 guint32 txid;
470 gint isresponse;
471 gint otherframe;
473 } rtmpt_packet_t;
475 /* Represents a header or a chunk that is split over two TCP
476 * segments
478 typedef struct rtmpt_frag {
479 int ishdr;
480 guint32 seq;
481 guint32 lastseq;
482 int have;
483 int len;
485 union {
486 guint8 d[18]; /* enough for a complete header (3 + 11 + 4) */
487 guint32 id;
488 } saved;
489 } rtmpt_frag_t;
491 /* The full message header information for the last packet on a particular
492 * ID - used for defaulting short headers
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 */
501 wmem_tree_t *packets;
502 } rtmpt_id_t;
504 /* Historical view of a whole TCP connection
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;
515 #ifdef DEBUG_RTMPT
516 static void rtmpt_debug(const char *fmt, ...)
518 va_list args;
519 va_start(args, fmt);
520 vprintf(fmt, args);
522 #define RTMPT_DEBUG rtmpt_debug
523 #else
524 static void rtmpt_debug(const char *fmt, ...){ (void)fmt; }
525 #define RTMPT_DEBUG 1 ? (void)0 : rtmpt_debug
526 #endif
528 /* Header length helpers */
530 static gint rtmpt_basic_header_length(gint id)
532 switch (id & 0x3f) {
533 case 0: return 2;
534 case 1: return 3;
535 default: return 1;
539 static gint rtmpt_message_header_length(gint id)
541 switch ((id>>6) & 3) {
542 case 0: return 11;
543 case 1: return 7;
544 case 2: return 3;
545 default: return 0;
549 /* Lightweight access to AMF0 blobs - more complete dissection is done
550 * in dissect_rtmpt_body_command */
552 static gint
553 rtmpt_get_amf_length(tvbuff_t *tvb, gint offset)
555 guint8 iObjType;
556 gint remain = tvb_length_remaining(tvb, offset);
557 guint32 depth = 0;
558 gint itemlen = 0;
559 gint rv = 0;
561 while (rv==0 || depth>0) {
563 if (depth>0) {
564 if (remain-rv<2) return remain;
565 itemlen = tvb_get_ntohs(tvb, offset+rv) + 2;
566 if (remain-rv<itemlen+1) return remain;
567 rv += itemlen;
570 if (remain-rv<1) return remain;
571 iObjType = tvb_get_guint8(tvb, offset+rv);
573 if (depth>0 && itemlen==2 && iObjType==AMF0_END_OF_OBJECT) {
574 rv++;
575 depth--;
576 continue;
579 switch (iObjType) {
580 case AMF0_NUMBER:
581 itemlen = 9;
582 break;
583 case AMF0_BOOLEAN:
584 itemlen = 2;
585 break;
586 case AMF0_STRING:
587 if (remain-rv<3) return remain;
588 itemlen = tvb_get_ntohs(tvb, offset+rv+1) + 3;
589 break;
590 case AMF0_NULL:
591 case AMF0_UNDEFINED:
592 case AMF0_UNSUPPORTED:
593 itemlen= 1;
594 break;
595 case AMF0_DATE:
596 itemlen = 11;
597 break;
598 case AMF0_LONG_STRING:
599 case AMF0_XML:
600 if (remain-rv<5) return remain;
601 itemlen = tvb_get_ntohl(tvb, offset+rv+1) + 5;
602 break;
603 case AMF0_INT64:
604 itemlen = 9;
605 break;
606 case AMF0_OBJECT:
607 itemlen = 1;
608 depth++;
609 break;
610 case AMF0_ECMA_ARRAY:
611 itemlen = 5;
612 depth++;
613 break;
614 default:
615 return remain;
618 if (remain-rv<itemlen) return remain;
619 rv += itemlen;
623 return rv;
626 static gchar*
627 rtmpt_get_amf_param(tvbuff_t *tvb, gint offset, gint param, const gchar *prop)
629 guint32 remain = tvb_length_remaining(tvb, offset);
630 guint32 itemlen;
631 guint32 iStringLength;
633 while (remain>0 && param>0) {
634 itemlen = rtmpt_get_amf_length(tvb, offset);
635 offset += itemlen;
636 remain -= itemlen;
637 param--;
640 if (remain>0 && param==0) {
641 guint8 iObjType = tvb_get_guint8(tvb, offset);
643 if (!prop && iObjType==AMF0_STRING && remain>=3) {
644 iStringLength = tvb_get_ntohs(tvb, offset+1);
645 if (remain>=iStringLength+3) {
646 return tvb_get_string(wmem_packet_scope(), tvb, offset+3, iStringLength);
650 if (prop && iObjType==AMF0_OBJECT) {
651 offset++;
652 remain--;
654 while (remain>2) {
655 guint32 iPropLength = tvb_get_ntohs(tvb, offset);
656 if (remain<2+iPropLength+3) break;
658 if (tvb_strneql(tvb, offset+2, prop, strlen(prop))==0) {
659 if (tvb_get_guint8(tvb, offset+2+iPropLength)!=AMF0_STRING) break;
661 iStringLength = tvb_get_ntohs(tvb, offset+2+iPropLength+1);
662 if (remain<2+iPropLength+3+iStringLength) break;
664 return tvb_get_string(wmem_packet_scope(), tvb, offset+2+iPropLength+3, iStringLength);
667 itemlen = rtmpt_get_amf_length(tvb, offset+2+iPropLength);
668 offset += 2+iPropLength+itemlen;
669 remain -= 2+iPropLength+itemlen;
674 return NULL;
677 static guint32
678 rtmpt_get_amf_txid(tvbuff_t *tvb, gint offset)
680 guint32 remain = tvb_length_remaining(tvb, offset);
682 if (remain>0) {
683 guint32 itemlen = rtmpt_get_amf_length(tvb, offset);
684 if (remain<itemlen) return 0;
685 offset += itemlen;
686 remain -= itemlen;
688 if (remain>=9) {
689 guint8 iObjType = tvb_get_guint8(tvb, offset);
690 if (iObjType==AMF0_NUMBER) {
691 return (guint32)tvb_get_ntohieee_double(tvb, offset+1);
695 return 0;
699 /* Generate a useful description for various packet types */
701 static gchar*
702 rtmpt_get_packet_desc(tvbuff_t *tvb, guint32 offset, guint32 remain, rtmpt_conv_t *rconv, int cdir, rtmpt_packet_t *tp, gint *deschasopcode)
704 if (tp->cmd==RTMPT_TYPE_CHUNK_SIZE || tp->cmd==RTMPT_TYPE_ABORT_MESSAGE ||
705 tp->cmd==RTMPT_TYPE_ACKNOWLEDGEMENT || tp->cmd==RTMPT_TYPE_WINDOW) {
706 if (tp->len>=4 && remain>=4) {
707 *deschasopcode = TRUE;
708 return wmem_strdup_printf(wmem_packet_scope(), "%s %d",
709 val_to_str(tp->cmd, rtmpt_opcode_vals, "Unknown (0x%01x)"),
710 tvb_get_ntohl(tvb, offset));
713 } else if (tp->cmd==RTMPT_TYPE_PEER_BANDWIDTH) {
714 if (tp->len>=5 && remain>=5) {
715 *deschasopcode = TRUE;
716 return wmem_strdup_printf(wmem_packet_scope(), "%s %d,%s",
717 val_to_str(tp->cmd, rtmpt_opcode_vals, "Unknown (0x%01x)"),
718 tvb_get_ntohl(tvb, offset),
719 val_to_str(tvb_get_guint8(tvb, offset+4), rtmpt_limit_vals, "Unknown (%d)"));
722 } else if (tp->cmd==RTMPT_TYPE_UCM) {
723 guint16 iUCM = -1;
724 const gchar *sFunc = NULL;
725 const gchar *sParam = "";
727 if (tp->len<2 || remain<2) return NULL;
729 iUCM = tvb_get_ntohs(tvb, offset);
730 sFunc = try_val_to_str(iUCM, rtmpt_ucm_vals);
731 if (sFunc==NULL) {
732 *deschasopcode = TRUE;
733 sFunc = wmem_strdup_printf(wmem_packet_scope(), "User Control Message 0x%01x", iUCM);
736 if (iUCM==RTMPT_UCM_STREAM_BEGIN || iUCM==RTMPT_UCM_STREAM_EOF ||
737 iUCM==RTMPT_UCM_STREAM_DRY || iUCM==RTMPT_UCM_STREAM_ISRECORDED) {
738 if (tp->len>=6 && remain>=6) {
739 sParam = wmem_strdup_printf(wmem_packet_scope(), " %d", tvb_get_ntohl(tvb, offset+2));
741 } else if (iUCM==RTMPT_UCM_SET_BUFFER) {
742 if (tp->len>=10 && remain>=10) {
743 sParam = wmem_strdup_printf(wmem_packet_scope(), " %d,%dms",
744 tvb_get_ntohl(tvb, offset+2),
745 tvb_get_ntohl(tvb, offset+6));
749 return wmem_strdup_printf(wmem_packet_scope(), "%s%s", sFunc, sParam);
751 } else if (tp->cmd==RTMPT_TYPE_COMMAND_AMF0 || tp->cmd==RTMPT_TYPE_COMMAND_AMF3 ||
752 tp->cmd==RTMPT_TYPE_DATA_AMF0 || tp->cmd==RTMPT_TYPE_DATA_AMF3) {
753 guint32 slen = 0;
754 guint32 soff = 0;
755 gchar *sFunc = NULL;
756 gchar *sParam = NULL;
758 if (tp->cmd==RTMPT_TYPE_COMMAND_AMF3 || tp->cmd==RTMPT_TYPE_DATA_AMF3) {
759 soff = 1;
761 if (tp->len>=3+soff && remain>=3+soff) {
762 slen = tvb_get_ntohs(tvb, offset+1+soff);
764 if (slen>0) {
765 sFunc = tvb_get_string(wmem_packet_scope(), tvb, offset+3+soff, slen);
766 RTMPT_DEBUG("got function call '%s'\n", sFunc);
768 if (strcmp(sFunc, "connect")==0) {
769 sParam = rtmpt_get_amf_param(tvb, offset+soff, 2, "app");
770 } else if (strcmp(sFunc, "play")==0) {
771 sParam = rtmpt_get_amf_param(tvb, offset+soff, 3, NULL);
772 } else if (strcmp(sFunc, "play2")==0) {
773 sParam = rtmpt_get_amf_param(tvb, offset+soff, 3, "streamName");
774 } else if (strcmp(sFunc, "releaseStream")==0) {
775 sParam = rtmpt_get_amf_param(tvb, offset+soff, 3, NULL);
776 } else if (strcmp(sFunc, "FCPublish")==0) {
777 sParam = rtmpt_get_amf_param(tvb, offset+soff, 3, NULL);
778 } else if (strcmp(sFunc, "publish")==0) {
779 sParam = rtmpt_get_amf_param(tvb, offset+soff, 3, NULL);
780 } else if (strcmp(sFunc, "onStatus")==0) {
781 if (tp->cmd==RTMPT_TYPE_COMMAND_AMF0 || tp->cmd==RTMPT_TYPE_COMMAND_AMF3) {
782 sParam = rtmpt_get_amf_param(tvb, offset+soff, 3, "code");
783 } else {
784 sParam = rtmpt_get_amf_param(tvb, offset+soff, 1, "code");
786 } else if (strcmp(sFunc, "onPlayStatus")==0) {
787 sParam = rtmpt_get_amf_param(tvb, offset+soff, 1, "code");
788 } else if (strcmp(sFunc, "_result")==0) {
789 sParam = rtmpt_get_amf_param(tvb, offset+soff, 3, "code");
790 tp->isresponse = TRUE;
791 } else if (strcmp(sFunc, "_error")==0) {
792 sParam = rtmpt_get_amf_param(tvb, offset+soff, 3, "code");
793 tp->isresponse = TRUE;
796 if (tp->txid!=0 && tp->otherframe==0) {
797 tp->otherframe = GPOINTER_TO_INT(wmem_tree_lookup32(rconv->txids[cdir^1], tp->txid));
798 if (tp->otherframe) {
799 RTMPT_DEBUG("got otherframe=%d\n", tp->otherframe);
804 if (sFunc) {
805 if (sParam) {
806 return wmem_strdup_printf(wmem_packet_scope(), "%s('%s')", sFunc, sParam);
807 } else {
808 return wmem_strdup_printf(wmem_packet_scope(), "%s()", sFunc);
813 return NULL;
817 /* Tree dissection helpers for various packet body forms */
819 static void
820 dissect_rtmpt_body_scm(tvbuff_t *tvb, gint offset, proto_tree *rtmpt_tree, guint scm)
822 switch (scm) {
823 case RTMPT_TYPE_CHUNK_SIZE:
824 proto_tree_add_item(rtmpt_tree, hf_rtmpt_scm_chunksize, tvb, offset, 4, ENC_BIG_ENDIAN);
825 break;
826 case RTMPT_TYPE_ABORT_MESSAGE:
827 proto_tree_add_item(rtmpt_tree, hf_rtmpt_scm_csid, tvb, offset, 4, ENC_BIG_ENDIAN);
828 break;
829 case RTMPT_TYPE_ACKNOWLEDGEMENT:
830 proto_tree_add_item(rtmpt_tree, hf_rtmpt_scm_seq, tvb, offset, 4, ENC_BIG_ENDIAN);
831 break;
832 case RTMPT_TYPE_UCM:
833 proto_tree_add_item(rtmpt_tree, hf_rtmpt_ucm_eventtype, tvb, offset, 2, ENC_BIG_ENDIAN);
834 break;
835 case RTMPT_TYPE_WINDOW:
836 proto_tree_add_item(rtmpt_tree, hf_rtmpt_scm_was, tvb, offset, 4, ENC_BIG_ENDIAN);
837 break;
838 case RTMPT_TYPE_PEER_BANDWIDTH:
839 proto_tree_add_item(rtmpt_tree, hf_rtmpt_scm_was, tvb, offset, 4, ENC_BIG_ENDIAN);
840 proto_tree_add_item(rtmpt_tree, hf_rtmpt_scm_limittype, tvb, offset+4, 1, ENC_BIG_ENDIAN);
841 break;
845 static gint
846 dissect_amf0_value_type(tvbuff_t *tvb, gint offset, proto_tree *tree, gboolean *amf3_encoding, proto_item *parent_ti);
849 * A "property list" is a sequence of name/value pairs, terminated by
850 * and "end of object" indicator. AMF0 "object"s and "ECMA array"s
851 * are encoded as property lists.
853 static gint
854 dissect_amf0_property_list(tvbuff_t *tvb, gint offset, proto_tree *tree, guint *countp, gboolean *amf3_encoding)
856 proto_item *prop_ti;
857 proto_tree *prop_tree;
858 proto_item *name_ti;
859 proto_tree *name_tree;
860 guint iStringLength;
861 gchar *iStringValue;
862 guint count = 0;
865 * XXX - at least as I read "3.1 AVM+ Type Marker" in the AMF0
866 * specification, the AVM+ Type Marker only affects "the following
867 * Object". For now, we have a single "AMF3 encoding" flag, and
868 * set it when we see the type marker, and never clear it.
870 for (;;) {
871 /* UTF-8: property name */
872 iStringLength = tvb_get_ntohs(tvb, offset);
873 if (iStringLength == 0 &&
874 tvb_get_guint8(tvb, offset + 2) == AMF0_END_OF_OBJECT)
875 break;
876 count++;
877 iStringValue = tvb_get_string(wmem_packet_scope(), tvb, offset + 2, iStringLength);
878 prop_ti = proto_tree_add_text(tree, tvb, offset, -1,
879 "Property '%s'",
880 iStringValue);
881 prop_tree = proto_item_add_subtree(prop_ti, ett_amf_property);
883 name_ti = proto_tree_add_text(prop_tree, tvb,
884 offset, 2+iStringLength,
885 "Name: %s", iStringValue);
886 name_tree = proto_item_add_subtree(name_ti, ett_amf_string);
888 proto_tree_add_uint(name_tree, hf_amf_stringlength, tvb, offset, 2, iStringLength);
889 offset += 2;
890 proto_tree_add_item(name_tree, hf_amf_string, tvb, offset, iStringLength, ENC_UTF_8|ENC_NA);
891 offset += iStringLength;
893 /* value-type: property value */
894 offset = dissect_amf0_value_type(tvb, offset, prop_tree, amf3_encoding, prop_ti);
895 proto_item_set_end(prop_ti, tvb, offset);
897 proto_tree_add_text(tree, tvb, offset, 3, "End Of Object Marker");
898 offset += 3;
900 *countp = count;
902 return offset;
905 static gint
906 dissect_amf0_value_type(tvbuff_t *tvb, gint offset, proto_tree *tree, gboolean *amf3_encoding, proto_item *parent_ti)
908 guint8 iObjType;
909 proto_item *ti;
910 proto_tree *val_tree;
911 gint iValueOffset = offset;
912 guint32 iIntegerValue;
913 double iDoubleValue;
914 gboolean iBooleanValue;
915 guint iStringLength;
916 gchar *iStringValue;
917 guint iArrayLength;
918 guint i;
919 nstime_t t;
920 gint64 iInteger64Value;
921 guint count;
923 iObjType = tvb_get_guint8(tvb, offset);
924 if (parent_ti != NULL)
925 proto_item_append_text(parent_ti, " %s",
926 val_to_str_const(iObjType, amf0_type_vals, "Unknown"));
927 switch (iObjType) {
929 case AMF0_OBJECT:
931 * For object types, make the top-level protocol tree
932 * item a field for that type.
934 ti = proto_tree_add_item(tree, hf_amf_object, tvb, offset, -1, ENC_NA);
935 break;
937 case AMF0_ECMA_ARRAY:
939 * For ECMA array types, make the top-level protocol tree
940 * item a field for that type.
942 ti = proto_tree_add_item(tree, hf_amf_ecmaarray, tvb, offset, -1, ENC_NA);
943 break;
945 case AMF0_STRICT_ARRAY:
947 * For strict array types, make the top-level protocol tree
948 * item a field for that type.
950 ti = proto_tree_add_item(tree, hf_amf_strictarray, tvb, offset, -1, ENC_NA);
951 break;
953 default:
955 * For all other types, make it just a text item; the
956 * field for that type will be used for the value.
958 ti = proto_tree_add_text(tree, tvb, offset, -1, "%s",
959 val_to_str_const(iObjType, amf0_type_vals, "Unknown"));
960 break;
963 val_tree = proto_item_add_subtree(ti, ett_amf_value);
964 proto_tree_add_uint(val_tree, hf_amf_amf0_type, tvb, iValueOffset, 1, iObjType);
965 iValueOffset++;
967 switch (iObjType) {
968 case AMF0_NUMBER:
969 iDoubleValue = tvb_get_ntohieee_double(tvb, iValueOffset);
970 proto_tree_add_double(val_tree, hf_amf_number, tvb, iValueOffset, 8, iDoubleValue);
971 iValueOffset += 8;
972 proto_item_append_text(ti, " %." G_STRINGIFY(DBL_DIG) "g", iDoubleValue);
973 if (parent_ti != NULL)
974 proto_item_append_text(parent_ti, " %." G_STRINGIFY(DBL_DIG) "g", iDoubleValue);
975 break;
976 case AMF0_BOOLEAN:
977 iBooleanValue = tvb_get_guint8(tvb, iValueOffset);
978 proto_tree_add_boolean(val_tree, hf_amf_boolean, tvb, iValueOffset, 1, iBooleanValue);
979 iValueOffset += 1;
980 proto_item_append_text(ti, iBooleanValue ? " true" : " false");
981 if (parent_ti != NULL)
982 proto_item_append_text(parent_ti, iBooleanValue ? " true" : " false");
983 break;
984 case AMF0_STRING:
985 iStringLength = tvb_get_ntohs(tvb, iValueOffset);
986 proto_tree_add_uint(val_tree, hf_amf_stringlength, tvb, iValueOffset, 2, iStringLength);
987 iValueOffset += 2;
988 iStringValue = tvb_get_string_enc(wmem_packet_scope(), tvb, iValueOffset, iStringLength, ENC_UTF_8|ENC_NA);
989 if (iStringLength != 0)
990 proto_tree_add_string(val_tree, hf_amf_string, tvb, iValueOffset, iStringLength, iStringValue);
991 iValueOffset += iStringLength;
992 proto_item_append_text(ti, " '%s'", iStringValue);
993 if (parent_ti != NULL)
994 proto_item_append_text(parent_ti, " '%s'", iStringValue);
995 break;
996 case AMF0_OBJECT:
997 iValueOffset = dissect_amf0_property_list(tvb, iValueOffset, val_tree, &count, amf3_encoding);
998 proto_item_append_text(ti, " (%u items)", count);
999 break;
1000 case AMF0_NULL:
1001 case AMF0_UNDEFINED:
1002 break;
1003 case AMF0_REFERENCE:
1004 iIntegerValue = tvb_get_ntohs(tvb, iValueOffset);
1005 proto_tree_add_uint(val_tree, hf_amf_object_reference, tvb, iValueOffset, 2, iIntegerValue);
1006 iValueOffset += 2;
1007 proto_item_append_text(ti, " %d", iIntegerValue);
1008 break;
1009 case AMF0_ECMA_ARRAY:
1011 * Counted list type, with end marker. The count appears to be
1012 * more of a hint than a rule, and is sometimes sent as 0 or
1013 * invalid.
1015 * Basically the same as OBJECT but with the extra count field.
1016 * There being many strange encoders/metadata injectors out
1017 * there, sometimes you see a valid count and no end marker.
1018 * Figuring out which you've got for a deeply nested structure
1019 * is non-trivial.
1021 iArrayLength = tvb_get_ntohl(tvb, iValueOffset);
1022 proto_tree_add_uint(val_tree, hf_amf_arraylength, tvb, iValueOffset, 4, iArrayLength);
1023 iValueOffset += 4;
1024 iValueOffset = dissect_amf0_property_list(tvb, iValueOffset, val_tree, &count, amf3_encoding);
1025 proto_item_append_text(ti, " (%u items)", count);
1026 break;
1027 case AMF0_END_OF_OBJECT:
1028 proto_tree_add_text(tree, tvb, iValueOffset, 3, "End Of Object Marker");
1029 iValueOffset += 3;
1030 break;
1031 case AMF0_STRICT_ARRAY:
1033 * Counted list type, without end marker. Number of values
1034 * is determined by count, values are assumed to form a
1035 * [0..N-1] numbered array and are presented as plain AMF
1036 * types, not OBJECT or ECMA_ARRAY style named properties.
1038 iArrayLength = tvb_get_ntohl(tvb, iValueOffset);
1039 proto_tree_add_uint(val_tree, hf_amf_arraylength, tvb, iValueOffset, 4, iArrayLength);
1040 iValueOffset += 4;
1041 for (i = 0; i < iArrayLength; i++)
1042 iValueOffset = dissect_amf0_value_type(tvb, iValueOffset, val_tree, amf3_encoding, NULL);
1043 proto_item_append_text(ti, " (%u items)", iArrayLength);
1044 break;
1045 case AMF0_DATE:
1046 iDoubleValue = tvb_get_ntohieee_double(tvb, iValueOffset);
1047 t.secs = (time_t)(iDoubleValue/1000);
1048 t.nsecs = (int)((iDoubleValue - 1000*(double)t.secs) * 1000000);
1049 proto_tree_add_time(val_tree, hf_amf_date, tvb, iValueOffset, 8, &t);
1050 iValueOffset += 8;
1051 proto_item_append_text(ti, " %s", abs_time_to_str(&t, ABSOLUTE_TIME_LOCAL, TRUE));
1052 if (parent_ti != NULL)
1053 proto_item_append_text(parent_ti, " %s", abs_time_to_str(&t, ABSOLUTE_TIME_LOCAL, TRUE));
1054 /* time-zone */
1055 iValueOffset += 2;
1056 break;
1057 case AMF0_LONG_STRING:
1058 case AMF0_XML: /* same representation */
1059 iStringLength = tvb_get_ntohl(tvb, iValueOffset);
1060 proto_tree_add_uint(val_tree, hf_amf_stringlength, tvb, iValueOffset, 2, iStringLength);
1061 iValueOffset += 4;
1062 iStringValue = tvb_get_string_enc(wmem_packet_scope(), tvb, iValueOffset, iStringLength, ENC_UTF_8|ENC_NA);
1063 if (iStringLength != 0)
1064 proto_tree_add_string(val_tree, (iObjType==AMF0_XML) ? hf_amf_xml_doc : hf_amf_longstring, tvb, iValueOffset, iStringLength, iStringValue);
1065 iValueOffset += iStringLength;
1066 proto_item_append_text(ti, " '%s'", iStringValue);
1067 if (parent_ti != NULL)
1068 proto_item_append_text(parent_ti, " '%s'", iStringValue);
1069 break;
1070 case AMF0_UNSUPPORTED:
1071 break;
1072 case AMF0_TYPED_OBJECT:
1073 /* class-name */
1074 iStringLength = tvb_get_ntohs(tvb, iValueOffset);
1075 proto_tree_add_uint(val_tree, hf_amf_stringlength, tvb, iValueOffset, 2, iStringLength);
1076 iValueOffset += 2;
1077 iStringValue = tvb_get_string_enc(wmem_packet_scope(), tvb, iValueOffset, iStringLength, ENC_UTF_8|ENC_NA);
1078 proto_tree_add_string(val_tree, hf_amf_string, tvb, iValueOffset, iStringLength, iStringValue);
1079 iValueOffset += iStringLength;
1080 iValueOffset = dissect_amf0_property_list(tvb, iValueOffset, val_tree, &count, amf3_encoding);
1081 break;
1082 case AMF0_AMF3_MARKER:
1083 *amf3_encoding = TRUE;
1084 break;
1085 case AMF0_INT64:
1086 iInteger64Value = tvb_get_ntoh64(tvb, iValueOffset);
1087 proto_tree_add_int64(val_tree, hf_amf_int64, tvb, iValueOffset, 8, iInteger64Value);
1088 iValueOffset += 8;
1089 proto_item_append_text(ti," %" G_GINT64_MODIFIER "d", iInteger64Value);
1090 if (parent_ti != NULL)
1091 proto_item_append_text(parent_ti," %" G_GINT64_MODIFIER "d", iInteger64Value);
1092 break;
1093 default:
1095 * If we can't determine the length, don't carry on;
1096 * just skip to the end of the tvbuff.
1098 iValueOffset = tvb_length(tvb);
1099 break;
1101 proto_item_set_end(ti, tvb, iValueOffset);
1102 return iValueOffset;
1105 static guint32
1106 amf_get_u29(tvbuff_t *tvb, int offset, guint *lenp)
1108 guint len = 0;
1109 guint8 iByte;
1110 guint32 iValue;
1112 iByte = tvb_get_guint8(tvb, offset);
1113 iValue = (iByte & 0x7F);
1114 offset++;
1115 len++;
1116 if (!(iByte & 0x80)) {
1117 /* 1 byte value */
1118 *lenp = len;
1119 return iValue;
1121 iByte = tvb_get_guint8(tvb, offset);
1122 iValue = (iValue << 7) | (iByte & 0x7F);
1123 offset++;
1124 len++;
1125 if (!(iByte & 0x80)) {
1126 /* 2 byte value */
1127 *lenp = len;
1128 return iValue;
1130 iByte = tvb_get_guint8(tvb, offset);
1131 iValue = (iValue << 7) | (iByte & 0x7F);
1132 offset++;
1133 len++;
1134 if (!(iByte & 0x80)) {
1135 /* 3 byte value */
1136 *lenp = len;
1137 return iValue;
1139 iByte = tvb_get_guint8(tvb, offset);
1140 iValue = (iValue << 8) | iByte;
1141 len++;
1142 *lenp = len;
1143 return iValue;
1146 static gint
1147 dissect_amf3_value_type(tvbuff_t *tvb, gint offset, proto_tree *tree, proto_item *parent_ti)
1149 guint8 iObjType;
1150 proto_item *ti;
1151 proto_tree *val_tree;
1152 gint iValueOffset = offset;
1153 guint iValueLength;
1154 guint32 iIntegerValue;
1155 double iDoubleValue;
1156 guint iStringLength;
1157 gchar *iStringValue;
1158 guint iArrayLength;
1159 proto_item *subval_ti;
1160 proto_tree *subval_tree;
1161 guint i;
1162 gboolean iTypeIsDynamic;
1163 guint iTraitCount;
1164 proto_item *traits_ti;
1165 proto_tree *traits_tree;
1166 proto_item *name_ti;
1167 proto_tree *name_tree;
1168 proto_item *member_ti;
1169 proto_tree *member_tree;
1170 guint8 *iByteArrayValue;
1172 iObjType = tvb_get_guint8(tvb, offset);
1173 if (parent_ti != NULL)
1174 proto_item_append_text(parent_ti, " %s",
1175 val_to_str_const(iObjType, amf3_type_vals, "Unknown"));
1176 switch (iObjType) {
1178 case AMF3_ARRAY:
1180 * For array types, make the top-level protocol tree
1181 * item a field for that type.
1183 ti = proto_tree_add_item(tree, hf_amf_array, tvb, offset, -1, ENC_NA);
1184 break;
1186 case AMF3_OBJECT:
1188 * For object types, make the top-level protocol tree
1189 * item a field for that type.
1191 ti = proto_tree_add_item(tree, hf_amf_object, tvb, offset, -1, ENC_NA);
1192 break;
1194 default:
1196 * For all other types, make it just a text item; the
1197 * field for that type will be used for the value.
1199 ti = proto_tree_add_text(tree, tvb, offset, -1, "%s",
1200 val_to_str_const(iObjType, amf3_type_vals, "Unknown"));
1201 break;
1204 val_tree = proto_item_add_subtree(ti, ett_amf_value);
1205 proto_tree_add_uint(val_tree, hf_amf_amf3_type, tvb, iValueOffset, 1, iObjType);
1206 iValueOffset++;
1208 switch (iObjType) {
1209 case AMF3_UNDEFINED:
1210 case AMF3_NULL:
1211 break;
1212 case AMF3_FALSE:
1213 proto_tree_add_boolean(val_tree, hf_amf_boolean, tvb, 0, 0, FALSE);
1214 proto_item_append_text(ti, " false");
1215 break;
1216 case AMF3_TRUE:
1217 proto_tree_add_boolean(val_tree, hf_amf_boolean, tvb, 0, 0, TRUE);
1218 proto_item_append_text(ti, " true");
1219 break;
1220 case AMF3_INTEGER:
1221 /* XXX - signed or unsigned? */
1222 iIntegerValue = amf_get_u29(tvb, iValueOffset, &iValueLength);
1223 proto_tree_add_uint(val_tree, hf_amf_integer, tvb, iValueOffset, iValueLength, iIntegerValue);
1224 proto_item_append_text(ti, " %u", iIntegerValue);
1225 if (parent_ti != NULL)
1226 proto_item_append_text(parent_ti, " %u", iIntegerValue);
1227 iValueOffset += iValueLength;
1228 break;
1229 case AMF3_DOUBLE:
1230 iDoubleValue = tvb_get_ntohieee_double(tvb, iValueOffset);
1231 proto_tree_add_double(val_tree, hf_amf_number, tvb, iValueOffset, 8, iDoubleValue);
1232 iValueOffset += 8;
1233 proto_item_append_text(ti, " %." G_STRINGIFY(DBL_DIG) "g", iDoubleValue);
1234 if (parent_ti != NULL)
1235 proto_item_append_text(parent_ti, " %." G_STRINGIFY(DBL_DIG) "g", iDoubleValue);
1236 break;
1237 case AMF3_STRING:
1238 iIntegerValue = amf_get_u29(tvb, iValueOffset, &iValueLength);
1239 if (iIntegerValue & 0x00000001) {
1240 /* the upper 28 bits of the integer value is a string length */
1241 iStringLength = iIntegerValue >> 1;
1242 proto_tree_add_uint(val_tree, hf_amf_stringlength, tvb, iValueOffset, iValueLength, iStringLength);
1243 iValueOffset += iValueLength;
1244 iStringValue = tvb_get_string_enc(wmem_packet_scope(), tvb, iValueOffset, iStringLength, ENC_UTF_8|ENC_NA);
1245 if (iStringLength != 0)
1246 proto_tree_add_string(val_tree, hf_amf_string, tvb, iValueOffset, iStringLength, iStringValue);
1247 iValueOffset += iStringLength;
1248 proto_item_append_text(ti, " '%s'", iStringValue);
1249 if (parent_ti != NULL)
1250 proto_item_append_text(parent_ti, " '%s'", iStringValue);
1251 } else {
1252 /* the upper 28 bits of the integer value are a string reference index */
1253 proto_tree_add_uint(val_tree, hf_amf_string_reference, tvb, iValueOffset, iValueLength, iIntegerValue >> 1);
1254 iValueOffset += iValueLength;
1255 proto_item_append_text(ti, " reference %u", iIntegerValue >> 1);
1256 if (parent_ti != NULL)
1257 proto_item_append_text(parent_ti, " reference %u", iIntegerValue >> 1);
1259 break;
1260 case AMF3_DATE:
1261 iIntegerValue = amf_get_u29(tvb, iValueOffset, &iValueLength);
1262 if (iIntegerValue & 0x00000001) {
1264 * The upper 28 bits of the integer value are
1265 * ignored; what follows is a double
1266 * containing milliseconds since the Epoch.
1268 nstime_t t;
1270 iValueOffset += iValueLength;
1271 iDoubleValue = tvb_get_ntohieee_double(tvb, iValueOffset);
1272 t.secs = (time_t)(iDoubleValue/1000);
1273 t.nsecs = (int)((iDoubleValue - 1000*(double)t.secs) * 1000000);
1274 proto_tree_add_time(val_tree, hf_amf_date, tvb, iValueOffset, 8, &t);
1275 iValueOffset += 8;
1276 proto_item_append_text(ti, "%s", abs_time_to_str(&t, ABSOLUTE_TIME_LOCAL, TRUE));
1277 if (parent_ti != NULL)
1278 proto_item_append_text(parent_ti, "%s", abs_time_to_str(&t, ABSOLUTE_TIME_LOCAL, TRUE));
1279 } else {
1280 /* the upper 28 bits of the integer value are an object reference index */
1281 proto_tree_add_uint(val_tree, hf_amf_object_reference, tvb, iValueOffset, iValueLength, iIntegerValue >> 1);
1282 iValueOffset += iValueLength;
1283 proto_item_append_text(ti, " object reference %u", iIntegerValue >> 1);
1284 if (parent_ti != NULL)
1285 proto_item_append_text(parent_ti, " object reference %u", iIntegerValue >> 1);
1287 break;
1288 case AMF3_ARRAY:
1289 iIntegerValue = amf_get_u29(tvb, iValueOffset, &iValueLength);
1290 if (iIntegerValue & 0x00000001) {
1292 * The upper 28 bits of the integer value are
1293 * a count of the number of elements in
1294 * the dense portion of the array.
1296 iArrayLength = iIntegerValue >> 1;
1297 proto_tree_add_uint(val_tree, hf_amf_arraydenselength, tvb, iValueOffset, iValueLength, iArrayLength);
1298 iValueOffset += iValueLength;
1301 * The AMF3 spec bit on the Array type is slightly
1302 * confusingly written, but seems to be saying that
1303 * the associative portion of the array follows the
1304 * size of the dense portion of the array, and the
1305 * dense portion of the array follows the associative
1306 * portion.
1308 * Dissect the associative portion.
1310 for (;;) {
1311 /* Fetch the name */
1312 iIntegerValue = amf_get_u29(tvb, iValueOffset, &iValueLength);
1313 if (iIntegerValue & 0x00000001) {
1314 /* the upper 28 bits of the integer value is a string length */
1315 iStringLength = iIntegerValue >> 1;
1316 if (iStringLength == 0) {
1317 /* null name marks the end of the associative part */
1318 proto_tree_add_text(val_tree, tvb, iValueOffset, iValueLength, "End of associative part");
1319 iValueOffset += iValueLength;
1320 break;
1322 iStringValue = tvb_get_string_enc(wmem_packet_scope(), tvb, iValueOffset+iValueLength, iStringLength, ENC_UTF_8|ENC_NA);
1323 subval_ti = proto_tree_add_text(val_tree, tvb, iValueOffset, iStringLength, "%s:", iStringValue);
1324 subval_tree = proto_item_add_subtree(subval_ti, ett_amf_array_element);
1325 proto_tree_add_uint(subval_tree, hf_amf_stringlength, tvb, iValueOffset, iValueLength, iStringLength);
1326 iValueOffset += iValueLength;
1327 proto_tree_add_string(subval_tree, hf_amf_string, tvb, iValueOffset, iStringLength, iStringValue);
1328 } else {
1329 /* the upper 28 bits of the integer value are a string reference index */
1330 subval_ti = proto_tree_add_text(val_tree, tvb, iValueOffset, iValueLength, "Reference %u:", iIntegerValue >> 1);
1331 subval_tree = proto_item_add_subtree(subval_ti, ett_amf_array_element);
1332 proto_tree_add_uint(subval_tree, hf_amf_string_reference, tvb, iValueOffset, iValueLength, iIntegerValue >> 1);
1335 /* Fetch the value */
1336 iObjType = tvb_get_guint8(tvb, offset);
1337 proto_item_append_text(subval_ti, "%s",
1338 val_to_str_const(iObjType, amf3_type_vals, "Unknown"));
1340 iValueOffset = dissect_amf3_value_type(tvb, iValueOffset, subval_tree, subval_ti);
1344 * Dissect the dense portion.
1346 for (i = 0; i < iArrayLength; i++)
1347 iValueOffset = dissect_amf3_value_type(tvb, iValueOffset, val_tree, NULL);
1349 proto_item_set_end(ti, tvb, iValueOffset);
1350 } else {
1351 /* the upper 28 bits of the integer value are an object reference index */
1352 proto_tree_add_uint(val_tree, hf_amf_object_reference, tvb, iValueOffset, iValueLength, iIntegerValue >> 1);
1353 proto_item_append_text(ti, " reference %u", iIntegerValue >> 1);
1354 if (parent_ti != NULL)
1355 proto_item_append_text(parent_ti, " reference %u", iIntegerValue >> 1);
1357 break;
1358 case AMF3_OBJECT:
1359 iIntegerValue = amf_get_u29(tvb, iValueOffset, &iValueLength);
1360 if (iIntegerValue & 0x00000001) {
1361 if (iIntegerValue & 0x00000002) {
1362 if (iIntegerValue & 0x00000004) {
1364 * U29O-traits-ext; the rest of
1365 * iIntegerValue is not significant,
1366 * and, worse, we have idea what
1367 * follows the class name, or even
1368 * how many bytes follow the class
1369 * name - that's by convention between
1370 * the client and server.
1372 iValueOffset += iValueLength;
1373 } else {
1375 * U29O-traits; the 0x00000008 bit
1376 * specifies whether the type is
1377 * dynamic.
1379 iTypeIsDynamic = (iIntegerValue & 0x00000008) ? TRUE : FALSE;
1380 iTraitCount = iIntegerValue >> 4;
1381 proto_tree_add_uint(val_tree, hf_amf_traitcount, tvb, iValueOffset, iValueLength, iTraitCount);
1382 iValueOffset += iValueLength;
1383 iIntegerValue = amf_get_u29(tvb, iValueOffset, &iValueLength);
1384 if (iIntegerValue & 0x00000001) {
1385 /* the upper 28 bits of the integer value is a string length */
1386 iStringLength = iIntegerValue >> 1;
1387 iStringValue = tvb_get_string_enc(wmem_packet_scope(), tvb, iValueOffset+iValueLength, iStringLength, ENC_UTF_8|ENC_NA);
1388 traits_ti = proto_tree_add_text(val_tree, tvb, iValueOffset, -1, "Traits for class %s (%u member names)", iStringValue, iTraitCount);
1389 traits_tree = proto_item_add_subtree(traits_ti, ett_amf_traits);
1390 name_ti = proto_tree_add_text(traits_tree, tvb,
1391 iValueOffset,
1392 iValueLength+iStringLength,
1393 "Class name: %s",
1394 iStringValue);
1395 name_tree = proto_item_add_subtree(name_ti, ett_amf_string);
1396 proto_tree_add_uint(name_tree, hf_amf_classnamelength, tvb, iValueOffset, iValueLength, iStringLength);
1397 iValueOffset += iValueLength;
1398 proto_tree_add_string(name_tree, hf_amf_classname, tvb, iValueOffset, iStringLength, iStringValue);
1399 iValueOffset += iStringLength;
1400 } else {
1401 /* the upper 28 bits of the integer value are a string reference index */
1402 traits_ti = proto_tree_add_text(val_tree, tvb, iValueOffset, iValueLength, "Traits for class (reference %u for name)", iIntegerValue >> 1);
1403 traits_tree = proto_item_add_subtree(traits_ti, ett_amf_traits);
1404 proto_tree_add_uint(traits_tree, hf_amf_string_reference, tvb, iValueOffset, iValueLength, iIntegerValue >> 1);
1405 iValueOffset += iValueLength;
1407 for (i = 0; i < iTraitCount; i++) {
1408 iIntegerValue = amf_get_u29(tvb, iValueOffset, &iValueLength);
1409 if (iIntegerValue & 0x00000001) {
1410 /* the upper 28 bits of the integer value is a string length */
1411 iStringLength = iIntegerValue >> 1;
1412 iStringValue = tvb_get_string_enc(wmem_packet_scope(), tvb, iValueOffset+iValueLength, iStringLength, ENC_UTF_8|ENC_NA);
1413 member_ti = proto_tree_add_text(traits_tree, tvb, iValueOffset, iValueLength+iStringLength, "Member '%s'", iStringValue);
1414 member_tree = proto_item_add_subtree(member_ti, ett_amf_trait_member);
1415 proto_tree_add_uint(member_tree, hf_amf_membernamelength, tvb, iValueOffset, iValueLength, iStringLength);
1416 iValueOffset += iValueLength;
1417 proto_tree_add_string(member_tree, hf_amf_membername, tvb, iValueOffset, iStringLength, iStringValue);
1418 iValueOffset += iStringLength;
1419 } else {
1420 /* the upper 28 bits of the integer value are a string reference index */
1421 proto_tree_add_uint(traits_tree, hf_amf_string_reference, tvb, iValueOffset, iValueLength, iIntegerValue >> 1);
1422 iValueOffset += iValueLength;
1425 for (i = 0; i < iTraitCount; i++)
1426 iValueOffset = dissect_amf3_value_type(tvb, iValueOffset, traits_tree, NULL);
1427 if (iTypeIsDynamic) {
1428 for (;;) {
1429 /* Fetch the name */
1430 iIntegerValue = amf_get_u29(tvb, iValueOffset, &iValueLength);
1431 if (iIntegerValue & 0x00000001) {
1432 /* the upper 28 bits of the integer value is a string length */
1433 iStringLength = iIntegerValue >> 1;
1434 if (iStringLength == 0) {
1435 /* null name marks the end of the associative part */
1436 proto_tree_add_text(traits_tree, tvb, iValueOffset, iValueLength, "End of dynamic members");
1437 iValueOffset += iValueLength;
1438 break;
1440 iStringValue = tvb_get_string_enc(wmem_packet_scope(), tvb, iValueOffset+iValueLength, iStringLength, ENC_UTF_8|ENC_NA);
1441 subval_ti = proto_tree_add_text(traits_tree, tvb, iValueOffset, -1, "%s:", iStringValue);
1442 subval_tree = proto_item_add_subtree(subval_ti, ett_amf_array_element);
1443 name_ti = proto_tree_add_text(subval_tree, tvb,
1444 iValueOffset,
1445 iValueLength+iStringLength,
1446 "Member name: %s",
1447 iStringValue);
1448 name_tree = proto_item_add_subtree(name_ti, ett_amf_string);
1449 proto_tree_add_uint(name_tree, hf_amf_membernamelength, tvb, iValueOffset, iValueLength, iStringLength);
1450 iValueOffset += iValueLength;
1451 proto_tree_add_string(name_tree, hf_amf_membername, tvb, iValueOffset, iStringLength, iStringValue);
1452 iValueOffset += iStringLength;
1453 } else {
1454 /* the upper 28 bits of the integer value are a string reference index */
1455 subval_ti = proto_tree_add_text(traits_tree, tvb, iValueOffset, iValueLength, "Reference %u:", iIntegerValue >> 1);
1456 subval_tree = proto_item_add_subtree(subval_ti, ett_amf_array_element);
1457 proto_tree_add_uint(subval_tree, hf_amf_string_reference, tvb, iValueOffset, iValueLength, iIntegerValue >> 1);
1458 iValueOffset += iValueLength;
1461 /* Fetch the value */
1462 iValueOffset = dissect_amf3_value_type(tvb, iValueOffset, subval_tree, subval_ti);
1463 proto_item_set_end(subval_ti, tvb, iValueOffset);
1466 proto_item_set_end(traits_ti, tvb, iValueOffset);
1468 } else {
1470 * U29O-traits-ref; the upper 27 bits of
1471 * the integer value are a traits reference
1472 * index.
1474 proto_tree_add_uint(val_tree, hf_amf_trait_reference, tvb, iValueOffset, iValueLength, iIntegerValue >> 2);
1475 iValueOffset += iValueLength;
1477 } else {
1479 * U29O-ref; the upper 28 bits of the integer value
1480 * are an object reference index.
1482 proto_tree_add_uint(val_tree, hf_amf_object_reference, tvb, iValueOffset, iValueLength, iIntegerValue >> 1);
1483 proto_item_append_text(ti, " reference %u", iIntegerValue >> 1);
1484 if (parent_ti != NULL)
1485 proto_item_append_text(parent_ti, " reference %u", iIntegerValue >> 1);
1487 break;
1488 case AMF3_XML:
1489 iIntegerValue = amf_get_u29(tvb, iValueOffset, &iValueLength);
1490 if (iIntegerValue & 0x00000001) {
1492 * The upper 28 bits of the integer value are
1493 * a count of the number of bytes in the
1494 * XML string.
1496 iStringLength = iIntegerValue >> 1;
1497 proto_tree_add_uint(val_tree, hf_amf_xmllength, tvb, iValueOffset, iValueLength, iStringLength);
1498 iValueOffset += iValueLength;
1499 proto_tree_add_item(val_tree, hf_amf_xml, tvb, iValueOffset, iStringLength, ENC_UTF_8|ENC_NA);
1500 } else {
1501 /* the upper 28 bits of the integer value are a string reference index */
1502 proto_tree_add_uint(val_tree, hf_amf_object_reference, tvb, iValueOffset, iValueLength, iIntegerValue >> 1);
1503 proto_item_append_text(ti, " reference %u", iIntegerValue >> 1);
1504 if (parent_ti != NULL)
1505 proto_item_append_text(parent_ti, " reference %u", iIntegerValue >> 1);
1507 break;
1508 case AMF3_BYTEARRAY:
1509 iIntegerValue = amf_get_u29(tvb, iValueOffset, &iValueLength);
1510 if (iIntegerValue & 0x00000001) {
1512 * The upper 28 bits of the integer value are
1513 * a count of the number of bytes in the
1514 * byte array.
1516 iArrayLength = iIntegerValue >> 1;
1517 proto_tree_add_uint(val_tree, hf_amf_bytearraylength, tvb, iValueOffset, iValueLength, iArrayLength);
1518 iValueOffset += iValueLength;
1519 iByteArrayValue = (guint8 *)tvb_memdup(wmem_packet_scope(), tvb, iValueOffset, iArrayLength);
1520 proto_tree_add_bytes(val_tree, hf_amf_bytearray, tvb, iValueOffset, iArrayLength, iByteArrayValue);
1521 proto_item_append_text(ti, " %s", bytes_to_str(iByteArrayValue, iArrayLength));
1522 if (parent_ti != NULL)
1523 proto_item_append_text(parent_ti, " %s", bytes_to_str(iByteArrayValue, iArrayLength));
1524 } else {
1525 /* the upper 28 bits of the integer value are a object reference index */
1526 proto_tree_add_uint(val_tree, hf_amf_object_reference, tvb, iValueOffset, iValueLength, iIntegerValue >> 1);
1527 proto_item_append_text(ti, " reference %u", iIntegerValue >> 1);
1528 if (parent_ti != NULL)
1529 proto_item_append_text(parent_ti, " reference %u", iIntegerValue >> 1);
1531 break;
1532 default:
1534 * If we can't determine the length, don't carry on;
1535 * just skip to the end of the tvbuff.
1537 iValueOffset = tvb_length(tvb);
1538 break;
1540 proto_item_set_end(ti, tvb, iValueOffset);
1541 return iValueOffset;
1544 static gint
1545 dissect_rtmpt_body_command(tvbuff_t *tvb, gint offset, proto_tree *rtmpt_tree, gboolean amf3)
1547 gboolean amf3_encoding = FALSE;
1549 if (amf3) {
1550 /* Looks like for the AMF3 variants we get a 0 byte here,
1551 * followed by AMF0 encoding - I've never seen actual AMF3
1552 * encoding used, which is completely different. I speculate
1553 * that if the byte is AMF0_AMF3_MARKER then the rest
1554 * will be in AMF3. For now, assume AMF0 only. */
1555 offset++;
1558 while (tvb_reported_length_remaining(tvb, offset) > 0)
1560 if (amf3_encoding)
1561 offset = dissect_amf3_value_type(tvb, offset, rtmpt_tree, NULL);
1562 else
1563 offset = dissect_amf0_value_type(tvb, offset, rtmpt_tree, &amf3_encoding, NULL);
1565 return offset;
1568 static void
1569 dissect_rtmpt_body_audio(tvbuff_t *tvb, gint offset, proto_tree *rtmpt_tree)
1571 guint8 iCtl;
1572 proto_item *ai;
1573 proto_tree *at;
1575 iCtl = tvb_get_guint8(tvb, offset);
1576 ai = proto_tree_add_uint_format(rtmpt_tree, hf_rtmpt_audio_control, tvb, offset, 1, iCtl,
1577 "Control: 0x%02x (%s %s %s %s)", iCtl,
1578 val_to_str_const((iCtl & 0xf0)>>4, rtmpt_audio_codecs, "Unknown codec"),
1579 val_to_str_const((iCtl & 0x0c)>>2, rtmpt_audio_rates, "Unknown rate"),
1580 val_to_str_const((iCtl & 0x02)>>1, rtmpt_audio_sizes, "Unknown sample size"),
1581 val_to_str_const(iCtl & 0x01, rtmpt_audio_types, "Unknown channel count"));
1583 at = proto_item_add_subtree(ai, ett_rtmpt_audio_control);
1584 proto_tree_add_uint(at, hf_rtmpt_audio_format, tvb, offset, 1, iCtl);
1585 proto_tree_add_uint(at, hf_rtmpt_audio_rate, tvb, offset, 1, iCtl);
1586 proto_tree_add_uint(at, hf_rtmpt_audio_size, tvb, offset, 1, iCtl);
1587 proto_tree_add_uint(at, hf_rtmpt_audio_type, tvb, offset, 1, iCtl);
1588 proto_tree_add_item(rtmpt_tree, hf_rtmpt_audio_data, tvb, offset+1, -1, ENC_NA);
1591 static void
1592 dissect_rtmpt_body_video(tvbuff_t *tvb, gint offset, proto_tree *rtmpt_tree)
1594 guint8 iCtl;
1595 proto_item *vi;
1596 proto_tree *vt;
1598 iCtl = tvb_get_guint8(tvb, offset);
1599 vi = proto_tree_add_uint_format(rtmpt_tree, hf_rtmpt_video_control, tvb, offset, 1, iCtl,
1600 "Control: 0x%02x (%s %s)", iCtl,
1601 val_to_str_const((iCtl & 0xf0)>>4, rtmpt_video_types, "Unknown frame type"),
1602 val_to_str_const(iCtl & 0x0f, rtmpt_video_codecs, "Unknown codec"));
1604 vt = proto_item_add_subtree(vi, ett_rtmpt_video_control);
1605 proto_tree_add_uint(vt, hf_rtmpt_video_type, tvb, offset, 1, iCtl);
1606 proto_tree_add_uint(vt, hf_rtmpt_video_format, tvb, offset, 1, iCtl);
1607 proto_tree_add_item(rtmpt_tree, hf_rtmpt_video_data, tvb, offset+1, -1, ENC_NA);
1610 static void
1611 dissect_rtmpt_body_aggregate(tvbuff_t *tvb, gint offset, proto_tree *rtmpt_tree)
1613 proto_item *tag_item = NULL;
1614 proto_tree *tag_tree = NULL;
1616 proto_item *data_item = NULL;
1617 proto_tree *data_tree = NULL;
1619 while (tvb_length_remaining(tvb, offset) > 0) {
1620 guint8 iTagType;
1621 guint iDataSize;
1623 iTagType = tvb_get_guint8(tvb, offset + 0);
1624 iDataSize = tvb_get_ntoh24(tvb, offset + 1);
1626 tag_item = proto_tree_add_text(rtmpt_tree, tvb, offset, 11+iDataSize+4, "%s",
1627 val_to_str_const(iTagType, rtmpt_tag_vals, "Unknown Tag"));
1628 tag_tree = proto_item_add_subtree(tag_item, ett_rtmpt_tag);
1629 proto_tree_add_item(tag_tree, hf_rtmpt_tag_type, tvb, offset+0, 1, ENC_BIG_ENDIAN);
1630 proto_tree_add_item(tag_tree, hf_rtmpt_tag_datasize, tvb, offset+1, 3, ENC_BIG_ENDIAN);
1631 proto_tree_add_item(tag_tree, hf_rtmpt_tag_timestamp, tvb, offset+4, 3, ENC_BIG_ENDIAN);
1632 proto_tree_add_item(tag_tree, hf_rtmpt_tag_ets, tvb, offset+7, 1, ENC_BIG_ENDIAN);
1633 proto_tree_add_item(tag_tree, hf_rtmpt_tag_streamid, tvb, offset+8, 3, ENC_BIG_ENDIAN);
1635 data_item = proto_tree_add_text(tag_tree, tvb, offset+11, iDataSize, "Data");
1636 data_tree = proto_item_add_subtree(data_item, ett_rtmpt_tag_data);
1638 switch (iTagType) {
1639 case 8:
1640 dissect_rtmpt_body_audio(tvb, offset + 11, data_tree);
1641 break;
1642 case 9:
1643 dissect_rtmpt_body_video(tvb, offset + 11, data_tree);
1644 break;
1645 case 18:
1646 dissect_rtmpt_body_command(tvb, offset + 11, data_tree, FALSE);
1647 break;
1648 default:
1649 break;
1652 proto_tree_add_item(tag_tree, hf_rtmpt_tag_tagsize, tvb, offset+11+iDataSize, 4, ENC_BIG_ENDIAN);
1653 offset += 11 + iDataSize + 4;
1657 /* The main dissector for unchunked packets */
1659 static void
1660 dissect_rtmpt(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, rtmpt_conv_t *rconv, int cdir, rtmpt_packet_t *tp)
1662 proto_tree *rtmpt_tree = NULL;
1663 proto_tree *rtmptroot_tree = NULL;
1664 proto_item *ti = NULL;
1665 gint offset = 0;
1667 gchar *sDesc = NULL;
1668 gint deschasopcode = FALSE;
1669 gboolean haveETS = FALSE;
1670 guint32 iBodyOffset = 0;
1671 guint32 iBodyRemain = 0;
1673 col_set_str(pinfo->cinfo, COL_PROTOCOL, "RTMP");
1675 RTMPT_DEBUG("Dissect: frame=%u visited=%d len=%d tree=%p\n",
1676 pinfo->fd->num, pinfo->fd->flags.visited,
1677 tvb_length_remaining(tvb, offset), tree);
1679 /* Clear any previous data in Info column (RTMP packets are protected by a "fence") */
1680 col_clear(pinfo->cinfo, COL_INFO);
1682 if (tvb_length_remaining(tvb, offset) < 1) return;
1684 if (tp->id<=RTMPT_ID_MAX) {
1685 if (tp->fmt<3
1686 && tvb_length_remaining(tvb, offset)>=tp->bhlen+3
1687 && tvb_get_ntoh24(tvb, offset+tp->bhlen)==0xffffff) {
1688 haveETS = TRUE;
1691 iBodyOffset = offset + tp->bhlen + tp->mhlen;
1692 iBodyRemain = tvb_length_remaining(tvb, iBodyOffset);
1694 if (tp->cmd==RTMPT_TYPE_CHUNK_SIZE && tp->len>=4 && iBodyRemain>=4) {
1695 guint32 newchunksize = tvb_get_ntohl(tvb, iBodyOffset);
1696 if (newchunksize<rtmpt_max_packet_size) {
1697 wmem_tree_insert32(rconv->chunksize[cdir], tp->lastseq, GINT_TO_POINTER(newchunksize));
1701 if (!PINFO_FD_VISITED(pinfo)) {
1702 if (tp->cmd==RTMPT_TYPE_COMMAND_AMF0 || tp->cmd==RTMPT_TYPE_COMMAND_AMF3 ||
1703 tp->cmd==RTMPT_TYPE_DATA_AMF0 || tp->cmd==RTMPT_TYPE_DATA_AMF3) {
1704 guint32 soff = 0;
1705 if (tp->cmd==RTMPT_TYPE_COMMAND_AMF3 || tp->cmd==RTMPT_TYPE_DATA_AMF3) {
1706 soff = 1;
1708 tp->txid = rtmpt_get_amf_txid(tvb, iBodyOffset+soff);
1709 if (tp->txid!=0) {
1710 RTMPT_DEBUG("got txid=%d\n", tp->txid);
1711 wmem_tree_insert32(rconv->txids[cdir], tp->txid, GINT_TO_POINTER(pinfo->fd->num));
1717 if (tree && tp->id<=RTMPT_ID_MAX)
1719 sDesc = rtmpt_get_packet_desc(tvb, iBodyOffset, iBodyRemain, rconv, cdir, tp, &deschasopcode);
1722 if (tp->id>RTMPT_ID_MAX) {
1723 col_append_sep_fstr(pinfo->cinfo, COL_INFO, "|", "%s",
1724 val_to_str(tp->id, rtmpt_handshake_vals, "Unknown (0x%01x)"));
1725 col_set_fence(pinfo->cinfo, COL_INFO);
1726 } else if (sDesc) {
1727 col_append_sep_fstr(pinfo->cinfo, COL_INFO, "|", "%s", sDesc);
1728 col_set_fence(pinfo->cinfo, COL_INFO);
1729 } else {
1730 col_append_sep_fstr(pinfo->cinfo, COL_INFO, "|", "%s",
1731 val_to_str(tp->cmd, rtmpt_opcode_vals, "Unknown (0x%01x)"));
1732 col_set_fence(pinfo->cinfo, COL_INFO);
1735 if (tree)
1737 ti = proto_tree_add_item(tree, proto_rtmpt, tvb, offset, -1, ENC_NA);
1739 if (tp->id>RTMPT_ID_MAX) {
1740 /* Dissect handshake */
1741 proto_item_append_text(ti, " (%s)",
1742 val_to_str(tp->id, rtmpt_handshake_vals, "Unknown (0x%01x)"));
1743 rtmptroot_tree = proto_item_add_subtree(ti, ett_rtmpt);
1744 ti = proto_tree_add_text(rtmptroot_tree, tvb, offset, -1, "%s",
1745 val_to_str(tp->id, rtmpt_handshake_vals, "Unknown (0x%01x)"));
1746 rtmpt_tree = proto_item_add_subtree(ti, ett_rtmpt_handshake);
1748 if (tp->id == RTMPT_TYPE_HANDSHAKE_1)
1750 proto_tree_add_item(rtmpt_tree, hf_rtmpt_handshake_c0, tvb, 0, 1, ENC_NA);
1751 proto_tree_add_item(rtmpt_tree, hf_rtmpt_handshake_c1, tvb, 1, 1536, ENC_NA);
1753 else if (tp->id == RTMPT_TYPE_HANDSHAKE_2)
1755 proto_tree_add_item(rtmpt_tree, hf_rtmpt_handshake_s0, tvb, 0, 1, ENC_NA);
1756 proto_tree_add_item(rtmpt_tree, hf_rtmpt_handshake_s1, tvb, 1, 1536, ENC_NA);
1757 proto_tree_add_item(rtmpt_tree, hf_rtmpt_handshake_s2, tvb, 1537, 1536, ENC_NA);
1759 else if (tp->id == RTMPT_TYPE_HANDSHAKE_3)
1761 proto_tree_add_item(rtmpt_tree, hf_rtmpt_handshake_c2, tvb, 0, 1536, ENC_NA);
1764 return;
1767 if (sDesc && deschasopcode) {
1768 proto_item_append_text(ti, " (%s)", sDesc);
1769 } else if (sDesc) {
1770 proto_item_append_text(ti, " (%s %s)",
1771 val_to_str(tp->cmd, rtmpt_opcode_vals, "Unknown (0x%01x)"), sDesc);
1772 } else {
1773 proto_item_append_text(ti, " (%s)",
1774 val_to_str(tp->cmd, rtmpt_opcode_vals, "Unknown (0x%01x)"));
1776 rtmptroot_tree = proto_item_add_subtree(ti, ett_rtmpt);
1778 /* Function call/response matching */
1779 if (tp->otherframe!=0) {
1780 proto_tree_add_uint(rtmptroot_tree,
1781 tp->isresponse ? hf_rtmpt_function_response : hf_rtmpt_function_call,
1782 tvb, offset, tp->bhlen+tp->mhlen+tp->len,
1783 tp->otherframe);
1786 /* Dissect header fields */
1787 ti = proto_tree_add_text(rtmptroot_tree, tvb, offset, tp->bhlen+tp->mhlen, RTMPT_TEXT_RTMP_HEADER);
1788 /* proto_item_append_text(ti, " (%s)", val_to_str(tp->cmd, rtmpt_opcode_vals, "Unknown (0x%01x)")); */
1789 rtmpt_tree = proto_item_add_subtree(ti, ett_rtmpt_header);
1791 if (tp->fmt <= 3) proto_tree_add_item(rtmpt_tree, hf_rtmpt_header_format, tvb, offset + 0, 1, ENC_BIG_ENDIAN);
1792 if (tp->fmt <= 3) proto_tree_add_item(rtmpt_tree, hf_rtmpt_header_csid, tvb, offset + 0, tp->bhlen, ENC_BIG_ENDIAN);
1793 if (tp->fmt <= 2) {
1794 if (tp->fmt>0) {
1795 proto_tree_add_item(rtmpt_tree, hf_rtmpt_header_timestamp_delta, tvb, offset + tp->bhlen, 3, ENC_BIG_ENDIAN);
1796 } else {
1797 proto_tree_add_item(rtmpt_tree, hf_rtmpt_header_timestamp, tvb, offset + tp->bhlen, 3, ENC_BIG_ENDIAN);
1799 if (haveETS) {
1800 proto_tree_add_item(rtmpt_tree, hf_rtmpt_header_ets, tvb, offset + tp->bhlen + tp->mhlen - 4, 4, ENC_BIG_ENDIAN);
1803 if ((tp->fmt>0 && !haveETS) || tp->fmt==3) {
1804 proto_tree_add_text(rtmpt_tree, tvb, offset + tp->bhlen, 0, "Timestamp: %d (calculated)", tp->ts);
1806 if (tp->fmt <= 1) proto_tree_add_item(rtmpt_tree, hf_rtmpt_header_body_size, tvb, offset + tp->bhlen + 3, 3, ENC_BIG_ENDIAN);
1807 if (tp->fmt <= 1) proto_tree_add_item(rtmpt_tree, hf_rtmpt_header_typeid, tvb, offset + tp->bhlen + 6, 1, ENC_BIG_ENDIAN);
1808 if (tp->fmt <= 0) proto_tree_add_item(rtmpt_tree, hf_rtmpt_header_streamid, tvb, offset + tp->bhlen + 7, 4, ENC_LITTLE_ENDIAN);
1810 /* Dissect body */
1811 if (tp->len==0) return;
1812 offset = iBodyOffset;
1814 ti = proto_tree_add_text(rtmptroot_tree, tvb, offset, -1, RTMPT_TEXT_RTMP_BODY);
1815 rtmpt_tree = proto_item_add_subtree(ti, ett_rtmpt_body);
1817 switch (tp->cmd) {
1818 case RTMPT_TYPE_CHUNK_SIZE:
1819 case RTMPT_TYPE_ABORT_MESSAGE:
1820 case RTMPT_TYPE_ACKNOWLEDGEMENT:
1821 case RTMPT_TYPE_UCM:
1822 case RTMPT_TYPE_WINDOW:
1823 case RTMPT_TYPE_PEER_BANDWIDTH:
1824 dissect_rtmpt_body_scm(tvb, offset, rtmpt_tree, tp->cmd);
1825 break;
1826 case RTMPT_TYPE_COMMAND_AMF0:
1827 case RTMPT_TYPE_DATA_AMF0:
1828 dissect_rtmpt_body_command(tvb, offset, rtmpt_tree, FALSE);
1829 break;
1830 case RTMPT_TYPE_COMMAND_AMF3:
1831 case RTMPT_TYPE_DATA_AMF3:
1832 dissect_rtmpt_body_command(tvb, offset, rtmpt_tree, TRUE);
1833 break;
1834 case RTMPT_TYPE_AUDIO_DATA:
1835 dissect_rtmpt_body_audio(tvb, offset, rtmpt_tree);
1836 break;
1837 case RTMPT_TYPE_VIDEO_DATA:
1838 dissect_rtmpt_body_video(tvb, offset, rtmpt_tree);
1839 break;
1840 case RTMPT_TYPE_AGGREGATE:
1841 dissect_rtmpt_body_aggregate(tvb, offset, rtmpt_tree);
1842 break;
1847 /* Unchunk a data stream into individual RTMP packets */
1849 static void
1850 dissect_rtmpt_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, rtmpt_conv_t *rconv, int cdir, guint32 seq, guint32 lastackseq)
1852 int offset = 0;
1853 int remain;
1854 int want;
1856 guint8 header_type;
1857 int basic_hlen;
1858 int message_hlen;
1860 guint32 id;
1861 guint32 ts = 0;
1862 guint32 tsd = 0;
1863 int body_len;
1864 guint8 cmd;
1865 guint32 src;
1866 int chunk_size;
1868 rtmpt_frag_t *tf;
1869 rtmpt_id_t *ti;
1870 rtmpt_packet_t *tp;
1871 tvbuff_t *pktbuf;
1873 remain = tvb_length(tvb);
1874 if (!remain) return;
1876 RTMPT_DEBUG("Segment: cdir=%d seq=%d-%d\n", cdir, seq, seq+remain-1);
1878 if (pinfo->fd->flags.visited) {
1879 /* Already done the work, so just dump the existing state */
1880 wmem_stack_t *packets;
1882 /* List all RTMP packets terminating in this TCP segment, from end to beginning */
1884 packets = wmem_stack_new(wmem_packet_scope());
1885 wmem_stack_push(packets, 0);
1887 tp = (rtmpt_packet_t *)wmem_tree_lookup32_le(rconv->packets[cdir], seq+remain-1);
1888 while (tp && tp->lastseq>=seq) {
1889 wmem_stack_push(packets, tp);
1890 tp = (rtmpt_packet_t *)wmem_tree_lookup32_le(rconv->packets[cdir], tp->lastseq-1);
1893 /* Dissect the generated list in reverse order (beginning to end) */
1895 while ((tp=(rtmpt_packet_t *)wmem_stack_pop(packets))!=NULL) {
1896 if (tp->resident) {
1897 pktbuf = tvb_new_child_real_data(tvb, tp->data.p, tp->have, tp->have);
1898 add_new_data_source(pinfo, pktbuf, "Unchunked RTMP");
1899 } else {
1900 pktbuf = tvb_new_subset(tvb, tp->data.offset, tp->have, tp->have);
1902 dissect_rtmpt(pktbuf, pinfo, tree, rconv, cdir, tp);
1905 return;
1908 while (remain>0) {
1909 tf = NULL;
1910 ti = NULL;
1911 tp = NULL;
1913 /* Check for outstanding fragmented headers/chunks first */
1915 if (offset==0) {
1916 tf = (rtmpt_frag_t *)wmem_tree_lookup32_le(rconv->frags[cdir], seq+offset-1);
1918 if (tf) {
1919 /* May need to reassemble cross-TCP-segment fragments */
1920 RTMPT_DEBUG(" tf seq=%d lseq=%d h=%d l=%d\n", tf->seq, tf->lastseq, tf->have, tf->len);
1921 if (tf->have>=tf->len || seq+offset<tf->seq || seq+offset>tf->lastseq+tf->len-tf->have) {
1922 tf = NULL;
1923 } else if (!tf->ishdr) {
1924 ti = (rtmpt_id_t *)wmem_tree_lookup32(rconv->ids[cdir], tf->saved.id);
1925 if (ti) tp = (rtmpt_packet_t *)wmem_tree_lookup32_le(ti->packets, seq+offset-1);
1926 if (tp && tp->chunkwant) {
1927 goto unchunk;
1929 tf = NULL;
1930 ti = NULL;
1931 tp = NULL;
1934 if (tf) {
1935 /* The preceding segment contained an incomplete chunk header */
1937 want = tf->len - tf->have;
1938 if (remain<want) want = remain;
1940 tvb_memcpy(tvb, tf->saved.d+tf->have, offset, want);
1942 id = tf->saved.d[0];
1943 header_type = (id>>6) & 3;
1944 basic_hlen = rtmpt_basic_header_length(id);
1946 if (header_type<3 && tf->have<basic_hlen+3 && tf->have+want>=basic_hlen+3) {
1947 if (pntoh24(tf->saved.d+basic_hlen)==0xffffff) {
1948 tf->len += 4;
1952 tf->have += want;
1953 tf->lastseq = seq+want-1;
1954 remain -= want;
1955 offset += want;
1957 if (tf->have<tf->len) {
1958 return;
1964 if (!tf) {
1965 /* No preceeding data, get header data starting at current position */
1966 id = tvb_get_guint8(tvb, offset);
1968 if (id==RTMPT_MAGIC && seq+offset==RTMPT_HANDSHAKE_OFFSET_1) {
1969 header_type = 4;
1970 basic_hlen = 1;
1971 message_hlen = 0;
1972 id = lastackseq==1 ? RTMPT_TYPE_HANDSHAKE_1 : RTMPT_TYPE_HANDSHAKE_2;
1973 } else if (seq+offset==RTMPT_HANDSHAKE_OFFSET_2) {
1974 header_type = 4;
1975 basic_hlen = 0;
1976 message_hlen = 0;
1977 id = RTMPT_TYPE_HANDSHAKE_3;
1978 } else {
1979 header_type = (id>>6) & 3;
1980 basic_hlen = rtmpt_basic_header_length(id);
1981 message_hlen = rtmpt_message_header_length(id);
1983 if (header_type<3 && remain>=basic_hlen+3) {
1984 if (tvb_get_ntoh24(tvb, offset+basic_hlen)==0xffffff) {
1985 message_hlen += 4;
1989 if (remain<basic_hlen+message_hlen) {
1990 /* Ran out of packet mid-header, save and try again next time */
1991 tf = wmem_new(wmem_file_scope(), rtmpt_frag_t);
1992 tf->ishdr = 1;
1993 tf->seq = seq + offset;
1994 tf->lastseq = tf->seq + remain - 1;
1995 tf->len = basic_hlen + message_hlen;
1996 tvb_memcpy(tvb, tf->saved.d, offset, remain);
1997 tf->have = remain;
1998 wmem_tree_insert32(rconv->frags[cdir], seq+offset, tf);
1999 return;
2002 id = id & 0x3f;
2003 if (id==0) id = tvb_get_guint8(tvb, offset+1) + 64;
2004 else if (id==1) id = tvb_get_letohs(tvb, offset+1) + 64;
2007 } else {
2008 /* Use reassembled header data */
2009 id = tf->saved.d[0];
2010 header_type = (id>>6) & 3;
2011 basic_hlen = rtmpt_basic_header_length(id);
2012 message_hlen = tf->len - basic_hlen;
2014 id = id & 0x3f;
2015 if (id==0) id = tf->saved.d[1] + 64;
2016 else if (id==1) id = pletohs(tf->saved.d+1) + 64;
2019 /* Calculate header values, defaulting from previous packets with same id */
2021 if (id<=RTMPT_ID_MAX) ti = (rtmpt_id_t *)wmem_tree_lookup32(rconv->ids[cdir], id);
2022 if (ti) tp = (rtmpt_packet_t *)wmem_tree_lookup32_le(ti->packets, seq+offset-1);
2024 if (header_type==0) src = tf ? pntohl(tf->saved.d+basic_hlen+7) : tvb_get_ntohl(tvb, offset+basic_hlen+7);
2025 else if (ti) src = ti->src;
2026 else src = 0;
2028 if (header_type<2) cmd = tf ? tf->saved.d[basic_hlen+6] : tvb_get_guint8(tvb, offset+basic_hlen+6);
2029 else if (ti) cmd = ti->cmd;
2030 else cmd = 0;
2032 /* Calculate chunk_size now as a last-resort default payload length */
2033 if (id>RTMPT_ID_MAX) {
2034 if (id==RTMPT_TYPE_HANDSHAKE_1) chunk_size = body_len = 1536;
2035 else if (id==RTMPT_TYPE_HANDSHAKE_2) chunk_size = body_len = 3072;
2036 else /* if (id==RTMPT_TYPE_HANDSHAKE_3) */ chunk_size = body_len = 1536;
2037 } else {
2038 chunk_size = GPOINTER_TO_INT(wmem_tree_lookup32_le(rconv->chunksize[cdir], seq+offset-1));
2039 if (!chunk_size) chunk_size = RTMPT_DEFAULT_CHUNK_SIZE;
2041 if (header_type<2) body_len = tf ? pntoh24(tf->saved.d+basic_hlen+3) : tvb_get_ntoh24(tvb, offset+basic_hlen+3);
2042 else if (ti) body_len = ti->len;
2043 else body_len = chunk_size;
2045 if (body_len>(gint)rtmpt_max_packet_size) {
2046 return;
2050 if (!ti || !tp || header_type<3 || tp->have==tp->want || tp->chunkhave!=tp->chunkwant) {
2051 /* Start a new packet if:
2052 * no previous packet with same id
2053 * not a short 1-byte header
2054 * previous packet with same id was complete
2055 * previous incomplete chunk not handled by fragment handler
2057 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",
2058 cdir, seq+offset,
2059 ti, tp, header_type, basic_hlen+message_hlen, id, tp?tp->have:0, tp?tp->want:0, body_len, chunk_size);
2061 if (!ti) {
2062 ti = wmem_new(wmem_file_scope(), rtmpt_id_t);
2063 ti->packets = wmem_tree_new(wmem_file_scope());
2064 ti->ts = 0;
2065 ti->tsd = 0;
2066 wmem_tree_insert32(rconv->ids[cdir], id, ti);
2069 if (header_type==0) {
2070 ts = tf ? pntoh24(tf->saved.d+basic_hlen) : tvb_get_ntoh24(tvb, offset+basic_hlen);
2071 if (ts==0xffffff) {
2072 ts = tf ? pntohl(tf->saved.d+basic_hlen+11) : tvb_get_ntohl(tvb, offset+basic_hlen+11);
2074 tsd = ts - ti->ts;
2075 } else if (header_type<3) {
2076 tsd = tf ? pntoh24(tf->saved.d+basic_hlen) : tvb_get_ntoh24(tvb, offset+basic_hlen);
2077 if (tsd==0xffffff) {
2078 ts = tf ? pntohl(tf->saved.d+basic_hlen+message_hlen-4) : tvb_get_ntohl(tvb, offset+basic_hlen+message_hlen-4);
2079 tsd = ti->tsd; /* questionable */
2080 } else {
2081 ts = ti->ts + tsd;
2083 } else {
2084 ts = ti->ts + ti->tsd;
2085 tsd = ti->tsd;
2088 /* create a new packet structure */
2089 tp = wmem_new(wmem_file_scope(), rtmpt_packet_t);
2090 tp->seq = tp->lastseq = tf ? tf->seq : seq+offset;
2091 tp->have = 0;
2092 tp->want = basic_hlen + message_hlen + body_len;
2093 tp->chunkwant = 0;
2094 tp->chunkhave = 0;
2095 tp->bhlen = basic_hlen;
2096 tp->mhlen = message_hlen;
2097 tp->fmt = header_type;
2098 tp->id = id;
2099 tp->ts = ts;
2100 tp->len = body_len;
2101 if (id>RTMPT_ID_MAX) tp->cmd = id;
2102 else tp->cmd = cmd & 0x7f;
2103 tp->src = src;
2104 tp->txid = 0;
2105 tp->isresponse = FALSE;
2106 tp->otherframe = 0;
2108 /* Save the header information for future defaulting needs */
2109 ti->ts = ts;
2110 ti->tsd = tsd;
2111 ti->len = body_len;
2112 ti->cmd = cmd;
2113 ti->src = src;
2115 /* store against the id only until unchunking is complete */
2116 wmem_tree_insert32(ti->packets, tp->seq, tp);
2118 if (!tf && body_len<=chunk_size && tp->want<=remain) {
2119 /* The easy case - a whole packet contiguous and fully within this segment */
2120 tp->resident = FALSE;
2121 tp->data.offset = offset;
2122 tp->lastseq = seq+offset+tp->want-1;
2123 tp->have = tp->want;
2125 wmem_tree_insert32(rconv->packets[cdir], tp->lastseq, tp);
2127 pktbuf = tvb_new_subset(tvb, tp->data.offset, tp->have, tp->have);
2128 dissect_rtmpt(pktbuf, pinfo, tree, rconv, cdir, tp);
2130 offset += tp->want;
2131 remain -= tp->want;
2132 continue;
2134 } else {
2135 /* Some more reassembly required */
2136 tp->resident = TRUE;
2137 tp->data.p = (guint8 *)wmem_alloc(wmem_file_scope(), tp->bhlen+tp->mhlen+tp->len);
2139 if (tf && tf->ishdr) {
2140 memcpy(tp->data.p, tf->saved.d, tf->len);
2141 } else {
2142 tvb_memcpy(tvb, tp->data.p, offset, basic_hlen+message_hlen);
2143 offset += basic_hlen + message_hlen;
2144 remain -= basic_hlen + message_hlen;
2147 tp->lastseq = seq+offset-1;
2148 tp->have = basic_hlen + message_hlen;
2150 if (tp->have==tp->want) {
2151 wmem_tree_insert32(rconv->packets[cdir], tp->lastseq, tp);
2153 pktbuf = tvb_new_child_real_data(tvb, tp->data.p, tp->have, tp->have);
2154 add_new_data_source(pinfo, pktbuf, "Unchunked RTMP");
2155 dissect_rtmpt(pktbuf, pinfo, tree, rconv, cdir, tp);
2156 continue;
2159 tp->chunkwant = chunk_size;
2160 if (tp->chunkwant>tp->want-tp->have) tp->chunkwant = tp->want - tp->have;
2162 } else {
2163 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",
2164 cdir, seq+offset,
2165 ti, tp, basic_hlen+message_hlen, id, tp?tp->have:0, tp?tp->want:0, body_len, chunk_size);
2167 tp->chunkwant = chunk_size;
2168 if (tp->chunkwant>tp->want-tp->have) tp->chunkwant = tp->want - tp->have;
2170 offset += basic_hlen + message_hlen;
2171 remain -= basic_hlen + message_hlen;
2174 tf = NULL;
2176 /* Last case to deal with is unchunking the packet body */
2177 unchunk:
2178 want = tp->chunkwant - tp->chunkhave;
2179 if (want > remain) want = remain;
2180 RTMPT_DEBUG(" cw=%d ch=%d r=%d w=%d\n", tp->chunkwant, tp->chunkhave, remain, want);
2182 tvb_memcpy(tvb, tp->data.p+tp->have, offset, want);
2184 if (tf) {
2185 tf->have += want;
2186 tf->lastseq = seq+offset+want-1;
2188 tp->lastseq = seq+offset+want-1;
2189 tp->have += want;
2190 tp->chunkhave += want;
2192 offset += want;
2193 remain -= want;
2195 if (tp->chunkhave==tp->chunkwant) {
2196 /* Chunk is complete - wait for next header */
2197 tp->chunkhave = 0;
2198 tp->chunkwant = 0;
2201 if (tp->have==tp->want) {
2202 /* Whole packet is complete */
2203 wmem_tree_insert32(rconv->packets[cdir], tp->lastseq, tp);
2205 pktbuf = tvb_new_child_real_data(tvb, tp->data.p, tp->have, tp->have);
2206 add_new_data_source(pinfo, pktbuf, "Unchunked RTMP");
2207 dissect_rtmpt(pktbuf, pinfo, tree, rconv, cdir, tp);
2208 } else if (tp->chunkhave<tp->chunkwant) {
2209 /* Chunk is split across segment boundary */
2210 rtmpt_frag_t *tf2 = wmem_new(wmem_file_scope(), rtmpt_frag_t);
2211 tf2->ishdr = 0;
2212 tf2->seq = seq + offset - want;
2213 tf2->lastseq = tf2->seq + remain - 1 + want;
2214 tf2->have = tp->chunkhave;
2215 tf2->len = tp->chunkwant;
2216 tf2->saved.id = tp->id;
2217 RTMPT_DEBUG(" inserting tf @ %d\n", seq+offset-want-1);
2218 wmem_tree_insert32(rconv->frags[cdir], seq+offset-want-1, tf2);
2223 static rtmpt_conv_t*
2224 rtmpt_init_rconv(conversation_t *conv)
2226 rtmpt_conv_t *rconv = wmem_new(wmem_file_scope(), rtmpt_conv_t);
2227 conversation_add_proto_data(conv, proto_rtmpt, rconv);
2229 rconv->seqs[0] = wmem_tree_new(wmem_file_scope());
2230 rconv->seqs[1] = wmem_tree_new(wmem_file_scope());
2231 rconv->frags[0] = wmem_tree_new(wmem_file_scope());
2232 rconv->frags[1] = wmem_tree_new(wmem_file_scope());
2233 rconv->ids[0] = wmem_tree_new(wmem_file_scope());
2234 rconv->ids[1] = wmem_tree_new(wmem_file_scope());
2235 rconv->packets[0] = wmem_tree_new(wmem_file_scope());
2236 rconv->packets[1] = wmem_tree_new(wmem_file_scope());
2237 rconv->chunksize[0] = wmem_tree_new(wmem_file_scope());
2238 rconv->chunksize[1] = wmem_tree_new(wmem_file_scope());
2239 rconv->txids[0] = wmem_tree_new(wmem_file_scope());
2240 rconv->txids[1] = wmem_tree_new(wmem_file_scope());
2242 return rconv;
2245 static int
2246 dissect_rtmpt_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data)
2248 conversation_t *conv;
2249 rtmpt_conv_t *rconv;
2250 int cdir;
2251 struct tcpinfo *tcpinfo = (struct tcpinfo*)data;
2253 conv = find_conversation(pinfo->fd->num, &pinfo->src, &pinfo->dst, pinfo->ptype, pinfo->srcport, pinfo->destport, 0);
2254 if (!conv) {
2255 conv = conversation_new(pinfo->fd->num, &pinfo->src, &pinfo->dst, pinfo->ptype, pinfo->srcport, pinfo->destport, 0);
2258 rconv = (rtmpt_conv_t*)conversation_get_proto_data(conv, proto_rtmpt);
2259 if (!rconv) {
2260 rconv = rtmpt_init_rconv(conv);
2263 cdir = (ADDRESSES_EQUAL(&conv->key_ptr->addr1, &pinfo->src) &&
2264 ADDRESSES_EQUAL(&conv->key_ptr->addr2, &pinfo->dst) &&
2265 conv->key_ptr->port1==pinfo->srcport &&
2266 conv->key_ptr->port2==pinfo->destport) ? 0 : 1;
2268 dissect_rtmpt_common(tvb, pinfo, tree, rconv, cdir, tcpinfo->seq, tcpinfo->lastackseq);
2269 return tvb_length(tvb);
2272 static void
2273 dissect_rtmpt_http(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
2275 conversation_t *conv;
2276 rtmpt_conv_t *rconv;
2277 int cdir;
2278 guint32 seq;
2279 guint32 lastackseq;
2280 guint32 offset;
2281 gint remain;
2283 offset = 0;
2284 remain = tvb_length_remaining(tvb, 0);
2287 * Request flow:
2289 * POST /open/1
2290 * request body is a single non-RTMP byte
2291 * response contains a client ID <cid> followed by NL
2292 * POST /send/<cid>/<seq>
2293 * <seq> starts at 0 after open and increments on each
2294 * subsequent post
2295 * request body is pure RTMP data
2296 * response is a single non-RTMP byte followed by RTMP data
2297 * POST /idle/<cid>/<seq>
2298 * request contains a single non-RTMP byte
2299 * response is a single non-RTMP byte followed by RTMP data
2300 * POST /close/<cid>/<seq>
2301 * request and response contain a single non-RTMP byte
2303 * Ideally here we'd know:
2305 * 1) Whether this is was a HTTP request or response
2306 * (this gives us cdir directly)
2307 * 2) The requested URL (for both cases)
2308 * (this tells us the type of framing bytes present,
2309 * so whether there are any real bytes present). We
2310 * could also use the client ID to identify the
2311 * conversation, since each POST is likely to be on
2312 * a different TCP connection, and there could be
2313 * multiple simultaneous sessions from a single
2314 * client (which we don't deal with here.)
2316 * As it is, we currently have to just guess, and are
2317 * likely easily confused.
2320 cdir = pinfo->srcport==pinfo->match_uint;
2322 if (cdir) {
2323 conv = find_conversation(pinfo->fd->num, &pinfo->dst, &pinfo->src, pinfo->ptype, 0, pinfo->srcport, 0);
2324 if (!conv) {
2325 RTMPT_DEBUG("RTMPT new conversation\n");
2326 conv = conversation_new(pinfo->fd->num, &pinfo->dst, &pinfo->src, pinfo->ptype, 0, pinfo->srcport, 0);
2328 } else {
2329 conv = find_conversation(pinfo->fd->num, &pinfo->src, &pinfo->dst, pinfo->ptype, 0, pinfo->destport, 0);
2330 if (!conv) {
2331 RTMPT_DEBUG("RTMPT new conversation\n");
2332 conv = conversation_new(pinfo->fd->num, &pinfo->src, &pinfo->dst, pinfo->ptype, 0, pinfo->destport, 0);
2336 rconv = (rtmpt_conv_t*)conversation_get_proto_data(conv, proto_rtmpt);
2337 if (!rconv) {
2338 rconv = rtmpt_init_rconv(conv);
2341 /* Work out a TCP-like sequence numbers for the tunneled data stream.
2342 * If we've seen the packet before we'll have stored the seq of our
2343 * last byte against the frame number - since we know how big we are
2344 * we can work out the seq of our first byte. If this is the first
2345 * time, we use the stored seq of the last byte of the previous frame
2346 * plus one. If there is no previous frame then we must be at seq=1!
2347 * (This is per-conversation and per-direction, of course.) */
2349 lastackseq = GPOINTER_TO_INT(wmem_tree_lookup32_le(rconv->seqs[cdir ^ 1], pinfo->fd->num))+1;
2351 if (cdir==1 && lastackseq<2 && remain==17) {
2352 /* Session startup: the client makes an /open/ request and
2353 * the server responds with a 16 bytes client
2354 * identifier followed by a newline */
2355 offset += 17;
2356 remain -= 17;
2357 } else if (cdir || remain==1) {
2358 /* All other server responses start with one byte which
2359 * is not part of the RTMP stream. Client /idle/ requests
2360 * contain a single byte also not part of the stream. We
2361 * must discard these */
2362 offset++;
2363 remain--;
2366 seq = GPOINTER_TO_INT(wmem_tree_lookup32(rconv->seqs[cdir], pinfo->fd->num));
2368 if (seq==0) {
2369 seq = GPOINTER_TO_INT(wmem_tree_lookup32_le(rconv->seqs[cdir], pinfo->fd->num));
2370 seq += remain;
2371 wmem_tree_insert32(rconv->seqs[cdir], pinfo->fd->num, GINT_TO_POINTER(seq));
2374 seq -= remain-1;
2376 RTMPT_DEBUG("RTMPT f=%d cdir=%d seq=%d lastackseq=%d len=%d\n", pinfo->fd->num, cdir, seq, lastackseq, remain);
2378 if (remain<1) return;
2380 if (offset>0) {
2381 tvbuff_t *tvbrtmp = tvb_new_subset(tvb, offset, remain, remain);
2382 dissect_rtmpt_common(tvbrtmp, pinfo, tree, rconv, cdir, seq, lastackseq);
2383 } else {
2384 dissect_rtmpt_common(tvb, pinfo, tree, rconv, cdir, seq, lastackseq);
2388 #if 0
2389 static gboolean
2390 dissect_rtmpt_heur(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data)
2392 conversation_t * conversation;
2393 if (tvb_length(tvb) >= 12)
2395 /* To avoid a too high rate of false positive, this heuristics only matches the protocol
2396 from the first server response packet and not from the client request packets before.
2397 Therefore it is necessary to a "Decode as" to properly decode the first packets */
2398 struct tcpinfo *tcpinfo = (struct tcpinfo *)data;
2399 if (tcpinfo->lastackseq == RTMPT_HANDSHAKE_OFFSET_2
2400 && tcpinfo->seq == RTMPT_HANDSHAKE_OFFSET_1
2401 && tvb_get_guint8(tvb, 0) == RTMPT_MAGIC)
2403 /* Register this dissector for this conversation */
2404 conversation = NULL;
2405 conversation = find_conversation(pinfo->fd->num, &pinfo->src, &pinfo->dst, pinfo->ptype, pinfo->srcport, pinfo->destport, 0);
2406 if (conversation == NULL)
2408 conversation = conversation_new(pinfo->fd->num, &pinfo->src, &pinfo->dst, pinfo->ptype, pinfo->srcport, pinfo->destport, 0);
2410 conversation_set_dissector(conversation, rtmpt_tcp_handle);
2412 /* Dissect the packet */
2413 dissect_rtmpt_tcp(tvb, pinfo, tree);
2414 return TRUE;
2417 return FALSE;
2419 #endif
2421 static void
2422 dissect_amf(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree)
2424 proto_item *ti;
2425 proto_tree *amf_tree, *headers_tree, *messages_tree;
2426 int offset;
2427 guint header_count, message_count, i;
2428 guint string_length;
2429 guint header_length, message_length;
2430 gboolean amf3_encoding = FALSE;
2433 * XXX - is "application/x-amf" just AMF3?
2435 ti = proto_tree_add_item(tree, proto_amf, tvb, 0, -1, ENC_NA);
2436 amf_tree = proto_item_add_subtree(ti, ett_amf);
2437 offset = 0;
2438 proto_tree_add_item(amf_tree, hf_amf_version, tvb, offset, 2, ENC_BIG_ENDIAN);
2439 offset += 2;
2440 header_count = tvb_get_ntohs(tvb, offset);
2441 proto_tree_add_uint(amf_tree, hf_amf_header_count, tvb, offset, 2, header_count);
2442 offset += 2;
2443 if (header_count != 0) {
2444 ti = proto_tree_add_text(amf_tree, tvb, offset, -1, "Headers");
2445 headers_tree = proto_item_add_subtree(ti, ett_amf_headers);
2446 for (i = 0; i < header_count; i++) {
2447 string_length = tvb_get_ntohs(tvb, offset);
2448 proto_tree_add_item(headers_tree, hf_amf_header_name, tvb, offset, 2, ENC_BIG_ENDIAN|ENC_UTF_8);
2449 offset += 2 + string_length;
2450 proto_tree_add_item(headers_tree, hf_amf_header_must_understand, tvb, offset, 1, ENC_NA);
2451 offset += 1;
2452 header_length = tvb_get_ntohl(tvb, offset);
2453 if (header_length == 0xFFFFFFFF)
2454 proto_tree_add_uint_format_value(headers_tree, hf_amf_header_length, tvb, offset, 4, header_length, "Unknown");
2455 else
2456 proto_tree_add_uint(headers_tree, hf_amf_header_length, tvb, offset, 4, header_length);
2457 offset += 4;
2458 if (amf3_encoding)
2459 offset = dissect_amf3_value_type(tvb, offset, headers_tree, NULL);
2460 else
2461 offset = dissect_amf0_value_type(tvb, offset, headers_tree, &amf3_encoding, NULL);
2464 message_count = tvb_get_ntohs(tvb, offset);
2465 proto_tree_add_uint(amf_tree, hf_amf_message_count, tvb, offset, 2, message_count);
2466 offset += 2;
2467 if (message_count != 0) {
2468 ti = proto_tree_add_text(amf_tree, tvb, offset, -1, "Messages");
2469 messages_tree = proto_item_add_subtree(ti, ett_amf_messages);
2470 for (i = 0; i < message_count; i++) {
2471 string_length = tvb_get_ntohs(tvb, offset);
2472 proto_tree_add_item(messages_tree, hf_amf_message_target_uri, tvb, offset, 2, ENC_BIG_ENDIAN|ENC_UTF_8);
2473 offset += 2 + string_length;
2474 string_length = tvb_get_ntohs(tvb, offset);
2475 proto_tree_add_item(messages_tree, hf_amf_message_response_uri, tvb, offset, 2, ENC_BIG_ENDIAN|ENC_UTF_8);
2476 offset += 2 + string_length;
2477 message_length = tvb_get_ntohl(tvb, offset);
2478 if (message_length == 0xFFFFFFFF)
2479 proto_tree_add_uint_format_value(messages_tree, hf_amf_message_length, tvb, offset, 4, message_length, "Unknown");
2480 else
2481 proto_tree_add_uint(messages_tree, hf_amf_message_length, tvb, offset, 4, message_length);
2482 offset += 4;
2483 offset = dissect_rtmpt_body_command(tvb, offset, messages_tree, FALSE);
2488 void
2489 proto_register_rtmpt(void)
2491 static hf_register_info hf[] = {
2492 /* RTMP Handshake data */
2493 { &hf_rtmpt_handshake_c0,
2494 { "Protocol version", "rtmpt.handshake.c0", FT_BYTES, BASE_NONE,
2495 NULL, 0x0, "RTMPT Handshake C0", HFILL }},
2497 { &hf_rtmpt_handshake_s0,
2498 { "Protocol version", "rtmpt.handshake.s0", FT_BYTES, BASE_NONE,
2499 NULL, 0x0, "RTMPT Handshake S0", HFILL }},
2501 { &hf_rtmpt_handshake_c1,
2502 { "Handshake data", "rtmpt.handshake.c1", FT_BYTES, BASE_NONE,
2503 NULL, 0x0, "RTMPT Handshake C1", HFILL }},
2505 { &hf_rtmpt_handshake_s1,
2506 { "Handshake data", "rtmpt.handshake.s1", FT_BYTES, BASE_NONE,
2507 NULL, 0x0, "RTMPT Handshake S1", HFILL }},
2509 { &hf_rtmpt_handshake_c2,
2510 { "Handshake data", "rtmpt.handshake.c2", FT_BYTES, BASE_NONE,
2511 NULL, 0x0, "RTMPT Handshake C2", HFILL }},
2513 { &hf_rtmpt_handshake_s2,
2514 { "Handshake data", "rtmpt.handshake.s2", FT_BYTES, BASE_NONE,
2515 NULL, 0x0, "RTMPT Handshake S2", HFILL }},
2517 /* RTMP chunk/packet header */
2518 { &hf_rtmpt_header_format,
2519 { "Format", "rtmpt.header.format", FT_UINT8, BASE_DEC,
2520 NULL, 0xC0, "RTMPT Basic Header format", HFILL }},
2522 { &hf_rtmpt_header_csid,
2523 { "Chunk Stream ID", "rtmpt.header.csid", FT_UINT8, BASE_DEC,
2524 NULL, 0x3F, "RTMPT Basic Header chunk stream ID", HFILL }},
2526 { &hf_rtmpt_header_timestamp,
2527 { "Timestamp", "rtmpt.header.timestamp", FT_UINT24, BASE_DEC,
2528 NULL, 0x0, "RTMPT Message Header timestamp", HFILL }},
2530 { &hf_rtmpt_header_timestamp_delta,
2531 { "Timestamp delta", "rtmpt.header.timestampdelta", FT_UINT24, BASE_DEC,
2532 NULL, 0x0, "RTMPT Message Header timestamp delta", HFILL }},
2534 { &hf_rtmpt_header_body_size,
2535 { "Body size", "rtmpt.header.bodysize", FT_UINT24, BASE_DEC,
2536 NULL, 0x0, "RTMPT Message Header body size", HFILL }},
2538 { &hf_rtmpt_header_typeid,
2539 { "Type ID", "rtmpt.header.typeid", FT_UINT8, BASE_HEX,
2540 VALS(rtmpt_opcode_vals), 0x0, "RTMPT Message Header type ID", HFILL }},
2542 { &hf_rtmpt_header_streamid,
2543 { "Stream ID", "rtmpt.header.streamid", FT_UINT32, BASE_DEC,
2544 NULL, 0x0, "RTMPT Header stream ID", HFILL }},
2546 { &hf_rtmpt_header_ets,
2547 { "Extended timestamp", "rtmpt.header.ets", FT_UINT24, BASE_DEC,
2548 NULL, 0x0, "RTMPT Message Header extended timestamp", HFILL }},
2550 /* Stream Control Messages */
2552 { &hf_rtmpt_scm_chunksize,
2553 { "Chunk size", "rtmpt.scm.chunksize", FT_UINT32, BASE_DEC,
2554 NULL, 0x0, "RTMPT SCM chunk size", HFILL }},
2556 { &hf_rtmpt_scm_csid,
2557 { "Chunk stream ID", "rtmpt.scm.csid", FT_UINT32, BASE_DEC,
2558 NULL, 0x0, "RTMPT SCM chunk stream ID", HFILL }},
2560 { &hf_rtmpt_scm_seq,
2561 { "Sequence number", "rtmpt.scm.seq", FT_UINT32, BASE_DEC,
2562 NULL, 0x0, "RTMPT SCM acknowledgement sequence number", HFILL }},
2564 { &hf_rtmpt_scm_was,
2565 { "Window acknowledgement size", "rtmpt.scm.was", FT_UINT32, BASE_DEC,
2566 NULL, 0x0, "RTMPT SCM window acknowledgement size", HFILL }},
2568 { &hf_rtmpt_scm_limittype,
2569 { "Limit type", "rtmpt.scm.limittype", FT_UINT8, BASE_DEC,
2570 VALS(rtmpt_limit_vals), 0x0, "RTMPT SCM window acknowledgement size", HFILL }},
2572 /* User Control Messages */
2573 { &hf_rtmpt_ucm_eventtype,
2574 { "Event type", "rtmpt.ucm.eventtype", FT_UINT16, BASE_DEC,
2575 VALS(rtmpt_ucm_vals), 0x0, "RTMPT UCM event type", HFILL }},
2577 /* Frame links */
2579 { &hf_rtmpt_function_call,
2580 { "Response to this call in frame", "rtmpt.function.call", FT_FRAMENUM, BASE_NONE,
2581 NULL, 0x0, "RTMPT function call", HFILL }},
2583 { &hf_rtmpt_function_response,
2584 { "Call for this response in frame", "rtmpt.function.response", FT_FRAMENUM, BASE_NONE,
2585 NULL, 0x0, "RTMPT function response", HFILL }},
2587 /* Audio packets */
2588 { &hf_rtmpt_audio_control,
2589 { "Audio control", "rtmpt.audio.control", FT_UINT8, BASE_HEX,
2590 NULL, 0x0, "RTMPT Audio control", HFILL }},
2592 { &hf_rtmpt_audio_format,
2593 { "Format", "rtmpt.audio.format", FT_UINT8, BASE_DEC,
2594 VALS(rtmpt_audio_codecs), 0xf0, "RTMPT Audio format", HFILL }},
2596 { &hf_rtmpt_audio_rate,
2597 { "Sample rate", "rtmpt.audio.rate", FT_UINT8, BASE_DEC,
2598 VALS(rtmpt_audio_rates), 0x0c, "RTMPT Audio sample rate", HFILL }},
2600 { &hf_rtmpt_audio_size,
2601 { "Sample size", "rtmpt.audio.size", FT_UINT8, BASE_DEC,
2602 VALS(rtmpt_audio_sizes), 0x02, "RTMPT Audio sample size", HFILL }},
2604 { &hf_rtmpt_audio_type,
2605 { "Channels", "rtmpt.audio.type", FT_UINT8, BASE_DEC,
2606 VALS(rtmpt_audio_types), 0x01, "RTMPT Audio channel count", HFILL }},
2608 { &hf_rtmpt_audio_data,
2609 { "Audio data", "rtmpt.audio.data", FT_BYTES, BASE_NONE,
2610 NULL, 0x0, "RTMPT Audio data", HFILL }},
2612 /* Video packets */
2613 { &hf_rtmpt_video_control,
2614 { "Video control", "rtmpt.video.control", FT_UINT8, BASE_HEX,
2615 NULL, 0x0, "RTMPT Video control", HFILL }},
2617 { &hf_rtmpt_video_type,
2618 { "Type", "rtmpt.video.type", FT_UINT8, BASE_DEC,
2619 VALS(rtmpt_video_types), 0xf0, "RTMPT Video type", HFILL }},
2621 { &hf_rtmpt_video_format,
2622 { "Format", "rtmpt.video.format", FT_UINT8, BASE_DEC,
2623 VALS(rtmpt_video_codecs), 0x0f, "RTMPT Video format", HFILL }},
2625 { &hf_rtmpt_video_data,
2626 { "Video data", "rtmpt.video.data", FT_BYTES, BASE_NONE,
2627 NULL, 0x0, "RTMPT Video data", HFILL }},
2629 /* Aggregate packets */
2630 { &hf_rtmpt_tag_type,
2631 { "Type", "rtmpt.tag.type", FT_UINT8, BASE_DEC,
2632 VALS(rtmpt_tag_vals), 0x0, "RTMPT Aggregate tag type", HFILL }},
2634 { &hf_rtmpt_tag_datasize,
2635 { "Data size", "rtmpt.tag.datasize", FT_UINT24, BASE_DEC,
2636 NULL, 0x0, "RTMPT Aggregate tag data size", HFILL }},
2638 { &hf_rtmpt_tag_timestamp,
2639 { "Timestamp", "rtmpt.tag.timestamp", FT_UINT24, BASE_DEC,
2640 NULL, 0x0, "RTMPT Aggregate tag timestamp", HFILL }},
2642 { &hf_rtmpt_tag_ets,
2643 { "Timestamp Extended", "rtmpt.tag.ets", FT_UINT8, BASE_DEC,
2644 NULL, 0x0, "RTMPT Aggregate tag timestamp extended", HFILL }},
2646 { &hf_rtmpt_tag_streamid,
2647 { "Stream ID", "rtmpt.tag.streamid", FT_UINT24, BASE_DEC,
2648 NULL, 0x0, "RTMPT Aggregate tag stream ID", HFILL }},
2650 { &hf_rtmpt_tag_tagsize,
2651 { "Previous tag size", "rtmpt.tag.tagsize", FT_UINT32, BASE_DEC,
2652 NULL, 0x0, "RTMPT Aggregate previous tag size", HFILL }}
2655 static gint *ett[] = {
2656 &ett_rtmpt,
2657 &ett_rtmpt_handshake,
2658 &ett_rtmpt_header,
2659 &ett_rtmpt_body,
2660 &ett_rtmpt_ucm,
2661 &ett_rtmpt_audio_control,
2662 &ett_rtmpt_video_control,
2663 &ett_rtmpt_tag,
2664 &ett_rtmpt_tag_data
2667 module_t *rtmpt_module;
2669 proto_rtmpt = proto_register_protocol("Real Time Messaging Protocol", "RTMPT", "rtmpt");
2670 proto_register_field_array(proto_rtmpt, hf, array_length(hf));
2671 proto_register_subtree_array(ett, array_length(ett));
2673 rtmpt_module = prefs_register_protocol(proto_rtmpt, NULL);
2674 prefs_register_bool_preference(rtmpt_module, "desegment",
2675 "Reassemble RTMPT messages spanning multiple TCP segments",
2676 "Whether the RTMPT dissector should reassemble messages spanning multiple TCP segments."
2677 " To use this option, you must also enable \"Allow subdissectors to reassemble TCP streams\""
2678 " in the TCP protocol settings.",
2679 &rtmpt_desegment);
2681 prefs_register_uint_preference(rtmpt_module, "max_packet_size",
2682 "Maximum packet size",
2683 "The largest acceptable packet size for reassembly",
2684 10, &rtmpt_max_packet_size);
2688 void
2689 proto_reg_handoff_rtmpt(void)
2691 dissector_handle_t amf_handle;
2693 /* heur_dissector_add("tcp", dissect_rtmpt_heur, proto_rtmpt); */
2694 rtmpt_tcp_handle = new_create_dissector_handle(dissect_rtmpt_tcp, proto_rtmpt);
2695 /* dissector_add_handle("tcp.port", rtmpt_tcp_handle); */
2696 dissector_add_uint("tcp.port", RTMP_PORT, rtmpt_tcp_handle);
2698 rtmpt_http_handle = create_dissector_handle(dissect_rtmpt_http, proto_rtmpt);
2699 dissector_add_string("media_type", "application/x-fcs", rtmpt_http_handle);
2701 amf_handle = create_dissector_handle(dissect_amf, proto_amf);
2702 dissector_add_string("media_type", "application/x-amf", amf_handle);
2705 void
2706 proto_register_amf(void)
2708 static hf_register_info hf[] = {
2709 { &hf_amf_version,
2710 { "AMF version", "amf.version", FT_UINT16, BASE_DEC,
2711 NULL, 0x0, NULL, HFILL }},
2713 { &hf_amf_header_count,
2714 { "Header count", "amf.header_count", FT_UINT16, BASE_DEC,
2715 NULL, 0x0, NULL, HFILL }},
2717 { &hf_amf_header_name,
2718 { "Name", "amf.header.name", FT_UINT_STRING, BASE_NONE,
2719 NULL, 0x0, NULL, HFILL }},
2721 { &hf_amf_header_must_understand,
2722 { "Must understand", "amf.header.must_understand", FT_BOOLEAN, BASE_NONE,
2723 NULL, 0x0, NULL, HFILL }},
2725 { &hf_amf_header_length,
2726 { "Length", "amf.header.length", FT_UINT32, BASE_DEC,
2727 NULL, 0x0, NULL, HFILL }},
2729 #if 0
2730 { &hf_amf_header_value_type,
2731 { "Value type", "amf.header.value_type", FT_UINT32, BASE_HEX,
2732 VALS(rtmpt_type_vals), 0x0, NULL, HFILL }},
2733 #endif
2735 { &hf_amf_message_count,
2736 { "Message count", "amf.message_count", FT_UINT16, BASE_DEC,
2737 NULL, 0x0, NULL, HFILL }},
2739 { &hf_amf_message_target_uri,
2740 { "Target URI", "amf.message.target_uri", FT_UINT_STRING, BASE_NONE,
2741 NULL, 0x0, NULL, HFILL }},
2743 { &hf_amf_message_response_uri,
2744 { "Response URI", "amf.message.response_uri", FT_UINT_STRING, BASE_NONE,
2745 NULL, 0x0, NULL, HFILL }},
2747 { &hf_amf_message_length,
2748 { "Length", "amf.message.length", FT_UINT32, BASE_DEC,
2749 NULL, 0x0, NULL, HFILL }},
2752 /* AMF basic types */
2753 { &hf_amf_amf0_type,
2754 { "AMF0 type", "amf.amf0_type", FT_UINT8, BASE_HEX,
2755 VALS(amf0_type_vals), 0x0, NULL, HFILL }},
2757 { &hf_amf_amf3_type,
2758 { "AMF3 type", "amf.amf3_type", FT_UINT8, BASE_HEX,
2759 VALS(amf3_type_vals), 0x0, NULL, HFILL }},
2761 { &hf_amf_number,
2762 { "Number", "amf.number", FT_DOUBLE, BASE_NONE,
2763 NULL, 0x0, "AMF number", HFILL }},
2765 { &hf_amf_integer,
2766 { "Integer", "amf.integer", FT_UINT32, BASE_DEC,
2767 NULL, 0x0, "RTMPT AMF3 integer", HFILL }},
2769 { &hf_amf_boolean,
2770 { "Boolean", "amf.boolean", FT_BOOLEAN, BASE_NONE,
2771 NULL, 0x0, "AMF boolean", HFILL }},
2773 { &hf_amf_stringlength,
2774 { "String length", "amf.stringlength", FT_UINT32, BASE_DEC,
2775 NULL, 0x0, "AMF string length", HFILL }},
2777 { &hf_amf_string,
2778 { "String", "amf.string", FT_STRING, BASE_NONE,
2779 NULL, 0x0, "AMF string", HFILL }},
2781 { &hf_amf_string_reference,
2782 { "String reference", "amf.string_reference", FT_UINT32, BASE_DEC,
2783 NULL, 0x0, "RTMPT AMF3 string reference", HFILL }},
2785 { &hf_amf_object_reference,
2786 { "Object reference", "amf.object_reference", FT_UINT32, BASE_DEC,
2787 NULL, 0x0, "AMF object reference", HFILL }},
2789 { &hf_amf_date,
2790 { "Date", "amf.date", FT_ABSOLUTE_TIME, ABSOLUTE_TIME_LOCAL,
2791 NULL, 0x0, "AMF date", HFILL }},
2793 #if 0
2794 { &hf_amf_longstringlength,
2795 { "String length", "amf.longstringlength", FT_UINT32, BASE_DEC,
2796 NULL, 0x0, "AMF long string length", HFILL }},
2797 #endif
2799 { &hf_amf_longstring,
2800 { "Long string", "amf.longstring", FT_STRING, BASE_NONE,
2801 NULL, 0x0, "AMF long string", HFILL }},
2803 { &hf_amf_xml_doc,
2804 { "XML document", "amf.xml_doc", FT_STRING, BASE_NONE,
2805 NULL, 0x0, "AMF XML document", HFILL }},
2807 { &hf_amf_xmllength,
2808 { "XML text length", "amf.xmllength", FT_UINT32, BASE_DEC,
2809 NULL, 0x0, "AMF E4X XML length", HFILL }},
2811 { &hf_amf_xml,
2812 { "XML", "amf.xml", FT_STRING, BASE_NONE,
2813 NULL, 0x0, "AMF E4X XML", HFILL }},
2815 { &hf_amf_int64,
2816 { "Int64", "amf.int64", FT_INT64, BASE_DEC,
2817 NULL, 0x0, "AMF int64", HFILL }},
2819 { &hf_amf_bytearraylength,
2820 { "ByteArray length", "amf.bytearraylength", FT_UINT32, BASE_DEC,
2821 NULL, 0x0, "RTMPT AMF3 ByteArray length", HFILL }},
2823 { &hf_amf_bytearray,
2824 { "ByteArray", "amf.bytearray", FT_BYTES, BASE_NONE,
2825 NULL, 0x0, "RTMPT AMF3 ByteArray", HFILL }},
2827 /* AMF object types and subfields of the object types */
2828 { &hf_amf_object,
2829 { "Object", "amf.object", FT_NONE, BASE_NONE,
2830 NULL, 0x0, "AMF object", HFILL }},
2832 { &hf_amf_traitcount,
2833 { "Trait count", "amf.traitcount", FT_UINT32, BASE_DEC,
2834 NULL, 0x0, "AMF count of traits for an object", HFILL }},
2836 { &hf_amf_classnamelength,
2837 { "Class name length", "amf.classnamelength", FT_UINT32, BASE_DEC,
2838 NULL, 0x0, "AMF class name length", HFILL }},
2840 { &hf_amf_classname,
2841 { "Class name", "amf.classname", FT_STRING, BASE_NONE,
2842 NULL, 0x0, "AMF class name", HFILL }},
2844 { &hf_amf_membernamelength,
2845 { "Member name length", "amf.membernamelength", FT_UINT32, BASE_DEC,
2846 NULL, 0x0, "AMF member name length", HFILL }},
2848 { &hf_amf_membername,
2849 { "Member name", "amf.membername", FT_STRING, BASE_NONE,
2850 NULL, 0x0, "AMF member name", HFILL }},
2852 { &hf_amf_trait_reference,
2853 { "Trait reference", "amf.trait_reference", FT_UINT32, BASE_DEC,
2854 NULL, 0x0, "AMF trait reference", HFILL }},
2856 { &hf_amf_ecmaarray,
2857 { "ECMA array", "amf.ecmaarray", FT_NONE, BASE_NONE,
2858 NULL, 0x0, "AMF ECMA array", HFILL }},
2860 { &hf_amf_strictarray,
2861 { "Strict array", "amf.strictarray", FT_NONE, BASE_NONE,
2862 NULL, 0x0, "AMF strict array", HFILL }},
2864 { &hf_amf_array,
2865 { "Array", "amf.array", FT_NONE, BASE_NONE,
2866 NULL, 0x0, "RTMPT AMF3 array", HFILL }},
2868 { &hf_amf_arraylength,
2869 { "Array length", "amf.arraylength", FT_UINT32, BASE_DEC,
2870 NULL, 0x0, "AMF array length", HFILL }},
2872 { &hf_amf_arraydenselength,
2873 { "Length of dense portion", "amf.arraydenselength", FT_UINT32, BASE_DEC,
2874 NULL, 0x0, "AMF length of dense portion of array", HFILL }},
2876 static gint *ett[] = {
2877 &ett_amf,
2878 &ett_amf_headers,
2879 &ett_amf_messages,
2880 &ett_amf_value,
2881 &ett_amf_property,
2882 &ett_amf_string,
2883 &ett_amf_array_element,
2884 &ett_amf_traits,
2885 &ett_amf_trait_member,
2888 proto_amf = proto_register_protocol("Action Message Format", "AMF", "amf");
2889 proto_register_field_array(proto_amf, hf, array_length(hf));
2890 proto_register_subtree_array(ett, array_length(ett));
2894 * Editor modelines - http://www.wireshark.org/tools/modelines.html
2896 * Local variables:
2897 * c-basic-offset: 8
2898 * tab-width: 8
2899 * indent-tabs-mode: nil
2900 * End:
2902 * vi: set shiftwidth=8 tabstop=8 expandtab:
2903 * :indentSize=8:tabSize=8:noTabs=true: