Squashed 'third_party/rawrtc/re/' content from commit f3163ce8b

Change-Id: I6a235e6ac0f03269d951026f9d195da05c40fdab
git-subtree-dir: third_party/rawrtc/re
git-subtree-split: f3163ce8b526a13b35ef71ce4dd6f43585064d8a
diff --git a/src/sip/strans.c b/src/sip/strans.c
new file mode 100644
index 0000000..13a875c
--- /dev/null
+++ b/src/sip/strans.c
@@ -0,0 +1,455 @@
+/**
+ * @file strans.c  SIP Server Transaction
+ *
+ * Copyright (C) 2010 Creytiv.com
+ */
+#include <re_types.h>
+#include <re_mem.h>
+#include <re_mbuf.h>
+#include <re_sa.h>
+#include <re_list.h>
+#include <re_hash.h>
+#include <re_fmt.h>
+#include <re_uri.h>
+#include <re_sys.h>
+#include <re_tmr.h>
+#include <re_udp.h>
+#include <re_msg.h>
+#include <re_sip.h>
+#include "sip.h"
+
+
+enum state {
+	TRYING,
+	PROCEEDING,
+	ACCEPTED,
+	COMPLETED,
+	CONFIRMED,
+};
+
+
+struct sip_strans {
+	struct le he;
+	struct le he_mrg;
+	struct tmr tmr;
+	struct tmr tmrg;
+	struct sa dst;
+	struct sip *sip;
+	struct sip_msg *msg;
+	struct mbuf *mb;
+	sip_cancel_h *cancelh;
+	void *arg;
+	enum state state;
+	uint32_t txc;
+	bool invite;
+};
+
+
+static void destructor(void *arg)
+{
+	struct sip_strans *st = arg;
+
+	hash_unlink(&st->he);
+	hash_unlink(&st->he_mrg);
+	tmr_cancel(&st->tmr);
+	tmr_cancel(&st->tmrg);
+	mem_deref(st->msg);
+	mem_deref(st->mb);
+}
+
+
+static bool strans_cmp(const struct sip_msg *msg1, const struct sip_msg *msg2)
+{
+	if (pl_cmp(&msg1->via.branch, &msg2->via.branch))
+		return false;
+
+	if (pl_cmp(&msg1->via.sentby, &msg2->via.sentby))
+		return false;
+
+	return true;
+}
+
+
+static bool cmp_handler(struct le *le, void *arg)
+{
+	struct sip_strans *st = le->data;
+	const struct sip_msg *msg = arg;
+
+	if (!strans_cmp(st->msg, msg))
+		return false;
+
+	if (pl_cmp(&st->msg->cseq.met, &msg->cseq.met))
+		return false;
+
+	return true;
+}
+
+
+static bool cmp_ack_handler(struct le *le, void *arg)
+{
+	struct sip_strans *st = le->data;
+	const struct sip_msg *msg = arg;
+
+	if (!strans_cmp(st->msg, msg))
+		return false;
+
+	if (pl_strcmp(&st->msg->cseq.met, "INVITE"))
+		return false;
+
+	return true;
+}
+
+
+static bool cmp_cancel_handler(struct le *le, void *arg)
+{
+	struct sip_strans *st = le->data;
+	const struct sip_msg *msg = arg;
+
+	if (!strans_cmp(st->msg, msg))
+		return false;
+
+	if (!pl_strcmp(&st->msg->cseq.met, "CANCEL"))
+		return false;
+
+	return true;
+}
+
+
+static bool cmp_merge_handler(struct le *le, void *arg)
+{
+	struct sip_strans *st = le->data;
+	const struct sip_msg *msg = arg;
+
+	if (pl_cmp(&st->msg->cseq.met, &msg->cseq.met))
+		return false;
+
+	if (st->msg->cseq.num != msg->cseq.num)
+		return false;
+
+	if (pl_cmp(&st->msg->callid, &msg->callid))
+		return false;
+
+	if (pl_cmp(&st->msg->from.tag, &msg->from.tag))
+		return false;
+
+	if (pl_cmp(&st->msg->ruri, &msg->ruri))
+		return false;
+
+	return true;
+}
+
+
+static void dummy_handler(void *arg)
+{
+	(void)arg;
+}
+
+
+static void tmr_handler(void *arg)
+{
+	struct sip_strans *st = arg;
+
+	mem_deref(st);
+}
+
+
+static void retransmit_handler(void *arg)
+{
+	struct sip_strans *st = arg;
+
+	(void)sip_send(st->sip, st->msg->sock, st->msg->tp, &st->dst,
+		       st->mb);
+
+	st->txc++;
+	tmr_start(&st->tmrg, MIN(SIP_T1<<st->txc, SIP_T2), retransmit_handler,
+		  st);
+}
+
+
+static bool ack_handler(struct sip *sip, const struct sip_msg *msg)
+{
+	struct sip_strans *st;
+
+	st = list_ledata(hash_lookup(sip->ht_strans,
+				     hash_joaat_pl(&msg->via.branch),
+				     cmp_ack_handler, (void *)msg));
+	if (!st)
+		return false;
+
+	switch (st->state) {
+
+	case ACCEPTED:
+		/* make sure ACKs for 2xx are passed to TU */
+		return false;
+
+	case COMPLETED:
+		if (sip_transp_reliable(st->msg->tp)) {
+			mem_deref(st);
+			break;
+		}
+
+		tmr_start(&st->tmr, SIP_T4, tmr_handler, st);
+		tmr_cancel(&st->tmrg);
+		st->state = CONFIRMED;
+		break;
+
+	default:
+		break;
+	}
+
+	return true;
+}
+
+
+static bool cancel_handler(struct sip *sip, const struct sip_msg *msg)
+{
+	struct sip_strans *st;
+
+	st = list_ledata(hash_lookup(sip->ht_strans,
+				     hash_joaat_pl(&msg->via.branch),
+				     cmp_cancel_handler, (void *)msg));
+	if (!st)
+		return false;
+
+	((struct sip_msg *)msg)->tag = st->msg->tag;
+
+	(void)sip_reply(sip, msg, 200, "OK");
+
+	switch (st->state) {
+
+	case TRYING:
+	case PROCEEDING:
+		st->cancelh(st->arg);
+		break;
+
+	default:
+		break;
+	}
+
+	return true;
+}
+
+
+static bool request_handler(const struct sip_msg *msg, void *arg)
+{
+	struct sip_strans *st;
+	struct sip *sip = arg;
+
+	if (!pl_strcmp(&msg->met, "ACK"))
+		return ack_handler(sip, msg);
+
+	st = list_ledata(hash_lookup(sip->ht_strans,
+				     hash_joaat_pl(&msg->via.branch),
+				     cmp_handler, (void *)msg));
+	if (st) {
+		switch (st->state) {
+
+		case PROCEEDING:
+		case COMPLETED:
+			(void)sip_send(st->sip, st->msg->sock, st->msg->tp,
+				       &st->dst, st->mb);
+			break;
+
+		default:
+			break;
+		}
+
+		return true;
+	}
+	else if (!pl_isset(&msg->to.tag)) {
+
+		st = list_ledata(hash_lookup(sip->ht_strans_mrg,
+					     hash_joaat_pl(&msg->callid),
+					     cmp_merge_handler, (void *)msg));
+		if (st) {
+			(void)sip_reply(sip, msg, 482, "Loop Detected");
+			return true;
+		}
+	}
+
+	if (!pl_strcmp(&msg->met, "CANCEL"))
+		return cancel_handler(sip, msg);
+
+	return false;
+}
+
+
+/**
+ * Allocate a SIP Server Transaction
+ *
+ * @param stp     Pointer to allocated SIP Server Transaction
+ * @param sip     SIP Stack instance
+ * @param msg     Incoming SIP message
+ * @param cancelh Cancel handler
+ * @param arg     Handler argument
+ *
+ * @return 0 if success, otherwise errorcode
+ */
+int sip_strans_alloc(struct sip_strans **stp, struct sip *sip,
+		     const struct sip_msg *msg, sip_cancel_h *cancelh,
+		     void *arg)
+{
+	struct sip_strans *st;
+
+	if (!stp || !sip || !msg)
+		return EINVAL;
+
+	st = mem_zalloc(sizeof(*st), destructor);
+	if (!st)
+		return ENOMEM;
+
+	hash_append(sip->ht_strans, hash_joaat_pl(&msg->via.branch),
+		    &st->he, st);
+
+	hash_append(sip->ht_strans_mrg, hash_joaat_pl(&msg->callid),
+		    &st->he_mrg, st);
+
+	st->invite  = !pl_strcmp(&msg->met, "INVITE");
+	st->msg     = mem_ref((void *)msg);
+	st->state   = TRYING;
+	st->cancelh = cancelh ? cancelh : dummy_handler;
+	st->arg     = arg;
+	st->sip     = sip;
+
+	*stp = st;
+
+	return 0;
+}
+
+
+/**
+ * Reply using a SIP Server Transaction
+ *
+ * @param stp   Pointer to allocated SIP Server Transaction
+ * @param sip   SIP Stack instance
+ * @param msg   Incoming SIP message
+ * @param dst   Destination network address
+ * @param scode Response status code
+ * @param mb    Buffer containing SIP response
+ *
+ * @return 0 if success, otherwise errorcode
+ */
+int sip_strans_reply(struct sip_strans **stp, struct sip *sip,
+		     const struct sip_msg *msg, const struct sa *dst,
+		     uint16_t scode, struct mbuf *mb)
+{
+	struct sip_strans *st = NULL;
+	int err;
+
+	if (!sip || !mb || !dst || (scode < 200 && !stp))
+		return EINVAL;
+
+	if (stp)
+		st = *stp;
+
+	if (!st) {
+		err = sip_strans_alloc(&st, sip, msg, NULL, NULL);
+		if (err)
+			return err;
+	}
+
+	mem_deref(st->mb);
+	st->mb = mem_ref(mb);
+	st->dst = *dst;
+
+	err = sip_send(sip, st->msg->sock, st->msg->tp, dst, mb);
+
+	if (stp)
+		*stp = (err || scode >= 200) ? NULL : st;
+
+	if (err) {
+		mem_deref(st);
+		return err;
+	}
+
+	if (st->invite) {
+		if (scode < 200) {
+			st->state = PROCEEDING;
+		}
+		else if (scode < 300) {
+			tmr_start(&st->tmr, 64 * SIP_T1, tmr_handler, st);
+			st->state = ACCEPTED;
+		}
+		else {
+			tmr_start(&st->tmr, 64 * SIP_T1, tmr_handler, st);
+			st->state = COMPLETED;
+
+			if (!sip_transp_reliable(st->msg->tp))
+				tmr_start(&st->tmrg, SIP_T1,
+					  retransmit_handler, st);
+		}
+	}
+	else {
+		if (scode < 200) {
+			st->state = PROCEEDING;
+		}
+		else {
+			if (!sip_transp_reliable(st->msg->tp)) {
+				tmr_start(&st->tmr, 64 * SIP_T1, tmr_handler,
+					  st);
+				st->state = COMPLETED;
+			}
+			else {
+				mem_deref(st);
+			}
+		}
+	}
+
+	return 0;
+}
+
+
+int sip_strans_init(struct sip *sip, uint32_t sz)
+{
+	int err;
+
+	err = sip_listen(NULL, sip, true, request_handler, sip);
+	if (err)
+		return err;
+
+	err = hash_alloc(&sip->ht_strans_mrg, sz);
+	if (err)
+		return err;
+
+	return hash_alloc(&sip->ht_strans, sz);
+}
+
+
+static const char *statename(enum state state)
+{
+	switch (state) {
+
+	case TRYING:     return "TRYING";
+	case PROCEEDING: return "PROCEEDING";
+	case ACCEPTED:   return "ACCEPTED";
+	case COMPLETED:  return "COMPLETED";
+	case CONFIRMED:  return "CONFIRMED";
+	default:         return "???";
+	}
+}
+
+
+static bool debug_handler(struct le *le, void *arg)
+{
+	struct sip_strans *st = le->data;
+	struct re_printf *pf = arg;
+
+	(void)re_hprintf(pf, "  %-10r %-10s %2llus (%r)\n",
+			 &st->msg->met,
+			 statename(st->state),
+			 tmr_get_expire(&st->tmr)/1000,
+			 &st->msg->via.branch);
+
+	return false;
+}
+
+
+int sip_strans_debug(struct re_printf *pf, const struct sip *sip)
+{
+	int err;
+
+	err = re_hprintf(pf, "server transactions:\n");
+	hash_apply(sip->ht_strans, debug_handler, pf);
+
+	return err;
+}