Ejemplo n.º 1
0
Archivo: sdes.c Proyecto: soramimi/qSIP
/**
 * Decode SDES items from a buffer
 *
 * @param mb   Buffer to decode from
 * @param sdes RTCP SDES to decode into
 *
 * @return 0 if success, otherwise errorcode
 */
int rtcp_sdes_decode(struct mbuf *mb, struct rtcp_sdes *sdes)
{
	size_t start;

	if (!sdes)
		return EINVAL;
	if (mbuf_get_left(mb) < RTCP_SRC_SIZE)
		return EBADMSG;

	start = mb->pos;
	sdes->src = ntohl(mbuf_read_u32(mb));

	/* Decode all SDES items */
	while (mbuf_get_left(mb) >= RTCP_SDES_MIN_SIZE) {
		uint8_t type;
		struct rtcp_sdes_item *item;

		type = mbuf_read_u8(mb);
		if (type == RTCP_SDES_END)
			break;

		if (mbuf_get_left(mb) < 1)
			return EBADMSG;

		if (!sdes->itemv) {
			sdes->itemv = mem_alloc(sizeof(*sdes->itemv), NULL);
			if (!sdes->itemv)
				return ENOMEM;
		}
		else {
			const size_t sz = (sdes->n + 1) * sizeof(*sdes->itemv);
			struct rtcp_sdes_item *itemv;

			itemv = mem_realloc(sdes->itemv, sz);
			if (!itemv)
				return ENOMEM;

			sdes->itemv = itemv;
		}

		item = &sdes->itemv[sdes->n];

		item->type = (enum rtcp_sdes_type)type;
		item->length = mbuf_read_u8(mb);
		if (mbuf_get_left(mb) < item->length)
			return EBADMSG;
		item->data = mem_alloc(item->length, NULL);
		if (!item->data)
			return ENOMEM;
		(void)mbuf_read_mem(mb, (uint8_t *)item->data, item->length);

		sdes->n++;
	}

	/* slurp padding */
	while ((mb->pos - start) & 0x3 && mbuf_get_left(mb))
		++mb->pos;

	return 0;
}
Ejemplo n.º 2
0
Archivo: cons.c Proyecto: GGGO/baresip
static void udp_recv(const struct sa *src, struct mbuf *mb, void *arg)
{
	struct ui_st *st = arg;
	struct mbuf *mbr = mbuf_alloc(64);
	struct re_printf pf;

	st->udp_peer = *src;

	pf.vph = print_handler;
	pf.arg = mbr;

	while (mbuf_get_left(mb)) {
		char ch = mbuf_read_u8(mb);

		if (ch == '\r')
			ch = '\n';

		ui_input_key(ch, &pf);
	}

	if (mbr->end > 0) {
		mbr->pos = 0;
		(void)udp_send(st->us, src, mbr);
	}

	mem_deref(mbr);
}
Ejemplo n.º 3
0
Archivo: pkt.c Proyecto: kaija/libre
/**
 * 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;
}
Ejemplo n.º 4
0
/**
 * Read a string from a memory buffer
 *
 * @param mb   Memory buffer
 * @param str  Buffer to read string to
 * @param size Size of buffer
 *
 * @return 0 if success, otherwise errorcode
 */
int mbuf_read_str(struct mbuf *mb, char *str, size_t size)
{
	if (!mb || !str)
		return EINVAL;

	while (size--) {
		const uint8_t c = mbuf_read_u8(mb);
		*str++ = c;
		if ('\0' == c)
			break;
	}

	return 0;
}
Ejemplo n.º 5
0
Archivo: cstr.c Proyecto: soramimi/qSIP
/**
 * Decode a DNS character string from a memory buffer
 *
 * @param mb  Memory buffer to decode from
 * @param str Pointer to allocated character string
 *
 * @return 0 if success, otherwise errorcode
 */
int dns_cstr_decode(struct mbuf *mb, char **str)
{
	uint8_t len;

	if (!mb || !str || (mbuf_get_left(mb) < 1))
		return EINVAL;

	len = mbuf_read_u8(mb);

	if (mbuf_get_left(mb) < len)
		return EBADMSG;

	return mbuf_strdup(mb, str, len);
}
Ejemplo n.º 6
0
static inline int fu_decode(struct fu *fu, struct mbuf *mb)
{
	uint8_t v;

	if (mbuf_get_left(mb) < 1)
		return EBADMSG;

	v = mbuf_read_u8(mb);

	fu->s    = v>>7 & 0x1;
	fu->e    = v>>6 & 0x1;
	fu->type = v>>0 & 0x3f;

	return 0;
}
Ejemplo n.º 7
0
static void tcp_recv_handler(struct mbuf *mb, void *arg)
{
	struct ui_st *st = arg;
	struct re_printf pf;

	pf.vph = tcp_write_handler;
	pf.arg = st->tc;

	while (mbuf_get_left(mb) > 0) {

		const char key = mbuf_read_u8(mb);

		st->h(key, &pf, st->arg);
	}
}
Ejemplo n.º 8
0
/* src=NULL means lost packet */
static int decode(struct aucodec_st *st, struct mbuf *dst, struct mbuf *src)
{
	uint16_t lengthv[MAX_FRAMES];
	uint16_t total_length = 0;
	uint32_t i, n;
	int err = 0;

	DEBUG_INFO("decode %u bytes\n", mbuf_get_left(src));

	if (st->low_overhead) {
		/* No length bytes */
		for (i=0; i<st->bpfn && !err; i++) {
			err = decode_frame(st, dst, src, st->bpfv[i]);
		}
	}
	else {
		bool done = false;

		/* Read the length bytes */
		for (i=0; i<ARRAY_SIZE(lengthv) && !done; i++) {
			uint8_t byte;

			if (mbuf_get_left(src) < 1)
				return EPROTO;

			/* Decode length */
			lengthv[i] = 0;
			do {
				byte = mbuf_read_u8(src);
				lengthv[i] += byte;
			}
			while (byte == 0xff);

			total_length += lengthv[i];

			if (total_length >= mbuf_get_left(src))
				done = true;
		}
		n = i;
		DEBUG_INFO("decoded %d frames\n", n);

		for (i=0; i<n && !err; i++) {
			err = decode_frame(st, dst, src, lengthv[i]);
		}
	}

	return err;
}
Ejemplo n.º 9
0
static void udp_recv(const struct sa *src, struct mbuf *mb, void *arg)
{
	struct ui_st *st = arg;
	struct mbuf *mbr = mbuf_alloc(64);
	struct re_printf pf;

	pf.vph = print_handler;
	pf.arg = mbr;

	while (mbuf_get_left(mb))
		st->h(mbuf_read_u8(mb), &pf, st->arg);

	mbr->pos = 0;
	(void)udp_send(st->us, src, mbr);

	mem_deref(mbr);
}
Ejemplo n.º 10
0
Archivo: cons.c Proyecto: GGGO/baresip
static void tcp_recv_handler(struct mbuf *mb, void *arg)
{
	struct ui_st *st = arg;
	struct re_printf pf;

	pf.vph = tcp_write_handler;
	pf.arg = st->tc;

	while (mbuf_get_left(mb) > 0) {

		char ch = mbuf_read_u8(mb);

		if (ch == '\r')
			ch = '\n';

		ui_input_key(ch, &pf);
	}
}
Ejemplo n.º 11
0
int stun_attr_decode(struct stun_attr **attrp, struct mbuf *mb,
		     const uint8_t *tid, struct stun_unknown_attr *ua)
{
	struct stun_attr *attr;
	size_t start, len;
	uint32_t i, n;
	int err = 0;

	if (!mb || !attrp)
		return EINVAL;

	if (mbuf_get_left(mb) < 4)
		return EBADMSG;

	attr = mem_zalloc(sizeof(*attr), destructor);
	if (!attr)
		return ENOMEM;

	attr->type = htons(mbuf_read_u16(mb));
	len = htons(mbuf_read_u16(mb));

	if (mbuf_get_left(mb) < len)
		goto badmsg;

	start = mb->pos;

	switch (attr->type) {

	case STUN_ATTR_MAPPED_ADDR:
	case STUN_ATTR_ALT_SERVER:
	case STUN_ATTR_RESP_ORIGIN:
	case STUN_ATTR_OTHER_ADDR:
		tid = NULL;
		/*@fallthrough@*/
	case STUN_ATTR_XOR_PEER_ADDR:
	case STUN_ATTR_XOR_RELAY_ADDR:
	case STUN_ATTR_XOR_MAPPED_ADDR:
		err = stun_addr_decode(mb, &attr->v.sa, tid);
		break;

	case STUN_ATTR_CHANGE_REQ:
		if (len != 4)
			goto badmsg;

		n = ntohl(mbuf_read_u32(mb));
		attr->v.change_req.ip   = (n >> 2) & 0x1;
		attr->v.change_req.port = (n >> 1) & 0x1;
		break;

	case STUN_ATTR_USERNAME:
	case STUN_ATTR_REALM:
	case STUN_ATTR_NONCE:
	case STUN_ATTR_SOFTWARE:
		err = str_decode(mb, &attr->v.str, len);
		break;

	case STUN_ATTR_MSG_INTEGRITY:
		if (len != 20)
			goto badmsg;

		err = mbuf_read_mem(mb, attr->v.msg_integrity, 20);
		break;

	case STUN_ATTR_ERR_CODE:
		if (len < 4)
			goto badmsg;

		mb->pos += 2;
		attr->v.err_code.code  = (mbuf_read_u8(mb) & 0x7) * 100;
		attr->v.err_code.code += mbuf_read_u8(mb);
		err = str_decode(mb, &attr->v.err_code.reason, len - 4);
		break;

	case STUN_ATTR_UNKNOWN_ATTR:
		for (i=0; i<len/2; i++) {
			uint16_t type = ntohs(mbuf_read_u16(mb));

			if (i >= ARRAY_SIZE(attr->v.unknown_attr.typev))
				continue;

			attr->v.unknown_attr.typev[i] = type;
			attr->v.unknown_attr.typec++;
		}
		break;

	case STUN_ATTR_CHANNEL_NUMBER:
	case STUN_ATTR_RESP_PORT:
		if (len < 2)
			goto badmsg;

	        attr->v.uint16 = ntohs(mbuf_read_u16(mb));
		break;

	case STUN_ATTR_LIFETIME:
	case STUN_ATTR_PRIORITY:
	case STUN_ATTR_FINGERPRINT:
		if (len != 4)
			goto badmsg;

	        attr->v.uint32 = ntohl(mbuf_read_u32(mb));
		break;

	case STUN_ATTR_DATA:
	case STUN_ATTR_PADDING:
		attr->v.mb.buf  = mem_ref(mb->buf);
		attr->v.mb.size = mb->size;
		attr->v.mb.pos  = mb->pos;
		attr->v.mb.end  = mb->pos + len;
		mb->pos += len;
		break;

	case STUN_ATTR_REQ_ADDR_FAMILY:
	case STUN_ATTR_REQ_TRANSPORT:
		if (len < 1)
			goto badmsg;

	        attr->v.uint8 = mbuf_read_u8(mb);
		break;

	case STUN_ATTR_EVEN_PORT:
		if (len < 1)
			goto badmsg;

		attr->v.even_port.r = (mbuf_read_u8(mb) >> 7) & 0x1;
		break;

	case STUN_ATTR_DONT_FRAGMENT:
	case STUN_ATTR_USE_CAND:
		if (len > 0)
			goto badmsg;

		/* no value */
		break;

	case STUN_ATTR_RSV_TOKEN:
	case STUN_ATTR_CONTROLLING:
	case STUN_ATTR_CONTROLLED:
		if (len != 8)
			goto badmsg;

	        attr->v.uint64 = sys_ntohll(mbuf_read_u64(mb));
		break;

	default:
		mb->pos += len;

		if (attr->type >= 0x8000)
			break;

		if (ua && ua->typec < ARRAY_SIZE(ua->typev))
			ua->typev[ua->typec++] = attr->type;
		break;
	}

	if (err)
		goto error;

	/* padding */
	while (((mb->pos - start) & 0x03) && mbuf_get_left(mb))
		++mb->pos;

	*attrp = attr;

	return 0;

 badmsg:
	err = EBADMSG;
 error:
	mem_deref(attr);

	return err;
}
Ejemplo n.º 12
0
int decode_h263(struct viddec_state *st, struct vidframe *frame,
		bool marker, uint16_t seq, struct mbuf *src)
{
	struct h263_hdr hdr;
	int err;

	if (!st || !frame)
		return EINVAL;

	if (!src)
		return 0;

	(void)seq;

	err = h263_hdr_decode(&hdr, src);
	if (err)
		return err;

#if 0
	debug(".....[%s seq=%5u ] MODE %s -"
	      " SBIT=%u EBIT=%u I=%s"
	      " (%5u/%5u bytes)\n",
	      marker ? "M" : " ", seq,
	      h263_hdr_mode(&hdr) == H263_MODE_A ? "A" : "B",
	      hdr.sbit, hdr.ebit, hdr.i ? "Inter" : "Intra",
	      mbuf_get_left(src), st->mb->end);
#endif

	if (!hdr.i)
		st->got_keyframe = true;

#if 0
	if (st->mb->pos == 0) {
		uint8_t *p = mbuf_buf(src);

		if (p[0] != 0x00 || p[1] != 0x00) {
			warning("invalid PSC detected (%02x %02x)\n",
				p[0], p[1]);
			return EPROTO;
		}
	}
#endif

	/*
	 * The H.263 Bit-stream can be fragmented on bit-level,
	 * indicated by SBIT and EBIT. Example:
	 *
	 *               8 bit  2 bit
	 *            .--------.--.
	 * Packet 1   |        |  |
	 * SBIT=0     '--------'--'
	 * EBIT=6
	 *                        .------.--------.--------.
	 * Packet 2               |      |        |        |
	 * SBIT=2                 '------'--------'--------'
	 * EBIT=0                   6bit    8bit     8bit
	 *
	 */

	if (hdr.sbit > 0) {
		const uint8_t mask  = (1 << (8 - hdr.sbit)) - 1;
		const uint8_t sbyte = mbuf_read_u8(src) & mask;

		st->mb->buf[st->mb->end - 1] |= sbyte;
	}

	return ffdecode(st, frame, marker, src);
}
Ejemplo n.º 13
0
static void recv_handler(struct mbuf *mb, void *arg)
{
	struct rst *rst = arg;
	size_t n;

	if (!rst->head_recv) {

		struct pl hdr, name, metaint, eoh;

		if (rst->mb) {
			size_t pos;
			int err;

			pos = rst->mb->pos;

			rst->mb->pos = rst->mb->end;

			err = mbuf_write_mem(rst->mb, mbuf_buf(mb),
					     mbuf_get_left(mb));
			if (err) {
				re_printf("rst: buffer write error: %m\n",
					  err);
				rst->tc = mem_deref(rst->tc);
				tmr_start(&rst->tmr, RETRY_WAIT,
					  reconnect, rst);
				return;
			}

			rst->mb->pos = pos;
		}
		else {
			rst->mb = mem_ref(mb);
		}

		if (re_regex((const char *)mbuf_buf(rst->mb),
			     mbuf_get_left(rst->mb),
			     "[^\r\n]1\r\n\r\n", &eoh))
			return;

		rst->head_recv = true;

		hdr.p = (const char *)mbuf_buf(rst->mb);
		hdr.l = eoh.p + 5 - hdr.p;

		if (!re_regex(hdr.p, hdr.l, "icy-name:[ \t]*[^\r\n]+\r\n",
			      NULL, &name))
			(void)pl_strdup(&rst->name, &name);

		if (!re_regex(hdr.p, hdr.l, "icy-metaint:[ \t]*[0-9]+\r\n",
			      NULL, &metaint))
			rst->metaint = pl_u32(&metaint);

		if (rst->metaint == 0) {
			re_printf("rst: icy meta interval not available\n");
			rst->tc = mem_deref(rst->tc);
			tmr_start(&rst->tmr, RETRY_WAIT, reconnect, rst);
			return;
		}

		rst_video_update(rst->vidsrc_st, rst->name, NULL);

		rst->mb->pos += hdr.l;

		re_printf("rst: name='%s' metaint=%zu\n",
			  rst->name, rst->metaint);

		if (rst->mb->pos >= rst->mb->end)
			return;

		mb = rst->mb;
	}

	while (mb->pos < mb->end) {

		if (rst->metasz > 0) {

			n = min(mbuf_get_left(mb), rst->metasz - rst->bytec);

			if (rst->meta)
				mbuf_read_mem(mb,
					     (uint8_t *)&rst->meta[rst->bytec],
					      n);
			else
				mb->pos += n;

			rst->bytec += n;
#if 0
			re_printf("rst: metadata %zu bytes\n", n);
#endif
			if (rst->bytec >= rst->metasz) {
#if 0
				re_printf("rst: metadata: [%s]\n", rst->meta);
#endif
				rst->metasz = 0;
				rst->bytec  = 0;

				rst_video_update(rst->vidsrc_st, rst->name,
						 rst->meta);
			}
		}
		else if (rst->bytec < rst->metaint) {

			n = min(mbuf_get_left(mb), rst->metaint - rst->bytec);

			rst_audio_feed(rst->ausrc_st, mbuf_buf(mb), n);

			rst->bytec += n;
			mb->pos    += n;
#if 0
			re_printf("rst: mp3data %zu bytes\n", n);
#endif
		}
		else {
			rst->metasz = mbuf_read_u8(mb) * 16;
			rst->bytec  = 0;

			rst->meta = mem_deref(rst->meta);
			rst->meta = mem_zalloc(rst->metasz + 1, NULL);
#if 0
			re_printf("rst: metalength %zu bytes\n", rst->metasz);
#endif
		}
	}
}
Ejemplo n.º 14
0
Archivo: option.c Proyecto: alfredh/rew
int pcp_option_decode(struct pcp_option **optp, struct mbuf *mb)
{
	struct pcp_option *opt;
	size_t start, len;
	uint16_t port;
	int err = 0;

	if (!optp || !mb)
		return EINVAL;

	if (mbuf_get_left(mb) < 4)
		return EBADMSG;

	opt = mem_zalloc(sizeof(*opt), destructor);
	if (!opt)
		return ENOMEM;

	opt->code = mbuf_read_u8(mb);
	(void)mbuf_read_u8(mb);
	len = ntohs(mbuf_read_u16(mb));

	if (mbuf_get_left(mb) < len)
		goto badmsg;

	start = mb->pos;

	switch (opt->code) {

	case PCP_OPTION_THIRD_PARTY:
		if (len < 16)
			goto badmsg;
		err = pcp_ipaddr_decode(mb, &opt->u.third_party);
		break;

	case PCP_OPTION_PREFER_FAILURE:
		/* no payload */
		break;

	case PCP_OPTION_FILTER:
		if (len < 20)
			goto badmsg;
		(void)mbuf_read_u8(mb);
		opt->u.filter.prefix_length = mbuf_read_u8(mb);
		port = ntohs(mbuf_read_u16(mb));
		err = pcp_ipaddr_decode(mb, &opt->u.filter.remote_peer);
		sa_set_port(&opt->u.filter.remote_peer, port);
		break;

	case PCP_OPTION_DESCRIPTION:
		err = mbuf_strdup(mb, &opt->u.description, len);
		break;

	default:
		mb->pos += len;

		(void)re_printf("pcp: ignore option code %d (len=%zu)\n",
				opt->code, len);
		break;
	}

	if (err)
		goto error;

	/* padding */
	while (((mb->pos - start) & 0x03) && mbuf_get_left(mb))
		++mb->pos;

	*optp = opt;

	return 0;

 badmsg:
	err = EBADMSG;
 error:
	mem_deref(opt);

	return err;
}
Ejemplo n.º 15
0
Archivo: pkt.c Proyecto: kaija/libre
/**
 * 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;
		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;
		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;
}