Skip to content

Commit

Permalink
+ Provide support for previously unsupported AMF3 types
Browse files Browse the repository at this point in the history
+ Dictionary can't be fully supported in PHP - produce a warning
+ Add tests for new types
  • Loading branch information
neoxic committed Oct 20, 2014
1 parent c552bdb commit fdba6d6
Show file tree
Hide file tree
Showing 3 changed files with 142 additions and 26 deletions.
45 changes: 25 additions & 20 deletions amf3.h
Expand Up @@ -7,30 +7,35 @@
#define AMF3_H


#define AMF3_UNDEFINED 0x00
#define AMF3_NULL 0x01
#define AMF3_FALSE 0x02
#define AMF3_TRUE 0x03
#define AMF3_INTEGER 0x04
#define AMF3_DOUBLE 0x05
#define AMF3_STRING 0x06
#define AMF3_XMLDOC 0x07
#define AMF3_DATE 0x08
#define AMF3_ARRAY 0x09
#define AMF3_OBJECT 0x0a
#define AMF3_XML 0x0b
#define AMF3_BYTEARRAY 0x0c

#define AMF3_MAX_INT 268435455 /* (2^28)-1 */
#define AMF3_MIN_INT -268435456 /* -(2^28) */
#define AMF3_UNDEFINED 0x00
#define AMF3_NULL 0x01
#define AMF3_FALSE 0x02
#define AMF3_TRUE 0x03
#define AMF3_INTEGER 0x04
#define AMF3_DOUBLE 0x05
#define AMF3_STRING 0x06
#define AMF3_XMLDOC 0x07
#define AMF3_DATE 0x08
#define AMF3_ARRAY 0x09
#define AMF3_OBJECT 0x0a
#define AMF3_XML 0x0b
#define AMF3_BYTEARRAY 0x0c
#define AMF3_VECTOR_INT 0x0d
#define AMF3_VECTOR_UINT 0x0e
#define AMF3_VECTOR_DOUBLE 0x0f
#define AMF3_VECTOR_OBJECT 0x10
#define AMF3_DICTIONARY 0x11

#define AMF3_MAX_INT 268435455 /* (2^28)-1 */
#define AMF3_MIN_INT -268435456 /* -(2^28) */

// Encoding options
#define AMF3_FORCE_OBJECT 0x01
#define AMF3_FORCE_OBJECT 0x01

// Decoding options
#define AMF3_CLASS_MAP 0x01
#define AMF3_CLASS_AUTOLOAD 0x02
#define AMF3_CLASS_CONSTRUCT 0x04
#define AMF3_CLASS_MAP 0x01
#define AMF3_CLASS_AUTOLOAD 0x02
#define AMF3_CLASS_CONSTRUCT 0x04


#define ZVAL_RESET(A) \
Expand Down
107 changes: 101 additions & 6 deletions amf3_decode.c
Expand Up @@ -41,6 +41,15 @@ static void traitsPtrDtor(void *p) {

static int decodeValue(const char *buf, int pos, int size, zval **val, int opts, HashTable *sht, HashTable *oht, HashTable *tht TSRMLS_DC);

static int decodeU8(const char *buf, int pos, int size, unsigned char *val TSRMLS_DC) {
if (pos >= size) {
php_error(E_WARNING, "Insufficient U8 data at position %d", pos);
return -1;
}
*val = buf[pos];
return 1;
}

static int decodeU29(const char *buf, int pos, int size, int *val TSRMLS_DC) {
int n = 0, ofs = 0;
unsigned char b;
Expand Down Expand Up @@ -73,6 +82,28 @@ static int decodeInteger(const char *buf, int pos, int size, zval **val TSRMLS_D
return ofs;
}

static int decodeU32(const char *buf, int pos, int size, zval **val, int sign TSRMLS_DC) {
union { int n; char c; } t;
union { unsigned n; char c[4]; } u;
long n;
if ((pos + 4) > size) {
php_error(E_WARNING, "Insufficient U32 data at position %d", pos);
return -1;
}
buf += pos;
t.n = 1;
if (!t.c) memcpy(u.c, buf, 4);
else { /* Little-endian machine */
int i;
for (i = 0; i < 4; ++i) u.c[i] = buf[3 - i];
}
if (sign) n = (signed)u.n;
else n = u.n;
ZVAL_RESET(*val);
ZVAL_LONG(*val, n);
return 4;
}

static int decodeDouble(const char *buf, int pos, int size, zval **val TSRMLS_DC) {
union { int n; char c; } t;
union { double d; char c[8]; } u;
Expand Down Expand Up @@ -382,13 +413,64 @@ static int decodeObject(const char *buf, int pos, int size, zval **val, int opts
return pos - old;
}

static int decodeVectorItem(const char *buf, int pos, int size, zval **val, int opts, HashTable *sht, HashTable *oht, HashTable *tht, int type TSRMLS_DC) {
switch (type) {
case AMF3_VECTOR_INT:
return decodeU32(buf, pos, size, val, 1 TSRMLS_CC);
case AMF3_VECTOR_UINT:
return decodeU32(buf, pos, size, val, 0 TSRMLS_CC);
case AMF3_VECTOR_DOUBLE:
return decodeDouble(buf, pos, size, val TSRMLS_CC);
case AMF3_VECTOR_OBJECT:
return decodeValue(buf, pos, size, val, opts, sht, oht, tht TSRMLS_CC);
default:
return -1;
}
}

static int decodeVector(const char *buf, int pos, int size, zval **val, int opts, HashTable *sht, HashTable *oht, HashTable *tht, int type TSRMLS_DC) {
int old = pos, ofs, len;
ofs = decodeRef(buf, pos, size, &len, val, oht TSRMLS_CC);
if (ofs < 0) return -1;
pos += ofs;
if (len != -1) {
zval *hv;
unsigned char fv;
ZVAL_RESET(*val);
array_init(*val);
storeRef(oht, *val);
ofs = decodeU8(buf, pos, size, &fv TSRMLS_CC); /* 'fixed-vector' marker */
if (ofs < 0) return -1;
pos += ofs;
if (type == AMF3_VECTOR_OBJECT) { /* 'object-type-name' marker */
ofs = decodeString(buf, pos, size, 0, 0, 0, sht, 0 TSRMLS_CC);
if (ofs < 0) return -1;
pos += ofs;
}
while (len--) {
hv = 0;
ofs = decodeVectorItem(buf, pos, size, &hv, opts, sht, oht, tht, type TSRMLS_CC);
if (hv) add_next_index_zval(*val, hv);
if (ofs < 0) return -1;
pos += ofs;
}
}
return pos - old;
}

static int decodeDictionary(const char *buf, int pos, int size, zval **val, int opts, HashTable *sht, HashTable *oht, HashTable *tht TSRMLS_DC) {
/* No support for dictionary in PHP */
php_error(E_WARNING, "Unsupported 'Dictionary' value at position %d", pos);
return -1;
}

static int decodeValue(const char *buf, int pos, int size, zval **val, int opts, HashTable *sht, HashTable *oht, HashTable *tht TSRMLS_DC) {
int old = pos, ofs;
if (pos >= size) {
php_error(E_WARNING, "Insufficient type data at position %d", pos);
return -1;
}
switch (buf[pos++]) {
unsigned char type;
ofs = decodeU8(buf, pos, size, &type TSRMLS_CC);
if (ofs < 0) return -1;
pos += ofs;
switch (type) {
case AMF3_UNDEFINED:
case AMF3_NULL:
ZVAL_RESET(*val);
Expand Down Expand Up @@ -439,8 +521,21 @@ static int decodeValue(const char *buf, int pos, int size, zval **val, int opts,
if (ofs < 0) return -1;
pos += ofs;
break;
case AMF3_VECTOR_INT:
case AMF3_VECTOR_UINT:
case AMF3_VECTOR_DOUBLE:
case AMF3_VECTOR_OBJECT:
ofs = decodeVector(buf, pos, size, val, opts, sht, oht, tht, type TSRMLS_CC);
if (ofs < 0) return -1;
pos += ofs;
break;
case AMF3_DICTIONARY:
ofs = decodeDictionary(buf, pos, size, val, opts, sht, oht, tht TSRMLS_CC);
if (ofs < 0) return -1;
pos += ofs;
break;
default:
php_error(E_WARNING, "Unsupported value type %d at position %d", buf[old], old);
php_error(E_WARNING, "Invalid value type %d at position %d", type, old);
return -1;
}
return pos - old;
Expand Down
16 changes: 16 additions & 0 deletions tests/stress.phpt
Expand Up @@ -159,6 +159,17 @@ $strs = array(
. "\x0a\x04" // Object (reference 2)
. "\x0a\x06" // Object (reference 3)
. "\x0a\x08" // Object (reference 4)
,
// Vector
"\x09\x11\x01" // Array (length 8)
. "\x0d\x05\x00\x00\x01\x02\x03\xff\xff\xff\xff" // Vector of ints [66051, -1]
. "\x0e\x05\x00\x00\x01\x02\x03\xff\xff\xff\xff" // Vector of uints [66051, 4294967295]
. "\x0f\x05\x00\x3f\xb9\x99\x99\x99\x99\x99\x9a\x3f\xc9\x99\x99\x99\x99\x99\x9a" // Vector of doubles [0.1, 0.2]
. "\x10\x07\x01\x03\x2a\x02\x03\x04\x00" // Vector of objects (type '*') [false, true, 0]
. "\x0d\x02" // Vector of ints (reference 1)
. "\x0e\x04" // Vector of uints (reference 2)
. "\x0f\x06" // Vector of doubles (reference 3)
. "\x10\x08" // Vector of objects (reference 4)
);

$ba = "\x11\x22\x33";
Expand All @@ -167,10 +178,15 @@ $o1 = array('A' => 1, 'B' => 2, 'C' => 3, 'D' => 4, 'E' => 5, '_class' => 'ABC')
$o2 = array('A' => null, 'B' => false, 'C' => true, 'F' => true, '_class' => 'ABC');
$o3 = array('_data' => false, '_class' => 'DEF');
$o4 = array('_data' => true, '_class' => 'DEF');
$vi = array(66051, -1);
$vu = array(66051, (int)4294967295); // Type cast makes 32-bit systems happy
$vd = array(0.1, 0.2);
$vo = array(false, true, 0);
$objs = array(
array(0.1, 'ABC', 'DEF', $ba, 0.1, 'ABC', 'DEF', $ba),
array($ma, $ma),
array($o1, $o2, $o3, $o4, $o1, $o2, $o3, $o4),
array($vi, $vu, $vd, $vo, $vi, $vu, $vd, $vo),
);

for ($i = 0; $i < count($strs); ++$i) {
Expand Down

0 comments on commit fdba6d6

Please sign in to comment.