aboutsummaryrefslogtreecommitdiffstats
path: root/epan/dissectors/packet-rtmpt.c
diff options
context:
space:
mode:
authorGuy Harris <guy@alum.mit.edu>2012-11-16 21:59:58 +0000
committerGuy Harris <guy@alum.mit.edu>2012-11-16 21:59:58 +0000
commit924bb8c13168e1d818691609dd7d6064a1271265 (patch)
tree01c165854d55942d7cf02ab27ac7640f81718fd0 /epan/dissectors/packet-rtmpt.c
parent015cdcef785cfcf62faf144f02e7b76ea0979204 (diff)
Add support for dissecting AMF messages; register the dissector for them
as handling the application/x-amf media type. Add support for dissecting AMF3. Dissect AMF0 dates as milliseconds-since-the-Epoch. Dissect AMF0 typed objects. Add URLs for various Adobe specs for RTMP, AMF0 and AMF3. svn path=/trunk/; revision=46047
Diffstat (limited to 'epan/dissectors/packet-rtmpt.c')
-rw-r--r--epan/dissectors/packet-rtmpt.c1337
1 files changed, 1037 insertions, 300 deletions
diff --git a/epan/dissectors/packet-rtmpt.c b/epan/dissectors/packet-rtmpt.c
index f3aa7bc2d4..10f4b3ca6e 100644
--- a/epan/dissectors/packet-rtmpt.c
+++ b/epan/dissectors/packet-rtmpt.c
@@ -46,6 +46,20 @@
* http://wiki.gnashdev.org/RTMP_Messages_Decoded
* http://www.acmewebworks.com/Downloads/openCS/TheAMF.pdf
* http://www.gnashdev.org/files/rtmp-decoded.pdf
+*
+* It's also available from Adobe at
+*
+* http://wwwimages.adobe.com/www.adobe.com/content/dam/Adobe/en/devnet/rtmp/pdf/rtmp_specification_1.0.pdf
+*
+* For AMF, see:
+*
+* http://download.macromedia.com/pub/labs/amf/amf0_spec_121207.pdf
+*
+* for AMF0 and
+*
+* http://amf3cplusplus.googlecode.com/svn-history/r4/trunk/doc/amf3_spec_05_05_08.pdf
+*
+* for AMF3.
*
* Default TCP port is 1935
*/
@@ -90,22 +104,37 @@ static int hf_rtmpt_scm_limittype = -1;
static int hf_rtmpt_ucm_eventtype = -1;
-static int hf_rtmpt_amf_type = -1;
+static int hf_rtmpt_amf0_type = -1;
+static int hf_rtmpt_amf3_type = -1;
static int hf_rtmpt_amf_number = -1;
+static int hf_rtmpt_amf_integer = -1;
static int hf_rtmpt_amf_boolean = -1;
static int hf_rtmpt_amf_stringlength = -1;
static int hf_rtmpt_amf_string = -1;
-static int hf_rtmpt_amf_reference = -1;
+static int hf_rtmpt_amf_string_reference = -1;
+static int hf_rtmpt_amf_object_reference = -1;
static int hf_rtmpt_amf_date = -1;
static int hf_rtmpt_amf_longstringlength = -1;
static int hf_rtmpt_amf_longstring = -1;
+static int hf_rtmpt_amf_xml_doc = -1;
+static int hf_rtmpt_amf_xmllength = -1;
static int hf_rtmpt_amf_xml = -1;
static int hf_rtmpt_amf_int64 = -1;
+static int hf_rtmpt_amf_bytearraylength = -1;
+static int hf_rtmpt_amf_bytearray = -1;
static int hf_rtmpt_amf_object = -1;
+static int hf_rtmpt_amf_traitcount = -1;
+static int hf_rtmpt_amf_classnamelength = -1;
+static int hf_rtmpt_amf_classname = -1;
+static int hf_rtmpt_amf_membernamelength = -1;
+static int hf_rtmpt_amf_membername = -1;
+static int hf_rtmpt_amf_trait_reference = -1;
static int hf_rtmpt_amf_ecmaarray = -1;
static int hf_rtmpt_amf_strictarray = -1;
+static int hf_rtmpt_amf_array = -1;
static int hf_rtmpt_amf_arraylength = -1;
+static int hf_rtmpt_amf_arraydenselength = -1;
static int hf_rtmpt_function_call = -1;
static int hf_rtmpt_function_response = -1;
@@ -140,6 +169,9 @@ static gint ett_rtmpt_string = -1;
static gint ett_rtmpt_object = -1;
static gint ett_rtmpt_mixed_array = -1;
static gint ett_rtmpt_array = -1;
+static gint ett_rtmpt_array_element = -1;
+static gint ett_rtmpt_traits = -1;
+static gint ett_rtmpt_trait_member = -1;
static gint ett_rtmpt_audio_control = -1;
static gint ett_rtmpt_video_control = -1;
static gint ett_rtmpt_tag = -1;
@@ -150,6 +182,23 @@ static dissector_handle_t rtmpt_http_handle;
static gboolean rtmpt_desegment = TRUE;
+static int proto_amf = -1;
+
+static int hf_amf_version = -1;
+static int hf_amf_header_count = -1;
+static int hf_amf_header_name = -1;
+static int hf_amf_header_must_understand = -1;
+static int hf_amf_header_length = -1;
+static int hf_amf_header_value_type = -1;
+static int hf_amf_message_count = -1;
+static int hf_amf_message_target_uri = -1;
+static int hf_amf_message_response_uri = -1;
+static int hf_amf_message_length = -1;
+
+static gint ett_amf = -1;
+static gint ett_amf_headers = -1;
+static gint ett_amf_messages = -1;
+
#define RTMP_PORT 1935
#define RTMPT_MAGIC 0x03
@@ -198,25 +247,41 @@ static gboolean rtmpt_desegment = TRUE;
#define RTMPT_UCM_PING_REQUEST 0x06
#define RTMPT_UCM_PING_RESPONSE 0x07
-#define RTMPT_AMF_NUMBER 0x00
-#define RTMPT_AMF_BOOLEAN 0x01
-#define RTMPT_AMF_STRING 0x02
-#define RTMPT_AMF_OBJECT 0x03
-#define RTMPT_AMF_MOVIECLIP 0x04
-#define RTMPT_AMF_NULL 0x05
-#define RTMPT_AMF_UNDEFINED 0x06
-#define RTMPT_AMF_REFERENCE 0x07
-#define RTMPT_AMF_ECMA_ARRAY 0x08
-#define RTMPT_AMF_END_OF_OBJECT 0x09
-#define RTMPT_AMF_STRICT_ARRAY 0x0A
-#define RTMPT_AMF_DATE 0x0B
-#define RTMPT_AMF_LONG_STRING 0x0C
-#define RTMPT_AMF_UNSUPPORTED 0x0D
-#define RTMPT_AMF_RECORDSET 0x0E
-#define RTMPT_AMF_XML 0x0F
-#define RTMPT_AMF_TYPED_OBJECT 0x10
-#define RTMPT_AMF_AMF3_MARKER 0x11
-#define RTMPT_AMF_INT64 0x22
+/* AMF0 type markers */
+#define RTMPT_AMF0_NUMBER 0x00
+#define RTMPT_AMF0_BOOLEAN 0x01
+#define RTMPT_AMF0_STRING 0x02
+#define RTMPT_AMF0_OBJECT 0x03
+#define RTMPT_AMF0_MOVIECLIP 0x04
+#define RTMPT_AMF0_NULL 0x05
+#define RTMPT_AMF0_UNDEFINED 0x06
+#define RTMPT_AMF0_REFERENCE 0x07
+#define RTMPT_AMF0_ECMA_ARRAY 0x08
+#define RTMPT_AMF0_END_OF_OBJECT 0x09
+#define RTMPT_AMF0_STRICT_ARRAY 0x0A
+#define RTMPT_AMF0_DATE 0x0B
+#define RTMPT_AMF0_LONG_STRING 0x0C
+#define RTMPT_AMF0_UNSUPPORTED 0x0D
+#define RTMPT_AMF0_RECORDSET 0x0E
+#define RTMPT_AMF0_XML 0x0F
+#define RTMPT_AMF0_TYPED_OBJECT 0x10
+#define RTMPT_AMF0_AMF3_MARKER 0x11
+#define RTMPT_AMF0_INT64 0x22
+
+/* AMF3 type markers */
+#define RTMPT_AMF3_UNDEFINED 0x00
+#define RTMPT_AMF3_NULL 0x01
+#define RTMPT_AMF3_FALSE 0x02
+#define RTMPT_AMF3_TRUE 0x03
+#define RTMPT_AMF3_INTEGER 0x04
+#define RTMPT_AMF3_DOUBLE 0x05
+#define RTMPT_AMF3_STRING 0x06
+#define RTMPT_AMF3_XML_DOC 0x07
+#define RTMPT_AMF3_DATE 0x08
+#define RTMPT_AMF3_ARRAY 0x09
+#define RTMPT_AMF3_OBJECT 0x0A
+#define RTMPT_AMF3_XML 0x0B
+#define RTMPT_AMF3_BYTEARRAY 0x0C
#define RTMPT_TEXT_RTMP_HEADER "RTMP Header"
#define RTMPT_TEXT_RTMP_BODY "RTMP Body"
@@ -267,33 +332,43 @@ static const value_string rtmpt_ucm_vals[] = {
{ 0, NULL }
};
-static const value_string rtmpt_type_vals[] = {
- { RTMPT_AMF_NUMBER, "Number" },
- { RTMPT_AMF_BOOLEAN, "Boolean" },
- { RTMPT_AMF_STRING, "String" },
- { RTMPT_AMF_OBJECT, "Object" },
- { RTMPT_AMF_MOVIECLIP, "Movie clip" },
- { RTMPT_AMF_NULL, "Null" },
- { RTMPT_AMF_UNDEFINED, "Undefined" },
- { RTMPT_AMF_REFERENCE, "Reference" },
- { RTMPT_AMF_ECMA_ARRAY, "ECMA array" },
- { RTMPT_AMF_END_OF_OBJECT, "End of object" },
- { RTMPT_AMF_STRICT_ARRAY, "Strict array" },
- { RTMPT_AMF_DATE, "Date" },
- { RTMPT_AMF_LONG_STRING, "Long string" },
- { RTMPT_AMF_UNSUPPORTED, "Unsupported" },
- { RTMPT_AMF_RECORDSET, "Record set" },
- { RTMPT_AMF_XML, "XML" },
- { RTMPT_AMF_TYPED_OBJECT, "Typed object" },
- { RTMPT_AMF_AMF3_MARKER, "Switch to AMF3" },
- { RTMPT_AMF_INT64, "Int64" },
+static const value_string rtmpt_amf0_type_vals[] = {
+ { RTMPT_AMF0_NUMBER, "Number" },
+ { RTMPT_AMF0_BOOLEAN, "Boolean" },
+ { RTMPT_AMF0_STRING, "String" },
+ { RTMPT_AMF0_OBJECT, "Object" },
+ { RTMPT_AMF0_MOVIECLIP, "Movie clip" },
+ { RTMPT_AMF0_NULL, "Null" },
+ { RTMPT_AMF0_UNDEFINED, "Undefined" },
+ { RTMPT_AMF0_REFERENCE, "Reference" },
+ { RTMPT_AMF0_ECMA_ARRAY, "ECMA array" },
+ { RTMPT_AMF0_END_OF_OBJECT, "End of object" },
+ { RTMPT_AMF0_STRICT_ARRAY, "Strict array" },
+ { RTMPT_AMF0_DATE, "Date" },
+ { RTMPT_AMF0_LONG_STRING, "Long string" },
+ { RTMPT_AMF0_UNSUPPORTED, "Unsupported" },
+ { RTMPT_AMF0_RECORDSET, "Record set" },
+ { RTMPT_AMF0_XML, "XML" },
+ { RTMPT_AMF0_TYPED_OBJECT, "Typed object" },
+ { RTMPT_AMF0_AMF3_MARKER, "Switch to AMF3" },
+ { RTMPT_AMF0_INT64, "Int64" },
{ 0, NULL }
};
-static const value_string rtmpt_object_vals[] = {
- { RTMPT_AMF_OBJECT, "Object" },
- { RTMPT_AMF_ECMA_ARRAY, "ECMA Array" },
- { RTMPT_AMF_STRICT_ARRAY, "Strict Array" },
+static const value_string rtmpt_amf3_type_vals[] = {
+ { RTMPT_AMF3_UNDEFINED, "Undefined" },
+ { RTMPT_AMF3_NULL, "Null" },
+ { RTMPT_AMF3_FALSE, "False" },
+ { RTMPT_AMF3_TRUE, "True" },
+ { RTMPT_AMF3_INTEGER, "Integer" },
+ { RTMPT_AMF3_DOUBLE, "Double" },
+ { RTMPT_AMF3_STRING, "String" },
+ { RTMPT_AMF3_XML_DOC, "XML document" },
+ { RTMPT_AMF3_DATE, "Date" },
+ { RTMPT_AMF3_ARRAY, "Array" },
+ { RTMPT_AMF3_OBJECT, "Object" },
+ { RTMPT_AMF3_XML, "XML" },
+ { RTMPT_AMF3_BYTEARRAY, "ByteArray" },
{ 0, NULL }
};
@@ -495,44 +570,44 @@ rtmpt_get_amf_length(tvbuff_t *tvb, gint offset)
if (remain-rv<1) return remain;
iObjType = tvb_get_guint8(tvb, offset+rv);
- if (depth>0 && itemlen==2 && iObjType==RTMPT_AMF_END_OF_OBJECT) {
+ if (depth>0 && itemlen==2 && iObjType==RTMPT_AMF0_END_OF_OBJECT) {
rv++;
depth--;
continue;
}
switch (iObjType) {
- case RTMPT_AMF_NUMBER:
+ case RTMPT_AMF0_NUMBER:
itemlen = 9;
break;
- case RTMPT_AMF_BOOLEAN:
+ case RTMPT_AMF0_BOOLEAN:
itemlen = 2;
break;
- case RTMPT_AMF_STRING:
+ case RTMPT_AMF0_STRING:
if (remain-rv<3) return remain;
itemlen = tvb_get_ntohs(tvb, offset+rv+1) + 3;
break;
- case RTMPT_AMF_NULL:
- case RTMPT_AMF_UNDEFINED:
- case RTMPT_AMF_UNSUPPORTED:
+ case RTMPT_AMF0_NULL:
+ case RTMPT_AMF0_UNDEFINED:
+ case RTMPT_AMF0_UNSUPPORTED:
itemlen= 1;
break;
- case RTMPT_AMF_DATE:
+ case RTMPT_AMF0_DATE:
itemlen = 11;
break;
- case RTMPT_AMF_LONG_STRING:
- case RTMPT_AMF_XML:
+ case RTMPT_AMF0_LONG_STRING:
+ case RTMPT_AMF0_XML:
if (remain-rv<5) return remain;
itemlen = tvb_get_ntohl(tvb, offset+rv+1) + 5;
break;
- case RTMPT_AMF_INT64:
+ case RTMPT_AMF0_INT64:
itemlen = 9;
break;
- case RTMPT_AMF_OBJECT:
+ case RTMPT_AMF0_OBJECT:
itemlen = 1;
depth++;
break;
- case RTMPT_AMF_ECMA_ARRAY:
+ case RTMPT_AMF0_ECMA_ARRAY:
itemlen = 5;
depth++;
break;
@@ -565,14 +640,14 @@ rtmpt_get_amf_param(tvbuff_t *tvb, gint offset, gint param, const gchar *prop)
if (remain>0 && param==0) {
guint8 iObjType = tvb_get_guint8(tvb, offset);
- if (!prop && iObjType==RTMPT_AMF_STRING && remain>=3) {
+ if (!prop && iObjType==RTMPT_AMF0_STRING && remain>=3) {
iStringLength = tvb_get_ntohs(tvb, offset+1);
if (remain>=iStringLength+3) {
return tvb_get_ephemeral_string(tvb, offset+3, iStringLength);
}
}
- if (prop && iObjType==RTMPT_AMF_OBJECT) {
+ if (prop && iObjType==RTMPT_AMF0_OBJECT) {
offset++;
remain--;
@@ -581,7 +656,7 @@ rtmpt_get_amf_param(tvbuff_t *tvb, gint offset, gint param, const gchar *prop)
if (remain<2+iPropLength+3) break;
if (tvb_strneql(tvb, offset+2, prop, strlen(prop))==0) {
- if (tvb_get_guint8(tvb, offset+2+iPropLength)!=RTMPT_AMF_STRING) break;
+ if (tvb_get_guint8(tvb, offset+2+iPropLength)!=RTMPT_AMF0_STRING) break;
iStringLength = tvb_get_ntohs(tvb, offset+2+iPropLength+1);
if (remain<2+iPropLength+3+iStringLength) break;
@@ -612,7 +687,7 @@ rtmpt_get_amf_txid(tvbuff_t *tvb, gint offset)
}
if (remain>=9) {
guint8 iObjType = tvb_get_guint8(tvb, offset);
- if (iObjType==RTMPT_AMF_NUMBER) {
+ if (iObjType==RTMPT_AMF0_NUMBER) {
return (guint32)tvb_get_ntohieee_double(tvb, offset+1);
}
}
@@ -767,256 +842,727 @@ dissect_rtmpt_body_scm(tvbuff_t *tvb, gint offset, proto_tree *rtmpt_tree, guint
}
}
-static void
-dissect_rtmpt_body_command(tvbuff_t *tvb, gint offset, proto_tree *rtmpt_tree, guint amf3)
+static gint
+dissect_amf0_value_type(tvbuff_t *tvb, gint offset, proto_tree *tree, gboolean *amf3_encoding, proto_item *parent_ti);
+
+/*
+ * A "property list" is a sequence of name/value pairs, terminated by
+ * and "end of object" indicator. AMF0 "object"s and "ECMA array"s
+ * are encoded as property lists.
+ */
+static gint
+dissect_amf0_property_list(tvbuff_t *tvb, gint offset, proto_tree *tree, guint *countp, gboolean *amf3_encoding)
{
- ep_stack_t amftrs;
- ep_stack_t amftis;
- ep_stack_t amfols;
- ep_stack_t amfots;
- ep_stack_t amfpcs;
- int depth = 0;
- int ot = 0;
- int pc = 0;
- proto_item *ti_object = NULL;
- gint iObjectLength = 0;
-
- amftrs = ep_stack_new();
- amftis = ep_stack_new();
- amfols = ep_stack_new();
- amfots = ep_stack_new();
- amfpcs = ep_stack_new();
+ proto_item *prop_ti;
+ proto_tree *prop_tree;
+ proto_item *name_ti;
+ proto_tree *name_tree;
+ guint iStringLength;
+ gchar *iStringValue;
+ guint count = 0;
- if (amf3) {
- /* Looks like for the AMF3 variants we get a 0 byte here,
- * followed by AMF0 encoding - I've never seen actual AMF3
- * encoding used, which is completely different. I speculate
- * that if the byte is RTMPT_AMF_AMF3_MARKER then the rest
- * will be in AMF3. For now, assume AMF0 only. */
- offset++;
+ /*
+ * XXX - at least as I read "3.1 AVM+ Type Marker" in the AMF0
+ * specification, the AVM+ Type Marker only affects "the following
+ * Object". For now, we have a single "AMF3 encoding" flag, and
+ * set it when we see the type marker, and never clear it.
+ */
+ for (;;) {
+ /* UTF-8: property name */
+ iStringLength = tvb_get_ntohs(tvb, offset);
+ if (iStringLength == 0 &&
+ tvb_get_guint8(tvb, offset + 2) == RTMPT_AMF0_END_OF_OBJECT)
+ break;
+ count++;
+ iStringValue = tvb_get_ephemeral_string(tvb, offset + 2, iStringLength);
+ prop_ti = proto_tree_add_text(tree, tvb, offset, -1,
+ "Property '%s'",
+ iStringValue);
+ prop_tree = proto_item_add_subtree(prop_ti, ett_rtmpt_property);
+
+ name_ti = proto_tree_add_text(prop_tree, tvb,
+ offset, 2+iStringLength,
+ "Name: %s", iStringValue);
+ name_tree = proto_item_add_subtree(name_ti, ett_rtmpt_string);
+
+ proto_tree_add_uint(name_tree, hf_rtmpt_amf_stringlength, tvb, offset, 2, iStringLength);
+ offset += 2;
+ proto_tree_add_item(name_tree, hf_rtmpt_amf_string, tvb, offset, iStringLength, ENC_UTF_8|ENC_NA);
+ offset += iStringLength;
+
+ /* value-type: property value */
+ offset = dissect_amf0_value_type(tvb, offset, prop_tree, amf3_encoding, prop_ti);
+ proto_item_set_end(prop_ti, tvb, offset);
}
+ proto_tree_add_text(tree, tvb, offset, 3, "End Of Object Marker");
+ offset += 3;
- while (tvb_length_remaining(tvb, offset) > 0)
- {
- guint8 iObjType = 0;
- gint iPropertyOffset = 0;
- gint iPropertyLength = 0;
- guint iStringLength = 0;
- gint iValueOffset = 0;
- gint iValueLength = 0; /* signed so we can use CLAMP() */
- guint iValueExtra = 0;
- gchar *sValue = "";
- int hfvalue = -1;
- guint encoding = ENC_NA;
- guint iPush = 0;
- proto_tree *rtmpt_tree_prop = NULL;
- proto_item *ti = NULL;
-
- rtmpt_tree_prop = rtmpt_tree;
-
- iValueOffset = offset;
-
- if (depth>0 && !ot) {
- iStringLength = tvb_get_ntohs(tvb, offset);
-
- if (iStringLength==0 && tvb_get_guint8(tvb, offset + 2)==RTMPT_AMF_END_OF_OBJECT)
- {
- iObjType = RTMPT_AMF_END_OF_OBJECT;
- goto popamf;
- }
+ *countp = count;
- iPropertyOffset = offset;
- iPropertyLength = 2 + iStringLength;
- iValueOffset += iPropertyLength;
- }
+ return offset;
+}
- iObjType = tvb_get_guint8(tvb, iValueOffset);
- iValueOffset++;
- iValueExtra = 1;
+static gint
+dissect_amf0_value_type(tvbuff_t *tvb, gint offset, proto_tree *tree, gboolean *amf3_encoding, proto_item *parent_ti)
+{
+ guint8 iObjType;
+ proto_item *ti;
+ proto_tree *val_tree;
+ gint iValueOffset = offset;
+ guint32 iIntegerValue;
+ double iDoubleValue;
+ gboolean iBooleanValue;
+ guint iStringLength;
+ gchar *iStringValue;
+ guint iArrayLength;
+ guint i;
+ nstime_t t;
+ gint64 iInteger64Value;
+ guint count;
+
+ iObjType = tvb_get_guint8(tvb, offset);
+ if (parent_ti != NULL)
+ proto_item_append_text(parent_ti, " %s",
+ val_to_str_const(iObjType, rtmpt_amf0_type_vals, "Unknown"));
+ switch (iObjType) {
+
+ case RTMPT_AMF0_OBJECT:
+ /*
+ * For object types, make the top-level protocol tree
+ * item a field for that type.
+ */
+ ti = proto_tree_add_item(tree, hf_rtmpt_amf_object, tvb, offset, -1, ENC_NA);
+ break;
- switch (iObjType) {
- case RTMPT_AMF_NUMBER:
- iValueLength = 8;
- hfvalue = hf_rtmpt_amf_number;
- encoding = ENC_BIG_ENDIAN;
- sValue = ep_strdup_printf(" %." STRINGIFY(DBL_DIG) "g", tvb_get_ntohieee_double(tvb, iValueOffset));
- break;
- case RTMPT_AMF_BOOLEAN:
- iValueLength = 1;
- hfvalue = hf_rtmpt_amf_boolean;
- encoding = ENC_BIG_ENDIAN;
- sValue = tvb_get_guint8(tvb, iValueOffset) ? " true" : " false";
- break;
- case RTMPT_AMF_STRING:
- iValueLength = tvb_get_ntohs(tvb, iValueOffset);
- iValueOffset += 2;
- iValueExtra = 3;
- hfvalue = hf_rtmpt_amf_string;
- encoding = ENC_ASCII|ENC_NA;
- sValue = ep_strdup_printf(" '%s'", tvb_get_ephemeral_string(tvb, iValueOffset, CLAMP(iValueLength, 0, ITEM_LABEL_LENGTH+1)));
- break;
- case RTMPT_AMF_OBJECT:
- /* Uncounted list type, with end marker */
- iValueLength = 0;
- hfvalue = hf_rtmpt_amf_object;
- encoding = ENC_NA;
- iPush = 1;
- break;
- case RTMPT_AMF_NULL:
- case RTMPT_AMF_UNDEFINED:
- iValueLength = 0;
- break;
- case RTMPT_AMF_REFERENCE:
- iValueLength = 2;
- hfvalue = hf_rtmpt_amf_reference;
- encoding = ENC_BIG_ENDIAN;
- sValue = ep_strdup_printf(" %d", tvb_get_ntohs(tvb, iValueOffset));
- break;
- case RTMPT_AMF_ECMA_ARRAY:
- /* Counted list type, with end marker. The count appears to be
- * more of a hint than a rule, and is sometimes sent as 0 or invalid.
- * Basically the same as OBJECT but with the extra count field.
- * There being many strange encoders/metadata injectors out there,
- * sometimes you see a valid count and no end marker. Figuring out
- * which you've got for a deeply nested structure is non-trivial.
- */
- iValueLength = 0;
- iValueOffset += 4;
- iValueExtra = 5;
- hfvalue = hf_rtmpt_amf_ecmaarray;
- encoding = ENC_NA;
- iPush = 1;
- break;
- case RTMPT_AMF_STRICT_ARRAY:
- /* Counted list type, without end marker. Number of values is determined
- * by count, values are assumed to form a [0..N-1] numbered array and are
- * presented as plain AMF types, not OBJECT or ECMA_ARRAY style named
- * properties */
- iValueLength = 4;
- hfvalue = hf_rtmpt_amf_strictarray;
- encoding = ENC_NA;
- iPush = 1;
- break;
- case RTMPT_AMF_DATE:
- iValueLength = 10;
- hfvalue = hf_rtmpt_amf_date;
- encoding = ENC_NA;
- break;
- case RTMPT_AMF_LONG_STRING:
- case RTMPT_AMF_XML: /* same representation */
- iValueLength = tvb_get_ntohl(tvb, iValueOffset);
- iValueOffset += 4;
- iValueExtra = 5;
- hfvalue = (iObjType==RTMPT_AMF_XML) ? hf_rtmpt_amf_xml : hf_rtmpt_amf_longstring;
- encoding = ENC_ASCII|ENC_NA; /* XXX - code page? */
- sValue = ep_strdup_printf(" '%s'", tvb_get_ephemeral_string(tvb, iValueOffset, CLAMP(iValueLength, 0, ITEM_LABEL_LENGTH+1)));
- break;
- case RTMPT_AMF_UNSUPPORTED:
- iValueLength = 0;
- break;
- case RTMPT_AMF_INT64:
- iValueLength = 8;
- hfvalue = hf_rtmpt_amf_int64;
- encoding = ENC_BIG_ENDIAN;
- sValue = ep_strdup_printf(" %" G_GINT64_MODIFIER "d", tvb_get_ntoh64(tvb, iValueOffset));
- break;
- default:
- /* If we can't determine the length, don't carry on */
- iValueLength = tvb_length_remaining(tvb, iValueOffset);
- sValue = "";
- break;
- }
+ case RTMPT_AMF0_ECMA_ARRAY:
+ /*
+ * For ECMA array types, make the top-level protocol tree
+ * item a field for that type.
+ */
+ ti = proto_tree_add_item(tree, hf_rtmpt_amf_ecmaarray, tvb, offset, -1, ENC_NA);
+ break;
- offset += iPropertyLength + iValueExtra + iValueLength;
- iObjectLength += iPropertyLength + iValueExtra + iValueLength;
- pc++;
+ case RTMPT_AMF0_STRICT_ARRAY:
+ /*
+ * For strict array types, make the top-level protocol tree
+ * item a field for that type.
+ */
+ ti = proto_tree_add_item(tree, hf_rtmpt_amf_strictarray, tvb, offset, -1, ENC_NA);
+ break;
- if (iPropertyLength>0) {
- proto_tree *name_tree = NULL;
- gchar *sProperty = tvb_get_ephemeral_string(tvb, iPropertyOffset+2, iPropertyLength-2);
+ default:
+ /*
+ * For all other types, make it just a text item; the
+ * field for that type will be used for the value.
+ */
+ ti = proto_tree_add_text(tree, tvb, offset, -1, "%s",
+ val_to_str_const(iObjType, rtmpt_amf0_type_vals, "Unknown"));
+ break;
+ }
- ti = proto_tree_add_text(rtmpt_tree, tvb,
- iPropertyOffset,
- iPropertyLength+iValueExtra+iValueLength,
- "Property '%s' %s%s",
- sProperty, val_to_str_const(iObjType, rtmpt_type_vals, "Unknown"), sValue);
- rtmpt_tree_prop = proto_item_add_subtree(ti, ett_rtmpt_property);
+ val_tree = proto_item_add_subtree(ti, ett_rtmpt_value);
+ proto_tree_add_uint(val_tree, hf_rtmpt_amf0_type, tvb, iValueOffset, 1, iObjType);
+ iValueOffset++;
+
+ switch (iObjType) {
+ case RTMPT_AMF0_NUMBER:
+ iDoubleValue = tvb_get_ntohieee_double(tvb, iValueOffset);
+ proto_tree_add_double(val_tree, hf_rtmpt_amf_number, tvb, iValueOffset, 8, iDoubleValue);
+ iValueOffset += 8;
+ proto_item_append_text(ti, " %." STRINGIFY(DBL_DIG) "g", iDoubleValue);
+ if (parent_ti != NULL)
+ proto_item_append_text(parent_ti, " %." STRINGIFY(DBL_DIG) "g", iDoubleValue);
+ break;
+ case RTMPT_AMF0_BOOLEAN:
+ iBooleanValue = tvb_get_guint8(tvb, iValueOffset);
+ proto_tree_add_boolean(val_tree, hf_rtmpt_amf_boolean, tvb, iValueOffset, 1, iBooleanValue);
+ iValueOffset += 1;
+ proto_item_append_text(ti, iBooleanValue ? " true" : " false");
+ if (parent_ti != NULL)
+ proto_item_append_text(parent_ti, iBooleanValue ? " true" : " false");
+ break;
+ case RTMPT_AMF0_STRING:
+ iStringLength = tvb_get_ntohs(tvb, iValueOffset);
+ proto_tree_add_uint(val_tree, hf_rtmpt_amf_stringlength, tvb, iValueOffset, 2, iStringLength);
+ iValueOffset += 2;
+ iStringValue = tvb_get_ephemeral_string_enc(tvb, iValueOffset, iStringLength, ENC_UTF_8|ENC_NA);
+ if (iStringLength != 0)
+ proto_tree_add_string(val_tree, hf_rtmpt_amf_string, tvb, iValueOffset, iStringLength, iStringValue);
+ iValueOffset += iStringLength;
+ proto_item_append_text(ti, " '%s'", iStringValue);
+ if (parent_ti != NULL)
+ proto_item_append_text(parent_ti, " '%s'", iStringValue);
+ break;
+ case RTMPT_AMF0_OBJECT:
+ iValueOffset = dissect_amf0_property_list(tvb, iValueOffset, val_tree, &count, amf3_encoding);
+ proto_item_append_text(ti, " (%u items)", count);
+ break;
+ case RTMPT_AMF0_NULL:
+ case RTMPT_AMF0_UNDEFINED:
+ break;
+ case RTMPT_AMF0_REFERENCE:
+ iIntegerValue = tvb_get_ntohs(tvb, iValueOffset);
+ proto_tree_add_uint(val_tree, hf_rtmpt_amf_object_reference, tvb, iValueOffset, 2, iIntegerValue);
+ iValueOffset += 2;
+ proto_item_append_text(ti, " %d", iIntegerValue);
+ break;
+ case RTMPT_AMF0_ECMA_ARRAY:
+ /*
+ * Counted list type, with end marker. The count appears to be
+ * more of a hint than a rule, and is sometimes sent as 0 or
+ * invalid.
+ *
+ * Basically the same as OBJECT but with the extra count field.
+ * There being many strange encoders/metadata injectors out
+ * there, sometimes you see a valid count and no end marker.
+ * Figuring out which you've got for a deeply nested structure
+ * is non-trivial.
+ */
+ iArrayLength = tvb_get_ntohl(tvb, iValueOffset);
+ proto_tree_add_uint(val_tree, hf_rtmpt_amf_arraylength, tvb, iValueOffset, 4, iArrayLength);
+ iValueOffset += 4;
+ iValueOffset = dissect_amf0_property_list(tvb, iValueOffset, val_tree, &count, amf3_encoding);
+ proto_item_append_text(ti, " (%u items)", count);
+ break;
+ case RTMPT_AMF0_END_OF_OBJECT:
+ proto_tree_add_text(tree, tvb, iValueOffset, 3, "End Of Object Marker");
+ iValueOffset += 3;
+ break;
+ case RTMPT_AMF0_STRICT_ARRAY:
+ /*
+ * Counted list type, without end marker. Number of values
+ * is determined by count, values are assumed to form a
+ * [0..N-1] numbered array and are presented as plain AMF
+ * types, not OBJECT or ECMA_ARRAY style named properties.
+ */
+ iArrayLength = tvb_get_ntohl(tvb, iValueOffset);
+ proto_tree_add_uint(val_tree, hf_rtmpt_amf_arraylength, tvb, iValueOffset, 4, iArrayLength);
+ iValueOffset += 4;
+ for (i = 0; i < iArrayLength; i++)
+ iValueOffset = dissect_amf0_value_type(tvb, iValueOffset, val_tree, amf3_encoding, NULL);
+ proto_item_append_text(ti, " (%u items)", iArrayLength);
+ break;
+ case RTMPT_AMF0_DATE:
+ iDoubleValue = tvb_get_ntohieee_double(tvb, iValueOffset);
+ t.secs = iDoubleValue/1000;
+ t.nsecs = (iDoubleValue - 1000*(double)t.secs) * 1000000;
+ proto_tree_add_time(val_tree, hf_rtmpt_amf_date, tvb, iValueOffset, 8, &t);
+ iValueOffset += 8;
+ proto_item_append_text(ti, " %s", abs_time_to_str(&t, ABSOLUTE_TIME_LOCAL, TRUE));
+ if (parent_ti != NULL)
+ proto_item_append_text(parent_ti, " %s", abs_time_to_str(&t, ABSOLUTE_TIME_LOCAL, TRUE));
+ /* time-zone */
+ iValueOffset += 2;
+ break;
+ case RTMPT_AMF0_LONG_STRING:
+ case RTMPT_AMF0_XML: /* same representation */
+ iStringLength = tvb_get_ntohl(tvb, iValueOffset);
+ proto_tree_add_uint(val_tree, hf_rtmpt_amf_stringlength, tvb, iValueOffset, 2, iStringLength);
+ iValueOffset += 4;
+ iStringValue = tvb_get_ephemeral_string_enc(tvb, iValueOffset, iStringLength, ENC_UTF_8|ENC_NA);
+ if (iStringLength != 0)
+ proto_tree_add_string(val_tree, (iObjType==RTMPT_AMF0_XML) ? hf_rtmpt_amf_xml_doc : hf_rtmpt_amf_longstring, tvb, iValueOffset, iStringLength, iStringValue);
+ iValueOffset += iStringLength;
+ proto_item_append_text(ti, " '%s'", iStringValue);
+ if (parent_ti != NULL)
+ proto_item_append_text(parent_ti, " '%s'", iStringValue);
+ break;
+ case RTMPT_AMF0_UNSUPPORTED:
+ break;
+ case RTMPT_AMF0_TYPED_OBJECT:
+ /* class-name */
+ iStringLength = tvb_get_ntohs(tvb, iValueOffset);
+ proto_tree_add_uint(val_tree, hf_rtmpt_amf_stringlength, tvb, iValueOffset, 2, iStringLength);
+ iValueOffset += 2;
+ iStringValue = tvb_get_ephemeral_string_enc(tvb, iValueOffset, iStringLength, ENC_UTF_8|ENC_NA);
+ proto_tree_add_string(val_tree, hf_rtmpt_amf_string, tvb, iValueOffset, iStringLength, iStringValue);
+ iValueOffset += iStringLength;
+ iValueOffset = dissect_amf0_property_list(tvb, iValueOffset, val_tree, &count, amf3_encoding);
+ break;
+ case RTMPT_AMF0_AMF3_MARKER:
+ *amf3_encoding = TRUE;
+ break;
+ case RTMPT_AMF0_INT64:
+ iInteger64Value = tvb_get_ntoh64(tvb, iValueOffset);
+ proto_tree_add_int64(val_tree, hf_rtmpt_amf_int64, tvb, iValueOffset, 8, iInteger64Value);
+ iValueOffset += 8;
+ proto_item_append_text(ti," %" G_GINT64_MODIFIER "d", iInteger64Value);
+ if (parent_ti != NULL)
+ proto_item_append_text(parent_ti," %" G_GINT64_MODIFIER "d", iInteger64Value);
+ break;
+ default:
+ /*
+ * If we can't determine the length, don't carry on;
+ * just skip to the end of the tvbuff.
+ */
+ iValueOffset = tvb_length(tvb);
+ break;
+ }
+ proto_item_set_end(ti, tvb, iValueOffset);
+ return iValueOffset;
+}
+
+static guint32
+amf_get_u29(tvbuff_t *tvb, int offset, guint *lenp)
+{
+ guint len = 0;
+ guint8 iByte;
+ guint32 iValue;
+
+ iByte = tvb_get_guint8(tvb, offset);
+ iValue = (iByte & 0x7F);
+ offset++;
+ len++;
+ if (!(iByte & 0x80)) {
+ /* 1 byte value */
+ *lenp = len;
+ return iValue;
+ }
+ iByte = tvb_get_guint8(tvb, offset);
+ iValue = (iValue << 7) | (iByte & 0x7F);
+ offset++;
+ len++;
+ if (!(iByte & 0x80)) {
+ /* 2 byte value */
+ *lenp = len;
+ return iValue;
+ }
+ iByte = tvb_get_guint8(tvb, offset);
+ iValue = (iValue << 7) | (iByte & 0x7F);
+ offset++;
+ len++;
+ if (!(iByte & 0x80)) {
+ /* 3 byte value */
+ *lenp = len;
+ return iValue;
+ }
+ iByte = tvb_get_guint8(tvb, offset);
+ iValue = (iValue << 8) | iByte;
+ len++;
+ *lenp = len;
+ return iValue;
+}
- ti = proto_tree_add_text(rtmpt_tree_prop, tvb,
- iPropertyOffset, iPropertyLength,
- "Name: %s", sProperty);
- name_tree = proto_item_add_subtree(ti, ett_rtmpt_string);
+static gint
+dissect_amf3_value_type(tvbuff_t *tvb, gint offset, proto_tree *tree, proto_item *parent_ti)
+{
+ guint8 iObjType;
+ proto_item *ti;
+ proto_tree *val_tree;
+ gint iValueOffset = offset;
+ guint iValueLength;
+ guint32 iIntegerValue;
+ double iDoubleValue;
+ guint iStringLength;
+ gchar *iStringValue;
+ guint iArrayLength;
+ proto_item *subval_ti;
+ proto_tree *subval_tree;
+ guint i;
+ gboolean iTypeIsDynamic;
+ guint iTraitCount;
+ proto_item *traits_ti;
+ proto_tree *traits_tree;
+ proto_item *name_ti;
+ proto_tree *name_tree;
+ proto_item *member_ti;
+ proto_tree *member_tree;
+ guint8 *iByteArrayValue;
+
+ iObjType = tvb_get_guint8(tvb, offset);
+ if (parent_ti != NULL)
+ proto_item_append_text(parent_ti, " %s",
+ val_to_str_const(iObjType, rtmpt_amf3_type_vals, "Unknown"));
+ switch (iObjType) {
+
+ case RTMPT_AMF3_ARRAY:
+ /*
+ * For array types, make the top-level protocol tree
+ * item a field for that type.
+ */
+ ti = proto_tree_add_item(tree, hf_rtmpt_amf_array, tvb, offset, -1, ENC_NA);
+ break;
- proto_tree_add_item(name_tree, hf_rtmpt_amf_stringlength, tvb, iPropertyOffset, 2, ENC_BIG_ENDIAN);
- proto_tree_add_item(name_tree, hf_rtmpt_amf_string, tvb, iPropertyOffset+2, iPropertyLength-2, ENC_ASCII|ENC_NA);
- }
+ case RTMPT_AMF3_OBJECT:
+ /*
+ * For object types, make the top-level protocol tree
+ * item a field for that type.
+ */
+ ti = proto_tree_add_item(tree, hf_rtmpt_amf_object, tvb, offset, -1, ENC_NA);
+ break;
- if (!iPush) {
- proto_tree *val_tree = NULL;
+ default:
+ /*
+ * For all other types, make it just a text item; the
+ * field for that type will be used for the value.
+ */
+ ti = proto_tree_add_text(tree, tvb, offset, -1, "%s",
+ val_to_str_const(iObjType, rtmpt_amf3_type_vals, "Unknown"));
+ break;
+ }
- ti = proto_tree_add_text(rtmpt_tree_prop, tvb,
- iValueOffset-iValueExtra, iValueExtra+iValueLength,
- "%s%s",
- val_to_str_const(iObjType, rtmpt_type_vals, "Unknown"), sValue);
- val_tree = proto_item_add_subtree(ti, ett_rtmpt_value);
+ val_tree = proto_item_add_subtree(ti, ett_rtmpt_value);
+ proto_tree_add_uint(val_tree, hf_rtmpt_amf3_type, tvb, iValueOffset, 1, iObjType);
+ iValueOffset++;
- proto_tree_add_item(val_tree, hf_rtmpt_amf_type, tvb, iValueOffset-iValueExtra, 1, ENC_BIG_ENDIAN);
- if (iObjType==RTMPT_AMF_STRING) {
- proto_tree_add_item(val_tree, hf_rtmpt_amf_stringlength, tvb, iValueOffset-iValueExtra+1, 2, ENC_BIG_ENDIAN);
- } else if (iObjType==RTMPT_AMF_LONG_STRING || iObjType==RTMPT_AMF_XML) {
- proto_tree_add_item(val_tree, hf_rtmpt_amf_longstringlength, tvb, iValueOffset-iValueExtra+1, 4, ENC_BIG_ENDIAN);
- }
- if (iValueLength>0 && hfvalue != -1) {
- proto_tree_add_item(val_tree, hfvalue, tvb, iValueOffset, iValueLength, encoding);
- }
+ switch (iObjType) {
+ case RTMPT_AMF3_UNDEFINED:
+ case RTMPT_AMF3_NULL:
+ break;
+ case RTMPT_AMF3_FALSE:
+ proto_tree_add_boolean(val_tree, hf_rtmpt_amf_boolean, tvb, 0, 0, FALSE);
+ proto_item_append_text(ti, " false");
+ break;
+ case RTMPT_AMF3_TRUE:
+ proto_tree_add_boolean(val_tree, hf_rtmpt_amf_boolean, tvb, 0, 0, TRUE);
+ proto_item_append_text(ti, " true");
+ break;
+ case RTMPT_AMF3_INTEGER:
+ /* XXX - signed or unsigned? */
+ iIntegerValue = amf_get_u29(tvb, iValueOffset, &iValueLength);
+ proto_tree_add_uint(val_tree, hf_rtmpt_amf_integer, tvb, iValueOffset, iValueLength, iIntegerValue);
+ proto_item_append_text(ti, " %u", iIntegerValue);
+ if (parent_ti != NULL)
+ proto_item_append_text(parent_ti, " %u", iIntegerValue);
+ iValueOffset += iValueLength;
+ break;
+ case RTMPT_AMF3_DOUBLE:
+ iDoubleValue = tvb_get_ntohieee_double(tvb, iValueOffset);
+ proto_tree_add_double(val_tree, hf_rtmpt_amf_number, tvb, iValueOffset, 8, iDoubleValue);
+ iValueOffset += 8;
+ proto_item_append_text(ti, " %." STRINGIFY(DBL_DIG) "g", iDoubleValue);
+ if (parent_ti != NULL)
+ proto_item_append_text(parent_ti, " %." STRINGIFY(DBL_DIG) "g", iDoubleValue);
+ break;
+ case RTMPT_AMF3_STRING:
+ iIntegerValue = amf_get_u29(tvb, iValueOffset, &iValueLength);
+ if (iIntegerValue & 0x00000001) {
+ /* the upper 28 bits of the integer value is a string length */
+ iStringLength = iIntegerValue >> 1;
+ proto_tree_add_uint(val_tree, hf_rtmpt_amf_stringlength, tvb, iValueOffset, iValueLength, iStringLength);
+ iValueOffset += iValueLength;
+ iStringValue = tvb_get_ephemeral_string_enc(tvb, iValueOffset, iStringLength, ENC_UTF_8|ENC_NA);
+ if (iStringLength != 0)
+ proto_tree_add_string(val_tree, hf_rtmpt_amf_string, tvb, iValueOffset, iStringLength, iStringValue);
+ iValueOffset += iStringLength;
+ proto_item_append_text(ti, " '%s'", iStringValue);
+ if (parent_ti != NULL)
+ proto_item_append_text(parent_ti, " '%s'", iStringValue);
+ } else {
+ /* the upper 28 bits of the integer value are a string reference index */
+ proto_tree_add_uint(val_tree, hf_rtmpt_amf_string_reference, tvb, iValueOffset, iValueLength, iIntegerValue >> 1);
+ iValueOffset += iValueLength;
+ proto_item_append_text(ti, " reference %u", iIntegerValue >> 1);
+ if (parent_ti != NULL)
+ proto_item_append_text(parent_ti, " reference %u", iIntegerValue >> 1);
}
-
- if (iPush) {
- depth++;
- ep_stack_push(amfols, GINT_TO_POINTER(iObjectLength));
- iObjectLength = iValueExtra;
- ep_stack_push(amfots, GINT_TO_POINTER(ot));
- ot = iValueLength>0 ? tvb_get_ntohl(tvb, iValueOffset)+1 : 0;
- ep_stack_push(amfpcs, GINT_TO_POINTER(pc));
- pc = 0;
- ep_stack_push(amftis, ti_object);
- ti_object = proto_tree_add_item(rtmpt_tree_prop, hfvalue, tvb, iValueOffset+iValueLength, 1, encoding);
- ep_stack_push(amftrs, rtmpt_tree);
- rtmpt_tree = proto_item_add_subtree(ti_object, ett_rtmpt_array);
-
- proto_tree_add_item(rtmpt_tree, hf_rtmpt_amf_type, tvb, iValueOffset-iValueExtra, 1, ENC_BIG_ENDIAN);
- if (iValueExtra>1 || iValueLength>0) {
- proto_tree_add_item(rtmpt_tree, hf_rtmpt_amf_arraylength, tvb, iValueOffset-iValueExtra+1, 4, ENC_BIG_ENDIAN);
- }
+ break;
+ case RTMPT_AMF3_DATE:
+ iIntegerValue = amf_get_u29(tvb, iValueOffset, &iValueLength);
+ if (iIntegerValue & 0x00000001) {
+ /*
+ * The upper 28 bits of the integer value are
+ * ignored; what follows is a double
+ * containing milliseconds since the Epoch.
+ */
+ nstime_t t;
+
+ iValueOffset += iValueLength;
+ iDoubleValue = tvb_get_ntohieee_double(tvb, iValueOffset);
+ t.secs = iDoubleValue/1000;
+ t.nsecs = (iDoubleValue - 1000*(double)t.secs) * 1000000;
+ proto_tree_add_time(val_tree, hf_rtmpt_amf_date, tvb, iValueOffset, 8, &t);
+ iValueOffset += 8;
+ proto_item_append_text(ti, "%s", abs_time_to_str(&t, ABSOLUTE_TIME_LOCAL, TRUE));
+ if (parent_ti != NULL)
+ proto_item_append_text(parent_ti, "%s", abs_time_to_str(&t, ABSOLUTE_TIME_LOCAL, TRUE));
+ } else {
+ /* the upper 28 bits of the integer value are an object reference index */
+ proto_tree_add_uint(val_tree, hf_rtmpt_amf_object_reference, tvb, iValueOffset, iValueLength, iIntegerValue >> 1);
+ iValueOffset += iValueLength;
+ proto_item_append_text(ti, " object reference %u", iIntegerValue >> 1);
+ if (parent_ti != NULL)
+ proto_item_append_text(parent_ti, " object reference %u", iIntegerValue >> 1);
}
+ break;
+ case RTMPT_AMF3_ARRAY:
+ iIntegerValue = amf_get_u29(tvb, iValueOffset, &iValueLength);
+ if (iIntegerValue & 0x00000001) {
+ /*
+ * The upper 28 bits of the integer value are
+ * a count of the number of elements in
+ * the dense portion of the array.
+ */
+ iArrayLength = iIntegerValue >> 1;
+ proto_tree_add_uint(val_tree, hf_rtmpt_amf_arraydenselength, tvb, iValueOffset, iValueLength, iArrayLength);
+ iValueOffset += iValueLength;
+
+ /*
+ * The AMF3 spec bit on the Array type is slightly
+ * confusingly written, but seems to be saying that
+ * the associative portion of the array follows the
+ * size of the dense portion of the array, and the
+ * dense portion of the array follows the associative
+ * portion.
+ *
+ * Dissect the associative portion.
+ */
+ for (;;) {
+ /* Fetch the name */
+ iIntegerValue = amf_get_u29(tvb, iValueOffset, &iValueLength);
+ if (iIntegerValue & 0x00000001) {
+ /* the upper 28 bits of the integer value is a string length */
+ iStringLength = iIntegerValue >> 1;
+ if (iStringLength == 0) {
+ /* null name marks the end of the associative part */
+ proto_tree_add_text(val_tree, tvb, iValueOffset, iValueLength, "End of associative part");
+ iValueOffset += iValueLength;
+ break;
+ }
+ iStringValue = tvb_get_ephemeral_string_enc(tvb, iValueOffset+iValueLength, iStringLength, ENC_UTF_8|ENC_NA);
+ subval_ti = proto_tree_add_text(val_tree, tvb, iValueOffset, iStringLength, "%s:", iStringValue);
+ subval_tree = proto_item_add_subtree(subval_ti, ett_rtmpt_array_element);
+ proto_tree_add_uint(subval_tree, hf_rtmpt_amf_stringlength, tvb, iValueOffset, iValueLength, iStringLength);
+ iValueOffset += iValueLength;
+ proto_tree_add_string(subval_tree, hf_rtmpt_amf_string, tvb, iValueOffset, iStringLength, iStringValue);
+ } else {
+ /* the upper 28 bits of the integer value are a string reference index */
+ subval_ti = proto_tree_add_text(val_tree, tvb, iValueOffset, iValueLength, "Reference %u:", iIntegerValue >> 1);
+ subval_tree = proto_item_add_subtree(subval_ti, ett_rtmpt_array_element);
+ proto_tree_add_uint(subval_tree, hf_rtmpt_amf_string_reference, tvb, iValueOffset, iValueLength, iIntegerValue >> 1);
+ }
- if (!ot || --ot>0) continue;
+ /* Fetch the value */
+ iObjType = tvb_get_guint8(tvb, offset);
+ proto_item_append_text(subval_ti, "%s",
+ val_to_str_const(iObjType, rtmpt_amf3_type_vals, "Unknown"));
-popamf:
- /* reached end of amf container */
+ iValueOffset = dissect_amf3_value_type(tvb, iValueOffset, subval_tree, subval_ti);
+ }
- if (iObjType==RTMPT_AMF_END_OF_OBJECT) {
- proto_tree_add_text(rtmpt_tree_prop, tvb, offset, 3, "End Of Object Marker");
- iObjectLength += 3;
- }
+ /*
+ * Dissect the dense portion.
+ */
+ for (i = 0; i < iArrayLength; i++)
+ iValueOffset = dissect_amf3_value_type(tvb, iValueOffset, val_tree, NULL);
- proto_item_set_len(ti_object, iObjectLength);
- proto_item_append_text(ti_object, " (%d items)", pc);
- depth--;
- rtmpt_tree = ep_stack_pop(amftrs);
- ti_object = ep_stack_pop(amftis);
- ot = GPOINTER_TO_INT(ep_stack_pop(amfots));
- pc = GPOINTER_TO_INT(ep_stack_pop(amfpcs));
- iObjectLength += GPOINTER_TO_INT(ep_stack_pop(amfols));
- if (iObjType==RTMPT_AMF_END_OF_OBJECT) {
- offset += 3;
+ proto_item_set_end(ti, tvb, iValueOffset);
+ } else {
+ /* the upper 28 bits of the integer value are an object reference index */
+ proto_tree_add_uint(val_tree, hf_rtmpt_amf_object_reference, tvb, iValueOffset, iValueLength, iIntegerValue >> 1);
+ proto_item_append_text(ti, " reference %u", iIntegerValue >> 1);
+ if (parent_ti != NULL)
+ proto_item_append_text(parent_ti, " reference %u", iIntegerValue >> 1);
}
-
- if (depth>0 && ot>0 && --ot==0) {
- iObjType = 0;
- goto popamf;
+ break;
+ case RTMPT_AMF3_OBJECT:
+ iIntegerValue = amf_get_u29(tvb, iValueOffset, &iValueLength);
+ if (iIntegerValue & 0x00000001) {
+ if (iIntegerValue & 0x00000002) {
+ if (iIntegerValue & 0x00000004) {
+ /*
+ * U29O-traits-ext; the rest of
+ * iIntegerValue is not significant,
+ * and, worse, we have idea what
+ * follows the class name, or even
+ * how many bytes follow the class
+ * name - that's by convention between
+ * the client and server.
+ */
+ iValueOffset += iValueLength;
+ } else {
+ /*
+ * U29O-traits; the 0x00000008 bit
+ * specifies whether the type is
+ * dynamic.
+ */
+ iTypeIsDynamic = (iIntegerValue & 0x00000008) ? TRUE : FALSE;
+ iTraitCount = iIntegerValue >> 4;
+ proto_tree_add_uint(val_tree, hf_rtmpt_amf_traitcount, tvb, iValueOffset, iValueLength, iTraitCount);
+ iValueOffset += iValueLength;
+ iIntegerValue = amf_get_u29(tvb, iValueOffset, &iValueLength);
+ if (iIntegerValue & 0x00000001) {
+ /* the upper 28 bits of the integer value is a string length */
+ iStringLength = iIntegerValue >> 1;
+ iStringValue = tvb_get_ephemeral_string_enc(tvb, iValueOffset+iValueLength, iStringLength, ENC_UTF_8|ENC_NA);
+ traits_ti = proto_tree_add_text(val_tree, tvb, iValueOffset, -1, "Traits for class %s (%u member names)", iStringValue, iTraitCount);
+ traits_tree = proto_item_add_subtree(traits_ti, ett_rtmpt_traits);
+ name_ti = proto_tree_add_text(traits_tree, tvb,
+ iValueOffset,
+ iValueLength+iStringLength,
+ "Class name: %s",
+ iStringValue);
+ name_tree = proto_item_add_subtree(name_ti, ett_rtmpt_string);
+ proto_tree_add_uint(name_tree, hf_rtmpt_amf_classnamelength, tvb, iValueOffset, iValueLength, iStringLength);
+ iValueOffset += iValueLength;
+ proto_tree_add_string(name_tree, hf_rtmpt_amf_classname, tvb, iValueOffset, iStringLength, iStringValue);
+ iValueOffset += iStringLength;
+ } else {
+ /* the upper 28 bits of the integer value are a string reference index */
+ traits_ti = proto_tree_add_text(val_tree, tvb, iValueOffset, iValueLength, "Traits for class (reference %u for name)", iIntegerValue >> 1);
+ traits_tree = proto_item_add_subtree(traits_ti, ett_rtmpt_traits);
+ proto_tree_add_uint(traits_tree, hf_rtmpt_amf_string_reference, tvb, iValueOffset, iValueLength, iIntegerValue >> 1);
+ iValueOffset += iValueLength;
+ }
+ for (i = 0; i < iTraitCount; i++) {
+ iIntegerValue = amf_get_u29(tvb, iValueOffset, &iValueLength);
+ if (iIntegerValue & 0x00000001) {
+ /* the upper 28 bits of the integer value is a string length */
+ iStringLength = iIntegerValue >> 1;
+ iStringValue = tvb_get_ephemeral_string_enc(tvb, iValueOffset+iValueLength, iStringLength, ENC_UTF_8|ENC_NA);
+ member_ti = proto_tree_add_text(traits_tree, tvb, iValueOffset, iValueLength+iStringLength, "Member '%s'", iStringValue);
+ member_tree = proto_item_add_subtree(member_ti, ett_rtmpt_trait_member);
+ proto_tree_add_uint(member_tree, hf_rtmpt_amf_membernamelength, tvb, iValueOffset, iValueLength, iStringLength);
+ iValueOffset += iValueLength;
+ proto_tree_add_string(member_tree, hf_rtmpt_amf_membername, tvb, iValueOffset, iStringLength, iStringValue);
+ iValueOffset += iStringLength;
+ } else {
+ /* the upper 28 bits of the integer value are a string reference index */
+ proto_tree_add_uint(traits_tree, hf_rtmpt_amf_string_reference, tvb, iValueOffset, iValueLength, iIntegerValue >> 1);
+ iValueOffset += iValueLength;
+ }
+ }
+ for (i = 0; i < iTraitCount; i++)
+ iValueOffset = dissect_amf3_value_type(tvb, iValueOffset, traits_tree, NULL);
+ if (iTypeIsDynamic) {
+ for (;;) {
+ /* Fetch the name */
+ iIntegerValue = amf_get_u29(tvb, iValueOffset, &iValueLength);
+ if (iIntegerValue & 0x00000001) {
+ /* the upper 28 bits of the integer value is a string length */
+ iStringLength = iIntegerValue >> 1;
+ if (iStringLength == 0) {
+ /* null name marks the end of the associative part */
+ proto_tree_add_text(traits_tree, tvb, iValueOffset, iValueLength, "End of dynamic members");
+ iValueOffset += iValueLength;
+ break;
+ }
+ iStringValue = tvb_get_ephemeral_string_enc(tvb, iValueOffset+iValueLength, iStringLength, ENC_UTF_8|ENC_NA);
+ subval_ti = proto_tree_add_text(traits_tree, tvb, iValueOffset, -1, "%s:", iStringValue);
+ subval_tree = proto_item_add_subtree(subval_ti, ett_rtmpt_array_element);
+ name_ti = proto_tree_add_text(subval_tree, tvb,
+ iValueOffset,
+ iValueLength+iStringLength,
+ "Member name: %s",
+ iStringValue);
+ name_tree = proto_item_add_subtree(name_ti, ett_rtmpt_string);
+ proto_tree_add_uint(name_tree, hf_rtmpt_amf_membernamelength, tvb, iValueOffset, iValueLength, iStringLength);
+ iValueOffset += iValueLength;
+ proto_tree_add_string(name_tree, hf_rtmpt_amf_membername, tvb, iValueOffset, iStringLength, iStringValue);
+ iValueOffset += iStringLength;
+ } else {
+ /* the upper 28 bits of the integer value are a string reference index */
+ subval_ti = proto_tree_add_text(traits_tree, tvb, iValueOffset, iValueLength, "Reference %u:", iIntegerValue >> 1);
+ subval_tree = proto_item_add_subtree(subval_ti, ett_rtmpt_array_element);
+ proto_tree_add_uint(subval_tree, hf_rtmpt_amf_string_reference, tvb, iValueOffset, iValueLength, iIntegerValue >> 1);
+ iValueOffset += iValueLength;
+ }
+
+ /* Fetch the value */
+ iValueOffset = dissect_amf3_value_type(tvb, iValueOffset, subval_tree, subval_ti);
+ proto_item_set_end(subval_ti, tvb, iValueOffset);
+ }
+ }
+ proto_item_set_end(traits_ti, tvb, iValueOffset);
+ }
+ } else {
+ /*
+ * U29O-traits-ref; the upper 27 bits of
+ * the integer value are a traits reference
+ * index.
+ */
+ proto_tree_add_uint(val_tree, hf_rtmpt_amf_trait_reference, tvb, iValueOffset, iValueLength, iIntegerValue >> 2);
+ iValueOffset += iValueLength;
+ }
+ } else {
+ /*
+ * U29O-ref; the upper 28 bits of the integer value
+ * are an object reference index.
+ */
+ proto_tree_add_uint(val_tree, hf_rtmpt_amf_object_reference, tvb, iValueOffset, iValueLength, iIntegerValue >> 1);
+ proto_item_append_text(ti, " reference %u", iIntegerValue >> 1);
+ if (parent_ti != NULL)
+ proto_item_append_text(parent_ti, " reference %u", iIntegerValue >> 1);
+ }
+ break;
+ case RTMPT_AMF3_XML:
+ iIntegerValue = amf_get_u29(tvb, iValueOffset, &iValueLength);
+ if (iIntegerValue & 0x00000001) {
+ /*
+ * The upper 28 bits of the integer value are
+ * a count of the number of bytes in the
+ * XML string.
+ */
+ iStringLength = iIntegerValue >> 1;
+ proto_tree_add_uint(val_tree, hf_rtmpt_amf_xmllength, tvb, iValueOffset, iValueLength, iArrayLength);
+ iValueOffset += iValueLength;
+ proto_tree_add_item(val_tree, hf_rtmpt_amf_xml, tvb, iValueOffset, iStringLength, ENC_UTF_8|ENC_NA);
+ } else {
+ /* the upper 28 bits of the integer value are a string reference index */
+ proto_tree_add_uint(val_tree, hf_rtmpt_amf_object_reference, tvb, iValueOffset, iValueLength, iIntegerValue >> 1);
+ proto_item_append_text(ti, " reference %u", iIntegerValue >> 1);
+ if (parent_ti != NULL)
+ proto_item_append_text(parent_ti, " reference %u", iIntegerValue >> 1);
}
+ break;
+ case RTMPT_AMF3_BYTEARRAY:
+ iIntegerValue = amf_get_u29(tvb, iValueOffset, &iValueLength);
+ if (iIntegerValue & 0x00000001) {
+ /*
+ * The upper 28 bits of the integer value are
+ * a count of the number of bytes in the
+ * byte array.
+ */
+ iArrayLength = iIntegerValue >> 1;
+ proto_tree_add_uint(val_tree, hf_rtmpt_amf_bytearraylength, tvb, iValueOffset, iValueLength, iArrayLength);
+ iValueOffset += iValueLength;
+ iByteArrayValue = ep_tvb_memdup(tvb, iValueOffset, iArrayLength);
+ proto_tree_add_bytes(val_tree, hf_rtmpt_amf_bytearray, tvb, iValueOffset, iArrayLength, iByteArrayValue);
+ proto_item_append_text(ti, " %s", bytes_to_str(iByteArrayValue, iArrayLength));
+ if (parent_ti != NULL)
+ proto_item_append_text(parent_ti, " %s", bytes_to_str(iByteArrayValue, iArrayLength));
+ } else {
+ /* the upper 28 bits of the integer value are a object reference index */
+ proto_tree_add_uint(val_tree, hf_rtmpt_amf_object_reference, tvb, iValueOffset, iValueLength, iIntegerValue >> 1);
+ proto_item_append_text(ti, " reference %u", iIntegerValue >> 1);
+ if (parent_ti != NULL)
+ proto_item_append_text(parent_ti, " reference %u", iIntegerValue >> 1);
+ }
+ break;
+ default:
+ /*
+ * If we can't determine the length, don't carry on;
+ * just skip to the end of the tvbuff.
+ */
+ iValueOffset = tvb_length(tvb);
+ break;
}
+ proto_item_set_end(ti, tvb, iValueOffset);
+ return iValueOffset;
+}
+
+static gint
+dissect_rtmpt_body_command(tvbuff_t *tvb, gint offset, proto_tree *rtmpt_tree, gboolean amf3)
+{
+ gboolean amf3_encoding = FALSE;
+
+ if (amf3) {
+ /* Looks like for the AMF3 variants we get a 0 byte here,
+ * followed by AMF0 encoding - I've never seen actual AMF3
+ * encoding used, which is completely different. I speculate
+ * that if the byte is RTMPT_AMF0_AMF3_MARKER then the rest
+ * will be in AMF3. For now, assume AMF0 only. */
+ offset++;
+ }
+
+ while (tvb_reported_length_remaining(tvb, offset) > 0)
+ {
+ if (amf3_encoding)
+ offset = dissect_amf3_value_type(tvb, offset, rtmpt_tree, NULL);
+ else
+ offset = dissect_amf0_value_type(tvb, offset, rtmpt_tree, &amf3_encoding, NULL);
+ }
+ return offset;
}
static void
@@ -1877,6 +2423,73 @@ dissect_rtmpt_heur(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
}
#endif
+static void
+dissect_amf(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree)
+{
+ proto_item *ti;
+ proto_tree *amf_tree, *headers_tree, *messages_tree;
+ int offset;
+ guint header_count, message_count, i;
+ guint string_length;
+ guint header_length, message_length;
+ gboolean amf3_encoding = FALSE;
+
+ /*
+ * XXX - is "application/x-amf" just AMF3?
+ */
+ ti = proto_tree_add_item(tree, proto_amf, tvb, 0, -1, ENC_NA);
+ amf_tree = proto_item_add_subtree(ti, ett_amf);
+ offset = 0;
+ proto_tree_add_item(amf_tree, hf_amf_version, tvb, offset, 2, ENC_BIG_ENDIAN);
+ offset += 2;
+ header_count = tvb_get_ntohs(tvb, offset);
+ proto_tree_add_uint(amf_tree, hf_amf_header_count, tvb, offset, 2, header_count);
+ offset += 2;
+ if (header_count != 0) {
+ ti = proto_tree_add_text(amf_tree, tvb, offset, -1, "Headers");
+ headers_tree = proto_item_add_subtree(ti, ett_amf_headers);
+ for (i = 0; i < header_count; i++) {
+ string_length = tvb_get_ntohs(tvb, offset);
+ proto_tree_add_item(headers_tree, hf_amf_header_name, tvb, offset, 2, ENC_BIG_ENDIAN|ENC_UTF_8);
+ offset += 2 + string_length;
+ proto_tree_add_item(headers_tree, hf_amf_header_must_understand, tvb, offset, 1, ENC_NA);
+ offset += 1;
+ header_length = tvb_get_ntohl(tvb, offset);
+ if (header_length == 0xFFFFFFFF)
+ proto_tree_add_uint_format_value(headers_tree, hf_amf_header_length, tvb, offset, 4, header_length, "Unknown");
+ else
+ proto_tree_add_uint(headers_tree, hf_amf_header_length, tvb, offset, 4, header_length);
+ offset += 4;
+ if (amf3_encoding)
+ offset = dissect_amf3_value_type(tvb, offset, headers_tree, NULL);
+ else
+ offset = dissect_amf0_value_type(tvb, offset, headers_tree, &amf3_encoding, NULL);
+ }
+ }
+ message_count = tvb_get_ntohs(tvb, offset);
+ proto_tree_add_uint(amf_tree, hf_amf_message_count, tvb, offset, 2, message_count);
+ offset += 2;
+ if (message_count != 0) {
+ ti = proto_tree_add_text(amf_tree, tvb, offset, -1, "Messages");
+ messages_tree = proto_item_add_subtree(ti, ett_amf_messages);
+ for (i = 0; i < message_count; i++) {
+ string_length = tvb_get_ntohs(tvb, offset);
+ proto_tree_add_item(messages_tree, hf_amf_message_target_uri, tvb, offset, 2, ENC_BIG_ENDIAN|ENC_UTF_8);
+ offset += 2 + string_length;
+ string_length = tvb_get_ntohs(tvb, offset);
+ proto_tree_add_item(messages_tree, hf_amf_message_response_uri, tvb, offset, 2, ENC_BIG_ENDIAN|ENC_UTF_8);
+ offset += 2 + string_length;
+ message_length = tvb_get_ntohl(tvb, offset);
+ if (message_length == 0xFFFFFFFF)
+ proto_tree_add_uint_format_value(messages_tree, hf_amf_message_length, tvb, offset, 4, message_length, "Unknown");
+ else
+ proto_tree_add_uint(messages_tree, hf_amf_message_length, tvb, offset, 4, message_length);
+ offset += 4;
+ offset = dissect_rtmpt_body_command(tvb, offset, messages_tree, FALSE);
+ }
+ }
+}
+
void
proto_register_rtmpt(void)
{
@@ -1967,32 +2580,44 @@ proto_register_rtmpt(void)
VALS(rtmpt_ucm_vals), 0x0, "RTMPT UCM event type", HFILL }},
/* AMF basic types */
- { &hf_rtmpt_amf_type,
- { "AMF type", "rtmpt.amf.type", FT_UINT8, BASE_DEC,
- VALS(rtmpt_type_vals), 0x0, "RTMPT AMF type", HFILL }},
+ { &hf_rtmpt_amf0_type,
+ { "AMF0 type", "rtmpt.amf0.type", FT_UINT8, BASE_HEX,
+ VALS(rtmpt_amf0_type_vals), 0x0, "RTMPT AMF0 type", HFILL }},
+
+ { &hf_rtmpt_amf3_type,
+ { "AMF3 type", "rtmpt.amf3.type", FT_UINT8, BASE_HEX,
+ VALS(rtmpt_amf3_type_vals), 0x0, "RTMPT AMF3 type", HFILL }},
{ &hf_rtmpt_amf_number,
{ "Number", "rtmpt.amf.number", FT_DOUBLE, BASE_NONE,
NULL, 0x0, "RTMPT AMF number", HFILL }},
+ { &hf_rtmpt_amf_integer,
+ { "Integer", "rtmpt.amf.integer", FT_UINT32, BASE_DEC,
+ NULL, 0x0, "RTMPT AMF3 integer", HFILL }},
+
{ &hf_rtmpt_amf_boolean,
{ "Boolean", "rtmpt.amf.boolean", FT_BOOLEAN, BASE_NONE,
NULL, 0x0, "RTMPT AMF boolean", HFILL }},
{ &hf_rtmpt_amf_stringlength,
- { "String length", "rtmpt.amf.stringlength", FT_UINT16, BASE_DEC,
+ { "String length", "rtmpt.amf.stringlength", FT_UINT32, BASE_DEC,
NULL, 0x0, "RTMPT AMF string length", HFILL }},
{ &hf_rtmpt_amf_string,
- { "String", "rtmpt.amf.string", FT_STRINGZ, BASE_NONE,
+ { "String", "rtmpt.amf.string", FT_STRING, BASE_NONE,
NULL, 0x0, "RTMPT AMF string", HFILL }},
- { &hf_rtmpt_amf_reference,
- { "Reference", "rtmpt.amf.reference", FT_UINT16, BASE_DEC,
+ { &hf_rtmpt_amf_string_reference,
+ { "String reference", "rtmpt.amf.string_reference", FT_UINT32, BASE_DEC,
+ NULL, 0x0, "RTMPT AMF3 string reference", HFILL }},
+
+ { &hf_rtmpt_amf_object_reference,
+ { "Object reference", "rtmpt.amf.object_reference", FT_UINT32, BASE_DEC,
NULL, 0x0, "RTMPT AMF object reference", HFILL }},
{ &hf_rtmpt_amf_date,
- { "Date", "rtmpt.amf.date", FT_BYTES, BASE_NONE,
+ { "Date", "rtmpt.amf.date", FT_ABSOLUTE_TIME, ABSOLUTE_TIME_LOCAL,
NULL, 0x0, "RTMPT AMF date", HFILL }},
{ &hf_rtmpt_amf_longstringlength,
@@ -2000,22 +2625,62 @@ proto_register_rtmpt(void)
NULL, 0x0, "RTMPT AMF long string length", HFILL }},
{ &hf_rtmpt_amf_longstring,
- { "Long string", "rtmpt.amf.longstring", FT_STRINGZ, BASE_NONE,
+ { "Long string", "rtmpt.amf.longstring", FT_STRING, BASE_NONE,
NULL, 0x0, "RTMPT AMF long string", HFILL }},
- { &hf_rtmpt_amf_xml,
- { "XML document", "rtmpt.amf.xml", FT_STRINGZ, BASE_NONE,
+ { &hf_rtmpt_amf_xml_doc,
+ { "XML document", "rtmpt.amf.xml_doc", FT_STRING, BASE_NONE,
NULL, 0x0, "RTMPT AMF XML document", HFILL }},
+ { &hf_rtmpt_amf_xmllength,
+ { "XML text length", "rtmpt.amf.xmllength", FT_UINT32, BASE_DEC,
+ NULL, 0x0, "RTMPT AMF E4X XML length", HFILL }},
+
+ { &hf_rtmpt_amf_xml,
+ { "XML", "rtmpt.amf.xml", FT_STRING, BASE_NONE,
+ NULL, 0x0, "RTMPT AMF E4X XML", HFILL }},
+
{ &hf_rtmpt_amf_int64,
{ "Int64", "rtmpt.amf.int64", FT_INT64, BASE_DEC,
NULL, 0x0, "RTMPT AMF int64", HFILL }},
-/* AMF object types */
+ { &hf_rtmpt_amf_bytearraylength,
+ { "ByteArray length", "rtmpt.amf.bytearraylength", FT_UINT32, BASE_DEC,
+ NULL, 0x0, "RTMPT AMF3 ByteArray length", HFILL }},
+
+ { &hf_rtmpt_amf_bytearray,
+ { "ByteArray", "rtmpt.amf.bytearray", FT_BYTES, BASE_NONE,
+ NULL, 0x0, "RTMPT AMF3 ByteArray", HFILL }},
+
+/* AMF object types and subfields of the object types */
{ &hf_rtmpt_amf_object,
{ "Object", "rtmpt.amf.object", FT_NONE, BASE_NONE,
NULL, 0x0, "RTMPT AMF object", HFILL }},
+ { &hf_rtmpt_amf_traitcount,
+ { "Trait count", "rtmpt.amf.traitcount", FT_UINT32, BASE_DEC,
+ NULL, 0x0, "RTMPT AMF count of traits for an object", HFILL }},
+
+ { &hf_rtmpt_amf_classnamelength,
+ { "Class name length", "rtmpt.amf.classnamelength", FT_UINT32, BASE_DEC,
+ NULL, 0x0, "RTMPT AMF class name length", HFILL }},
+
+ { &hf_rtmpt_amf_classname,
+ { "Class name", "rtmpt.amf.classname", FT_STRING, BASE_NONE,
+ NULL, 0x0, "RTMPT AMF class name", HFILL }},
+
+ { &hf_rtmpt_amf_membernamelength,
+ { "Member name length", "rtmpt.amf.membernamelength", FT_UINT32, BASE_DEC,
+ NULL, 0x0, "RTMPT AMF member name length", HFILL }},
+
+ { &hf_rtmpt_amf_membername,
+ { "Member name", "rtmpt.amf.membername", FT_STRING, BASE_NONE,
+ NULL, 0x0, "RTMPT AMF member name", HFILL }},
+
+ { &hf_rtmpt_amf_trait_reference,
+ { "Trait reference", "rtmpt.amf.trait_reference", FT_UINT32, BASE_DEC,
+ NULL, 0x0, "RTMPT AMF trait reference", HFILL }},
+
{ &hf_rtmpt_amf_ecmaarray,
{ "ECMA array", "rtmpt.amf.ecmaarray", FT_NONE, BASE_NONE,
NULL, 0x0, "RTMPT AMF ECMA array", HFILL }},
@@ -2024,10 +2689,18 @@ proto_register_rtmpt(void)
{ "Strict array", "rtmpt.amf.strictarray", FT_NONE, BASE_NONE,
NULL, 0x0, "RTMPT AMF strict array", HFILL }},
+ { &hf_rtmpt_amf_array,
+ { "Array", "rtmpt.amf.array", FT_NONE, BASE_NONE,
+ NULL, 0x0, "RTMPT AMF3 array", HFILL }},
+
{ &hf_rtmpt_amf_arraylength,
{ "Array length", "rtmpt.amf.arraylength", FT_UINT32, BASE_DEC,
NULL, 0x0, "RTMPT AMF array length", HFILL }},
+ { &hf_rtmpt_amf_arraydenselength,
+ { "Length of dense portion", "rtmpt.amf.arraydenselength", FT_UINT32, BASE_DEC,
+ NULL, 0x0, "RTMPT AMF length of dense portion of array", HFILL }},
+
/* Frame links */
{ &hf_rtmpt_function_call,
@@ -2118,6 +2791,9 @@ proto_register_rtmpt(void)
&ett_rtmpt_object,
&ett_rtmpt_mixed_array,
&ett_rtmpt_array,
+ &ett_rtmpt_array_element,
+ &ett_rtmpt_traits,
+ &ett_rtmpt_trait_member,
&ett_rtmpt_audio_control,
&ett_rtmpt_video_control,
&ett_rtmpt_tag,
@@ -2137,11 +2813,14 @@ proto_register_rtmpt(void)
" To use this option, you must also enable \"Allow subdissectors to reassemble TCP streams\""
" in the TCP protocol settings.",
&rtmpt_desegment);
+
}
void
proto_reg_handoff_rtmpt(void)
{
+ dissector_handle_t amf_handle;
+
/* heur_dissector_add("tcp", dissect_rtmpt_heur, proto_rtmpt); */
rtmpt_tcp_handle = create_dissector_handle(dissect_rtmpt_tcp, proto_rtmpt);
/* dissector_add_handle("tcp.port", rtmpt_tcp_handle); */
@@ -2149,8 +2828,66 @@ proto_reg_handoff_rtmpt(void)
rtmpt_http_handle = create_dissector_handle(dissect_rtmpt_http, proto_rtmpt);
dissector_add_string("media_type", "application/x-fcs", rtmpt_http_handle);
+
+ amf_handle = create_dissector_handle(dissect_amf, proto_amf);
+ dissector_add_string("media_type", "application/x-amf", amf_handle);
}
+void
+proto_register_amf(void)
+{
+ static hf_register_info hf[] = {
+ { &hf_amf_version,
+ { "AMF version", "amf.version", FT_UINT16, BASE_DEC,
+ NULL, 0x0, NULL, HFILL }},
+
+ { &hf_amf_header_count,
+ { "Header count", "amf.header_count", FT_UINT16, BASE_DEC,
+ NULL, 0x0, NULL, HFILL }},
+
+ { &hf_amf_header_name,
+ { "Name", "amf.header.name", FT_UINT_STRING, BASE_NONE,
+ NULL, 0x0, NULL, HFILL }},
+
+ { &hf_amf_header_must_understand,
+ { "Must understand", "amf.header.must_understand", FT_BOOLEAN, BASE_NONE,
+ NULL, 0x0, NULL, HFILL }},
+
+ { &hf_amf_header_length,
+ { "Length", "amf.header.length", FT_UINT32, BASE_DEC,
+ NULL, 0x0, NULL, HFILL }},
+
+ { &hf_amf_header_value_type,
+ { "Value type", "amf.header.value_type", FT_UINT32, BASE_HEX,
+ /*VALS(rtmpt_type_vals)*/NULL, 0x0, NULL, HFILL }},
+
+ { &hf_amf_message_count,
+ { "Message count", "amf.message_count", FT_UINT16, BASE_DEC,
+ NULL, 0x0, NULL, HFILL }},
+
+ { &hf_amf_message_target_uri,
+ { "Target URI", "amf.message.target_uri", FT_UINT_STRING, BASE_NONE,
+ NULL, 0x0, NULL, HFILL }},
+
+ { &hf_amf_message_response_uri,
+ { "Response URI", "amf.message.response_uri", FT_UINT_STRING, BASE_NONE,
+ NULL, 0x0, NULL, HFILL }},
+
+ { &hf_amf_message_length,
+ { "Length", "amf.message.length", FT_UINT32, BASE_DEC,
+ NULL, 0x0, NULL, HFILL }},
+ };
+ static gint *ett[] = {
+ &ett_amf,
+ &ett_amf_headers,
+ &ett_amf_messages
+ };
+
+ proto_amf = proto_register_protocol("Action Message Format", "AMF", "amf");
+ proto_register_field_array(proto_rtmpt, hf, array_length(hf));
+ proto_register_subtree_array(ett, array_length(ett));
+};
+
/*
* Editor modelines - http://www.wireshark.org/tools/modelines.html
*