blob: de3510816c5239879eb0d54460460cb7a7addb03 [file] [log] [blame]
/**
* @file rtmp/amf_dec.c Real Time Messaging Protocol (RTMP) -- AMF Decoding
*
* Copyright (C) 2010 Creytiv.com
*/
#include <string.h>
#include <re_types.h>
#include <re_fmt.h>
#include <re_mem.h>
#include <re_mbuf.h>
#include <re_net.h>
#include <re_sa.h>
#include <re_list.h>
#include <re_sys.h>
#include <re_odict.h>
#include <re_rtmp.h>
#include "rtmp.h"
enum {
AMF_HASH_SIZE = 32
};
static int amf_decode_value(struct odict *dict, const char *key,
struct mbuf *mb);
static int amf_decode_object(struct odict *dict, struct mbuf *mb)
{
char *key = NULL;
uint16_t len;
int err = 0;
while (mbuf_get_left(mb) > 0) {
if (mbuf_get_left(mb) < 2)
return ENODATA;
len = ntohs(mbuf_read_u16(mb));
if (len == 0) {
uint8_t val;
if (mbuf_get_left(mb) < 1)
return ENODATA;
val = mbuf_read_u8(mb);
if (val == RTMP_AMF_TYPE_OBJECT_END)
return 0;
else
return EBADMSG;
}
if (mbuf_get_left(mb) < len)
return ENODATA;
err = mbuf_strdup(mb, &key, len);
if (err)
return err;
err = amf_decode_value(dict, key, mb);
key = mem_deref(key);
if (err)
return err;
}
return 0;
}
static int amf_decode_value(struct odict *dict, const char *key,
struct mbuf *mb)
{
union {
uint64_t i;
double f;
} num;
struct odict *object = NULL;
char *str = NULL;
uint32_t i, array_len;
uint8_t type;
uint16_t len;
bool boolean;
int err = 0;
if (mbuf_get_left(mb) < 1)
return ENODATA;
type = mbuf_read_u8(mb);
switch (type) {
case RTMP_AMF_TYPE_NUMBER:
if (mbuf_get_left(mb) < 8)
return ENODATA;
num.i = sys_ntohll(mbuf_read_u64(mb));
err = odict_entry_add(dict, key, ODICT_DOUBLE, num.f);
break;
case RTMP_AMF_TYPE_BOOLEAN:
if (mbuf_get_left(mb) < 1)
return ENODATA;
boolean = !!mbuf_read_u8(mb);
err = odict_entry_add(dict, key, ODICT_BOOL, boolean);
break;
case RTMP_AMF_TYPE_STRING:
if (mbuf_get_left(mb) < 2)
return ENODATA;
len = ntohs(mbuf_read_u16(mb));
if (mbuf_get_left(mb) < len)
return ENODATA;
err = mbuf_strdup(mb, &str, len);
if (err)
return err;
err = odict_entry_add(dict, key, ODICT_STRING, str);
mem_deref(str);
break;
case RTMP_AMF_TYPE_NULL:
err = odict_entry_add(dict, key, ODICT_NULL);
break;
case RTMP_AMF_TYPE_ECMA_ARRAY:
if (mbuf_get_left(mb) < 4)
return ENODATA;
array_len = ntohl(mbuf_read_u32(mb));
(void)array_len; /* ignore array length */
/* fallthrough */
case RTMP_AMF_TYPE_OBJECT:
err = odict_alloc(&object, 32);
if (err)
return err;
err = amf_decode_object(object, mb);
if (err) {
mem_deref(object);
return err;
}
err = odict_entry_add(dict, key, ODICT_OBJECT, object);
mem_deref(object);
break;
case RTMP_AMF_TYPE_STRICT_ARRAY:
if (mbuf_get_left(mb) < 4)
return ENODATA;
array_len = ntohl(mbuf_read_u32(mb));
if (!array_len)
return EPROTO;
err = odict_alloc(&object, 32);
if (err)
return err;
for (i=0; i<array_len; i++) {
char ix[32];
re_snprintf(ix, sizeof(ix), "%u", i);
err = amf_decode_value(object, ix, mb);
if (err) {
mem_deref(object);
return err;
}
}
err = odict_entry_add(dict, key, ODICT_ARRAY, object);
mem_deref(object);
break;
default:
err = EPROTO;
break;
}
return err;
}
int rtmp_amf_decode(struct odict **msgp, struct mbuf *mb)
{
struct odict *msg;
unsigned ix = 0;
int err;
if (!msgp || !mb)
return EINVAL;
err = odict_alloc(&msg, AMF_HASH_SIZE);
if (err)
return err;
/* decode all entries on root-level */
while (mbuf_get_left(mb) > 0) {
char key[16];
re_snprintf(key, sizeof(key), "%u", ix++);
/* note: key is the numerical index */
err = amf_decode_value(msg, key, mb);
if (err)
goto out;
}
out:
if (err)
mem_deref(msg);
else
*msgp = msg;
return err;
}