blob: cd4880d2d5d6042571bbd65c0a3ca730b72cb485 [file] [log] [blame]
/**
* @file pkt.c RTCP Packet handling
*
* 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_list.h>
#include <re_sys.h>
#include <re_sa.h>
#include <re_rtp.h>
#include "rtcp.h"
#define DEBUG_MODULE "rtcp_pkt"
#define DEBUG_LEVEL 5
#include <re_dbg.h>
static void rtcp_destructor(void *data)
{
struct rtcp_msg *msg = data;
size_t i, j;
switch (msg->hdr.pt) {
case RTCP_SR:
mem_deref(msg->r.sr.rrv);
break;
case RTCP_RR:
mem_deref(msg->r.rr.rrv);
break;
case RTCP_SDES:
if (!msg->r.sdesv)
break;
for (i=0; i<msg->hdr.count; i++) {
struct rtcp_sdes *sdes = &msg->r.sdesv[i];
for (j=0; j<sdes->n; j++) {
mem_deref(sdes->itemv[j].data);
}
mem_deref(sdes->itemv);
}
mem_deref(msg->r.sdesv);
break;
case RTCP_BYE:
mem_deref(msg->r.bye.srcv);
mem_deref(msg->r.bye.reason);
break;
case RTCP_APP:
mem_deref(msg->r.app.data);
break;
case RTCP_RTPFB:
case RTCP_PSFB:
mem_deref(msg->r.fb.fci.p);
break;
default:
/* nothing allocated */
break;
}
}
/**
* Encode the RTCP Header
*
* @param mb Buffer to encode into
* @param count Number of sub-elemements
* @param type RTCP Packet type
* @param length Packet length in words
*
* @return 0 for success, otherwise errorcode
*/
int rtcp_hdr_encode(struct mbuf *mb, uint8_t count, enum rtcp_type type,
uint16_t length)
{
int err;
if (!mb)
return EINVAL;
err = mbuf_write_u8(mb, RTCP_VERSION<<6 | count);
err |= mbuf_write_u8(mb, type);
err |= mbuf_write_u16(mb, htons(length));
return err;
}
/**
* Decode the RTCP Header
*
* @param mb Buffer to decode from
* @param hdr RTCP Header to decode into
*
* @return 0 for success, otherwise errorcode
*/
int rtcp_hdr_decode(struct mbuf *mb, struct rtcp_hdr *hdr)
{
uint8_t b;
if (!hdr)
return EINVAL;
if (mbuf_get_left(mb) < RTCP_HDR_SIZE)
return EBADMSG;
b = mbuf_read_u8(mb);
hdr->pt = mbuf_read_u8(mb);
hdr->length = ntohs(mbuf_read_u16(mb));
hdr->version = (b >> 6) & 0x3;
hdr->p = (b >> 5) & 0x1;
hdr->count = (b >> 0) & 0x1f;
return 0;
}
int rtcp_vencode(struct mbuf *mb, enum rtcp_type type, uint32_t count,
va_list ap)
{
size_t i, pos;
uint16_t len;
const uint8_t *data;
size_t data_len;
const uint32_t *srcv;
const char *reason;
rtcp_encode_h *ench;
void *arg;
int err = 0;
if (!mb)
return EINVAL;
pos = mb->pos;
/* Skip header - encoded last */
mb->pos = mb->end = (mb->pos + RTCP_HDR_SIZE);
switch (type) {
case RTCP_SR:
for (i=0; i<6; i++)
err |= mbuf_write_u32(mb, htonl(va_arg(ap, uint32_t)));
ench = va_arg(ap, rtcp_encode_h *);
arg = va_arg(ap, void *);
if (ench)
err |= ench(mb, arg);
break;
case RTCP_RR:
err = mbuf_write_u32(mb, htonl(va_arg(ap, uint32_t)));
ench = va_arg(ap, rtcp_encode_h *);
arg = va_arg(ap, void *);
if (ench)
err |= ench(mb, arg);
break;
case RTCP_SDES:
ench = va_arg(ap, rtcp_encode_h *);
arg = va_arg(ap, void *);
if (ench)
err |= ench(mb, arg);
break;
case RTCP_BYE:
srcv = va_arg(ap, uint32_t *);
reason = va_arg(ap, char *);
for (i=0; i<count && !err; i++) {
err = mbuf_write_u32(mb, htonl(srcv[i]));
}
if (reason) {
err |= mbuf_write_u8(mb, strlen(reason));
err |= mbuf_write_str(mb, reason);
}
break;
case RTCP_APP:
err = mbuf_write_u32(mb, htonl(va_arg(ap, uint32_t)));
err |= mbuf_write_mem(mb, va_arg(ap, uint8_t *), 4);
data = va_arg(ap, const uint8_t *);
data_len = va_arg(ap, size_t);
if (data) {
if (data_len % 4) {
DEBUG_WARNING("not a multiple of 32bits\n");
return EBADMSG;
}
err |= mbuf_write_mem(mb, data, data_len);
}
break;
case RTCP_FIR:
err = mbuf_write_u32(mb, htonl(va_arg(ap, uint32_t)));
break;
case RTCP_NACK:
err = mbuf_write_u32(mb, htonl(va_arg(ap, uint32_t)));
err |= mbuf_write_u16(mb, htons(va_arg(ap, uint32_t)));
err |= mbuf_write_u16(mb, htons(va_arg(ap, uint32_t)));
break;
case RTCP_RTPFB:
case RTCP_PSFB:
err = mbuf_write_u32(mb, htonl(va_arg(ap, uint32_t)));
err |= mbuf_write_u32(mb, htonl(va_arg(ap, uint32_t)));
ench = va_arg(ap, rtcp_encode_h *);
arg = va_arg(ap, void *);
if (ench)
err |= ench(mb, arg);
break;
default:
return EINVAL;
}
if (err)
return err;
/* padding to 32 bits */
while ((mb->end - pos) & 0x3)
err |= mbuf_write_u8(mb, 0x00);
if (err)
return err;
/* Encode RTCP Header */
mb->pos = pos;
len = (mb->end - pos - RTCP_HDR_SIZE)/sizeof(uint32_t);
err = rtcp_hdr_encode(mb, count, type, len);
if (err)
return err;
mb->pos = mb->end;
return 0;
}
/**
* Encode an RTCP Packet into a buffer
*
* @param mb Buffer to encode into
* @param type RTCP Packet type
* @param count Packet-specific count
*
* @return 0 for success, otherwise errorcode
*/
int rtcp_encode(struct mbuf *mb, enum rtcp_type type, uint32_t count, ...)
{
va_list ap;
int err;
va_start(ap, count);
err = rtcp_vencode(mb, type, count, ap);
va_end(ap);
return err;
}
/**
* Decode one RTCP message from a buffer
*
* @param msgp Pointer to allocated RTCP Message
* @param mb Buffer to decode from
*
* @return 0 for success, otherwise errorcode
*/
int rtcp_decode(struct rtcp_msg **msgp, struct mbuf *mb)
{
struct rtcp_msg *msg = NULL;
size_t start, i, sz, count, rem;
int err;
if (!msgp)
return EINVAL;
if (mbuf_get_left(mb) < RTCP_HDR_SIZE)
return EBADMSG;
msg = mem_zalloc(sizeof(*msg), rtcp_destructor);
if (!msg)
return ENOMEM;
start = mb->pos;
/* decode and check header */
err = rtcp_hdr_decode(mb, &msg->hdr);
if (err)
goto out;
if (msg->hdr.version != RTCP_VERSION)
goto badmsg;
/* check length and remaining */
rem = msg->hdr.length * sizeof(uint32_t);
if (mbuf_get_left(mb) < rem)
goto badmsg;
count = msg->hdr.count;
switch (msg->hdr.pt) {
case RTCP_SR:
if (mbuf_get_left(mb) < (RTCP_SRC_SIZE + RTCP_SR_SIZE))
goto badmsg;
msg->r.sr.ssrc = ntohl(mbuf_read_u32(mb));
msg->r.sr.ntp_sec = ntohl(mbuf_read_u32(mb));
msg->r.sr.ntp_frac = ntohl(mbuf_read_u32(mb));
msg->r.sr.rtp_ts = ntohl(mbuf_read_u32(mb));
msg->r.sr.psent = ntohl(mbuf_read_u32(mb));
msg->r.sr.osent = ntohl(mbuf_read_u32(mb));
err = rtcp_rr_alloc(&msg->r.sr.rrv, count);
if (err)
goto out;
for (i=0; i<count && !err; i++)
err = rtcp_rr_decode(mb, &msg->r.sr.rrv[i]);
break;
case RTCP_RR:
if (mbuf_get_left(mb) < RTCP_SRC_SIZE)
goto badmsg;
msg->r.rr.ssrc = ntohl(mbuf_read_u32(mb));
err = rtcp_rr_alloc(&msg->r.rr.rrv, count);
if (err)
goto out;
for (i=0; i<count && !err; i++)
err = rtcp_rr_decode(mb, &msg->r.rr.rrv[i]);
break;
case RTCP_SDES:
if (count == 0)
break;
sz = count * sizeof(*msg->r.sdesv);
msg->r.sdesv = mem_zalloc(sz, NULL);
if (!msg->r.sdesv) {
err = ENOMEM;
goto out;
}
for (i=0; i<msg->hdr.count && !err; i++)
err = rtcp_sdes_decode(mb, &msg->r.sdesv[i]);
break;
case RTCP_BYE:
sz = count * sizeof(*msg->r.bye.srcv);
msg->r.bye.srcv = mem_alloc(sz, NULL);
if (!msg->r.bye.srcv) {
err = ENOMEM;
goto out;
}
if (mbuf_get_left(mb) < sz)
goto badmsg;
for (i=0; i<count; i++)
msg->r.bye.srcv[i] = ntohl(mbuf_read_u32(mb));
/* decode reason (optional) */
if (rem > count*sizeof(uint32_t)) {
const size_t len = mbuf_read_u8(mb);
if (mbuf_get_left(mb) < len)
goto badmsg;
err = mbuf_strdup(mb, &msg->r.bye.reason, len);
}
break;
case RTCP_APP:
if (mbuf_get_left(mb) < RTCP_APP_SIZE)
goto badmsg;
msg->r.app.src = ntohl(mbuf_read_u32(mb));
(void)mbuf_read_mem(mb, (uint8_t *)msg->r.app.name,
sizeof(msg->r.app.name));
if (rem > RTCP_APP_SIZE) {
msg->r.app.data_len = rem - RTCP_APP_SIZE;
msg->r.app.data = mem_alloc(msg->r.app.data_len, NULL);
if (!msg->r.app.data) {
err = ENOMEM;
goto out;
}
if (mbuf_get_left(mb) < msg->r.app.data_len)
goto badmsg;
(void)mbuf_read_mem(mb, msg->r.app.data,
msg->r.app.data_len);
}
break;
case RTCP_FIR:
if (mbuf_get_left(mb) < RTCP_FIR_SIZE)
goto badmsg;
msg->r.fir.ssrc = ntohl(mbuf_read_u32(mb));
break;
case RTCP_NACK:
if (mbuf_get_left(mb) < RTCP_NACK_SIZE)
goto badmsg;
msg->r.nack.ssrc = ntohl(mbuf_read_u32(mb));
msg->r.nack.fsn = ntohs(mbuf_read_u16(mb));
msg->r.nack.blp = ntohs(mbuf_read_u16(mb));
break;
case RTCP_RTPFB:
if (mbuf_get_left(mb) < RTCP_FB_SIZE)
goto badmsg;
if (msg->hdr.length < 2)
goto badmsg;
msg->r.fb.ssrc_packet = ntohl(mbuf_read_u32(mb));
msg->r.fb.ssrc_media = ntohl(mbuf_read_u32(mb));
msg->r.fb.n = msg->hdr.length - 2;
err = rtcp_rtpfb_decode(mb, msg);
break;
case RTCP_PSFB:
if (mbuf_get_left(mb) < RTCP_FB_SIZE)
goto badmsg;
if (msg->hdr.length < 2)
goto badmsg;
msg->r.fb.ssrc_packet = ntohl(mbuf_read_u32(mb));
msg->r.fb.ssrc_media = ntohl(mbuf_read_u32(mb));
msg->r.fb.n = msg->hdr.length - 2;
err = rtcp_psfb_decode(mb, msg);
break;
default:
/* unknown message type */
mbuf_advance(mb, rem);
break;
}
if (err)
goto out;
/* slurp padding */
while ((mb->pos - start) & 0x3 && mbuf_get_left(mb))
++mb->pos;
out:
if (err)
mem_deref(msg);
else
*msgp = msg;
return err;
badmsg:
mem_deref(msg);
return EBADMSG;
}