For proto_tree_add_item(..., proto_xxx, ...)use ENC_NA as the encoding arg.
[obnox/wireshark/wip.git] / epan / dissectors / packet-rtmpt.c
1 /* packet-rtmpt.c
2  * Routines for Real Time Messaging Protocol packet dissection
3  * metatech <metatech@flashmail.com>
4  *
5  * $Id$
6  *
7  * Wireshark - Network traffic analyzer
8  * By Gerald Combs <gerald@wireshark.org>
9  * Copyright 1998 Gerald Combs
10  *
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.
15  *
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.
20  *
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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
24  */
25
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)
29 *
30 *   RTMP in a nutshell
31 *
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.
36 *
37 *   Documentation:
38 *       RTMP protocol description on Wiki of Red5 Open Source Flash Server
39 *   Default TCP port is 1935
40 */
41
42 #ifdef HAVE_CONFIG_H
43 # include "config.h"
44 #endif
45
46 #include <string.h>
47
48 #include <epan/packet.h>
49
50 #include <epan/emem.h>
51 #include <epan/conversation.h>
52 #include <epan/strutil.h>
53 #include <epan/prefs.h>
54 #include "packet-tcp.h"
55
56 /* #define DEBUG_RTMPT 1 */
57
58 static int proto_rtmpt = -1;
59
60 static int hf_rtmpt_handshake_c0 = -1;
61 static int hf_rtmpt_handshake_s0 = -1;
62 static int hf_rtmpt_handshake_c1 = -1;
63 static int hf_rtmpt_handshake_s1 = -1;
64 static int hf_rtmpt_handshake_c2 = -1;
65 static int hf_rtmpt_handshake_s2 = -1;
66
67 static int hf_rtmpt_header_format = -1;
68 static int hf_rtmpt_header_csid = -1;
69 static int hf_rtmpt_header_timestamp = -1;
70 static int hf_rtmpt_header_timestamp_delta = -1;
71 static int hf_rtmpt_header_body_size = -1;
72 static int hf_rtmpt_header_typeid = -1;
73 static int hf_rtmpt_header_streamid = -1;
74 static int hf_rtmpt_header_ets = -1;
75
76 static int hf_rtmpt_scm_chunksize = -1;
77 static int hf_rtmpt_scm_csid = -1;
78 static int hf_rtmpt_scm_seq = -1;
79 static int hf_rtmpt_scm_was = -1;
80 static int hf_rtmpt_scm_limittype = -1;
81
82 static int hf_rtmpt_ucm_eventtype = -1;
83
84 static int hf_rtmpt_amf_type = -1;
85 static int hf_rtmpt_amf_number = -1;
86 static int hf_rtmpt_amf_boolean = -1;
87 static int hf_rtmpt_amf_stringlength = -1;
88 static int hf_rtmpt_amf_string = -1;
89 static int hf_rtmpt_amf_reference = -1;
90 static int hf_rtmpt_amf_date = -1;
91 static int hf_rtmpt_amf_longstringlength = -1;
92 static int hf_rtmpt_amf_longstring = -1;
93 static int hf_rtmpt_amf_xml = -1;
94 static int hf_rtmpt_amf_int64 = -1;
95
96 static int hf_rtmpt_amf_object = -1;
97 static int hf_rtmpt_amf_ecmaarray = -1;
98 static int hf_rtmpt_amf_strictarray = -1;
99 static int hf_rtmpt_amf_arraylength = -1;
100
101 static int hf_rtmpt_function_call = -1;
102 static int hf_rtmpt_function_response = -1;
103
104 static int hf_rtmpt_audio_control = -1;
105 static int hf_rtmpt_audio_format = -1;
106 static int hf_rtmpt_audio_rate = -1;
107 static int hf_rtmpt_audio_size = -1;
108 static int hf_rtmpt_audio_type = -1;
109 static int hf_rtmpt_audio_data = -1;
110
111 static int hf_rtmpt_video_control = -1;
112 static int hf_rtmpt_video_type = -1;
113 static int hf_rtmpt_video_format = -1;
114 static int hf_rtmpt_video_data = -1;
115
116 static int hf_rtmpt_tag_type = -1;
117 static int hf_rtmpt_tag_datasize = -1;
118 static int hf_rtmpt_tag_timestamp = -1;
119 static int hf_rtmpt_tag_ets = -1;
120 static int hf_rtmpt_tag_streamid = -1;
121 static int hf_rtmpt_tag_tagsize = -1;
122
123 static gint ett_rtmpt = -1;
124 static gint ett_rtmpt_handshake = -1;
125 static gint ett_rtmpt_header = -1;
126 static gint ett_rtmpt_body = -1;
127 static gint ett_rtmpt_ucm = -1;
128 static gint ett_rtmpt_value = -1;
129 static gint ett_rtmpt_property = -1;
130 static gint ett_rtmpt_string = -1;
131 static gint ett_rtmpt_object = -1;
132 static gint ett_rtmpt_mixed_array = -1;
133 static gint ett_rtmpt_array = -1;
134 static gint ett_rtmpt_audio_control = -1;
135 static gint ett_rtmpt_video_control = -1;
136 static gint ett_rtmpt_tag = -1;
137 static gint ett_rtmpt_tag_data = -1;
138
139 static dissector_handle_t rtmpt_tcp_handle;
140 static dissector_handle_t rtmpt_http_handle;
141
142 static gboolean rtmpt_desegment = TRUE;
143
144 #define RTMP_PORT                     1935
145
146 #define RTMPT_MAGIC                   0x03
147 #define RTMPT_HANDSHAKE_OFFSET_1      1
148 #define RTMPT_HANDSHAKE_OFFSET_2      1538
149 #define RTMPT_HANDSHAKE_OFFSET_3      3074
150 #define RTMPT_HANDSHAKE_LENGTH_1      1537
151 #define RTMPT_HANDSHAKE_LENGTH_2      3073
152 #define RTMPT_HANDSHAKE_LENGTH_3      1536
153 #define RTMPT_DEFAULT_CHUNK_SIZE      128
154
155 /* Native Bandwidth Detection (using the checkBandwidth(), onBWCheck(),
156  * onBWDone() calls) transmits a series of increasing size packets over
157  * the course of 2 seconds. On a fast link the largest packet can just
158  * exceed 256KB. */
159 /* #define RTMPT_MAX_PACKET_SIZE     131072 */
160 /* #define RTMPT_MAX_PACKET_SIZE     262144 */
161 #define RTMPT_MAX_PACKET_SIZE         524288
162
163 #define RTMPT_ID_MAX                  65599
164 #define RTMPT_TYPE_HANDSHAKE_1        0x100001
165 #define RTMPT_TYPE_HANDSHAKE_2        0x100002
166 #define RTMPT_TYPE_HANDSHAKE_3        0x100003
167
168 #define RTMPT_TYPE_CHUNK_SIZE         0x01
169 #define RTMPT_TYPE_ABORT_MESSAGE      0x02
170 #define RTMPT_TYPE_ACKNOWLEDGEMENT    0x03
171 #define RTMPT_TYPE_UCM                0x04
172 #define RTMPT_TYPE_WINDOW             0x05
173 #define RTMPT_TYPE_PEER_BANDWIDTH     0x06
174 #define RTMPT_TYPE_AUDIO_DATA         0x08
175 #define RTMPT_TYPE_VIDEO_DATA         0x09
176 #define RTMPT_TYPE_DATA_AMF3          0x0F
177 #define RTMPT_TYPE_SHARED_AMF3        0x10
178 #define RTMPT_TYPE_COMMAND_AMF3       0x11
179 #define RTMPT_TYPE_DATA_AMF0          0x12
180 #define RTMPT_TYPE_SHARED_AMF0        0x13
181 #define RTMPT_TYPE_COMMAND_AMF0       0x14
182 #define RTMPT_TYPE_AGGREGATE          0x16
183
184 #define RTMPT_UCM_STREAM_BEGIN        0x00
185 #define RTMPT_UCM_STREAM_EOF          0x01
186 #define RTMPT_UCM_STREAM_DRY          0x02
187 #define RTMPT_UCM_SET_BUFFER          0x03
188 #define RTMPT_UCM_STREAM_ISRECORDED   0x04
189 #define RTMPT_UCM_PING_REQUEST        0x06
190 #define RTMPT_UCM_PING_RESPONSE       0x07
191
192 #define RTMPT_AMF_NUMBER              0x00
193 #define RTMPT_AMF_BOOLEAN             0x01
194 #define RTMPT_AMF_STRING              0x02
195 #define RTMPT_AMF_OBJECT              0x03
196 #define RTMPT_AMF_MOVIECLIP           0x04
197 #define RTMPT_AMF_NULL                0x05
198 #define RTMPT_AMF_UNDEFINED           0x06
199 #define RTMPT_AMF_REFERENCE           0x07
200 #define RTMPT_AMF_ECMA_ARRAY          0x08
201 #define RTMPT_AMF_END_OF_OBJECT       0x09
202 #define RTMPT_AMF_STRICT_ARRAY        0x0A
203 #define RTMPT_AMF_DATE                0x0B
204 #define RTMPT_AMF_LONG_STRING         0x0C
205 #define RTMPT_AMF_UNSUPPORTED         0x0D
206 #define RTMPT_AMF_RECORDSET           0x0E
207 #define RTMPT_AMF_XML                 0x0F
208 #define RTMPT_AMF_TYPED_OBJECT        0x10
209 #define RTMPT_AMF_AMF3_MARKER         0x11
210 #define RTMPT_AMF_INT64               0x22
211
212 #define RTMPT_TEXT_RTMP_HEADER        "RTMP Header"
213 #define RTMPT_TEXT_RTMP_BODY          "RTMP Body"
214
215 static const value_string rtmpt_handshake_vals[] = {
216   { RTMPT_TYPE_HANDSHAKE_1,           "Handshake C0+C1" },
217   { RTMPT_TYPE_HANDSHAKE_2,           "Handshake S0+S1+S2" },
218   { RTMPT_TYPE_HANDSHAKE_3,           "Handshake C2" },
219   { 0, NULL }
220 };
221
222 static const value_string rtmpt_opcode_vals[] = {
223   { RTMPT_TYPE_CHUNK_SIZE,            "Set Chunk Size" },
224   { RTMPT_TYPE_ABORT_MESSAGE,         "Abort Message" },
225   { RTMPT_TYPE_ACKNOWLEDGEMENT,       "Acknowledgement" },
226   { RTMPT_TYPE_UCM,                   "User Control Message" },
227   { RTMPT_TYPE_WINDOW,                "Window Acknowledgement Size" },
228   { RTMPT_TYPE_PEER_BANDWIDTH,        "Set Peer Bandwidth" },
229   { RTMPT_TYPE_AUDIO_DATA,            "Audio Data" },
230   { RTMPT_TYPE_VIDEO_DATA,            "Video Data" },
231   { RTMPT_TYPE_DATA_AMF3,             "AMF3 Data" },
232   { RTMPT_TYPE_SHARED_AMF3,           "AMF3 Shared Object" },
233   { RTMPT_TYPE_COMMAND_AMF3,          "AMF3 Command" },
234   { RTMPT_TYPE_DATA_AMF0,             "AMF0 Data" },
235   { RTMPT_TYPE_SHARED_AMF0,           "AMF0 Shared Object" },
236   { RTMPT_TYPE_COMMAND_AMF0,          "AMF0 Command" },
237   { RTMPT_TYPE_AGGREGATE,             "Aggregate" },
238   { 0, NULL }
239 };
240
241 static const value_string rtmpt_limit_vals[] = {
242 /* These are a complete guess, from the order of the documented
243  * options - the values aren't actually specified */
244   { 0,                                "Hard" },
245   { 1,                                "Soft" },
246   { 2,                                "Dynamic" },
247   { 0, NULL }
248 };
249
250 static const value_string rtmpt_ucm_vals[] = {
251   { RTMPT_UCM_STREAM_BEGIN,           "Stream Begin" },
252   { RTMPT_UCM_STREAM_EOF,             "Stream EOF" },
253   { RTMPT_UCM_STREAM_DRY,             "Stream Dry" },
254   { RTMPT_UCM_SET_BUFFER,             "Set Buffer Length" },
255   { RTMPT_UCM_STREAM_ISRECORDED,      "Stream Is Recorded" },
256   { RTMPT_UCM_PING_REQUEST,           "Ping Request" },
257   { RTMPT_UCM_PING_RESPONSE,          "Ping Response" },
258   { 0, NULL }
259 };
260
261 static const value_string rtmpt_type_vals[] = {
262   { RTMPT_AMF_NUMBER,                 "Number" },
263   { RTMPT_AMF_BOOLEAN,                "Boolean" },
264   { RTMPT_AMF_STRING,                 "String" },
265   { RTMPT_AMF_OBJECT,                 "Object" },
266   { RTMPT_AMF_MOVIECLIP,              "Movie clip" },
267   { RTMPT_AMF_NULL,                   "Null" },
268   { RTMPT_AMF_UNDEFINED,              "Undefined" },
269   { RTMPT_AMF_REFERENCE,              "Reference" },
270   { RTMPT_AMF_ECMA_ARRAY,             "ECMA array" },
271   { RTMPT_AMF_END_OF_OBJECT,          "End of object" },
272   { RTMPT_AMF_STRICT_ARRAY,           "Strict array" },
273   { RTMPT_AMF_DATE,                   "Date" },
274   { RTMPT_AMF_LONG_STRING,            "Long string" },
275   { RTMPT_AMF_UNSUPPORTED,            "Unsupported" },
276   { RTMPT_AMF_RECORDSET,              "Record set" },
277   { RTMPT_AMF_XML,                    "XML" },
278   { RTMPT_AMF_TYPED_OBJECT,           "Typed object" },
279   { RTMPT_AMF_AMF3_MARKER,            "Switch to AMF3" },
280   { RTMPT_AMF_INT64,                  "Int64" },
281   { 0, NULL }
282 };
283
284 static const value_string rtmpt_object_vals[] = {
285   { RTMPT_AMF_OBJECT,                 "Object" },
286   { RTMPT_AMF_ECMA_ARRAY,             "ECMA Array" },
287   { RTMPT_AMF_STRICT_ARRAY,           "Strict Array" },
288   { 0, NULL }
289 };
290
291 static const value_string rtmpt_tag_vals[] = {
292   { RTMPT_TYPE_AUDIO_DATA,            "Audio Tag" },
293   { RTMPT_TYPE_VIDEO_DATA,            "Video Tag" },
294   { RTMPT_TYPE_DATA_AMF0,             "Script Tag" },
295   { 0, NULL }
296 };
297
298 /* [Spec] http://www.adobe.com/content/dam/Adobe/en/devnet/rtmp/pdf/rtmp_specification_1.0.pdf       */
299 /* [DevG] http://help.adobe.com/en_US/flashmediaserver/devguide/index.html "working with Live Video" */
300 static const value_string rtmpt_audio_codecs[] = {
301   {  0,                               "Uncompressed" },             /* [DevG] */
302   {  1,                               "ADPCM" },                    /* [DevG] */
303   {  2,                               "MP3" },                      /* [DevG] */
304   {  5,                               "Nellymoser 8kHz Mono" },     /* [DevG] */
305   {  6,                               "Nellymoser 8kHz Stereo" },   /* [DevG] */
306   {  7,                               "G711A" },                    /* [Spec] */
307   {  8,                               "G711U" },                    /* [Spec] */
308   {  9,                               "Nellymoser 16kHz" },         /* [Spec] */
309   { 10,                               "HE-AAC" },                   /* [DevG] */
310   { 11,                               "SPEEX" },                    /* [DevG] */
311   { 0, NULL }
312 };
313
314 static const value_string rtmpt_audio_rates[] = {
315   { 0,                                "5.5 kHz" },
316   { 1,                                "11 kHz" },
317   { 2,                                "22 kHz" },
318   { 3,                                "44 kHz" },
319   { 0, NULL }
320 };
321
322 static const value_string rtmpt_audio_sizes[] = {
323   { 0,                                "8 bit" },
324   { 1,                                "16 bit" },
325   { 0, NULL }
326 };
327
328 static const value_string rtmpt_audio_types[] = {
329   { 0,                                "mono" },
330   { 1,                                "stereo" },
331   { 0, NULL }
332 };
333
334 static const value_string rtmpt_video_types[] = {
335   { 1,                                "keyframe" },
336   { 2,                                "inter-frame" },
337   { 3,                                "disposable inter-frame" },
338   { 0, NULL }
339 };
340
341 static const value_string rtmpt_video_codecs[] = {
342   { 2,                                "Sorensen H.263" },
343   { 3,                                "Screen video" },
344   { 4,                                "On2 VP6" },
345   { 5,                                "On2 VP6+alpha" },
346   { 6,                                "Screen video version 2" },
347   { 7,                                "H.264" },
348   { 0, NULL }
349 };
350
351 /* Holds the reassembled data for a packet during un-chunking
352  */
353 typedef struct rtmpt_packet {
354         guint32 seq;
355         guint32 lastseq;
356
357         int resident;
358         union {
359                 guint8 *p;
360                 guint32 offset;
361         } data;
362
363         /* used during unchunking */
364         int want;
365         int have;
366         int chunkwant;
367         int chunkhave;
368
369         guint8 bhlen;
370         guint8 mhlen;
371
372         /* Chunk Basic Header */
373         guint8 fmt; /* byte 0 */
374         guint32 id;   /* byte 0 */
375
376         /* Chunk Message Header (offsets assume bhlen==1) */
377         guint32 ts;  /* bytes 1-3, or from ETS @ mhlen-4 if -1 */
378         guint32 len; /* bytes 4-6 */
379         guint8 cmd;  /* byte 7 */
380         guint32 src; /* bytes 8-11 */
381
382         guint32 txid;
383         gint isresponse;
384         gint otherframe;
385
386 } rtmpt_packet_t;
387
388 /* Represents a header or a chunk that is split over two TCP
389  * segments
390  */
391 typedef struct rtmpt_frag {
392         int ishdr;
393         guint32 seq;
394         guint32 lastseq;
395         int have;
396         int len;
397
398         union {
399                 guint8 d[18]; /* enough for a complete header (3 + 11 + 4) */
400                 guint32 id;
401         } saved;
402 } rtmpt_frag_t;
403
404 /* The full message header information for the last packet on a particular
405  * ID - used for defaulting short headers
406  */
407 typedef struct rtmpt_id {
408         guint32 ts;  /* bytes 1-3 */
409         guint32 tsd;
410         guint32 len; /* bytes 4-6 */
411         guint32 src; /* bytes 8-11 */
412         guint8 cmd;  /* byte 7 */
413
414         emem_tree_t *packets;
415 } rtmpt_id_t;
416
417 /* Historical view of a whole TCP connection
418  */
419 typedef struct rtmpt_conv {
420         emem_tree_t *seqs[2];
421         emem_tree_t *frags[2];
422         emem_tree_t *ids[2];
423         emem_tree_t *packets[2];
424         emem_tree_t *chunksize[2];
425         emem_tree_t *txids[2];
426 } rtmpt_conv_t;
427
428 #ifdef DEBUG_RTMPT
429 static void rtmpt_debug(const char *fmt, ...)
430 {
431         va_list args;
432         va_start(args, fmt);
433         vprintf(fmt, args);
434 }
435 #define RTMPT_DEBUG rtmpt_debug
436 #else
437 static void rtmpt_debug(const char *fmt, ...){ (void)fmt; }
438 #define RTMPT_DEBUG 1 ? (void)0 : rtmpt_debug
439 #endif
440
441 /* Header length helpers */
442
443 static gint rtmpt_basic_header_length(gint id)
444 {
445         switch (id & 0x3f) {
446         case 0: return 2;
447         case 1: return 3;
448         default: return 1;
449         }
450 }
451
452 static gint rtmpt_message_header_length(gint id)
453 {
454         switch ((id>>6) & 3) {
455         case 0: return 11;
456         case 1: return 7;
457         case 2: return 3;
458         default: return 0;
459         }
460 }
461
462 /* Lightweight access to AMF0 blobs - more complete dissection is done
463  * in dissect_rtmpt_body_command */
464
465 static gint
466 rtmpt_get_amf_length(tvbuff_t *tvb, gint offset)
467 {
468         guint8 iObjType;
469         gint remain = tvb_length_remaining(tvb, offset);
470         guint32 depth = 0;
471         gint itemlen = 0;
472         gint rv = 0;
473
474         while (rv==0 || depth>0) {
475
476                 if (depth>0) {
477                         if (remain-rv<2) return remain;
478                         itemlen = tvb_get_ntohs(tvb, offset+rv) + 2;
479                         if (remain-rv<itemlen+1) return remain;
480                         rv += itemlen;
481                 }
482
483                 if (remain-rv<1) return remain;
484                 iObjType = tvb_get_guint8(tvb, offset+rv);
485
486                 if (depth>0 && itemlen==2 && iObjType==RTMPT_AMF_END_OF_OBJECT) {
487                         rv++;
488                         depth--;
489                         continue;
490                 }
491
492                 switch (iObjType) {
493                 case RTMPT_AMF_NUMBER:
494                         itemlen = 9;
495                         break;
496                 case RTMPT_AMF_BOOLEAN:
497                         itemlen = 2;
498                         break;
499                 case RTMPT_AMF_STRING:
500                         if (remain-rv<3) return remain;
501                         itemlen = tvb_get_ntohs(tvb, offset+rv+1) + 3;
502                         break;
503                 case RTMPT_AMF_NULL:
504                 case RTMPT_AMF_UNDEFINED:
505                 case RTMPT_AMF_UNSUPPORTED:
506                         itemlen= 1;
507                         break;
508                 case RTMPT_AMF_DATE:
509                         itemlen = 11;
510                         break;
511                 case RTMPT_AMF_LONG_STRING:
512                 case RTMPT_AMF_XML:
513                         if (remain-rv<5) return remain;
514                         itemlen = tvb_get_ntohl(tvb, offset+rv+1) + 5;
515                         break;
516                 case RTMPT_AMF_INT64:
517                         itemlen = 9;
518                         break;
519                 case RTMPT_AMF_OBJECT:
520                         itemlen = 1;
521                         depth++;
522                         break;
523                 case RTMPT_AMF_ECMA_ARRAY:
524                         itemlen = 5;
525                         depth++;
526                         break;
527                 default:
528                         return remain;
529                 }
530
531                 if (remain-rv<itemlen) return remain;
532                 rv += itemlen;
533
534         }
535
536         return rv;
537 }
538
539 static gchar*
540 rtmpt_get_amf_param(tvbuff_t *tvb, gint offset, gint param, const gchar *prop)
541 {
542         guint32 remain = tvb_length_remaining(tvb, offset);
543         guint32 itemlen;
544         guint32 iStringLength;
545
546         while (remain>0 && param>0) {
547                 itemlen = rtmpt_get_amf_length(tvb, offset);
548                 offset += itemlen;
549                 remain -= itemlen;
550                 param--;
551         }
552
553         if (remain>0 && param==0) {
554                 guint8 iObjType = tvb_get_guint8(tvb, offset);
555
556                 if (!prop && iObjType==RTMPT_AMF_STRING && remain>=3) {
557                         iStringLength = tvb_get_ntohs(tvb, offset+1);
558                         if (remain>=iStringLength+3) {
559                                 return tvb_get_ephemeral_string(tvb, offset+3, iStringLength);
560                         }
561                 }
562
563                 if (prop && iObjType==RTMPT_AMF_OBJECT) {
564                         offset++;
565                         remain--;
566
567                         while (remain>2) {
568                                 guint32 iPropLength = tvb_get_ntohs(tvb, offset);
569                                 if (remain<2+iPropLength+3) break;
570
571                                 if (tvb_strneql(tvb, offset+2, prop, strlen(prop))==0) {
572                                         if (tvb_get_guint8(tvb, offset+2+iPropLength)!=RTMPT_AMF_STRING) break;
573
574                                         iStringLength = tvb_get_ntohs(tvb, offset+2+iPropLength+1);
575                                         if (remain<2+iPropLength+3+iStringLength) break;
576
577                                         return tvb_get_ephemeral_string(tvb, offset+2+iPropLength+3, iStringLength);
578                                 }
579
580                                 itemlen = rtmpt_get_amf_length(tvb, offset+2+iPropLength);
581                                 offset += 2+iPropLength+itemlen;
582                                 remain -= 2+iPropLength+itemlen;
583                         }
584                 }
585         }
586
587         return NULL;
588 }
589
590 static guint32
591 rtmpt_get_amf_txid(tvbuff_t *tvb, gint offset)
592 {
593         guint32 remain = tvb_length_remaining(tvb, offset);
594
595         if (remain>0) {
596                 guint32 itemlen = rtmpt_get_amf_length(tvb, offset);
597                 if (remain<itemlen) return 0;
598                 offset += itemlen;
599                 remain -= itemlen;
600         }
601         if (remain>=9) {
602                 guint8 iObjType = tvb_get_guint8(tvb, offset);
603                 if (iObjType==RTMPT_AMF_NUMBER) {
604                         return (guint32)tvb_get_ntohieee_double(tvb, offset+1);
605                 }
606         }
607
608         return 0;
609 }
610
611
612 /* Generate a useful description for various packet types */
613
614 static gchar*
615 rtmpt_get_packet_desc(tvbuff_t *tvb, guint32 offset, guint32 remain, rtmpt_conv_t *rconv, int cdir, rtmpt_packet_t *tp, gint *deschasopcode)
616 {
617         if (tp->cmd==RTMPT_TYPE_CHUNK_SIZE || tp->cmd==RTMPT_TYPE_ABORT_MESSAGE ||
618             tp->cmd==RTMPT_TYPE_ACKNOWLEDGEMENT || tp->cmd==RTMPT_TYPE_WINDOW) {
619                 if (tp->len>=4 && remain>=4) {
620                         *deschasopcode = TRUE;
621                         return ep_strdup_printf("%s %d",
622                                                 val_to_str(tp->cmd, rtmpt_opcode_vals, "Unknown (0x%01x)"),
623                                                 tvb_get_ntohl(tvb, offset));
624                 }
625
626         } else if (tp->cmd==RTMPT_TYPE_PEER_BANDWIDTH) {
627                 if (tp->len>=5 && remain>=5) {
628                         *deschasopcode = TRUE;
629                         return ep_strdup_printf("%s %d,%s",
630                                                 val_to_str(tp->cmd, rtmpt_opcode_vals, "Unknown (0x%01x)"),
631                                                 tvb_get_ntohl(tvb, offset),
632                                                 val_to_str(tvb_get_guint8(tvb, offset+4), rtmpt_limit_vals, "Unknown (%d)"));
633                 }
634
635         } else if (tp->cmd==RTMPT_TYPE_UCM) {
636                 guint16 iUCM = -1;
637                 const gchar *sFunc = NULL;
638                 const gchar *sParam = "";
639
640                 if (tp->len<2 || remain<2) return NULL;
641
642                 iUCM = tvb_get_ntohs(tvb, offset);
643                 sFunc = match_strval(iUCM, rtmpt_ucm_vals);
644                 if (sFunc==NULL) {
645                         *deschasopcode = TRUE;
646                         sFunc = ep_strdup_printf("User Control Message 0x%01x", iUCM);
647                 }
648
649                 if (iUCM==RTMPT_UCM_STREAM_BEGIN || iUCM==RTMPT_UCM_STREAM_EOF ||
650                     iUCM==RTMPT_UCM_STREAM_DRY || iUCM==RTMPT_UCM_STREAM_ISRECORDED) {
651                         if (tp->len>=6 && remain>=6) {
652                                 sParam = ep_strdup_printf(" %d", tvb_get_ntohl(tvb, offset+2));
653                         }
654                 } else if (iUCM==RTMPT_UCM_SET_BUFFER) {
655                         if (tp->len>=10 && remain>=10) {
656                                 sParam = ep_strdup_printf(" %d,%dms",
657                                                           tvb_get_ntohl(tvb, offset+2),
658                                                           tvb_get_ntohl(tvb, offset+6));
659                         }
660                 }
661
662                 return ep_strdup_printf("%s%s", sFunc, sParam);
663
664         } else if (tp->cmd==RTMPT_TYPE_COMMAND_AMF0 || tp->cmd==RTMPT_TYPE_COMMAND_AMF3 ||
665                    tp->cmd==RTMPT_TYPE_DATA_AMF0 || tp->cmd==RTMPT_TYPE_DATA_AMF3) {
666                 guint32 slen = 0;
667                 guint32 soff = 0;
668                 gchar *sFunc = NULL;
669                 gchar *sParam = NULL;
670
671                 if (tp->cmd==RTMPT_TYPE_COMMAND_AMF3 || tp->cmd==RTMPT_TYPE_DATA_AMF3) {
672                         soff = 1;
673                 }
674                 if (tp->len>=3+soff && remain>=3+soff) {
675                         slen = tvb_get_ntohs(tvb, offset+1+soff);
676                 }
677                 if (slen>0) {
678                         sFunc = tvb_get_ephemeral_string(tvb, offset+3+soff, slen);
679                         RTMPT_DEBUG("got function call '%s'\n", sFunc);
680
681                         if (strcmp(sFunc, "connect")==0) {
682                                 sParam = rtmpt_get_amf_param(tvb, offset+soff, 2, "app");
683                         } else if (strcmp(sFunc, "play")==0) {
684                                 sParam = rtmpt_get_amf_param(tvb, offset+soff, 3, NULL);
685                         } else if (strcmp(sFunc, "play2")==0) {
686                                 sParam = rtmpt_get_amf_param(tvb, offset+soff, 3, "streamName");
687                         } else if (strcmp(sFunc, "releaseStream")==0) {
688                                 sParam = rtmpt_get_amf_param(tvb, offset+soff, 3, NULL);
689                         } else if (strcmp(sFunc, "FCPublish")==0) {
690                                 sParam = rtmpt_get_amf_param(tvb, offset+soff, 3, NULL);
691                         } else if (strcmp(sFunc, "publish")==0) {
692                                 sParam = rtmpt_get_amf_param(tvb, offset+soff, 3, NULL);
693                         } else if (strcmp(sFunc, "onStatus")==0) {
694                                 if (tp->cmd==RTMPT_TYPE_COMMAND_AMF0 || tp->cmd==RTMPT_TYPE_COMMAND_AMF3) {
695                                         sParam = rtmpt_get_amf_param(tvb, offset+soff, 3, "code");
696                                 } else {
697                                         sParam = rtmpt_get_amf_param(tvb, offset+soff, 1, "code");
698                                 }
699                         } else if (strcmp(sFunc, "onPlayStatus")==0) {
700                                 sParam = rtmpt_get_amf_param(tvb, offset+soff, 1, "code");
701                         } else if (strcmp(sFunc, "_result")==0) {
702                                 sParam = rtmpt_get_amf_param(tvb, offset+soff, 3, "code");
703                                 tp->isresponse = TRUE;
704                         } else if (strcmp(sFunc, "_error")==0) {
705                                 sParam = rtmpt_get_amf_param(tvb, offset+soff, 3, "code");
706                                 tp->isresponse = TRUE;
707                         }
708
709                         if (tp->txid!=0 && tp->otherframe==0) {
710                                 tp->otherframe = GPOINTER_TO_INT(se_tree_lookup32(rconv->txids[cdir^1], tp->txid));
711                                 if (tp->otherframe) {
712                                         RTMPT_DEBUG("got otherframe=%d\n", tp->otherframe);
713                                 }
714                         }
715                 }
716
717                 if (sFunc) {
718                         if (sParam) {
719                                 return ep_strdup_printf("%s('%s')", sFunc, sParam);
720                         } else {
721                                 return ep_strdup_printf("%s()", sFunc);
722                         }
723                 }
724         }
725
726         return NULL;
727 }
728
729
730 /* Tree dissection helpers for various packet body forms */
731
732 static void
733 dissect_rtmpt_body_scm(tvbuff_t *tvb, gint offset, proto_tree *rtmpt_tree, guint scm)
734 {
735         switch (scm) {
736         case RTMPT_TYPE_CHUNK_SIZE:
737                 proto_tree_add_item(rtmpt_tree, hf_rtmpt_scm_chunksize, tvb, offset, 4, ENC_BIG_ENDIAN);
738                 break;
739         case RTMPT_TYPE_ABORT_MESSAGE:
740                 proto_tree_add_item(rtmpt_tree, hf_rtmpt_scm_csid, tvb, offset, 4, ENC_BIG_ENDIAN);
741                 break;
742         case RTMPT_TYPE_ACKNOWLEDGEMENT:
743                 proto_tree_add_item(rtmpt_tree, hf_rtmpt_scm_seq, tvb, offset, 4, ENC_BIG_ENDIAN);
744                 break;
745         case RTMPT_TYPE_UCM:
746                 proto_tree_add_item(rtmpt_tree, hf_rtmpt_ucm_eventtype, tvb, offset, 2, ENC_BIG_ENDIAN);
747                 break;
748         case RTMPT_TYPE_WINDOW:
749                 proto_tree_add_item(rtmpt_tree, hf_rtmpt_scm_was, tvb, offset, 4, ENC_BIG_ENDIAN);
750                 break;
751         case RTMPT_TYPE_PEER_BANDWIDTH:
752                 proto_tree_add_item(rtmpt_tree, hf_rtmpt_scm_was, tvb, offset, 4, ENC_BIG_ENDIAN);
753                 proto_tree_add_item(rtmpt_tree, hf_rtmpt_scm_limittype, tvb, offset+4, 1, ENC_BIG_ENDIAN);
754                 break;
755         }
756 }
757
758 static void
759 dissect_rtmpt_body_command(tvbuff_t *tvb, gint offset, proto_tree *rtmpt_tree, guint amf3)
760 {
761         ep_stack_t amftrs;
762         ep_stack_t amftis;
763         ep_stack_t amfols;
764         ep_stack_t amfots;
765         ep_stack_t amfpcs;
766         int depth = 0;
767         int ot = 0;
768         int pc = 0;
769         proto_item *ti_object = NULL;
770         gint iObjectLength = 0;
771
772         amftrs = ep_stack_new();
773         amftis = ep_stack_new();
774         amfols = ep_stack_new();
775         amfots = ep_stack_new();
776         amfpcs = ep_stack_new();
777
778         if (amf3) {
779                 /* Looks like for the AMF3 variants we get a 0 byte here,
780                  * followed by AMF0 encoding - I've never seen actual AMF3
781                  * encoding used, which is completely different. I speculate
782                  * that if the byte is RTMPT_AMF_AMF3_MARKER then the rest
783                  * will be in AMF3. For now, assume AMF0 only. */
784                 offset++;
785         }
786
787         while (tvb_length_remaining(tvb, offset) > 0)
788         {
789                 guint8 iObjType = 0;
790                 gint iPropertyOffset = 0;
791                 gint iPropertyLength = 0;
792                 guint iStringLength = 0;
793                 gint iValueOffset = 0;
794                 gint iValueLength = 0; /* signed so we can use CLAMP() */
795                 guint iValueExtra = 0;
796                 gchar *sValue = "";
797                 int hfvalue = -1;
798                 guint iPush = 0;
799                 proto_tree *rtmpt_tree_prop = NULL;
800                 proto_item *ti = NULL;
801
802                 rtmpt_tree_prop = rtmpt_tree;
803
804                 iValueOffset = offset;
805
806                 if (depth>0 && !ot) {
807                         iStringLength = tvb_get_ntohs(tvb, offset);
808
809                         if (iStringLength==0 && tvb_get_guint8(tvb, offset + 2)==RTMPT_AMF_END_OF_OBJECT)
810                         {
811                                 iObjType = RTMPT_AMF_END_OF_OBJECT;
812                                 goto popamf;
813                         }
814
815                         iPropertyOffset = offset;
816                         iPropertyLength = 2 + iStringLength;
817                         iValueOffset += iPropertyLength;
818                 }
819
820                 iObjType = tvb_get_guint8(tvb, iValueOffset);
821                 iValueOffset++;
822                 iValueExtra = 1;
823
824                 switch (iObjType) {
825                 case RTMPT_AMF_NUMBER:
826                         iValueLength = 8;
827                         hfvalue = hf_rtmpt_amf_number;
828                         sValue = ep_strdup_printf(" %." STRINGIFY(DBL_DIG) "g", tvb_get_ntohieee_double(tvb, iValueOffset));
829                         break;
830                 case RTMPT_AMF_BOOLEAN:
831                         iValueLength = 1;
832                         hfvalue = hf_rtmpt_amf_boolean;
833                         sValue = tvb_get_guint8(tvb, iValueOffset) ? " true" : " false";
834                         break;
835                 case RTMPT_AMF_STRING:
836                         iValueLength = tvb_get_ntohs(tvb, iValueOffset);
837                         iValueOffset += 2;
838                         iValueExtra = 3;
839                         hfvalue = hf_rtmpt_amf_string;
840                         sValue = ep_strdup_printf(" '%s'", tvb_get_ephemeral_string(tvb, iValueOffset, CLAMP(iValueLength, 0, ITEM_LABEL_LENGTH+1)));
841                         break;
842                 case RTMPT_AMF_OBJECT:
843                         /* Uncounted list type, with end marker */
844                         iValueLength = 0;
845                         hfvalue = hf_rtmpt_amf_object;
846                         iPush = 1;
847                         break;
848                 case RTMPT_AMF_NULL:
849                 case RTMPT_AMF_UNDEFINED:
850                         iValueLength = 0;
851                         break;
852                 case RTMPT_AMF_REFERENCE:
853                         iValueLength = 2;
854                         hfvalue = hf_rtmpt_amf_reference;
855                         sValue = ep_strdup_printf(" %d", tvb_get_ntohs(tvb, iValueOffset));
856                         break;
857                 case RTMPT_AMF_ECMA_ARRAY:
858                         /* Counted list type, with end marker. The count appears to be
859                          * more of a hint than a rule, and is sometimes sent as 0 or invalid.
860                          * Basically the same as OBJECT but with the extra count field.
861                          * There being many strange encoders/metadata injectors out there,
862                          * sometimes you see a valid count and no end marker. Figuring out
863                          * which you've got for a deeply nested structure is non-trivial.
864                          */
865                         iValueLength = 0;
866                         iValueOffset += 4;
867                         iValueExtra = 5;
868                         hfvalue = hf_rtmpt_amf_ecmaarray;
869                         iPush = 1;
870                         break;
871                 case RTMPT_AMF_STRICT_ARRAY:
872                         /* Counted list type, without end marker. Number of values is determined
873                          * by count, values are assumed to form a [0..N-1] numbered array and are
874                          * presented as plain AMF types, not OBJECT or ECMA_ARRAY style named
875                          * properties */
876                         iValueLength = 4;
877                         hfvalue = hf_rtmpt_amf_strictarray;
878                         iPush = 1;
879                         break;
880                 case RTMPT_AMF_DATE:
881                         iValueLength = 10;
882                         hfvalue = hf_rtmpt_amf_date;
883                         break;
884                 case RTMPT_AMF_LONG_STRING:
885                 case RTMPT_AMF_XML: /* same representation */
886                         iValueLength = tvb_get_ntohl(tvb, iValueOffset);
887                         iValueOffset += 4;
888                         iValueExtra = 5;
889                         hfvalue = (iObjType==RTMPT_AMF_XML) ? hf_rtmpt_amf_xml : hf_rtmpt_amf_longstring;
890                         sValue = ep_strdup_printf(" '%s'", tvb_get_ephemeral_string(tvb, iValueOffset, CLAMP(iValueLength, 0, ITEM_LABEL_LENGTH+1)));
891                         break;
892                 case RTMPT_AMF_UNSUPPORTED:
893                         iValueLength = 0;
894                         break;
895                 case RTMPT_AMF_INT64:
896                         iValueLength = 8;
897                         hfvalue = hf_rtmpt_amf_int64;
898                         sValue = ep_strdup_printf(" %" G_GINT64_MODIFIER "d", tvb_get_ntoh64(tvb, iValueOffset));
899                         break;
900                 default:
901                         /* If we can't determine the length, don't carry on */
902                         iValueLength = tvb_length_remaining(tvb, iValueOffset);
903                         sValue = "";
904                         break;
905                 }
906
907                 offset += iPropertyLength + iValueExtra + iValueLength;
908                 iObjectLength += iPropertyLength + iValueExtra + iValueLength;
909                 pc++;
910
911                 if (iPropertyLength>0) {
912                         proto_tree *name_tree = NULL;
913                         gchar *sProperty = tvb_get_ephemeral_string(tvb, iPropertyOffset+2, iPropertyLength-2);
914
915                         ti = proto_tree_add_text(rtmpt_tree, tvb,
916                                                  iPropertyOffset,
917                                                  iPropertyLength+iValueExtra+iValueLength,
918                                                  "Property '%s' %s%s",
919                                                  sProperty, val_to_str(iObjType, rtmpt_type_vals, "Unknown"), sValue);
920                         rtmpt_tree_prop = proto_item_add_subtree(ti, ett_rtmpt_property);
921
922                         ti = proto_tree_add_text(rtmpt_tree_prop, tvb,
923                                                  iPropertyOffset, iPropertyLength,
924                                                  "Name: %s", sProperty);
925                         name_tree = proto_item_add_subtree(ti, ett_rtmpt_string);
926
927                         proto_tree_add_item(name_tree, hf_rtmpt_amf_stringlength, tvb, iPropertyOffset, 2, ENC_BIG_ENDIAN);
928                         proto_tree_add_item(name_tree, hf_rtmpt_amf_string, tvb, iPropertyOffset+2, iPropertyLength-2, ENC_ASCII|ENC_NA);
929                 }
930
931                 if (!iPush) {
932                         proto_tree *val_tree = NULL;
933
934                         ti = proto_tree_add_text(rtmpt_tree_prop, tvb,
935                                                  iValueOffset-iValueExtra, iValueExtra+iValueLength,
936                                                  "%s%s",
937                                                  val_to_str(iObjType, rtmpt_type_vals, "Unknown"), sValue);
938                         val_tree = proto_item_add_subtree(ti, ett_rtmpt_value);
939
940                         proto_tree_add_item(val_tree, hf_rtmpt_amf_type, tvb, iValueOffset-iValueExtra, 1, ENC_BIG_ENDIAN);
941                         if (iObjType==RTMPT_AMF_STRING) {
942                                 proto_tree_add_item(val_tree, hf_rtmpt_amf_stringlength, tvb, iValueOffset-iValueExtra+1, 2, ENC_BIG_ENDIAN);
943                         } else if (iObjType==RTMPT_AMF_LONG_STRING || iObjType==RTMPT_AMF_XML) {
944                                 proto_tree_add_item(val_tree, hf_rtmpt_amf_longstringlength, tvb, iValueOffset-iValueExtra+1, 4, ENC_BIG_ENDIAN);
945                         }
946                         if (iValueLength>0 && hfvalue!=-1) {
947                                 proto_tree_add_item(val_tree, hfvalue, tvb, iValueOffset, iValueLength, FALSE);
948                         }
949                 }
950
951                 if (iPush) {
952                         depth++;
953                         ep_stack_push(amfols, GINT_TO_POINTER(iObjectLength));
954                         iObjectLength = iValueExtra;
955                         ep_stack_push(amfots, GINT_TO_POINTER(ot));
956                         ot = iValueLength>0 ? tvb_get_ntohl(tvb, iValueOffset)+1 : 0;
957                         ep_stack_push(amfpcs, GINT_TO_POINTER(pc));
958                         pc = 0;
959                         ep_stack_push(amftis, ti_object);
960                         ti_object = proto_tree_add_item(rtmpt_tree_prop, hfvalue, tvb, iValueOffset+iValueLength, 1, FALSE);
961                         ep_stack_push(amftrs, rtmpt_tree);
962                         rtmpt_tree = proto_item_add_subtree(ti_object, ett_rtmpt_array);
963
964                         proto_tree_add_item(rtmpt_tree, hf_rtmpt_amf_type, tvb, iValueOffset-iValueExtra, 1, ENC_BIG_ENDIAN);
965                         if (iValueExtra>1 || iValueLength>0) {
966                                 proto_tree_add_item(rtmpt_tree, hf_rtmpt_amf_arraylength, tvb, iValueOffset-iValueExtra+1, 4, ENC_BIG_ENDIAN);
967                         }
968                 }
969
970                 if (!ot || --ot>0) continue;
971
972 popamf:
973                 /* reached end of amf container */
974
975                 if (iObjType==RTMPT_AMF_END_OF_OBJECT) {
976                         proto_tree_add_text(rtmpt_tree_prop, tvb, offset, 3, "End Of Object Marker");
977                         iObjectLength += 3;
978                 }
979
980                 proto_item_set_len(ti_object, iObjectLength);
981                 proto_item_append_text(ti_object, " (%d items)", pc);
982                 depth--;
983                 rtmpt_tree = ep_stack_pop(amftrs);
984                 ti_object = ep_stack_pop(amftis);
985                 ot = GPOINTER_TO_INT(ep_stack_pop(amfots));
986                 pc = GPOINTER_TO_INT(ep_stack_pop(amfpcs));
987                 iObjectLength += GPOINTER_TO_INT(ep_stack_pop(amfols));
988                 if (iObjType==RTMPT_AMF_END_OF_OBJECT) {
989                         offset += 3;
990                 }
991
992                 if (depth>0 && ot>0 && --ot==0) {
993                         iObjType = 0;
994                         goto popamf;
995                 }
996         }
997 }
998
999 static void
1000 dissect_rtmpt_body_audio(tvbuff_t *tvb, gint offset, proto_tree *rtmpt_tree)
1001 {
1002         guint8 iCtl;
1003         proto_item *ai;
1004         proto_tree *at;
1005
1006         iCtl = tvb_get_guint8(tvb, offset);
1007         ai = proto_tree_add_uint_format(rtmpt_tree, hf_rtmpt_audio_control, tvb, offset, 1, iCtl,
1008                                         "Control: 0x%02x (%s %s %s %s)", iCtl,
1009                                         val_to_str((iCtl & 0xf0)>>4, rtmpt_audio_codecs, "Unknown codec"),
1010                                         val_to_str((iCtl & 0x0c)>>2, rtmpt_audio_rates, "Unknown rate"),
1011                                         val_to_str((iCtl & 0x02)>>1, rtmpt_audio_sizes, "Unknown sample size"),
1012                                         val_to_str(iCtl & 0x01, rtmpt_audio_types, "Unknown channel count"));
1013
1014         at = proto_item_add_subtree(ai, ett_rtmpt_audio_control);
1015         proto_tree_add_uint(at, hf_rtmpt_audio_format, tvb, offset, 1, iCtl);
1016         proto_tree_add_uint(at, hf_rtmpt_audio_rate, tvb, offset, 1, iCtl);
1017         proto_tree_add_uint(at, hf_rtmpt_audio_size, tvb, offset, 1, iCtl);
1018         proto_tree_add_uint(at, hf_rtmpt_audio_type, tvb, offset, 1, iCtl);
1019         proto_tree_add_item(rtmpt_tree, hf_rtmpt_audio_data, tvb, offset+1, -1, ENC_NA);
1020 }
1021
1022 static void
1023 dissect_rtmpt_body_video(tvbuff_t *tvb, gint offset, proto_tree *rtmpt_tree)
1024 {
1025         guint8 iCtl;
1026         proto_item *vi;
1027         proto_tree *vt;
1028
1029         iCtl = tvb_get_guint8(tvb, offset);
1030         vi = proto_tree_add_uint_format(rtmpt_tree, hf_rtmpt_video_control, tvb, offset, 1, iCtl,
1031                                         "Control: 0x%02x (%s %s)", iCtl,
1032                                         val_to_str((iCtl & 0xf0)>>4, rtmpt_video_types, "Unknown frame type"),
1033                                         val_to_str(iCtl & 0x0f, rtmpt_video_codecs, "Unknown codec"));
1034
1035         vt = proto_item_add_subtree(vi, ett_rtmpt_video_control);
1036         proto_tree_add_uint(vt, hf_rtmpt_video_type, tvb, offset, 1, iCtl);
1037         proto_tree_add_uint(vt, hf_rtmpt_video_format, tvb, offset, 1, iCtl);
1038         proto_tree_add_item(rtmpt_tree, hf_rtmpt_video_data, tvb, offset+1, -1, ENC_NA);
1039 }
1040
1041 static void
1042 dissect_rtmpt_body_aggregate(tvbuff_t *tvb, gint offset, proto_tree *rtmpt_tree)
1043 {
1044         proto_item *tag_item = NULL;
1045         proto_tree *tag_tree = NULL;
1046
1047         proto_item *data_item = NULL;
1048         proto_tree *data_tree = NULL;
1049
1050         while (tvb_length_remaining(tvb, offset) > 0) {
1051                 guint8 iTagType = 0;
1052                 guint iDataSize = 0;
1053
1054                 iTagType = tvb_get_guint8(tvb, offset + 0);
1055                 iDataSize = tvb_get_ntoh24(tvb, offset + 1);
1056
1057                 tag_item = proto_tree_add_text(rtmpt_tree, tvb, offset, 11+iDataSize+4, "%s", val_to_str(iTagType, rtmpt_tag_vals, "Unknown Tag"));
1058                 tag_tree = proto_item_add_subtree(tag_item, ett_rtmpt_tag);
1059                 proto_tree_add_item(tag_tree, hf_rtmpt_tag_type, tvb, offset+0, 1, ENC_BIG_ENDIAN);
1060                 proto_tree_add_item(tag_tree, hf_rtmpt_tag_datasize, tvb, offset+1, 3, ENC_BIG_ENDIAN);
1061                 proto_tree_add_item(tag_tree, hf_rtmpt_tag_timestamp, tvb, offset+4, 3, ENC_BIG_ENDIAN);
1062                 proto_tree_add_item(tag_tree, hf_rtmpt_tag_ets, tvb, offset+7, 1, ENC_BIG_ENDIAN);
1063                 proto_tree_add_item(tag_tree, hf_rtmpt_tag_streamid, tvb, offset+8, 3, ENC_BIG_ENDIAN);
1064
1065                 data_item = proto_tree_add_text(tag_tree, tvb, offset+11, iDataSize, "Data");
1066                 data_tree = proto_item_add_subtree(data_item, ett_rtmpt_tag_data);
1067
1068                 switch (iTagType) {
1069                 case 8:
1070                         dissect_rtmpt_body_audio(tvb, offset + 11, data_tree);
1071                         break;
1072                 case 9:
1073                         dissect_rtmpt_body_video(tvb, offset + 11, data_tree);
1074                         break;
1075                 case 18:
1076                         dissect_rtmpt_body_command(tvb, offset + 11, data_tree, FALSE);
1077                         break;
1078                 default:
1079                         break;
1080                 }
1081
1082                 proto_tree_add_item(tag_tree, hf_rtmpt_tag_tagsize, tvb, offset+11+iDataSize, 4, ENC_BIG_ENDIAN);
1083                 offset += 11 + iDataSize + 4;
1084         }
1085 }
1086
1087 /* The main dissector for unchunked packets */
1088
1089 static void
1090 dissect_rtmpt(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, rtmpt_conv_t *rconv, int cdir, rtmpt_packet_t *tp)
1091 {
1092         proto_tree      *rtmpt_tree = NULL;
1093         proto_tree      *rtmptroot_tree = NULL;
1094         proto_item      *ti = NULL;
1095         gint offset = 0;
1096         static gint iPreviousFrameNumber = -1;
1097
1098         gchar *sDesc = NULL;
1099         gint deschasopcode = FALSE;
1100         gboolean haveETS = FALSE;
1101         guint32 iBodyOffset = 0;
1102         guint32 iBodyRemain = 0;
1103
1104         col_set_str(pinfo->cinfo, COL_PROTOCOL, "RTMP");
1105
1106         RTMPT_DEBUG("Dissect: frame=%d prev=%d visited=%d len=%d col=%d tree=%p\n", pinfo->fd->num, iPreviousFrameNumber, pinfo->fd->flags.visited, tvb_length_remaining(tvb, offset), check_col(pinfo->cinfo, COL_INFO), tree);
1107
1108         /* This is a trick to know whether this is the first PDU in this packet or not */
1109         if (iPreviousFrameNumber != (gint) PINFO_FD_NUM(pinfo))
1110                 col_clear(pinfo->cinfo, COL_INFO);
1111         else
1112                 col_append_str(pinfo->cinfo, COL_INFO, " | ");
1113         iPreviousFrameNumber = pinfo->fd->num;
1114
1115         if (tvb_length_remaining(tvb, offset) < 1) return;
1116
1117         if (tp->id<=RTMPT_ID_MAX) {
1118                 if (tp->fmt<3 && tvb_length_remaining(tvb, offset)>=tp->bhlen+3 && tvb_get_ntoh24(tvb, offset+tp->bhlen)==0xffffff) {
1119                         haveETS = TRUE;
1120                 }
1121
1122                 iBodyOffset = offset + tp->bhlen + tp->mhlen;
1123                 iBodyRemain = tvb_length_remaining(tvb, iBodyOffset);
1124
1125                 if (tp->cmd==RTMPT_TYPE_CHUNK_SIZE && tp->len>=4 && iBodyRemain>=4) {
1126                         guint32 newchunksize = tvb_get_ntohl(tvb, iBodyOffset);
1127                         if (newchunksize<RTMPT_MAX_PACKET_SIZE) {
1128                                 se_tree_insert32(rconv->chunksize[cdir], tp->lastseq, GINT_TO_POINTER(newchunksize));
1129                         }
1130                 }
1131
1132                 if (!PINFO_FD_VISITED(pinfo)) {
1133                         if (tp->cmd==RTMPT_TYPE_COMMAND_AMF0 || tp->cmd==RTMPT_TYPE_COMMAND_AMF3 ||
1134                             tp->cmd==RTMPT_TYPE_DATA_AMF0 || tp->cmd==RTMPT_TYPE_DATA_AMF3) {
1135                                 guint32 soff = 0;
1136                                 if (tp->cmd==RTMPT_TYPE_COMMAND_AMF3 || tp->cmd==RTMPT_TYPE_DATA_AMF3) {
1137                                         soff = 1;
1138                                 }
1139                                 tp->txid = rtmpt_get_amf_txid(tvb, iBodyOffset+soff);
1140                                 if (tp->txid!=0) {
1141                                         RTMPT_DEBUG("got txid=%d\n", tp->txid);
1142                                         se_tree_insert32(rconv->txids[cdir], tp->txid, GINT_TO_POINTER(pinfo->fd->num));
1143                                 }
1144                         }
1145                 }
1146         }
1147
1148         if ((check_col(pinfo->cinfo, COL_INFO) || tree) && tp->id<=RTMPT_ID_MAX)
1149         {
1150                 sDesc = rtmpt_get_packet_desc(tvb, iBodyOffset, iBodyRemain, rconv, cdir, tp, &deschasopcode);
1151         }
1152
1153         if (check_col(pinfo->cinfo, COL_INFO))
1154         {
1155                 if (tp->id>RTMPT_ID_MAX) {
1156                         col_append_fstr(pinfo->cinfo, COL_INFO, "%s", val_to_str(tp->id, rtmpt_handshake_vals, "Unknown (0x%01x)"));
1157                 } else if (sDesc) {
1158                         col_append_fstr(pinfo->cinfo, COL_INFO, "%s", sDesc);
1159                 } else {
1160                         col_append_fstr(pinfo->cinfo, COL_INFO, "%s", val_to_str(tp->cmd, rtmpt_opcode_vals, "Unknown (0x%01x)"));
1161                 }
1162         }
1163
1164         if (tree)
1165         {
1166                 ti = proto_tree_add_item(tree, proto_rtmpt, tvb, offset, -1, ENC_NA);
1167
1168                 if (tp->id>RTMPT_ID_MAX) {
1169                         /* Dissect handshake */
1170                         proto_item_append_text(ti, " (%s)", val_to_str(tp->id, rtmpt_handshake_vals, "Unknown (0x%01x)"));
1171                         rtmptroot_tree = proto_item_add_subtree(ti, ett_rtmpt);
1172                         ti = proto_tree_add_text(rtmptroot_tree, tvb, offset, -1, "%s", val_to_str(tp->id, rtmpt_handshake_vals, "Unknown (0x%01x)"));
1173                         rtmpt_tree = proto_item_add_subtree(ti, ett_rtmpt_handshake);
1174
1175                         if (tp->id == RTMPT_TYPE_HANDSHAKE_1)
1176                         {
1177                                 proto_tree_add_item(rtmpt_tree, hf_rtmpt_handshake_c0, tvb, 0, 1, ENC_NA);
1178                                 proto_tree_add_item(rtmpt_tree, hf_rtmpt_handshake_c1, tvb, 1, 1536, ENC_NA);
1179                         }
1180                         else if (tp->id == RTMPT_TYPE_HANDSHAKE_2)
1181                         {
1182                                 proto_tree_add_item(rtmpt_tree, hf_rtmpt_handshake_s0, tvb, 0, 1, ENC_NA);
1183                                 proto_tree_add_item(rtmpt_tree, hf_rtmpt_handshake_s1, tvb, 1, 1536, ENC_NA);
1184                                 proto_tree_add_item(rtmpt_tree, hf_rtmpt_handshake_s2, tvb, 1537, 1536, ENC_NA);
1185                         }
1186                         else if (tp->id == RTMPT_TYPE_HANDSHAKE_3)
1187                         {
1188                                 proto_tree_add_item(rtmpt_tree, hf_rtmpt_handshake_c2, tvb, 0, 1536, ENC_NA);
1189                         }
1190
1191                         return;
1192                 }
1193
1194                 if (sDesc && deschasopcode) {
1195                         proto_item_append_text(ti, " (%s)", sDesc);
1196                 } else if (sDesc) {
1197                         proto_item_append_text(ti, " (%s %s)", val_to_str(tp->cmd, rtmpt_opcode_vals, "Unknown (0x%01x)"), sDesc);
1198                 } else {
1199                         proto_item_append_text(ti, " (%s)", val_to_str(tp->cmd, rtmpt_opcode_vals, "Unknown (0x%01x)"));
1200                 }
1201                 rtmptroot_tree = proto_item_add_subtree(ti, ett_rtmpt);
1202
1203                 /* Function call/response matching */
1204                 if (tp->otherframe!=0) {
1205                         proto_tree_add_uint(rtmptroot_tree,
1206                                             tp->isresponse ? hf_rtmpt_function_response : hf_rtmpt_function_call,
1207                                             tvb, offset, tp->bhlen+tp->mhlen+tp->len,
1208                                             tp->otherframe);
1209                 }
1210
1211                 /* Dissect header fields */
1212                 ti = proto_tree_add_text(rtmptroot_tree, tvb, offset, tp->bhlen+tp->mhlen, RTMPT_TEXT_RTMP_HEADER);
1213 /*                proto_item_append_text(ti, " (%s)", val_to_str(tp->cmd, rtmpt_opcode_vals, "Unknown (0x%01x)")); */
1214                 rtmpt_tree = proto_item_add_subtree(ti, ett_rtmpt_header);
1215
1216                 if (tp->fmt <= 3) proto_tree_add_item(rtmpt_tree, hf_rtmpt_header_format, tvb, offset + 0, 1, ENC_BIG_ENDIAN);
1217                 if (tp->fmt <= 3) proto_tree_add_item(rtmpt_tree, hf_rtmpt_header_csid, tvb, offset + 0, tp->bhlen, ENC_BIG_ENDIAN);
1218                 if (tp->fmt <= 2) {
1219                         if (tp->fmt>0) {
1220                                 proto_tree_add_item(rtmpt_tree, hf_rtmpt_header_timestamp_delta, tvb, offset + tp->bhlen, 3, ENC_BIG_ENDIAN);
1221                         } else {
1222                                 proto_tree_add_item(rtmpt_tree, hf_rtmpt_header_timestamp, tvb, offset + tp->bhlen, 3, ENC_BIG_ENDIAN);
1223                         }
1224                         if (haveETS) {
1225                                 proto_tree_add_item(rtmpt_tree, hf_rtmpt_header_ets, tvb, offset + tp->bhlen + tp->mhlen - 4, 4, ENC_BIG_ENDIAN);
1226                         }
1227                 }
1228                 if ((tp->fmt>0 && !haveETS) || tp->fmt==3) {
1229                         proto_tree_add_text(rtmpt_tree, tvb, offset + tp->bhlen, 0, "Timestamp: %d (calculated)", tp->ts);
1230                 }
1231                 if (tp->fmt <= 1) proto_tree_add_item(rtmpt_tree, hf_rtmpt_header_body_size, tvb, offset + tp->bhlen + 3, 3, ENC_BIG_ENDIAN);
1232                 if (tp->fmt <= 1) proto_tree_add_item(rtmpt_tree, hf_rtmpt_header_typeid, tvb, offset + tp->bhlen + 6, 1, ENC_BIG_ENDIAN);
1233                 if (tp->fmt <= 0) proto_tree_add_item(rtmpt_tree, hf_rtmpt_header_streamid, tvb, offset + tp->bhlen + 7, 4, ENC_LITTLE_ENDIAN);
1234
1235                 /* Dissect body */
1236                 if (tp->len==0) return;
1237                 offset = iBodyOffset;
1238
1239                 ti = proto_tree_add_text(rtmptroot_tree, tvb, offset, -1, RTMPT_TEXT_RTMP_BODY);
1240                 rtmpt_tree = proto_item_add_subtree(ti, ett_rtmpt_body);
1241
1242                 switch (tp->cmd) {
1243                 case RTMPT_TYPE_CHUNK_SIZE:
1244                 case RTMPT_TYPE_ABORT_MESSAGE:
1245                 case RTMPT_TYPE_ACKNOWLEDGEMENT:
1246                 case RTMPT_TYPE_UCM:
1247                 case RTMPT_TYPE_WINDOW:
1248                 case RTMPT_TYPE_PEER_BANDWIDTH:
1249                         dissect_rtmpt_body_scm(tvb, offset, rtmpt_tree, tp->cmd);
1250                         break;
1251                 case RTMPT_TYPE_COMMAND_AMF0:
1252                 case RTMPT_TYPE_DATA_AMF0:
1253                         dissect_rtmpt_body_command(tvb, offset, rtmpt_tree, FALSE);
1254                         break;
1255                 case RTMPT_TYPE_COMMAND_AMF3:
1256                 case RTMPT_TYPE_DATA_AMF3:
1257                         dissect_rtmpt_body_command(tvb, offset, rtmpt_tree, TRUE);
1258                         break;
1259                 case RTMPT_TYPE_AUDIO_DATA:
1260                         dissect_rtmpt_body_audio(tvb, offset, rtmpt_tree);
1261                         break;
1262                 case RTMPT_TYPE_VIDEO_DATA:
1263                         dissect_rtmpt_body_video(tvb, offset, rtmpt_tree);
1264                         break;
1265                 case RTMPT_TYPE_AGGREGATE:
1266                         dissect_rtmpt_body_aggregate(tvb, offset, rtmpt_tree);
1267                         break;
1268                 }
1269         }
1270 }
1271
1272 /* Unchunk a data stream into individual RTMP packets */
1273
1274 static void
1275 dissect_rtmpt_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, rtmpt_conv_t *rconv, int cdir, guint32 seq, guint32 lastackseq)
1276 {
1277         int offset = 0;
1278         int remain;
1279         int want;
1280
1281         guint8 header_type;
1282         int basic_hlen;
1283         int message_hlen;
1284
1285         guint32 id;
1286         guint32 ts = 0;
1287         guint32 tsd = 0;
1288         int body_len;
1289         guint8 cmd;
1290         guint32 src;
1291         int chunk_size;
1292
1293         rtmpt_frag_t *tf;
1294         rtmpt_id_t *ti;
1295         rtmpt_packet_t *tp;
1296         tvbuff_t *pktbuf;
1297
1298         remain = tvb_length(tvb);
1299         if (!remain) return;
1300
1301         RTMPT_DEBUG("Segment: cdir=%d seq=%d-%d\n", cdir, seq, seq+remain-1);
1302
1303         if (pinfo->fd->flags.visited) {
1304                 /* Already done the work, so just dump the existing state */
1305                 ep_stack_t packets;
1306
1307                 /* List all RTMP packets terminating in this TCP segment, from end to beginning */
1308
1309                 packets = ep_stack_new();
1310                 ep_stack_push(packets, 0);
1311
1312                 tp = se_tree_lookup32_le(rconv->packets[cdir], seq+remain-1);
1313                 while (tp && tp->lastseq>=seq) {
1314                         ep_stack_push(packets, tp);
1315                         tp = se_tree_lookup32_le(rconv->packets[cdir], tp->lastseq-1);
1316                 }
1317
1318                 /* Dissect the generated list in reverse order (beginning to end) */
1319
1320                 while ((tp=ep_stack_pop(packets))!=NULL) {
1321                         if (tp->resident) {
1322                                 pktbuf = tvb_new_real_data(tp->data.p, tp->have, tp->have);
1323                                 add_new_data_source(pinfo, pktbuf, "Unchunked RTMP");
1324                         } else {
1325                                 pktbuf = tvb_new_subset(tvb, tp->data.offset, tp->have, tp->have);
1326                         }
1327                         dissect_rtmpt(pktbuf, pinfo, tree, rconv, cdir, tp);
1328                 }
1329
1330                 return;
1331         }
1332
1333         while (remain>0) {
1334                 tf = NULL;
1335                 ti = NULL;
1336                 tp = NULL;
1337
1338                 /* Check for outstanding fragmented headers/chunks first */
1339
1340                 if (offset==0) {
1341                         tf = se_tree_lookup32_le(rconv->frags[cdir], seq+offset-1);
1342
1343                         if (tf) {
1344                                 /* May need to reassemble cross-TCP-segment fragments */
1345                                 RTMPT_DEBUG("  tf seq=%d lseq=%d h=%d l=%d\n", tf->seq, tf->lastseq, tf->have, tf->len);
1346                                 if (tf->have>=tf->len || seq+offset<tf->seq || seq+offset>tf->lastseq+tf->len-tf->have) {
1347                                         tf = NULL;
1348                                 } else if (!tf->ishdr) {
1349                                         ti = se_tree_lookup32(rconv->ids[cdir], tf->saved.id);
1350                                         if (ti) tp = se_tree_lookup32_le(ti->packets, seq+offset-1);
1351                                         if (tp && tp->chunkwant) {
1352                                                 goto unchunk;
1353                                         }
1354                                         tf = NULL;
1355                                         ti = NULL;
1356                                         tp = NULL;
1357                                 }
1358
1359                                 if (tf) {
1360                                         /* The preceding segment contained an incomplete chunk header */
1361
1362                                         want = tf->len - tf->have;
1363                                         if (remain<want) want = remain;
1364
1365                                         tvb_memcpy(tvb, tf->saved.d+tf->have, offset, want);
1366
1367                                         id = tf->saved.d[0];
1368                                         header_type = (id>>6) & 3;
1369                                         basic_hlen = rtmpt_basic_header_length(id);
1370                                         message_hlen = rtmpt_message_header_length(id);
1371
1372                                         if (header_type<3 && tf->have<basic_hlen+3 && tf->have+want>=basic_hlen+3) {
1373                                                 if (pntoh24(tf->saved.d+basic_hlen)==0xffffff) {
1374                                                         tf->len += 4;
1375                                                 }
1376                                         }
1377
1378                                         tf->have += want;
1379                                         tf->lastseq = seq+want-1;
1380                                         remain -= want;
1381                                         offset += want;
1382
1383                                         if (tf->have<tf->len) {
1384                                                 return;
1385                                         }
1386                                 }
1387                         }
1388                 }
1389
1390                 if (!tf) {
1391                         /* No preceeding data, get header data starting at current position */
1392                         id = tvb_get_guint8(tvb, offset);
1393
1394                         if (id==RTMPT_MAGIC && seq+offset==RTMPT_HANDSHAKE_OFFSET_1) {
1395                                 header_type = 4;
1396                                 basic_hlen = 1;
1397                                 message_hlen = 0;
1398                                 id = lastackseq==1 ? RTMPT_TYPE_HANDSHAKE_1 : RTMPT_TYPE_HANDSHAKE_2;
1399                         } else if (seq+offset==RTMPT_HANDSHAKE_OFFSET_2) {
1400                                 header_type = 4;
1401                                 basic_hlen = 0;
1402                                 message_hlen = 0;
1403                                 id = RTMPT_TYPE_HANDSHAKE_3;
1404                         } else {
1405                                 header_type = (id>>6) & 3;
1406                                 basic_hlen = rtmpt_basic_header_length(id);
1407                                 message_hlen = rtmpt_message_header_length(id);
1408
1409                                 if (header_type<3 && remain>=basic_hlen+3) {
1410                                         if (tvb_get_ntoh24(tvb, offset+basic_hlen)==0xffffff) {
1411                                                 message_hlen += 4;
1412                                         }
1413                                 }
1414
1415                                 if (remain<basic_hlen+message_hlen) {
1416                                         /* Ran out of packet mid-header, save and try again next time */
1417                                         tf = se_alloc(sizeof(rtmpt_frag_t));
1418                                         tf->ishdr = 1;
1419                                         tf->seq = seq + offset;
1420                                         tf->lastseq = tf->seq + remain - 1;
1421                                         tf->len = basic_hlen + message_hlen;
1422                                         tvb_memcpy(tvb, tf->saved.d, offset, remain);
1423                                         tf->have = remain;
1424                                         se_tree_insert32(rconv->frags[cdir], seq+offset, tf);
1425                                         return;
1426                                 }
1427
1428                                 id = id & 0x3f;
1429                                 if (id==0) id = tvb_get_guint8(tvb, offset+1) + 64;
1430                                 else if (id==1) id = tvb_get_letohs(tvb, offset+1) + 64;
1431                         }
1432
1433                 } else {
1434                         /* Use reassembled header data */
1435                         id = tf->saved.d[0];
1436                         header_type = (id>>6) & 3;
1437                         basic_hlen = rtmpt_basic_header_length(id);
1438                         message_hlen = tf->len - basic_hlen;
1439
1440                         id = id & 0x3f;
1441                         if (id==0) id = tf->saved.d[1] + 64;
1442                         else if (id==1) id = pletohs(tf->saved.d+1) + 64;
1443                 }
1444
1445                 /* Calculate header values, defaulting from previous packets with same id */
1446
1447                 if (id<=RTMPT_ID_MAX) ti = se_tree_lookup32(rconv->ids[cdir], id);
1448                 if (ti) tp = se_tree_lookup32_le(ti->packets, seq+offset-1);
1449
1450                 if (header_type==0) src = tf ? pntohl(tf->saved.d+basic_hlen+7) : tvb_get_ntohl(tvb, offset+basic_hlen+7);
1451                 else if (ti) src = ti->src;
1452                 else src = 0;
1453
1454                 if (header_type<2) cmd = tf ? tf->saved.d[basic_hlen+6] : tvb_get_guint8(tvb, offset+basic_hlen+6);
1455                 else if (ti) cmd = ti->cmd;
1456                 else cmd = 0;
1457
1458                 /* Calculate chunk_size now as a last-resort default payload length */
1459                 if (id>RTMPT_ID_MAX) {
1460                         if (id==RTMPT_TYPE_HANDSHAKE_1) chunk_size = body_len = 1536;
1461                         else if (id==RTMPT_TYPE_HANDSHAKE_2) chunk_size = body_len = 3072;
1462                         else /* if (id==RTMPT_TYPE_HANDSHAKE_3) */ chunk_size = body_len = 1536;
1463                 } else {
1464                         chunk_size = GPOINTER_TO_INT(se_tree_lookup32_le(rconv->chunksize[cdir], seq+offset-1));
1465                         if (!chunk_size) chunk_size = RTMPT_DEFAULT_CHUNK_SIZE;
1466
1467                         if (header_type<2) body_len = tf ? pntoh24(tf->saved.d+basic_hlen+3) : tvb_get_ntoh24(tvb, offset+basic_hlen+3);
1468                         else if (ti) body_len = ti->len;
1469                         else body_len = chunk_size;
1470
1471                         if (body_len>RTMPT_MAX_PACKET_SIZE) {
1472                                 return;
1473                         }
1474                 }
1475
1476                 if (!ti || !tp || header_type<3 || tp->have==tp->want || tp->chunkhave!=tp->chunkwant) {
1477                         /* Start a new packet if:
1478                          *   no previous packet with same id
1479                          *   not a short 1-byte header
1480                          *   previous packet with same id was complete
1481                          *   previous incomplete chunk not handled by fragment handler
1482                          */
1483                         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",
1484                                                 cdir, seq+offset,
1485                                                 ti, tp, header_type, basic_hlen+message_hlen, id, tp?tp->have:0, tp?tp->want:0, body_len, chunk_size);
1486
1487                         if (!ti) {
1488                                 ti = se_alloc(sizeof(rtmpt_id_t));
1489                                 ti->packets = se_tree_create_non_persistent(EMEM_TREE_TYPE_RED_BLACK, "rtmpt_packets");
1490                                 ti->ts = 0;
1491                                 ti->tsd = 0;
1492                                 se_tree_insert32(rconv->ids[cdir], id, ti);
1493                         }
1494
1495                         if (header_type==0) {
1496                                 ts = tf ? pntoh24(tf->saved.d+basic_hlen) : tvb_get_ntoh24(tvb, offset+basic_hlen);
1497                                 if (ts==0xffffff) {
1498                                         ts = tf ? pntohl(tf->saved.d+basic_hlen+11) : tvb_get_ntohl(tvb, offset+basic_hlen+11);
1499                                 }
1500                                 tsd = ts - ti->ts;
1501                         } else if (header_type<3) {
1502                                 tsd = tf ? pntoh24(tf->saved.d+basic_hlen) : tvb_get_ntoh24(tvb, offset+basic_hlen);
1503                                 if (tsd==0xffffff) {
1504                                         ts = tf ? pntohl(tf->saved.d+basic_hlen+message_hlen-4) : tvb_get_ntohl(tvb, offset+basic_hlen+message_hlen-4);
1505                                         tsd = ti->tsd; /* questionable */
1506                                 } else {
1507                                         ts = ti->ts + tsd;
1508                                 }
1509                         } else {
1510                                 ts = ti->ts + ti->tsd;
1511                                 tsd = ti->tsd;
1512                         }
1513
1514                         /* create a new packet structure */
1515                         tp = se_alloc(sizeof(rtmpt_packet_t));
1516                         tp->seq = tp->lastseq = tf ? tf->seq : seq+offset;
1517                         tp->have = 0;
1518                         tp->want = basic_hlen + message_hlen + body_len;
1519                         tp->chunkwant = 0;
1520                         tp->chunkhave = 0;
1521                         tp->bhlen = basic_hlen;
1522                         tp->mhlen = message_hlen;
1523                         tp->fmt = header_type;
1524                         tp->id = id;
1525                         tp->ts = ts;
1526                         tp->len = body_len;
1527                         if (id>RTMPT_ID_MAX) tp->cmd = id;
1528                         else tp->cmd = cmd & 0x7f;
1529                         tp->src = src;
1530                         tp->txid = 0;
1531                         tp->isresponse = FALSE;
1532                         tp->otherframe = 0;
1533
1534                         /* Save the header information for future defaulting needs */
1535                         ti->ts = ts;
1536                         ti->tsd = tsd;
1537                         ti->len = body_len;
1538                         ti->cmd = cmd;
1539                         ti->src = src;
1540
1541                         /* store against the id only until unchunking is complete */
1542                         se_tree_insert32(ti->packets, tp->seq, tp);
1543
1544                         if (!tf && body_len<=chunk_size && tp->want<=remain) {
1545                                 /* The easy case - a whole packet contiguous and fully within this segment */
1546                                 tp->resident = FALSE;
1547                                 tp->data.offset = offset;
1548                                 tp->lastseq = seq+offset+tp->want-1;
1549                                 tp->have = tp->want;
1550
1551                                 se_tree_insert32(rconv->packets[cdir], tp->lastseq, tp);
1552
1553                                 pktbuf = tvb_new_subset(tvb, tp->data.offset, tp->have, tp->have);
1554                                 dissect_rtmpt(pktbuf, pinfo, tree, rconv, cdir, tp);
1555
1556                                 offset += tp->want;
1557                                 remain -= tp->want;
1558                                 continue;
1559
1560                         } else {
1561                                 /* Some more reassembly required */
1562                                 tp->resident = TRUE;
1563                                 tp->data.p = se_alloc(tp->bhlen+tp->mhlen+tp->len);
1564
1565                                 if (tf && tf->ishdr) {
1566                                         memcpy(tp->data.p, tf->saved.d, tf->len);
1567                                 } else {
1568                                         tvb_memcpy(tvb, tp->data.p, offset, basic_hlen+message_hlen);
1569                                         offset += basic_hlen + message_hlen;
1570                                         remain -= basic_hlen + message_hlen;
1571                                 }
1572
1573                                 tp->lastseq = seq+offset-1;
1574                                 tp->have = basic_hlen + message_hlen;
1575
1576                                 if (tp->have==tp->want) {
1577                                         se_tree_insert32(rconv->packets[cdir], tp->lastseq, tp);
1578
1579                                         pktbuf = tvb_new_real_data(tp->data.p, tp->have, tp->have);
1580                                         add_new_data_source(pinfo, pktbuf, "Unchunked RTMP");
1581                                         dissect_rtmpt(pktbuf, pinfo, tree, rconv, cdir, tp);
1582                                         continue;
1583                                 }
1584
1585                                 tp->chunkwant = chunk_size;
1586                                 if (tp->chunkwant>tp->want-tp->have) tp->chunkwant = tp->want - tp->have;
1587                         }
1588                 } else {
1589                         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",
1590                                                 cdir, seq+offset,
1591                                                 ti, tp, basic_hlen+message_hlen, id, tp?tp->have:0, tp?tp->want:0, body_len, chunk_size);
1592
1593                         tp->chunkwant = chunk_size;
1594                         if (tp->chunkwant>tp->want-tp->have) tp->chunkwant = tp->want - tp->have;
1595
1596                         offset += basic_hlen + message_hlen;
1597                         remain -= basic_hlen + message_hlen;
1598                 }
1599
1600                 tf = NULL;
1601
1602                 /* Last case to deal with is unchunking the packet body */
1603 unchunk:
1604                 want = tp->chunkwant - tp->chunkhave;
1605                 if (want > remain) want = remain;
1606                 RTMPT_DEBUG("  cw=%d ch=%d r=%d w=%d\n", tp->chunkwant, tp->chunkhave, remain, want);
1607
1608                 tvb_memcpy(tvb, tp->data.p+tp->have, offset, want);
1609
1610                 if (tf) {
1611                         tf->have += want;
1612                         tf->lastseq = seq+offset+want-1;
1613                 }
1614                 tp->lastseq = seq+offset+want-1;
1615                 tp->have += want;
1616                 tp->chunkhave += want;
1617
1618                 offset += want;
1619                 remain -= want;
1620
1621                 if (tp->chunkhave==tp->chunkwant) {
1622                         /* Chunk is complete - wait for next header */
1623                         tp->chunkhave = 0;
1624                         tp->chunkwant = 0;
1625                 }
1626
1627                 if (tp->have==tp->want) {
1628                         /* Whole packet is complete */
1629                         se_tree_insert32(rconv->packets[cdir], tp->lastseq, tp);
1630
1631                         pktbuf = tvb_new_real_data(tp->data.p, tp->have, tp->have);
1632                         add_new_data_source(pinfo, pktbuf, "Unchunked RTMP");
1633                         dissect_rtmpt(pktbuf, pinfo, tree, rconv, cdir, tp);
1634                 } else if (tp->chunkhave<tp->chunkwant) {
1635                         /* Chunk is split across segment boundary */
1636                         rtmpt_frag_t *tf2 = se_alloc(sizeof(rtmpt_frag_t));
1637                         tf2->ishdr = 0;
1638                         tf2->seq = seq + offset - want;
1639                         tf2->lastseq = tf2->seq + remain - 1 + want;
1640                         tf2->have = tp->chunkhave;
1641                         tf2->len = tp->chunkwant;
1642                         tf2->saved.id = tp->id;
1643                         RTMPT_DEBUG("  inserting tf @ %d\n", seq+offset-want-1);
1644                         se_tree_insert32(rconv->frags[cdir], seq+offset-want-1, tf2);
1645                 }
1646         }
1647 }
1648
1649 static rtmpt_conv_t*
1650 rtmpt_init_rconv(conversation_t *conv)
1651 {
1652         rtmpt_conv_t *rconv = se_alloc(sizeof(rtmpt_conv_t));
1653         conversation_add_proto_data(conv, proto_rtmpt, rconv);
1654
1655         rconv->seqs[0] = se_tree_create_non_persistent(EMEM_TREE_TYPE_RED_BLACK, "rtmpt_seqs0");
1656         rconv->seqs[1] = se_tree_create_non_persistent(EMEM_TREE_TYPE_RED_BLACK, "rtmpt_seqs1");
1657         rconv->frags[0] = se_tree_create_non_persistent(EMEM_TREE_TYPE_RED_BLACK, "rtmpt_frags0");
1658         rconv->frags[1] = se_tree_create_non_persistent(EMEM_TREE_TYPE_RED_BLACK, "rtmpt_frags1");
1659         rconv->ids[0] = se_tree_create_non_persistent(EMEM_TREE_TYPE_RED_BLACK, "rtmpt_ids0");
1660         rconv->ids[1] = se_tree_create_non_persistent(EMEM_TREE_TYPE_RED_BLACK, "rtmpt_ids1");
1661         rconv->packets[0] = se_tree_create_non_persistent(EMEM_TREE_TYPE_RED_BLACK, "rtmpt_packets0");
1662         rconv->packets[1] = se_tree_create_non_persistent(EMEM_TREE_TYPE_RED_BLACK, "rtmpt_packets1");
1663         rconv->chunksize[0] = se_tree_create_non_persistent(EMEM_TREE_TYPE_RED_BLACK, "rtmpt_chunksize0");
1664         rconv->chunksize[1] = se_tree_create_non_persistent(EMEM_TREE_TYPE_RED_BLACK, "rtmpt_chunksize1");
1665         rconv->txids[0] = se_tree_create_non_persistent(EMEM_TREE_TYPE_RED_BLACK, "rtmpt_txids0");
1666         rconv->txids[1] = se_tree_create_non_persistent(EMEM_TREE_TYPE_RED_BLACK, "rtmpt_txids1");
1667
1668         return rconv;
1669 }
1670
1671 static void
1672 dissect_rtmpt_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
1673 {
1674         conversation_t *conv;
1675         rtmpt_conv_t *rconv;
1676         int cdir;
1677         struct tcpinfo *tcpinfo;
1678
1679         conv = find_conversation(pinfo->fd->num, &pinfo->src, &pinfo->dst, pinfo->ptype, pinfo->srcport, pinfo->destport, 0);
1680         if (!conv) {
1681                 conv = conversation_new(pinfo->fd->num, &pinfo->src, &pinfo->dst, pinfo->ptype, pinfo->srcport, pinfo->destport, 0);
1682         }
1683
1684         rconv = (rtmpt_conv_t*)conversation_get_proto_data(conv, proto_rtmpt);
1685         if (!rconv) {
1686                 rconv = rtmpt_init_rconv(conv);
1687         }
1688
1689         cdir = (ADDRESSES_EQUAL(&conv->key_ptr->addr1, &pinfo->src) &&
1690                 ADDRESSES_EQUAL(&conv->key_ptr->addr2, &pinfo->dst) &&
1691                 conv->key_ptr->port1==pinfo->srcport &&
1692                 conv->key_ptr->port2==pinfo->destport) ? 0 : 1;
1693
1694         tcpinfo = pinfo->private_data;
1695         dissect_rtmpt_common(tvb, pinfo, tree, rconv, cdir, tcpinfo->seq, tcpinfo->lastackseq);
1696 }
1697
1698 static void
1699 dissect_rtmpt_http(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
1700 {
1701         conversation_t *conv;
1702         rtmpt_conv_t *rconv;
1703         int cdir;
1704         guint32 seq;
1705         guint32 lastackseq;
1706         guint32 offset;
1707         gint remain;
1708
1709         offset = 0;
1710         remain = tvb_length_remaining(tvb, 0);
1711
1712         /*
1713          * Request flow:
1714          *
1715          *  POST /open/1
1716          *    request body is a single non-RTMP byte
1717          *    response contains a client ID <cid> followed by NL
1718          *  POST /send/<cid>/<seq>
1719          *    <seq> starts at 0 after open and increments on each
1720          *    subsequent post
1721          *    request body is pure RTMP data
1722          *    response is a single non-RTMP byte followed by RTMP data
1723          *  POST /idle/<cid>/<seq>
1724          *    request contains a single non-RTMP byte
1725          *    response is a single non-RTMP byte followed by RTMP data
1726          *  POST /close/<cid>/<seq>
1727          *    request and response contain a single non-RTMP byte
1728          *
1729          * Ideally here we'd know:
1730          *
1731          *  1) Whether this is was a HTTP request or response
1732          *     (this gives us cdir directly)
1733          *  2) The requested URL (for both cases)
1734          *     (this tells us the type of framing bytes present,
1735          *     so whether there are any real bytes present). We
1736          *     could also use the client ID to identify the
1737          *     conversation, since each POST is likely to be on
1738          *     a different TCP connection, and there could be
1739          *     multiple simultaneous sessions from a single
1740          *     client (which we don't deal with here.)
1741          *
1742          *  As it is, we currently have to just guess, and are
1743          *  likely easily confused.
1744          */
1745
1746         cdir = pinfo->srcport==pinfo->match_uint;
1747
1748         if (cdir) {
1749                 conv = find_conversation(pinfo->fd->num, &pinfo->dst, &pinfo->src, pinfo->ptype, 0, pinfo->srcport, 0);
1750                 if (!conv) {
1751                         RTMPT_DEBUG("RTMPT new conversation\n");
1752                         conv = conversation_new(pinfo->fd->num, &pinfo->dst, &pinfo->src, pinfo->ptype, 0, pinfo->srcport, 0);
1753                 }
1754         } else {
1755                 conv = find_conversation(pinfo->fd->num, &pinfo->src, &pinfo->dst, pinfo->ptype, 0, pinfo->destport, 0);
1756                 if (!conv) {
1757                         RTMPT_DEBUG("RTMPT new conversation\n");
1758                         conv = conversation_new(pinfo->fd->num, &pinfo->src, &pinfo->dst, pinfo->ptype, 0, pinfo->destport, 0);
1759                 }
1760         }
1761
1762         rconv = (rtmpt_conv_t*)conversation_get_proto_data(conv, proto_rtmpt);
1763         if (!rconv) {
1764                 rconv = rtmpt_init_rconv(conv);
1765         }
1766
1767         /* Work out a TCP-like sequence numbers for the tunneled data stream.
1768          * If we've seen the packet before we'll have stored the seq of our
1769          * last byte against the frame number - since we know how big we are
1770          * we can work out the seq of our first byte. If this is the first
1771          * time, we use the stored seq of the last byte of the previous frame
1772          * plus one. If there is no previous frame then we must be at seq=1!
1773          * (This is per-conversation and per-direction, of course.) */
1774
1775         lastackseq = GPOINTER_TO_INT(se_tree_lookup32_le(rconv->seqs[cdir ^ 1], pinfo->fd->num))+1;
1776
1777         if (cdir==1 && lastackseq<2 && remain==17) {
1778                 /* Session startup: the client makes an /open/ request and
1779                  * the server responds with a 16 bytes client
1780                  * identifier followed by a newline */
1781                 offset += 17;
1782                 remain -= 17;
1783         } else if (cdir || remain==1) {
1784                 /* All other server responses start with one byte which
1785                  * is not part of the RTMP stream. Client /idle/ requests
1786                  * contain a single byte also not part of the stream. We
1787                  * must discard these */
1788                 offset++;
1789                 remain--;
1790         }
1791
1792         seq = GPOINTER_TO_INT(se_tree_lookup32(rconv->seqs[cdir], pinfo->fd->num));
1793
1794         if (seq==0) {
1795                 seq = GPOINTER_TO_INT(se_tree_lookup32_le(rconv->seqs[cdir], pinfo->fd->num));
1796                 seq += remain;
1797                 se_tree_insert32(rconv->seqs[cdir], pinfo->fd->num, GINT_TO_POINTER(seq));
1798         }
1799
1800         seq -= remain-1;
1801
1802         RTMPT_DEBUG("RTMPT f=%d cdir=%d seq=%d lastackseq=%d len=%d\n", pinfo->fd->num, cdir, seq, lastackseq, remain);
1803
1804         if (remain<1) return;
1805
1806         if (offset>0) {
1807                 tvbuff_t *tvbrtmp = tvb_new_subset(tvb, offset, remain, remain);
1808                 dissect_rtmpt_common(tvbrtmp, pinfo, tree, rconv, cdir, seq, lastackseq);
1809         } else {
1810                 dissect_rtmpt_common(tvb, pinfo, tree, rconv, cdir, seq, lastackseq);
1811         }
1812 }
1813
1814 #if 0
1815 static gboolean
1816 dissect_rtmpt_heur(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
1817 {
1818         conversation_t * conversation;
1819         if (tvb_length(tvb) >= 12)
1820         {
1821                 /* To avoid a too high rate of false positive, this heurisitics only matches the protocol
1822                    from the first server response packet and not from the client request packets before.
1823                    Therefore it is necessary to a "Decode as" to properly decode the first packets */
1824                 struct tcpinfo *tcpinfo = pinfo->private_data;
1825                 if (tcpinfo->lastackseq == RTMPT_HANDSHAKE_OFFSET_2 && tcpinfo->seq == RTMPT_HANDSHAKE_OFFSET_1 && tvb_get_guint8(tvb, 0) == RTMPT_MAGIC)
1826                 {
1827                         /* Register this dissector for this conversation */
1828                         conversation = NULL;
1829                         conversation = find_conversation(pinfo->fd->num, &pinfo->src, &pinfo->dst, pinfo->ptype, pinfo->srcport, pinfo->destport, 0);
1830                         if (conversation == NULL)
1831                         {
1832                                 conversation = conversation_new(pinfo->fd->num, &pinfo->src, &pinfo->dst, pinfo->ptype, pinfo->srcport, pinfo->destport, 0);
1833                         }
1834                         conversation_set_dissector(conversation, rtmpt_tcp_handle);
1835
1836                         /* Dissect the packet */
1837                         dissect_rtmpt_tcp(tvb, pinfo, tree);
1838                         return TRUE;
1839                 }
1840         }
1841         return FALSE;
1842 }
1843 #endif
1844
1845 void
1846 proto_register_rtmpt(void)
1847 {
1848   static hf_register_info hf[] = {
1849 /* RTMP Handshake data */
1850     { &hf_rtmpt_handshake_c0,
1851       { "Protocol version", "rtmpt.handshake.c0", FT_BYTES, BASE_NONE, NULL, 0x0, "RTMPT Handshake C0", HFILL }},
1852
1853     { &hf_rtmpt_handshake_s0,
1854       { "Protocol version", "rtmpt.handshake.s0", FT_BYTES, BASE_NONE, NULL, 0x0, "RTMPT Handshake S0", HFILL }},
1855
1856     { &hf_rtmpt_handshake_c1,
1857       { "Handshake data", "rtmpt.handshake.c1", FT_BYTES, BASE_NONE, NULL, 0x0, "RTMPT Handshake C1", HFILL }},
1858
1859     { &hf_rtmpt_handshake_s1,
1860       { "Handshake data", "rtmpt.handshake.s1", FT_BYTES, BASE_NONE, NULL, 0x0, "RTMPT Handshake S1", HFILL }},
1861
1862     { &hf_rtmpt_handshake_c2,
1863       { "Handshake data", "rtmpt.handshake.c2", FT_BYTES, BASE_NONE, NULL, 0x0, "RTMPT Handshake C2", HFILL }},
1864
1865     { &hf_rtmpt_handshake_s2,
1866       { "Handshake data", "rtmpt.handshake.s2", FT_BYTES, BASE_NONE, NULL, 0x0, "RTMPT Handshake S2", HFILL }},
1867
1868 /* RTMP chunk/packet header */
1869     { &hf_rtmpt_header_format,
1870       { "Format", "rtmpt.header.format", FT_UINT8, BASE_DEC, NULL, 0xC0, "RTMPT Basic Header format", HFILL }},
1871
1872     { &hf_rtmpt_header_csid,
1873      { "Chunk Stream ID", "rtmpt.header.csid", FT_UINT8, BASE_DEC, NULL, 0x3F, "RTMPT Basic Header chunk stream ID", HFILL }},
1874
1875     { &hf_rtmpt_header_timestamp,
1876       { "Timestamp", "rtmpt.header.timestamp", FT_UINT24, BASE_DEC, NULL, 0x0, "RTMPT Message Header timestamp", HFILL }},
1877
1878     { &hf_rtmpt_header_timestamp_delta,
1879       { "Timestamp delta", "rtmpt.header.timestampdelta", FT_UINT24, BASE_DEC, NULL, 0x0, "RTMPT Message Header timestamp delta", HFILL }},
1880
1881     { &hf_rtmpt_header_body_size,
1882       { "Body size", "rtmpt.header.bodysize", FT_UINT24, BASE_DEC, NULL, 0x0, "RTMPT Message Header body size", HFILL }},
1883
1884     { &hf_rtmpt_header_typeid,
1885       { "Type ID", "rtmpt.header.typeid", FT_UINT8, BASE_HEX, VALS(rtmpt_opcode_vals), 0x0, "RTMPT Message Header type ID", HFILL }},
1886
1887     { &hf_rtmpt_header_streamid,
1888       { "Stream ID", "rtmpt.header.streamid", FT_UINT32, BASE_DEC, NULL, 0x0, "RTMPT Header stream ID", HFILL }},
1889
1890     { &hf_rtmpt_header_ets,
1891       { "Extended timestamp", "rtmpt.header.ets", FT_UINT24, BASE_DEC, NULL, 0x0, "RTMPT Message Header extended timestamp", HFILL }},
1892
1893 /* Stream Control Messages */
1894
1895     { &hf_rtmpt_scm_chunksize,
1896       { "Chunk size", "rtmpt.scm.chunksize", FT_UINT32, BASE_DEC, NULL, 0x0, "RTMPT SCM chunk size", HFILL }},
1897
1898     { &hf_rtmpt_scm_csid,
1899       { "Chunk stream ID", "rtmpt.scm.csid", FT_UINT32, BASE_DEC, NULL, 0x0, "RTMPT SCM chunk stream ID", HFILL }},
1900
1901     { &hf_rtmpt_scm_seq,
1902       { "Sequence number", "rtmpt.scm.seq", FT_UINT32, BASE_DEC, NULL, 0x0, "RTMPT SCM acknowledgement sequence number", HFILL }},
1903
1904     { &hf_rtmpt_scm_was,
1905       { "Window acknowledgement size", "rtmpt.scm.seq", FT_UINT32, BASE_DEC, NULL, 0x0, "RTMPT SCM window acknowledgement size", HFILL }},
1906
1907     { &hf_rtmpt_scm_limittype,
1908       { "Limit type", "rtmpt.scm.limittype", FT_UINT8, BASE_DEC, VALS(rtmpt_limit_vals), 0x0, "RTMPT SCM window acknowledgement size", HFILL }},
1909
1910 /* User Control Messages */
1911     { &hf_rtmpt_ucm_eventtype,
1912       { "Event type", "rtmpt.ucm.eventtype", FT_UINT16, BASE_DEC, VALS(rtmpt_ucm_vals), 0x0, "RTMPT UCM event type", HFILL }},
1913
1914 /* AMF basic types */
1915     { &hf_rtmpt_amf_type,
1916       { "AMF type", "rtmpt.amf.type", FT_UINT8, BASE_DEC, VALS(rtmpt_type_vals), 0x0, "RTMPT AMF type", HFILL }},
1917
1918     { &hf_rtmpt_amf_number,
1919       { "Number", "rtmpt.amf.number", FT_DOUBLE, BASE_NONE, NULL, 0x0, "RTMPT AMF number", HFILL }},
1920
1921     { &hf_rtmpt_amf_boolean,
1922       { "Boolean", "rtmpt.amf.boolean", FT_BOOLEAN, BASE_DEC, NULL, 0x0, "RTMPT AMF boolean", HFILL }},
1923
1924     { &hf_rtmpt_amf_stringlength,
1925       { "String length", "rtmpt.amf.longstringlength", FT_UINT16, BASE_DEC, NULL, 0x0, "RTMPT AMF string length", HFILL }},
1926
1927     { &hf_rtmpt_amf_string,
1928       { "String", "rtmpt.amf.string", FT_STRINGZ, BASE_NONE, NULL, 0x0, "RTMPT AMF string", HFILL }},
1929
1930     { &hf_rtmpt_amf_reference,
1931       { "Reference", "rtmpt.amf.reference", FT_UINT16, BASE_DEC, NULL, 0x0, "RTMPT AMF object reference", HFILL }},
1932
1933     { &hf_rtmpt_amf_date,
1934       { "Date", "rtmpt.amf.date", FT_BYTES, BASE_NONE, NULL, 0x0, "RTMPT AMF date", HFILL }},
1935
1936     { &hf_rtmpt_amf_longstringlength,
1937       { "String length", "rtmpt.amf.longstringlength", FT_UINT32, BASE_DEC, NULL, 0x0, "RTMPT AMF long string length", HFILL }},
1938
1939     { &hf_rtmpt_amf_longstring,
1940       { "Long string", "rtmpt.amf.longstring", FT_STRINGZ, BASE_NONE, NULL, 0x0, "RTMPT AMF long string", HFILL }},
1941
1942     { &hf_rtmpt_amf_xml,
1943       { "XML document", "rtmpt.amf.xml", FT_STRINGZ, BASE_NONE, NULL, 0x0, "RTMPT AMF XML document", HFILL }},
1944
1945     { &hf_rtmpt_amf_int64,
1946       { "Int64", "rtmpt.amf.int64", FT_INT64, BASE_DEC, NULL, 0x0, "RTMPT AMF int64", HFILL }},
1947
1948 /* AMF object types */
1949     { &hf_rtmpt_amf_object,
1950       { "Object", "rtmpt.amf.object", FT_NONE, BASE_NONE, NULL, 0x0, "RTMPT AMF object", HFILL }},
1951
1952     { &hf_rtmpt_amf_ecmaarray,
1953       { "ECMA array", "rtmpt.amf.ecmaarray", FT_NONE, BASE_NONE, NULL, 0x0, "RTMPT AMF ECMA array", HFILL }},
1954
1955     { &hf_rtmpt_amf_strictarray,
1956       { "Strict array", "rtmpt.amf.strictarray", FT_NONE, BASE_NONE, NULL, 0x0, "RTMPT AMF strict array", HFILL }},
1957
1958     { &hf_rtmpt_amf_arraylength,
1959       { "Array length", "rtmpt.amf.arraylength", FT_UINT32, BASE_DEC, NULL, 0x0, "RTMPT AMF array length", HFILL }},
1960
1961 /* Frame links */
1962
1963     { &hf_rtmpt_function_call,
1964       { "Response to this call in frame", "rtmpt.function.call", FT_FRAMENUM, BASE_NONE, NULL, 0x0, "RTMPT function call", HFILL }},
1965
1966     { &hf_rtmpt_function_response,
1967       { "Call for this response in frame", "rtmpt.function.response", FT_FRAMENUM, BASE_NONE, NULL, 0x0, "RTMPT function response", HFILL }},
1968
1969 /* Audio packets */
1970     { &hf_rtmpt_audio_control,
1971       { "Audio control", "rtmpt.audio.control", FT_UINT8, BASE_HEX, NULL, 0x0, "RTMPT Audio control", HFILL }},
1972
1973     { &hf_rtmpt_audio_format,
1974       { "Format", "rtmpt.audio.format", FT_UINT8, BASE_DEC, VALS(rtmpt_audio_codecs), 0xf0, "RTMPT Audio format", HFILL }},
1975
1976     { &hf_rtmpt_audio_rate,
1977       { "Sample rate", "rtmpt.audio.rate", FT_UINT8, BASE_DEC, VALS(rtmpt_audio_rates), 0x0c, "RTMPT Audio sample rate", HFILL }},
1978
1979     { &hf_rtmpt_audio_size,
1980       { "Sample size", "rtmpt.audio.size", FT_UINT8, BASE_DEC, VALS(rtmpt_audio_sizes), 0x02, "RTMPT Audio sample size", HFILL }},
1981
1982     { &hf_rtmpt_audio_type,
1983       { "Channels", "rtmpt.audio.type", FT_UINT8, BASE_DEC, VALS(rtmpt_audio_types), 0x01, "RTMPT Audio channel count", HFILL }},
1984
1985     { &hf_rtmpt_audio_data,
1986       { "Audio data", "rtmpt.audio.data", FT_BYTES, BASE_NONE, NULL, 0x0, "RTMPT Audio data", HFILL }},
1987
1988 /* Video packets */
1989     { &hf_rtmpt_video_control,
1990       { "Video control", "rtmpt.video.control", FT_UINT8, BASE_HEX, NULL, 0x0, "RTMPT Video control", HFILL }},
1991
1992     { &hf_rtmpt_video_type,
1993       { "Type", "rtmpt.video.type", FT_UINT8, BASE_DEC, VALS(rtmpt_video_types), 0xf0, "RTMPT Video type", HFILL }},
1994
1995     { &hf_rtmpt_video_format,
1996       { "Format", "rtmpt.video.format", FT_UINT8, BASE_DEC, VALS(rtmpt_video_codecs), 0x0f, "RTMPT Video format", HFILL }},
1997
1998     { &hf_rtmpt_video_data,
1999       { "Video data", "rtmpt.video.data", FT_BYTES, BASE_NONE, NULL, 0x0, "RTMPT Video data", HFILL }},
2000
2001 /* Aggregate packets */
2002     { &hf_rtmpt_tag_type,
2003       { "Type", "rtmpt.tag.type", FT_UINT8, BASE_DEC, VALS(rtmpt_tag_vals), 0x0, "RTMPT Aggregate tag type", HFILL }},
2004
2005     { &hf_rtmpt_tag_datasize,
2006       { "Data size", "rtmpt.tag.datasize", FT_UINT24, BASE_DEC, NULL, 0x0, "RTMPT Aggregate tag data size", HFILL }},
2007
2008     { &hf_rtmpt_tag_timestamp,
2009       { "Timestamp", "rtmpt.tag.timestamp", FT_UINT24, BASE_DEC, NULL, 0x0, "RTMPT Aggregate tag timestamp", HFILL }},
2010
2011     { &hf_rtmpt_tag_ets,
2012       { "Timestamp Extended", "rtmpt.tag.ets", FT_UINT8, BASE_DEC, NULL, 0x0, "RTMPT Aggregate tag timestamp extended", HFILL }},
2013
2014     { &hf_rtmpt_tag_streamid,
2015       { "Stream ID", "rtmpt.tag.streamid", FT_UINT24, BASE_DEC, NULL, 0x0, "RTMPT Aggregate tag stream ID", HFILL }},
2016
2017     { &hf_rtmpt_tag_tagsize,
2018       { "Previous tag size", "rtmpt.tag.tagsize", FT_UINT32, BASE_DEC, NULL, 0x0, "RTMPT Aggregate previous tag size", HFILL }}
2019
2020   };
2021   static gint *ett[] = {
2022     &ett_rtmpt,
2023     &ett_rtmpt_handshake,
2024     &ett_rtmpt_header,
2025     &ett_rtmpt_body,
2026     &ett_rtmpt_ucm,
2027     &ett_rtmpt_value,
2028     &ett_rtmpt_property,
2029     &ett_rtmpt_string,
2030     &ett_rtmpt_object,
2031     &ett_rtmpt_mixed_array,
2032     &ett_rtmpt_array,
2033     &ett_rtmpt_audio_control,
2034     &ett_rtmpt_video_control,
2035     &ett_rtmpt_tag,
2036     &ett_rtmpt_tag_data
2037   };
2038
2039   module_t *rtmpt_module;
2040
2041   proto_rtmpt = proto_register_protocol("Real Time Messaging Protocol", "RTMPT", "rtmpt");
2042   proto_register_field_array(proto_rtmpt, hf, array_length(hf));
2043   proto_register_subtree_array(ett, array_length(ett));
2044
2045   rtmpt_module = prefs_register_protocol(proto_rtmpt, NULL);
2046   prefs_register_bool_preference(rtmpt_module, "desegment",
2047     "Reassemble RTMPT messages spanning multiple TCP segments",
2048     "Whether the RTMPT dissector should reassemble messages spanning multiple TCP segments."
2049     " To use this option, you must also enable \"Allow subdissectors to reassemble TCP streams\" in the TCP protocol settings.",
2050     &rtmpt_desegment);
2051 }
2052
2053 void
2054 proto_reg_handoff_rtmpt(void)
2055 {
2056 /*      heur_dissector_add("tcp", dissect_rtmpt_heur, proto_rtmpt); */
2057         rtmpt_tcp_handle = create_dissector_handle(dissect_rtmpt_tcp, proto_rtmpt);
2058 /*      dissector_add_handle("tcp.port", rtmpt_tcp_handle); */
2059         dissector_add_uint("tcp.port", RTMP_PORT, rtmpt_tcp_handle);
2060
2061         rtmpt_http_handle = create_dissector_handle(dissect_rtmpt_http, proto_rtmpt);
2062         dissector_add_string("media_type", "application/x-fcs", rtmpt_http_handle);
2063 }
2064
2065 /*
2066  * Editor modelines  -  http://www.wireshark.org/tools/modelines.html
2067  *
2068  * Local variables:
2069  * c-basic-offset: 8
2070  * tab-width: 8
2071  * indent-tabs-mode: nil
2072  * End:
2073  *
2074  * vi: set shiftwidth=8 tabstop=8 expandtab:
2075  * :indentSize=8:tabSize=8:noTabs=true:
2076  */