nginx-mod-rtmp/ngx_rtmp_amf0.c

455 lines
11 KiB
C
Raw Normal View History

2012-03-08 16:21:22 +01:00
/*
* Copyright (c) 2012 Roman Arutyunyan
*/
#include "ngx_rtmp_amf0.h"
#include "ngx_rtmp.h"
2012-03-08 16:21:22 +01:00
#include <string.h>
static inline void*
ngx_rtmp_amf0_reverse_copy(void *dst, void* src, size_t len)
{
size_t k;
if (dst == NULL || src == NULL) {
return NULL;
}
for(k = 0; k < len; ++k) {
((u_char*)dst)[k] = ((u_char*)src)[len - 1 - k];
}
return dst;
}
#define NGX_RTMP_AMF0_DEBUG_SIZE 16
#ifdef NGX_DEBUG
static void
ngx_rtmp_amf0_debug(const char* op, ngx_log_t *log, u_char *p, size_t n)
{
u_char hstr[3 * NGX_RTMP_AMF0_DEBUG_SIZE + 1];
u_char str[NGX_RTMP_AMF0_DEBUG_SIZE + 1];
u_char *hp, *sp;
static u_char hex[] = "0123456789ABCDEF";
size_t i;
hp = hstr;
sp = str;
for(i = 0; i < n && i < NGX_RTMP_AMF0_DEBUG_SIZE; ++i) {
*hp++ = ' ';
2012-03-13 14:51:41 +01:00
if (p) {
*hp++ = hex[(*p & 0xf0) >> 4];
*hp++ = hex[*p & 0x0f];
*sp++ = (*p >= 0x20 && *p <= 0x7e) ?
*p : (u_char)'?';
++p;
} else {
*hp++ = 'X';
*hp++ = 'X';
*sp++ = '?';
}
}
*hp = *sp = '\0';
ngx_log_debug4(NGX_LOG_DEBUG_RTMP, log, 0,
"AMF0 %s (%d)%s '%s'", op, n, hstr, str);
}
#endif
2012-03-08 16:21:22 +01:00
static ngx_int_t
ngx_rtmp_amf0_get(ngx_rtmp_amf0_ctx_t *ctx, void *p, size_t n)
2012-03-08 16:21:22 +01:00
{
ngx_buf_t *b;
2012-03-08 18:45:10 +01:00
size_t size;
2012-03-13 06:41:51 +01:00
ngx_chain_t *l;
#ifdef NGX_DEBUG
void *op = p;
2012-03-15 12:49:05 +01:00
size_t on = n;
#endif
2012-03-08 16:21:22 +01:00
if (!n)
2012-03-08 18:45:10 +01:00
return NGX_OK;
2012-03-08 16:21:22 +01:00
2012-03-13 06:41:51 +01:00
for(l = ctx->link; l; l = l->next) {
2012-03-08 16:21:22 +01:00
2012-03-13 06:41:51 +01:00
b = l->buf;
2012-03-08 16:21:22 +01:00
2012-03-13 14:51:41 +01:00
if (b->last >= n + b->pos) {
if (p) {
2012-03-08 16:21:22 +01:00
p = ngx_cpymem(p, b->pos, n);
}
2012-03-08 16:21:22 +01:00
b->pos += n;
2012-03-15 12:49:05 +01:00
ctx->link = l;
#ifdef NGX_DEBUG
2012-03-15 12:49:05 +01:00
ngx_rtmp_amf0_debug("read", ctx->log, (u_char*)op, on);
#endif
2012-03-08 16:21:22 +01:00
return NGX_OK;
}
2012-03-08 18:45:10 +01:00
size = b->last - b->pos;
2012-03-15 12:49:05 +01:00
if (p) {
2012-03-08 18:45:10 +01:00
p = ngx_cpymem(p, b->pos, size);
2012-03-15 12:49:05 +01:00
}
2012-03-08 16:21:22 +01:00
n -= size;
}
ngx_log_debug1(NGX_LOG_DEBUG_RTMP, ctx->log, 0,
"AMF0 read eof (%d)", n);
2012-03-08 16:21:22 +01:00
return NGX_ERROR;
}
static ngx_int_t
ngx_rtmp_amf0_put(ngx_rtmp_amf0_ctx_t *ctx, void *p, size_t n)
2012-03-08 16:21:22 +01:00
{
2012-03-08 18:45:10 +01:00
ngx_buf_t *b;
size_t size;
ngx_chain_t *l, *ln;
#ifdef NGX_DEBUG
ngx_rtmp_amf0_debug("write", ctx->log, (u_char*)p, n);
#endif
l = ctx->link;
2012-03-08 16:21:22 +01:00
while(n) {
b = l ? l->buf : NULL;
2012-03-08 16:21:22 +01:00
if (b == NULL || b->last == b->end) {
ln = ctx->alloc(ctx->arg);
if (ln == NULL) {
2012-03-08 16:21:22 +01:00
return NGX_ERROR;
}
2012-03-08 16:21:22 +01:00
if (l == NULL) {
l = ln;
ctx->first = l;
2012-03-08 16:21:22 +01:00
} else {
l->next = ln;
l = ln;
2012-03-08 16:21:22 +01:00
}
2012-03-13 14:51:41 +01:00
ctx->link = l;
b = l->buf;
2012-03-08 16:21:22 +01:00
}
2012-03-08 18:45:10 +01:00
size = b->end - b->last;
if (size >= n) {
2012-03-08 16:21:22 +01:00
b->last = ngx_cpymem(b->last, p, n);
return NGX_OK;
}
2012-03-08 18:45:10 +01:00
b->last = ngx_cpymem(b->last, p, size);
p = (u_char*)p + size;
n -= size;
2012-03-08 16:21:22 +01:00
}
2012-03-08 18:45:10 +01:00
return NGX_OK;
2012-03-08 16:21:22 +01:00
}
static ngx_int_t
ngx_rtmp_amf0_read_object(ngx_rtmp_amf0_ctx_t *ctx, ngx_rtmp_amf0_elt_t *elts,
2012-03-08 16:21:22 +01:00
size_t nelts)
{
uint8_t type;
uint16_t len;
size_t n, namelen, maxlen;
ngx_int_t rc;
2012-03-13 14:51:41 +01:00
u_char buf[2];
2012-03-08 16:21:22 +01:00
maxlen = 0;
2012-03-08 18:45:10 +01:00
for(n = 0; n < nelts; ++n) {
namelen = strlen(elts[n].name);
2012-03-08 16:21:22 +01:00
if (namelen > maxlen)
maxlen = namelen;
}
for(;;) {
char name[maxlen + 1];
/* read key */
2012-03-13 14:51:41 +01:00
if (ngx_rtmp_amf0_get(ctx, buf, 2) != NGX_OK)
2012-03-08 16:21:22 +01:00
return NGX_ERROR;
2012-03-13 14:51:41 +01:00
ngx_rtmp_amf0_reverse_copy(&len, buf, 2);
2012-03-08 16:21:22 +01:00
if (!len)
break;
if (len <= maxlen) {
rc = ngx_rtmp_amf0_get(ctx, name, len);
2012-03-08 16:21:22 +01:00
name[len] = 0;
} else {
rc = ngx_rtmp_amf0_get(ctx, name, maxlen);
2012-03-08 16:21:22 +01:00
if (rc != NGX_OK)
return NGX_ERROR;
name[maxlen] = 0;
rc = ngx_rtmp_amf0_get(ctx, 0, len - maxlen);
2012-03-08 16:21:22 +01:00
}
if (rc != NGX_OK)
return NGX_ERROR;
/* TODO: if we require array to be sorted on name
* then we could be able to use binary search */
for(n = 0; n < nelts && strcmp(name, elts[n].name); ++n);
if (ngx_rtmp_amf0_read(ctx, n < nelts ? &elts[n] : NULL, 1) != NGX_OK)
2012-03-08 16:21:22 +01:00
return NGX_ERROR;
}
if (ngx_rtmp_amf0_get(ctx, &type, 1) != NGX_OK
2012-03-08 16:21:22 +01:00
|| type != NGX_RTMP_AMF0_END)
{
return NGX_ERROR;
}
return NGX_OK;
}
2012-03-08 18:45:10 +01:00
#define NGX_RTMP_AMF0_TILL_END_FLAG ((size_t)1 << (sizeof(size_t) * 8 - 1))
2012-03-08 16:21:22 +01:00
ngx_int_t
ngx_rtmp_amf0_read(ngx_rtmp_amf0_ctx_t *ctx, ngx_rtmp_amf0_elt_t *elts, size_t nelts)
2012-03-08 16:21:22 +01:00
{
void *data;
uint8_t type;
2012-03-08 18:45:10 +01:00
size_t n;
2012-03-08 16:21:22 +01:00
uint16_t len;
ngx_int_t rc;
int till_end;
u_char buf[8];
2012-03-08 16:21:22 +01:00
if (nelts & NGX_RTMP_AMF0_TILL_END_FLAG) {
till_end = 1;
nelts = nelts & ~NGX_RTMP_AMF0_TILL_END_FLAG;
} else {
till_end = 0;
}
for(n = 0; till_end || n < nelts; ++n) {
if (ngx_rtmp_amf0_get(ctx, &type, sizeof(type)) != NGX_OK)
2012-03-08 16:21:22 +01:00
return NGX_ERROR;
data = (n >= nelts || elts == NULL || elts->type != type)
? NULL
: elts->data;
switch(type) {
case NGX_RTMP_AMF0_NUMBER:
if (ngx_rtmp_amf0_get(ctx, buf, 8) != NGX_OK) {
2012-03-08 16:21:22 +01:00
return NGX_ERROR;
}
2012-03-13 14:51:41 +01:00
ngx_rtmp_amf0_reverse_copy(data, buf, 8);
2012-03-08 16:21:22 +01:00
break;
case NGX_RTMP_AMF0_BOOLEAN:
if (ngx_rtmp_amf0_get(ctx, data, 1) != NGX_OK) {
2012-03-08 16:21:22 +01:00
return NGX_ERROR;
}
2012-03-08 16:21:22 +01:00
break;
case NGX_RTMP_AMF0_STRING:
if (ngx_rtmp_amf0_get(ctx, buf, 2) != NGX_OK) {
2012-03-08 16:21:22 +01:00
return NGX_ERROR;
}
ngx_rtmp_amf0_reverse_copy(&len, buf, 2);
2012-03-08 16:21:22 +01:00
if (data == NULL) {
rc = ngx_rtmp_amf0_get(ctx, data, len);
2012-03-08 16:21:22 +01:00
2012-03-08 18:45:10 +01:00
} else if (elts->len <= len) {
rc = ngx_rtmp_amf0_get(ctx, data, elts->len - 1);
2012-03-08 16:21:22 +01:00
if (rc != NGX_OK)
return NGX_ERROR;
2012-03-08 18:45:10 +01:00
((char*)data)[elts->len - 1] = 0;
rc = ngx_rtmp_amf0_get(ctx, NULL, len - elts->len + 1);
2012-03-08 16:21:22 +01:00
} else {
rc = ngx_rtmp_amf0_get(ctx, data, len);
2012-03-08 18:45:10 +01:00
((char*)data)[len] = 0;
2012-03-08 16:21:22 +01:00
}
if (rc != NGX_OK) {
2012-03-08 16:21:22 +01:00
return NGX_ERROR;
}
2012-03-08 16:21:22 +01:00
break;
case NGX_RTMP_AMF0_NULL:
break;
case NGX_RTMP_AMF0_OBJECT:
if (ngx_rtmp_amf0_read_object(ctx, data,
2012-03-08 16:21:22 +01:00
elts ? elts->len / sizeof(ngx_rtmp_amf0_elt_t) : 0
) != NGX_OK)
{
return NGX_ERROR;
}
break;
case NGX_RTMP_AMF0_ARRAY:
if (ngx_rtmp_amf0_read(ctx, data,
2012-03-08 16:21:22 +01:00
elts ? (elts->len / sizeof(ngx_rtmp_amf0_elt_t))
| NGX_RTMP_AMF0_TILL_END_FLAG : 0
) != NGX_OK)
{
return NGX_ERROR;
}
break;
case NGX_RTMP_AMF0_END:
return NGX_OK;
default:
return NGX_ERROR;
}
if (elts) {
2012-03-08 16:21:22 +01:00
++elts;
}
2012-03-08 16:21:22 +01:00
}
return NGX_OK;
}
static ngx_int_t
ngx_rtmp_amf0_write_object(ngx_rtmp_amf0_ctx_t *ctx,
2012-03-08 16:21:22 +01:00
ngx_rtmp_amf0_elt_t *elts, size_t nelts)
{
uint16_t len, len_sb;
2012-03-08 16:21:22 +01:00
size_t n;
char *name;
u_char buf[2];
2012-03-08 16:21:22 +01:00
for(n = 0; n < nelts; ++n) {
name = elts[n].name;
len_sb = len = strlen(name);
2012-03-08 16:21:22 +01:00
if (ngx_rtmp_amf0_put(ctx,
ngx_rtmp_amf0_reverse_copy(buf,
&len, 2), 2) != NGX_OK)
{
return NGX_ERROR;
}
if (ngx_rtmp_amf0_put(ctx, name, len) != NGX_OK) {
2012-03-08 16:21:22 +01:00
return NGX_ERROR;
}
2012-03-08 16:21:22 +01:00
if (ngx_rtmp_amf0_write(ctx, &elts[n], 1) != NGX_OK) {
2012-03-08 16:21:22 +01:00
return NGX_ERROR;
}
2012-03-08 16:21:22 +01:00
}
len = 0;
if (ngx_rtmp_amf0_put(ctx, "\00\00", 2) != NGX_OK) {
2012-03-08 16:21:22 +01:00
return NGX_ERROR;
}
2012-03-08 16:21:22 +01:00
return NGX_OK;
}
ngx_int_t
ngx_rtmp_amf0_write(ngx_rtmp_amf0_ctx_t *ctx,
2012-03-08 16:21:22 +01:00
ngx_rtmp_amf0_elt_t *elts, size_t nelts)
{
size_t n;
uint8_t type;
void *data;
uint16_t len;
u_char buf[8];
2012-03-08 16:21:22 +01:00
for(n = 0; n < nelts; ++n) {
type = elts[n].type;
data = elts[n].data;
len = elts[n].len;
if (ngx_rtmp_amf0_put(ctx, &type, sizeof(type)) != NGX_OK)
2012-03-08 16:21:22 +01:00
return NGX_ERROR;
switch(type) {
case NGX_RTMP_AMF0_NUMBER:
if (ngx_rtmp_amf0_put(ctx,
ngx_rtmp_amf0_reverse_copy(buf,
data, 8), 8) != NGX_OK)
{
2012-03-08 16:21:22 +01:00
return NGX_ERROR;
}
2012-03-08 16:21:22 +01:00
break;
case NGX_RTMP_AMF0_BOOLEAN:
if (ngx_rtmp_amf0_put(ctx, data, 1) != NGX_OK) {
2012-03-08 16:21:22 +01:00
return NGX_ERROR;
}
2012-03-08 16:21:22 +01:00
break;
case NGX_RTMP_AMF0_STRING:
if (ngx_rtmp_amf0_put(ctx,
ngx_rtmp_amf0_reverse_copy(buf,
&len, 2), 2) != NGX_OK)
{
return NGX_ERROR;
}
if (ngx_rtmp_amf0_put(ctx, data, len) != NGX_OK) {
2012-03-08 16:21:22 +01:00
return NGX_ERROR;
}
break;
case NGX_RTMP_AMF0_NULL:
break;
case NGX_RTMP_AMF0_OBJECT:
type = NGX_RTMP_AMF0_END;
if (ngx_rtmp_amf0_write_object(ctx, data,
2012-03-08 16:21:22 +01:00
elts[n].len / sizeof(ngx_rtmp_amf0_elt_t)) != NGX_OK
|| ngx_rtmp_amf0_put(ctx, &type,
2012-03-08 16:21:22 +01:00
sizeof(type)) != NGX_OK)
{
return NGX_ERROR;
}
break;
case NGX_RTMP_AMF0_ARRAY:
type = NGX_RTMP_AMF0_END;
if (ngx_rtmp_amf0_write(ctx, data,
2012-03-08 16:21:22 +01:00
elts[n].len / sizeof(ngx_rtmp_amf0_elt_t)) != NGX_OK
|| ngx_rtmp_amf0_put(ctx, &type,
2012-03-08 16:21:22 +01:00
sizeof(type)) != NGX_OK)
{
return NGX_ERROR;
}
break;
case NGX_RTMP_AMF0_END:
return NGX_OK;
default:
return NGX_ERROR;
}
}
return NGX_OK;
}