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/dialog.c b/src/sip/dialog.c
new file mode 100644
index 0000000..5863e20
--- /dev/null
+++ b/src/sip/dialog.c
@@ -0,0 +1,659 @@
+/**
+ * @file dialog.c  SIP Dialog
+ *
+ * Copyright (C) 2010 Creytiv.com
+ */
+#include <string.h>
+#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 {
+	ROUTE_OFFSET = 7,
+	X64_STRSIZE = 17,
+};
+
+struct sip_dialog {
+	struct uri route;
+	struct mbuf *mb;
+	char *callid;
+	char *ltag;
+	char *rtag;
+	char *uri;
+	uint32_t hash;
+	uint32_t lseq;
+	uint32_t rseq;
+	size_t cpos;
+};
+
+
+struct route_enc {
+	struct mbuf *mb;
+	size_t end;
+};
+
+
+static int x64_strdup(char **strp, uint64_t val)
+{
+	char *str;
+
+	str = mem_alloc(X64_STRSIZE, NULL);
+	if (!str)
+		return ENOMEM;
+
+	(void)re_snprintf(str, X64_STRSIZE, "%016llx", val);
+
+	*strp = str;
+
+	return 0;
+}
+
+
+static void destructor(void *arg)
+{
+	struct sip_dialog *dlg = arg;
+
+	mem_deref(dlg->callid);
+	mem_deref(dlg->ltag);
+	mem_deref(dlg->rtag);
+	mem_deref(dlg->uri);
+	mem_deref(dlg->mb);
+}
+
+
+/**
+ * Allocate a SIP Dialog
+ *
+ * @param dlgp      Pointer to allocated SIP Dialog
+ * @param uri       Target URI
+ * @param to_uri    To URI
+ * @param from_name From displayname (optional)
+ * @param from_uri  From URI
+ * @param routev    Route vector
+ * @param routec    Route count
+ *
+ * @return 0 if success, otherwise errorcode
+ */
+int sip_dialog_alloc(struct sip_dialog **dlgp,
+		     const char *uri, const char *to_uri,
+		     const char *from_name, const char *from_uri,
+		     const char *routev[], uint32_t routec)
+{
+	const uint64_t ltag = rand_u64();
+	struct sip_dialog *dlg;
+	struct sip_addr addr;
+	size_t rend = 0;
+	struct pl pl;
+	uint32_t i;
+	int err;
+
+	if (!dlgp || !uri || !to_uri || !from_uri)
+		return EINVAL;
+
+	dlg = mem_zalloc(sizeof(*dlg), destructor);
+	if (!dlg)
+		return ENOMEM;
+
+	dlg->hash = hash_fast_str(from_uri);
+	dlg->lseq = rand_u16();
+
+	err = str_dup(&dlg->uri, uri);
+	if (err)
+		goto out;
+
+	err = x64_strdup(&dlg->callid, rand_u64());
+	if (err)
+		goto out;
+
+	err = x64_strdup(&dlg->ltag, ltag);
+	if (err)
+		goto out;
+
+	dlg->mb = mbuf_alloc(512);
+	if (!dlg->mb) {
+		err = ENOMEM;
+		goto out;
+	}
+
+	for (i=0; i<routec; i++) {
+		err |= mbuf_printf(dlg->mb, "Route: <%s;lr>\r\n", routev[i]);
+		if (i == 0)
+			rend = dlg->mb->pos - 2;
+	}
+	err |= mbuf_printf(dlg->mb, "To: <%s>\r\n", to_uri);
+	dlg->cpos = dlg->mb->pos;
+	err |= mbuf_printf(dlg->mb, "From: %s%s%s<%s>;tag=%016llx\r\n",
+			   from_name ? "\"" : "", from_name,
+			   from_name ? "\" " : "",
+			   from_uri, ltag);
+	if (err)
+		goto out;
+
+	dlg->mb->pos = 0;
+
+	if (rend) {
+		pl.p = (const char *)mbuf_buf(dlg->mb) + ROUTE_OFFSET;
+		pl.l = rend - ROUTE_OFFSET;
+		err = sip_addr_decode(&addr, &pl);
+		dlg->route = addr.uri;
+	}
+	else {
+		pl_set_str(&pl, dlg->uri);
+		err = uri_decode(&dlg->route, &pl);
+	}
+
+ out:
+	if (err)
+		mem_deref(dlg);
+	else
+		*dlgp = dlg;
+
+	return err;
+}
+
+
+static bool record_route_handler(const struct sip_hdr *hdr,
+				 const struct sip_msg *msg,
+				 void *arg)
+{
+	struct route_enc *renc = arg;
+	(void)msg;
+
+	if (mbuf_printf(renc->mb, "Route: %r\r\n", &hdr->val))
+		return true;
+
+	if (!renc->end)
+	        renc->end = renc->mb->pos - 2;
+
+	return false;
+}
+
+
+/**
+ * Accept and create a SIP Dialog from an incoming SIP Message
+ *
+ * @param dlgp Pointer to allocated SIP Dialog
+ * @param msg  SIP Message
+ *
+ * @return 0 if success, otherwise errorcode
+ */
+int sip_dialog_accept(struct sip_dialog **dlgp, const struct sip_msg *msg)
+{
+	const struct sip_hdr *contact;
+	struct sip_dialog *dlg;
+	struct route_enc renc;
+	struct sip_addr addr;
+	struct pl pl;
+	int err;
+
+	if (!dlgp || !msg || !msg->req)
+		return EINVAL;
+
+	contact = sip_msg_hdr(msg, SIP_HDR_CONTACT);
+
+	if (!contact || !msg->callid.p)
+		return EBADMSG;
+
+	if (sip_addr_decode(&addr, &contact->val))
+		return EBADMSG;
+
+	dlg = mem_zalloc(sizeof(*dlg), destructor);
+	if (!dlg)
+		return ENOMEM;
+
+	dlg->hash = rand_u32();
+	dlg->lseq = rand_u16();
+	dlg->rseq = msg->cseq.num;
+
+	err = pl_strdup(&dlg->uri, &addr.auri);
+	if (err)
+		goto out;
+
+	err = pl_strdup(&dlg->callid, &msg->callid);
+	if (err)
+		goto out;
+
+	err = x64_strdup(&dlg->ltag, msg->tag);
+	if (err)
+		goto out;
+
+	err = pl_strdup(&dlg->rtag, &msg->from.tag);
+	if (err)
+		goto out;
+
+	dlg->mb = mbuf_alloc(512);
+	if (!dlg->mb) {
+		err = ENOMEM;
+		goto out;
+	}
+
+	renc.mb  = dlg->mb;
+	renc.end = 0;
+
+	err |= sip_msg_hdr_apply(msg, true, SIP_HDR_RECORD_ROUTE,
+				 record_route_handler, &renc) ? ENOMEM : 0;
+	err |= mbuf_printf(dlg->mb, "To: %r\r\n", &msg->from.val);
+	err |= mbuf_printf(dlg->mb, "From: %r;tag=%016llx\r\n", &msg->to.val,
+			   msg->tag);
+	if (err)
+		goto out;
+
+	dlg->mb->pos = 0;
+
+	if (renc.end) {
+		pl.p = (const char *)mbuf_buf(dlg->mb) + ROUTE_OFFSET;
+		pl.l = renc.end - ROUTE_OFFSET;
+		err = sip_addr_decode(&addr, &pl);
+		dlg->route = addr.uri;
+	}
+	else {
+		pl_set_str(&pl, dlg->uri);
+		err = uri_decode(&dlg->route, &pl);
+	}
+
+ out:
+	if (err)
+		mem_deref(dlg);
+	else
+		*dlgp = dlg;
+
+	return err;
+}
+
+
+/**
+ * Initialize a SIP Dialog from an incoming SIP Message
+ *
+ * @param dlg SIP Dialog to initialize
+ * @param msg SIP Message
+ *
+ * @return 0 if success, otherwise errorcode
+ */
+int sip_dialog_create(struct sip_dialog *dlg, const struct sip_msg *msg)
+{
+	char *uri = NULL, *rtag = NULL;
+	const struct sip_hdr *contact;
+	struct route_enc renc;
+	struct sip_addr addr;
+	struct pl pl;
+	int err;
+
+	if (!dlg || dlg->rtag || !dlg->cpos || !msg)
+		return EINVAL;
+
+	contact = sip_msg_hdr(msg, SIP_HDR_CONTACT);
+
+	if (!contact)
+		return EBADMSG;
+
+	if (sip_addr_decode(&addr, &contact->val))
+		return EBADMSG;
+
+	renc.mb = mbuf_alloc(512);
+	if (!renc.mb)
+		return ENOMEM;
+
+	err = pl_strdup(&uri, &addr.auri);
+	if (err)
+		goto out;
+
+	err = pl_strdup(&rtag, msg->req ? &msg->from.tag : &msg->to.tag);
+	if (err)
+		goto out;
+
+	renc.end = 0;
+
+	err |= sip_msg_hdr_apply(msg, msg->req, SIP_HDR_RECORD_ROUTE,
+				 record_route_handler, &renc) ? ENOMEM : 0;
+	err |= mbuf_printf(renc.mb, "To: %r\r\n",
+			   msg->req ? &msg->from.val : &msg->to.val);
+
+	dlg->mb->pos = dlg->cpos;
+	err |= mbuf_write_mem(renc.mb, mbuf_buf(dlg->mb),
+			      mbuf_get_left(dlg->mb));
+	dlg->mb->pos = 0;
+
+	if (err)
+		goto out;
+
+	renc.mb->pos = 0;
+
+	if (renc.end) {
+		pl.p = (const char *)mbuf_buf(renc.mb) + ROUTE_OFFSET;
+		pl.l = renc.end - ROUTE_OFFSET;
+		err = sip_addr_decode(&addr, &pl);
+		if (err)
+			goto out;
+
+		dlg->route = addr.uri;
+	}
+	else {
+		struct uri tmp;
+
+		pl_set_str(&pl, uri);
+		err = uri_decode(&tmp, &pl);
+		if (err)
+			goto out;
+
+		dlg->route = tmp;
+	}
+
+	mem_deref(dlg->mb);
+	mem_deref(dlg->uri);
+
+	dlg->mb   = mem_ref(renc.mb);
+	dlg->rtag = mem_ref(rtag);
+	dlg->uri  = mem_ref(uri);
+	dlg->rseq = msg->req ? msg->cseq.num : 0;
+	dlg->cpos = 0;
+
+ out:
+	mem_deref(renc.mb);
+	mem_deref(rtag);
+	mem_deref(uri);
+
+	return err;
+}
+
+
+/**
+ * Fork a SIP Dialog from an incoming SIP Message
+ *
+ * @param dlgp Pointer to allocated SIP Dialog
+ * @param odlg Original SIP Dialog
+ * @param msg  SIP Message
+ *
+ * @return 0 if success, otherwise errorcode
+ */
+int sip_dialog_fork(struct sip_dialog **dlgp, struct sip_dialog *odlg,
+		    const struct sip_msg *msg)
+{
+	const struct sip_hdr *contact;
+	struct sip_dialog *dlg;
+	struct route_enc renc;
+	struct sip_addr addr;
+	struct pl pl;
+	int err;
+
+	if (!dlgp || !odlg || !odlg->cpos || !msg)
+		return EINVAL;
+
+	contact = sip_msg_hdr(msg, SIP_HDR_CONTACT);
+
+	if (!contact || !msg->callid.p)
+		return EBADMSG;
+
+	if (sip_addr_decode(&addr, &contact->val))
+		return EBADMSG;
+
+	dlg = mem_zalloc(sizeof(*dlg), destructor);
+	if (!dlg)
+		return ENOMEM;
+
+	dlg->callid = mem_ref(odlg->callid);
+	dlg->ltag   = mem_ref(odlg->ltag);
+	dlg->hash   = odlg->hash;
+	dlg->lseq   = odlg->lseq;
+	dlg->rseq   = msg->req ? msg->cseq.num : 0;
+
+	err = pl_strdup(&dlg->uri, &addr.auri);
+	if (err)
+		goto out;
+
+	err = pl_strdup(&dlg->rtag, msg->req ? &msg->from.tag : &msg->to.tag);
+	if (err)
+		goto out;
+
+	dlg->mb = mbuf_alloc(512);
+	if (!dlg->mb) {
+		err = ENOMEM;
+		goto out;
+	}
+
+	renc.mb  = dlg->mb;
+	renc.end = 0;
+
+	err |= sip_msg_hdr_apply(msg, msg->req, SIP_HDR_RECORD_ROUTE,
+				 record_route_handler, &renc) ? ENOMEM : 0;
+	err |= mbuf_printf(dlg->mb, "To: %r\r\n",
+			   msg->req ? &msg->from.val : &msg->to.val);
+
+	odlg->mb->pos = odlg->cpos;
+	err |= mbuf_write_mem(dlg->mb, mbuf_buf(odlg->mb),
+			      mbuf_get_left(odlg->mb));
+	odlg->mb->pos = 0;
+
+	if (err)
+		goto out;
+
+	dlg->mb->pos = 0;
+
+	if (renc.end) {
+		pl.p = (const char *)mbuf_buf(dlg->mb) + ROUTE_OFFSET;
+		pl.l = renc.end - ROUTE_OFFSET;
+		err = sip_addr_decode(&addr, &pl);
+		dlg->route = addr.uri;
+	}
+	else {
+		pl_set_str(&pl, dlg->uri);
+		err = uri_decode(&dlg->route, &pl);
+	}
+
+ out:
+	if (err)
+		mem_deref(dlg);
+	else
+		*dlgp = dlg;
+
+	return err;
+}
+
+
+/**
+ * Update an existing SIP Dialog from a SIP Message
+ *
+ * @param dlg SIP Dialog to update
+ * @param msg SIP Message
+ *
+ * @return 0 if success, otherwise errorcode
+ */
+int sip_dialog_update(struct sip_dialog *dlg, const struct sip_msg *msg)
+{
+	const struct sip_hdr *contact;
+	struct sip_addr addr;
+	char *uri;
+	int err;
+
+	if (!dlg || !msg)
+		return EINVAL;
+
+	contact = sip_msg_hdr(msg, SIP_HDR_CONTACT);
+	if (!contact)
+		return EBADMSG;
+
+	if (sip_addr_decode(&addr, &contact->val))
+		return EBADMSG;
+
+	err = pl_strdup(&uri, &addr.auri);
+	if (err)
+		return err;
+
+	if (dlg->route.scheme.p == dlg->uri) {
+
+		struct uri tmp;
+		struct pl pl;
+
+		pl_set_str(&pl, uri);
+		err = uri_decode(&tmp, &pl);
+		if (err)
+			goto out;
+
+		dlg->route = tmp;
+	}
+
+	mem_deref(dlg->uri);
+	dlg->uri = mem_ref(uri);
+
+ out:
+	mem_deref(uri);
+
+	return err;
+}
+
+
+/**
+ * Check if a remote sequence number is valid
+ *
+ * @param dlg SIP Dialog
+ * @param msg SIP Message
+ *
+ * @return True if valid, False if invalid
+ */
+bool sip_dialog_rseq_valid(struct sip_dialog *dlg, const struct sip_msg *msg)
+{
+	if (!dlg || !msg || !msg->req)
+		return false;
+
+	if (msg->cseq.num < dlg->rseq)
+		return false;
+
+	dlg->rseq = msg->cseq.num;
+
+	return true;
+}
+
+
+int sip_dialog_encode(struct mbuf *mb, struct sip_dialog *dlg, uint32_t cseq,
+		      const char *met)
+{
+	int err = 0;
+
+	if (!mb || !dlg || !met)
+		return EINVAL;
+
+	err |= mbuf_write_mem(mb, mbuf_buf(dlg->mb), mbuf_get_left(dlg->mb));
+	err |= mbuf_printf(mb, "Call-ID: %s\r\n", dlg->callid);
+	err |= mbuf_printf(mb, "CSeq: %u %s\r\n", strcmp(met, "ACK") ?
+			   dlg->lseq++ : cseq, met);
+
+	return err;
+}
+
+
+const char *sip_dialog_uri(const struct sip_dialog *dlg)
+{
+	return dlg ? dlg->uri : NULL;
+}
+
+
+const struct uri *sip_dialog_route(const struct sip_dialog *dlg)
+{
+	return dlg ? &dlg->route : NULL;
+}
+
+
+uint32_t sip_dialog_hash(const struct sip_dialog *dlg)
+{
+	return dlg ? dlg->hash : 0;
+}
+
+
+/**
+ * Get the Call-ID from a SIP Dialog
+ *
+ * @param dlg SIP Dialog
+ *
+ * @return Call-ID string
+ */
+const char *sip_dialog_callid(const struct sip_dialog *dlg)
+{
+	return dlg ? dlg->callid : NULL;
+}
+
+
+/**
+ * Get the local sequence number from a SIP Dialog
+ *
+ * @param dlg SIP Dialog
+ *
+ * @return Local sequence number
+ */
+uint32_t sip_dialog_lseq(const struct sip_dialog *dlg)
+{
+	return dlg ? dlg->lseq : 0;
+}
+
+
+/**
+ * Check if a SIP Dialog is established
+ *
+ * @param dlg SIP Dialog
+ *
+ * @return True if established, False if not
+ */
+bool sip_dialog_established(const struct sip_dialog *dlg)
+{
+	return dlg && dlg->rtag;
+}
+
+
+/**
+ * Compare a SIP Dialog against a SIP Message
+ *
+ * @param dlg SIP Dialog
+ * @param msg SIP Message
+ *
+ * @return True if match, False if no match
+ */
+bool sip_dialog_cmp(const struct sip_dialog *dlg, const struct sip_msg *msg)
+{
+	if (!dlg || !msg)
+		return false;
+
+	if (pl_strcmp(&msg->callid, dlg->callid))
+		return false;
+
+	if (pl_strcmp(msg->req ? &msg->to.tag : &msg->from.tag, dlg->ltag))
+		return false;
+
+	if (pl_strcmp(msg->req ? &msg->from.tag : &msg->to.tag, dlg->rtag))
+		return false;
+
+	return true;
+}
+
+
+/**
+ * Compare a half SIP Dialog against a SIP Message
+ *
+ * @param dlg SIP Dialog
+ * @param msg SIP Message
+ *
+ * @return True if match, False if no match
+ */
+bool sip_dialog_cmp_half(const struct sip_dialog *dlg,
+			 const struct sip_msg *msg)
+{
+	if (!dlg || !msg)
+		return false;
+
+	if (pl_strcmp(&msg->callid, dlg->callid))
+		return false;
+
+	if (pl_strcmp(msg->req ? &msg->to.tag : &msg->from.tag, dlg->ltag))
+		return false;
+
+	return true;
+}