/**
 * @file dtls_srtp/srtp.c Secure RTP
 *
 * Copyright (C) 2010 Alfred E. Heggestad
 */

#include <re.h>
#include <baresip.h>
#include "dtls_srtp.h"


struct srtp_stream {
	struct srtp *srtp;
};


/*
 * See RFC 5764 figure 3:
 *
 *                  +----------------+
 *                  | 127 < B < 192 -+--> forward to RTP
 *                  |                |
 *      packet -->  |  19 < B < 64  -+--> forward to DTLS
 *                  |                |
 *                  |       B < 2   -+--> forward to STUN
 *                  +----------------+
 *
 */
static inline bool is_rtp_or_rtcp(const struct mbuf *mb)
{
	uint8_t b;

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

	b = mbuf_buf(mb)[0];

	return 127 < b && b < 192;
}


static inline bool is_rtcp_packet(const struct mbuf *mb)
{
	uint8_t pt;

	if (mbuf_get_left(mb) < 2)
		return false;

	pt = mbuf_buf(mb)[1] & 0x7f;

	return rtp_pt_is_rtcp(pt);
}


static bool is_dtls_packet(const struct mbuf *mb)
{
	uint8_t b;

	if (mbuf_get_left(mb) < 13)
		return false;

	b = mbuf_buf(mb)[0];

	return 19 < b && b < 64;
}


static void destructor(void *arg)
{
	struct srtp_stream *s = arg;

	mem_deref(s->srtp);
}


static bool send_handler(int *err, struct sa *dst, struct mbuf *mb, void *arg)
{
	struct comp *comp = arg;
	(void)dst;

	if (!is_rtp_or_rtcp(mb))
		return false;

	if (is_rtcp_packet(mb)) {
		*err = srtcp_encrypt(comp->tx->srtp, mb);
		if (*err) {
			warning("srtp: srtcp_encrypt failed (%m)\n", *err);
		}
	}
	else {
		*err = srtp_encrypt(comp->tx->srtp, mb);
		if (*err) {
			warning("srtp: srtp_encrypt failed (%m)\n", *err);
		}
	}


	return *err ? true : false;  /* continue processing */
}


static bool recv_handler(struct sa *src, struct mbuf *mb, void *arg)
{
	struct comp *comp = arg;
	int err;
	(void)src;

	if (is_dtls_packet(mb)) {
		info("srtp: received DTLS packet on SRTP socket\n");
	}

	if (!is_rtp_or_rtcp(mb))
		return false;

	if (is_rtcp_packet(mb)) {
		err = srtcp_decrypt(comp->rx->srtp, mb);
	}
	else {
		err = srtp_decrypt(comp->rx->srtp, mb);
	}

	if (err) {
		warning("srtp: recv: failed to decrypt %s-packet (%m)\n",
			is_rtcp_packet(mb) ? "RTCP" : "RTP", err);
		return true;   /* error - drop packet */
	}

	return false;  /* continue processing */
}


int srtp_stream_add(struct srtp_stream **sp, enum srtp_suite suite,
		    const uint8_t *key, size_t key_size, bool tx)
{
	struct srtp_stream *s;
	int err = 0;
	(void)tx;

	if (!sp || !key)
		return EINVAL;

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

	err = srtp_alloc(&s->srtp, suite, key, key_size, 0);
	if (err) {
		warning("srtp: srtp_alloc() failed (%m)\n", err);
		goto out;
	}

 out:
	if (err)
		mem_deref(s);
	else
		*sp = s;

	return err;
}


int srtp_install(struct comp *comp)
{
	return udp_register_helper(&comp->uh_srtp, comp->app_sock,
				   LAYER_SRTP,
				   send_handler, recv_handler, comp);
}
